"veyron2": Principal in Runtime
Initialize a security.Principal in the runtime during rt.Init()
If the "VEYRON_CREDENTIALS" environment variable is set to a
directory then the principal gets intialized from the specified
directory and all subsequent mutations tp the principal's state
get saved to the directory. Otherwise, an in-memory principal
object is created.
It is okay for "VEYRON_CREDENTIALS" to point to an empty directory,
in which case a new principal object is created and then saved to
the directory.
Change-Id: I2558626a6ec8134d2b99c5a13e9a1182b3787923
diff --git a/runtimes/google/rt/blessingroots.go b/runtimes/google/rt/blessingroots.go
index a457b7a..62f3026 100644
--- a/runtimes/google/rt/blessingroots.go
+++ b/runtimes/google/rt/blessingroots.go
@@ -78,16 +78,16 @@
return encodeAndStore(br.store, br.dir, blessingRootsDataFile, blessingRootsSigFile, br.signer)
}
-// NewInMemoryBlessingRoots returns an in-memory security.BlessingRoots.
+// newInMemoryBlessingRoots returns an in-memory security.BlessingRoots.
//
// The returned BlessingRoots is initialized with an empty set of keys.
-func NewInMemoryBlessingRoots() security.BlessingRoots {
+func newInMemoryBlessingRoots() security.BlessingRoots {
return &blessingRoots{
store: make(map[string][]security.BlessingPattern),
}
}
-// NewPersistingBlessingRoots returns a security.BlessingRoots that signs
+// newPersistingBlessingRoots returns a security.BlessingRoots that signs
// and persists all updates to the provided directory. Signing is carried
// out using the provided signer.
//
@@ -96,7 +96,7 @@
// BlessingRoots object constructed from the same signer.
//
// Any errors obtained in reading or verifying the data are returned.
-func NewPersistingBlessingRoots(directory string, signer serialization.Signer) (security.BlessingRoots, error) {
+func newPersistingBlessingRoots(directory string, signer serialization.Signer) (security.BlessingRoots, error) {
if directory == "" || signer == nil {
return nil, errors.New("directory or signer is not specified")
}
diff --git a/runtimes/google/rt/blessingroots_test.go b/runtimes/google/rt/blessingroots_test.go
index 2ce703c..53b99de 100644
--- a/runtimes/google/rt/blessingroots_test.go
+++ b/runtimes/google/rt/blessingroots_test.go
@@ -69,7 +69,7 @@
}
func TestInMemoryBlessingRoots(t *testing.T) {
- br := NewInMemoryBlessingRoots()
+ br := newInMemoryBlessingRoots()
rootsTester := rootsTester{mkKey(), mkKey(), mkKey()}
if err := rootsTester.testAdd(br); err != nil {
t.Error(err)
@@ -95,9 +95,9 @@
dir := newTempDir("blessingstore")
defer os.RemoveAll(dir)
signer := newPrincipal(t)
- br, err := NewPersistingBlessingRoots(dir, signer)
+ br, err := newPersistingBlessingRoots(dir, signer)
if err != nil {
- t.Fatalf("NewPersistingBlessingRoots failed: %s", err)
+ t.Fatalf("newPersistingBlessingRoots failed: %s", err)
}
if err := rootsTester.testAdd(br); err != nil {
@@ -109,9 +109,9 @@
// Test that all mutations are appropriately reflected in a BlessingRoots
// constructed from same directory and signer.
- br, err = NewPersistingBlessingRoots(dir, signer)
+ br, err = newPersistingBlessingRoots(dir, signer)
if err != nil {
- t.Fatalf("NewPersistingBlessingRoots failed: %s", err)
+ t.Fatalf("newPersistingBlessingRoots failed: %s", err)
}
if err := rootsTester.testRecognized(br); err != nil {
t.Error(err)
diff --git a/runtimes/google/rt/blessingstore.go b/runtimes/google/rt/blessingstore.go
index d095530..30b14fe 100644
--- a/runtimes/google/rt/blessingstore.go
+++ b/runtimes/google/rt/blessingstore.go
@@ -102,7 +102,7 @@
blessings, err := security.UnionOfBlessings(matchingBlessings...)
if err != nil {
// This case should never be hit.
- vlog.Errorf("BlessingStore: %s is broken, could union Blessings obtained from it: %s", s, err)
+ vlog.Errorf("BlessingStore: %s is broken, could not union Blessings obtained from it: %s", s, err)
return nil
}
return blessings
@@ -146,17 +146,17 @@
return encodeAndStore(s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer)
}
-// NewInMemoryBlessingStore returns an in-memory security.BlessingStore for a
+// newInMemoryBlessingStore returns an in-memory security.BlessingStore for a
// principal with the provided PublicKey.
//
// The returned BlessingStore is initialized with an empty set of blessings.
-func NewInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore {
+func newInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore {
return &blessingStore{
publicKey: publicKey,
}
}
-// NewPersistingBlessingStore returns a security.BlessingStore for a principal
+// newPersistingBlessingStore returns a security.BlessingStore for a principal
// with the provided PublicKey that signs and persists all updates to the
// specified directory. Signing is carried out using the provided signer.
//
@@ -165,7 +165,7 @@
// BlessingStore object constructed from the same PublicKey and signer.
//
// Any errors obtained in reading or verifying the data are returned.
-func NewPersistingBlessingStore(publicKey security.PublicKey, directory string, signer serialization.Signer) (security.BlessingStore, error) {
+func newPersistingBlessingStore(publicKey security.PublicKey, directory string, signer serialization.Signer) (security.BlessingStore, error) {
if directory == "" || signer == nil {
return nil, errors.New("directory or signer is not specified")
}
diff --git a/runtimes/google/rt/blessingstore_test.go b/runtimes/google/rt/blessingstore_test.go
index 6699f58..096fb19 100644
--- a/runtimes/google/rt/blessingstore_test.go
+++ b/runtimes/google/rt/blessingstore_test.go
@@ -113,7 +113,7 @@
func TestInMemoryBlessingStore(t *testing.T) {
tester, pkey := newStoreTester(t)
- s := NewInMemoryBlessingStore(pkey)
+ s := newInMemoryBlessingStore(pkey)
if err := tester.testAdd(s); err != nil {
t.Error(err)
}
@@ -140,9 +140,9 @@
dir := newTempDir("blessingstore")
defer os.RemoveAll(dir)
signer := newPrincipal(t)
- s, err := NewPersistingBlessingStore(pkey, dir, signer)
+ s, err := newPersistingBlessingStore(pkey, dir, signer)
if err != nil {
- t.Fatalf("NewPersistingBlessingStore failed: %v", err)
+ t.Fatalf("newPersistingBlessingStore failed: %v", err)
}
if err := tester.testAdd(s); err != nil {
@@ -156,9 +156,9 @@
}
// Test that all mutations are appropriately reflected in a BlessingStore constructed
// from same public key, directory and signer.
- s, err = NewPersistingBlessingStore(pkey, dir, signer)
+ s, err = newPersistingBlessingStore(pkey, dir, signer)
if err != nil {
- t.Fatalf("NewPersistingBlessingStore failed: %v", err)
+ t.Fatalf("newPersistingBlessingStore failed: %v", err)
}
if err := tester.testForPeer(s); err != nil {
t.Error(err)
@@ -193,7 +193,7 @@
pkey = p.PublicKey()
)
- s := NewInMemoryBlessingStore(pkey)
+ s := newInMemoryBlessingStore(pkey)
add(s, alice, "...")
add(s, roundTrip(alice), "...")
diff --git a/runtimes/google/rt/rt.go b/runtimes/google/rt/rt.go
index bffc6fa..cb0bda2 100644
--- a/runtimes/google/rt/rt.go
+++ b/runtimes/google/rt/rt.go
@@ -32,6 +32,7 @@
ns naming.Namespace
signals chan os.Signal
id security.PrivateID
+ principal security.Principal
store security.PublicIDStore
client ipc.Client
mgmt *mgmtImpl
diff --git a/runtimes/google/rt/rt_test.go b/runtimes/google/rt/rt_test.go
index 9e4866d..53d3512 100644
--- a/runtimes/google/rt/rt_test.go
+++ b/runtimes/google/rt/rt_test.go
@@ -2,17 +2,40 @@
import (
"fmt"
+ "io/ioutil"
"os"
+ "reflect"
"regexp"
"testing"
_ "veyron.io/veyron/veyron/lib/testutil"
"veyron.io/veyron/veyron/lib/testutil/blackbox"
+ irt "veyron.io/veyron/veyron/runtimes/google/rt"
+ "veyron.io/veyron/veyron2"
+ "veyron.io/veyron/veyron2/naming"
"veyron.io/veyron/veyron2/rt"
+ "veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vlog"
)
+type context struct {
+ local security.Principal
+}
+
+func (*context) Method() string { return "" }
+func (*context) Name() string { return "" }
+func (*context) Suffix() string { return "" }
+func (*context) Label() (l security.Label) { return }
+func (*context) Discharges() map[string]security.Discharge { return nil }
+func (*context) LocalID() security.PublicID { return nil }
+func (*context) RemoteID() security.PublicID { return nil }
+func (c *context) LocalPrincipal() security.Principal { return c.local }
+func (*context) LocalBlessings() security.Blessings { return nil }
+func (*context) RemoteBlessings() security.Blessings { return nil }
+func (*context) LocalEndpoint() naming.Endpoint { return nil }
+func (*context) RemoteEndpoint() naming.Endpoint { return nil }
+
func init() {
blackbox.CommandTable["child"] = child
}
@@ -72,3 +95,67 @@
c.Expect("done")
c.ExpectEOFAndWait()
}
+
+func TestInitPrincipal(t *testing.T) {
+ newRT := func() veyron2.Runtime {
+ r, err := rt.New()
+ if err != nil {
+ t.Fatalf("rt.New failed: %v", err)
+ }
+ return r
+ }
+ testPrincipal := func(r veyron2.Runtime) security.Principal {
+ p := r.Principal()
+ if p == nil {
+ t.Fatalf("rt.Principal() returned nil")
+ }
+ blessings := p.BlessingStore().Default()
+ if blessings == nil {
+ t.Fatalf("rt.Principal().BlessingStore().Default() returned nil")
+
+ }
+ if n := len(blessings.ForContext(&context{local: p})); n != 1 {
+ t.Fatalf("rt.Principal().BlessingStore().Default() returned Blessing %v with %d recognized blessings, want exactly one recognized blessing", blessings, n)
+ }
+ return p
+ }
+ origCredentialsDir := os.Getenv(irt.VeyronCredentialsEnvVar)
+ defer os.Setenv(irt.VeyronCredentialsEnvVar, origCredentialsDir)
+
+ // Test that even with VEYRON_CREDENTIALS unset the runtime's Principal
+ // is correctly initialized.
+ if err := os.Setenv(irt.VeyronCredentialsEnvVar, ""); err != nil {
+ t.Fatal(err)
+ }
+ testPrincipal(newRT())
+
+ // Test that with VEYRON_CREDENTIALS set the runtime's Principal is correctly
+ // initialized.
+ credentials, err := ioutil.TempDir("", "credentials")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(credentials)
+ if err := os.Setenv(irt.VeyronCredentialsEnvVar, credentials); err != nil {
+ t.Fatal(err)
+ }
+ p := testPrincipal(newRT())
+
+ // Mutate the roots and store of this principal.
+ blessing, err := p.BlessSelf("irrelevant")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := p.BlessingStore().Add(blessing, security.AllPrincipals); err != nil {
+ t.Fatal(err)
+ }
+ if err := p.AddToRoots(blessing); err != nil {
+ t.Fatal(err)
+ }
+
+ // Test that the same principal gets initialized on creating a new runtime
+ // from the same credentials directory.
+ if got := newRT().Principal(); !reflect.DeepEqual(got, p) {
+ t.Fatalf("Initialized Principal: %v, expected: %v", got, p)
+ }
+}
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index c4d8f80..e402cee 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -1,9 +1,13 @@
package rt
import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
"fmt"
"os"
"os/user"
+ "path"
"strconv"
isecurity "veyron.io/veyron/veyron/runtimes/google/security"
@@ -15,6 +19,15 @@
"veyron.io/veyron/veyron2/vlog"
)
+const (
+ privateKeyFile = "privatekey.pem"
+ VeyronCredentialsEnvVar = "VEYRON_CREDENTIALS"
+)
+
+func (rt *vrt) Principal() security.Principal {
+ return rt.principal
+}
+
func (rt *vrt) NewIdentity(name string) (security.PrivateID, error) {
return isecurity.NewPrivateID(name, nil)
}
@@ -28,6 +41,81 @@
}
func (rt *vrt) initSecurity() error {
+ if err := rt.initOldSecurity(); err != nil {
+ return err
+ }
+ if err := rt.initPrincipal(); err != nil {
+ return fmt.Errorf("principal initialization failed: %v", err)
+ }
+ if err := rt.initDefaultBlessings(); err != nil {
+ return fmt.Errorf("default blessing initialization failed: %v", err)
+ }
+ return nil
+}
+
+func (rt *vrt) initPrincipal() error {
+ // TODO(ataly, ashankar): Check if agent environment variables are
+ // specified and if so initialize principal from agent.
+
+ if dir := os.Getenv(VeyronCredentialsEnvVar); len(dir) > 0 {
+ // TODO(ataly, ashankar): If multiple runtimes are getting
+ // initialized at the same time from the same VEYRON_CREDENTIALS
+ // we will need some kind of locking for the credential files.
+ return rt.initPrincipalFromCredentials(dir)
+ }
+ return rt.initTemporaryPrincipal()
+}
+
+func (rt *vrt) initDefaultBlessings() error {
+ if rt.principal.BlessingStore().Default() != nil {
+ return nil
+ }
+ blessing, err := rt.principal.BlessSelf(defaultBlessingName())
+ if err != nil {
+ return err
+ }
+ if err := rt.principal.BlessingStore().SetDefault(blessing); err != nil {
+ return err
+ }
+ if err := rt.principal.AddToRoots(blessing); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (rt *vrt) initPrincipalFromCredentials(dir string) error {
+ key, err := initKey(dir)
+ if err != nil {
+ return fmt.Errorf("could not initialize ECDSA private key from credentials directory %v: %v", dir, err)
+ }
+
+ signer := security.NewInMemoryECDSASigner(key)
+ store, roots, err := initStoreAndRootsFromCredentials(dir, signer)
+ if err != nil {
+ return fmt.Errorf("could not initialize BlessingStore and BlessingRoots from credentials directory %v: %v", dir, err)
+ }
+
+ if rt.principal, err = security.CreatePrincipal(signer, store, roots); err != nil {
+ return fmt.Errorf("could not create Principal object: %v", err)
+ }
+ return nil
+}
+
+func (rt *vrt) initTemporaryPrincipal() error {
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ return err
+ }
+ signer := security.NewInMemoryECDSASigner(key)
+ if rt.principal, err = security.CreatePrincipal(signer, newInMemoryBlessingStore(signer.PublicKey()), newInMemoryBlessingRoots()); err != nil {
+ return fmt.Errorf("could not create Principal object: %v", err)
+ }
+ return nil
+}
+
+// TODO(ataly, ashankar): Get rid of this method once we get rid of
+// PrivateID and PublicIDStore.
+func (rt *vrt) initOldSecurity() error {
if err := rt.initIdentity(); err != nil {
return err
}
@@ -61,7 +149,7 @@
return fmt.Errorf("Could not load identity from the VEYRON_IDENTITY environment variable (%q): %v", file, err)
}
} else {
- name := defaultIdentityName()
+ name := defaultBlessingName()
vlog.VI(2).Infof("No identity provided to the runtime, minting one for %q", name)
if rt.id, err = rt.NewIdentity(name); err != nil || rt.id == nil {
return fmt.Errorf("Could not create new identity: %v", err)
@@ -88,7 +176,7 @@
return nil
}
-func defaultIdentityName() string {
+func defaultBlessingName() string {
var name string
if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
name = user.Username
@@ -132,3 +220,49 @@
}
return isecurity.NewPrivateID("selfSigned", signer)
}
+
+func initKey(dir string) (*ecdsa.PrivateKey, error) {
+ keyPath := path.Join(dir, privateKeyFile)
+ if f, err := os.Open(keyPath); err == nil {
+ defer f.Close()
+ v, err := vsecurity.LoadPEMKey(f)
+ if err != nil {
+ return nil, err
+ }
+ key, ok := v.(*ecdsa.PrivateKey)
+ if !ok {
+ return nil, fmt.Errorf("could not read ECDSA private key from data of type %T", v)
+ }
+ return key, nil
+ } else if !os.IsNotExist(err) {
+ return nil, err
+ }
+
+ key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ f, err := os.OpenFile(keyPath, os.O_WRONLY|os.O_CREATE, 0600)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+ return key, vsecurity.SavePEMKey(f, key)
+}
+
+func initStoreAndRootsFromCredentials(dir string, secsigner security.Signer) (security.BlessingStore, security.BlessingRoots, error) {
+ signer, err := security.CreatePrincipal(secsigner, nil, nil)
+ if err != nil {
+ return nil, nil, err
+ }
+ store, err := newPersistingBlessingStore(signer.PublicKey(), dir, signer)
+ if err != nil {
+ return nil, nil, err
+ }
+ roots, err := newPersistingBlessingRoots(dir, signer)
+ if err != nil {
+ return nil, nil, err
+ }
+ return store, roots, nil
+}
diff --git a/runtimes/google/rt/util_test.go b/runtimes/google/rt/util_test.go
index 7974fbe..bff9d50 100644
--- a/runtimes/google/rt/util_test.go
+++ b/runtimes/google/rt/util_test.go
@@ -30,7 +30,7 @@
t.Fatalf("Failed to create private key for principal: %v", err)
}
signer := security.NewInMemoryECDSASigner(key)
- p, err := security.CreatePrincipal(signer, NewInMemoryBlessingStore(signer.PublicKey()), NewInMemoryBlessingRoots())
+ p, err := security.CreatePrincipal(signer, newInMemoryBlessingStore(signer.PublicKey()), newInMemoryBlessingRoots())
if err != nil {
t.Fatalf("security.CreatePrincipal failed: %v", err)
}
diff --git a/runtimes/google/testing/mocks/runtime/panic_runtime.go b/runtimes/google/testing/mocks/runtime/panic_runtime.go
index 03a1fcb..e0b1294 100644
--- a/runtimes/google/testing/mocks/runtime/panic_runtime.go
+++ b/runtimes/google/testing/mocks/runtime/panic_runtime.go
@@ -26,6 +26,7 @@
func (*PanicRuntime) NewIdentity(name string) (security.PrivateID, error) { panic(badRuntime) }
func (*PanicRuntime) PublicIDStore() security.PublicIDStore { panic(badRuntime) }
func (*PanicRuntime) Identity() security.PrivateID { panic(badRuntime) }
+func (*PanicRuntime) Principal() security.Principal { panic(badRuntime) }
func (*PanicRuntime) NewClient(opts ...ipc.ClientOpt) (ipc.Client, error) { panic(badRuntime) }
func (*PanicRuntime) NewServer(opts ...ipc.ServerOpt) (ipc.Server, error) { panic(badRuntime) }
func (*PanicRuntime) Client() ipc.Client { panic(badRuntime) }