blob: 24c856928c807eabe65509b199cb70f71531d76f [file] [log] [blame]
Todd Wang0d12d712014-10-06 17:25:41 -07001// The following enables go generate to generate the doc.go file.
2// Things to look out for:
3// 1) go:generate evaluates double-quoted strings into a single argument.
4// 2) go:generate performs $NAME expansion, so the bash cmd can't contain '$'.
5// 3) We generate into a *.tmp file first, otherwise "go run" will pick up the
6// initially empty *.go file, and fail.
7// 4) Since "go run" ignores build directives, we must manually filter out
8// main_*.go for different platforms.
9//
10//go:generate bash -c "{ echo -e '// This file was auto-generated via go generate.\n// DO NOT UPDATE MANUALLY\n\n/*' && veyron go run `echo *.go | tr ' ' '\n' | grep -v main_darwin.go` help -style=godoc ... && echo -e '*/\npackage main'; } > ./doc.go.tmp && mv ./doc.go.tmp ./doc.go"
Todd Wangfcb72a52014-10-01 09:53:56 -070011
Jiri Simsa5293dcb2014-05-10 09:56:38 -070012package main
13
14import (
15 "bytes"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070016 "fmt"
17 "io"
18 "os"
Ankurde0edf72014-05-30 15:42:10 -070019 "os/user"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070020 "time"
21
Jiri Simsa519c5072014-09-17 21:37:57 -070022 "veyron.io/veyron/veyron/lib/cmdline"
23 "veyron.io/veyron/veyron/services/identity"
24 "veyron.io/veyron/veyron/services/identity/util"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070025
Jiri Simsa519c5072014-09-17 21:37:57 -070026 "veyron.io/veyron/veyron2"
27 "veyron.io/veyron/veyron2/rt"
28 "veyron.io/veyron/veyron2/security"
29 "veyron.io/veyron/veyron2/vdl/vdlutil"
30 "veyron.io/veyron/veyron2/vlog"
Jiri Simsa5293dcb2014-05-10 09:56:38 -070031)
32
33var (
Asim Shankar61071792014-07-22 13:03:18 -070034 // Flags for the "bless" command
35 flagBlessWith string
36 flagBlessFor time.Duration
37
38 // Flags for the "seekblessing" command
39 flagSeekBlessingFor string
40 flagSeekBlessingOAuthClientID string
41 flagSeekBlessingFrom string
42
43 cmdPrint = &cmdline.Command{
44 Name: "print",
45 Short: "Print out information about the provided identity",
46 Long: `
47Print dumps out information about the identity encoded in the provided file,
48or if no filename is provided, then the identity that would be used by binaries
49started in the same environment.
50`,
51 ArgsName: "[<file>]",
52 ArgsLong: `
53<file> is the path to a file containing a base64-encoded, VOM encoded identity,
54typically obtained from this tool. - is used for STDIN and an empty string
55implies the identity encoded in the environment.
56`,
57 Run: func(cmd *cmdline.Command, args []string) error {
58 if len(args) > 1 {
59 return fmt.Errorf("require at most one argument, <file>, provided %d", len(args))
60 }
61 id := rt.R().Identity()
62 if len(args) == 1 {
63 if err := decode(args[0], &id); err != nil {
64 return err
65 }
66 }
67 fmt.Println("Name : ", id.PublicID())
68 fmt.Printf("Go Type : %T\n", id)
Asim Shankar1c5b94a2014-09-05 16:36:12 -070069 fmt.Printf("PublicKey: %v\n", id.PublicID().PublicKey())
Asim Shankar61071792014-07-22 13:03:18 -070070 fmt.Println("Any caveats in the identity are not printed")
71 return nil
72 },
73 }
74
75 cmdGenerate = &cmdline.Command{
76 Name: "generate",
77 Short: "Generate an identity with a newly minted private key",
78 Long: `
79Generate a new private key and create an identity that binds <name> to
80this key.
81
82Since the generated identity has a newly minted key, it will be typically
83unusable at other veyron services as those services have placed no trust
84in this key. In such cases, you likely want to seek a blessing for this
85generated identity using the 'bless' command.
86`,
87 ArgsName: "[<name>]",
88 ArgsLong: `
89<name> is the name to bind the newly minted private key to. If not specified,
90a name will be generated based on the hostname of the machine and the name of
91the user running this command.
92`,
93 Run: func(cmd *cmdline.Command, args []string) error {
94 r := rt.R()
95 var name string
96 switch len(args) {
97 case 0:
98 name = defaultIdentityName()
99 case 1:
100 name = args[0]
101 default:
102 return fmt.Errorf("require at most one argument, provided %d", len(args))
103 }
104 id, err := r.NewIdentity(name)
105 if err != nil {
Jiri Simsa519c5072014-09-17 21:37:57 -0700106 return fmt.Errorf("NewIdentity(%q) failed: %v", name, err)
Asim Shankar61071792014-07-22 13:03:18 -0700107 }
108 output, err := util.Base64VomEncode(id)
109 if err != nil {
110 return fmt.Errorf("failed to encode identity: %v", err)
111 }
112 fmt.Println(output)
113 return nil
114 },
115 }
116
117 cmdBless = &cmdline.Command{
118 Name: "bless",
119 Short: "Bless another identity with your own",
120 Long: `
121Bless uses the identity of the tool (either from an environment variable or
122explicitly specified using --with) to bless another identity encoded in a
123file (or STDIN). No caveats are applied to this blessing other than expiration,
124which is specified with --for.
125
126The output consists of a base64-vom encoded security.PrivateID or security.PublicID,
127depending on what was provided as input.
128
129For example, if the tool has an identity veyron/user/device, then
130bless /tmp/blessee batman
131will generate a blessing with the name veyron/user/device/batman
132
133The identity of the tool can be specified with the --with flag:
134bless --with /tmp/id /tmp/blessee batman
135`,
Nicolas LaCasse95847192014-09-29 17:28:32 -0700136 ArgsName: "<file> <name>",
Asim Shankar61071792014-07-22 13:03:18 -0700137 ArgsLong: `
138<file> is the name of the file containing a base64-vom encoded security.PublicID
139or security.PrivateID
140
141<name> is the name to use for the blessing.
142`,
143 Run: func(cmd *cmdline.Command, args []string) error {
144 if len(args) != 2 {
145 return fmt.Errorf("expected exactly two arguments (<file> and <name>), got %d", len(args))
146 }
147 blesser := rt.R().Identity()
148 if len(flagBlessWith) > 0 {
149 if err := decode(flagBlessWith, &blesser); err != nil {
150 return err
151 }
152 }
153 name := args[1]
154 var blessee security.PublicID
155 var private security.PrivateID
Asim Shankar76f431a2014-07-23 10:03:20 -0700156 encoded, err := read(args[0])
157 if err != nil {
158 return err
159 }
160 if util.Base64VomDecode(encoded, &blessee); err != nil || blessee == nil {
161 if err := util.Base64VomDecode(encoded, &private); err != nil || private == nil {
162 return fmt.Errorf("failed to extract security.PublicID or security.PrivateID: (%v, %v)", private, err)
Asim Shankar61071792014-07-22 13:03:18 -0700163 }
164 blessee = private.PublicID()
165 }
166 blessed, err := blesser.Bless(blessee, name, flagBlessFor, nil)
167 if err != nil {
168 return err
169 }
170 var object interface{} = blessed
171 if private != nil {
172 object, err = private.Derive(blessed)
173 if err != nil {
174 return err
175 }
176 }
177 output, err := util.Base64VomEncode(object)
178 if err != nil {
179 return err
180 }
181 fmt.Println(output)
182 return nil
183 },
184 }
185
186 cmdSeekBlessing = &cmdline.Command{
187 Name: "seekblessing",
188 Short: "Seek a blessing from the default veyron identity provider",
189 Long: `
190Seeks a blessing from a default, hardcoded Veyron identity provider which
191requires the caller to first authenticate with Google using OAuth. Simply
192run the command to see what happens.
193
194The blessing is sought for the identity that this tool is using. An alternative
195can be provided with the --for flag.
196`,
197 Run: func(cmd *cmdline.Command, args []string) error {
198 r := rt.R()
199 id := r.Identity()
200
201 if len(flagSeekBlessingFor) > 0 {
202 if err := decode(flagSeekBlessingFor, &id); err != nil {
203 return err
204 }
205 var err error
206 if r, err = rt.New(veyron2.RuntimeID(id)); err != nil {
207 return err
208 }
209 }
210
211 blessedChan := make(chan string)
212 defer close(blessedChan)
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700213 macaroonChan, err := getMacaroonForBlessRPC(flagSeekBlessingFrom, blessedChan)
Asim Shankar61071792014-07-22 13:03:18 -0700214 if err != nil {
215 return fmt.Errorf("failed to get authorization code from Google: %v", err)
216 }
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700217 macaroon := <-macaroonChan
218 service := <-macaroonChan
Asim Shankar61071792014-07-22 13:03:18 -0700219
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700220 ctx, cancel := r.NewContext().WithTimeout(time.Minute)
221 defer cancel()
222
Asim Shankar61071792014-07-22 13:03:18 -0700223 wait := time.Second
224 const maxWait = 20 * time.Second
225 var reply vdlutil.Any
226 for {
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700227 blesser, err := identity.BindMacaroonBlesser(service, r.Client())
Asim Shankar61071792014-07-22 13:03:18 -0700228 if err == nil {
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700229 reply, err = blesser.Bless(ctx, macaroon)
Asim Shankar61071792014-07-22 13:03:18 -0700230 }
231 if err != nil {
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700232 vlog.Infof("Failed to get blessing from %q: %v, will try again in %v", service, err, wait)
Asim Shankar61071792014-07-22 13:03:18 -0700233 time.Sleep(wait)
Asim Shankar1c3b1812014-07-31 18:54:51 -0700234 if wait = wait + 2*time.Second; wait > maxWait {
Asim Shankar61071792014-07-22 13:03:18 -0700235 wait = maxWait
236 }
237 continue
238 }
239 blessed, ok := reply.(security.PublicID)
240 if !ok {
241 return fmt.Errorf("received %T, want security.PublicID", reply)
242 }
243 if id, err = id.Derive(blessed); err != nil {
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700244 return fmt.Errorf("received incompatible blessing from %q: %v", service, err)
Asim Shankar61071792014-07-22 13:03:18 -0700245 }
246 output, err := util.Base64VomEncode(id)
247 if err != nil {
248 return fmt.Errorf("failed to encode blessing: %v", err)
249 }
250 fmt.Println(output)
251 blessedChan <- fmt.Sprint(blessed)
Suharsh Sivakumard308c7e2014-10-03 12:46:50 -0700252 // Wait for getTokenForBlessRPC to clean up:
253 <-macaroonChan
Asim Shankar61071792014-07-22 13:03:18 -0700254 return nil
255 }
256 },
257 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700258)
259
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700260func main() {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700261 rt.Init()
Asim Shankar61071792014-07-22 13:03:18 -0700262 cmdBless.Flags.StringVar(&flagBlessWith, "with", "", "Path to file containing identity to bless with (or - for STDIN)")
263 cmdBless.Flags.DurationVar(&flagBlessFor, "for", 365*24*time.Hour, "Expiry time of blessing (defaults to 1 year)")
264 cmdSeekBlessing.Flags.StringVar(&flagSeekBlessingFor, "for", "", "Path to file containing identity to bless (or - for STDIN)")
Asim Shankar4877d3c2014-10-03 19:40:29 -0700265 cmdSeekBlessing.Flags.StringVar(&flagSeekBlessingFrom, "from", "https://proxy.envyor.com:8125/google", "URL to use to begin the seek blessings process")
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700266
Asim Shankar61071792014-07-22 13:03:18 -0700267 (&cmdline.Command{
268 Name: "identity",
Todd Wangfcb72a52014-10-01 09:53:56 -0700269 Short: "Create and manage veyron identities",
Asim Shankar61071792014-07-22 13:03:18 -0700270 Long: `
271The identity tool helps create and manage keys and blessings that are used for
272identification in veyron.
273`,
274 Children: []*cmdline.Command{cmdPrint, cmdGenerate, cmdBless, cmdSeekBlessing},
275 }).Main()
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700276}
277
Asim Shankar76f431a2014-07-23 10:03:20 -0700278func read(fname string) (string, error) {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700279 if len(fname) == 0 {
Asim Shankar76f431a2014-07-23 10:03:20 -0700280 return "", nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700281 }
Asim Shankar76f431a2014-07-23 10:03:20 -0700282 f := os.Stdin
283 if fname != "-" {
284 var err error
285 if f, err = os.Open(fname); err != nil {
286 return "", fmt.Errorf("failed to open %q: %v", fname, err)
287 }
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700288 }
289 defer f.Close()
290 var buf bytes.Buffer
291 if _, err := io.Copy(&buf, f); err != nil {
Asim Shankar76f431a2014-07-23 10:03:20 -0700292 return "", fmt.Errorf("failed to read %q: %v", fname, err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700293 }
Asim Shankar76f431a2014-07-23 10:03:20 -0700294 return buf.String(), nil
295}
296
297func decode(fname string, val interface{}) error {
298 str, err := read(fname)
299 if err != nil {
300 return err
301 }
302 if err := util.Base64VomDecode(str, val); err != nil || val == nil {
Asim Shankar61071792014-07-22 13:03:18 -0700303 return fmt.Errorf("failed to decode %q: %v", fname, err)
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700304 }
Asim Shankar61071792014-07-22 13:03:18 -0700305 return nil
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700306}
Ankurde0edf72014-05-30 15:42:10 -0700307
308func defaultIdentityName() string {
309 var name string
310 if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
311 name = user.Username
312 } else {
313 name = "anonymous"
314 }
315 if host, _ := os.Hostname(); len(host) > 0 {
316 name = name + "@" + host
317 }
318 return name
319}