Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion command/ca/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
[**--sshpop-cert**=<file>] [**--sshpop-key**=<file>]
[**--cnf**=<fingerprint>] [**--cnf-file**=<file>]
[**--ssh**] [**--host**] [**--principal**=<name>] [**--k8ssa-token-path**=<file>]
[**--ca-url**=<uri>] [**--root**=<file>] [**--context**=<name>]`,
[**--ca-url**=<uri>] [**--root**=<file>] [**--context**=<name>]
[**--set**=<key=value>] [**--set-file**=<file>]`,
Description: `**step ca token** command generates a one-time token granting access to the
certificates authority.

Expand Down Expand Up @@ -174,6 +175,18 @@
$ step ca token --kms yubikey:pin-value=123456 \
--x5c-cert yubikey:slot-id=82 --x5c-key yubikey:slot-id=82 \
internal.example.com
'''

Generate a token with custom data in the "user" claim. The example below can be
accessed in a template as **{{ .Token.user.field }}**, rendering to the string
"value".

This is distinct from **.Insecure.User**: any attributes set using this option
are added to a claim named "user" in the signed JWT produced by this command.
This data may therefore be considered trusted (insofar as the token itself is
trusted).
'''
$ step ca token --set field=value internal.example.com
'''`,
Flags: []cli.Flag{
provisionerKidFlag,
Expand Down Expand Up @@ -244,6 +257,8 @@
flags.CaURL,
flags.Root,
flags.Context,
flags.TemplateSet,
flags.TemplateSetFile,
},
}
}
Expand Down Expand Up @@ -350,11 +365,29 @@
tokenOpts = append(tokenOpts, cautils.WithConfirmationFingerprint(cnf))
}

templateData, err := flags.GetTemplateData(ctx)
if err != nil {
return err
}
if templateData != nil {
tokenOpts = append(tokenOpts, cautils.WithCustomAttributes(templateData))
}

Check warning on line 374 in command/ca/token.go

View check run for this annotation

Codecov / codecov/patch

command/ca/token.go#L368-L374

Added lines #L368 - L374 were not covered by tests

// --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")
}

// --offline doesn't support tokenOpts, so reject set/set-file
if offline {
if len(ctx.StringSlice("set")) > 0 {
return errs.IncompatibleFlagWithFlag(ctx, "offline", "set")
}
if ctx.String("set-file") != "" {
return errs.IncompatibleFlagWithFlag(ctx, "offline", "set-file")
}

Check warning on line 388 in command/ca/token.go

View check run for this annotation

Codecov / codecov/patch

command/ca/token.go#L382-L388

Added lines #L382 - L388 were not covered by tests
}

// parse times or durations
notBefore, ok := flags.ParseTimeOrDuration(ctx.String("not-before"))
if !ok {
Expand Down
19 changes: 19 additions & 0 deletions token/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@
}
}

// WithUserData returns an Option function that merges the provided map with the
// existing user claim in the payload.
func WithUserData(v map[string]interface{}) Options {
return func(c *Claims) error {
if _, ok := c.ExtraClaims[UserClaim]; !ok {
c.Set(UserClaim, make(map[string]interface{}))
}
s := c.ExtraClaims[UserClaim]
sm, ok := s.(map[string]interface{})
if !ok {
return fmt.Errorf("%q claim is %T, not map[string]interface{}", UserClaim, s)
}
for k, val := range v {
sm[k] = val
}
return nil

Check warning on line 98 in token/options.go

View check run for this annotation

Codecov / codecov/patch

token/options.go#L85-L98

Added lines #L85 - L98 were not covered by tests
}
}

// WithSSH returns an Options function that sets the step claim with the ssh
// property in the value.
func WithSSH(v interface{}) Options {
Expand Down
3 changes: 3 additions & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ const SANSClaim = "sans"
// StepClaim is the property name for a JWT claim the stores the custom information in the certificate.
const StepClaim = "step"

// UserClaim is the property name for a JWT claim that stores user-provided custom information.
const UserClaim = "user"

// ConfirmationClaim is the property name for a JWT claim that stores a JSON
// object used as Proof-Of-Possession.
const ConfirmationClaim = "cnf"
Expand Down
13 changes: 13 additions & 0 deletions utils/cautils/certificate_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
SSHPublicKey ssh.PublicKey
CertificateRequest *x509.CertificateRequest
ConfirmationFingerprint string
CustomAttributes map[string]interface{}
}

// sharedContext is used to share information between commands.
Expand Down Expand Up @@ -88,6 +89,18 @@
})
}

// WithCustomAttributes adds custom attributes to be set in the "user" claim.
func WithCustomAttributes(v map[string]interface{}) Option {
return newFuncFlowOption(func(fo *flowContext) {
if fo.CustomAttributes == nil {
fo.CustomAttributes = make(map[string]interface{})
}
for k, val := range v {
fo.CustomAttributes[k] = val
}

Check warning on line 100 in utils/cautils/certificate_flow.go

View check run for this annotation

Codecov / codecov/patch

utils/cautils/certificate_flow.go#L93-L100

Added lines #L93 - L100 were not covered by tests
})
}

// NewCertificateFlow initializes a cli flow to get a new certificate.
func NewCertificateFlow(ctx *cli.Context, opts ...Option) (*CertificateFlow, error) {
var err error
Expand Down
10 changes: 10 additions & 0 deletions utils/cautils/token_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@
opts = append(opts, token.WithConfirmationFingerprint(sharedContext.ConfirmationFingerprint))
}

// Add custom user data, if set.
if sharedContext.CustomAttributes != nil {
opts = append(opts, token.WithUserData(sharedContext.CustomAttributes))
}

Check warning on line 114 in utils/cautils/token_generator.go

View check run for this annotation

Codecov / codecov/patch

utils/cautils/token_generator.go#L112-L114

Added lines #L112 - L114 were not covered by tests

return t.Token(sub, opts...)
}

Expand All @@ -126,6 +131,11 @@
ValidBefore: notAfter,
})}, opts...)

// Add custom user data, if set.
if sharedContext.CustomAttributes != nil {
opts = append(opts, token.WithUserData(sharedContext.CustomAttributes))
}

Check warning on line 137 in utils/cautils/token_generator.go

View check run for this annotation

Codecov / codecov/patch

utils/cautils/token_generator.go#L134-L137

Added lines #L134 - L137 were not covered by tests

return t.Token(sub, opts...)
}

Expand Down