veyron/lib/modules: Use the agent for managing credentials in modules.Shell.
Change-Id: I17737af8d4fe2281d2f0e5a267cbff268758f788
diff --git a/lib/filelocker/locker_test.go b/lib/filelocker/locker_test.go
index 4e96ef9..b58bfe2 100644
--- a/lib/filelocker/locker_test.go
+++ b/lib/filelocker/locker_test.go
@@ -63,7 +63,7 @@
filepath := newFile()
defer os.Remove(filepath)
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -136,7 +136,7 @@
filepath := newFile()
defer os.Remove(filepath)
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/lib/modules/core/core_test.go b/lib/modules/core/core_test.go
index b314ad3..64eeabb 100644
--- a/lib/modules/core/core_test.go
+++ b/lib/modules/core/core_test.go
@@ -18,6 +18,7 @@
"v.io/core/veyron/lib/modules/core"
"v.io/core/veyron/lib/testutil"
_ "v.io/core/veyron/profiles"
+ "v.io/core/veyron2/rt"
)
func TestCommands(t *testing.T) {
@@ -37,7 +38,11 @@
// TODO(cnicolaou): add test for proxyd
func newShell(t *testing.T) (*modules.Shell, func()) {
- sh, err := modules.NewShell(nil)
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -48,6 +53,7 @@
} else {
sh.Cleanup(nil, nil)
}
+ runtime.Cleanup()
}
}
@@ -193,7 +199,6 @@
}
cltSession := expect.NewSession(t, clt.Stdout(), time.Minute)
cltSession.Expect("test: a message")
- srv.Shutdown(nil, nil)
}
func TestExec(t *testing.T) {
diff --git a/lib/modules/examples_test.go b/lib/modules/examples_test.go
index 2154214..ee78224 100644
--- a/lib/modules/examples_test.go
+++ b/lib/modules/examples_test.go
@@ -28,7 +28,7 @@
return
}
// Parent process.
- sh, _ := modules.NewShell(nil)
+ sh, _ := modules.NewShell(runtime.NewContext(), nil)
defer sh.Cleanup(nil, nil)
h, _ := sh.Start("echo", nil, "a", "b")
h.Shutdown(os.Stdout, os.Stderr)
@@ -41,7 +41,7 @@
func ExampleDispatchAndExit() {
// DispatchAndExit will call os.Exit(0) when executed within the child.
modules.DispatchAndExit()
- sh, _ := modules.NewShell(nil)
+ sh, _ := modules.NewShell(runtime.NewContext(), nil)
defer sh.Cleanup(nil, nil)
h, _ := sh.Start("echo", nil, "c", "d")
h.Shutdown(os.Stdout, os.Stderr)
diff --git a/lib/modules/exec.go b/lib/modules/exec.go
index 351af5f..b7e77c0 100644
--- a/lib/modules/exec.go
+++ b/lib/modules/exec.go
@@ -5,11 +5,13 @@
"io"
"os"
"os/exec"
+ "strconv"
"strings"
"sync"
"time"
vexec "v.io/core/veyron/lib/exec"
+ "v.io/core/veyron2/mgmt"
"v.io/core/veyron2/vlog"
)
@@ -108,7 +110,7 @@
return newargs, append(cleaned, eh.entryPoint)
}
-func (eh *execHandle) start(sh *Shell, env []string, args ...string) (Handle, error) {
+func (eh *execHandle) start(sh *Shell, agentfd *os.File, env []string, args ...string) (Handle, error) {
eh.mu.Lock()
defer eh.mu.Unlock()
eh.sh = sh
@@ -132,8 +134,20 @@
if err != nil {
return nil, err
}
+ config := vexec.NewConfig()
+ serialized, err := sh.config.Serialize()
+ if err != nil {
+ return nil, err
+ }
+ config.MergeFrom(serialized)
+ if agentfd != nil {
+ childfd := len(cmd.ExtraFiles) + vexec.FileOffset
+ config.Set(mgmt.SecurityAgentFDConfigKey, strconv.Itoa(childfd))
+ cmd.ExtraFiles = append(cmd.ExtraFiles, agentfd)
+ defer agentfd.Close()
+ }
- handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{Config: sh.config})
+ handle := vexec.NewParentHandle(cmd, vexec.ConfigOpt{Config: config})
eh.stdout = stdout
eh.stderr = stderr
eh.stdin = stdin
diff --git a/lib/modules/func.go b/lib/modules/func.go
index 4103fae..e7372e6 100644
--- a/lib/modules/func.go
+++ b/lib/modules/func.go
@@ -55,9 +55,13 @@
return args, env
}
-func (fh *functionHandle) start(sh *Shell, env []string, args ...string) (Handle, error) {
+func (fh *functionHandle) start(sh *Shell, agent *os.File, env []string, args ...string) (Handle, error) {
fh.mu.Lock()
defer fh.mu.Unlock()
+ // In process commands need their own reference to a principal.
+ if agent != nil {
+ agent.Close()
+ }
fh.sh = sh
for _, p := range []*pipe{&fh.stdin, &fh.stdout} {
var err error
diff --git a/lib/modules/modules_internal_test.go b/lib/modules/modules_internal_test.go
index b8229e3..3c26937 100644
--- a/lib/modules/modules_internal_test.go
+++ b/lib/modules/modules_internal_test.go
@@ -6,6 +6,10 @@
"path/filepath"
"runtime"
"testing"
+
+ _ "v.io/core/veyron/profiles"
+
+ "v.io/core/veyron2/rt"
)
func Echo(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
@@ -26,7 +30,12 @@
}
func TestState(t *testing.T) {
- sh, err := NewShell(nil)
+ r, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer r.Cleanup()
+ sh, err := NewShell(r.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/lib/modules/modules_test.go b/lib/modules/modules_test.go
index 6915d68..1f4e311 100644
--- a/lib/modules/modules_test.go
+++ b/lib/modules/modules_test.go
@@ -3,7 +3,6 @@
import (
"bufio"
"bytes"
- "errors"
"fmt"
"io"
"os"
@@ -18,19 +17,22 @@
"v.io/core/veyron/lib/flags/consts"
"v.io/core/veyron/lib/modules"
"v.io/core/veyron/lib/testutil"
- tsecurity "v.io/core/veyron/lib/testutil/security"
+ "v.io/core/veyron/lib/testutil/security"
_ "v.io/core/veyron/profiles"
- vsecurity "v.io/core/veyron/security"
- "v.io/core/veyron2/security"
+ "v.io/core/veyron2"
+ "v.io/core/veyron2/rt"
)
const credentialsEnvPrefix = "\"" + consts.VeyronCredentials + "="
+var runtime veyron2.Runtime
+
func init() {
testutil.Init()
modules.RegisterChild("envtest", "envtest: <variables to print>...", PrintFromEnv)
modules.RegisterChild("printenv", "printenv", PrintEnv)
+ modules.RegisterChild("printblessing", "printblessing", PrintBlessing)
modules.RegisterChild("echos", "[args]*", Echo)
modules.RegisterChild("errortestChild", "", ErrorMain)
modules.RegisterChild("ignores_stdin", "", ignoresStdin)
@@ -38,6 +40,11 @@
modules.RegisterFunction("envtestf", "envtest: <variables to print>...", PrintFromEnv)
modules.RegisterFunction("echof", "[args]*", Echo)
modules.RegisterFunction("errortestFunc", "", ErrorMain)
+ var err error
+ runtime, err = rt.New()
+ if err != nil {
+ panic(err)
+ }
}
func ignoresStdin(io.Reader, io.Writer, io.Writer, map[string]string, ...string) error {
@@ -53,6 +60,12 @@
return nil
}
+func PrintBlessing(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
+ blessing := veyron2.GetPrincipal(runtime.NewContext()).BlessingStore().Default()
+ fmt.Fprintf(stdout, "%s", blessing)
+ return nil
+}
+
func PrintFromEnv(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error {
for _, a := range args[1:] {
if v := env[a]; len(v) > 0 {
@@ -132,8 +145,21 @@
}
}
+func getBlessing(t *testing.T, sh *modules.Shell, env ...string) string {
+ h, err := sh.Start("printblessing", env)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ scanner := bufio.NewScanner(h.Stdout())
+ if !waitForInput(scanner) {
+ t.Errorf("timeout")
+ return ""
+ }
+ return scanner.Text()
+}
+
func TestChild(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -143,8 +169,62 @@
testCommand(t, sh, "envtest", key, val)
}
+func TestAgent(t *testing.T) {
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(os.Stdout, os.Stderr)
+ a := getBlessing(t, sh)
+ b := getBlessing(t, sh)
+ if a != b {
+ t.Errorf("Expected same blessing for children, got %s and %s", a, b)
+ }
+ sh2, err := modules.NewShell(runtime.NewContext(), nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh2.Cleanup(os.Stdout, os.Stderr)
+ c := getBlessing(t, sh2)
+ if a == c {
+ t.Errorf("Expected different blessing for each shell, got %s and %s", a, c)
+ }
+}
+
+func TestCustomPrincipal(t *testing.T) {
+ p := security.NewPrincipal("myshell")
+ cleanDebug := p.BlessingStore().DebugString()
+ sh, err := modules.NewShell(runtime.NewContext(), p)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(os.Stdout, os.Stderr)
+ blessing := getBlessing(t, sh)
+ if blessing != "myshell/child" {
+ t.Errorf("Bad blessing. Expected myshell/child, go %q", blessing)
+ }
+ newDebug := p.BlessingStore().DebugString()
+ if cleanDebug != newDebug {
+ t.Errorf("Shell modified custom principal. Was:\n%q\nNow:\n%q", cleanDebug, newDebug)
+ }
+}
+
+func TestNoAgent(t *testing.T) {
+ creds, _ := security.NewCredentials("noagent")
+ defer os.RemoveAll(creds)
+ sh, err := modules.NewShell(nil, nil)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(os.Stdout, os.Stderr)
+ blessing := getBlessing(t, sh, fmt.Sprintf("VEYRON_CREDENTIALS=%s", creds))
+ if blessing != "noagent" {
+ t.Errorf("Bad blessing. Expected noagent, go %q", blessing)
+ }
+}
+
func TestChildNoRegistration(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -159,7 +239,7 @@
}
func TestFunction(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -170,7 +250,7 @@
}
func TestErrorChild(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -210,7 +290,7 @@
}
func TestShutdownSubprocess(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -222,7 +302,7 @@
// forever if a child does not die upon closing stdin; but instead times out and
// returns an appropriate error.
func TestShutdownSubprocessIgnoresStdin(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -247,7 +327,7 @@
// implementation inappropriately sets stdout to the file that is to be closed
// in Wait.
func TestStdoutRace(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -279,7 +359,7 @@
}
func TestShutdownFunction(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -288,7 +368,7 @@
}
func TestErrorFunc(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -312,7 +392,7 @@
}
func TestEnvelope(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -347,146 +427,24 @@
}
}
- foundVeyronCredentials := false
for _, want := range shEnv {
- if strings.HasPrefix(want, credentialsEnvPrefix) {
- foundVeyronCredentials = true
- continue
- }
if !find(want, childEnv) {
t.Errorf("failed to find %s in %#v", want, childEnv)
}
}
- if !foundVeyronCredentials {
- t.Errorf("%v environment variable not set in command envelope", consts.VeyronCredentials)
- }
- foundVeyronCredentials = false
for _, want := range childEnv {
if want == "\""+exec.VersionVariable+"=\"" {
continue
}
- if strings.HasPrefix(want, credentialsEnvPrefix) {
- foundVeyronCredentials = true
- continue
- }
if !find(want, shEnv) {
t.Errorf("failed to find %s in %#v", want, shEnv)
}
}
- if !foundVeyronCredentials {
- t.Errorf("%v environment variable not set for command", consts.VeyronCredentials)
- }
-}
-
-func TestEnvCredentials(t *testing.T) {
- startChildAndGetCredentials := func(sh *modules.Shell, env []string) string {
- h, err := sh.Start("printenv", env)
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- defer h.Shutdown(os.Stderr, os.Stderr)
- scanner := bufio.NewScanner(h.Stdout())
- for scanner.Scan() {
- o := scanner.Text()
- if strings.HasPrefix(o, credentialsEnvPrefix) {
- return strings.TrimSuffix(strings.TrimPrefix(o, credentialsEnvPrefix), "\"")
- }
- }
- return ""
- }
- validateCredentials := func(dir string, wantBlessing string) error {
- if len(dir) == 0 {
- return errors.New("credentials directory not found")
- }
- p, err := vsecurity.LoadPersistentPrincipal(dir, nil)
- if err != nil {
- return err
- }
- def := p.BlessingStore().Default()
- if blessingsSharedWithAll := p.BlessingStore().ForPeer("random_peer"); !reflect.DeepEqual(blessingsSharedWithAll, def) {
- return fmt.Errorf("Blessing shared with all peers: %v is different from default Blessings: %v", blessingsSharedWithAll, def)
- }
- if got := def.ForContext(security.NewContext(&security.ContextParams{LocalPrincipal: p})); len(got) != 1 || got[0] != wantBlessing {
- return fmt.Errorf("got blessings: %v, want exactly one blessing: %v", got, wantBlessing)
- }
- return nil
- }
-
- // Test child credentials when runtime is not initialized and VeyronCredentials
- // is not set.
- sh, err := modules.NewShell(nil)
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- if err := validateCredentials(startChildAndGetCredentials(sh, nil), "test-shell/child"); err != nil {
- t.Fatal(err)
- }
-
- // Test that no two children have the same credentials directory.
- if cred1, cred2 := startChildAndGetCredentials(sh, nil), startChildAndGetCredentials(sh, nil); cred1 == cred2 {
- t.Fatalf("The same credentials directory %v was set for two children", cred1)
- }
- sh.Cleanup(nil, nil)
-
- // Test child credentials when VeyronCredentials are set.
- old := os.Getenv(consts.VeyronCredentials)
- defer os.Setenv(consts.VeyronCredentials, old)
- dir, _ := tsecurity.NewCredentials("os")
- defer os.RemoveAll(dir)
- if err := os.Setenv(consts.VeyronCredentials, dir); err != nil {
- t.Fatal(err)
- }
- sh, err = modules.NewShell(nil)
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- if err := validateCredentials(startChildAndGetCredentials(sh, nil), "os/child"); err != nil {
- t.Fatal(err)
- }
- sh.Cleanup(nil, nil)
-
- // Test that os VeyronCredentials are not deleted by the Cleanup.
- if finfo, err := os.Stat(dir); err != nil || !finfo.IsDir() {
- t.Fatalf("%q is not a directory", dir)
- }
-
- // Test that VeyronCredentials specified on the shell override the OS ones.
- dir, _ = tsecurity.NewCredentials("shell")
- defer os.RemoveAll(dir)
- sh, err = modules.NewShell(nil)
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- sh.SetVar(consts.VeyronCredentials, dir)
- if err := validateCredentials(startChildAndGetCredentials(sh, nil), "shell/child"); err != nil {
- t.Fatal(err)
- }
- sh.ClearVar(consts.VeyronCredentials)
- if credentials := startChildAndGetCredentials(sh, nil); len(credentials) != 0 {
- t.Fatalf("found credentials %v set for child even when shell credentials were cleared", credentials)
- }
- anotherDir, _ := tsecurity.NewCredentials("anotherShell")
- defer os.RemoveAll(anotherDir)
- sh.SetVar(consts.VeyronCredentials, anotherDir)
- if err := validateCredentials(startChildAndGetCredentials(sh, nil), "anotherShell/child"); err != nil {
- t.Fatal(err)
- }
- sh.Cleanup(nil, nil)
-
- // Test that VeyronCredentials specified as a parameter overrides the OS and
- // shell ones.
- dir, _ = tsecurity.NewCredentials("param")
- defer os.RemoveAll(dir)
- env := []string{consts.VeyronCredentials + "=" + dir}
- if err := validateCredentials(startChildAndGetCredentials(sh, env), "param"); err != nil {
- t.Fatal(err)
- }
-
}
func TestEnvMerge(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/lib/modules/shell.go b/lib/modules/shell.go
index 0c6a5eb..ecaefde 100644
--- a/lib/modules/shell.go
+++ b/lib/modules/shell.go
@@ -29,20 +29,10 @@
// In particular stdin, stdout and stderr are provided as parameters, as is
// a map representation of the shell's environment.
//
-// Every Shell created by NewShell is initialized with its VeyronCredentials
-// environment variable set. The variable is set to the os's VeyronCredentials
-// if that is set, otherwise to a freshly created credentials directory. The
-// shell's VeyronCredentials can be set and cleared using the SetVar and
-// ClearVar methods respectively.
-//
-// By default, the VeyronCredentials for each command Start-ed by the shell are
-// set to a freshly created credentials directory that is blessed by the shell's
-// credentials (i.e., if shell's credentials have not been cleared). Thus, each
-// child of the shell gets its own credentials directory with a blessing from the
-// shell of the form
-// <shell's default blessing>/child
-// These default credentials provided by the shell to each command can be
-// overridden by specifying VeyronCredentials in the environment provided as a
+// By default, every Shell created by NewShell starts a security agent
+// to manage principals for child processes. These default
+// credentials can be overridden by passing a nil context to NewShell
+// then specifying VeyronCredentials in the environment provided as a
// parameter to the Start method.
package modules
@@ -53,13 +43,17 @@
"math/rand"
"os"
"sync"
+ "syscall"
"time"
"v.io/core/veyron2/security"
"v.io/core/veyron/lib/exec"
"v.io/core/veyron/lib/flags/consts"
- vsecurity "v.io/core/veyron/security"
+ "v.io/core/veyron/security/agent"
+ "v.io/core/veyron/security/agent/keymgr"
+ "v.io/core/veyron2"
+ "v.io/core/veyron2/context"
)
const (
@@ -72,38 +66,55 @@
mu sync.Mutex
env map[string]string
handles map[Handle]struct{}
- // tmpCredDirs are the temporary directories created by this
- // shell. These must be removed when the shell is cleaned up.
- tempCredDirs []string
+ // tmpCredDir is the temporary directory created by this
+ // shell. This must be removed when the shell is cleaned up.
+ tempCredDir string
startTimeout, waitTimeout time.Duration
config exec.Config
principal security.Principal
blessing security.Blessings
+ agent *keymgr.Agent
+ ctx *context.T
+ cancelCtx func()
}
-// NewShell creates a new instance of Shell. It will also add a blessing
-// to the supplied principal and ensure that any child processes are
-// created with principals that are in turn blessed with it. A typical
-// use case is to pass in the runtime's principal and hence allow the
-// process hosting the shell to interact with its children and vice
-// versa. If a nil principal is passed in then NewShell will create a new
-// principal that shares a blessing with all of its children, in this mode,
-// any child processes can interact with each other but not with the parent.
-func NewShell(p security.Principal) (*Shell, error) {
+// NewShell creates a new instance of Shell.
+// If ctx is non-nil, the shell will manage Principals for child processes.
+// By default it adds a blessing to ctx's principal to ensure isolation. Any child processes
+// are created with principals derived from this new blessing.
+// However, if p is non-nil the shell will skip the new blessing and child processes
+// will have their principals derived from p's default blessing(s).
+func NewShell(ctx *context.T, p security.Principal) (*Shell, error) {
sh := &Shell{
env: make(map[string]string),
handles: make(map[Handle]struct{}),
startTimeout: time.Minute,
waitTimeout: 10 * time.Second,
config: exec.NewConfig(),
- principal: p,
}
- if p == nil {
- if err := sh.initShellCredentials(); err != nil {
- return nil, err
- }
+ if ctx == nil {
return sh, nil
}
+ var err error
+ ctx, sh.cancelCtx = context.WithCancel(ctx)
+ if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
+ return nil, err
+ }
+ sh.ctx = ctx
+
+ if sh.tempCredDir, err = ioutil.TempDir("", "shell_credentials"); err != nil {
+ return nil, err
+ }
+ if sh.agent, err = keymgr.NewLocalAgent(ctx, sh.tempCredDir, nil); err != nil {
+ return nil, err
+ }
+ if p != nil {
+ sh.principal = p
+ sh.blessing = p.BlessingStore().Default()
+ return sh, nil
+ }
+ p = veyron2.GetPrincipal(ctx)
+ sh.principal = p
gen := rand.New(rand.NewSource(time.Now().UnixNano()))
// Use a unique blessing tree per shell.
blessingName := fmt.Sprintf("%s-%d", shellBlessingExtension, gen.Int63())
@@ -124,60 +135,42 @@
return sh, nil
}
-func (sh *Shell) initShellCredentials() error {
- sh.mu.Lock()
- defer sh.mu.Unlock()
- if dir := os.Getenv(consts.VeyronCredentials); len(dir) != 0 {
- sh.env[consts.VeyronCredentials] = dir
- return nil
+func (sh *Shell) getChildCredentials() (*os.File, error) {
+ if sh.ctx == nil {
+ return nil, nil
}
-
- dir, err := ioutil.TempDir("", "shell_credentials")
- if err != nil {
- return err
- }
- sh.env[consts.VeyronCredentials] = dir
- sh.tempCredDirs = append(sh.tempCredDirs, dir)
- return nil
-}
-
-func (sh *Shell) getChildCredentials(shellCredDir string) (string, error) {
root := sh.principal
rootBlessing := sh.blessing
- if root == nil {
- r, err := principalFromDir(shellCredDir)
- if err != nil {
- return "", err
- }
- root = r
- rootBlessing = root.BlessingStore().Default()
- }
-
- dir, err := ioutil.TempDir("", "shell_child_credentials")
+ _, conn, err := sh.agent.NewPrincipal(sh.ctx, true)
if err != nil {
- return "", err
+ return nil, err
}
- sh.tempCredDirs = append(sh.tempCredDirs, dir)
- // Create a principal and default blessing for the child that is
- // derived from the blessing created for this shell. This can
- // be used by the parent to invoke RPCs on any children and for the
- // children to invoke RPCs on each other.
- p, err := vsecurity.CreatePersistentPrincipal(dir, nil)
+ ctx, cancel := context.WithCancel(sh.ctx)
+ if ctx, _, err = veyron2.SetNewStreamManager(ctx); err != nil {
+ return nil, err
+ }
+ defer cancel()
+ fd, err := syscall.Dup(int(conn.Fd()))
if err != nil {
- return "", err
+ return nil, err
+ }
+ syscall.CloseOnExec(fd)
+ p, err := agent.NewAgentPrincipal(ctx, fd)
+ if err != nil {
+ return nil, err
}
blessingForChild, err := root.Bless(p.PublicKey(), rootBlessing, childBlessingExtension, security.UnconstrainedUse())
if err != nil {
- return "", err
+ return nil, err
}
if err := p.BlessingStore().SetDefault(blessingForChild); err != nil {
- return "", err
+ return nil, err
}
if _, err := p.BlessingStore().Set(blessingForChild, security.AllPrincipals); err != nil {
- return "", err
+ return nil, err
}
if err := p.AddToRoots(blessingForChild); err != nil {
- return "", err
+ return nil, err
}
if sh.blessing != nil {
@@ -185,7 +178,7 @@
// by the parent, should the child choose to invoke RPCs on the parent.
blessingFromChild, err := root.Bless(p.PublicKey(), root.BlessingStore().Default(), childBlessingExtension, security.UnconstrainedUse())
if err != nil {
- return "", err
+ return nil, err
}
// We store this blessing as the one to use with a pattern that matches
// the root's name.
@@ -194,14 +187,14 @@
ctx := security.NewContext(&security.ContextParams{LocalPrincipal: root})
rootName := root.BlessingStore().Default().ForContext(ctx)[0]
if _, err := p.BlessingStore().Set(blessingFromChild, security.BlessingPattern(rootName)); err != nil {
- return "", err
+ return nil, err
}
if err := p.AddToRoots(blessingFromChild); err != nil {
- return "", err
+ return nil, err
}
}
- return dir, nil
+ return conn, nil
}
type Main func(stdin io.Reader, stdout, stderr io.Writer, env map[string]string, args ...string) error
@@ -244,12 +237,16 @@
if err != nil {
return nil, err
}
+ p, err := sh.getChildCredentials()
+ if err != nil {
+ return nil, err
+ }
cmd := registry.getCommand(name)
if cmd == nil {
return nil, fmt.Errorf("%s: not registered", name)
}
expanded := append([]string{name}, sh.expand(args...)...)
- h, err := cmd.factory().start(sh, cenv, expanded...)
+ h, err := cmd.factory().start(sh, p, cenv, expanded...)
if err != nil {
// If the error is a timeout, then h can be used to recover
// any output from the process.
@@ -399,18 +396,14 @@
}
}
- // TODO(ataly, ashankar, caprita): The following code may lead to
- // removing the credential directories for child processes that are
- // still alive (this can happen e.g. if the Wait on the child timed
- // out). While we can hope that some error will reach the caller of
- // Cleanup (because we harvest the error codes from Shutdown), it may
- // lead to failures that are harder to debug because the original issue
- // will get masked by the credentials going away. One possibility is
- // to only remove the credentials dir when Shutdown returns no timeout
- // error.
- for _, dir := range sh.tempCredDirs {
- os.RemoveAll(dir)
+ if sh.cancelCtx != nil {
+ // Note(ribrdb, caprita): This will shutdown the agents. If there
+ // were errors shutting down it is possible there could be child
+ // processes still running, and stopping the agent may cause
+ // additional failures.
+ sh.cancelCtx()
}
+ os.RemoveAll(sh.tempCredDir)
return err
}
@@ -425,15 +418,7 @@
// want the child to directly use the directory specified
// by the shell's VeyronCredentials.
delete(m1, consts.VeyronCredentials)
-
- // Set the VeyronCredentials environment variable for the child
- // if it is not already set and the shell's VeyronCredentials are set.
- if len(evmap[consts.VeyronCredentials]) == 0 && (len(sh.env[consts.VeyronCredentials]) != 0 || sh.principal != nil) {
- var err error
- if evmap[consts.VeyronCredentials], err = sh.getChildCredentials(sh.env[consts.VeyronCredentials]); err != nil {
- return nil, err
- }
- }
+ delete(m1, agent.FdVarName)
m2 := mergeMaps(m1, evmap)
r := []string{}
@@ -477,5 +462,5 @@
// commands.
type command interface {
envelope(sh *Shell, env []string, args ...string) ([]string, []string)
- start(sh *Shell, env []string, args ...string) (Handle, error)
+ start(sh *Shell, agent *os.File, env []string, args ...string) (Handle, error)
}
diff --git a/lib/signals/signals_test.go b/lib/signals/signals_test.go
index b5ea0bc..21d3430 100644
--- a/lib/signals/signals_test.go
+++ b/lib/signals/signals_test.go
@@ -20,10 +20,8 @@
"v.io/core/veyron2/services/mgmt/appcycle"
"v.io/core/veyron/lib/expect"
- "v.io/core/veyron/lib/flags/consts"
"v.io/core/veyron/lib/modules"
"v.io/core/veyron/lib/testutil"
- "v.io/core/veyron/lib/testutil/security"
"v.io/core/veyron/profiles"
vflag "v.io/core/veyron/security/flag"
"v.io/core/veyron/services/mgmt/device"
@@ -125,8 +123,8 @@
}
}
-func newShell(t *testing.T, command string) (*modules.Shell, modules.Handle, *expect.Session) {
- sh, err := modules.NewShell(nil)
+func newShell(t *testing.T, r veyron2.Runtime, command string) (*modules.Shell, modules.Handle, *expect.Session) {
+ sh, err := modules.NewShell(r.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -143,7 +141,12 @@
// TestCleanShutdownSignal verifies that sending a signal to a child that
// handles it by default causes the child to shut down cleanly.
func TestCleanShutdownSignal(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaults")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaults")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
checkSignalIsDefault(t, syscall.SIGINT)
@@ -156,7 +159,12 @@
// TestCleanShutdownStop verifies that sending a stop comamnd to a child that
// handles stop commands by default causes the child to shut down cleanly.
func TestCleanShutdownStop(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaults")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaults")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
fmt.Fprintf(h.Stdin(), "stop\n")
@@ -170,7 +178,12 @@
// that handles stop command as part of a custom set of signals handled, causes
// the child to shut down cleanly.
func TestCleanShutdownStopCustom(t *testing.T) {
- sh, h, s := newShell(t, "handleCustomWithStop")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleCustomWithStop")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
fmt.Fprintf(h.Stdin(), "stop\n")
@@ -191,7 +204,12 @@
// TestStopNoHandler verifies that sending a stop command to a child that does
// not handle stop commands causes the child to exit immediately.
func TestStopNoHandler(t *testing.T) {
- sh, h, s := newShell(t, "handleCustom")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleCustom")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
fmt.Fprintf(h.Stdin(), "stop\n")
@@ -202,7 +220,12 @@
// that handles these signals by default causes the child to exit immediately
// upon receiving the second signal.
func TestDoubleSignal(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaults")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaults")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
checkSignalIsDefault(t, syscall.SIGTERM)
@@ -217,7 +240,12 @@
// to a child that handles these by default causes the child to exit immediately
// upon receiving the stop command.
func TestSignalAndStop(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaults")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaults")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
checkSignalIsDefault(t, syscall.SIGTERM)
@@ -231,7 +259,12 @@
// that handles stop commands by default causes the child to exit immediately
// upon receiving the second stop command.
func TestDoubleStop(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaults")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaults")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
fmt.Fprintf(h.Stdin(), "stop\n")
@@ -243,7 +276,12 @@
// TestSendUnhandledSignal verifies that sending a signal that the child does
// not handle causes the child to exit as per the signal being sent.
func TestSendUnhandledSignal(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaults")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaults")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
checkSignalIsNotDefault(t, syscall.SIGABRT)
@@ -256,7 +294,12 @@
// process to exit (ensures that there is no dependency in ShutdownOnSignals
// on having a goroutine read from the returned channel).
func TestDoubleSignalIgnoreChan(t *testing.T) {
- sh, h, s := newShell(t, "handleDefaultsIgnoreChan")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleDefaultsIgnoreChan")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
// Even if we ignore the channel that ShutdownOnSignals returns,
@@ -271,7 +314,12 @@
// TestHandlerCustomSignal verifies that sending a non-default signal to a
// server that listens for that signal causes the server to shut down cleanly.
func TestHandlerCustomSignal(t *testing.T) {
- sh, h, s := newShell(t, "handleCustom")
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
+ sh, h, s := newShell(t, runtime, "handleCustom")
defer sh.Cleanup(os.Stderr, os.Stderr)
s.Expect("ready")
checkSignalIsNotDefault(t, syscall.SIGABRT)
@@ -285,8 +333,13 @@
// to a server that listens for that signal causes the server to shut down
// cleanly, even when a STOP signal is also among the handled signals.
func TestHandlerCustomSignalWithStop(t *testing.T) {
+ runtime, err := rt.New()
+ if err != nil {
+ panic(err)
+ }
+ defer runtime.Cleanup()
for _, signal := range []syscall.Signal{syscall.SIGABRT, syscall.SIGHUP} {
- sh, h, s := newShell(t, "handleCustomWithStop")
+ sh, h, s := newShell(t, runtime, "handleCustomWithStop")
s.Expect("ready")
checkSignalIsNotDefault(t, signal)
syscall.Kill(h.Pid(), signal)
@@ -349,20 +402,14 @@
defer runtime.Cleanup()
ctx := runtime.NewContext()
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(runtime.NewContext(), nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
defer sh.Cleanup(os.Stderr, os.Stderr)
- // Set the child process up with a blessing from the parent so that
- // the default authorization works for RPCs between the two.
- principal := veyron2.GetPrincipal(ctx)
- childcreds, _ := security.ForkCredentials(principal, "child")
- defer os.RemoveAll(childcreds)
configServer, configServiceName, ch := createConfigServer(t, ctx)
defer configServer.Stop()
- sh.SetVar(consts.VeyronCredentials, childcreds)
sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
diff --git a/lib/testutil/integration/util.go b/lib/testutil/integration/util.go
index 0255ab3..531cec3 100644
--- a/lib/testutil/integration/util.go
+++ b/lib/testutil/integration/util.go
@@ -20,6 +20,8 @@
"v.io/core/veyron/lib/modules"
"v.io/core/veyron/lib/modules/core"
tsecurity "v.io/core/veyron/lib/testutil/security"
+ "v.io/core/veyron2/options"
+ "v.io/core/veyron2/rt"
"v.io/core/veyron2/security"
)
@@ -364,7 +366,11 @@
func NewTestEnvironment(t *testing.T) TestEnvironment {
t.Log("creating root principal")
principal := tsecurity.NewPrincipal("root")
- shell, err := modules.NewShell(principal)
+ runtime, err := rt.New(options.RuntimePrincipal{principal})
+ if err != nil {
+ t.Fatalf("rt.New() failed: %v", err)
+ }
+ shell, err := modules.NewShell(runtime.NewContext(), principal)
if err != nil {
t.Fatalf("NewShell() failed: %v", err)
}
diff --git a/runtimes/google/ipc/client_test.go b/runtimes/google/ipc/client_test.go
index ace8a6d..a124355 100644
--- a/runtimes/google/ipc/client_test.go
+++ b/runtimes/google/ipc/client_test.go
@@ -6,12 +6,14 @@
"os"
"path/filepath"
"runtime"
+ "strings"
"testing"
"time"
"v.io/core/veyron2"
"v.io/core/veyron2/context"
"v.io/core/veyron2/naming"
+ "v.io/core/veyron2/options"
"v.io/core/veyron2/rt"
verror "v.io/core/veyron2/verror2"
"v.io/core/veyron2/vlog"
@@ -20,7 +22,9 @@
"v.io/core/veyron/lib/flags/consts"
"v.io/core/veyron/lib/modules"
"v.io/core/veyron/lib/modules/core"
+ tsecurity "v.io/core/veyron/lib/testutil/security"
"v.io/core/veyron/profiles"
+ inaming "v.io/core/veyron/runtimes/google/naming"
)
var r veyron2.Runtime
@@ -42,8 +46,7 @@
}
func runMountTable(t *testing.T, ctx *context.T) (*modules.Shell, func()) {
- principal := veyron2.GetPrincipal(ctx)
- sh, err := modules.NewShell(principal)
+ sh, err := modules.NewShell(ctx, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -56,13 +59,6 @@
rootSession := expect.NewSession(t, root.Stdout(), time.Minute)
rootSession.ExpectVar("PID")
rootName := rootSession.ExpectVar("MT_NAME")
- if t.Failed() {
- t.Fatalf("%s", rootSession.Error())
- }
- sh.SetVar(consts.NamespaceRootPrefix, rootName)
- if err = veyron2.GetNamespace(ctx).SetRoots(rootName); err != nil {
- t.Fatalf("unexpected error setting namespace roots: %s", err)
- }
deferFn := func() {
if testing.Verbose() {
@@ -75,6 +71,16 @@
root.Shutdown(nil, nil)
}
}
+
+ if t.Failed() {
+ deferFn()
+ t.Fatalf("%s", rootSession.Error())
+ }
+ sh.SetVar(consts.NamespaceRootPrefix, rootName)
+ if err = veyron2.GetNamespace(ctx).SetRoots(rootName); err != nil {
+ t.Fatalf("unexpected error setting namespace roots: %s", err)
+ }
+
return sh, deferFn
}
@@ -451,5 +457,71 @@
testForVerror(t, verr, verror.NoServers)
}
+// TestReconnect verifies that the client transparently re-establishes the
+// connection to the server if the server dies and comes back (on the same
+// endpoint).
+func TestReconnect(t *testing.T) {
+ principal := tsecurity.NewPrincipal("client")
+ r, err := rt.New(options.RuntimePrincipal{principal})
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ sh, err := modules.NewShell(r.NewContext(), principal)
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ defer sh.Cleanup(os.Stderr, os.Stderr)
+ server, err := sh.Start(core.EchoServerCommand, nil, "--", "--veyron.tcp.address=127.0.0.1:0", "mymessage", "")
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ session := expect.NewSession(t, server.Stdout(), time.Minute)
+ session.ReadLine()
+ serverName := session.ExpectVar("NAME")
+ serverEP, _ := naming.SplitAddressName(serverName)
+ ep, _ := inaming.NewEndpoint(serverEP)
+ makeCall := func() (string, error) {
+ ctx, _ := context.WithDeadline(r.NewContext(), time.Now().Add(10*time.Second))
+ call, err := veyron2.GetClient(ctx).StartCall(ctx, serverName, "Echo", []interface{}{"bratman"})
+ if err != nil {
+ return "", fmt.Errorf("START: %s", err)
+ }
+ var result string
+ var rerr error
+ if err = call.Finish(&result, &rerr); err != nil {
+ return "", err
+ }
+ return result, nil
+ }
+ expected := "mymessage: bratman\n"
+ if result, err := makeCall(); err != nil || result != expected {
+ t.Errorf("Got (%q, %v) want (%q, nil)", result, err, expected)
+ }
+ // Kill the server, verify client can't talk to it anymore.
+ sh.SetWaitTimeout(time.Minute)
+ if err := server.Shutdown(os.Stderr, os.Stderr); err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+
+ if _, err := makeCall(); err == nil || (!strings.HasPrefix(err.Error(), "START") && !strings.Contains(err.Error(), "EOF")) {
+ t.Fatalf(`Got (%v) want ("START: <err>" or "EOF") as server is down`, err)
+ }
+
+ // Resurrect the server with the same address, verify client
+ // re-establishes the connection. This is racy if another
+ // process grabs the port.
+ server, err = sh.Start(core.EchoServerCommand, nil, "--", "--veyron.tcp.address="+ep.Address, "mymessage again", "")
+ if err != nil {
+ t.Fatalf("unexpected error: %s", err)
+ }
+ session = expect.NewSession(t, server.Stdout(), time.Minute)
+ defer server.Shutdown(os.Stderr, os.Stderr)
+ expected = "mymessage again: bratman\n"
+ if result, err := makeCall(); err != nil || result != expected {
+ t.Errorf("Got (%q, %v) want (%q, nil)", result, err, expected)
+ }
+
+}
+
// TODO(cnicolaou:) tests for:
// -- Test for bad discharges error and correct invalidation, client.go:870..880
diff --git a/runtimes/google/ipc/resolve_test.go b/runtimes/google/ipc/resolve_test.go
index 3b3c2cd..ab64566 100644
--- a/runtimes/google/ipc/resolve_test.go
+++ b/runtimes/google/ipc/resolve_test.go
@@ -27,7 +27,7 @@
}
func TestResolveToEndpoint(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("modules.NewShell failed: %s", err)
}
diff --git a/runtimes/google/ipc/server_test.go b/runtimes/google/ipc/server_test.go
index 9a2975c..1f362be 100644
--- a/runtimes/google/ipc/server_test.go
+++ b/runtimes/google/ipc/server_test.go
@@ -83,70 +83,6 @@
}
}
-// TestReconnect verifies that the client transparently re-establishes the
-// connection to the server if the server dies and comes back (on the same
-// endpoint).
-func TestReconnect(t *testing.T) {
- principal := tsecurity.NewPrincipal("client")
- b := createBundle(t, principal, nil, nil) // We only need the client from the bundle.
- defer b.cleanup(t)
- sh, err := modules.NewShell(principal)
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- defer sh.Cleanup(os.Stderr, os.Stderr)
- server, err := sh.Start(core.EchoServerCommand, nil, "--", "--veyron.tcp.address=127.0.0.1:0", "mymessage", "")
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- session := expect.NewSession(t, server.Stdout(), time.Minute)
- session.ReadLine()
- serverName := session.ExpectVar("NAME")
- serverEP, _ := naming.SplitAddressName(serverName)
- ep, _ := inaming.NewEndpoint(serverEP)
- makeCall := func() (string, error) {
- ctx, _ := context.WithDeadline(testContext(), time.Now().Add(10*time.Second))
- call, err := b.client.StartCall(ctx, serverName, "Echo", []interface{}{"bratman"})
- if err != nil {
- return "", fmt.Errorf("START: %s", err)
- }
- var result string
- var rerr error
- if err = call.Finish(&result, &rerr); err != nil {
- return "", err
- }
- return result, nil
- }
- expected := "mymessage: bratman\n"
- if result, err := makeCall(); err != nil || result != expected {
- t.Errorf("Got (%q, %v) want (%q, nil)", result, err, expected)
- }
- // Kill the server, verify client can't talk to it anymore.
- sh.SetWaitTimeout(time.Minute)
- if err := server.Shutdown(os.Stderr, os.Stderr); err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
-
- if _, err := makeCall(); err == nil || (!strings.HasPrefix(err.Error(), "START") && !strings.Contains(err.Error(), "EOF")) {
- t.Fatalf(`Got (%v) want ("START: <err>" or "EOF") as server is down`, err)
- }
-
- // Resurrect the server with the same address, verify client
- // re-establishes the connection. This is racy if another
- // process grabs the port.
- server, err = sh.Start(core.EchoServerCommand, nil, "--", "--veyron.tcp.address="+ep.Address, "mymessage again", "")
- if err != nil {
- t.Fatalf("unexpected error: %s", err)
- }
- session = expect.NewSession(t, server.Stdout(), time.Minute)
- defer server.Shutdown(os.Stderr, os.Stderr)
- expected = "mymessage again: bratman\n"
- if result, err := makeCall(); err != nil || result != expected {
- t.Errorf("Got (%q, %v) want (%q, nil)", result, err, expected)
- }
-
-}
-
type proxyHandle struct {
ns naming.Namespace
sh *modules.Shell
@@ -155,7 +91,7 @@
}
func (h *proxyHandle) Start(t *testing.T, args ...string) error {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/runtimes/google/ipc/stream/manager/manager_test.go b/runtimes/google/ipc/stream/manager/manager_test.go
index ada68cf..e1fb7b4 100644
--- a/runtimes/google/ipc/stream/manager/manager_test.go
+++ b/runtimes/google/ipc/stream/manager/manager_test.go
@@ -508,7 +508,7 @@
func testServerRestartDuringClientLifetime(t *testing.T, protocol string) {
client := InternalNew(naming.FixedRoutingID(0xcccccccc))
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/runtimes/google/rt/mgmt_test.go b/runtimes/google/rt/mgmt_test.go
index d735fe3..6062899 100644
--- a/runtimes/google/rt/mgmt_test.go
+++ b/runtimes/google/rt/mgmt_test.go
@@ -18,10 +18,8 @@
"v.io/core/veyron2/services/mgmt/appcycle"
"v.io/core/veyron/lib/expect"
- "v.io/core/veyron/lib/flags/consts"
"v.io/core/veyron/lib/modules"
"v.io/core/veyron/lib/testutil"
- "v.io/core/veyron/lib/testutil/security"
"v.io/core/veyron/profiles"
"v.io/core/veyron/runtimes/google/rt"
vflag "v.io/core/veyron/security/flag"
@@ -121,7 +119,7 @@
// TestNoWaiters verifies that the child process exits in the absence of any
// wait channel being registered with its runtime.
func TestNoWaiters(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -152,7 +150,7 @@
// TestForceStop verifies that ForceStop causes the child process to exit
// immediately.
func TestForceStop(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -305,14 +303,11 @@
r, _ := rt.New(profileOpt)
ctx := r.NewContext()
- p := veyron2.GetPrincipal(ctx)
- childcreds, _ := security.ForkCredentials(p, appCmd)
configServer, configServiceName, ch := createConfigServer(t, ctx)
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(ctx, veyron2.GetPrincipal(ctx))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- sh.SetVar(consts.VeyronCredentials, childcreds)
sh.SetConfigKey(mgmt.ParentNameConfigKey, configServiceName)
sh.SetConfigKey(mgmt.ProtocolConfigKey, "tcp")
sh.SetConfigKey(mgmt.AddressConfigKey, "127.0.0.1:0")
@@ -330,7 +325,6 @@
return ctx, h, appCycle, func() {
configServer.Stop()
sh.Cleanup(os.Stderr, os.Stderr)
- 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_test.go b/runtimes/google/rt/rt_test.go
index f6ae79d..0d1c7a0 100644
--- a/runtimes/google/rt/rt_test.go
+++ b/runtimes/google/rt/rt_test.go
@@ -70,7 +70,7 @@
}
func TestInitArgs(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -146,7 +146,7 @@
return err
}
fmt.Fprintf(stdout, "RUNNER_DEFAULT_BLESSING=%v\n", defaultBlessing(p))
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(ctx, p)
if err != nil {
return err
}
@@ -169,7 +169,7 @@
}
func TestPrincipalInheritance(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -232,7 +232,7 @@
t.Fatal(err)
}
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/runtimes/google/rt/security.go b/runtimes/google/rt/security.go
index f8df35c..5f6a14d 100644
--- a/runtimes/google/rt/security.go
+++ b/runtimes/google/rt/security.go
@@ -2,6 +2,7 @@
import (
"os"
+ "syscall"
"v.io/core/veyron2/security"
@@ -60,5 +61,15 @@
}
func (rt *vrt) connectToAgent(fd int) (security.Principal, error) {
- return agent.NewAgentPrincipal(rt.NewContext(), fd)
+ // Dup the fd, so we can create multiple runtimes.
+ syscall.ForkLock.Lock()
+ newfd, err := syscall.Dup(fd)
+ if err == nil {
+ syscall.CloseOnExec(newfd)
+ }
+ syscall.ForkLock.Unlock()
+ if err != nil {
+ return nil, err
+ }
+ return agent.NewAgentPrincipal(rt.NewContext(), newfd)
}
diff --git a/runtimes/google/rt/securityx.go b/runtimes/google/rt/securityx.go
index 8ebb15d..0c8ca3f 100644
--- a/runtimes/google/rt/securityx.go
+++ b/runtimes/google/rt/securityx.go
@@ -5,6 +5,7 @@
"os"
"os/user"
"strconv"
+ "syscall"
"v.io/core/veyron2/context"
"v.io/core/veyron2/mgmt"
@@ -75,7 +76,12 @@
if fd == "" {
return -1, nil
}
- return strconv.Atoi(fd)
+ ifd, err := strconv.Atoi(fd)
+ if err == nil && handle != nil {
+ // If we're using a handle, children can't inherit the agent.
+ syscall.CloseOnExec(ifd)
+ }
+ return ifd, err
}
func defaultBlessingName() string {
diff --git a/runtimes/google/rt/shutdown_test.go b/runtimes/google/rt/shutdown_test.go
index b29baa6..8c7c20a 100644
--- a/runtimes/google/rt/shutdown_test.go
+++ b/runtimes/google/rt/shutdown_test.go
@@ -26,7 +26,7 @@
}
func newShell(t *testing.T) *modules.Shell {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/runtimes/google/rt/signal_test.go b/runtimes/google/rt/signal_test.go
index aeb249b..d9b296d 100644
--- a/runtimes/google/rt/signal_test.go
+++ b/runtimes/google/rt/signal_test.go
@@ -77,7 +77,7 @@
}
func TestWithRuntime(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
@@ -97,7 +97,7 @@
}
func TestWithoutRuntime(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/security/agent/client.go b/security/agent/client.go
index d2574dc..496c08d 100644
--- a/security/agent/client.go
+++ b/security/agent/client.go
@@ -77,7 +77,6 @@
name: naming.JoinAddressName(naming.FormatEndpoint(addr.Network(), addr.String()), ""),
ctx: ctx,
}
-
agent := &client{caller: caller}
if err := agent.fetchPublicKey(); err != nil {
return nil, err
diff --git a/security/agent/keymgr/client.go b/security/agent/keymgr/client.go
index 3d0641c..65f175e 100644
--- a/security/agent/keymgr/client.go
+++ b/security/agent/keymgr/client.go
@@ -7,6 +7,7 @@
"os"
"strconv"
"sync"
+ "syscall"
"v.io/core/veyron/lib/unixfd"
"v.io/core/veyron/security/agent/server"
@@ -36,6 +37,19 @@
return newAgent(defaultManagerSocket)
}
+func NewLocalAgent(ctx *context.T, path string, passphrase []byte) (*Agent, error) {
+ file, err := server.RunKeyManager(ctx, path, passphrase)
+ if err != nil {
+ return nil, err
+ }
+ syscall.CloseOnExec(int(file.Fd()))
+ conn, err := net.FileConn(file)
+ if err != nil {
+ return nil, err
+ }
+ return &Agent{conn: conn.(*net.UnixConn)}, nil
+}
+
func newAgent(fd int) (a *Agent, err error) {
file := os.NewFile(uintptr(fd), "fd")
defer file.Close()
diff --git a/security/agent/server/server.go b/security/agent/server/server.go
index 28569fa..808b5a5 100644
--- a/security/agent/server/server.go
+++ b/security/agent/server/server.go
@@ -192,6 +192,7 @@
for {
clientAddr, _, ack, err := unixfd.ReadConnection(conn, buf)
if err == io.EOF {
+ conn.Close()
return
}
if clientAddr != nil {
diff --git a/security/flag/flag_test.go b/security/flag/flag_test.go
index f2edf84..fbf46bc 100644
--- a/security/flag/flag_test.go
+++ b/security/flag/flag_test.go
@@ -67,7 +67,7 @@
}
func TestNewAuthorizerOrDie(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/services/mgmt/application/impl/acl_test.go b/services/mgmt/application/impl/acl_test.go
index 4f1a558..c603e34 100644
--- a/services/mgmt/application/impl/acl_test.go
+++ b/services/mgmt/application/impl/acl_test.go
@@ -92,7 +92,7 @@
}
func TestApplicationUpdateACL(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// setup mock up directory to put state in
@@ -221,7 +221,7 @@
}
func TestPerAppACL(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// setup mock up directory to put state in
@@ -357,7 +357,7 @@
}
func TestInitialACLSet(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Setup mock up directory to put state in.
diff --git a/services/mgmt/device/impl/impl_test.go b/services/mgmt/device/impl/impl_test.go
index 3abef07..d509be9 100644
--- a/services/mgmt/device/impl/impl_test.go
+++ b/services/mgmt/device/impl/impl_test.go
@@ -327,7 +327,7 @@
// command. Further versions are started through the soft link that the device
// manager itself updates.
func TestDeviceManagerUpdateAndRevert(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, veyron2.GetPrincipal(globalCtx))
defer deferFn()
// Set up mock application and binary repositories.
@@ -341,10 +341,8 @@
// convenient to put it there so we have everything in one place.
currLink := filepath.Join(root, "current_link")
- crDir, crEnv := mgmttest.CredentialsForChild(globalCtx, "devicemanager")
- defer os.RemoveAll(crDir)
dmArgs := []string{"factoryDM", root, "unused_helper", mockApplicationRepoName, currLink}
- args, env := sh.CommandEnvelope(deviceManagerCmd, crEnv, dmArgs...)
+ args, env := sh.CommandEnvelope(deviceManagerCmd, nil, dmArgs...)
scriptPathFactory := generateDeviceManagerScript(t, root, args, env)
@@ -355,7 +353,7 @@
// We instruct the initial device manager that we run to pause before
// stopping its service, so that we get a chance to verify that
// attempting an update while another one is ongoing will fail.
- dmPauseBeforeStopEnv := append(crEnv, "PAUSE_BEFORE_STOP=1")
+ dmPauseBeforeStopEnv := []string{"PAUSE_BEFORE_STOP=1"}
// Start the initial version of the device manager, the so-called
// "factory" version. We use the modules-generated command to start it.
@@ -380,7 +378,7 @@
// Set up a second version of the device manager. The information in the
// envelope will be used by the device manager to stage the next
// version.
- crDir, crEnv = mgmttest.CredentialsForChild(globalCtx, "devicemanager")
+ crDir, crEnv := mgmttest.CredentialsForChild(globalCtx, "child")
defer os.RemoveAll(crDir)
*envelope = envelopeFromShell(sh, crEnv, deviceManagerCmd, application.DeviceManagerTitle, "v2DM")
updateDevice(t, "factoryDM")
@@ -423,7 +421,7 @@
}
// Create a third version of the device manager and issue an update.
- crDir, crEnv = mgmttest.CredentialsForChild(globalCtx, "devicemanager")
+ crDir, crEnv = mgmttest.CredentialsForChild(globalCtx, "child")
defer os.RemoveAll(crDir)
*envelope = envelopeFromShell(sh, crEnv, deviceManagerCmd, application.DeviceManagerTitle, "v3DM")
updateDevice(t, "v2DM")
@@ -562,7 +560,7 @@
// TestAppLifeCycle installs an app, starts it, suspends it, resumes it, and
// then stops it.
func TestAppLifeCycle(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Set up mock application and binary repositories.
@@ -775,7 +773,7 @@
// TestDeviceManagerClaim claims a devicemanager and tests ACL permissions on
// its methods.
func TestDeviceManagerClaim(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Set up mock application and binary repositories.
@@ -841,7 +839,7 @@
}
func TestDeviceManagerUpdateACL(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Set up mock application and binary repositories.
@@ -945,7 +943,7 @@
// This should bring up a functioning device manager. In the end it runs
// Uninstall and verifies that the installation is gone.
func TestDeviceManagerInstallation(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
testDir, cleanup := mgmttest.SetupRootDir(t, "devicemanager")
defer cleanup()
@@ -998,7 +996,7 @@
}
func TestDeviceManagerGlobAndDebug(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Set up mock application and binary repositories.
@@ -1162,7 +1160,7 @@
}
func TestDeviceManagerPackages(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Set up mock application and binary repositories.
@@ -1232,7 +1230,7 @@
// TODO(rjkroege): Verify that associations persist across restarts once
// permanent storage is added.
func TestAccountAssociation(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
root, cleanup := mgmttest.SetupRootDir(t, "devicemanager")
@@ -1331,7 +1329,7 @@
}
func TestAppWithSuidHelper(t *testing.T) {
- sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx)
+ sh, deferFn := mgmttest.CreateShellAndMountTable(t, globalCtx, nil)
defer deferFn()
// Set up mock application and binary repositories.
diff --git a/services/mgmt/lib/testutil/modules.go b/services/mgmt/lib/testutil/modules.go
index 307993c..b98460d 100644
--- a/services/mgmt/lib/testutil/modules.go
+++ b/services/mgmt/lib/testutil/modules.go
@@ -11,6 +11,7 @@
"v.io/core/veyron2/context"
"v.io/core/veyron2/ipc"
"v.io/core/veyron2/rt"
+ "v.io/core/veyron2/security"
"v.io/core/veyron2/vlog"
"v.io/core/veyron/lib/expect"
@@ -18,7 +19,7 @@
"v.io/core/veyron/lib/modules"
"v.io/core/veyron/lib/modules/core"
"v.io/core/veyron/lib/testutil"
- "v.io/core/veyron/lib/testutil/security"
+ tsecurity "v.io/core/veyron/lib/testutil/security"
)
const (
@@ -46,7 +47,7 @@
// CredentialsForChild creates credentials for a child process.
func CredentialsForChild(ctx *context.T, blessing string) (string, []string) {
- creds, _ := security.ForkCredentials(veyron2.GetPrincipal(ctx), blessing)
+ creds, _ := tsecurity.ForkCredentials(veyron2.GetPrincipal(ctx), blessing)
return creds, []string{consts.VeyronCredentials + "=" + creds}
}
@@ -60,8 +61,8 @@
// CreateShellAndMountTable builds a new modules shell and its
// associated mount table.
-func CreateShellAndMountTable(t *testing.T, ctx *context.T) (*modules.Shell, func()) {
- sh, err := modules.NewShell(nil)
+func CreateShellAndMountTable(t *testing.T, ctx *context.T, p security.Principal) (*modules.Shell, func()) {
+ sh, err := modules.NewShell(ctx, p)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/tools/naming/simulator/driver.go b/tools/naming/simulator/driver.go
index 847d224..685f19a 100644
--- a/tools/naming/simulator/driver.go
+++ b/tools/naming/simulator/driver.go
@@ -119,7 +119,7 @@
return
}
- shell, err := modules.NewShell(nil)
+ shell, err := modules.NewShell(ctx, nil)
if err != nil {
fmt.Fprintf(os.Stderr, "unexpected error: %s\n", err)
os.Exit(1)
diff --git a/tools/naming/simulator/driver_test.go b/tools/naming/simulator/driver_test.go
index 51bbeb7..657b46a 100644
--- a/tools/naming/simulator/driver_test.go
+++ b/tools/naming/simulator/driver_test.go
@@ -40,7 +40,7 @@
}
func TestVariables(t *testing.T) {
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(nil, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
diff --git a/tools/servicerunner/main.go b/tools/servicerunner/main.go
index 25c47c9..09a8e37 100644
--- a/tools/servicerunner/main.go
+++ b/tools/servicerunner/main.go
@@ -68,16 +68,11 @@
vars := map[string]string{}
- sh, err := modules.NewShell(nil)
+ sh, err := modules.NewShell(r.NewContext(), nil)
if err != nil {
panic(fmt.Sprintf("modules.NewShell: %s", err))
}
defer sh.Cleanup(os.Stderr, os.Stderr)
- v, ok := sh.GetVar(consts.VeyronCredentials)
- if !ok {
- panic("modules.Shell: missing " + consts.VeyronCredentials)
- }
- vars[consts.VeyronCredentials] = v
h, err := sh.Start(core.RootMTCommand, nil, "--", "--veyron.tcp.protocol=ws", "--veyron.tcp.address=127.0.0.1:0")
panicOnError(err)
diff --git a/tools/servicerunner/servicerunner_test.go b/tools/servicerunner/servicerunner_test.go
index 9996af2..524f277 100644
--- a/tools/servicerunner/servicerunner_test.go
+++ b/tools/servicerunner/servicerunner_test.go
@@ -47,7 +47,6 @@
}
fmt.Println(vars)
expectedVars := []string{
- "VEYRON_CREDENTIALS",
"MT_NAME",
"PROXY_NAME",
"WSPR_ADDR",