blob: a0a61c11bd897cb4f1eafef5d14b6780e5c3ef24 [file] [log] [blame]
package main
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"os/user"
"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 (
// Flags for the "blessself" command
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",
Long: `
Prints out information about the blessing (typically obtained from this tool)
encoded in the provided file.
`,
ArgsName: "<file>",
ArgsLong: `
<file> is the path to a file containing a blessing typically obtained from
this tool. - is used for STDIN.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("requires exactly one argument, <file>, provided %d", len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
fmt.Printf("Blessings: %v\n", blessings)
fmt.Printf("PublicKey: %v\n", blessings.PublicKey())
return nil
},
}
cmdBlessSelf = &cmdline.Command{
Name: "blessself",
Short: "Generate a self-signed blessing",
Long: `
Returns a blessing with name <name> and self-signed by the principal
specified by the environment (VEYRON_CREDENTIALS) that this tool is
running in. Optionally, the blessing can be restricted with an expiry
caveat specified using the --for flag.
`,
ArgsName: "[<name>]",
ArgsLong: `
<name> is the name used to create the self-signed blessing. If not
specified, a name will be generated based on the hostname of the
machine and the name of the user running this command.
`,
Run: func(cmd *cmdline.Command, args []string) error {
var name string
switch len(args) {
case 0:
name = defaultBlessingName()
case 1:
name = args[0]
default:
return fmt.Errorf("requires at most one argument, provided %d", len(args))
}
var caveats []security.Caveat
if flagBlessFor != 0 {
cav, err := security.ExpiryCaveat(time.Now().Add(flagBlessFor))
if err != nil {
return fmt.Errorf("failed to create expiration Caveat: %v", err)
}
caveats = append(caveats, cav)
}
blessing, err := rt.R().Principal().BlessSelf(name, caveats...)
if err != nil {
return fmt.Errorf("failed to create self-signed blessing for name %q: %v", name, err)
}
return dumpBlessings(blessing)
},
}
cmdForPeer = &cmdline.Command{
Name: "store.forpeer",
Short: "Return blessings marked for the provided peer",
Long: `
Returns blessings that are marked for the provided peer in the
BlessingStore specified by the environment (VEYRON_CREDENTIALS)
that this tool is running in.
`,
ArgsName: "[<peer_1> ... <peer_k>]",
ArgsLong: `
<peer_1> ... <peer_k> are the (human-readable string) blessings bound
to the peer. The returned blessings are marked with a pattern that is
matched by at least one of these. If no arguments are specified,
store.forpeer returns the blessings that are marked for all peers (i.e.,
blessings set on the store with the "..." pattern).
`,
Run: func(cmd *cmdline.Command, args []string) error {
return dumpBlessings(rt.R().Principal().BlessingStore().ForPeer(args...))
},
}
cmdDefault = &cmdline.Command{
Name: "store.default",
Short: "Return blessings marked as default",
Long: `
Returns blessings that are marked as default in the BlessingStore
specified by the environment (VEYRON_CREDENTIALS) that this tool
is running in.
`,
Run: func(cmd *cmdline.Command, args []string) error {
return dumpBlessings(rt.R().Principal().BlessingStore().Default())
},
}
cmdSet = &cmdline.Command{
Name: "store.set",
Short: "Set provided blessings for peer",
Long: `
Marks the provided blessings to be shared with the provided
peers on the BlessingStore specified by the environment
(VEYRON_CREDENTIALS) that this tool is running in.
'set b pattern' marks the intention to reveal b to peers who
present blessings of their own matching 'pattern'.
'set nil pattern' can be used to remove the blessings previously
associated with the pattern (by a prior 'set' command).
It is an error to call 'store.set' with blessings whose public
key does not match the public key of this principal specified
by the environment.
`,
ArgsName: "<file> <pattern>",
ArgsLong: `
<file> is the path to a file containing a blessing typically obtained
from this tool. - is used for STDIN.
<pattern> is the BlessingPattern used to identify peers with whom this
blessing can be shared with.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("requires exactly two arguments <file>, <pattern>, provided %d", cmd.Name, len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
pattern := security.BlessingPattern(args[1])
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 nil
},
}
cmdSetDefault = &cmdline.Command{
Name: "store.setdefault",
Short: "Set provided blessings as default",
Long: `
Sets the provided blessings as default in the BlessingStore specified
by the environment (VEYRON_CREDENTIALS) that this tool is running in.
It is an error to call 'store.setdefault' with blessings whose public key
does not match the public key of the principal specified by the environment.
`,
ArgsName: "<file>",
ArgsLong: `
<file> is the path to a file containing a blessing typically obtained from
this tool. - is used for STDIN.
`,
Run: func(cmd *cmdline.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("requires exactly one argument, <file>, provided %d", cmd.Name, len(args))
}
blessings, err := decodeBlessings(args[0])
if err != nil {
return fmt.Errorf("failed to decode provided blessings: %v", err)
}
if err := rt.R().Principal().BlessingStore().SetDefault(blessings); err != nil {
return fmt.Errorf("failed to set blessings %v as default: %v", blessings, err)
}
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() {
if len(os.Getenv("VEYRON_CREDENTIALS")) == 0 {
// TODO(ataly, ashankar): Handle this case
fmt.Fprintf(os.Stderr, "ERROR: Please set the VEYRON_CREDENTIALS environment variable\n")
os.Exit(2)
}
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",
Short: "Create and manage veyron principals",
Long: `
The principal tool helps create and manage blessings and the set of trusted
roots bound to a principal.
All objects are printed using base64-VOM-encoding.
`,
Children: []*cmdline.Command{cmdDump, cmdPrint, cmdBlessSelf, cmdDefault, cmdForPeer, cmdSetDefault, cmdSet, cmdSeekBlessings},
}).Main()
}
func decodeBlessings(fname string) (security.Blessings, error) {
var wire security.WireBlessings
if err := decode(fname, &wire); err != nil {
return nil, err
}
return security.NewBlessings(wire)
}
func dumpBlessings(blessings security.Blessings) error {
if blessings == nil {
return errors.New("no blessings found")
}
str, err := util.Base64VomEncode(blessings)
if err != nil {
return fmt.Errorf("base64-VOM encoding failed: %v", err)
}
fmt.Println(str)
return nil
}
func read(fname string) (string, error) {
if len(fname) == 0 {
return "", nil
}
f := os.Stdin
if fname != "-" {
var err error
if f, err = os.Open(fname); err != nil {
return "", fmt.Errorf("failed to open %q: %v", fname, err)
}
}
defer f.Close()
var buf bytes.Buffer
if _, err := io.Copy(&buf, f); err != nil {
return "", fmt.Errorf("failed to read %q: %v", fname, err)
}
return buf.String(), nil
}
func decode(fname string, val interface{}) error {
str, err := read(fname)
if err != nil {
return err
}
if err := util.Base64VomDecode(str, val); err != nil || val == nil {
return fmt.Errorf("failed to decode %q: %v", fname, err)
}
return nil
}
func defaultBlessingName() string {
var name string
if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
name = user.Username
} else {
name = "anonymous"
}
if host, _ := os.Hostname(); len(host) > 0 {
name = name + "@" + host
}
return name
}