Merge "veyron/lib/glob: Recursive matches are not exact matches."
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 96ed5d5..baa2f6b 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -45,14 +45,10 @@
"io"
"io/ioutil"
"os"
- "path/filepath"
"strings"
"sync"
"veyron.io/veyron/veyron2/vlog"
-
- isecurity "veyron.io/veyron/veyron/runtimes/google/security"
- seclib "veyron.io/veyron/veyron/security"
)
// Shell represents the context within which commands are run.
@@ -61,7 +57,7 @@
env map[string]string
cmds map[string]*commandDesc
handles map[Handle]struct{}
- idfile string
+ credDir string
}
type commandDesc struct {
@@ -82,9 +78,9 @@
var child = &childRegistrar{mains: make(map[string]*childEntryPoint)}
// NewShell creates a new instance of Shell. If this new instance is
-// is a test and no identity has been configured in the environment
-// via VEYRON_IDENTITY then CreateAndUseNewID will be used to configure a new
-// ID for the shell and its children.
+// is a test and no credentials have been configured in the environment
+// via VEYRON_CREDENTIALS then CreateAndUseNewCredentials will be used to
+// configure a new ID for the shell and its children.
// NewShell takes optional regexp patterns that can be used to specify
// subprocess commands that are implemented in the same binary as this shell
// (i.e. have been registered using modules.RegisterChild) to be
@@ -98,8 +94,9 @@
cmds: make(map[string]*commandDesc),
handles: make(map[Handle]struct{}),
}
- if flag.Lookup("test.run") != nil && os.Getenv("VEYRON_IDENTITY") == "" {
- if err := sh.CreateAndUseNewID(); err != nil {
+ if flag.Lookup("test.run") != nil && os.Getenv("VEYRON_CREDENTIALS") == "" {
+ if err := sh.CreateAndUseNewCredentials(); err != nil {
+ // TODO(cnicolaou): return an error rather than panic.
panic(err)
}
}
@@ -109,25 +106,15 @@
return sh
}
-// CreateAndUseNewID setups a new ID and then configures the shell and all of its
-// children to use to it.
-func (sh *Shell) CreateAndUseNewID() error {
- id, err := isecurity.NewPrivateID("test", nil)
+// CreateAndUseNewCredentials setups a new credentials directory and then
+// configures the shell and all of its children to use to it.
+func (sh *Shell) CreateAndUseNewCredentials() error {
+ dir, err := ioutil.TempDir("", "veyron_credentials")
if err != nil {
return err
}
- f, err := ioutil.TempFile("", filepath.Base(os.Args[0]))
- if err != nil {
- return err
- }
- defer f.Close()
- filePath := f.Name()
- if err := seclib.SaveIdentity(f, id); err != nil {
- os.Remove(filePath)
- return err
- }
- sh.idfile = filePath
- sh.SetVar("VEYRON_IDENTITY", sh.idfile)
+ sh.credDir = dir
+ sh.SetVar("VEYRON_CREDENTIALS", sh.credDir)
return nil
}
@@ -274,8 +261,8 @@
for k, _ := range handles {
k.Shutdown(stdout, stderr)
}
- if len(sh.idfile) > 0 {
- os.Remove(sh.idfile)
+ if len(sh.credDir) > 0 {
+ os.RemoveAll(sh.credDir)
}
}
diff --git a/lib/netconfig/ipaux_other.go b/lib/netconfig/ipaux_other.go
index 5345a8f..23496f0 100644
--- a/lib/netconfig/ipaux_other.go
+++ b/lib/netconfig/ipaux_other.go
@@ -1,4 +1,4 @@
-// +build plan9 windows nacl
+// +build !linux,!bsd
// TODO(bprosnitz) Should change for nacl?
package netconfig
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index ade0df3..634e1d2 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -324,22 +324,20 @@
// TestCleanRemoteShutdown verifies that remote shutdown works correctly.
func TestCleanRemoteShutdown(t *testing.T) {
- r := rt.Init()
+ r := rt.Init(veyron2.ForceNewSecurityModel{})
defer r.Cleanup()
sh := modules.NewShell()
sh.AddSubprocess("handleDefaults", "")
defer sh.Cleanup(os.Stderr, os.Stderr)
- // This sets up the child's identity to be derived from the parent's (so
- // that default authorization works for RPCs between the two).
- // TODO(caprita): Consider making this boilerplate part of blackbox.
- id := r.Identity()
- idFile := security.SaveIdentityToFile(security.NewBlessedIdentity(id, "test"))
- defer os.Remove(idFile)
+ // Set the child process up with a blessing from the parent so that
+ // the default authorization works for RPCs between the two.
+ childcreds := security.NewVeyronCredentials(r.Principal(), "child")
+ defer os.RemoveAll(childcreds)
configServer, configServiceName, ch := createConfigServer(t)
defer configServer.Stop()
- sh.SetVar("VEYRON_IDENTITY", idFile)
+ sh.SetVar("VEYRON_CREDENTIALS", childcreds)
sh.SetVar(mgmt.ParentNodeManagerConfigKey, configServiceName)
h, err := sh.Start("handleDefaults")
if err != nil {
diff --git a/lib/testutil/security/util.go b/lib/testutil/security/util.go
index 9a45b2c..2bf1fdb 100644
--- a/lib/testutil/security/util.go
+++ b/lib/testutil/security/util.go
@@ -5,34 +5,55 @@
import (
"io/ioutil"
"os"
- "strconv"
- "time"
- "veyron.io/veyron/veyron/lib/testutil"
- isecurity "veyron.io/veyron/veyron/runtimes/google/security"
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/security"
)
-// NewBlessedIdentity creates a new identity and blesses it using the provided blesser
-// under the provided name. This function is meant to be used for testing purposes only,
-// it panics if there is an error.
-func NewBlessedIdentity(blesser security.PrivateID, name string) security.PrivateID {
- id, err := isecurity.NewPrivateID("test", nil)
+// NewVeyronCredentials generates a directory with a new principal
+// that can be used as a value for the VEYRON_CREDENTIALS environment
+// variable to initialize a Runtime.
+//
+// The principal created uses a blessing from 'parent', with the extension
+// 'name' as its default blessing.
+//
+// It returns the path to the directory created.
+func NewVeyronCredentials(parent security.Principal, name string) string {
+ dir, err := ioutil.TempDir("", "veyron_credentials")
if err != nil {
panic(err)
}
+ p, _, err := vsecurity.NewPersistentPrincipal(dir)
+ if err != nil {
+ panic(err)
+ }
+ blessings, err := parent.Bless(p.PublicKey(), parent.BlessingStore().Default(), name, security.UnconstrainedUse())
+ if err != nil {
+ panic(err)
+ }
+ SetDefaultBlessings(p, blessings)
+ return dir
+}
- blessedID, err := blesser.Bless(id.PublicID(), name, 5*time.Minute, nil)
- if err != nil {
+// SetDefaultBlessings updates the BlessingStore and BlessingRoots of p
+// so that:
+// (1) b is revealed to all clients that connect to Servers operated
+// by 'p' (BlessingStore.Default)
+// (2) b is revealed to all servers that clients connect to on behalf
+// of p (BlessingStore.Set(..., security.AllPrincipals))
+// (3) p recognizes all blessings that have the same root certificate as b.
+// (AddToRoots)
+func SetDefaultBlessings(p security.Principal, b security.Blessings) {
+ if err := p.BlessingStore().SetDefault(b); err != nil {
panic(err)
}
- derivedID, err := id.Derive(blessedID)
- if err != nil {
+ if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
panic(err)
}
- return derivedID
+ if err := p.AddToRoots(b); err != nil {
+ panic(err)
+ }
}
// SaveACLToFile saves the provided ACL in JSON format to a randomly created
@@ -51,23 +72,3 @@
}
return f.Name()
}
-
-// SaveIdentityToFile saves the provided identity in Base64VOM format
-// to a randomly created temporary file, and returns the path to the file.
-// This function is meant to be used for testing purposes only, it panics
-// if there is an error. The caller must ensure that the created file
-// is removed once it is no longer needed.
-func SaveIdentityToFile(id security.PrivateID) string {
- f, err := ioutil.TempFile("", strconv.Itoa(testutil.Rand.Int()))
- if err != nil {
- panic(err)
- }
- defer f.Close()
- filePath := f.Name()
-
- if err := vsecurity.SaveIdentity(f, id); err != nil {
- os.Remove(filePath)
- panic(err)
- }
- return filePath
-}
diff --git a/lib/testutil/security/util_test.go b/lib/testutil/security/util_test.go
index 5125d1e..ea9ee21 100644
--- a/lib/testutil/security/util_test.go
+++ b/lib/testutil/security/util_test.go
@@ -1,47 +1,37 @@
package security
import (
- "fmt"
"os"
"reflect"
"testing"
- isecurity "veyron.io/veyron/veyron/runtimes/google/security"
vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
)
-func TestNewBlessedIdentity(t *testing.T) {
+func TestNewVeyronCredentials(t *testing.T) {
r, err := rt.New()
if err != nil {
- t.Fatalf("rt.New failed: %v", err)
+ t.Fatal(err)
}
defer r.Cleanup()
- newID := func(name string) security.PrivateID {
- id, err := r.NewIdentity(name)
- if err != nil {
- t.Fatalf("r.NewIdentity failed: %v", err)
- }
- isecurity.TrustIdentityProviders(id)
- return id
+
+ parent := r.Principal()
+ childdir := NewVeyronCredentials(parent, "child")
+ _, existed, err := vsecurity.NewPersistentPrincipal(childdir)
+ if err != nil {
+ t.Fatal(err)
}
- testdata := []struct {
- blesser security.PrivateID
- blessingName, name string
- }{
- {blesser: newID("google"), blessingName: "alice", name: "PrivateID:google/alice"},
- {blesser: newID("google"), blessingName: "bob", name: "PrivateID:google/bob"},
- {blesser: newID("veyron"), blessingName: "alice", name: "PrivateID:veyron/alice"},
- {blesser: newID("veyron"), blessingName: "bob", name: "PrivateID:veyron/bob"},
- {blesser: NewBlessedIdentity(newID("google"), "alice"), blessingName: "tv", name: "PrivateID:google/alice/tv"},
+ if !existed {
+ t.Fatalf("Expected NewVeyronCredentials to have populated the directory %q", childdir)
}
- for _, d := range testdata {
- if got, want := fmt.Sprintf("%s", NewBlessedIdentity(d.blesser, d.blessingName)), d.name; got != want {
- t.Errorf("NewBlessedIdentity(%q, %q): Got %q, want %q", d.blesser, d.blessingName, got, want)
- }
- }
+ // TODO(ashankar,ataly): Figure out what APIs should we use for more detailed testing
+ // of the child principal, for example:
+ // - Parent should recognize the child's default blessing
+ // - Child should recognize the parent's default blessing
+ // - Child's blessing name should be that of the parent with "/child" appended.
}
func TestSaveACLToFile(t *testing.T) {
@@ -76,31 +66,3 @@
t.Fatalf("Got ACL %v, but want %v", loadedACL, acl)
}
}
-
-func TestSaveIdentityToFile(t *testing.T) {
- r, err := rt.New()
- if err != nil {
- t.Fatalf("rt.New failed: %v", err)
- }
- defer r.Cleanup()
- id, err := r.NewIdentity("test")
- if err != nil {
- t.Fatalf("r.NewIdentity failed: %v", err)
- }
-
- filePath := SaveIdentityToFile(id)
- defer os.Remove(filePath)
-
- f, err := os.Open(filePath)
- if err != nil {
- t.Fatalf("os.Open(%v) failed: %v", filePath, err)
- }
- defer f.Close()
- loadedID, err := vsecurity.LoadIdentity(f)
- if err != nil {
- t.Fatalf("LoadIdentity failed: %v", err)
- }
- if !reflect.DeepEqual(loadedID, id) {
- t.Fatalf("Got Identity %v, but want %v", loadedID, id)
- }
-}
diff --git a/runtimes/google/ipc/server.go b/runtimes/google/ipc/server.go
index 610e2a6..ddefad2 100644
--- a/runtimes/google/ipc/server.go
+++ b/runtimes/google/ipc/server.go
@@ -152,6 +152,8 @@
return nil, err
}
}
+ // TODO(cnicolaou): pass ServesMountTableOpt to streamMgr.Listen so that
+ // it can more cleanly set the IsMountTable bit in the endpoint.
ln, ep, err := s.streamMgr.Listen(protocol, address, s.listenerOpts...)
if err != nil {
vlog.Errorf("ipc: Listen on %v %v failed: %v", protocol, address, err)
@@ -197,18 +199,18 @@
// Each flow is served from its own goroutine.
s.active.Add(1)
if protocol == inaming.Network {
- go func(ln stream.Listener, ep naming.Endpoint, proxy string) {
+ go func(ln stream.Listener, ep *inaming.Endpoint, proxy string) {
s.proxyListenLoop(ln, ep, proxy)
s.active.Done()
- }(ln, ep, proxyName)
+ }(ln, iep, proxyName)
} else {
go func(ln stream.Listener, ep naming.Endpoint) {
s.listenLoop(ln, ep)
s.active.Done()
- }(ln, ep)
+ }(ln, iep)
}
s.Unlock()
- s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
+ s.publisher.AddServer(s.publishEP(iep, s.servesMountTable), s.servesMountTable)
return ep, nil
}
@@ -221,7 +223,6 @@
if !ok {
return nil, nil, fmt.Errorf("failed translating internal endpoint data types")
}
-
switch iep.Protocol {
case "tcp", "tcp4", "tcp6":
host, port, err := net.SplitHostPort(iep.Address)
@@ -338,41 +339,48 @@
vlog.Errorf("ipc: Listen on %v %v failed: %v", protocol, address, err)
return nil, err
}
+ ipep, ok := pep.(*inaming.Endpoint)
+ if !ok {
+ return nil, fmt.Errorf("failed translating internal endpoint data types")
+ }
// We have a goroutine for listening on proxy connections.
s.active.Add(1)
- go func(ln stream.Listener, ep naming.Endpoint, proxy string) {
+ go func(ln stream.Listener, ep *inaming.Endpoint, proxy string) {
s.proxyListenLoop(ln, ep, proxy)
s.active.Done()
- }(pln, pep, listenSpec.Proxy)
+ }(pln, ipep, listenSpec.Proxy)
s.listeners[pln] = nil
- s.publisher.AddServer(s.publishEP(pep), s.servesMountTable)
+ // TODO(cnicolaou,p): AddServer no longer needs to take the
+ // servesMountTable bool since it can be extracted from the endpoint.
+ s.publisher.AddServer(s.publishEP(ipep, s.servesMountTable), s.servesMountTable)
} else {
- s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
+ s.publisher.AddServer(s.publishEP(ep, s.servesMountTable), s.servesMountTable)
}
s.Unlock()
return ep, nil
}
-func (s *server) publishEP(ep naming.Endpoint) string {
+func (s *server) publishEP(ep *inaming.Endpoint, servesMountTable bool) string {
var name string
if !s.servesMountTable {
// Make sure that client MountTable code doesn't try and
// ResolveStep past this final address.
name = "//"
}
+ ep.IsMountTable = servesMountTable
return naming.JoinAddressName(ep.String(), name)
}
-func (s *server) proxyListenLoop(ln stream.Listener, ep naming.Endpoint, proxy string) {
+func (s *server) proxyListenLoop(ln stream.Listener, iep *inaming.Endpoint, proxy string) {
const (
min = 5 * time.Millisecond
max = 5 * time.Minute
)
for {
- s.listenLoop(ln, ep)
+ s.listenLoop(ln, iep)
// The listener is done, so:
// (1) Unpublish its name
- s.publisher.RemoveServer(s.publishEP(ep))
+ s.publisher.RemoveServer(s.publishEP(iep, s.servesMountTable))
// (2) Reconnect to the proxy unless the server has been stopped
backoff := min
ln = nil
@@ -384,9 +392,17 @@
vlog.VI(1).Infof("Failed to resolve proxy %q (%v), will retry in %v", proxy, err, backoff)
break
}
+ var ep naming.Endpoint
ln, ep, err = s.streamMgr.Listen(inaming.Network, resolved, s.listenerOpts...)
if err == nil {
- vlog.VI(1).Infof("Reconnected to proxy at %q listener: (%v, %v)", proxy, ln, ep)
+ var ok bool
+ iep, ok = ep.(*inaming.Endpoint)
+ if !ok {
+ vlog.Errorf("failed translating internal endpoint data types")
+ ln = nil
+ continue
+ }
+ vlog.VI(1).Infof("Reconnected to proxy at %q listener: (%v, %v)", proxy, ln, iep)
break
}
if backoff = backoff * 2; backoff > max {
@@ -397,8 +413,13 @@
return
}
}
+ // TODO(cnicolaou,ashankar): this won't work when we are both
+ // proxying and publishing locally, which is the common case.
+ // listenLoop, dhcpLoop and the original publish are all publishing
+ // addresses to the same name, but the client is not smart enough
+ // to choose sensibly between them.
// (3) reconnected, publish new address
- s.publisher.AddServer(s.publishEP(ep), s.servesMountTable)
+ s.publisher.AddServer(s.publishEP(iep, s.servesMountTable), s.servesMountTable)
s.Lock()
s.listeners[ln] = nil
s.Unlock()
@@ -438,7 +459,7 @@
for _, a := range addrs {
if ip := netstate.AsIP(a); ip != nil {
dhcpl.ep.Address = net.JoinHostPort(ip.String(), dhcpl.port)
- fn(s.publishEP(dhcpl.ep))
+ fn(s.publishEP(dhcpl.ep, s.servesMountTable))
}
}
}
@@ -459,6 +480,11 @@
s.Unlock()
return
}
+ // TODO(cnicolaou,ashankar): this won't work when we are both
+ // proxying and publishing locally, which is the common case.
+ // listenLoop, dhcpLoop and the original publish are all publishing
+ // addresses to the same name, but the client is not smart enough
+ // to choose sensibly between them.
publisher := s.publisher
s.Unlock()
switch setting.Name() {
diff --git a/runtimes/google/ipc/stream/benchmark/throughput_flow.go b/runtimes/google/ipc/stream/benchmark/throughput_flow.go
index 01d925b..ff69e6f 100644
--- a/runtimes/google/ipc/stream/benchmark/throughput_flow.go
+++ b/runtimes/google/ipc/stream/benchmark/throughput_flow.go
@@ -5,8 +5,6 @@
"testing"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
- "veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
- "veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
@@ -27,11 +25,9 @@
// that calling stream.Manager.Dial to each of the endpoints will end up
// creating a new VIF.
func createListeners(mode veyron2.VCSecurityLevel, m stream.Manager, N int) (servers []listener, err error) {
- // TODO(ashankar): Remove once PublicIDs are gone since at that point, if no Principal is provided, an anonymous one should be gereated within vc.go
- serverP := vc.LocalPrincipal{sectest.NewPrincipal("server")}
for i := 0; i < N; i++ {
var l listener
- if l.ln, l.ep, err = m.Listen("tcp", "127.0.0.1:0", mode, serverP); err != nil {
+ if l.ln, l.ep, err = m.Listen("tcp", "127.0.0.1:0", mode); err != nil {
return
}
servers = append(servers, l)
@@ -52,14 +48,12 @@
rchan := make(chan io.ReadCloser, nFlows)
wchan := make(chan io.WriteCloser, nFlows)
- // TODO(ashankar): Remove once PublicIDs are gone since at that point, if no Principal is provided, an anonymous one should be gereated within vc.go
- clientP := vc.LocalPrincipal{sectest.NewPrincipal("client")}
go func() {
defer close(wchan)
for i := 0; i < nVIFs; i++ {
ep := lns[i].ep
for j := 0; j < nVCsPerVIF; j++ {
- vc, err := client.Dial(ep, mode, clientP)
+ vc, err := client.Dial(ep, mode)
if err != nil {
b.Error(err)
return
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index 5729c30..1130e2c 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -320,7 +320,7 @@
// Have the server read from each flow and write to rchan.
rchan := make(chan string)
- ln, ep, err := server.Listen("tcp", "127.0.0.1:0", newPrincipal("server"))
+ ln, ep, err := server.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
@@ -356,7 +356,7 @@
var vcs [nVCs]stream.VC
for i := 0; i < nVCs; i++ {
var err error
- vcs[i], err = client.Dial(ep, newPrincipal("client"))
+ vcs[i], err = client.Dial(ep)
if err != nil {
t.Fatal(err)
}
@@ -433,7 +433,6 @@
func TestServerRestartDuringClientLifetime(t *testing.T) {
client := InternalNew(naming.FixedRoutingID(0xcccccccc))
- clientP := newPrincipal("client") // TODO(ashankar): Remove. Once the PublicID code is gone anonymous principals should be generated within vc.go if one is not provided.
server := blackbox.HelperCommand(t, "runServer", "127.0.0.1:0")
server.Cmd.Start()
addr, err := server.ReadLineFromChild()
@@ -444,12 +443,12 @@
if err != nil {
t.Fatalf("inaming.NewEndpoint(%q): %v", addr, err)
}
- if _, err := client.Dial(ep, clientP); err != nil {
+ if _, err := client.Dial(ep); err != nil {
t.Fatal(err)
}
server.Cleanup()
// A new VC cannot be created since the server is dead
- if _, err := client.Dial(ep, clientP); err == nil {
+ if _, err := client.Dial(ep); err == nil {
t.Fatal("Expected client.Dial to fail since server is dead")
}
// Restarting the server, listening on the same address as before
@@ -459,7 +458,7 @@
if addr2, err := server.ReadLineFromChild(); addr2 != addr || err != nil {
t.Fatalf("Got (%q, %v) want (%q, nil)", addr2, err, addr)
}
- if _, err := client.Dial(ep, clientP); err != nil {
+ if _, err := client.Dial(ep); err != nil {
t.Fatal(err)
}
}
@@ -471,7 +470,7 @@
func runServer(argv []string) {
server := InternalNew(naming.FixedRoutingID(0x55555555))
- _, ep, err := server.Listen("tcp", argv[0], newPrincipal("server"))
+ _, ep, err := server.Listen("tcp", argv[0])
if err != nil {
fmt.Println(err)
return
diff --git a/runtimes/google/ipc/stream/proxy/proxy.go b/runtimes/google/ipc/stream/proxy/proxy.go
index 4099d71..683ea0c 100644
--- a/runtimes/google/ipc/stream/proxy/proxy.go
+++ b/runtimes/google/ipc/stream/proxy/proxy.go
@@ -127,14 +127,7 @@
// New creates a new Proxy that listens for network connections on the provided
// (network, address) pair and routes VC traffic between accepted connections.
-//
-// TODO(ashankar): Change principal to security.Principal once the old security model is ripped out.
-func New(rid naming.RoutingID, principal interface{}, network, address, pubAddress string) (*Proxy, error) {
- if _, ok := principal.(security.Principal); principal != nil && !ok {
- if _, ok := principal.(security.PrivateID); !ok {
- return nil, fmt.Errorf("principal argument must be either a security.Principal or a security.PrivateID, not a %T", principal)
- }
- }
+func New(rid naming.RoutingID, principal security.Principal, network, address, pubAddress string) (*Proxy, error) {
ln, err := net.Listen(network, address)
if err != nil {
return nil, fmt.Errorf("net.Listen(%q, %q) failed: %v", network, address, err)
@@ -148,11 +141,7 @@
servers: &servermap{m: make(map[naming.RoutingID]*server)},
processes: make(map[*process]struct{}),
pubAddress: pubAddress,
- }
- if p, ok := principal.(security.Principal); ok {
- proxy.principal = vc.LocalPrincipal{p}
- } else if principal != nil {
- proxy.principal = vc.FixedLocalID(principal.(security.PrivateID))
+ principal: vc.LocalPrincipal{principal},
}
go proxy.listenLoop()
return proxy, nil
@@ -518,6 +507,11 @@
vc.Close("duplicate OpenVC request")
return nil
}
+ version, err := version.CommonVersion(m.DstEndpoint, m.SrcEndpoint)
+ if err != nil {
+ p.SendCloseVC(m.VCI, fmt.Errorf("incompatible IPC protocol versions: %v", err))
+ return nil
+ }
vc := vc.InternalNew(vc.Params{
VCI: m.VCI,
LocalEP: m.DstEndpoint,
@@ -525,6 +519,7 @@
Pool: iobuf.NewPool(0),
ReserveBytes: message.HeaderSizeBytes,
Helper: p,
+ Version: version,
})
p.servers[m.VCI] = vc
proxyLog().Infof("Registered VC %v from server on process %v", vc, p)
diff --git a/runtimes/google/ipc/stream/proxy/proxy_test.go b/runtimes/google/ipc/stream/proxy/proxy_test.go
index 8803604..e705c6c 100644
--- a/runtimes/google/ipc/stream/proxy/proxy_test.go
+++ b/runtimes/google/ipc/stream/proxy/proxy_test.go
@@ -10,23 +10,14 @@
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/security"
_ "veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/proxy"
+ "veyron.io/veyron/veyron/runtimes/google/ipc/stream/sectest"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
- isecurity "veyron.io/veyron/veyron/runtimes/google/security"
)
-func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name, nil)
- if err != nil {
- panic(err)
- }
- return id
-}
-
func TestProxy(t *testing.T) {
proxy, err := proxy.New(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), nil, "tcp", "127.0.0.1:0", "")
if err != nil {
@@ -114,9 +105,9 @@
}
}
-func TestProxyIdentity(t *testing.T) {
- proxyID := newID("proxy")
- proxy, err := proxy.New(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), proxyID, "tcp", "127.0.0.1:0", "")
+func TestProxyAuthentication(t *testing.T) {
+ pproxy := sectest.NewPrincipal("proxy")
+ proxy, err := proxy.New(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), pproxy, "tcp", "127.0.0.1:0", "")
if err != nil {
t.Fatal(err)
}
@@ -134,12 +125,12 @@
if err != nil {
t.Fatal(err)
}
- if got, want := fmt.Sprintf("%v", flow.RemoteID()), fmt.Sprintf("%v", proxyID.PublicID()); got != want {
- t.Errorf("Proxy has identity %q want %q", flow.RemoteID(), want)
+ if got, want := flow.RemoteBlessings(), pproxy.BlessingStore().Default(); !reflect.DeepEqual(got, want) {
+ t.Errorf("Proxy authenticated as [%v], want [%v]", got, want)
}
}
-func TestServerIdentity(t *testing.T) {
+func TestServerBlessings(t *testing.T) {
proxy, err := proxy.New(naming.FixedRoutingID(0xbbbbbbbbbbbbbbbb), nil, "tcp", "127.0.0.1:0", "")
if err != nil {
t.Fatal(err)
@@ -147,11 +138,8 @@
server := manager.InternalNew(naming.FixedRoutingID(0x5555555555555555))
defer server.Shutdown()
- serverID := newID("server")
- if err != nil {
- t.Fatal(err)
- }
- ln, ep, err := server.Listen(proxy.Endpoint().Network(), proxy.Endpoint().String(), vc.FixedLocalID(serverID))
+ pserver := sectest.NewPrincipal("server")
+ ln, ep, err := server.Listen(proxy.Endpoint().Network(), proxy.Endpoint().String(), vc.LocalPrincipal{pserver})
if err != nil {
t.Fatal(err)
}
@@ -174,8 +162,8 @@
if err != nil {
t.Fatal(err)
}
- if got, want := flow.RemoteID(), serverID.PublicID(); !reflect.DeepEqual(got, want) {
- t.Errorf("Got %q want %q", got, want)
+ if got, want := flow.RemoteBlessings(), pserver.BlessingStore().Default(); !reflect.DeepEqual(got, want) {
+ t.Errorf("Got [%v] want [%v]", got, want)
}
}
diff --git a/runtimes/google/ipc/stream/vc/auth.go b/runtimes/google/ipc/stream/vc/auth.go
index e3b06ec..5746a99 100644
--- a/runtimes/google/ipc/stream/vc/auth.go
+++ b/runtimes/google/ipc/stream/vc/auth.go
@@ -75,6 +75,7 @@
errChannelIDMismatch = errors.New("channel id does not match expectation")
errInvalidIdentityInMessage = errors.New("invalid identity in authentication message")
errInvalidSignatureInMessage = errors.New("signature does not verify in authentication handshake message")
+ errNoCertificatesReceived = errors.New("no certificates received")
errSingleCertificateRequired = errors.New("exactly one X.509 certificate chain with exactly one certificate is required")
)
@@ -316,6 +317,9 @@
if err != nil {
return nil, err
}
+ if b == nil {
+ return nil, errNoCertificatesReceived
+ }
if !sig.Verify(b.PublicKey(), append(tag, crypter.ChannelBinding()...)) {
return nil, errInvalidSignatureInMessage
}
diff --git a/runtimes/google/ipc/stream/vc/init.go b/runtimes/google/ipc/stream/vc/init.go
index 8ff0aa5..96c21bd 100644
--- a/runtimes/google/ipc/stream/vc/init.go
+++ b/runtimes/google/ipc/stream/vc/init.go
@@ -1,6 +1,11 @@
package vc
import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "fmt"
+
isecurity "veyron.io/veyron/veyron/runtimes/google/security"
"veyron.io/veyron/veyron2/security"
@@ -8,10 +13,53 @@
)
var anonymousID security.PrivateID
+var anonymousPrincipal security.Principal
func init() {
- var err error
- if anonymousID, err = isecurity.NewPrivateID("anonymous", nil); err != nil {
- vlog.Fatalf("could not create anonymousID for IPCs: %s", err)
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ vlog.Fatalf("could not create private key for anonymous principal: %v", err)
}
+ store := &anonymousBlessingStore{k: security.NewECDSAPublicKey(&key.PublicKey)}
+ if anonymousPrincipal, err = security.CreatePrincipal(security.NewInMemoryECDSASigner(key), store, nil); err != nil {
+ vlog.Fatalf("could not create anonymous principal: %v", err)
+ }
+ if store.b, err = anonymousPrincipal.BlessSelf("anonymous"); err != nil {
+ vlog.Fatalf("failed to generate the one blessing to be used by the anonymous principal: %v", err)
+ }
+ if anonymousID, err = isecurity.NewPrivateID("anonymous", nil); err != nil {
+ vlog.Fatalf("could not create anonymousID for IPCs: %v", err)
+ }
+}
+
+// TODO(ashankar,ataly): Figure out what to do with this!
+// (Most likely move the BlessingStore implementation from veyron/runtimes/google/rt to veyron/security
+// and use that?)
+type anonymousBlessingStore struct {
+ k security.PublicKey
+ b security.Blessings
+}
+
+func (s *anonymousBlessingStore) Set(security.Blessings, security.BlessingPattern) (security.Blessings, error) {
+ return nil, fmt.Errorf("cannot store blessings with an anonymous principal")
+}
+
+func (s *anonymousBlessingStore) ForPeer(...string) security.Blessings {
+ return s.b
+}
+
+func (s *anonymousBlessingStore) SetDefault(security.Blessings) error {
+ return fmt.Errorf("cannot change default blessing associated with the anonymous principal")
+}
+
+func (s *anonymousBlessingStore) Default() security.Blessings {
+ return s.b
+}
+
+func (s *anonymousBlessingStore) PublicKey() security.PublicKey {
+ return s.k
+}
+
+func (anonymousBlessingStore) DebugString() string {
+ return "anonymous BlessingStore"
}
diff --git a/runtimes/google/ipc/stream/vc/vc.go b/runtimes/google/ipc/stream/vc/vc.go
index f0b4ad9..0093009 100644
--- a/runtimes/google/ipc/stream/vc/vc.go
+++ b/runtimes/google/ipc/stream/vc/vc.go
@@ -397,11 +397,12 @@
}
switch securityLevel {
case veyron2.VCSecurityConfidential:
- // TODO(ashankar): This should change to:
- // if principal == nil { either return error or principal = newAnonymousPrincipal }
- if localID == nil && principal == nil {
+ if localID == nil {
localID = FixedLocalID(anonymousID)
}
+ if principal == nil {
+ principal = anonymousPrincipal
+ }
case veyron2.VCSecurityNone:
return nil
default:
@@ -437,7 +438,7 @@
rID, lID security.PublicID
rBlessings, lBlessings security.Blessings
)
- if principal == nil {
+ if vc.useOldSecurityModel() {
if rID, lID, err = authenticateAsClientOld(authConn, localID, crypter, vc.version); err != nil {
return vc.err(fmt.Errorf("authentication (using the deprecated PublicID objects) failed: %v", err))
}
@@ -451,14 +452,17 @@
vc.handshakeFID = handshakeFID
vc.authFID = authFID
vc.crypter = crypter
- vc.localID = lID
- vc.remoteID = rID
- vc.localPrincipal = principal
- vc.remoteBlessings = rBlessings
- vc.localBlessings = lBlessings
+ if vc.useOldSecurityModel() {
+ vc.localID = lID
+ vc.remoteID = rID
+ } else {
+ vc.localPrincipal = principal
+ vc.remoteBlessings = rBlessings
+ vc.localBlessings = lBlessings
+ }
vc.mu.Unlock()
- if principal != nil {
+ if !vc.useOldSecurityModel() {
vlog.VI(1).Infof("Client VC %v authenticated. RemoteBlessings:%v, LocalBlessings:%v", vc, rBlessings, lBlessings)
} else {
vlog.VI(1).Infof("Client VC %v authenticated using about-to-be-deleted protocol. RemoteID:%v LocalID:%v", vc, rID, lID)
@@ -511,6 +515,9 @@
if localID == nil {
localID = FixedLocalID(anonymousID)
}
+ if principal == nil {
+ principal = anonymousPrincipal
+ }
case veyron2.VCSecurityNone:
return finish(ln, nil)
default:
@@ -557,7 +564,7 @@
rID, lID security.PublicID
rBlessings, lBlessings security.Blessings
)
- if principal == nil {
+ if vc.useOldSecurityModel() {
if rID, lID, err = authenticateAsServerOld(authConn, localID, crypter, vc.version); err != nil {
sendErr(fmt.Errorf("Authentication failed (with soon-to-be-removed protocol): %v", err))
return
@@ -571,18 +578,21 @@
vc.mu.Lock()
vc.crypter = crypter
- vc.localID = lID
- vc.remoteID = rID
- vc.localPrincipal = principal
- vc.localBlessings = lBlessings
- vc.remoteBlessings = rBlessings
+ if vc.useOldSecurityModel() {
+ vc.localID = lID
+ vc.remoteID = rID
+ } else {
+ vc.localPrincipal = principal
+ vc.localBlessings = lBlessings
+ vc.remoteBlessings = rBlessings
+ }
close(vc.acceptHandshakeDone)
vc.acceptHandshakeDone = nil
vc.mu.Unlock()
- if principal == nil {
- vlog.VI(1).Infof("Server VC %v authenticated using about-to-be-deleted protocol. RemoteID:%v LocalID:%v", vc, rID, lID)
- } else {
+ if !vc.useOldSecurityModel() {
vlog.VI(1).Infof("Server VC %v authenticated. RemoteBlessings:%v, LocalBlessings:%v", vc, rBlessings, lBlessings)
+ } else {
+ vlog.VI(1).Infof("Server VC %v authenticated using about-to-be-deleted protocol. RemoteID:%v LocalID:%v", vc, rID, lID)
}
result <- HandshakeResult{ln, nil}
}()
@@ -671,7 +681,10 @@
vc.mu.Lock()
defer vc.mu.Unlock()
vc.waitForHandshakeLocked()
- return anonymousIfNilPublicID(vc.localID)
+ if vc.useOldSecurityModel() {
+ return anonymousIfNilPublicID(vc.localID)
+ }
+ return nil
}
// RemoteID returns the identity of the remote end of the VC.
@@ -679,7 +692,10 @@
vc.mu.Lock()
defer vc.mu.Unlock()
vc.waitForHandshakeLocked()
- return anonymousIfNilPublicID(vc.remoteID)
+ if vc.useOldSecurityModel() {
+ return anonymousIfNilPublicID(vc.remoteID)
+ }
+ return nil
}
// waitForHandshakeLocked blocks until an in-progress handshake (encryption
@@ -714,7 +730,9 @@
l = append(l, "Handshake not completed yet")
} else {
l = append(l, "Encryption: "+vc.crypter.String())
- l = append(l, fmt.Sprintf("LocalPrincipal:%v LocalBlessings:%v RemoteBlessings:%v", vc.localPrincipal.PublicKey(), vc.localBlessings, vc.remoteBlessings))
+ if vc.localPrincipal != nil {
+ l = append(l, fmt.Sprintf("LocalPrincipal:%v LocalBlessings:%v RemoteBlessings:%v", vc.localPrincipal.PublicKey(), vc.localBlessings, vc.remoteBlessings))
+ }
l = append(l, fmt.Sprintf("LocalID:%q RemoteID:%q", anonymousIfNilPublicID(vc.localID), anonymousIfNilPublicID(vc.remoteID)))
}
for fid, f := range vc.flowMap {
@@ -725,6 +743,9 @@
return strings.Join(l, "\n")
}
+// TODO(ashankar,ataly): Remove once the old security model is ripped out.
+func (vc *VC) useOldSecurityModel() bool { return vc.version < version.IPCVersion4 }
+
// readHandlerImpl is an adapter for the readHandler interface required by
// the reader type.
type readHandlerImpl struct {
diff --git a/runtimes/google/ipc/stream/vc/vc_test.go b/runtimes/google/ipc/stream/vc/vc_test.go
index e380374..c469fe6 100644
--- a/runtimes/google/ipc/stream/vc/vc_test.go
+++ b/runtimes/google/ipc/stream/vc/vc_test.go
@@ -35,7 +35,7 @@
SecurityNone = veyron2.VCSecurityNone
SecurityTLS = veyron2.VCSecurityConfidential
- LatestVersion = version.IPCVersion3
+ LatestVersion = version.IPCVersion4
)
// testFlowEcho writes a random string of 'size' bytes on the flow and then
@@ -100,8 +100,6 @@
if err != nil {
t.Fatal(err)
}
- // TODO(ashankar): This should not be nil, but rather a dummy object (whose public
- // key does not match that of the principal)
if flow.RemoteBlessings() != nil {
t.Errorf("Server sent blessing %v over insecure transport", flow.RemoteBlessings())
}
@@ -183,8 +181,8 @@
func TestConnect(t *testing.T) { testConnect(t, SecurityNone) }
func TestConnectTLS(t *testing.T) { testConnect(t, SecurityTLS) }
-func testConnect_Version2(t *testing.T, security veyron2.VCSecurityLevel) {
- h, vc := New(security, version.IPCVersion2, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"))
+func testConnect_Version3(t *testing.T, security veyron2.VCSecurityLevel) {
+ h, vc := New(security, version.IPCVersion3, sectest.NewPrincipal("client"), sectest.NewPrincipal("server"))
defer h.Close()
flow, err := vc.Connect()
if err != nil {
@@ -192,8 +190,8 @@
}
testFlowEcho(t, flow, 10)
}
-func TestConnect_Version2(t *testing.T) { testConnect_Version2(t, SecurityNone) }
-func TestConnect_Version2TLS(t *testing.T) { testConnect_Version2(t, SecurityTLS) }
+func TestConnect_Version3(t *testing.T) { testConnect_Version3(t, SecurityNone) }
+func TestConnect_Version3TLS(t *testing.T) { testConnect_Version3(t, SecurityTLS) }
// helper function for testing concurrent operations on multiple flows over the
// same VC. Such tests are most useful when running the race detector.
@@ -448,6 +446,8 @@
type endpoint naming.RoutingID
func (e endpoint) Network() string { return "test" }
+func (e endpoint) VersionedString(int) string { return e.String() }
func (e endpoint) String() string { return naming.RoutingID(e).String() }
func (e endpoint) RoutingID() naming.RoutingID { return naming.RoutingID(e) }
func (e endpoint) Addr() net.Addr { return nil }
+func (e endpoint) ServesMountTable() bool { return false }
diff --git a/runtimes/google/ipc/stream/vif/vif.go b/runtimes/google/ipc/stream/vif/vif.go
index 06d27bd..c30a543 100644
--- a/runtimes/google/ipc/stream/vif/vif.go
+++ b/runtimes/google/ipc/stream/vif/vif.go
@@ -177,10 +177,22 @@
return vif, nil
}
+func adjustIPCVersionForOldSecurityModel(in naming.Endpoint, opts []stream.VCOpt) naming.Endpoint {
+ out := in
+ for _, o := range opts {
+ if r, ok := o.(*version.Range); ok {
+ out = r.Endpoint(out.Addr().Network(), out.Addr().String(), out.RoutingID())
+ vlog.Infof("Adjusted Dialer endpoint from %v to %v for OpenVC message because the old security model is being used", in, out)
+ }
+ }
+ return out
+}
+
// Dial creates a new VC to the provided remote identity, authenticating the VC
// with the provided local identity.
func (vif *VIF) Dial(remoteEP naming.Endpoint, opts ...stream.VCOpt) (stream.VC, error) {
- vc, err := vif.newVC(vif.allocVCI(), vif.localEP, remoteEP, true)
+ localEP := adjustIPCVersionForOldSecurityModel(vif.localEP, opts)
+ vc, err := vif.newVC(vif.allocVCI(), localEP, remoteEP, true)
if err != nil {
return nil, err
}
@@ -189,7 +201,7 @@
err = vif.sendOnExpressQ(&message.OpenVC{
VCI: vc.VCI(),
DstEndpoint: remoteEP,
- SrcEndpoint: vif.localEP,
+ SrcEndpoint: localEP,
Counters: counters})
if err != nil {
err = fmt.Errorf("vif.sendOnExpressQ(OpenVC) failed: %v", err)
@@ -667,11 +679,10 @@
// ShutdownVCs closes all VCs established to the provided remote endpoint.
// Returns the number of VCs that were closed.
func (vif *VIF) ShutdownVCs(remote naming.Endpoint) int {
- remoteStr := remote.String()
vcs := vif.vcMap.List()
n := 0
for _, vc := range vcs {
- if vc.RemoteAddr().String() == remoteStr {
+ if naming.Compare(vc.RemoteAddr().RoutingID(), remote.RoutingID()) {
vlog.VI(1).Infof("VCI %d on VIF %s being closed because of ShutdownVCs call", vc.VCI(), vif)
vif.closeVCAndSendMsg(vc, "")
n++
diff --git a/runtimes/google/ipc/stream/vif/vif_test.go b/runtimes/google/ipc/stream/vif/vif_test.go
index afddf53..aec55c7 100644
--- a/runtimes/google/ipc/stream/vif/vif_test.go
+++ b/runtimes/google/ipc/stream/vif/vif_test.go
@@ -325,7 +325,7 @@
ep := tc.ep.Endpoint("test", "addr", naming.FixedRoutingID(0x5))
clientVC, _, err := createVC(client, server, ep)
if (err != nil) != tc.expectError {
- t.Errorf("Error mismatch. Wanted error: %v, got %v", tc.expectError, err)
+ t.Errorf("Error mismatch. Wanted error: %v, got %v (client:%v, server:%v ep:%v)", tc.expectError, err, tc.client, tc.server, tc.ep)
}
if err != nil {
diff --git a/runtimes/google/ipc/version/version.go b/runtimes/google/ipc/version/version.go
index ca86911..33d251f 100644
--- a/runtimes/google/ipc/version/version.go
+++ b/runtimes/google/ipc/version/version.go
@@ -14,6 +14,10 @@
Min, Max version.IPCVersion
}
+// TODO(ashankar): Remove when the transition to the new security API is complete.
+func (*Range) IPCClientOpt() {}
+func (*Range) IPCStreamVCOpt() {}
+
var (
// supportedRange represents the range of protocol verions supported by this
// implementation.
@@ -21,7 +25,7 @@
// change that's not both forward and backward compatible.
// Min should be incremented whenever we want to remove
// support for old protocol versions.
- supportedRange = &Range{Min: version.IPCVersion2, Max: version.IPCVersion3}
+ supportedRange = &Range{Min: version.IPCVersion2, Max: version.IPCVersion4}
// Export the methods on supportedRange.
Endpoint = supportedRange.Endpoint
diff --git a/runtimes/google/naming/endpoint.go b/runtimes/google/naming/endpoint.go
index a69ffbe..e2fefd9 100644
--- a/runtimes/google/naming/endpoint.go
+++ b/runtimes/google/naming/endpoint.go
@@ -29,10 +29,11 @@
RID naming.RoutingID
MinIPCVersion version.IPCVersion
MaxIPCVersion version.IPCVersion
+ IsMountTable bool
}
// NewEndpoint creates a new endpoint from a string as per naming.NewEndpoint
-func NewEndpoint(input string) (naming.Endpoint, error) {
+func NewEndpoint(input string) (*Endpoint, error) {
var ep Endpoint
// The prefix and suffix are optional.
@@ -54,6 +55,8 @@
err = ep.parseV1(parts)
case 2:
err = ep.parseV2(parts)
+ case 3:
+ err = ep.parseV3(parts)
default:
err = errInvalidEndpointString
}
@@ -112,6 +115,20 @@
return strconv.FormatUint(uint64(v), 10)
}
+func parseMountTableFlag(input string) (bool, error) {
+ if len(input) == 1 {
+ switch f := input[0]; f {
+ case 'm':
+ return true, nil
+ case 's':
+ return false, nil
+ default:
+ return false, fmt.Errorf("%c is not one of 'm' or 's'", f)
+ }
+ }
+ return false, fmt.Errorf("flag is either missing or too long")
+}
+
func (ep *Endpoint) parseV2(parts []string) error {
var err error
if len(parts) != 6 {
@@ -129,6 +146,20 @@
return nil
}
+func (ep *Endpoint) parseV3(parts []string) error {
+ var err error
+ if len(parts) != 7 {
+ return errInvalidEndpointString
+ }
+ if err = ep.parseV2(parts[:6]); err != nil {
+ return err
+ }
+ if ep.IsMountTable, err = parseMountTableFlag(parts[6]); err != nil {
+ return fmt.Errorf("invalid mount table flag: %v", err)
+ }
+ return nil
+}
+
func (ep *Endpoint) RoutingID() naming.RoutingID {
//nologcall
return ep.RID
@@ -137,19 +168,46 @@
//nologcall
return Network
}
+
+var defaultVersion = 2
+
+func (ep *Endpoint) VersionedString(version int) string {
+ switch version {
+ default:
+ return ep.VersionedString(defaultVersion)
+ case 1:
+ return fmt.Sprintf("@1@%s@%s@@", ep.Protocol, ep.Address)
+ case 2:
+ return fmt.Sprintf("@2@%s@%s@%s@%s@%s@@",
+ ep.Protocol, ep.Address, ep.RID,
+ printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion))
+ case 3:
+ mt := "s"
+ if ep.IsMountTable {
+ mt = "m"
+ }
+ return fmt.Sprintf("@3@%s@%s@%s@%s@%s@%s@@",
+ ep.Protocol, ep.Address, ep.RID,
+ printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion),
+ mt)
+ }
+}
+
func (ep *Endpoint) String() string {
//nologcall
- return fmt.Sprintf("%s2@%s@%s@%s@%s@%s@@",
- separator, ep.Protocol, ep.Address, ep.RID,
- printIPCVersion(ep.MinIPCVersion), printIPCVersion(ep.MaxIPCVersion))
+ return ep.VersionedString(defaultVersion)
}
-func (ep *Endpoint) version() int { return 2 }
func (ep *Endpoint) Addr() net.Addr {
//nologcall
return &addr{network: ep.Protocol, address: ep.Address}
}
+func (ep *Endpoint) ServesMountTable() bool {
+ //nologcall
+ return true
+}
+
type addr struct {
network, address string
}
diff --git a/runtimes/google/naming/endpoint_test.go b/runtimes/google/naming/endpoint_test.go
index f0d036b..00135c5 100644
--- a/runtimes/google/naming/endpoint_test.go
+++ b/runtimes/google/naming/endpoint_test.go
@@ -29,6 +29,14 @@
MinIPCVersion: 2,
MaxIPCVersion: 3,
}
+ v3 := &Endpoint{
+ Protocol: "tcp",
+ Address: "batman.com:2345",
+ RID: naming.FixedRoutingID(0x0),
+ MinIPCVersion: 2,
+ MaxIPCVersion: 3,
+ IsMountTable: true,
+ }
testcasesA := []struct {
endpoint naming.Endpoint
@@ -53,10 +61,13 @@
String string
Input string
min, max version.IPCVersion
+ servesMT bool
}{
- {v1, "@2@tcp@batman.com:1234@000000000000000000000000dabbad00@@@@", "", version.UnknownIPCVersion, version.UnknownIPCVersion},
- {v2, "@2@tcp@batman.com:2345@000000000000000000000000dabbad00@1@10@@", "", 1, 10},
- {v2hp, "@2@tcp@batman.com:2345@00000000000000000000000000000000@2@3@@", "batman.com:2345", 2, 3},
+ {v1, "@2@tcp@batman.com:1234@000000000000000000000000dabbad00@@@@", "", version.UnknownIPCVersion, version.UnknownIPCVersion, false},
+ {v2, "@2@tcp@batman.com:2345@000000000000000000000000dabbad00@1@10@@", "", 1, 10, false},
+ {v2hp, "@2@tcp@batman.com:2345@00000000000000000000000000000000@2@3@@", "batman.com:2345", 2, 3, false},
+ // the v3 format is ignored unless explicitly enabled.
+ {v3, "@2@tcp@batman.com:2345@00000000000000000000000000000000@2@3@@", "batman.com:2345", 2, 3, true},
}
for _, test := range testcasesB {
@@ -71,7 +82,8 @@
ep, err = NewEndpoint(str)
} else {
ep, err = NewEndpoint(naming.FormatEndpoint("tcp", str,
- version.IPCVersionRange{test.min, test.max}))
+ version.IPCVersionRange{test.min, test.max},
+ naming.ServesMountTableOpt(test.servesMT)))
}
if err != nil {
t.Errorf("Endpoint(%q) failed with %v", str, err)
@@ -81,6 +93,25 @@
t.Errorf("Got endpoint %T = %#v, want %T = %#v for string %q", ep, ep, test.Endpoint, test.Endpoint, str)
}
}
+
+ // Enable V3 endpoints.
+ defaultVersion = 3
+ for _, c := range []struct {
+ servesMT bool
+ str string
+ }{
+ {true, "@3@tcp@a:10@00000000000000000000000000000000@1@3@m@@"},
+ {false, "@3@tcp@a:10@00000000000000000000000000000000@1@3@s@@"},
+ } {
+ ep, _ := NewEndpoint(naming.FormatEndpoint("tcp", "a:10",
+ version.IPCVersionRange{1, 3},
+ naming.ServesMountTableOpt(c.servesMT)))
+ if got, want := ep.String(), c.str; got != want {
+ t.Errorf("got: %s, want: %s", got, want)
+ }
+ }
+ // Disable V3 endpoints.
+ defaultVersion = 2
}
type endpointTest struct {
diff --git a/runtimes/google/rt/ipc.go b/runtimes/google/rt/ipc.go
index 093ab53..0e1281a 100644
--- a/runtimes/google/rt/ipc.go
+++ b/runtimes/google/rt/ipc.go
@@ -7,14 +7,18 @@
iipc "veyron.io/veyron/veyron/runtimes/google/ipc"
imanager "veyron.io/veyron/veyron/runtimes/google/ipc/stream/manager"
"veyron.io/veyron/veyron/runtimes/google/ipc/stream/vc"
+ iversion "veyron.io/veyron/veyron/runtimes/google/ipc/version"
ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/context"
+ "veyron.io/veyron/veyron2/i18n"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/ipc/stream"
+ "veyron.io/veyron/veyron2/ipc/version"
"veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vtrace"
)
@@ -100,8 +104,10 @@
}
}
// Add the option that provides the local identity to the client.
- otherOpts = append(otherOpts, rt.newLocalID(id))
-
+ otherOpts = append(otherOpts, rt.newLocalID(id), vc.LocalPrincipal{rt.principal})
+ if !rt.useNewSecurityModelInIPCClients {
+ otherOpts = append(otherOpts, &iversion.Range{Min: version.IPCVersion2, Max: version.IPCVersion3})
+ }
return iipc.InternalNewClient(sm, ns, otherOpts...)
}
@@ -110,7 +116,10 @@
}
func (rt *vrt) NewContext() context.T {
- ctx, _ := ivtrace.WithNewSpan(iipc.InternalNewContext(rt), "Root")
+ ctx := iipc.InternalNewContext(rt)
+ ctx = i18n.ContextWithLangID(ctx, rt.lang)
+ ctx = verror2.ContextWithComponentName(ctx, rt.program)
+ ctx, _ = ivtrace.WithNewSpan(ctx, "Root")
return ctx
}
@@ -160,7 +169,7 @@
}
}
// Add the option that provides the local identity to the server.
- otherOpts = append(otherOpts, rt.newLocalID(id))
+ otherOpts = append(otherOpts, rt.newLocalID(id), vc.LocalPrincipal{rt.principal})
ctx := rt.NewContext()
return iipc.InternalNewServer(ctx, sm, ns, otherOpts...)
diff --git a/runtimes/google/rt/ipc_test.go b/runtimes/google/rt/ipc_test.go
index 3f97b45..91492a5 100644
--- a/runtimes/google/rt/ipc_test.go
+++ b/runtimes/google/rt/ipc_test.go
@@ -1,11 +1,8 @@
package rt_test
import (
- "fmt"
"reflect"
- "sort"
"testing"
- "time"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc"
@@ -15,182 +12,149 @@
_ "veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/profiles"
- isecurity "veyron.io/veyron/veyron/runtimes/google/security"
vsecurity "veyron.io/veyron/veyron/security"
)
type testService struct{}
-func (*testService) EchoIDs(call ipc.ServerCall) (server, client []string) {
- return call.LocalID().Names(), call.RemoteID().Names()
+func (testService) EchoBlessings(call ipc.ServerCall) []string {
+ return call.RemoteBlessings().ForContext(call)
}
-type S []string
-
-func newID(name string) security.PrivateID {
- id, err := isecurity.NewPrivateID(name, nil)
+func newRT() veyron2.Runtime {
+ r, err := rt.New(veyron2.ForceNewSecurityModel{})
if err != nil {
panic(err)
}
- return id
+ return r
}
-func bless(blessor security.PrivateID, blessee security.PublicID, name string) security.PublicID {
- blessedID, err := blessor.Bless(blessee, name, 5*time.Minute, nil)
+type rootPrincipal struct {
+ p security.Principal
+ b security.Blessings
+}
+
+func newRootPrincipal(name string) *rootPrincipal {
+ p, err := vsecurity.NewPrincipal()
if err != nil {
panic(err)
}
- return blessedID
-}
-
-func add(store security.PublicIDStore, id security.PublicID, pattern security.BlessingPattern) {
- if err := store.Add(id, pattern); err != nil {
+ b, err := p.BlessSelf(name)
+ if err != nil {
panic(err)
}
+ return &rootPrincipal{p, b}
}
-func call(r veyron2.Runtime, client ipc.Client, name string) (clientNames, serverNames []string, err error) {
- c, err := client.StartCall(r.NewContext(), name, "EchoIDs", nil)
+func (r *rootPrincipal) Bless(p security.Principal, extension string) security.Blessings {
+ b, err := r.p.Bless(p.PublicKey(), r.b, extension, security.UnconstrainedUse())
if err != nil {
- return nil, nil, err
+ panic(err)
}
- if err := c.Finish(&serverNames, &clientNames); err != nil {
- return nil, nil, err
- }
- sort.Strings(clientNames)
- sort.Strings(serverNames)
- return
+ return b
}
-func TestClientServerIDs(t *testing.T) {
- stopServer := func(server ipc.Server) {
- if err := server.Stop(); err != nil {
- t.Fatalf("server.Stop failed: %s", err)
+func union(blessings ...security.Blessings) security.Blessings {
+ var ret security.Blessings
+ var err error
+ for _, b := range blessings {
+ if ret, err = security.UnionOfBlessings(ret, b); err != nil {
+ panic(err)
}
}
+ return ret
+}
+
+func TestClientServerBlessings(t *testing.T) {
var (
- self = newID("self")
- google = newID("google")
- veyron = newID("veyron")
+ rootAlpha, rootBeta, rootUnrecognized = newRootPrincipal("alpha"), newRootPrincipal("beta"), newRootPrincipal("unrecognized")
+ clientRT, serverRT = newRT(), newRT()
+ pclient, pserver = clientRT.Principal(), serverRT.Principal()
- googleGmailService = bless(google, self.PublicID(), "gmail")
- googleYoutubeService = bless(google, self.PublicID(), "youtube")
- veyronService = bless(veyron, self.PublicID(), "service")
- googleGmailClient = bless(google, self.PublicID(), "gmailClient")
- googleYoutubeClient = bless(google, self.PublicID(), "youtubeClient")
- veyronClient = bless(veyron, self.PublicID(), "client")
+ // A bunch of blessings
+ alphaClient = rootAlpha.Bless(pclient, "client")
+ betaClient = rootBeta.Bless(pclient, "client")
+ unrecognizedClient = rootUnrecognized.Bless(pclient, "client")
+
+ alphaServer = rootAlpha.Bless(pserver, "server")
+ betaServer = rootBeta.Bless(pserver, "server")
+ unrecognizedServer = rootUnrecognized.Bless(pserver, "server")
)
- isecurity.TrustIdentityProviders(google)
- isecurity.TrustIdentityProviders(veyron)
+ // Setup the client's blessing store
+ pclient.BlessingStore().Set(alphaClient, "alpha/server")
+ pclient.BlessingStore().Set(betaClient, "beta/...")
+ pclient.BlessingStore().Set(unrecognizedClient, security.AllPrincipals)
- serverR, err := rt.New(veyron2.RuntimeID(self))
+ tests := []struct {
+ server security.Blessings // Blessings presented by the server.
+
+ // Expected output
+ wantServer []string // Client's view of the server's blessings
+ wantClient []string // Server's view fo the client's blessings
+ }{
+ {
+ server: unrecognizedServer,
+ wantServer: nil,
+ wantClient: nil,
+ },
+ {
+ server: alphaServer,
+ wantServer: []string{"alpha/server"},
+ wantClient: []string{"alpha/client"},
+ },
+ {
+ server: union(alphaServer, betaServer),
+ wantServer: []string{"alpha/server", "beta/server"},
+ wantClient: []string{"alpha/client", "beta/client"},
+ },
+ }
+
+ // Have the client and server both trust both the root principals.
+ for _, rt := range []veyron2.Runtime{clientRT, serverRT} {
+ for _, root := range []*rootPrincipal{rootAlpha, rootBeta} {
+ if err := rt.Principal().AddToRoots(root.b); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+ // Start the server process.
+ server, err := serverRT.NewServer()
if err != nil {
- t.Fatalf("rt.New() failed: %s", err)
+ t.Fatal(err)
}
- clientR, err := rt.New(veyron2.RuntimeID(self))
- if err != nil {
- t.Fatalf("rt.New() failed: %s", err)
+ defer server.Stop()
+ var serverObjectName string
+ if endpoint, err := server.ListenX(profiles.LocalListenSpec); err != nil {
+ t.Fatal(err)
+ } else {
+ serverObjectName = naming.JoinAddressName(endpoint.String(), "")
}
-
- // Add PublicIDs for running "google/gmail" and "google/youtube" services to
- // serverR's PublicIDStore. Since these PublicIDs are meant to be by
- // servers only they are tagged with "".
- add(serverR.PublicIDStore(), googleGmailService, "")
- add(serverR.PublicIDStore(), googleYoutubeService, "")
- // Add PublicIDs for communicating the "google/gmail" and "google/youtube" services
- // to the clientR's PublicIDStore.
- add(clientR.PublicIDStore(), googleGmailClient, "google/...")
- add(clientR.PublicIDStore(), googleYoutubeClient, "google/youtube")
-
- type testcase struct {
- server, client security.PublicID
- defaultPattern security.BlessingPattern
- wantServerNames, wantClientNames []string
+ if err := server.Serve("", ipc.LeafDispatcher(testService{}, vsecurity.NewACLAuthorizer(vsecurity.OpenACL()))); err != nil {
+ t.Fatal(err)
}
- tests := []testcase{
- {
- defaultPattern: security.AllPrincipals,
- wantServerNames: S{"self", "google/gmail", "google/youtube"},
- wantClientNames: S{"self", "google/gmailClient", "google/youtubeClient"},
- },
- {
- defaultPattern: "google/gmail",
- wantServerNames: S{"google/gmail"},
- wantClientNames: S{"self", "google/gmailClient"},
- },
- {
- defaultPattern: "google/youtube",
- wantServerNames: S{"google/youtube"},
- wantClientNames: S{"self", "google/gmailClient", "google/youtubeClient"},
- },
- {
- server: veyronService,
- defaultPattern: security.AllPrincipals,
- wantServerNames: S{"veyron/service"},
- wantClientNames: S{"self"},
- },
- {
- client: veyronClient,
- defaultPattern: security.AllPrincipals,
- wantServerNames: S{"self", "google/gmail", "google/youtube"},
- wantClientNames: S{"veyron/client"},
- },
- {
- server: veyronService,
- client: veyronClient,
- defaultPattern: security.AllPrincipals,
- wantServerNames: S{"veyron/service"},
- wantClientNames: S{"veyron/client"},
- },
- }
- name := func(t testcase) string {
- return fmt.Sprintf("TestCase{clientPublicIDStore: %v, serverPublicIDStore: %v, client option: %v, server option: %v}", clientR.PublicIDStore(), serverR.PublicIDStore(), t.client, t.server)
- }
+ // Let it rip!
for _, test := range tests {
- if err := serverR.PublicIDStore().SetDefaultBlessingPattern(test.defaultPattern); err != nil {
- t.Errorf("serverR.PublicIDStore.SetDefaultBlessingPattern failed: %s", err)
- continue
- }
- server, err := serverR.NewServer(veyron2.LocalID(test.server))
+ // Create a new client per test so as to not re-use established authenticated VCs.
+ // TODO(ashankar,suharshs): Once blessings are exchanged "per-RPC", one client for all cases will suffice.
+ client, err := clientRT.NewClient()
if err != nil {
- t.Errorf("serverR.NewServer(...) failed: %s", err)
+ t.Errorf("clientRT.NewClient failed: %v", err)
continue
}
- endpoint, err := server.ListenX(profiles.LocalListenSpec)
- if err != nil {
- t.Errorf("error listening to service: ", err)
+ if err := pserver.BlessingStore().SetDefault(test.server); err != nil {
+ t.Errorf("pserver.SetDefault(%v) failed: %v", test.server, err)
continue
}
- defer stopServer(server)
- if err := server.Serve("", ipc.LeafDispatcher(&testService{},
- vsecurity.NewACLAuthorizer(security.ACL{In: map[security.BlessingPattern]security.LabelSet{
- security.AllPrincipals: security.AllLabels,
- }}))); err != nil {
- t.Errorf("error serving service: ", err)
- continue
+ var gotClient []string
+ if call, err := client.StartCall(clientRT.NewContext(), serverObjectName, "EchoBlessings", nil); err != nil {
+ t.Errorf("client.StartCall failed: %v", err)
+ } else if err = call.Finish(&gotClient); err != nil {
+ t.Errorf("call.Finish failed: %v", err)
+ } else if !reflect.DeepEqual(gotClient, test.wantClient) {
+ t.Errorf("%v: Got %v, want %v for client blessings", test.server, gotClient, test.wantServer)
+ } else if gotServer, _ := call.RemoteBlessings(); !reflect.DeepEqual(gotServer, test.wantServer) {
+ t.Errorf("%v: Got %v, want %v for server blessings", test.server, gotServer, test.wantClient)
}
-
- client, err := clientR.NewClient(veyron2.LocalID(test.client))
- if err != nil {
- t.Errorf("clientR.NewClient(...) failed: %s", err)
- continue
- }
- defer client.Close()
-
- clientNames, serverNames, err := call(clientR, client, naming.JoinAddressName(fmt.Sprintf("%v", endpoint), ""))
- if err != nil {
- t.Errorf("IPC failed: %s", err)
- continue
- }
- sort.Strings(test.wantClientNames)
- sort.Strings(test.wantServerNames)
- if !reflect.DeepEqual(clientNames, test.wantClientNames) {
- t.Errorf("TestCase: %s, Got clientNames: %v, want: %v", name(test), clientNames, test.wantClientNames)
- continue
- }
- if !reflect.DeepEqual(serverNames, test.wantServerNames) {
- t.Errorf("TestCase: %s, Got serverNames: %v, want: %v", name(test), serverNames, test.wantServerNames)
- continue
- }
+ client.Close()
}
}
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index df084f2..e15eaa2 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -263,12 +263,11 @@
// refer to the global rt.R() function), but we take care to make sure
// that the "google" runtime we are trying to test in this package is
// the one being used.
- r, _ := rt.New(veyron2.RuntimeOpt{veyron2.GoogleRuntimeName})
+ r, _ := rt.New(veyron2.RuntimeOpt{veyron2.GoogleRuntimeName}, veyron2.ForceNewSecurityModel{})
c := blackbox.HelperCommand(t, "app")
- id := r.Identity()
- idFile := security.SaveIdentityToFile(security.NewBlessedIdentity(id, "test"))
+ childcreds := security.NewVeyronCredentials(r.Principal(), "app")
configServer, configServiceName, ch := createConfigServer(t, r)
- c.Cmd.Env = append(c.Cmd.Env, fmt.Sprintf("VEYRON_IDENTITY=%v", idFile),
+ c.Cmd.Env = append(c.Cmd.Env, fmt.Sprintf("VEYRON_CREDENTIALS=%v", childcreds),
fmt.Sprintf("%v=%v", mgmt.ParentNodeManagerConfigKey, configServiceName))
c.Cmd.Start()
appCycleName := <-ch
@@ -279,7 +278,7 @@
return r, c, appCycle, func() {
configServer.Stop()
c.Cleanup()
- os.Remove(idFile)
+ os.RemoveAll(childcreds)
// Don't do r.Cleanup() since the runtime needs to be used by
// more than one test case.
}
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index fa8ab10..3692c37 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -4,11 +4,13 @@
"flag"
"fmt"
"os"
+ "path/filepath"
"strings"
"sync"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/config"
+ "veyron.io/veyron/veyron2/i18n"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/ipc/stream"
"veyron.io/veyron/veyron2/naming"
@@ -39,13 +41,20 @@
debug debugServer
nServers int // GUARDED_BY(mu)
cleaningUp bool // GUARDED_BY(mu)
+
+ // TODO(ashankar,ataly): Variables to help with the transition between the
+ // old and new security model. Will be removed once the transition is complete.
+ useNewSecurityModelInIPCClients bool
+
+ lang i18n.LangID // Language, from environment variables.
+ program string // Program name, from os.Args[0].
}
var _ veyron2.Runtime = (*vrt)(nil)
// Implements veyron2/rt.New
func New(opts ...veyron2.ROpt) (veyron2.Runtime, error) {
- rt := &vrt{mgmt: new(mgmtImpl)}
+ rt := &vrt{mgmt: new(mgmtImpl), lang: i18n.LangIDFromEnv(), program: filepath.Base(os.Args[0])}
flag.Parse()
rt.initHTTPDebugServer()
nsRoots := []string{}
@@ -65,6 +74,8 @@
if v.Name != "google" && v.Name != "" {
return nil, fmt.Errorf("%q is the wrong name for this runtime", v.Name)
}
+ case veyron2.ForceNewSecurityModel:
+ rt.useNewSecurityModelInIPCClients = true
default:
return nil, fmt.Errorf("option has wrong type %T", o)
}
diff --git a/runtimes/google/rt/sectransition/sectransition.go b/runtimes/google/rt/sectransition/sectransition.go
new file mode 100644
index 0000000..1ac2734
--- /dev/null
+++ b/runtimes/google/rt/sectransition/sectransition.go
@@ -0,0 +1,72 @@
+// This package provides a shell test during the security model transition.
+package main
+
+import (
+ "flag"
+ "fmt"
+ "time"
+
+ "veyron.io/veyron/veyron/lib/signals"
+
+ "veyron.io/veyron/veyron2/ipc"
+ "veyron.io/veyron/veyron2/naming"
+ "veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/vlog"
+)
+
+var runServer = flag.Bool("server", false, "If true, start a server. If false, start a client")
+
+type service struct{}
+
+func (service) Ping(call ipc.ServerCall) (string, error) {
+ return fmt.Sprintf("ClientBlessings: %v\nClientPublicID: %v", call.RemoteBlessings(), call.RemoteID()), nil
+}
+
+type authorizer struct{}
+
+func (authorizer) Authorize(security.Context) error { return nil }
+
+func main() {
+ r := rt.Init()
+ defer r.Cleanup()
+
+ if *runServer {
+ startServer(r.NewServer())
+ } else if len(flag.Args()) != 1 {
+ vlog.Fatalf("Expected exactly 1 argument, got %d (%v)", len(flag.Args()), flag.Args())
+ } else {
+ ctx, _ := r.NewContext().WithDeadline(time.Now().Add(10 * time.Second))
+ startClient(r.Client().StartCall(ctx, flag.Arg(0), "Ping", nil))
+ }
+}
+
+func startServer(server ipc.Server, err error) {
+ if err != nil {
+ vlog.Fatal(err)
+ }
+ defer server.Stop()
+
+ ep, err := server.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ vlog.Fatal(err)
+ }
+ fmt.Println("SERVER:", naming.JoinAddressName(ep.String(), ""))
+ server.Serve("", ipc.LeafDispatcher(service{}, authorizer{}))
+ <-signals.ShutdownOnSignals()
+}
+
+func startClient(call ipc.Call, err error) {
+ if err != nil {
+ vlog.Fatal(err)
+ }
+ var result string
+ var apperr error
+ if err = call.Finish(&result, &apperr); err != nil {
+ vlog.Fatalf("ipc.Call.Finish error: %v", err)
+ }
+ if apperr != nil {
+ vlog.Fatalf("Application error: %v", apperr)
+ }
+ fmt.Println(result)
+}
diff --git a/runtimes/google/rt/sectransition/test.sh b/runtimes/google/rt/sectransition/test.sh
new file mode 100755
index 0000000..d142e93
--- /dev/null
+++ b/runtimes/google/rt/sectransition/test.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+
+# Test compatibility of clients and servers using a combination of the old
+# and new security models (triggered by environment variables).
+
+. "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
+
+readonly WORKDIR=$(shell::tmp_dir)
+set +e
+
+build() {
+ veyron go build veyron.io/veyron/veyron/runtimes/google/rt/sectransition || shell_test::fail "line ${LINENO}: failed to build sectransition binary"
+ veyron go build veyron.io/veyron/veyron/tools/identity || shell_test::fail "line ${LINENO}: failed to build identity"
+}
+
+
+startserver() {
+ # The server has access to both the old and new security model.
+ export VEYRON_IDENTITY="${WORKDIR}/old"
+ export VEYRON_CREDENTIALS="${WORKDIR}/new"
+ ./sectransition --server --logtostderr >"${SERVERLOG}" 2>&1 &
+ shell::wait_for "${SERVERLOG}" "SERVER"
+ local EP=$(grep "SERVER: " "${SERVERLOG}" | sed -e 's/SERVER: //')
+ echo "${EP}"
+}
+
+runclient() {
+ ./sectransition "${EP}" >"${CLIENTLOG}" 2>&1
+}
+
+oldmodel() {
+ awk '/ClientPublicID:/ {print $2}' "${CLIENTLOG}"
+}
+
+newmodel() {
+ awk '/ClientBlessings:/ {print $2}' "${CLIENTLOG}"
+}
+
+main() {
+ cd "${WORKDIR}"
+ build
+
+ # Generate an identity (old security model) that may be used by the client.
+ local -r OLD="${WORKDIR}/old"
+ ./identity generate "old" > "${OLD}"
+
+ local -r SERVERLOG="${WORKDIR}/server.log"
+ local -r CLIENTLOG="${WORKDIR}/client.log"
+ local -r EP=$(startserver)
+
+ # No environment variables set: PublicIDs from the old model should be exchanged.
+ unset VEYRON_IDENTITY
+ unset VEYRON_CREDENTIALS
+ runclient
+ echo " No environment variables: PublicID:$(oldmodel), Blessings:$(newmodel)"
+ if [[ $(oldmodel) == "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: PublicID not set when neither environment variable is set"
+ fi
+ if [[ $(newmodel) != "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: Blessings should not be set when neither environment variable is set (was $(newmodel))"
+ fi
+
+
+ # Old model envvar is set: not the new one: PublicIDs from the old model should be exchanged.
+ export VEYRON_IDENTITY="${WORKDIR}/old"
+ unset VEYRON_CREDENTIALS
+ runclient
+ echo " VEYRON_IDENTITY: PublicID:$(oldmodel), Blessings:$(newmodel)"
+ if [[ $(oldmodel) == "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: PublicID not set when only VEYRON_IDENTITY is set"
+ fi
+ if [[ $(newmodel) != "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: Blessings should not be set when only VEYRON_IDENTITY is set (was $(newmodel))"
+ fi
+
+ # New model envvar is set: Blessings should be exchanged.
+ unset VEYRON_IDENTITY
+ export VEYRON_CREDENTIALS="${WORKDIR}/new"
+ runclient
+ echo " VEYRON_CREDENTIALS: PublicID:$(oldmodel), Blessings:$(newmodel)"
+ if [[ $(oldmodel) != "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: PublicID should not be exchanged when VEYRON_CREDENTIALS is set (was $(oldmodel))"
+ fi
+ if [[ $(newmodel) == "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: Blessings should be exchanged when VEYRON_CREDENTIALS is set (was $(newmodel))"
+ fi
+
+ # Both environment variables are set: Blessings should be exchanged.
+ export VEYRON_IDENTITY="${WORKDIR}/old"
+ export VEYRON_CREDENTIALS="${WORKDIR}/new"
+ runclient
+ echo "VEYRON_IDENTITY & VEYRON_CREDENTIALS: PublicID:$(oldmodel), Blessings:$(newmodel)"
+ if [[ $(oldmodel) != "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: PublicID should not be exchanged when VEYRON_CREDENTIALS is set (was $(oldmodel))"
+ fi
+ if [[ $(newmodel) == "<nil>" ]]; then
+ shell_test::fail "line ${LINENO}: Blessings should be exchanged when VEYRON_CREDENTIALS is set (was $(newmodel))"
+ fi
+
+ shell_test::pass
+}
+
+main "$@"
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index 0370941..f034ce7 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -36,6 +36,9 @@
}
func (rt *vrt) initSecurity() error {
+ // Use the new security model in ipc.Client only if it was expicitly specified.
+ // At a later date, we will switch to using the new model always.
+ rt.useNewSecurityModelInIPCClients = rt.useNewSecurityModelInIPCClients || len(os.Getenv(VeyronCredentialsEnvVar)) > 0
if err := rt.initOldSecurity(); err != nil {
return err
}
diff --git a/runtimes/google/security/identity_test.go b/runtimes/google/security/identity_test.go
index d459e73..0802218 100644
--- a/runtimes/google/security/identity_test.go
+++ b/runtimes/google/security/identity_test.go
@@ -338,8 +338,6 @@
// Caveats
// Can only call "Play" at the Google service
cavOnlyPlay = mkCaveat(security.MethodCaveat("Play"))
- // Can only talk to the "Google" service
- cavOnlyGoogle = mkCaveat(security.PeerBlessingsCaveat("google"))
)
// We create a Caveat from the CaveatValidator "unregisteredCaveat".
@@ -383,6 +381,7 @@
{server: googleChain.PublicID(), method: "Hello", authErr: `security.methodCaveat=[Play] fails validation for method "Hello"`},
},
},
+ /* TODO(ashankar): Test case disabled since PeerBlessingsCaveat is disabled.
{
client: bless(cAlice, veyronChain, "alice", cavOnlyGoogle),
tests: []rpc{
@@ -391,6 +390,7 @@
{server: googleChain.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
},
},
+ */
{
client: bless(cAlice, veyronChain, "alice", cavUnregistered),
tests: []rpc{
@@ -400,6 +400,7 @@
{server: googleChain.PublicID(), method: "Hello", authErr: "caveat bytes could not be VOM-decoded"},
},
},
+ /* TODO(ashankar): Test case disabled since PeerBlessingsCaveat is disabled.
{
client: bless(cAlice, veyronChain, "alice", cavOnlyGoogle, cavOnlyPlay),
tests: []rpc{
@@ -409,13 +410,15 @@
{server: googleChain.PublicID(), method: "Play", authNames: S{"veyron/alice"}},
},
},
+ */
{
- client: bless(cAlice, veyronChain, "alice", cavOnlyGoogle, cavUnregistered),
+ client: bless(cAlice, veyronChain, "alice", cavUnregistered),
tests: []rpc{
{server: googleChain.PublicID(), method: "Play", authErr: "caveat bytes could not be VOM-decoded"},
{server: googleChain.PublicID(), method: "Hello", authErr: "caveat bytes could not be VOM-decoded"},
},
},
+ /* TODO(ashankar): Test case disabled since PeerBlessingsCaveat is disabled.
// client has multiple blessings
{
client: newSetPublicID(bless(cAlice, veyronChain, "valice", cavOnlyPlay, cavUnregistered), bless(cAlice, googleChain, "galice", cavOnlyGoogle)),
@@ -426,6 +429,7 @@
{server: googleChain.PublicID(), method: "Play", authNames: S{"google/galice"}},
},
},
+ */
}
for _, d := range testdata {
// Validate that the client identity (with all its blessings) is valid for wire transmission.
@@ -496,6 +500,11 @@
}
}
+/*
+TODO(ashankar): Test disabled because PeerBlessingCaveat has been disabled.
+In any case, we expect this whole package to be deleted (it is the "old" security
+model), so won't bother with trying to fix up the tests
+
func TestAuthorizeWithThirdPartyCaveats(t *testing.T) {
mkveyron := func(id security.PrivateID, name string) security.PrivateID {
return derive(bless(id.PublicID(), veyronChain, name), id)
@@ -619,7 +628,7 @@
}
}
}
-
+*/
type SortedThirdPartyCaveats []security.ThirdPartyCaveat
func (s SortedThirdPartyCaveats) Len() int { return len(s) }
diff --git a/security/acl_authorizer.go b/security/acl_authorizer.go
index 7a305ab..4ab195c 100644
--- a/security/acl_authorizer.go
+++ b/security/acl_authorizer.go
@@ -29,10 +29,8 @@
if ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil && reflect.DeepEqual(ctx.LocalBlessings().PublicKey(), ctx.RemoteBlessings().PublicKey()) {
return nil
}
- if newAPI := (ctx.LocalBlessings() != nil && ctx.RemoteBlessings() != nil); !newAPI {
- if ctx.LocalID() != nil && ctx.RemoteID() != nil && reflect.DeepEqual(ctx.LocalID(), ctx.RemoteID()) {
- return nil
- }
+ if ctx.LocalID() != nil && ctx.RemoteID() != nil && reflect.DeepEqual(ctx.LocalID(), ctx.RemoteID()) {
+ return nil
}
var blessings []string
if ctx.RemoteBlessings() != nil {
diff --git a/security/blessingroots.go b/security/blessingroots.go
index 33bb145..5f314d8 100644
--- a/security/blessingroots.go
+++ b/security/blessingroots.go
@@ -1,8 +1,9 @@
package security
import (
- "crypto/sha256"
+ "bytes"
"errors"
+ "fmt"
"sync"
"veyron.io/veyron/veyron/security/serialization"
@@ -28,8 +29,7 @@
if err != nil {
return "", err
}
- rootBytesHash := sha256.Sum256(rootBytes)
- return string(rootBytesHash[:]), nil
+ return string(rootBytes), nil
}
func (br *blessingRoots) Add(root security.PublicKey, pattern security.BlessingPattern) error {
@@ -71,6 +71,27 @@
return errors.New("PublicKey is not a recognized root for this blessing")
}
+// DebugString return a human-readable string encoding of the roots
+// DebugString encodes all roots into a string in the following
+// format
+//
+// Public key : Pattern
+// <public key> : <pattern>
+// ...
+// <public key> : <pattern>
+func (br *blessingRoots) DebugString() string {
+ const format = "%-47s : %s\n"
+ b := bytes.NewBufferString(fmt.Sprintf(format, "Public key", "Pattern"))
+ for keyBytes, pattern := range br.store {
+ key, err := security.UnmarshalPublicKey([]byte(keyBytes))
+ if err != nil {
+ return fmt.Sprintf("failed to decode public key: %v", err)
+ }
+ b.WriteString(fmt.Sprintf(format, key, pattern))
+ }
+ return b.String()
+}
+
func (br *blessingRoots) save() error {
if (br.signer == nil) && (br.dir == "") {
return nil
diff --git a/security/blessingstore.go b/security/blessingstore.go
index 54b1b0b..b135580 100644
--- a/security/blessingstore.go
+++ b/security/blessingstore.go
@@ -1,6 +1,7 @@
package security
import (
+ "bytes"
"errors"
"fmt"
"reflect"
@@ -113,6 +114,25 @@
return fmt.Sprintf("{state: %v, publicKey: %v, dir: %v}", s.state, s.publicKey, s.dir)
}
+// DebugString return a human-readable string encoding of the store
+// in the following format
+// Default blessing : <Default blessing of the store>
+//
+// Peer pattern : Blessings
+// <pattern> : <blessings>
+// ...
+// <pattern> : <blessings>
+func (br *blessingStore) DebugString() string {
+ const format = "%-30s : %s\n"
+ b := bytes.NewBufferString(fmt.Sprintf("Default blessings: %v\n", br.state.Default))
+
+ b.WriteString(fmt.Sprintf(format, "Peer pattern", "Blessings"))
+ for pattern, blessings := range br.state.Store {
+ b.WriteString(fmt.Sprintf(format, pattern, blessings))
+ }
+ return b.String()
+}
+
func (s *blessingStore) save() error {
if (s.signer == nil) && (s.dir == "") {
return nil
diff --git a/services/identity/blesser/oauth.go b/services/identity/blesser/oauth.go
index 9463692..be6dd9f 100644
--- a/services/identity/blesser/oauth.go
+++ b/services/identity/blesser/oauth.go
@@ -64,17 +64,17 @@
})
}
-func (b *googleOAuth) BlessUsingAccessToken(ctx ipc.ServerContext, accesstoken string) (vdlutil.Any, error) {
+func (b *googleOAuth) BlessUsingAccessToken(ctx ipc.ServerContext, accesstoken string) (vdlutil.Any, string, error) {
if len(b.accessTokenClients) == 0 {
- return nil, fmt.Errorf("server not configured for blessing based on access tokens")
+ return nil, "", fmt.Errorf("server not configured for blessing based on access tokens")
}
// URL from: https://developers.google.com/accounts/docs/OAuth2UserAgent#validatetoken
tokeninfo, err := http.Get("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + accesstoken)
if err != nil {
- return nil, fmt.Errorf("unable to use token: %v", err)
+ return nil, "", fmt.Errorf("unable to use token: %v", err)
}
if tokeninfo.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("unable to verify access token: %v", tokeninfo.StatusCode)
+ return nil, "", fmt.Errorf("unable to verify access token: %v", tokeninfo.StatusCode)
}
// tokeninfo contains a JSON-encoded struct
var token struct {
@@ -88,7 +88,7 @@
AccessType string `json:"access_type"`
}
if err := json.NewDecoder(tokeninfo.Body).Decode(&token); err != nil {
- return "", fmt.Errorf("invalid JSON response from Google's tokeninfo API: %v", err)
+ return nil, "", fmt.Errorf("invalid JSON response from Google's tokeninfo API: %v", err)
}
audienceMatch := false
for _, c := range b.accessTokenClients {
@@ -99,31 +99,64 @@
}
if !audienceMatch {
vlog.Infof("Got access token [%+v], wanted one of client ids %v", token, b.accessTokenClients)
- return "", fmt.Errorf("token not meant for this purpose, confused deputy? https://developers.google.com/accounts/docs/OAuth2UserAgent#validatetoken")
+ return nil, "", fmt.Errorf("token not meant for this purpose, confused deputy? https://developers.google.com/accounts/docs/OAuth2UserAgent#validatetoken")
}
if !token.VerifiedEmail {
- return nil, fmt.Errorf("email not verified")
+ return nil, "", fmt.Errorf("email not verified")
+ }
+ if ctx.LocalPrincipal() == nil || ctx.RemoteBlessings() == nil {
+ // TODO(ataly, ashankar): Old security model, remove this block.
+ return b.blessOldModel(ctx, token.Email)
}
return b.bless(ctx, token.Email)
}
-func (b *googleOAuth) bless(ctx ipc.ServerContext, name string) (vdlutil.Any, error) {
+func (b *googleOAuth) bless(ctx ipc.ServerContext, email string) (vdlutil.Any, string, error) {
+ var caveat security.Caveat
+ var err error
+ if b.revocationManager != nil {
+ // TODO(ataly, ashankar): Update the RevocationManager so that it uses the
+ // new security model.
+ revocationCaveat, err := b.revocationManager.NewCaveat(b.rt.Identity().PublicID(), b.dischargerLocation)
+ if err != nil {
+ return nil, "", err
+ }
+ caveat, err = security.NewCaveat(revocationCaveat)
+ } else {
+ caveat, err = security.ExpiryCaveat(time.Now().Add(b.duration))
+ }
+ if err != nil {
+ return nil, "", err
+ }
+ blessing, err := ctx.LocalPrincipal().Bless(ctx.RemoteBlessings().PublicKey(), ctx.LocalBlessings(), email, caveat)
+ if err != nil {
+ return nil, "", err
+ }
+ return blessing, email, nil
+}
+
+// DEPRECATED
+// TODO(ataly, ashankar): Remove this method once we get rid of the old security model.
+func (b *googleOAuth) blessOldModel(ctx ipc.ServerContext, name string) (vdlutil.Any, string, error) {
if len(b.domain) > 0 && !strings.HasSuffix(name, "@"+b.domain) {
- return nil, fmt.Errorf("blessings for name %q are not allowed due to domain restriction", name)
+ return nil, "", fmt.Errorf("blessings for name %q are not allowed due to domain restriction", name)
}
self := b.rt.Identity()
var err error
// Use the blessing that was used to authenticate with the client to bless it.
if self, err = self.Derive(ctx.LocalID()); err != nil {
- return nil, err
+ return nil, "", err
}
var revocationCaveat security.ThirdPartyCaveat
if b.revocationManager != nil {
revocationCaveat, err = b.revocationManager.NewCaveat(b.rt.Identity().PublicID(), b.dischargerLocation)
if err != nil {
- return nil, err
+ return nil, "", err
}
}
-
- return revocation.Bless(self, ctx.RemoteID(), name, b.duration, nil, revocationCaveat)
+ blessing, err := revocation.Bless(self, ctx.RemoteID(), name, b.duration, nil, revocationCaveat)
+ if err != nil {
+ return nil, "", err
+ }
+ return blessing, name, nil
}
diff --git a/services/identity/googleoauth/handler.go b/services/identity/googleoauth/handler.go
index b8b805b..f008dce 100644
--- a/services/identity/googleoauth/handler.go
+++ b/services/identity/googleoauth/handler.go
@@ -497,19 +497,6 @@
},
Placeholder: "Comma-separated method names.",
},
- "PeerBlessingsCaveat": {
- New: func(args ...string) (security.Caveat, error) {
- if len(args) < 1 {
- return security.Caveat{}, fmt.Errorf("must pass at least one blessing pattern")
- }
- var patterns []security.BlessingPattern
- for _, arg := range args {
- patterns = append(patterns, security.BlessingPattern(arg))
- }
- return security.PeerBlessingsCaveat(patterns[0], patterns[1:]...)
- },
- Placeholder: "Comma-separated blessing patterns.",
- },
}
// exchangeAuthCodeForEmail exchanges the authorization code (which must
diff --git a/services/identity/identity.vdl b/services/identity/identity.vdl
index bec7d91..faad788 100644
--- a/services/identity/identity.vdl
+++ b/services/identity/identity.vdl
@@ -14,13 +14,10 @@
// veyron virtual circuit).
// Thus, if Mallory possesses the access token associated with Alice's account,
// she may be able to obtain a blessing with Alice's name on it.
-//
-// TODO(ashankar): Update this to use the new security model:
-// (blessing security.WireBlessing, error)
type OAuthBlesser interface {
// BlessUsingAccessToken uses the provided access token to obtain the email
- // address and returns a blessing.
- BlessUsingAccessToken(token string) (blessing any, err error)
+ // address and returns a blessing along with the email address.
+ BlessUsingAccessToken(token string) (blessing any, email string, err error)
}
// MacaroonBlesser returns a blessing given the provided macaroon string.
diff --git a/services/identity/identity.vdl.go b/services/identity/identity.vdl.go
index a947249..2525ee7 100644
--- a/services/identity/identity.vdl.go
+++ b/services/identity/identity.vdl.go
@@ -31,16 +31,13 @@
// veyron virtual circuit).
// Thus, if Mallory possesses the access token associated with Alice's account,
// she may be able to obtain a blessing with Alice's name on it.
-//
-// TODO(ashankar): Update this to use the new security model:
-// (blessing security.WireBlessing, error)
// OAuthBlesser is the interface the client binds and uses.
// OAuthBlesser_ExcludingUniversal is the interface without internal framework-added methods
// to enable embedding without method collisions. Not to be used directly by clients.
type OAuthBlesser_ExcludingUniversal interface {
// BlessUsingAccessToken uses the provided access token to obtain the email
- // address and returns a blessing.
- BlessUsingAccessToken(ctx _gen_context.T, token string, opts ..._gen_ipc.CallOpt) (reply _gen_vdlutil.Any, err error)
+ // address and returns a blessing along with the email address.
+ BlessUsingAccessToken(ctx _gen_context.T, token string, opts ..._gen_ipc.CallOpt) (blessing _gen_vdlutil.Any, email string, err error)
}
type OAuthBlesser interface {
_gen_ipc.UniversalServiceMethods
@@ -51,8 +48,8 @@
type OAuthBlesserService interface {
// BlessUsingAccessToken uses the provided access token to obtain the email
- // address and returns a blessing.
- BlessUsingAccessToken(context _gen_ipc.ServerContext, token string) (reply _gen_vdlutil.Any, err error)
+ // address and returns a blessing along with the email address.
+ BlessUsingAccessToken(context _gen_ipc.ServerContext, token string) (blessing _gen_vdlutil.Any, email string, err error)
}
// BindOAuthBlesser returns the client stub implementing the OAuthBlesser
@@ -102,12 +99,12 @@
return _gen_veyron2.RuntimeFromContext(ctx).Client()
}
-func (__gen_c *clientStubOAuthBlesser) BlessUsingAccessToken(ctx _gen_context.T, token string, opts ..._gen_ipc.CallOpt) (reply _gen_vdlutil.Any, err error) {
+func (__gen_c *clientStubOAuthBlesser) BlessUsingAccessToken(ctx _gen_context.T, token string, opts ..._gen_ipc.CallOpt) (blessing _gen_vdlutil.Any, email string, err error) {
var call _gen_ipc.Call
if call, err = __gen_c.client(ctx).StartCall(ctx, __gen_c.name, "BlessUsingAccessToken", []interface{}{token}, opts...); err != nil {
return
}
- if ierr := call.Finish(&reply, &err); ierr != nil {
+ if ierr := call.Finish(&blessing, &email, &err); ierr != nil {
err = ierr
}
return
@@ -173,6 +170,7 @@
},
OutArgs: []_gen_ipc.MethodArgument{
{Name: "blessing", Type: 65},
+ {Name: "email", Type: 3},
{Name: "err", Type: 66},
},
}
@@ -201,8 +199,8 @@
return
}
-func (__gen_s *ServerStubOAuthBlesser) BlessUsingAccessToken(call _gen_ipc.ServerCall, token string) (reply _gen_vdlutil.Any, err error) {
- reply, err = __gen_s.service.BlessUsingAccessToken(call, token)
+func (__gen_s *ServerStubOAuthBlesser) BlessUsingAccessToken(call _gen_ipc.ServerCall, token string) (blessing _gen_vdlutil.Any, email string, err error) {
+ blessing, email, err = __gen_s.service.BlessUsingAccessToken(call, token)
return
}
diff --git a/services/mgmt/node/impl/app_invoker.go b/services/mgmt/node/impl/app_invoker.go
index 35a8268..8e4cdb6 100644
--- a/services/mgmt/node/impl/app_invoker.go
+++ b/services/mgmt/node/impl/app_invoker.go
@@ -488,7 +488,7 @@
return errOperationFailed
}
// Wait for the child process to start.
- timeout := 20 * time.Second
+ timeout := 10 * time.Second
if err := handle.WaitForReady(timeout); err != nil {
vlog.Errorf("WaitForReady(%v) failed: %v", timeout, err)
return errOperationFailed
diff --git a/services/mgmt/node/impl/dispatcher.go b/services/mgmt/node/impl/dispatcher.go
index b63ea47..ed930d9 100644
--- a/services/mgmt/node/impl/dispatcher.go
+++ b/services/mgmt/node/impl/dispatcher.go
@@ -228,31 +228,42 @@
// The implementation of the node manager is split up into several
// invokers, which are instantiated depending on the receiver name
// prefix.
- var receiver interface{}
switch components[0] {
case nodeSuffix:
- receiver = node.NewServerNode(&nodeInvoker{
+ receiver := node.NewServerNode(&nodeInvoker{
callback: d.internal.callback,
updating: d.internal.updating,
config: d.config,
disp: d,
})
+ return ipc.ReflectInvoker(receiver), d.auth, nil
case appsSuffix:
- receiver = node.NewServerApplication(&appInvoker{
+ receiver := node.NewServerApplication(&appInvoker{
callback: d.internal.callback,
config: d.config,
suffix: components[1:],
})
+ // TODO(caprita,rjkroege): Once we implement per-object ACLs
+ // (i.e. each installation and instance), replace d.auth with
+ // per-object authorizer.
+ return ipc.ReflectInvoker(receiver), d.auth, nil
case configSuffix:
if len(components) != 2 {
return nil, nil, errInvalidSuffix
}
- receiver = inode.NewServerConfig(&configInvoker{
+ receiver := inode.NewServerConfig(&configInvoker{
callback: d.internal.callback,
suffix: components[1],
})
+ // The nil authorizer ensures that only principals blessed by
+ // the node manager can talk back to it. All apps started by
+ // the node manager should fall in that category.
+ //
+ // TODO(caprita,rjkroege): We should further refine this, by
+ // only allowing the app to update state referring to itself
+ // (and not other apps).
+ return ipc.ReflectInvoker(receiver), nil, nil
default:
return nil, nil, errInvalidSuffix
}
- return ipc.ReflectInvoker(receiver), d.auth, nil
}
diff --git a/services/mgmt/node/impl/impl_test.go b/services/mgmt/node/impl/impl_test.go
index 6d66f0f..32458f4 100644
--- a/services/mgmt/node/impl/impl_test.go
+++ b/services/mgmt/node/impl/impl_test.go
@@ -1,10 +1,10 @@
package impl_test
import (
- // "bytes"
+ "bytes"
"crypto/md5"
"encoding/base64"
- // "encoding/hex"
+ "encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
@@ -19,13 +19,12 @@
"strings"
"syscall"
"testing"
- "time"
"veyron.io/veyron/veyron/lib/exec"
"veyron.io/veyron/veyron/lib/signals"
"veyron.io/veyron/veyron/lib/testutil/blackbox"
- // tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
- // vsecurity "veyron.io/veyron/veyron/security"
+ tsecurity "veyron.io/veyron/veyron/lib/testutil/security"
+ vsecurity "veyron.io/veyron/veyron/security"
"veyron.io/veyron/veyron/services/mgmt/node/config"
"veyron.io/veyron/veyron/services/mgmt/node/impl"
suidhelper "veyron.io/veyron/veyron/services/mgmt/suidhelper/impl"
@@ -65,7 +64,7 @@
}
// All the tests require a runtime; so just create it here.
- rt.Init()
+ rt.Init(veyron2.ForceNewSecurityModel{})
// Disable the cache because we will be manipulating/using the namespace
// across multiple processes and want predictable behaviour without
@@ -722,32 +721,26 @@
type granter struct {
ipc.CallOpt
- self security.PrivateID
- blessing security.PublicID
+ p security.Principal
+ extension string
}
-func (g *granter) Grant(id security.PublicID) (security.PublicID, error) {
- var err error
- g.blessing, err = g.self.Bless(id, "claimernode", 10*time.Minute, nil)
- return g.blessing, err
+func (g *granter) Grant(other security.Blessings) (security.Blessings, error) {
+ return g.p.Bless(other.PublicKey(), g.p.BlessingStore().Default(), g.extension, security.UnconstrainedUse())
}
-func newRuntimeClient(t *testing.T, id security.PrivateID) (veyron2.Runtime, ipc.Client) {
- runtime, err := rt.New(veyron2.RuntimeID(id))
+func newRuntime(t *testing.T) veyron2.Runtime {
+ runtime, err := rt.New(veyron2.ForceNewSecurityModel{})
if err != nil {
t.Fatalf("rt.New() failed: %v", err)
}
runtime.Namespace().SetRoots(rt.R().Namespace().Roots()[0])
- nodeClient, err := runtime.NewClient()
- if err != nil {
- t.Fatalf("rt.NewClient() failed %v", err)
- }
- return runtime, nodeClient
+ return runtime
}
-func tryInstall(rt veyron2.Runtime, c ipc.Client) error {
+func tryInstall(rt veyron2.Runtime) error {
appsName := "nm//apps"
- stub, err := node.BindApplication(appsName, c)
+ stub, err := node.BindApplication(appsName, rt.Client())
if err != nil {
return fmt.Errorf("BindApplication(%v) failed: %v", appsName, err)
}
@@ -757,9 +750,6 @@
return nil
}
-// TODO(ashankar): Temporarily disabled during security model transition.
-// Fix up and restore!
-/*
// TestNodeManagerClaim claims a nodemanager and tests ACL permissions on its methods.
func TestNodeManagerClaim(t *testing.T) {
// Set up mount table, application, and binary repositories.
@@ -782,8 +772,13 @@
readPID(t, nm)
// Create an envelope for an app.
- app := blackbox.HelperCommand(t, "app", "")
- defer setupChildCommand(app)()
+ app := blackbox.HelperCommand(t, "app", "trapp")
+ // Ensure the child has a blessing that (a) allows it talk back to the
+ // node manager config service and (b) allows the node manager to talk
+ // to the app. We achieve this by making the app's blessing derived
+ // from the node manager's blessing post claiming (which will be
+ // "mydevice").
+ defer setupChildCommandWithBlessing(app, "mydevice/child")()
appTitle := "google naps"
*envelope = *envelopeFromCmd(appTitle, app.Cmd)
@@ -792,39 +787,45 @@
t.Fatalf("BindNode failed: %v", err)
}
- // Create a new identity and runtime.
- claimerIdentity := tsecurity.NewBlessedIdentity(rt.R().Identity(), "claimer")
- newRT, nodeClient := newRuntimeClient(t, claimerIdentity)
- defer newRT.Cleanup()
+ selfRT := rt.R()
+ otherRT := newRuntime(t)
+ defer otherRT.Cleanup()
- // Nodemanager should have open ACLs before we claim it and so an Install
- // should succeed.
- if err = tryInstall(newRT, nodeClient); err != nil {
- t.Fatalf("%v", err)
+ // Nodemanager should have open ACLs before we claim it and so an Install from otherRT should succeed.
+ if err = tryInstall(otherRT); err != nil {
+ t.Fatal(err)
}
- // Claim the nodemanager with this identity.
- if err = nodeStub.Claim(rt.R().NewContext(), &granter{self: claimerIdentity}); err != nil {
- t.Fatalf("Claim failed: %v", err)
+ // Claim the nodemanager with selfRT as <defaultblessing>/mydevice
+ if err = nodeStub.Claim(selfRT.NewContext(), &granter{p: selfRT.Principal(), extension: "mydevice"}); err != nil {
+ t.Fatal(err)
}
- if err = tryInstall(newRT, nodeClient); err != nil {
- t.Fatalf("%v", err)
+ // Installation should succeed since rt.R() (a.k.a. selfRT) is now the
+ // "owner" of the nodemanager.
+ appID := installApp(t)
+
+ // otherRT should be unable to install though, since the ACLs have changed now.
+ if err = tryInstall(otherRT); err == nil {
+ t.Fatalf("Install should have failed from otherRT")
}
- // Try to install with a new identity. This should fail.
- randomIdentity := tsecurity.NewBlessedIdentity(rt.R().Identity(), "random")
- newRT, nodeClient = newRuntimeClient(t, randomIdentity)
- defer newRT.Cleanup()
- if err = tryInstall(newRT, nodeClient); err == nil {
- t.Fatalf("Install should have failed with random identity")
+
+ // Create a script wrapping the test target that implements suidhelper.
+ generateSuidHelperScript(t, root)
+
+ // Create the local server that the app uses to let us know it's ready.
+ // TODO(caprita): Factor this code snippet out, it's pretty common.
+ server, _ := newServer()
+ defer server.Stop()
+ pingCh := make(chan string, 1)
+ if err := server.Serve("pingserver", ipc.LeafDispatcher(pingServerDisp(pingCh), nil)); err != nil {
+ t.Fatalf("Serve(%q, <dispatcher>) failed: %v", "pingserver", err)
}
- // Try to install with the original identity. This should still work as the original identity
- // name is a prefix of the identity used by newRT.
- nodeClient, err = rt.R().NewClient()
- if err != nil {
- t.Fatalf("rt.NewClient() failed %v", err)
- }
- if err = tryInstall(rt.R(), nodeClient); err != nil {
- t.Fatalf("%v", err)
- }
+
+ // Start an instance of the app.
+ instanceID := startApp(t, appID)
+ <-pingCh
+ resolve(t, "trapp", 1)
+ suspendApp(t, appID, instanceID)
+
// TODO(gauthamt): Test that ACLs persist across nodemanager restarts
}
@@ -838,6 +839,24 @@
root, cleanup := setupRootDir(t)
defer cleanup()
+ var (
+ proot = newRootPrincipal("root")
+ // The two "processes"/runtimes which will act as IPC clients to the
+ // nodemanager process.
+ selfRT = rt.R()
+ otherRT = newRuntime(t)
+ )
+ defer otherRT.Cleanup()
+ // By default, selfRT and otherRT will have blessings generated based on the
+ // username/machine name running this process. Since these blessings will appear
+ // in ACLs, give them recognizable names.
+ if err := setDefaultBlessings(selfRT.Principal(), proot, "self"); err != nil {
+ t.Fatal(err)
+ }
+ if err := setDefaultBlessings(otherRT.Principal(), proot, "other"); err != nil {
+ t.Fatal(err)
+ }
+
// Set up the node manager. Since we won't do node manager updates,
// don't worry about its application envelope and current link.
nm := blackbox.HelperCommand(t, "nodeManager", "nm", root, "unused app repo name", "unused curr link")
@@ -858,7 +877,7 @@
if err != nil {
t.Fatalf("BindNode failed: %v", err)
}
- acl, etag, err := nodeStub.GetACL(rt.R().NewContext())
+ acl, etag, err := nodeStub.GetACL(selfRT.NewContext())
if err != nil {
t.Fatalf("GetACL failed:%v", err)
}
@@ -866,59 +885,42 @@
t.Fatalf("getACL expected:default, got:%v(%v)", etag, acl)
}
- // Create a new identity and claim the node manager
- claimerIdentity := tsecurity.NewBlessedIdentity(rt.R().Identity(), "claimer")
- grant := &granter{self: claimerIdentity}
- if err = nodeStub.Claim(rt.R().NewContext(), grant); err != nil {
- t.Fatalf("Claim failed: %v", err)
+ // Claim the nodemanager as "root/self/mydevice"
+ if err = nodeStub.Claim(selfRT.NewContext(), &granter{p: selfRT.Principal(), extension: "mydevice"}); err != nil {
+ t.Fatal(err)
}
- expectedACL := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
- for _, name := range grant.blessing.Names() {
- expectedACL.In[security.BlessingPattern(name)] = security.AllLabels
- }
+ expectedACL := security.ACL{In: map[security.BlessingPattern]security.LabelSet{"root/self/mydevice": security.AllLabels}}
var b bytes.Buffer
if err := vsecurity.SaveACL(&b, expectedACL); err != nil {
t.Fatalf("Failed to saveACL:%v", err)
}
md5hash := md5.Sum(b.Bytes())
expectedETAG := hex.EncodeToString(md5hash[:])
- acl, etag, err = nodeStub.GetACL(rt.R().NewContext())
- if err != nil {
- t.Fatalf("GetACL failed")
+ if acl, etag, err = nodeStub.GetACL(selfRT.NewContext()); err != nil {
+ t.Fatal(err)
}
if etag != expectedETAG {
t.Fatalf("getACL expected:%v(%v), got:%v(%v)", expectedACL, expectedETAG, acl, etag)
}
- // Try to install with a new identity. This should fail.
- randomIdentity := tsecurity.NewBlessedIdentity(rt.R().Identity(), "random")
- newRT, nodeClient := newRuntimeClient(t, randomIdentity)
- defer newRT.Cleanup()
- if err = tryInstall(newRT, nodeClient); err == nil {
+ // Install from otherRT should fail, since it does not match the ACL.
+ if err = tryInstall(otherRT); err == nil {
t.Fatalf("Install should have failed with random identity")
}
- newACL := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
- for _, name := range randomIdentity.PublicID().Names() {
- newACL.In[security.BlessingPattern(name)] = security.AllLabels
- }
- // SetACL with invalid etag
- if err = nodeStub.SetACL(rt.R().NewContext(), newACL, "invalid"); err == nil {
+ newACL := security.ACL{In: map[security.BlessingPattern]security.LabelSet{"root/other": security.AllLabels}}
+ if err = nodeStub.SetACL(selfRT.NewContext(), newACL, "invalid"); err == nil {
t.Fatalf("SetACL should have failed with invalid etag")
}
- if err = nodeStub.SetACL(rt.R().NewContext(), newACL, etag); err != nil {
- t.Fatalf("SetACL failed:%v", err)
+ if err = nodeStub.SetACL(selfRT.NewContext(), newACL, etag); err != nil {
+ t.Fatal(err)
}
- if err = tryInstall(newRT, nodeClient); err != nil {
- t.Fatalf("Install failed with new identity:%v", err)
+ // Install should now fail with selfRT, which no longer matches the ACLs but succeed with otherRT, which does.
+ if err = tryInstall(selfRT); err == nil {
+ t.Errorf("Install should have failed with selfRT since it should no longer match the ACL")
}
- // Try to install with the claimer identity. This should fail as the ACLs
- // belong to the random identity
- newRT, nodeClient = newRuntimeClient(t, claimerIdentity)
- defer newRT.Cleanup()
- if err = tryInstall(newRT, nodeClient); err == nil {
- t.Fatalf("Install should have failed with claimer identity")
+ if err = tryInstall(otherRT); err != nil {
+ t.Error(err)
}
}
-*/
func TestNodeManagerGlob(t *testing.T) {
// Set up mount table, application, and binary repositories.
@@ -1007,3 +1009,34 @@
}
}
}
+
+// rootPrincipal encapsulates a principal that acts as an "identity provider".
+type rootPrincipal struct {
+ p security.Principal
+ b security.Blessings
+}
+
+func (r *rootPrincipal) Bless(key security.PublicKey, as string) (security.Blessings, error) {
+ return r.p.Bless(key, r.b, as, security.UnconstrainedUse())
+}
+
+func newRootPrincipal(name string) *rootPrincipal {
+ p, err := vsecurity.NewPrincipal()
+ if err != nil {
+ panic(err)
+ }
+ b, err := p.BlessSelf(name)
+ if err != nil {
+ panic(err)
+ }
+ return &rootPrincipal{p, b}
+}
+
+func setDefaultBlessings(p security.Principal, root *rootPrincipal, name string) error {
+ b, err := root.Bless(p.PublicKey(), name)
+ if err != nil {
+ return err
+ }
+ tsecurity.SetDefaultBlessings(p, b)
+ return nil
+}
diff --git a/services/mgmt/node/impl/util_test.go b/services/mgmt/node/impl/util_test.go
index ad9e22a..37d3cfb 100644
--- a/services/mgmt/node/impl/util_test.go
+++ b/services/mgmt/node/impl/util_test.go
@@ -61,16 +61,20 @@
// TODO(caprita): Move this setup into the blackbox lib.
// setupChildCommand configures the child to use the right mounttable root
-// and identity. It returns a cleanup function.
+// and blessings. It returns a cleanup function.
func setupChildCommand(child *blackbox.Child) func() {
+ return setupChildCommandWithBlessing(child, "child")
+}
+
+func setupChildCommandWithBlessing(child *blackbox.Child, blessing string) func() {
cmd := child.Cmd
for i, root := range rt.R().Namespace().Roots() {
cmd.Env = exec.Setenv(cmd.Env, fmt.Sprintf("NAMESPACE_ROOT%d", i), root)
}
- idFile := security.SaveIdentityToFile(security.NewBlessedIdentity(rt.R().Identity(), "test"))
- cmd.Env = exec.Setenv(cmd.Env, "VEYRON_IDENTITY", idFile)
+ childcreds := security.NewVeyronCredentials(rt.R().Principal(), blessing)
+ cmd.Env = exec.Setenv(cmd.Env, "VEYRON_CREDENTIALS", childcreds)
return func() {
- os.Remove(idFile)
+ os.RemoveAll(childcreds)
}
}
diff --git a/services/mounttable/lib/mounttable_test.go b/services/mounttable/lib/mounttable_test.go
index c773312..a8a6286 100644
--- a/services/mounttable/lib/mounttable_test.go
+++ b/services/mounttable/lib/mounttable_test.go
@@ -2,6 +2,7 @@
import (
"errors"
+ "fmt"
"reflect"
"runtime/debug"
"sort"
@@ -26,14 +27,12 @@
// stupidNS is a version of naming.Namespace that we can control. This exists so that we have some
// firm ground to stand on vis a vis the stub interface.
type stupidNS struct {
- id ipc.ClientOpt
+ r veyron2.Runtime
}
-var (
- rootID = veyron2.LocalID(security.FakePublicID("root"))
- bobID = veyron2.LocalID(security.FakePublicID("bob"))
- aliceID = veyron2.LocalID(security.FakePublicID("alice"))
-)
+// Simulate different processes with different runtimes.
+// rootRT is the one running the mounttable service.
+var rootRT, aliceRT, bobRT veyron2.Runtime
const ttlSecs = 60 * 60
@@ -42,11 +41,10 @@
t.Fatal(string(debug.Stack()))
}
-// quuxClient returns an ipc.Client that uses the simple namespace for name
-// resolution.
-func quuxClient(id ipc.ClientOpt) ipc.Client {
- ns := stupidNS{id}
- c, err := rt.R().NewClient(id, veyron2.Namespace(ns))
+// quuxClient returns an ipc.Client that would be used by the provided runtime
+// and uses the simple namespace for name resolution.
+func quuxClient(r veyron2.Runtime) ipc.Client {
+ c, err := r.NewClient(veyron2.Namespace(stupidNS{r}))
if err != nil {
panic(err)
}
@@ -74,11 +72,11 @@
}
// Resolve via another
- objectPtr, err := mounttable.BindMountTable("/"+address+"//"+suffix, quuxClient(ns.id))
+ objectPtr, err := mounttable.BindMountTable("/"+address+"//"+suffix, quuxClient(ns.r))
if err != nil {
return nil, err
}
- ss, suffix, err := objectPtr.ResolveStep(rt.R().NewContext())
+ ss, suffix, err := objectPtr.ResolveStep(ns.r.NewContext())
if err != nil {
return nil, err
}
@@ -119,12 +117,12 @@
return []string{}
}
-func doMount(t *testing.T, name, service string, shouldSucceed bool, id ipc.ClientOpt) {
- mtpt, err := mounttable.BindMountTable(name, quuxClient(id))
+func doMount(t *testing.T, name, service string, shouldSucceed bool, as veyron2.Runtime) {
+ mtpt, err := mounttable.BindMountTable(name, quuxClient(as))
if err != nil {
boom(t, "Failed to BindMountTable: %s", err)
}
- if err := mtpt.Mount(rt.R().NewContext(), service, uint32(ttlSecs), 0, veyron2.RetryTimeoutOpt(0)); err != nil {
+ if err := mtpt.Mount(as.NewContext(), service, uint32(ttlSecs), 0, veyron2.RetryTimeoutOpt(0)); err != nil {
if shouldSucceed {
boom(t, "Failed to Mount %s onto %s: %s", service, name, err)
}
@@ -133,12 +131,12 @@
}
}
-func doUnmount(t *testing.T, name, service string, shouldSucceed bool, id ipc.ClientOpt) {
- mtpt, err := mounttable.BindMountTable(name, quuxClient(id))
+func doUnmount(t *testing.T, name, service string, shouldSucceed bool, as veyron2.Runtime) {
+ mtpt, err := mounttable.BindMountTable(name, quuxClient(as))
if err != nil {
boom(t, "Failed to BindMountTable: %s", err)
}
- if err := mtpt.Unmount(rt.R().NewContext(), service, veyron2.RetryTimeoutOpt(0)); err != nil {
+ if err := mtpt.Unmount(as.NewContext(), service, veyron2.RetryTimeoutOpt(0)); err != nil {
if shouldSucceed {
boom(t, "Failed to Unmount %s onto %s: %s", service, name, err)
}
@@ -147,22 +145,22 @@
}
}
-func create(t *testing.T, name, contents string) {
- objectPtr, err := BindCollection(name, quuxClient(rootID))
+func export(t *testing.T, name, contents string, as veyron2.Runtime) {
+ objectPtr, err := BindCollection(name, quuxClient(as))
if err != nil {
boom(t, "Failed to BindCollection: %s", err)
}
- if err := objectPtr.Export(rt.R().NewContext(), contents, true); err != nil {
+ if err := objectPtr.Export(as.NewContext(), contents, true); err != nil {
boom(t, "Failed to Export %s to %s: %s", name, contents, err)
}
}
-func checkContents(t *testing.T, name, expected string, shouldSucceed bool, id ipc.ClientOpt) {
- objectPtr, err := BindCollection(name, quuxClient(id))
+func checkContents(t *testing.T, name, expected string, shouldSucceed bool, as veyron2.Runtime) {
+ objectPtr, err := BindCollection(name, quuxClient(as))
if err != nil {
boom(t, "Failed to BindCollection: %s", err)
}
- contents, err := objectPtr.Lookup(rt.R().NewContext(), veyron2.RetryTimeoutOpt(0))
+ contents, err := objectPtr.Lookup(as.NewContext(), veyron2.RetryTimeoutOpt(0))
if err != nil {
if shouldSucceed {
boom(t, "Failed to Lookup %s: %s", name, err)
@@ -178,13 +176,7 @@
}
func newMT(t *testing.T, acl string) (ipc.Server, string) {
- // It is necessary for the private key of runtime's identity and
- // the public key of the LocalIDOpts passed to clients to correspond.
- // Since the LocalIDOpts are FakePublicIDs, we initialize the runtime
- // below with a FakePrivateID. (Note all FakePublicIDs and FakePrivateIDs
- // always have corresponding public and private keys respectively.)
- r := rt.Init(veyron2.RuntimeID(security.FakePrivateID("irrelevant")))
- server, err := r.NewServer(veyron2.ServesMountTableOpt(true))
+ server, err := rootRT.NewServer(veyron2.ServesMountTableOpt(true))
if err != nil {
boom(t, "r.NewServer: %s", err)
}
@@ -207,8 +199,7 @@
}
func newCollection(t *testing.T, acl string) (ipc.Server, string) {
- r := rt.Init()
- server, err := r.NewServer()
+ server, err := rootRT.NewServer()
if err != nil {
boom(t, "r.NewServer: %s", err)
}
@@ -238,69 +229,69 @@
// Mount the collection server into the mount table.
vlog.Infof("Mount the collection server into the mount table.")
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/stuff"), collectionName, true, rootID)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/stuff"), collectionName, true, rootRT)
// Create a few objects and make sure we can read them.
vlog.Infof("Create a few objects.")
- create(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain")
- create(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/in/spain"), "in spain")
- create(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/falls"), "falls mainly on the plain")
+ export(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", rootRT)
+ export(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/in/spain"), "in spain", rootRT)
+ export(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/falls"), "falls mainly on the plain", rootRT)
vlog.Infof("Make sure we can read them.")
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/in/spain"), "in spain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/falls"), "falls mainly on the plain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable//stuff/falls"), "falls mainly on the plain", false, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/nonexistant"), "falls mainly on the plain", false, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", true, bobID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", false, aliceID)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/in/spain"), "in spain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/falls"), "falls mainly on the plain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable//stuff/falls"), "falls mainly on the plain", false, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/nonexistant"), "falls mainly on the plain", false, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", true, bobRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/the/rain"), "the rain", false, aliceRT)
// Test multiple mounts.
vlog.Infof("Multiple mounts.")
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable//a/b"), collectionName, true, rootID)
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/x/y"), collectionName, true, rootID)
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/alpha//beta"), collectionName, true, rootID)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable//a/b"), collectionName, true, rootRT)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/x/y"), collectionName, true, rootRT)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/alpha//beta"), collectionName, true, rootRT)
vlog.Infof("Make sure we can read them.")
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/falls"), "falls mainly on the plain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/x/y/falls"), "falls mainly on the plain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/alpha/beta/falls"), "falls mainly on the plain", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", true, aliceID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", false, bobID)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuff/falls"), "falls mainly on the plain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/x/y/falls"), "falls mainly on the plain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/alpha/beta/falls"), "falls mainly on the plain", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", true, aliceRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", false, bobRT)
// Test generic unmount.
vlog.Info("Test generic unmount.")
- doUnmount(t, naming.JoinAddressName(mtAddr, "//mounttable/a/b"), "", true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", false, rootID)
+ doUnmount(t, naming.JoinAddressName(mtAddr, "//mounttable/a/b"), "", true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", false, rootRT)
// Test specific unmount.
vlog.Info("Test specific unmount.")
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/a/b"), collectionName, true, rootID)
- doUnmount(t, naming.JoinAddressName(mtAddr, "//mounttable/a/b"), collectionName, true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", false, rootID)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/a/b"), collectionName, true, rootRT)
+ doUnmount(t, naming.JoinAddressName(mtAddr, "//mounttable/a/b"), collectionName, true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/a/b/falls"), "falls mainly on the plain", false, rootRT)
// Try timing out a mount.
vlog.Info("Try timing out a mount.")
ft := NewFakeTimeClock()
setServerListClock(ft)
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/stuffWithTTL"), collectionName, true, rootID)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuffWithTTL/the/rain"), "the rain", true, rootID)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable/stuffWithTTL"), collectionName, true, rootRT)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuffWithTTL/the/rain"), "the rain", true, rootRT)
ft.advance(time.Duration(ttlSecs+4) * time.Second)
- checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuffWithTTL/the/rain"), "the rain", false, rootID)
+ checkContents(t, naming.JoinAddressName(mtAddr, "mounttable/stuffWithTTL/the/rain"), "the rain", false, rootRT)
// Test unauthorized mount.
vlog.Info("Test unauthorized mount.")
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable//a/b"), collectionName, false, bobID)
- doMount(t, naming.JoinAddressName(mtAddr, "//mounttable//a/b"), collectionName, false, aliceID)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable//a/b"), collectionName, false, bobRT)
+ doMount(t, naming.JoinAddressName(mtAddr, "//mounttable//a/b"), collectionName, false, aliceRT)
- doUnmount(t, naming.JoinAddressName(mtAddr, "//mounttable/x/y"), collectionName, false, bobID)
+ doUnmount(t, naming.JoinAddressName(mtAddr, "//mounttable/x/y"), collectionName, false, bobRT)
}
-func doGlob(t *testing.T, name, pattern string, id ipc.ClientOpt) []string {
- mtpt, err := mounttable.BindMountTable(name, quuxClient(id))
+func doGlob(t *testing.T, name, pattern string, as veyron2.Runtime) []string {
+ mtpt, err := mounttable.BindMountTable(name, quuxClient(as))
if err != nil {
boom(t, "Failed to BindMountTable: %s", err)
}
- stream, err := mtpt.Glob(rt.R().NewContext(), pattern)
+ stream, err := mtpt.Glob(as.NewContext(), pattern)
if err != nil {
boom(t, "Failed call to %s.Glob(%s): %s", name, pattern, err)
}
@@ -336,9 +327,9 @@
// set up a mount space
fakeServer := naming.JoinAddressName(estr, "//quux")
- doMount(t, naming.JoinAddressName(estr, "//one/bright/day"), fakeServer, true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//in/the/middle"), fakeServer, true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//of/the/night"), fakeServer, true, rootID)
+ doMount(t, naming.JoinAddressName(estr, "//one/bright/day"), fakeServer, true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//in/the/middle"), fakeServer, true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//of/the/night"), fakeServer, true, rootRT)
// Try various globs.
tests := []struct {
@@ -356,7 +347,7 @@
{"", []string{""}},
}
for _, test := range tests {
- out := doGlob(t, naming.JoinAddressName(estr, "//"), test.in, rootID)
+ out := doGlob(t, naming.JoinAddressName(estr, "//"), test.in, rootRT)
checkMatch(t, test.expected, out)
}
@@ -365,11 +356,11 @@
{
name := naming.JoinAddressName(estr, "//of/the/night/two/dead/boys/got/up/to/fight")
pattern := "*"
- m, err := mounttable.BindGlobbable(name, quuxClient(rootID))
+ m, err := mounttable.BindGlobbable(name, quuxClient(rootRT))
if err != nil {
boom(t, "Failed to BindMountTable: %s", err)
}
- stream, err := m.Glob(rt.R().NewContext(), pattern)
+ stream, err := m.Glob(rootRT.NewContext(), pattern)
if err != nil {
boom(t, "Failed call to %s.Glob(%s): %s", name, pattern, err)
}
@@ -400,24 +391,24 @@
// set up a mount space
fakeServer := naming.JoinAddressName(estr, "quux")
- doMount(t, naming.JoinAddressName(estr, "//one/bright/day"), fakeServer, true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//a/b/c"), fakeServer, true, rootID)
+ doMount(t, naming.JoinAddressName(estr, "//one/bright/day"), fakeServer, true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//a/b/c"), fakeServer, true, rootRT)
// Try various globs.
tests := []struct {
- id ipc.ClientOpt
+ as veyron2.Runtime
in string
expected []string
}{
- {rootID, "*", []string{"one", "a"}},
- {aliceID, "*", []string{"one", "a"}},
- {bobID, "*", []string{"one"}},
- {rootID, "*/...", []string{"one", "a", "one/bright", "a/b", "one/bright/day", "a/b/c"}},
- {aliceID, "*/...", []string{"one", "a", "one/bright", "a/b", "one/bright/day", "a/b/c"}},
- {bobID, "*/...", []string{"one", "one/bright", "one/bright/day"}},
+ {rootRT, "*", []string{"one", "a"}},
+ {aliceRT, "*", []string{"one", "a"}},
+ {bobRT, "*", []string{"one"}},
+ {rootRT, "*/...", []string{"one", "a", "one/bright", "a/b", "one/bright/day", "a/b/c"}},
+ {aliceRT, "*/...", []string{"one", "a", "one/bright", "a/b", "one/bright/day", "a/b/c"}},
+ {bobRT, "*/...", []string{"one", "one/bright", "one/bright/day"}},
}
for _, test := range tests {
- out := doGlob(t, naming.JoinAddressName(estr, "//"), test.in, test.id)
+ out := doGlob(t, naming.JoinAddressName(estr, "//"), test.in, test.as)
checkMatch(t, test.expected, out)
}
}
@@ -426,12 +417,12 @@
server, estr := newMT(t, "")
defer server.Stop()
- doMount(t, naming.JoinAddressName(estr, "//mounttable/endpoint"), naming.JoinAddressName(estr, "life/on/the/mississippi"), true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/hostport"), "/atrampabroad:8000", true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/hostport-endpoint-platypus"), "/@atrampabroad:8000@@", true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/invalid/not/rooted"), "atrampabroad:8000", false, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/invalid/no/port"), "/atrampabroad", false, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/invalid/endpoint"), "/@following the equator:8000@@@", false, rootID)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/endpoint"), naming.JoinAddressName(estr, "life/on/the/mississippi"), true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/hostport"), "/atrampabroad:8000", true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/hostport-endpoint-platypus"), "/@atrampabroad:8000@@", true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/invalid/not/rooted"), "atrampabroad:8000", false, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/invalid/no/port"), "/atrampabroad", false, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/invalid/endpoint"), "/@following the equator:8000@@@", false, rootRT)
}
func TestExpiry(t *testing.T) {
@@ -444,21 +435,21 @@
ft := NewFakeTimeClock()
setServerListClock(ft)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/a1/b1"), collectionName, true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/a1/b2"), collectionName, true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/a2/b1"), collectionName, true, rootID)
- doMount(t, naming.JoinAddressName(estr, "//mounttable/a2/b2/c"), collectionName, true, rootID)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/a1/b1"), collectionName, true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/a1/b2"), collectionName, true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/a2/b1"), collectionName, true, rootRT)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/a2/b2/c"), collectionName, true, rootRT)
- checkMatch(t, []string{"a1/b1", "a2/b1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*/b1/...", rootID))
+ checkMatch(t, []string{"a1/b1", "a2/b1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*/b1/...", rootRT))
ft.advance(time.Duration(ttlSecs/2) * time.Second)
- checkMatch(t, []string{"a1/b1", "a2/b1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*/b1/...", rootID))
- checkMatch(t, []string{"c"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable/a2/b2"), "*", rootID))
+ checkMatch(t, []string{"a1/b1", "a2/b1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*/b1/...", rootRT))
+ checkMatch(t, []string{"c"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable/a2/b2"), "*", rootRT))
// Refresh only a1/b1. All the other mounts will expire upon the next
// ft advance.
- doMount(t, naming.JoinAddressName(estr, "//mounttable/a1/b1"), collectionName, true, rootID)
+ doMount(t, naming.JoinAddressName(estr, "//mounttable/a1/b1"), collectionName, true, rootRT)
ft.advance(time.Duration(ttlSecs/2+4) * time.Second)
- checkMatch(t, []string{"a1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*", rootID))
- checkMatch(t, []string{"a1/b1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*/b1/...", rootID))
+ checkMatch(t, []string{"a1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*", rootRT))
+ checkMatch(t, []string{"a1/b1"}, doGlob(t, naming.JoinAddressName(estr, "//mounttable"), "*/b1/...", rootRT))
}
func TestBadACLs(t *testing.T) {
@@ -475,3 +466,42 @@
boom(t, "Expected error for missing '/' acl")
}
}
+
+func init() {
+ // Create the runtime for each of the three "processes"
+ rootRT = rt.Init(veyron2.ForceNewSecurityModel{})
+ var err error
+ if aliceRT, err = rt.New(veyron2.ForceNewSecurityModel{}); err != nil {
+ panic(err)
+ }
+ if bobRT, err = rt.New(veyron2.ForceNewSecurityModel{}); err != nil {
+ panic(err)
+ }
+
+ // And setup their blessings so that they present "root", "alice" and "bob"
+ // and these blessings are recognized by the others.
+ principals := map[string]security.Principal{
+ "root": rootRT.Principal(),
+ "alice": aliceRT.Principal(),
+ "bob": bobRT.Principal(),
+ }
+ for name, p := range principals {
+ blessing, err := p.BlessSelf(name)
+ if err != nil {
+ panic(fmt.Sprintf("BlessSelf(%q) failed: %v", name, err))
+ }
+ // Share this blessing with all servers and use it when serving clients.
+ if err = p.BlessingStore().SetDefault(blessing); err != nil {
+ panic(fmt.Sprintf("%v: %v", blessing, err))
+ }
+ if _, err = p.BlessingStore().Set(blessing, security.AllPrincipals); err != nil {
+ panic(fmt.Sprintf("%v: %v", blessing, err))
+ }
+ // Have all principals trust the root of this blessing.
+ for _, other := range principals {
+ if err := other.AddToRoots(blessing); err != nil {
+ panic(err)
+ }
+ }
+ }
+}
diff --git a/services/mounttable/lib/neighborhood_test.go b/services/mounttable/lib/neighborhood_test.go
index edccc7f..cb95d4c 100644
--- a/services/mounttable/lib/neighborhood_test.go
+++ b/services/mounttable/lib/neighborhood_test.go
@@ -7,9 +7,7 @@
"testing"
"time"
- "veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/services/mounttable"
"veyron.io/veyron/veyron2/vlog"
@@ -26,10 +24,8 @@
}
func TestNeighborhood(t *testing.T) {
- r := rt.Init()
- id := veyron2.LocalID(rt.R().Identity().PublicID())
vlog.Infof("TestNeighborhood")
- server, err := r.NewServer()
+ server, err := rootRT.NewServer()
if err != nil {
boom(t, "r.NewServer: %s", err)
}
@@ -63,7 +59,7 @@
// Wait for the mounttable to appear in mdns
L:
for tries := 1; tries < 2; tries++ {
- names := doGlob(t, naming.JoinAddressName(estr, "//"), "*", id)
+ names := doGlob(t, naming.JoinAddressName(estr, "//"), "*", rootRT)
t.Logf("names %v", names)
for _, n := range names {
if n == serverName {
@@ -74,18 +70,18 @@
}
// Make sure we get back a root for the server.
- want, got := []string{""}, doGlob(t, naming.JoinAddressName(estr, "//"+serverName), "", id)
+ want, got := []string{""}, doGlob(t, naming.JoinAddressName(estr, "//"+serverName), "", rootRT)
if !reflect.DeepEqual(want, got) {
t.Errorf("Unexpected Glob result want: %q, got: %q", want, got)
}
// Make sure we can resolve through the neighborhood.
expectedSuffix := "a/b"
- objectPtr, err := mounttable.BindMountTable(naming.JoinAddressName(estr, "//"+serverName+"/"+expectedSuffix), quuxClient(id))
+ objectPtr, err := mounttable.BindMountTable(naming.JoinAddressName(estr, "//"+serverName+"/"+expectedSuffix), quuxClient(rootRT))
if err != nil {
boom(t, "BindMountTable: %s", err)
}
- servers, suffix, err := objectPtr.ResolveStep(r.NewContext())
+ servers, suffix, err := objectPtr.ResolveStep(rootRT.NewContext())
if err != nil {
boom(t, "resolveStep: %s", err)
}
diff --git a/services/mounttable/lib/testdata/test.acl b/services/mounttable/lib/testdata/test.acl
index 9ffe006..597dda9 100644
--- a/services/mounttable/lib/testdata/test.acl
+++ b/services/mounttable/lib/testdata/test.acl
@@ -1,5 +1,5 @@
{
-"/": {"In": {"fake/root": "RW", "...": "R"}},
-"/stuff": {"In": {"fake/root": "RW", "fake/bob": "R"}},
-"/a": {"In": {"fake/root": "RW", "fake/alice": "R"}}
+"/": {"In": {"root": "RW", "...": "R"}},
+"/stuff": {"In": {"root": "RW", "bob": "R"}},
+"/a": {"In": {"root": "RW", "alice": "R"}}
}
\ No newline at end of file
diff --git a/tools/naming/simulator/driver.go b/tools/naming/simulator/driver.go
index 60028b5..db4748e 100644
--- a/tools/naming/simulator/driver.go
+++ b/tools/naming/simulator/driver.go
@@ -108,8 +108,8 @@
shell := modules.NewShell()
defer shell.Cleanup(os.Stderr, os.Stderr)
- if os.Getenv("VEYRON_IDENTITY") == "" {
- shell.CreateAndUseNewID()
+ if os.Getenv("VEYRON_CREDENTIALS") == "" {
+ shell.CreateAndUseNewCredentials()
}
core.Install(shell)
diff --git a/tools/principal/bless.go b/tools/principal/bless.go
new file mode 100644
index 0000000..578cc03
--- /dev/null
+++ b/tools/principal/bless.go
@@ -0,0 +1,135 @@
+package main
+
+import (
+ "crypto/rand"
+ "encoding/base64"
+ "fmt"
+ "html/template"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "os/exec"
+ "strings"
+
+ "veyron.io/veyron/veyron/services/identity/googleoauth"
+ "veyron.io/veyron/veyron2/vlog"
+)
+
+func getMacaroonForBlessRPC(blessServerURL string, blessedChan <-chan string) (<-chan string, error) {
+ // Setup a HTTP server to recieve a blessing macaroon from the identity server.
+ // Steps:
+ // 1. Generate a state token to be included in the HTTP request
+ // (though, arguably, the random port assigment for the HTTP server is enough
+ // for XSRF protection)
+ // 2. Setup a HTTP server which will receive the final blessing macaroon from the id server.
+ // 3. Print out the link (to start the auth flow) for the user to click.
+ // 4. Return the macaroon and the rpc object name(where to make the MacaroonBlesser.Bless RPC call)
+ // in the "result" channel.
+ var stateBuf [32]byte
+ if _, err := rand.Read(stateBuf[:]); err != nil {
+ return nil, fmt.Errorf("failed to generate state token for OAuth: %v", err)
+ }
+ state := base64.URLEncoding.EncodeToString(stateBuf[:])
+
+ ln, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ return nil, fmt.Errorf("failed to setup authorization code interception server: %v", err)
+ }
+ result := make(chan string)
+
+ redirectURL := fmt.Sprintf("http://%s/macaroon", ln.Addr())
+ http.HandleFunc("/macaroon", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html")
+ tmplArgs := struct {
+ Blessings, ErrShort, ErrLong string
+ }{}
+ defer func() {
+ if len(tmplArgs.ErrShort) > 0 {
+ w.WriteHeader(http.StatusBadRequest)
+ }
+ if err := tmpl.Execute(w, tmplArgs); err != nil {
+ vlog.Info("Failed to render template:", err)
+ }
+ }()
+
+ toolState := r.FormValue("state")
+ if toolState != state {
+ tmplArgs.ErrShort = "Unexpected request"
+ tmplArgs.ErrLong = "Mismatched state parameter. Possible cross-site-request-forgery?"
+ return
+ }
+ result <- r.FormValue("macaroon")
+ result <- r.FormValue("object_name")
+ defer close(result)
+ blessed, ok := <-blessedChan
+ if !ok {
+ tmplArgs.ErrShort = "No blessings received"
+ tmplArgs.ErrLong = "Unable to obtain blessings from the Veyron service"
+ return
+ }
+ tmplArgs.Blessings = blessed
+ ln.Close()
+ })
+ go http.Serve(ln, nil)
+
+ // Print the link to start the flow.
+ url, err := seekBlessingsURL(blessServerURL, redirectURL, state)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create seekBlessingsURL: %s", err)
+ }
+ fmt.Fprintln(os.Stderr, "Please visit the following URL to seek blessings:")
+ fmt.Fprintln(os.Stderr, url)
+ // Make an attempt to start the browser as a convenience.
+ // If it fails, doesn't matter - the client can see the URL printed above.
+ // Use exec.Command().Start instead of exec.Command().Run since there is no
+ // need to wait for the command to return (and indeed on some window managers,
+ // the command will not exit until the browser is closed).
+ if len(openCommand) != 0 {
+ exec.Command(openCommand, url).Start()
+ }
+ return result, nil
+}
+
+func seekBlessingsURL(blessServerURL, redirectURL, state string) (string, error) {
+ baseURL, err := url.Parse(joinURL(blessServerURL, googleoauth.SeekBlessingsRoute))
+ if err != nil {
+ return "", fmt.Errorf("failed to parse url: %v", err)
+ }
+ params := url.Values{}
+ params.Add("redirect_url", redirectURL)
+ params.Add("state", state)
+ baseURL.RawQuery = params.Encode()
+ return baseURL.String(), nil
+}
+
+func joinURL(baseURL, suffix string) string {
+ if !strings.HasSuffix(baseURL, "/") {
+ baseURL += "/"
+ }
+ return baseURL + suffix
+}
+
+var tmpl = template.Must(template.New("name").Parse(`<!doctype html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title>Veyron Identity: Google</title>
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
+{{if .Blessings}}
+<!--Attempt to close the window. Though this script does not work on many browser configurations-->
+<script type="text/javascript">window.close();</script>
+{{end}}
+</head>
+<body>
+<div class="container">
+{{if .ErrShort}}
+<h1><span class="label label-danger">error</span>{{.ErrShort}}</h1>
+<div class="well">{{.ErrLong}}</div>
+{{else}}
+<h3>Received blessings: <tt>{{.Blessings}}</tt></h3>
+{{end}}
+</div>
+</body>
+</html>`))
diff --git a/tools/principal/main.go b/tools/principal/main.go
index 51a9181..a0a61c1 100644
--- a/tools/principal/main.go
+++ b/tools/principal/main.go
@@ -10,10 +10,12 @@
"time"
"veyron.io/veyron/veyron/lib/cmdline"
+ "veyron.io/veyron/veyron/services/identity"
"veyron.io/veyron/veyron/services/identity/util"
"veyron.io/veyron/veyron2/rt"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/vdl/vdlutil"
)
var (
@@ -21,6 +23,31 @@
flagBlessFor time.Duration
flagAddForPeer string
+ // Flags for the "seekblessing" command
+ flagSeekBlessingFrom string
+ flagSkipSetDefault bool
+ flagForPeer string
+
+ cmdDump = &cmdline.Command{
+ Name: "dump",
+ Short: "Dump out information about the principal",
+ Long: `
+Dumps out information about the principal specified by the environment
+(VEYRON_CREDENTIALS) that this tool is running in.
+`,
+ Run: func(cmd *cmdline.Command, args []string) error {
+ p := rt.R().Principal()
+ fmt.Printf("Public key : %v\n", p.PublicKey())
+ fmt.Println("")
+ fmt.Println("---------------- BlessingStore ----------------")
+ fmt.Printf("%v", p.BlessingStore().DebugString())
+ fmt.Println("")
+ fmt.Println("---------------- BlessingRoots ----------------")
+ fmt.Printf("%v", p.Roots().DebugString())
+ return nil
+ },
+ }
+
cmdPrint = &cmdline.Command{
Name: "print",
Short: "Print out information about the provided blessing",
@@ -195,6 +222,67 @@
return nil
},
}
+
+ cmdSeekBlessings = &cmdline.Command{
+ Name: "seekblessings",
+ Short: "Seek blessings from a web-based Veyron blesser",
+ Long: `
+Seeks blessings from a web-based Veyron blesser which
+requires the caller to first authenticate with Google using OAuth. Simply
+run the command to see what happens.
+
+The blessings are sought for the principal specified by the environment
+(VEYRON_CREDENTIALS) that this tool is running in.
+
+The blessings obtained are set as default, unless a --skip_set_default flag
+is provided, and are also set for sharing with all peers, unless a more
+specific peer pattern is provided using the --for_peer flag.
+`,
+ Run: func(cmd *cmdline.Command, args []string) error {
+ blessedChan := make(chan string)
+ defer close(blessedChan)
+ macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingFrom, blessedChan)
+ if err != nil {
+ return fmt.Errorf("failed to get macaroon from Veyron blesser: %v", err)
+ }
+ macaroon := <-macaroonChan
+ service := <-macaroonChan
+
+ ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
+ defer cancel()
+
+ var reply vdlutil.Any
+ blesser, err := identity.BindMacaroonBlesser(service)
+ if err == nil {
+ reply, err = blesser.Bless(ctx, macaroon)
+ }
+ if err != nil {
+ return fmt.Errorf("failed to get blessing from %q: %v", service, err)
+ }
+ wire, ok := reply.(security.WireBlessings)
+ if !ok {
+ return fmt.Errorf("received %T, want security.WireBlessings", reply)
+ }
+ blessings, err := security.NewBlessings(wire)
+ if err != nil {
+ return fmt.Errorf("failed to construct Blessings object from wire data: %v", err)
+ }
+ blessedChan <- fmt.Sprint(blessings)
+ // Wait for getTokenForBlessRPC to clean up:
+ <-macaroonChan
+
+ if !flagSkipSetDefault {
+ if err := rt.R().Principal().BlessingStore().SetDefault(blessings); err != nil {
+ return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
+ }
+ }
+ pattern := security.BlessingPattern(flagForPeer)
+ if _, err := rt.R().Principal().BlessingStore().Set(blessings, pattern); err != nil {
+ return fmt.Errorf("failed to set blessings %v for peers %v: %v", blessings, pattern, err)
+ }
+ return dumpBlessings(blessings)
+ },
+ }
)
func main() {
@@ -205,6 +293,9 @@
}
rt.Init()
cmdBlessSelf.Flags.DurationVar(&flagBlessFor, "for", 0*time.Hour, "Expiry time of Blessing (optional)")
+ cmdSeekBlessings.Flags.StringVar(&flagSeekBlessingFrom, "from", "https://proxy.envyor.com:8125/google", "URL to use to begin the seek blessings process")
+ cmdSeekBlessings.Flags.BoolVar(&flagSkipSetDefault, "skip_set_default", false, "flag to indicate that the blessings obtained from the Veyron blesser must not be set as default on the principals's blessing store")
+ cmdSeekBlessings.Flags.StringVar(&flagForPeer, "for_peer", "...", "pattern to be used while setting the blessings obtained from the Veyron blesser on the principal's blessing store")
(&cmdline.Command{
Name: "principal",
@@ -215,7 +306,7 @@
All objects are printed using base64-VOM-encoding.
`,
- Children: []*cmdline.Command{cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer, cmdSetDefault, cmdSet},
+ Children: []*cmdline.Command{cmdDump, cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer, cmdSetDefault, cmdSet, cmdSeekBlessings},
}).Main()
}
diff --git a/tools/principal/main_darwin.go b/tools/principal/main_darwin.go
new file mode 100644
index 0000000..bceafd2
--- /dev/null
+++ b/tools/principal/main_darwin.go
@@ -0,0 +1,5 @@
+// +build darwin
+
+package main
+
+const openCommand = "open"
diff --git a/tools/principal/main_linux.go b/tools/principal/main_linux.go
new file mode 100644
index 0000000..cb73c65
--- /dev/null
+++ b/tools/principal/main_linux.go
@@ -0,0 +1,5 @@
+// +build linux
+
+package main
+
+const openCommand = "xdg-open"
diff --git a/tools/principal/main_nacl.go b/tools/principal/main_nacl.go
new file mode 100644
index 0000000..9dea3b5
--- /dev/null
+++ b/tools/principal/main_nacl.go
@@ -0,0 +1,3 @@
+package main
+
+const openCommand = ""
diff --git a/tools/principal/test.sh b/tools/principal/test.sh
index 3041116..770abb3 100755
--- a/tools/principal/test.sh
+++ b/tools/principal/test.sh
@@ -3,6 +3,8 @@
# Test the principal command-line tool.
#
# This tests most operations of the principal command-line tool.
+# Not the "seekblessing" command yet, since that requires
+# starting a separate server.
source "${VEYRON_ROOT}/scripts/lib/shell_test.sh"
@@ -23,10 +25,11 @@
# Set VEYRON_CREDENTIALS.
export VEYRON_CREDENTIALS="${WORKDIR}"
- ./principal store.default >/dev/null || shell_test::fail "line ${LINENO}: store.default failed"
- ./principal store.forpeer >/dev/null || shell_test::fail "line ${LINENO}: store.forpeer failed"
+ ./principal dump >/dev/null || shell_test::fail "line ${LINENO}: dump failed"
./principal blessself >/dev/null || shell_test::fail "line ${LINENO}: blessself failed"
./principal blessself alice >alice || shell_test::fail "line ${LINENO}: blessself alice failed"
+ ./principal store.default >/dev/null || shell_test::fail "line ${LINENO}: store.default failed"
+ ./principal store.forpeer >/dev/null || shell_test::fail "line ${LINENO}: store.forpeer failed"
# Test print
local GOT=$(./principal print alice | extractBlessings)