/
cmd_oneshot.go
155 lines (135 loc) · 4.71 KB
/
cmd_oneshot.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.
package client
import (
"errors"
"fmt"
"os"
"strings"
"golang.org/x/net/context"
"github.com/keybase/cli"
"github.com/adamwalz/keybase-client/go/libcmdline"
"github.com/adamwalz/keybase-client/go/libkb"
keybase1 "github.com/adamwalz/keybase-client/go/protocol/keybase1"
"github.com/keybase/go-framed-msgpack-rpc/rpc"
)
func NewCmdOneshot(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
flags := []cli.Flag{
cli.StringFlag{
Name: "paperkey",
Usage: "DANGEROUS: specify a paper key (or try the KEYBASE_PAPERKEY environment variable)",
},
cli.StringFlag{
Name: "u,username",
Usage: "specify a username (or try the KEYBASE_USERNAME environment variable)",
},
}
cmd := cli.Command{
Name: "oneshot",
Usage: "Establish a oneshot device, as in logging into keybase from a disposable container",
Action: func(c *cli.Context) {
cl.ChooseCommand(NewCmdOneshotRunner(g), "oneshot", c)
cl.SetNoStandalone()
},
Flags: flags,
Description: `"keybase oneshot" is used to establish a temporary device that will
be thrown away after the corresponding "keybase service" process exits (or
logout is called). For instance, you can use this, instead of login, for
running keybase in a Docker container.
It won't write any credential information out disk, and it won't make any
changes to the user's sigchain. It will rather hold the given paperkey in
memory for as long as the service is running (or until "keybase logout" is
called) and then will disappear.
It needs a username and a paperkey to work, either passed in via standard input,
command-line flags, or the environment.
The default way to pass a paperkey into oneshot is via standard input.
On a terminal, you'll get a familiar passphrase prompt. In a script or
programmatic environment, when standard input is not a terminal, you can pipe
the paper key to standard input, with or without a trailing newline.
Otherwise, and this is more dangerous, you can pass a paperkey via the environment
or via command line flags. Other processes running on the machine can inspect these
data, so be very careful with this input choice.
Also note that by default keybase shouldn't be run as root, but in Docker (or
other containers), root can be the best option, so you can use "keybase oneshot"
in concert with the KEYBASE_ALLOW_ROOT=1 environment variable.
Exploding messages work in oneshot mode with the caveat that you cannot run
multiple instances of oneshot mode with the same paperkey simulateously. Each
instance will each try to create ephemeral keys but require a distinct
paperkey to uniquely identify itself as a separate device. In addition,
ephemeral keys are *purged entirely* when closing the oneshot session, you
will not be able to access any old ephemeral content when starting up
again.`,
}
return cmd
}
type CmdOneshot struct {
libkb.Contextified
Username string
PaperKey string
}
func NewCmdOneshotRunner(g *libkb.GlobalContext) *CmdOneshot {
return &CmdOneshot{
Contextified: libkb.NewContextified(g),
}
}
func (c *CmdOneshot) Run() error {
protocols := []rpc.Protocol{}
if err := RegisterProtocolsWithContext(protocols, c.G()); err != nil {
return err
}
client, err := GetLoginClient(c.G())
if err != nil {
return err
}
paperkey, err := c.getPaperKey()
if err != nil {
return err
}
err = client.LoginOneshot(context.Background(), keybase1.LoginOneshotArg{SessionID: 0, Username: c.Username, PaperKey: paperkey})
return err
}
func (c *CmdOneshot) ParseArgv(ctx *cli.Context) (err error) {
nargs := len(ctx.Args())
if nargs != 0 {
return errors.New("didn't expect any arguments")
}
c.Username, err = c.getOption(ctx, "username", true /* required */)
if err != nil {
return err
}
c.PaperKey, err = c.getOption(ctx, "paperkey", false /* required */)
if err != nil {
return err
}
return nil
}
func (c *CmdOneshot) getPaperKey() (ret string, err error) {
if len(c.PaperKey) > 0 {
return c.PaperKey, nil
}
ret, err = c.G().UI.GetTerminalUI().PromptPasswordMaybeScripted(PromptDescriptorPaperKey, "paper key: ")
return ret, err
}
func (c *CmdOneshot) getOption(ctx *cli.Context, s string, required bool) (string, error) {
v := ctx.String(s)
if len(v) > 0 {
return v, nil
}
envVarName := fmt.Sprintf("KEYBASE_%s", strings.ToUpper(s))
v = os.Getenv(envVarName)
if len(v) > 0 {
return v, nil
}
var err error
if required {
err = fmt.Errorf("Need a --%s option or a %s environment variable", s, envVarName)
}
return "", err
}
func (c *CmdOneshot) GetUsage() libkb.Usage {
return libkb.Usage{
Config: false,
KbKeyring: false,
API: false,
}
}