Add multiuse capability to k8ssa provisioners

This commit is contained in:
max furman 2019-11-06 15:53:16 -08:00
parent 0ae9bab21e
commit 54e3cf7322
6 changed files with 62 additions and 25 deletions

View file

@ -35,6 +35,7 @@ type SSHSignRequest struct {
ValidAfter TimeDuration `json:"validAfter,omitempty"` ValidAfter TimeDuration `json:"validAfter,omitempty"`
ValidBefore TimeDuration `json:"validBefore,omitempty"` ValidBefore TimeDuration `json:"validBefore,omitempty"`
AddUserPublicKey []byte `json:"addUserPublicKey,omitempty"` AddUserPublicKey []byte `json:"addUserPublicKey,omitempty"`
KeyID string `json:"keyID"`
} }
// Validate validates the SSHSignRequest. // Validate validates the SSHSignRequest.
@ -239,6 +240,7 @@ func (h *caHandler) SSHSign(w http.ResponseWriter, r *http.Request) {
opts := provisioner.SSHOptions{ opts := provisioner.SSHOptions{
CertType: body.CertType, CertType: body.CertType,
KeyID: body.KeyID,
Principals: body.Principals, Principals: body.Principals,
ValidBefore: body.ValidBefore, ValidBefore: body.ValidBefore,
ValidAfter: body.ValidAfter, ValidAfter: body.ValidAfter,

View file

@ -180,8 +180,7 @@ func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
if !p.claimer.IsSSHCAEnabled() { if !p.claimer.IsSSHCAEnabled() {
return nil, errors.Errorf("ssh ca is disabled for provisioner %s", p.GetID()) return nil, errors.Errorf("ssh ca is disabled for provisioner %s", p.GetID())
} }
// TODO: fix audiences claims, err := p.authorizeToken(token, p.audiences.SSHSign)
claims, err := p.authorizeToken(token, p.audiences.Sign)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -192,8 +191,6 @@ func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
signOptions := []SignOption{ signOptions := []SignOption{
// validates user's SSHOptions with the ones in the token // validates user's SSHOptions with the ones in the token
sshCertificateOptionsValidator(*opts), sshCertificateOptionsValidator(*opts),
// set the key id to the token subject
sshCertificateKeyIDModifier(claims.Subject),
} }
t := now() t := now()
@ -219,6 +216,8 @@ func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
&sshDefaultExtensionModifier{}, &sshDefaultExtensionModifier{},
// Set the validity bounds if not set. // Set the validity bounds if not set.
sshDefaultValidityModifier(p.claimer), sshDefaultValidityModifier(p.claimer),
// Validate that the keyID is equivalent to the token subject.
sshCertKeyIDValidator(claims.Subject),
// Validate public key // Validate public key
&sshDefaultPublicKeyValidator{}, &sshDefaultPublicKeyValidator{},
// Validate the validity period. // Validate the validity period.
@ -230,7 +229,6 @@ func (p *JWK) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption,
// AuthorizeSSHRevoke returns nil if the token is valid, false otherwise. // AuthorizeSSHRevoke returns nil if the token is valid, false otherwise.
func (p *JWK) AuthorizeSSHRevoke(ctx context.Context, token string) error { func (p *JWK) AuthorizeSSHRevoke(ctx context.Context, token string) error {
// TODO fix audience.
_, err := p.authorizeToken(token, p.audiences.SSHRevoke) _, err := p.authorizeToken(token, p.audiences.SSHRevoke)
return err return err
} }

View file

@ -212,11 +212,6 @@ func (p *K8sSA) AuthorizeSign(ctx context.Context, token string) ([]SignOption,
return nil, err return nil, err
} }
// Check for SSH sign-ing request.
if MethodFromContext(ctx) == SignSSHMethod {
return nil, errors.New("ssh certificates not enabled for k8s ServiceAccount provisioners")
}
return []SignOption{ return []SignOption{
// modifiers / withOptions // modifiers / withOptions
newProvisionerExtensionOption(TypeK8sSA, p.Name, ""), newProvisionerExtensionOption(TypeK8sSA, p.Name, ""),
@ -227,14 +222,41 @@ func (p *K8sSA) AuthorizeSign(ctx context.Context, token string) ([]SignOption,
}, nil }, nil
} }
// AuthorizeRenewal returns an error if the renewal is disabled. // AuthorizeRenew returns an error if the renewal is disabled.
func (p *K8sSA) AuthorizeRenewal(cert *x509.Certificate) error { func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() { if p.claimer.IsDisableRenewal() {
return errors.Errorf("renew is disabled for provisioner %s", p.GetID()) return errors.Errorf("renew is disabled for provisioner %s", p.GetID())
} }
return nil return nil
} }
// AuthorizeSSHSign validates an request for an SSH certificate.
func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() {
return nil, errors.Errorf("authorizeSSHSign: ssh ca is disabled for provisioner %s", p.GetID())
}
_, err := p.authorizeToken(token, p.audiences.SSHSign)
if err != nil {
return nil, errors.Wrap(err, "authorizeSSHSign")
}
// Default to a user certificate with no principals if not set
signOptions := []SignOption{sshCertificateDefaultsModifier{CertType: SSHUserCert}}
return append(signOptions,
// Set the default extensions.
&sshDefaultExtensionModifier{},
// Set the validity bounds if not set.
sshDefaultValidityModifier(p.claimer),
// Validate public key
&sshDefaultPublicKeyValidator{},
// Validate the validity period.
&sshCertificateValidityValidator{p.claimer},
// Require and validate all the default fields in the SSH certificate.
&sshCertificateDefaultValidator{},
), nil
}
/* /*
func checkAccess(authz kauthz.AuthorizationV1Interface) error { func checkAccess(authz kauthz.AuthorizationV1Interface) error {
r := &kauthzApi.SelfSubjectAccessReview{ r := &kauthzApi.SelfSubjectAccessReview{

View file

@ -322,7 +322,7 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
return nil, err return nil, err
} }
signOptions := []SignOption{ signOptions := []SignOption{
// set the key id to the token subject // set the key id to the token email
sshCertificateKeyIDModifier(claims.Email), sshCertificateKeyIDModifier(claims.Email),
} }

View file

@ -49,6 +49,7 @@ type SSHCertificateOptionsValidator interface {
// SSHOptions contains the options that can be passed to the SignSSH method. // SSHOptions contains the options that can be passed to the SignSSH method.
type SSHOptions struct { type SSHOptions struct {
CertType string `json:"certType"` CertType string `json:"certType"`
KeyID string `json:"keyID"`
Principals []string `json:"principals"` Principals []string `json:"principals"`
ValidAfter TimeDuration `json:"validAfter,omitempty"` ValidAfter TimeDuration `json:"validAfter,omitempty"`
ValidBefore TimeDuration `json:"validBefore,omitempty"` ValidBefore TimeDuration `json:"validBefore,omitempty"`
@ -70,6 +71,8 @@ func (o SSHOptions) Modify(cert *ssh.Certificate) error {
default: default:
return errors.Errorf("ssh certificate has an unknown type: %s", o.CertType) return errors.Errorf("ssh certificate has an unknown type: %s", o.CertType)
} }
cert.KeyId = o.KeyID
cert.ValidPrincipals = o.Principals cert.ValidPrincipals = o.Principals
if !o.ValidAfter.IsZero() { if !o.ValidAfter.IsZero() {
cert.ValidAfter = uint64(o.ValidAfter.Time().Unix()) cert.ValidAfter = uint64(o.ValidAfter.Time().Unix())
@ -373,6 +376,17 @@ func (v sshDefaultPublicKeyValidator) Valid(cert *ssh.Certificate) error {
} }
} }
// sshCertKeyIDValidator implements a validator for the KeyId attribute.
type sshCertKeyIDValidator string
// Valid returns an error if the given certificate does not contain the necessary fields.
func (v sshCertKeyIDValidator) Valid(cert *ssh.Certificate) error {
if string(v) != cert.KeyId {
return errors.Errorf("invalid ssh certificate KeyId; want %s, but got %s", string(v), cert.KeyId)
}
return nil
}
// sshCertTypeUInt32 // sshCertTypeUInt32
func sshCertTypeUInt32(ct string) uint32 { func sshCertTypeUInt32(ct string) uint32 {
switch ct { switch ct {

View file

@ -183,14 +183,6 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er
return nil, err return nil, err
} }
// Check for SSH sign-ing request.
if MethodFromContext(ctx) == SignSSHMethod {
if !p.claimer.IsSSHCAEnabled() {
return nil, errors.Errorf("ssh ca is disabled for provisioner %s", p.GetID())
}
return p.authorizeSSHSign(claims)
}
// NOTE: This is for backwards compatibility with older versions of cli // NOTE: This is for backwards compatibility with older versions of cli
// and certificates. Older versions added the token subject as the only SAN // and certificates. Older versions added the token subject as the only SAN
// in a CSR by default. // in a CSR by default.
@ -222,8 +214,17 @@ func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error
return nil return nil
} }
// authorizeSSHSign returns the list of SignOption for a SignSSH request. // AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *X5C) authorizeSSHSign(claims *x5cPayload) ([]SignOption, error) { func (p *X5C) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, error) {
if !p.claimer.IsSSHCAEnabled() {
return nil, errors.Errorf("ssh ca is disabled for provisioner %s", p.GetID())
}
claims, err := p.authorizeToken(token, p.audiences.SSHSign)
if err != nil {
return nil, err
}
if claims.Step == nil || claims.Step.SSH == nil { if claims.Step == nil || claims.Step.SSH == nil {
return nil, errors.New("authorization token must be an SSH provisioning token") return nil, errors.New("authorization token must be an SSH provisioning token")
} }
@ -231,8 +232,6 @@ func (p *X5C) authorizeSSHSign(claims *x5cPayload) ([]SignOption, error) {
signOptions := []SignOption{ signOptions := []SignOption{
// validates user's SSHOptions with the ones in the token // validates user's SSHOptions with the ones in the token
sshCertificateOptionsValidator(*opts), sshCertificateOptionsValidator(*opts),
// set the key id to the token subject
sshCertificateKeyIDModifier(claims.Subject),
} }
// Add modifiers from custom claims // Add modifiers from custom claims
@ -258,6 +257,8 @@ func (p *X5C) authorizeSSHSign(claims *x5cPayload) ([]SignOption, error) {
&sshDefaultExtensionModifier{}, &sshDefaultExtensionModifier{},
// Checks the validity bounds, and set the validity if has not been set. // Checks the validity bounds, and set the validity if has not been set.
sshLimitValidityModifier(p.claimer, claims.chains[0][0].NotAfter), sshLimitValidityModifier(p.claimer, claims.chains[0][0].NotAfter),
// set the key id to the token subject
sshCertKeyIDValidator(claims.Subject),
// Validate public key. // Validate public key.
&sshDefaultPublicKeyValidator{}, &sshDefaultPublicKeyValidator{},
// Validate the validity period. // Validate the validity period.