-
Notifications
You must be signed in to change notification settings - Fork 309
Add handling of cnf claim
#1092
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
c85690b
4616c58
e207f94
3eb2564
2f0500a
8a2d36e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import ( | |
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/pkg/errors" | ||
| "github.com/smallstep/certificates/api" | ||
| "github.com/smallstep/certificates/pki" | ||
| "github.com/smallstep/cli/flags" | ||
|
|
@@ -12,6 +13,8 @@ import ( | |
| "github.com/urfave/cli" | ||
| "go.step.sm/cli-utils/command" | ||
| "go.step.sm/cli-utils/errs" | ||
| "go.step.sm/crypto/pemutil" | ||
| "golang.org/x/crypto/ssh" | ||
| ) | ||
|
|
||
| func tokenCommand() cli.Command { | ||
|
|
@@ -27,6 +30,7 @@ func tokenCommand() cli.Command { | |
| [**--output-file**=<file>] [**--kms**=uri] [**--key**=<file>] [**--san**=<SAN>] [**--offline**] | ||
| [**--revoke**] [**--x5c-cert**=<file>] [**--x5c-key**=<file>] [**--x5c-insecure**] | ||
| [**--sshpop-cert**=<file>] [**--sshpop-key**=<file>] | ||
| [**--cnf-file**=<file>] [**--cnf-kid**=<fingerprint>] | ||
| [**--ssh**] [**--host**] [**--principal**=<name>] [**--k8ssa-token-path**=<file>] | ||
| [**--ca-url**=<uri>] [**--root**=<file>] [**--context**=<name>]`, | ||
| Description: `**step ca token** command generates a one-time token granting access to the | ||
|
|
@@ -82,6 +86,11 @@ Get a new token that becomes valid in 30 minutes and expires 5 minutes after tha | |
| $ step ca token --not-before 30m --not-after 35m internal.example.com | ||
| ''' | ||
|
|
||
| Get a new token with a confirmation claim to enforce the use of a given CSR: | ||
| ''' | ||
| step ca token --cnf-file internal.csr internal.smallstep.com | ||
| ''' | ||
|
|
||
| Get a new token signed with the given private key, the public key must be | ||
| configured in the certificate authority: | ||
| ''' | ||
|
|
@@ -133,6 +142,11 @@ Get a new token for an SSH host certificate: | |
| $ step ca token my-remote.hostname --ssh --host | ||
| ''' | ||
|
|
||
| Get a new token with a confirmation claim to enforce the use of a given public key: | ||
| ''' | ||
| step ca token --ssh --host --cnf-file internal.pub internal.smallstep.com | ||
| ''' | ||
|
|
||
| Generate a renew token and use it in a renew after expiry request: | ||
| ''' | ||
| $ TOKEN=$(step ca token --x5c-cert internal.crt --x5c-key internal.key --renew internal.example.com) | ||
|
|
@@ -186,6 +200,8 @@ multiple principals.`, | |
| flags.SSHPOPKey, | ||
| flags.NebulaCert, | ||
| flags.NebulaKey, | ||
| flags.ConfirmationFile, | ||
| flags.ConfirmationKid, | ||
| cli.StringFlag{ | ||
| Name: "key", | ||
| Usage: `The private key <file> used to sign the JWT. This is usually downloaded from | ||
|
|
@@ -240,6 +256,9 @@ func tokenAction(ctx *cli.Context) error { | |
| isSSH := ctx.Bool("ssh") | ||
| isHost := ctx.Bool("host") | ||
| principals := ctx.StringSlice("principal") | ||
| // confirmation claims | ||
| cnfFile := ctx.String("cnf-file") | ||
| cnfKid := ctx.String("cnf-kid") | ||
|
|
||
| switch { | ||
| case isSSH && len(sans) > 0: | ||
|
|
@@ -252,6 +271,8 @@ func tokenAction(ctx *cli.Context) error { | |
| return errs.RequiredWithFlag(ctx, "host", "ssh") | ||
| case !isSSH && len(principals) > 0: | ||
| return errs.RequiredWithFlag(ctx, "principal", "ssh") | ||
| case cnfFile != "" && cnfKid != "": | ||
| return errs.IncompatibleFlagWithFlag(ctx, "cnf-file", "cnf-kid") | ||
| } | ||
|
|
||
| // Default token type is always a 'Sign' token. | ||
|
|
@@ -295,6 +316,31 @@ func tokenAction(ctx *cli.Context) error { | |
| } | ||
| } | ||
|
|
||
| // Add options to create a confirmation claim if a CSR or SSH public key is | ||
| // passed. | ||
| var tokenOpts []cautils.Option | ||
| if cnfFile != "" { | ||
| in, err := utils.ReadFile(cnfFile) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| if isSSH { | ||
| sshPub, _, _, _, err := ssh.ParseAuthorizedKey(in) | ||
| if err != nil { | ||
| return errors.Wrap(err, "error parsing ssh public key") | ||
| } | ||
| tokenOpts = append(tokenOpts, cautils.WithSSHPublicKey(sshPub)) | ||
| } else { | ||
| csr, err := pemutil.ParseCertificateRequest(in) | ||
| if err != nil { | ||
| return errors.Wrap(err, "error parsing certificate request") | ||
| } | ||
| tokenOpts = append(tokenOpts, cautils.WithCertificateRequest(csr)) | ||
|
Comment on lines
+334
to
+345
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of passing the CSR or SSH public key, the claim itself could be calculated here, and just pass it as the fingerprint directly? Or is there some flow in which to defer calculation of the fingerprint is necessary?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| } | ||
| } else if cnfKid != "" { | ||
| tokenOpts = append(tokenOpts, cautils.WithConfirmationKid(cnfKid)) | ||
| } | ||
|
|
||
| // --san and --type revoke are incompatible. Revocation tokens do not support SANs. | ||
| if typ == cautils.RevokeType && len(sans) > 0 { | ||
| return errs.IncompatibleFlagWithFlag(ctx, "san", "revoke") | ||
|
|
@@ -327,7 +373,7 @@ func tokenAction(ctx *cli.Context) error { | |
| return err | ||
| } | ||
| } else { | ||
| token, err = cautils.NewTokenFlow(ctx, typ, subject, sans, caURL, root, notBefore, notAfter, certNotBefore, certNotAfter) | ||
| token, err = cautils.NewTokenFlow(ctx, typ, subject, sans, caURL, root, notBefore, notAfter, certNotBefore, certNotAfter, tokenOpts...) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -379,6 +379,21 @@ be stored in the 'sshpop' header.`, | |
| be stored in the 'nebula' header.`, | ||
| } | ||
|
|
||
| // ConfirmationFile is a cli.Flag used to add a confirmation claim in the | ||
| // tokens. It will add a confirmation kid with the fingerprint of the CSR or | ||
| // an SSH public key. | ||
| ConfirmationFile = cli.StringFlag{ | ||
| Name: "cnf-file", | ||
| Usage: `The CSR or SSH public key <file> to restrict this token for.`, | ||
| } | ||
|
|
||
| // ConfirmationKid is a cli.Flag used to add a confirmation claim in the | ||
| // token. | ||
| ConfirmationKid = cli.StringFlag{ | ||
| Name: "cnf-kid", | ||
| Usage: `The <fingerprint> of the CSR or SSH public key to restrict this token for.`, | ||
| } | ||
|
hslatman marked this conversation as resolved.
Outdated
|
||
|
|
||
|
Comment on lines
+382
to
+394
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these include something about the SSH public key, or want to keep it simple?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They used to include it, but due to a comment in the certificates PR that suggested removing the cnf for SSH, I've removed it from here. Although I kept the SSH public key in the context. |
||
| // Team is a cli.Flag used to pass the team ID. | ||
| Team = cli.StringFlag{ | ||
| Name: "team", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF14RP3HJkO1yoZHjo9t/4bJgyJGiSPxhm6FApa3VtG1 mariano@overlook.local |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| -----BEGIN CERTIFICATE REQUEST----- | ||
| MIIBBDCBqwIBADAbMRkwFwYDVQQDDBB0ZXN0QGV4YW1wbGUuY29tMFkwEwYHKoZI | ||
| zj0CAQYIKoZIzj0DAQcDQgAEPj0tlICeGPiz361yM+AGlZmDK+N/cT0SVloozOQH | ||
| 1ljdNbookliEX8eRnFnelZRaql1KhrVOXhfwBmd/eGhti6AuMCwGCSqGSIb3DQEJ | ||
| DjEfMB0wGwYDVR0RBBQwEoEQdGVzdEBleGFtcGxlLmNvbTAKBggqhkjOPQQDAgNI | ||
| ADBFAiEA4WuukEVIFJQHNqlZVsWtsWsSVLNRCxBBJfH7/+txNw4CIGyK3eo5MDvR | ||
| DepPHVRF16/b+iW/4HgAgIC90+5Q4IrL | ||
| -----END CERTIFICATE REQUEST----- |
Uh oh!
There was an error while loading. Please reload this page.