blob: e4938849eed104a3ebc55b8905ee7d95e95ac887 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// The following enables go generate to generate the doc.go file.
//go:generate go run $V23_ROOT/release/go/src/v.io/x/lib/cmdline/testdata/gendoc.go . -help
package main
import (
"os"
"os/exec"
"path/filepath"
"syscall"
"time"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/security"
"v.io/x/lib/cmdline"
"v.io/x/lib/vlog"
"v.io/x/ref"
"v.io/x/ref/lib/v23cmd"
"v.io/x/ref/services/agent/agentlib"
"v.io/x/ref/services/agent/keymgr"
"v.io/x/ref/services/role"
_ "v.io/x/ref/runtime/factories/generic"
)
var (
durationFlag time.Duration
nameFlag string
roleFlag string
)
var cmdVrun = &cmdline.Command{
Runner: v23cmd.RunnerFunc(vrun),
Name: "vrun",
Short: "executes commands with a derived Vanadium principal",
Long: "Command vrun executes commands with a derived Vanadium principal.",
ArgsName: "<command> [command args...]",
}
func main() {
cmdline.HideGlobalFlagsExcept()
syscall.CloseOnExec(3)
syscall.CloseOnExec(4)
cmdVrun.Flags.DurationVar(&durationFlag, "duration", 1*time.Hour, "Duration for the blessing.")
cmdVrun.Flags.StringVar(&nameFlag, "name", "", "Name to use for the blessing. Uses the command name if unset.")
cmdVrun.Flags.StringVar(&roleFlag, "role", "", "Role object from which to request the blessing. If set, the blessings from this role server are used and --name is ignored. If not set, the default blessings of the calling principal are extended with --name.")
cmdline.Main(cmdVrun)
}
func vrun(ctx *context.T, env *cmdline.Env, args []string) error {
if len(args) == 0 {
args = []string{"bash", "--norc"}
}
principal, conn, err := createPrincipal(ctx, env)
if err != nil {
return env.UsageErrorf("%v", err)
}
if len(roleFlag) == 0 {
if len(nameFlag) == 0 {
nameFlag = filepath.Base(args[0])
}
if err := bless(ctx, principal, nameFlag); err != nil {
return err
}
} else {
// The role server expects the client's blessing name to end
// with RoleSuffix. This is to avoid accidentally granting role
// access to anything else that might have been blessed by the
// same principal.
if err := bless(ctx, principal, role.RoleSuffix); err != nil {
return err
}
rCtx, err := v23.WithPrincipal(ctx, principal)
if err != nil {
return err
}
if err := setupRoleBlessings(rCtx, roleFlag); err != nil {
return err
}
}
return doExec(args, conn)
}
func bless(ctx *context.T, p security.Principal, name string) error {
caveat, err := security.NewExpiryCaveat(time.Now().Add(durationFlag))
if err != nil {
vlog.Errorf("Couldn't create caveat")
return err
}
rp := v23.GetPrincipal(ctx)
blessing, err := rp.Bless(p.PublicKey(), rp.BlessingStore().Default(), name, caveat)
if err != nil {
vlog.Errorf("Couldn't bless")
return err
}
if err = p.BlessingStore().SetDefault(blessing); err != nil {
vlog.Errorf("Couldn't set default blessing")
return err
}
if _, err = p.BlessingStore().Set(blessing, security.AllPrincipals); err != nil {
vlog.Errorf("Couldn't set default client blessing")
return err
}
if err = p.AddToRoots(blessing); err != nil {
vlog.Errorf("Couldn't set trusted roots")
return err
}
return nil
}
func doExec(cmd []string, conn *os.File) error {
if conn.Fd() != 3 {
if err := syscall.Dup2(int(conn.Fd()), 3); err != nil {
vlog.Errorf("Couldn't dup fd")
return err
}
conn.Close()
}
p, err := exec.LookPath(cmd[0])
if err != nil {
vlog.Errorf("Couldn't find %q", cmd[0])
return err
}
err = syscall.Exec(p, cmd, os.Environ())
vlog.Errorf("Couldn't exec %s.", cmd[0])
return err
}
func createPrincipal(ctx *context.T, env *cmdline.Env) (security.Principal, *os.File, error) {
kagent, err := keymgr.NewAgent()
if err != nil {
vlog.Errorf("Could not initialize agent")
return nil, nil, err
}
_, conn, err := kagent.NewPrincipal(ctx, true)
if err != nil {
vlog.Errorf("Couldn't create principal")
return nil, nil, err
}
ep, err := v23.NewEndpoint(env.Vars[ref.EnvAgentEndpoint])
if err != nil {
vlog.Errorf("Couldn't parse %v=%q: %v", ref.EnvAgentEndpoint, env.Vars[ref.EnvAgentEndpoint], err)
return nil, nil, err
}
// Connect to the Principal
fd, err := syscall.Dup(int(conn.Fd()))
if err != nil {
vlog.Errorf("Couldn't copy fd")
return nil, nil, err
}
syscall.CloseOnExec(fd)
ep, err = v23.NewEndpoint(agentlib.AgentEndpoint(fd))
if err != nil {
vlog.Errorf("Error creating endpoint: %v", err)
return nil, nil, err
}
principal, err := agentlib.NewAgentPrincipal(ctx, ep, v23.GetClient(ctx))
if err != nil {
vlog.Errorf("Couldn't connect to principal")
}
return principal, conn, nil
}
func setupRoleBlessings(ctx *context.T, roleStr string) error {
b, err := role.RoleClient(roleStr).SeekBlessings(ctx)
if err != nil {
return err
}
p := v23.GetPrincipal(ctx)
// TODO(rthellend,ashankar): Revisit this configuration.
// SetDefault: Should we expect users to want to act as a server on behalf of the role (by default?)
// AllPrincipals: Do we not want to be discriminating about which services we use the role blessing at.
if err := p.BlessingStore().SetDefault(b); err != nil {
return err
}
if _, err := p.BlessingStore().Set(b, security.AllPrincipals); err != nil {
return err
}
return nil
}