From ea436a3a34a9923f224915ec11f45be8b28e9d77 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Thu, 7 Oct 2021 18:49:48 -0700 Subject: [PATCH] Allow to initialize using azurekms as a kms. --- command/ca/init.go | 90 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/command/ca/init.go b/command/ca/init.go index 8185f253..bffbaeab 100644 --- a/command/ca/init.go +++ b/command/ca/init.go @@ -1,6 +1,7 @@ package ca import ( + "context" "crypto/rand" "crypto/x509" "fmt" @@ -11,6 +12,7 @@ import ( "github.com/manifoldco/promptui" "github.com/smallstep/certificates/cas/apiv1" + "github.com/smallstep/certificates/kms" "github.com/smallstep/certificates/pki" "github.com/smallstep/cli/command" "github.com/smallstep/cli/crypto/pemutil" @@ -18,6 +20,9 @@ import ( "github.com/smallstep/cli/ui" "github.com/smallstep/cli/utils" "github.com/urfave/cli" + + // Enable azurekms + _ "github.com/smallstep/certificates/kms/azurekms" ) func initCommand() cli.Command { @@ -26,10 +31,11 @@ func initCommand() cli.Command { Action: cli.ActionFunc(initAction), Usage: "initialize the CA PKI", UsageText: `**step ca init** -[**--root**=] [**--key**=] [**--pki**] [**--ssh**] [**--name**=] +[**--root**=] [**--key**=] [**--pki**] [**--ssh**] +[**--helm**] [**--deployment-type**=] [**--name**=] [**--dns**=] [**--address**=
] [**--provisioner**=] [**--provisioner-password-file**=] [**--password-file**=] -[**--with-ca-url**=] [**--no-db**]`, +[**--with-ca-url**=] [**--ra**=] [**--kms**=] [**--no-db**]`, Description: `**step ca init** command initializes a public key infrastructure (PKI) to be used by the Certificate Authority.`, Flags: []cli.Flag{ @@ -111,6 +117,13 @@ Use the '--dns' flag multiple times to configure multiple DNS names.`, Name: "ra", Usage: `The registration authority to use. Currently "StepCAS" and "CloudCAS" are supported.`, }, + cli.StringFlag{ + Name: "kms", + Usage: `The key manager service to use to manage keys. Options are: + **azurekms** + : Use Azure Key Vault to manage X.509 and SSH keys. The key URIs have + the following format .`, + }, cli.StringFlag{ Name: "issuer", Usage: `The registration authority issuer to use. @@ -165,6 +178,7 @@ func initAction(ctx *cli.Context) (err error) { root := ctx.String("root") key := ctx.String("key") ra := strings.ToLower(ctx.String("ra")) + kmsName := strings.ToLower(ctx.String("kms")) pkiOnly := ctx.Bool("pki") noDB := ctx.Bool("no-db") helm := ctx.Bool("helm") @@ -183,6 +197,10 @@ func initAction(ctx *cli.Context) (err error) { } case ra != "" && ra != apiv1.CloudCAS && ra != apiv1.StepCAS: return errs.InvalidFlagValue(ctx, "ra", ctx.String("ra"), "StepCAS or CloudCAS") + case kmsName != "" && kmsName != "azurekms": + return errs.InvalidFlagValue(ctx, "kms", ctx.String("kms"), "azurekms") + case kmsName != "" && ra != "": + return errs.IncompatibleFlagWithFlag(ctx, "kms", "ra") case pkiOnly && noDB: return errs.IncompatibleFlagWithFlag(ctx, "pki", "no-db") case pkiOnly && helm: @@ -212,6 +230,7 @@ func initAction(ctx *cli.Context) (err error) { var name, org, resource string var casOptions apiv1.Options var deploymentType pki.DeploymentType + var opts []pki.Option switch ra { case apiv1.CloudCAS: var create bool @@ -356,14 +375,68 @@ func initAction(ctx *cli.Context) (err error) { if err != nil { return err } - org = name + + // Get names for key managers keys. + // Currently only azure is supported. + var keyManager kms.KeyManager + if kmsName != "" { + var rootURI, intermediateURI, sshHostURI, sshUserURI string + keyManager, err = kms.New(context.Background(), kms.Options{ + Type: kmsName, + }) + if err != nil { + return err + } + + var validateFunc func(s string) error + if v, ok := keyManager.(interface{ ValidateName(s string) error }); ok { + validateFunc = v.ValidateName + } else { + validateFunc = func(s string) error { + return nil + } + } + + ui.Println("What would be the URI for the root certificate key?") + rootURI, err = ui.Prompt("(e.g. azurekms:name=my-root-key;vault=my-vault)", ui.WithValidateFunc(validateFunc)) + if err != nil { + return err + } + ui.Println("What would be the URI for the intermediate certificate key?") + intermediateURI, err = ui.Prompt("(e.g. azurekms:name=my-intermediate-key;vault=my-vault)", ui.WithValidateFunc(validateFunc)) + if err != nil { + return err + } + + if ctx.Bool("ssh") { + ui.Println("What would be the URI for the SSH host key?") + sshHostURI, err = ui.Prompt("(e.g. azurekms:name=my-host-key;vault=my-vault)", ui.WithValidateFunc(validateFunc)) + if err != nil { + return err + } + + ui.Println("What would be the URI for the SSH user key?") + sshUserURI, err = ui.Prompt("(e.g. azurekms:name=my-user-key;vault=my-vault)", ui.WithValidateFunc(validateFunc)) + if err != nil { + return err + } + } + + // Add uris to the pki options. Empty URIs will be ignored. + opts = append(opts, pki.WithKMS(kmsName)) + opts = append(opts, pki.WithKeyURIs(rootURI, intermediateURI, sshHostURI, sshUserURI)) + } + + // set org and resource to pki name + org, resource = name, name + casOptions = apiv1.Options{ - Type: apiv1.SoftCAS, - IsCreator: true, + Type: apiv1.SoftCAS, + IsCreator: true, + KeyManager: keyManager, } } - var opts []pki.Option if pkiOnly { opts = append(opts, pki.WithPKIOnly()) } else { @@ -409,12 +482,12 @@ func initAction(ctx *cli.Context) (err error) { } } - opts = []pki.Option{ + opts = append(opts, pki.WithAddress(address), pki.WithCaURL(caURL), pki.WithDNSNames(dnsNames), pki.WithDeploymentType(deploymentType), - } + ) if deploymentType == pki.StandaloneDeployment { opts = append(opts, pki.WithProvisioner(provisioner)) } @@ -473,6 +546,7 @@ func initAction(ctx *cli.Context) (err error) { // Generate root certificate if not set. if rootCrt == nil && rootKey == nil { ui.Print("Generating root certificate... ") + fmt.Printf("name: %s, org: %s, resource: %s\n", name, org, resource) root, err = p.GenerateRootCertificate(name, org, resource, pass) if err != nil { return err