Skip to content

Commit a329403

Browse files
authored
Merge pull request #64 from smallstep/mariano/init-flags
Add flags to specify the arguments to `step ca init`
2 parents 0ba3b19 + 5944c77 commit a329403

File tree

4 files changed

+109
-18
lines changed

4 files changed

+109
-18
lines changed

command/ca/init.go

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import (
66
"io"
77
"strings"
88

9-
"github.com/smallstep/cli/crypto/pemutil"
10-
119
"github.com/smallstep/cli/command"
10+
"github.com/smallstep/cli/crypto/pemutil"
1211
"github.com/smallstep/cli/crypto/pki"
1312
"github.com/smallstep/cli/errs"
1413
stepx509 "github.com/smallstep/cli/pkg/x509"
1514
"github.com/smallstep/cli/ui"
15+
"github.com/smallstep/cli/utils"
1616
"github.com/urfave/cli"
1717
)
1818

@@ -38,7 +38,31 @@ func initCommand() cli.Command {
3838
},
3939
cli.BoolFlag{
4040
Name: "pki",
41-
Usage: "Generate only the PKI without the CA configuration",
41+
Usage: "Generate only the PKI without the CA configuration.",
42+
},
43+
cli.StringFlag{
44+
Name: "name",
45+
Usage: "The <name> of the new PKI.",
46+
},
47+
cli.StringFlag{
48+
Name: "dns",
49+
Usage: "The comma sepparated DNS <names> or IP addresses of the new CA.",
50+
},
51+
cli.StringFlag{
52+
Name: "address",
53+
Usage: "The <address> that the new CA will listen at.",
54+
},
55+
cli.StringFlag{
56+
Name: "provisioner",
57+
Usage: "The <name> of the first provisioner.",
58+
},
59+
cli.StringFlag{
60+
Name: "password-file",
61+
Usage: `The path to the <file> containing the password to encrypt the keys.`,
62+
},
63+
cli.StringFlag{
64+
Name: "with-ca-url",
65+
Usage: `<URI> of the Step Certificate Authority to write in defaults.json`,
4266
},
4367
},
4468
}
@@ -49,12 +73,14 @@ func initAction(ctx *cli.Context) error {
4973
return err
5074
}
5175

76+
var password string
5277
var rootCrt *stepx509.Certificate
5378
var rootKey interface{}
5479

5580
root := ctx.String("root")
5681
key := ctx.String("key")
5782
configure := !ctx.Bool("pki")
83+
caURL := ctx.String("with-ca-url")
5884
switch {
5985
case len(root) > 0 && len(key) == 0:
6086
return errs.RequiredWithFlag(ctx, "root", "key")
@@ -70,18 +96,29 @@ func initAction(ctx *cli.Context) error {
7096
}
7197
}
7298

99+
passwordFile := ctx.String("password-file")
100+
if passwordFile != "" {
101+
b, err := utils.ReadPasswordFromFile(passwordFile)
102+
if err != nil {
103+
return err
104+
}
105+
password = string(b)
106+
}
107+
73108
p, err := pki.New(pki.GetPublicPath(), pki.GetSecretsPath(), pki.GetConfigPath())
74109
if err != nil {
75110
return err
76111
}
77112

78-
name, err := ui.Prompt("What would you like to name your new PKI? (e.g. Smallstep)", ui.WithValidateNotEmpty())
113+
name, err := ui.Prompt("What would you like to name your new PKI? (e.g. Smallstep)",
114+
ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("name")))
79115
if err != nil {
80116
return err
81117
}
82118

83119
if configure {
84-
names, err := ui.Prompt("What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.])", ui.WithValidateFunc(ui.DNS()))
120+
names, err := ui.Prompt("What DNS names or IP addresses would you like to add to your new CA? (e.g. ca.smallstep.com[,1.1.1.1,etc.])",
121+
ui.WithValidateFunc(ui.DNS()), ui.WithValue(ctx.String("dns")))
85122
if err != nil {
86123
return err
87124
}
@@ -95,22 +132,26 @@ func initAction(ctx *cli.Context) error {
95132
dnsNames = append(dnsNames, strings.TrimSpace(name))
96133
}
97134

98-
address, err := ui.Prompt("What address will your new CA listen at? (e.g. :443)", ui.WithValidateFunc(ui.Address()))
135+
address, err := ui.Prompt("What address will your new CA listen at? (e.g. :443)",
136+
ui.WithValidateFunc(ui.Address()), ui.WithValue(ctx.String("address")))
99137
if err != nil {
100138
return err
101139
}
102140

103-
provisioner, err := ui.Prompt("What would you like to name the first provisioner for your new CA? (e.g. [email protected])", ui.WithValidateNotEmpty())
141+
provisioner, err := ui.Prompt("What would you like to name the first provisioner for your new CA? (e.g. [email protected])",
142+
ui.WithValidateNotEmpty(), ui.WithValue(ctx.String("provisioner")))
104143
if err != nil {
105144
return err
106145
}
107146

108147
p.SetProvisioner(provisioner)
109148
p.SetAddress(address)
110149
p.SetDNSNames(dnsNames)
150+
p.SetCAURL(caURL)
111151
}
112152

113-
pass, err := ui.PromptPasswordGenerate("What do you want your password to be? [leave empty and we'll generate one]", ui.WithRichPrompt())
153+
pass, err := ui.PromptPasswordGenerate("What do you want your password to be? [leave empty and we'll generate one]",
154+
ui.WithRichPrompt(), ui.WithValue(password))
114155
if err != nil {
115156
return err
116157
}

crypto/pki/pki.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ type PKI struct {
119119
provisioner string
120120
address string
121121
dnsNames []string
122+
caURL string
122123
}
123124

124125
// New creates a new PKI configuration.
@@ -193,6 +194,11 @@ func (p *PKI) SetDNSNames(s []string) {
193194
p.dnsNames = s
194195
}
195196

197+
// SetCAURL sets the ca-url to use in the defaults.json.
198+
func (p *PKI) SetCAURL(s string) {
199+
p.caURL = s
200+
}
201+
196202
// GenerateKeyPairs generates the key pairs used by the certificate authority.
197203
func (p *PKI) GenerateKeyPairs(pass []byte) error {
198204
var err error
@@ -315,21 +321,23 @@ func (p *PKI) Save() error {
315321
}
316322

317323
// Generate the CA URL.
318-
url := p.dnsNames[0]
319-
_, port, err := net.SplitHostPort(p.address)
320-
if err != nil {
321-
return errors.Wrapf(err, "error parsing %s", p.address)
322-
}
323-
if port == "443" {
324-
url = fmt.Sprintf("https://%s", url)
325-
} else {
326-
url = fmt.Sprintf("https://%s:%s", url, port)
324+
if p.caURL == "" {
325+
p.caURL = p.dnsNames[0]
326+
_, port, err := net.SplitHostPort(p.address)
327+
if err != nil {
328+
return errors.Wrapf(err, "error parsing %s", p.address)
329+
}
330+
if port == "443" {
331+
p.caURL = fmt.Sprintf("https://%s", p.caURL)
332+
} else {
333+
p.caURL = fmt.Sprintf("https://%s:%s", p.caURL, port)
334+
}
327335
}
328336

329337
defaults := &caDefaults{
330338
Root: p.root,
331339
CAConfig: p.config,
332-
CAUrl: url,
340+
CAUrl: p.caURL,
333341
Fingerprint: p.rootFingerprint,
334342
}
335343
b, err = json.MarshalIndent(defaults, "", " ")

ui/options.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,44 @@ import (
77
type options struct {
88
mask rune
99
defaultValue string
10+
value string
1011
allowEdit bool
1112
printTemplate string
1213
promptTemplates *promptui.PromptTemplates
1314
selectTemplates *promptui.SelectTemplates
1415
validateFunc promptui.ValidateFunc
1516
}
1617

18+
// apply applies the given options.
1719
func (o *options) apply(opts []Option) *options {
1820
for _, fn := range opts {
1921
fn(o)
2022
}
2123
return o
2224
}
2325

26+
// getValue validates the value and returns it.
27+
func (o *options) getValue() (string, error) {
28+
if o.validateFunc == nil {
29+
return o.value, nil
30+
}
31+
if err := o.validateFunc(o.value); err != nil {
32+
return "", err
33+
}
34+
return o.value, nil
35+
}
36+
37+
// getValueBytes validates the value and returns it as a byte slice.
38+
func (o *options) getValueBytes() ([]byte, error) {
39+
if o.validateFunc == nil {
40+
return []byte(o.value), nil
41+
}
42+
if err := o.validateFunc(o.value); err != nil {
43+
return nil, err
44+
}
45+
return []byte(o.value), nil
46+
}
47+
2448
// Option is the type of the functions that modify the prompt options.
2549
type Option func(*options)
2650

@@ -38,6 +62,14 @@ func WithDefaultValue(s string) Option {
3862
}
3963
}
4064

65+
// WithValue sets a custom string as the result of a prompt. If value is set,
66+
// the prompt won't be displayed.
67+
func WithValue(value string) Option {
68+
return func(o *options) {
69+
o.value = value
70+
}
71+
}
72+
4173
// WithAllowEdit if true, let's the user edit the default value set.
4274
func WithAllowEdit(b bool) Option {
4375
return func(o *options) {

ui/ui.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ func Prompt(label string, opts ...Option) (string, error) {
9898
}
9999
o.apply(opts)
100100

101+
// Return value if set
102+
if o.value != "" {
103+
return o.getValue()
104+
}
105+
101106
prompt := &promptui.Prompt{
102107
Label: label,
103108
Default: o.defaultValue,
@@ -128,6 +133,11 @@ func PromptPassword(label string, opts ...Option) ([]byte, error) {
128133
}
129134
o.apply(opts)
130135

136+
// Return value if set
137+
if o.value != "" {
138+
return o.getValueBytes()
139+
}
140+
131141
prompt := &promptui.Prompt{
132142
Label: label,
133143
Mask: o.mask,

0 commit comments

Comments
 (0)