package ca import ( "encoding/pem" "fmt" "os" "github.com/pkg/errors" "github.com/urfave/cli" "github.com/smallstep/certificates/api" "github.com/smallstep/certificates/ca" "github.com/smallstep/certificates/pki" "github.com/smallstep/cli-utils/command" "github.com/smallstep/cli-utils/errs" "github.com/smallstep/cli-utils/fileutil" "github.com/smallstep/cli-utils/ui" "go.step.sm/crypto/pemutil" "github.com/smallstep/cli/flags" ) type flowType int const ( rootsFlow flowType = iota federationFlow ) func rootsCommand() cli.Command { return cli.Command{ Name: "roots", Action: command.ActionFunc(rootsAction), Usage: "download all the root certificates", UsageText: `**step ca roots** [] [**--ca-url**=] [**--root**=] [**--context**=]`, Description: `**step ca roots** downloads a certificate bundle with all the root certificates. ## POSITIONAL ARGUMENTS : File to write all the root certificates (PEM format) ## EXAMPLES Download the roots with flags set by : ''' $ step ca roots roots.pem ''' Download the roots with custom flags: ''' $ step ca roots roots.pem \ --ca-url https://ca.example.com \ --root /path/to/root_ca.crt ''' Print the roots using flags set by : ''' $ step ca roots '''`, Flags: []cli.Flag{ flags.CaURL, flags.Force, flags.Root, flags.Context, }, } } func federationCommand() cli.Command { return cli.Command{ Name: "federation", Action: command.ActionFunc(federationAction), Usage: "download all the federated certificates", UsageText: `**step ca federation** [] [**--ca-url**=] [**--root**=] [**--context**=]`, Description: `**step ca federation** downloads a certificate bundle with all the root certificates in the federation. ## POSITIONAL ARGUMENTS : File to write federation certificates (PEM format) ## EXAMPLES Download the federated roots with flags set by : ''' $ step ca federation federation.pem ''' Download the federated roots with custom flags: ''' $ step ca federation federation.pem \ --ca-url https://ca.example.com \ --root /path/to/root_ca.crt ''' Print the federated roots using flags set by : ''' $ step ca federation '''`, Flags: []cli.Flag{ flags.CaURL, flags.Force, flags.Root, flags.Context, }, } } func rootsAction(ctx *cli.Context) error { return rootsAndFederationFlow(ctx, rootsFlow) } func federationAction(ctx *cli.Context) error { return rootsAndFederationFlow(ctx, federationFlow) } func rootsAndFederationFlow(ctx *cli.Context, typ flowType) error { if err := errs.MinMaxNumberOfArguments(ctx, 0, 1); err != nil { return err } caURL, err := flags.ParseCaURL(ctx) if err != nil { return err } root := ctx.String("root") if root == "" { root = pki.GetRootCAPath() if _, err := os.Stat(root); err != nil { return errs.RequiredFlag(ctx, "root") } } client, err := ca.NewClient(caURL, ca.WithRootFile(root)) if err != nil { return err } var certs []api.Certificate switch typ { case rootsFlow: roots, err := client.Roots() if err != nil { return err } certs = roots.Certificates case federationFlow: federation, err := client.Federation() if err != nil { return err } certs = federation.Certificates default: return errors.New("unknown flow type: this should not happen") } var data []byte for _, cert := range certs { block, err := pemutil.Serialize(cert.Certificate) if err != nil { return err } data = append(data, pem.EncodeToMemory(block)...) } if outFile := ctx.Args().Get(0); outFile != "" { if err := fileutil.WriteFile(outFile, data, 0o600); err != nil { return err } switch typ { case rootsFlow: ui.Printf("The root certificate bundle has been saved in %s.\n", outFile) case federationFlow: ui.Printf("The federation certificate bundle has been saved in %s.\n", outFile) default: return errors.New("unknown flow type: this should not happen") } } else { fmt.Print(string(data)) } return nil }