Add multiuse capability to k8ssa provisioners

This commit is contained in:
max furman 2019-11-06 15:53:16 -08:00
parent 499d698c75
commit 946094d2b7
6 changed files with 62 additions and 25 deletions

View file

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

View file

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

View file

@ -212,11 +212,6 @@ func (p *K8sSA) AuthorizeSign(ctx context.Context, token string) ([]SignOption,
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{
// modifiers / withOptions
newProvisionerExtensionOption(TypeK8sSA, p.Name, ""),
@ -227,14 +222,41 @@ func (p *K8sSA) AuthorizeSign(ctx context.Context, token string) ([]SignOption,
}, nil
}
// AuthorizeRenewal returns an error if the renewal is disabled.
func (p *K8sSA) AuthorizeRenewal(cert *x509.Certificate) error {
// AuthorizeRenew returns an error if the renewal is disabled.
func (p *K8sSA) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error {
if p.claimer.IsDisableRenewal() {
return errors.Errorf("renew is disabled for provisioner %s", p.GetID())
}
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 {
r := &kauthzApi.SelfSubjectAccessReview{

View file

@ -322,7 +322,7 @@ func (o *OIDC) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption
return nil, err
}
signOptions := []SignOption{
// set the key id to the token subject
// set the key id to the token 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.
type SSHOptions struct {
CertType string `json:"certType"`
KeyID string `json:"keyID"`
Principals []string `json:"principals"`
ValidAfter TimeDuration `json:"validAfter,omitempty"`
ValidBefore TimeDuration `json:"validBefore,omitempty"`
@ -70,6 +71,8 @@ func (o SSHOptions) Modify(cert *ssh.Certificate) error {
default:
return errors.Errorf("ssh certificate has an unknown type: %s", o.CertType)
}
cert.KeyId = o.KeyID
cert.ValidPrincipals = o.Principals
if !o.ValidAfter.IsZero() {
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
func sshCertTypeUInt32(ct string) uint32 {
switch ct {

View file

@ -183,14 +183,6 @@ func (p *X5C) AuthorizeSign(ctx context.Context, token string) ([]SignOption, er
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
// and certificates. Older versions added the token subject as the only SAN
// in a CSR by default.
@ -222,8 +214,17 @@ func (p *X5C) AuthorizeRenew(ctx context.Context, cert *x509.Certificate) error
return nil
}
// authorizeSSHSign returns the list of SignOption for a SignSSH request.
func (p *X5C) authorizeSSHSign(claims *x5cPayload) ([]SignOption, error) {
// AuthorizeSSHSign returns the list of SignOption for a SignSSH request.
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 {
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{
// validates user's SSHOptions with the ones in the token
sshCertificateOptionsValidator(*opts),
// set the key id to the token subject
sshCertificateKeyIDModifier(claims.Subject),
}
// Add modifiers from custom claims
@ -258,6 +257,8 @@ func (p *X5C) authorizeSSHSign(claims *x5cPayload) ([]SignOption, error) {
&sshDefaultExtensionModifier{},
// Checks the validity bounds, and set the validity if has not been set.
sshLimitValidityModifier(p.claimer, claims.chains[0][0].NotAfter),
// set the key id to the token subject
sshCertKeyIDValidator(claims.Subject),
// Validate public key.
&sshDefaultPublicKeyValidator{},
// Validate the validity period.