Skip to content

Commit 6f71570

Browse files
authored
Merge pull request #57 from smallstep/mariano/federation
Add support for multiple roots and federation
2 parents fc8e827 + 4b297bf commit 6f71570

File tree

4 files changed

+180
-4
lines changed

4 files changed

+180
-4
lines changed

Gopkg.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

command/ca/ca.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ $ step ca renew internal.crt internal.key \
7171
newTokenCommand(),
7272
newCertificateCommand(),
7373
renewCertificateCommand(),
74-
rootComand(),
7574
provisioner.Command(),
7675
signCertificateCommand(),
76+
rootComand(),
77+
rootsCommand(),
78+
federationCommand(),
7779
},
7880
}
7981

command/ca/federation.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package ca
2+
3+
import (
4+
"encoding/pem"
5+
"os"
6+
7+
"github.com/pkg/errors"
8+
"github.com/smallstep/certificates/api"
9+
"github.com/smallstep/certificates/ca"
10+
"github.com/smallstep/cli/command"
11+
"github.com/smallstep/cli/crypto/pemutil"
12+
"github.com/smallstep/cli/crypto/pki"
13+
"github.com/smallstep/cli/errs"
14+
"github.com/smallstep/cli/flags"
15+
"github.com/smallstep/cli/ui"
16+
"github.com/smallstep/cli/utils"
17+
"github.com/urfave/cli"
18+
)
19+
20+
type flowType int
21+
22+
const (
23+
rootsFlow flowType = iota
24+
federationFlow
25+
)
26+
27+
func rootsCommand() cli.Command {
28+
return cli.Command{
29+
Name: "roots",
30+
Action: command.ActionFunc(rootsAction),
31+
Usage: "download all the root certificates",
32+
UsageText: `**step ca roots** <roots-file>
33+
[**--ca-url**=<uri>] [**--root**=<file>]`,
34+
Description: `**step ca roots** downloads a certificate bundle with all the root
35+
certificates.
36+
37+
## POSITIONAL ARGUMENTS
38+
39+
<roots-file>
40+
: File to write all the root certificates (PEM format)
41+
42+
## EXAMPLES
43+
44+
Download the roots with flags set by <step ca bootstrap>:
45+
'''
46+
$ step ca roots roots.pem
47+
'''
48+
49+
Download the roots with custom flags:
50+
'''
51+
$ step ca roots roots.pem \
52+
--ca-url https://ca.example.com \
53+
--root /path/to/root_ca.crt
54+
'''`,
55+
Flags: []cli.Flag{
56+
caURLFlag,
57+
rootFlag,
58+
flags.Force,
59+
},
60+
}
61+
}
62+
63+
func federationCommand() cli.Command {
64+
return cli.Command{
65+
Name: "federation",
66+
Action: command.ActionFunc(federationAction),
67+
Usage: "download all the federated certificates",
68+
UsageText: `**step ca federation** <federation-file>
69+
[**--ca-url**=<uri>] [**--root**=<file>]`,
70+
Description: `**step ca federation** downloads a certificate bundle with all the root
71+
certificates in the federation.
72+
73+
## POSITIONAL ARGUMENTS
74+
75+
<federation-file>
76+
: File to write federation certificates (PEM format)
77+
78+
## EXAMPLES
79+
80+
Download the federated roots with flags set by <step ca bootstrap>:
81+
'''
82+
$ step ca federation federation.pem
83+
'''
84+
85+
Download the federated roots with custom flags:
86+
'''
87+
$ step ca federation federation.pem \
88+
--ca-url https://ca.example.com \
89+
--root /path/to/root_ca.crt
90+
'''
91+
`,
92+
Flags: []cli.Flag{
93+
caURLFlag,
94+
rootFlag,
95+
flags.Force,
96+
},
97+
}
98+
}
99+
100+
func rootsAction(ctx *cli.Context) error {
101+
return rootsAndFederationFlow(ctx, rootsFlow)
102+
}
103+
104+
func federationAction(ctx *cli.Context) error {
105+
return rootsAndFederationFlow(ctx, federationFlow)
106+
}
107+
108+
func rootsAndFederationFlow(ctx *cli.Context, typ flowType) error {
109+
if err := errs.NumberOfArguments(ctx, 1); err != nil {
110+
return err
111+
}
112+
113+
caURL := ctx.String("ca-url")
114+
if len(caURL) == 0 {
115+
return errs.RequiredFlag(ctx, "ca-url")
116+
}
117+
118+
root := ctx.String("root")
119+
if len(root) == 0 {
120+
root = pki.GetRootCAPath()
121+
if _, err := os.Stat(root); err != nil {
122+
return errs.RequiredFlag(ctx, "root")
123+
}
124+
}
125+
126+
client, err := ca.NewClient(caURL, ca.WithRootFile(root))
127+
if err != nil {
128+
return err
129+
}
130+
131+
var certs []api.Certificate
132+
switch typ {
133+
case rootsFlow:
134+
roots, err := client.Roots()
135+
if err != nil {
136+
return err
137+
}
138+
certs = roots.Certificates
139+
case federationFlow:
140+
federation, err := client.Federation()
141+
if err != nil {
142+
return err
143+
}
144+
certs = federation.Certificates
145+
default:
146+
return errors.New("unknown flow type: this should not happen")
147+
}
148+
149+
var data []byte
150+
for _, cert := range certs {
151+
block, err := pemutil.Serialize(cert.Certificate)
152+
if err != nil {
153+
return err
154+
}
155+
data = append(data, pem.EncodeToMemory(block)...)
156+
}
157+
158+
outFile := ctx.Args().Get(0)
159+
if err := utils.WriteFile(outFile, data, 0600); err != nil {
160+
return err
161+
}
162+
163+
switch typ {
164+
case rootsFlow:
165+
ui.Printf("The root certificate bundle has been saved in %s.\n", outFile)
166+
case federationFlow:
167+
ui.Printf("The federation certificate bundle has been saved in %s.\n", outFile)
168+
default:
169+
return errors.New("unknown flow type: this should not happen")
170+
}
171+
172+
return nil
173+
}

crypto/pki/pki.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,8 @@ func (p *PKI) Save() error {
285285
}
286286

287287
config := authority.Config{
288-
Root: p.root,
288+
Root: []string{p.root},
289+
FederatedRoots: []string{},
289290
IntermediateCert: p.intermediate,
290291
IntermediateKey: p.intermediateKey,
291292
Address: p.address,

0 commit comments

Comments
 (0)