forked from TrueCloudLab/certificates
Add template support on k8ssa provisioner.
This commit is contained in:
parent
6c36ceb158
commit
a78f7e8913
5 changed files with 62 additions and 24 deletions
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/smallstep/certificates/errs"
|
||||
"github.com/smallstep/certificates/sshutil"
|
||||
"github.com/smallstep/certificates/x509util"
|
||||
"github.com/smallstep/cli/crypto/pemutil"
|
||||
"github.com/smallstep/cli/jose"
|
||||
|
@ -46,6 +47,7 @@ type K8sSA struct {
|
|||
PubKeys []byte `json:"publicKeys,omitempty"`
|
||||
Claims *Claims `json:"claims,omitempty"`
|
||||
Options *Options `json:"options,omitempty"`
|
||||
SSHOptions *SSHOptions `json:"sshOptions,omitempty"`
|
||||
claimer *Claimer
|
||||
audiences Audiences
|
||||
//kauthn kauthn.AuthenticationV1Interface
|
||||
|
@ -249,16 +251,27 @@ func (p *K8sSA) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOptio
|
|||
if !p.claimer.IsSSHCAEnabled() {
|
||||
return nil, errs.Unauthorized("k8ssa.AuthorizeSSHSign; sshCA is disabled for k8sSA provisioner %s", p.GetID())
|
||||
}
|
||||
if _, err := p.authorizeToken(token, p.audiences.SSHSign); err != nil {
|
||||
claims, err := p.authorizeToken(token, p.audiences.SSHSign)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "k8ssa.AuthorizeSSHSign")
|
||||
}
|
||||
|
||||
// Default to a user certificate with no principals if not set
|
||||
signOptions := []SignOption{sshCertDefaultsModifier{CertType: SSHUserCert}}
|
||||
// Certificate templates.
|
||||
// Set some default variables to be used in the templates.
|
||||
data := sshutil.CreateTemplateData(sshutil.HostCert, claims.ServiceAccountName, []string{claims.ServiceAccountName})
|
||||
if v, err := unsafeParseSigned(token); err == nil {
|
||||
data.SetToken(v)
|
||||
}
|
||||
|
||||
templateOptions, err := CustomSSHTemplateOptions(p.SSHOptions, data, sshutil.CertificateRequestTemplate)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "k8ssa.AuthorizeSSHSign")
|
||||
}
|
||||
signOptions := []SignOption{templateOptions}
|
||||
|
||||
return append(signOptions,
|
||||
// Set the default extensions.
|
||||
&sshDefaultExtensionModifier{},
|
||||
// Require type, key-id and principals in the SignSSHOptions.
|
||||
&sshCertOptionsRequireValidator{CertType: true, KeyID: true, Principals: true},
|
||||
// Set the validity bounds if not set.
|
||||
&sshDefaultDuration{p.claimer},
|
||||
// Validate public key
|
||||
|
|
|
@ -299,6 +299,26 @@ func (v sshCertOptionsValidator) Valid(got SignSSHOptions) error {
|
|||
return want.match(got)
|
||||
}
|
||||
|
||||
// sshCertOptionsRequireValidator defines which elements in the SignSSHOptions are required.
|
||||
type sshCertOptionsRequireValidator struct {
|
||||
CertType bool
|
||||
KeyID bool
|
||||
Principals bool
|
||||
}
|
||||
|
||||
func (v sshCertOptionsRequireValidator) Valid(got SignSSHOptions) error {
|
||||
switch {
|
||||
case v.CertType && got.CertType == "":
|
||||
return errors.New("ssh certificate certType cannot be empty")
|
||||
case v.KeyID && got.KeyID == "":
|
||||
return errors.New("ssh certificate keyID cannot be empty")
|
||||
case v.Principals && len(got.Principals) == 0:
|
||||
return errors.New("ssh certificate principals cannot be empty")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type sshCertValidityValidator struct {
|
||||
*Claimer
|
||||
}
|
||||
|
|
|
@ -206,8 +206,6 @@ func (a *Authority) GetSSHBastion(ctx context.Context, user string, hostname str
|
|||
// SignSSH creates a signed SSH certificate with the given public key and options.
|
||||
func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisioner.SignSSHOptions, signOpts ...provisioner.SignOption) (*ssh.Certificate, error) {
|
||||
var (
|
||||
err error
|
||||
certType sshutil.CertType
|
||||
certOptions []sshutil.Option
|
||||
mods []provisioner.SSHCertModifier
|
||||
validators []provisioner.SSHCertValidator
|
||||
|
@ -216,14 +214,6 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi
|
|||
// Set backdate with the configured value
|
||||
opts.Backdate = a.config.AuthorityConfig.Backdate.Duration
|
||||
|
||||
// Validate certificate type.
|
||||
if opts.CertType != "" {
|
||||
certType, err = sshutil.CertTypeFromString(opts.CertType)
|
||||
if err != nil {
|
||||
return nil, errs.Wrap(http.StatusBadRequest, err, "authority.SignSSH")
|
||||
}
|
||||
}
|
||||
|
||||
for _, op := range signOpts {
|
||||
switch o := op.(type) {
|
||||
// add options to NewCertificate
|
||||
|
@ -251,7 +241,7 @@ func (a *Authority) SignSSH(ctx context.Context, key ssh.PublicKey, opts provisi
|
|||
|
||||
// Simulated certificate request with request options.
|
||||
cr := sshutil.CertificateRequest{
|
||||
Type: certType,
|
||||
Type: opts.CertType,
|
||||
KeyID: opts.KeyID,
|
||||
Principals: opts.Principals,
|
||||
Key: key,
|
||||
|
|
|
@ -11,7 +11,7 @@ import "golang.org/x/crypto/ssh"
|
|||
// passed with the API instead of the validated ones.
|
||||
type CertificateRequest struct {
|
||||
Key ssh.PublicKey
|
||||
Type CertType
|
||||
Type string
|
||||
KeyID string
|
||||
Principals []string
|
||||
}
|
||||
|
|
|
@ -143,3 +143,18 @@ const DefaultIIDCertificate = `{
|
|||
{{- end }}
|
||||
"extensions": {{ toJson .Extensions }}
|
||||
}`
|
||||
|
||||
const CertificateRequestTemplate = `{
|
||||
"type": "{{ .Insecure.CR.Type }}",
|
||||
"keyId": "{{ .Insecure.CR.KeyID }}",
|
||||
"principals": {{ toJson .Insecure.CR.Principals }}
|
||||
{{- if eq .Insecure.CR.Type "user" }}
|
||||
, "extensions": {
|
||||
"permit-X11-forwarding": "",
|
||||
"permit-agent-forwarding": "",
|
||||
"permit-port-forwarding": "",
|
||||
"permit-pty": "",
|
||||
"permit-user-rc": ""
|
||||
}
|
||||
{{- end }}
|
||||
}`
|
||||
|
|
Loading…
Reference in a new issue