From 6c36ceb158f42409b79f9360a9ad386590ec78b7 Mon Sep 17 00:00:00 2001 From: Mariano Cano Date: Wed, 29 Jul 2020 18:05:35 -0700 Subject: [PATCH] Add initial template support for iid provisisioners. --- authority/provisioner/aws.go | 64 +++++++++++++++++++-------------- authority/provisioner/azure.go | 60 ++++++++++++++++++------------- authority/provisioner/gcp.go | 65 ++++++++++++++++++++-------------- 3 files changed, 111 insertions(+), 78 deletions(-) diff --git a/authority/provisioner/aws.go b/authority/provisioner/aws.go index 9f3764c0..cdfe49c3 100644 --- a/authority/provisioner/aws.go +++ b/authority/provisioner/aws.go @@ -17,6 +17,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/jose" ) @@ -126,14 +127,15 @@ type awsInstanceIdentityDocument struct { // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html type AWS struct { *base - Type string `json:"type"` - Name string `json:"name"` - Accounts []string `json:"accounts"` - DisableCustomSANs bool `json:"disableCustomSANs"` - DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` - InstanceAge Duration `json:"instanceAge,omitempty"` - Claims *Claims `json:"claims,omitempty"` - Options *Options `json:"options,omitempty"` + Type string `json:"type"` + Name string `json:"name"` + Accounts []string `json:"accounts"` + DisableCustomSANs bool `json:"disableCustomSANs"` + DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` + InstanceAge Duration `json:"instanceAge,omitempty"` + Claims *Claims `json:"claims,omitempty"` + Options *Options `json:"options,omitempty"` + SSHOptions *SSHOptions `json:"sshOptions,omitempty"` claimer *Claimer config *awsConfig audiences Audiences @@ -468,34 +470,42 @@ func (p *AWS) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, doc := claims.document - signOptions := []SignOption{ - // set the key id to the instance id - sshCertKeyIDModifier(doc.InstanceID), - } - - // Only enforce known principals if disable custom sans is true. - var principals []string - if p.DisableCustomSANs { - principals = []string{ - doc.PrivateIP, - fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region), - } + // Validated principals. + principals := []string{ + doc.PrivateIP, + fmt.Sprintf("ip-%s.%s.compute.internal", strings.Replace(doc.PrivateIP, ".", "-", -1), doc.Region), } // Default to cert type to host defaults := SignSSHOptions{ - CertType: SSHHostCert, - Principals: principals, + CertType: SSHHostCert, + } + defaultTemplate := sshutil.DefaultIIDCertificate + + // Only enforce known principals if disable custom sans is true. + if p.DisableCustomSANs { + defaults.Principals = principals + defaultTemplate = sshutil.DefaultCertificate } // Validate user options - signOptions = append(signOptions, sshCertOptionsValidator(defaults)) - // Set defaults if not given as user options - signOptions = append(signOptions, sshCertDefaultsModifier(defaults)) + signOptions := []SignOption{ + sshCertOptionsValidator(defaults), + } + + // Certificate templates. + data := sshutil.CreateTemplateData(sshutil.HostCert, doc.InstanceID, principals) + if v, err := unsafeParseSigned(token); err == nil { + data.SetToken(v) + } + + templateOptions, err := CustomSSHTemplateOptions(p.SSHOptions, data, defaultTemplate) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "aws.AuthorizeSSHSign") + } + signOptions = append(signOptions, templateOptions) return append(signOptions, - // Set the default extensions. - &sshDefaultExtensionModifier{}, // Set the validity bounds if not set. &sshDefaultDuration{p.claimer}, // Validate public key diff --git a/authority/provisioner/azure.go b/authority/provisioner/azure.go index a677f9a9..27835b3f 100644 --- a/authority/provisioner/azure.go +++ b/authority/provisioner/azure.go @@ -14,6 +14,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/jose" ) @@ -83,15 +84,16 @@ type azurePayload struct { // and https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service type Azure struct { *base - Type string `json:"type"` - Name string `json:"name"` - TenantID string `json:"tenantID"` - ResourceGroups []string `json:"resourceGroups"` - Audience string `json:"audience,omitempty"` - DisableCustomSANs bool `json:"disableCustomSANs"` - DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` - Claims *Claims `json:"claims,omitempty"` - Options *Options `json:"options,omitempty"` + Type string `json:"type"` + Name string `json:"name"` + TenantID string `json:"tenantID"` + ResourceGroups []string `json:"resourceGroups"` + Audience string `json:"audience,omitempty"` + DisableCustomSANs bool `json:"disableCustomSANs"` + DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` + Claims *Claims `json:"claims,omitempty"` + Options *Options `json:"options,omitempty"` + SSHOptions *SSHOptions `json:"sshOptions,omitempty"` claimer *Claimer config *azureConfig oidcConfig openIDConfiguration @@ -338,30 +340,40 @@ func (p *Azure) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOptio if err != nil { return nil, errs.Wrap(http.StatusInternalServerError, err, "azure.AuthorizeSSHSign") } - signOptions := []SignOption{ - // set the key id to the instance name - sshCertKeyIDModifier(name), + + // Validated principals + principals := []string{name} + + // Default options and template + defaults := SignSSHOptions{ + CertType: SSHHostCert, } + defaultTemplate := sshutil.DefaultIIDCertificate // Only enforce known principals if disable custom sans is true. - var principals []string if p.DisableCustomSANs { - principals = []string{name} + defaults.Principals = principals + defaultTemplate = sshutil.DefaultCertificate } - // Default to host + known hostnames - defaults := SignSSHOptions{ - CertType: SSHHostCert, - Principals: principals, - } // Validate user options - signOptions = append(signOptions, sshCertOptionsValidator(defaults)) - // Set defaults if not given as user options - signOptions = append(signOptions, sshCertDefaultsModifier(defaults)) + signOptions := []SignOption{ + sshCertOptionsValidator(defaults), + } + + // Certificate templates. + data := sshutil.CreateTemplateData(sshutil.HostCert, name, principals) + if v, err := unsafeParseSigned(token); err == nil { + data.SetToken(v) + } + + templateOptions, err := CustomSSHTemplateOptions(p.SSHOptions, data, defaultTemplate) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "azure.AuthorizeSSHSign") + } + signOptions = append(signOptions, templateOptions) return append(signOptions, - // Set the default extensions. - &sshDefaultExtensionModifier{}, // Set the validity bounds if not set. &sshDefaultDuration{p.claimer}, // Validate public key diff --git a/authority/provisioner/gcp.go b/authority/provisioner/gcp.go index 69a42ec2..8f53553c 100644 --- a/authority/provisioner/gcp.go +++ b/authority/provisioner/gcp.go @@ -15,6 +15,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/jose" ) @@ -77,15 +78,16 @@ func newGCPConfig() *gcpConfig { // https://cloud.google.com/compute/docs/instances/verifying-instance-identity type GCP struct { *base - Type string `json:"type"` - Name string `json:"name"` - ServiceAccounts []string `json:"serviceAccounts"` - ProjectIDs []string `json:"projectIDs"` - DisableCustomSANs bool `json:"disableCustomSANs"` - DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` - InstanceAge Duration `json:"instanceAge,omitempty"` - Claims *Claims `json:"claims,omitempty"` - Options *Options `json:"options,omitempty"` + Type string `json:"type"` + Name string `json:"name"` + ServiceAccounts []string `json:"serviceAccounts"` + ProjectIDs []string `json:"projectIDs"` + DisableCustomSANs bool `json:"disableCustomSANs"` + DisableTrustOnFirstUse bool `json:"disableTrustOnFirstUse"` + InstanceAge Duration `json:"instanceAge,omitempty"` + Claims *Claims `json:"claims,omitempty"` + Options *Options `json:"options,omitempty"` + SSHOptions *SSHOptions `json:"sshOptions,omitempty"` claimer *Claimer config *gcpConfig keyStore *keyStore @@ -379,33 +381,42 @@ func (p *GCP) AuthorizeSSHSign(ctx context.Context, token string) ([]SignOption, ce := claims.Google.ComputeEngine - signOptions := []SignOption{ - // set the key id to the instance name - sshCertKeyIDModifier(ce.InstanceName), + // Validated principals + principals := []string{ + fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID), + fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID), } + // Default options and template + defaults := SignSSHOptions{ + CertType: SSHHostCert, + } + defaultTemplate := sshutil.DefaultIIDCertificate + // Only enforce known principals if disable custom sans is true. - var principals []string if p.DisableCustomSANs { - principals = []string{ - fmt.Sprintf("%s.c.%s.internal", ce.InstanceName, ce.ProjectID), - fmt.Sprintf("%s.%s.c.%s.internal", ce.InstanceName, ce.Zone, ce.ProjectID), - } + defaults.Principals = principals + defaultTemplate = sshutil.DefaultCertificate } - // Default to host + known hostnames - defaults := SignSSHOptions{ - CertType: SSHHostCert, - Principals: principals, - } // Validate user options - signOptions = append(signOptions, sshCertOptionsValidator(defaults)) - // Set defaults if not given as user options - signOptions = append(signOptions, sshCertDefaultsModifier(defaults)) + signOptions := []SignOption{ + sshCertOptionsValidator(defaults), + } + + // Certificate templates. + data := sshutil.CreateTemplateData(sshutil.HostCert, ce.InstanceName, principals) + if v, err := unsafeParseSigned(token); err == nil { + data.SetToken(v) + } + + templateOptions, err := CustomSSHTemplateOptions(p.SSHOptions, data, defaultTemplate) + if err != nil { + return nil, errs.Wrap(http.StatusInternalServerError, err, "gcp.AuthorizeSSHSign") + } + signOptions = append(signOptions, templateOptions) return append(signOptions, - // Set the default extensions - &sshDefaultExtensionModifier{}, // Set the validity bounds if not set. &sshDefaultDuration{p.claimer}, // Validate public key