forked from TrueCloudLab/certificates
Add multiuse capability to k8ssa provisioners
This commit is contained in:
parent
0ae9bab21e
commit
54e3cf7322
6 changed files with 62 additions and 25 deletions
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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{
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue