forked from TrueCloudLab/certificates
Compare commits
13 commits
Author | SHA1 | Date | |
---|---|---|---|
|
a1350b14fb | ||
|
c9df65ebae | ||
|
d9d7c52997 | ||
|
ff424fa944 | ||
|
7282245e88 | ||
|
9a7582d1d3 | ||
|
7796ad8f90 | ||
|
2d666cfc4f | ||
|
904f416d20 | ||
|
d89c3a942e | ||
|
aa30c2c73c | ||
|
31533c4a15 | ||
|
5bfe96d8c7 |
29 changed files with 400 additions and 171 deletions
|
@ -35,9 +35,6 @@ var (
|
||||||
// DefaultEnableSSHCA enable SSH CA features per provisioner or globally
|
// DefaultEnableSSHCA enable SSH CA features per provisioner or globally
|
||||||
// for all provisioners.
|
// for all provisioners.
|
||||||
DefaultEnableSSHCA = false
|
DefaultEnableSSHCA = false
|
||||||
// DefaultDisableSmallstepExtensions disables the Smallstep extensions in
|
|
||||||
// the certificate.
|
|
||||||
DefaultDisableSmallstepExtensions = false
|
|
||||||
// DefaultCRLCacheDuration is the default cache duration for the CRL.
|
// DefaultCRLCacheDuration is the default cache duration for the CRL.
|
||||||
DefaultCRLCacheDuration = &provisioner.Duration{Duration: 24 * time.Hour}
|
DefaultCRLCacheDuration = &provisioner.Duration{Duration: 24 * time.Hour}
|
||||||
// DefaultCRLExpiredDuration is the default duration in which expired
|
// DefaultCRLExpiredDuration is the default duration in which expired
|
||||||
|
@ -58,7 +55,6 @@ var (
|
||||||
EnableSSHCA: &DefaultEnableSSHCA,
|
EnableSSHCA: &DefaultEnableSSHCA,
|
||||||
DisableRenewal: &DefaultDisableRenewal,
|
DisableRenewal: &DefaultDisableRenewal,
|
||||||
AllowRenewalAfterExpiry: &DefaultAllowRenewalAfterExpiry,
|
AllowRenewalAfterExpiry: &DefaultAllowRenewalAfterExpiry,
|
||||||
DisableSmallstepExtensions: &DefaultDisableSmallstepExtensions,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -257,7 +257,7 @@ func (p *ACME) AuthorizeSign(context.Context, string) ([]SignOption, error) {
|
||||||
opts := []SignOption{
|
opts := []SignOption{
|
||||||
p,
|
p,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeACME, p.Name, "").WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeACME, p.Name, ""),
|
||||||
newForceCNOption(p.ForceCN),
|
newForceCNOption(p.ForceCN),
|
||||||
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
|
"github.com/smallstep/certificates/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// awsIssuer is the string used as issuer in the generated tokens.
|
// awsIssuer is the string used as issuer in the generated tokens.
|
||||||
|
@ -514,14 +515,18 @@ func (p *AWS) AuthorizeSign(_ context.Context, token string) ([]SignOption, erro
|
||||||
p,
|
p,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeAWS, p.Name, doc.AccountID, "InstanceID", doc.InstanceID).WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeAWS, p.Name, doc.AccountID, "InstanceID", doc.InstanceID),
|
||||||
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
defaultPublicKeyValidator{},
|
defaultPublicKeyValidator{},
|
||||||
commonNameValidator(payload.Claims.Subject),
|
commonNameValidator(payload.Claims.Subject),
|
||||||
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
||||||
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_X509),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_X509,
|
||||||
|
webhook.WithAuthorizationPrincipal(doc.InstanceID),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,6 +809,10 @@ func (p *AWS) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||||
// Ensure that all principal names are allowed
|
// Ensure that all principal names are allowed
|
||||||
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
||||||
// Call webhooks
|
// Call webhooks
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_SSH),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_SSH,
|
||||||
|
webhook.WithAuthorizationPrincipal(doc.InstanceID),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
|
"github.com/smallstep/certificates/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// azureOIDCBaseURL is the base discovery url for Microsoft Azure tokens.
|
// azureOIDCBaseURL is the base discovery url for Microsoft Azure tokens.
|
||||||
|
@ -397,13 +398,17 @@ func (p *Azure) AuthorizeSign(_ context.Context, token string) ([]SignOption, er
|
||||||
p,
|
p,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeAzure, p.Name, p.TenantID).WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeAzure, p.Name, p.TenantID),
|
||||||
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
defaultPublicKeyValidator{},
|
defaultPublicKeyValidator{},
|
||||||
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
||||||
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_X509),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_X509,
|
||||||
|
webhook.WithAuthorizationPrincipal(identityObjectID),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,7 +426,7 @@ func (p *Azure) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption,
|
||||||
return nil, errs.Unauthorized("azure.AuthorizeSSHSign; sshCA is disabled for provisioner '%s'", p.GetName())
|
return nil, errs.Unauthorized("azure.AuthorizeSSHSign; sshCA is disabled for provisioner '%s'", p.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
_, name, _, _, _, err := p.authorizeToken(token)
|
_, name, _, _, identityObjectID, err := p.authorizeToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "azure.AuthorizeSSHSign")
|
return nil, errs.Wrap(http.StatusInternalServerError, err, "azure.AuthorizeSSHSign")
|
||||||
}
|
}
|
||||||
|
@ -473,7 +478,11 @@ func (p *Azure) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption,
|
||||||
// Ensure that all principal names are allowed
|
// Ensure that all principal names are allowed
|
||||||
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
||||||
// Call webhooks
|
// Call webhooks
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_SSH),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_SSH,
|
||||||
|
webhook.WithAuthorizationPrincipal(identityObjectID),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,6 @@ type Claims struct {
|
||||||
// Renewal properties
|
// Renewal properties
|
||||||
DisableRenewal *bool `json:"disableRenewal,omitempty"`
|
DisableRenewal *bool `json:"disableRenewal,omitempty"`
|
||||||
AllowRenewalAfterExpiry *bool `json:"allowRenewalAfterExpiry,omitempty"`
|
AllowRenewalAfterExpiry *bool `json:"allowRenewalAfterExpiry,omitempty"`
|
||||||
|
|
||||||
// Other properties
|
|
||||||
DisableSmallstepExtensions *bool `json:"disableSmallstepExtensions,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Claimer is the type that controls claims. It provides an interface around the
|
// Claimer is the type that controls claims. It provides an interface around the
|
||||||
|
@ -50,7 +47,6 @@ func (c *Claimer) Claims() Claims {
|
||||||
disableRenewal := c.IsDisableRenewal()
|
disableRenewal := c.IsDisableRenewal()
|
||||||
allowRenewalAfterExpiry := c.AllowRenewalAfterExpiry()
|
allowRenewalAfterExpiry := c.AllowRenewalAfterExpiry()
|
||||||
enableSSHCA := c.IsSSHCAEnabled()
|
enableSSHCA := c.IsSSHCAEnabled()
|
||||||
disableSmallstepExtensions := c.IsDisableSmallstepExtensions()
|
|
||||||
|
|
||||||
return Claims{
|
return Claims{
|
||||||
MinTLSDur: &Duration{c.MinTLSCertDuration()},
|
MinTLSDur: &Duration{c.MinTLSCertDuration()},
|
||||||
|
@ -65,7 +61,6 @@ func (c *Claimer) Claims() Claims {
|
||||||
EnableSSHCA: &enableSSHCA,
|
EnableSSHCA: &enableSSHCA,
|
||||||
DisableRenewal: &disableRenewal,
|
DisableRenewal: &disableRenewal,
|
||||||
AllowRenewalAfterExpiry: &allowRenewalAfterExpiry,
|
AllowRenewalAfterExpiry: &allowRenewalAfterExpiry,
|
||||||
DisableSmallstepExtensions: &disableSmallstepExtensions,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,15 +110,6 @@ func (c *Claimer) IsDisableRenewal() bool {
|
||||||
return *c.claims.DisableRenewal
|
return *c.claims.DisableRenewal
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDisableSmallstepExtensions returns if the Smallstep extensions, like the
|
|
||||||
// provisioner extension, should be excluded from the certificate.
|
|
||||||
func (c *Claimer) IsDisableSmallstepExtensions() bool {
|
|
||||||
if c.claims == nil || c.claims.DisableSmallstepExtensions == nil {
|
|
||||||
return *c.global.DisableSmallstepExtensions
|
|
||||||
}
|
|
||||||
return *c.claims.DisableSmallstepExtensions
|
|
||||||
}
|
|
||||||
|
|
||||||
// AllowRenewalAfterExpiry returns if the renewal flow is authorized if the
|
// AllowRenewalAfterExpiry returns if the renewal flow is authorized if the
|
||||||
// certificate is expired. If the property is not set within the provisioner
|
// certificate is expired. If the property is not set within the provisioner
|
||||||
// then the global value from the authority configuration will be used.
|
// then the global value from the authority configuration will be used.
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
|
"github.com/smallstep/certificates/webhook"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
@ -77,7 +78,7 @@ func (c *Controller) AuthorizeSSHRenew(ctx context.Context, cert *ssh.Certificat
|
||||||
return DefaultAuthorizeSSHRenew(ctx, c, cert)
|
return DefaultAuthorizeSSHRenew(ctx, c, cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) newWebhookController(templateData WebhookSetter, certType linkedca.Webhook_CertType) *WebhookController {
|
func (c *Controller) newWebhookController(templateData WebhookSetter, certType linkedca.Webhook_CertType, opts ...webhook.RequestBodyOption) *WebhookController {
|
||||||
client := c.webhookClient
|
client := c.webhookClient
|
||||||
if client == nil {
|
if client == nil {
|
||||||
client = http.DefaultClient
|
client = http.DefaultClient
|
||||||
|
@ -87,6 +88,7 @@ func (c *Controller) newWebhookController(templateData WebhookSetter, certType l
|
||||||
client: client,
|
client: client,
|
||||||
webhooks: c.webhooks,
|
webhooks: c.webhooks,
|
||||||
certType: certType,
|
certType: certType,
|
||||||
|
options: opts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,18 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.step.sm/crypto/pemutil"
|
||||||
"go.step.sm/crypto/x509util"
|
"go.step.sm/crypto/x509util"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/authority/policy"
|
"github.com/smallstep/certificates/authority/policy"
|
||||||
|
"github.com/smallstep/certificates/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
var trueValue = true
|
var trueValue = true
|
||||||
|
@ -449,16 +452,39 @@ func TestDefaultAuthorizeSSHRenew(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_newWebhookController(t *testing.T) {
|
func Test_newWebhookController(t *testing.T) {
|
||||||
|
cert, err := pemutil.ReadCertificate("testdata/certs/x5c-leaf.crt", pemutil.WithFirstBlock())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
opts := []webhook.RequestBodyOption{webhook.WithX5CCertificate(cert)}
|
||||||
|
|
||||||
|
type args struct {
|
||||||
|
templateData WebhookSetter
|
||||||
|
certType linkedca.Webhook_CertType
|
||||||
|
opts []webhook.RequestBodyOption
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *WebhookController
|
||||||
|
}{
|
||||||
|
{"ok", args{x509util.TemplateData{"foo": "bar"}, linkedca.Webhook_X509, nil}, &WebhookController{
|
||||||
|
TemplateData: x509util.TemplateData{"foo": "bar"},
|
||||||
|
certType: linkedca.Webhook_X509,
|
||||||
|
client: http.DefaultClient,
|
||||||
|
}},
|
||||||
|
{"ok with options", args{x509util.TemplateData{"foo": "bar"}, linkedca.Webhook_SSH, opts}, &WebhookController{
|
||||||
|
TemplateData: x509util.TemplateData{"foo": "bar"},
|
||||||
|
certType: linkedca.Webhook_SSH,
|
||||||
|
client: http.DefaultClient,
|
||||||
|
options: opts,
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
c := &Controller{}
|
c := &Controller{}
|
||||||
data := x509util.TemplateData{"foo": "bar"}
|
got := c.newWebhookController(tt.args.templateData, tt.args.certType, tt.args.opts...)
|
||||||
ctl := c.newWebhookController(data, linkedca.Webhook_X509)
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
if !reflect.DeepEqual(ctl.TemplateData, data) {
|
t.Errorf("newWebhookController() = %v, want %v", got, tt.want)
|
||||||
t.Error("Failed to set templateData")
|
|
||||||
}
|
}
|
||||||
if ctl.certType != linkedca.Webhook_X509 {
|
|
||||||
t.Error("Failed to set certType")
|
|
||||||
}
|
|
||||||
if ctl.client == nil {
|
|
||||||
t.Error("Failed to set client")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
|
"github.com/smallstep/certificates/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// gcpCertsURL is the url that serves Google OAuth2 public keys.
|
// gcpCertsURL is the url that serves Google OAuth2 public keys.
|
||||||
|
@ -269,13 +270,17 @@ func (p *GCP) AuthorizeSign(_ context.Context, token string) ([]SignOption, erro
|
||||||
p,
|
p,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeGCP, p.Name, claims.Subject, "InstanceID", ce.InstanceID, "InstanceName", ce.InstanceName).WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeGCP, p.Name, claims.Subject, "InstanceID", ce.InstanceID, "InstanceName", ce.InstanceName),
|
||||||
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
defaultPublicKeyValidator{},
|
defaultPublicKeyValidator{},
|
||||||
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
||||||
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_X509),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_X509,
|
||||||
|
webhook.WithAuthorizationPrincipal(ce.InstanceID),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,6 +447,10 @@ func (p *GCP) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||||
// Ensure that all principal names are allowed
|
// Ensure that all principal names are allowed
|
||||||
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), nil),
|
||||||
// Call webhooks
|
// Call webhooks
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_SSH),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_SSH,
|
||||||
|
webhook.WithAuthorizationPrincipal(ce.InstanceID),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ func (p *JWK) AuthorizeSign(_ context.Context, token string) ([]SignOption, erro
|
||||||
self,
|
self,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID).WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeJWK, p.Name, p.Key.KeyID),
|
||||||
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
commonNameValidator(claims.Subject),
|
commonNameValidator(claims.Subject),
|
||||||
|
|
|
@ -238,7 +238,7 @@ func (p *K8sSA) AuthorizeSign(_ context.Context, token string) ([]SignOption, er
|
||||||
p,
|
p,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeK8sSA, p.Name, "").WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeK8sSA, p.Name, ""),
|
||||||
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(p.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
defaultPublicKeyValidator{},
|
defaultPublicKeyValidator{},
|
||||||
|
|
|
@ -150,7 +150,7 @@ func (p *Nebula) AuthorizeSign(_ context.Context, token string) ([]SignOption, e
|
||||||
p,
|
p,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeNebula, p.Name, "").WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeNebula, p.Name, ""),
|
||||||
profileLimitDuration{
|
profileLimitDuration{
|
||||||
def: p.ctl.Claimer.DefaultTLSCertDuration(),
|
def: p.ctl.Claimer.DefaultTLSCertDuration(),
|
||||||
notBefore: crt.Details.NotBefore,
|
notBefore: crt.Details.NotBefore,
|
||||||
|
|
|
@ -351,7 +351,7 @@ func (o *OIDC) AuthorizeSign(_ context.Context, token string) ([]SignOption, err
|
||||||
o,
|
o,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID).WithControllerOptions(o.ctl),
|
newProvisionerExtensionOption(TypeOIDC, o.Name, o.ClientID),
|
||||||
profileDefaultDuration(o.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(o.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
defaultPublicKeyValidator{},
|
defaultPublicKeyValidator{},
|
||||||
|
|
|
@ -190,7 +190,7 @@ func (s *SCEP) AuthorizeSign(context.Context, string) ([]SignOption, error) {
|
||||||
return []SignOption{
|
return []SignOption{
|
||||||
s,
|
s,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeSCEP, s.Name, "").WithControllerOptions(s.ctl),
|
newProvisionerExtensionOption(TypeSCEP, s.Name, ""),
|
||||||
newForceCNOption(s.ForceCN),
|
newForceCNOption(s.ForceCN),
|
||||||
profileDefaultDuration(s.ctl.Claimer.DefaultTLSCertDuration()),
|
profileDefaultDuration(s.ctl.Claimer.DefaultTLSCertDuration()),
|
||||||
// validators
|
// validators
|
||||||
|
|
|
@ -430,7 +430,6 @@ func (o *forceCNOption) Modify(cert *x509.Certificate, _ SignOptions) error {
|
||||||
|
|
||||||
type provisionerExtensionOption struct {
|
type provisionerExtensionOption struct {
|
||||||
Extension
|
Extension
|
||||||
Disabled bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProvisionerExtensionOption(typ Type, name, credentialID string, keyValuePairs ...string) *provisionerExtensionOption {
|
func newProvisionerExtensionOption(typ Type, name, credentialID string, keyValuePairs ...string) *provisionerExtensionOption {
|
||||||
|
@ -444,18 +443,7 @@ func newProvisionerExtensionOption(typ Type, name, credentialID string, keyValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithControllerOptions returns the provisionerExtensionOption options from the
|
|
||||||
// controller. Currently only the claim DisableSmallstepExtensions is used.
|
|
||||||
func (o *provisionerExtensionOption) WithControllerOptions(c *Controller) *provisionerExtensionOption {
|
|
||||||
o.Disabled = c.Claimer.IsDisableSmallstepExtensions()
|
|
||||||
return o
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *provisionerExtensionOption) Modify(cert *x509.Certificate, _ SignOptions) error {
|
func (o *provisionerExtensionOption) Modify(cert *x509.Certificate, _ SignOptions) error {
|
||||||
if o.Disabled {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ext, err := o.ToExtension()
|
ext, err := o.ToExtension()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.NewError(http.StatusInternalServerError, err, "error creating certificate")
|
return errs.NewError(http.StatusInternalServerError, err, "error creating certificate")
|
||||||
|
|
|
@ -604,23 +604,13 @@ func Test_newProvisionerExtension_Option(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Claims with smallstep extensions disabled.
|
|
||||||
claimer, err := NewClaimer(&Claims{
|
|
||||||
DisableSmallstepExtensions: &trueValue,
|
|
||||||
}, globalProvisionerClaims)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
modifier *provisionerExtensionOption
|
|
||||||
cert *x509.Certificate
|
cert *x509.Certificate
|
||||||
valid func(*x509.Certificate)
|
valid func(*x509.Certificate)
|
||||||
}
|
}
|
||||||
tests := map[string]func() test{
|
tests := map[string]func() test{
|
||||||
"ok/one-element": func() test {
|
"ok/one-element": func() test {
|
||||||
return test{
|
return test{
|
||||||
modifier: newProvisionerExtensionOption(TypeJWK, "name", "credentialId", "key", "value"),
|
|
||||||
cert: new(x509.Certificate),
|
cert: new(x509.Certificate),
|
||||||
valid: func(cert *x509.Certificate) {
|
valid: func(cert *x509.Certificate) {
|
||||||
if assert.Len(t, 1, cert.ExtraExtensions) {
|
if assert.Len(t, 1, cert.ExtraExtensions) {
|
||||||
|
@ -635,7 +625,6 @@ func Test_newProvisionerExtension_Option(t *testing.T) {
|
||||||
},
|
},
|
||||||
"ok/replace": func() test {
|
"ok/replace": func() test {
|
||||||
return test{
|
return test{
|
||||||
modifier: newProvisionerExtensionOption(TypeJWK, "name", "credentialId", "key", "value"),
|
|
||||||
cert: &x509.Certificate{ExtraExtensions: []pkix.Extension{{Id: StepOIDProvisioner, Critical: true}, {Id: []int{1, 2, 3}}}},
|
cert: &x509.Certificate{ExtraExtensions: []pkix.Extension{{Id: StepOIDProvisioner, Critical: true}, {Id: []int{1, 2, 3}}}},
|
||||||
valid: func(cert *x509.Certificate) {
|
valid: func(cert *x509.Certificate) {
|
||||||
if assert.Len(t, 2, cert.ExtraExtensions) {
|
if assert.Len(t, 2, cert.ExtraExtensions) {
|
||||||
|
@ -647,22 +636,11 @@ func Test_newProvisionerExtension_Option(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ok/disabled": func() test {
|
|
||||||
return test{
|
|
||||||
modifier: newProvisionerExtensionOption(TypeJWK, "name", "credentialId", "key", "value").WithControllerOptions(&Controller{
|
|
||||||
Claimer: claimer,
|
|
||||||
}),
|
|
||||||
cert: new(x509.Certificate),
|
|
||||||
valid: func(cert *x509.Certificate) {
|
|
||||||
assert.Len(t, 0, cert.ExtraExtensions)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for name, run := range tests {
|
for name, run := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
tt := run()
|
tt := run()
|
||||||
assert.FatalError(t, tt.modifier.Modify(tt.cert, SignOptions{}))
|
assert.FatalError(t, newProvisionerExtensionOption(TypeJWK, "name", "credentialId", "key", "value").Modify(tt.cert, SignOptions{}))
|
||||||
tt.valid(tt.cert)
|
tt.valid(tt.cert)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ var (
|
||||||
defaultDisableRenewal = false
|
defaultDisableRenewal = false
|
||||||
defaultAllowRenewalAfterExpiry = false
|
defaultAllowRenewalAfterExpiry = false
|
||||||
defaultEnableSSHCA = true
|
defaultEnableSSHCA = true
|
||||||
defaultDisableSmallstepExtensions = false
|
|
||||||
globalProvisionerClaims = Claims{
|
globalProvisionerClaims = Claims{
|
||||||
MinTLSDur: &Duration{5 * time.Minute},
|
MinTLSDur: &Duration{5 * time.Minute},
|
||||||
MaxTLSDur: &Duration{24 * time.Hour},
|
MaxTLSDur: &Duration{24 * time.Hour},
|
||||||
|
@ -41,7 +40,6 @@ var (
|
||||||
EnableSSHCA: &defaultEnableSSHCA,
|
EnableSSHCA: &defaultEnableSSHCA,
|
||||||
DisableRenewal: &defaultDisableRenewal,
|
DisableRenewal: &defaultDisableRenewal,
|
||||||
AllowRenewalAfterExpiry: &defaultAllowRenewalAfterExpiry,
|
AllowRenewalAfterExpiry: &defaultAllowRenewalAfterExpiry,
|
||||||
DisableSmallstepExtensions: &defaultDisableSmallstepExtensions,
|
|
||||||
}
|
}
|
||||||
testAudiences = Audiences{
|
testAudiences = Audiences{
|
||||||
Sign: []string{"https://ca.smallstep.com/1.0/sign", "https://ca.smallstep.com/sign"},
|
Sign: []string{"https://ca.smallstep.com/1.0/sign", "https://ca.smallstep.com/sign"},
|
||||||
|
|
|
@ -30,6 +30,7 @@ type WebhookController struct {
|
||||||
client *http.Client
|
client *http.Client
|
||||||
webhooks []*Webhook
|
webhooks []*Webhook
|
||||||
certType linkedca.Webhook_CertType
|
certType linkedca.Webhook_CertType
|
||||||
|
options []webhook.RequestBodyOption
|
||||||
TemplateData WebhookSetter
|
TemplateData WebhookSetter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +40,14 @@ func (wc *WebhookController) Enrich(req *webhook.RequestBody) error {
|
||||||
if wc == nil {
|
if wc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply extra options in the webhook controller
|
||||||
|
for _, fn := range wc.options {
|
||||||
|
if err := fn(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, wh := range wc.webhooks {
|
for _, wh := range wc.webhooks {
|
||||||
if wh.Kind != linkedca.Webhook_ENRICHING.String() {
|
if wh.Kind != linkedca.Webhook_ENRICHING.String() {
|
||||||
continue
|
continue
|
||||||
|
@ -63,6 +72,14 @@ func (wc *WebhookController) Authorize(req *webhook.RequestBody) error {
|
||||||
if wc == nil {
|
if wc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply extra options in the webhook controller
|
||||||
|
for _, fn := range wc.options {
|
||||||
|
if err := fn(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, wh := range wc.webhooks {
|
for _, wh := range wc.webhooks {
|
||||||
if wh.Kind != linkedca.Webhook_AUTHORIZING.String() {
|
if wh.Kind != linkedca.Webhook_AUTHORIZING.String() {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"crypto/hmac"
|
"crypto/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -16,6 +17,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/webhook"
|
"github.com/smallstep/certificates/webhook"
|
||||||
|
"go.step.sm/crypto/pemutil"
|
||||||
"go.step.sm/crypto/x509util"
|
"go.step.sm/crypto/x509util"
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
)
|
)
|
||||||
|
@ -96,12 +98,18 @@ func TestWebhookController_isCertTypeOK(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebhookController_Enrich(t *testing.T) {
|
func TestWebhookController_Enrich(t *testing.T) {
|
||||||
|
cert, err := pemutil.ReadCertificate("testdata/certs/x5c-leaf.crt", pemutil.WithFirstBlock())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
ctl *WebhookController
|
ctl *WebhookController
|
||||||
req *webhook.RequestBody
|
req *webhook.RequestBody
|
||||||
responses []*webhook.ResponseBody
|
responses []*webhook.ResponseBody
|
||||||
expectErr bool
|
expectErr bool
|
||||||
expectTemplateData any
|
expectTemplateData any
|
||||||
|
assertRequest func(t *testing.T, req *webhook.RequestBody)
|
||||||
}
|
}
|
||||||
tests := map[string]test{
|
tests := map[string]test{
|
||||||
"ok/no enriching webhooks": {
|
"ok/no enriching webhooks": {
|
||||||
|
@ -170,6 +178,29 @@ func TestWebhookController_Enrich(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"ok/with options": {
|
||||||
|
ctl: &WebhookController{
|
||||||
|
client: http.DefaultClient,
|
||||||
|
webhooks: []*Webhook{{Name: "people", Kind: "ENRICHING"}},
|
||||||
|
TemplateData: x509util.TemplateData{},
|
||||||
|
options: []webhook.RequestBodyOption{webhook.WithX5CCertificate(cert)},
|
||||||
|
},
|
||||||
|
req: &webhook.RequestBody{},
|
||||||
|
responses: []*webhook.ResponseBody{{Allow: true, Data: map[string]any{"role": "bar"}}},
|
||||||
|
expectErr: false,
|
||||||
|
expectTemplateData: x509util.TemplateData{"Webhooks": map[string]any{"people": map[string]any{"role": "bar"}}},
|
||||||
|
assertRequest: func(t *testing.T, req *webhook.RequestBody) {
|
||||||
|
key, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
assert.Equals(t, &webhook.X5CCertificate{
|
||||||
|
Raw: cert.Raw,
|
||||||
|
PublicKey: key,
|
||||||
|
PublicKeyAlgorithm: cert.PublicKeyAlgorithm.String(),
|
||||||
|
NotBefore: cert.NotBefore,
|
||||||
|
NotAfter: cert.NotAfter,
|
||||||
|
}, req.X5CCertificate)
|
||||||
|
},
|
||||||
|
},
|
||||||
"deny": {
|
"deny": {
|
||||||
ctl: &WebhookController{
|
ctl: &WebhookController{
|
||||||
client: http.DefaultClient,
|
client: http.DefaultClient,
|
||||||
|
@ -181,6 +212,20 @@ func TestWebhookController_Enrich(t *testing.T) {
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
expectTemplateData: x509util.TemplateData{},
|
expectTemplateData: x509util.TemplateData{},
|
||||||
},
|
},
|
||||||
|
"fail/with options": {
|
||||||
|
ctl: &WebhookController{
|
||||||
|
client: http.DefaultClient,
|
||||||
|
webhooks: []*Webhook{{Name: "people", Kind: "ENRICHING"}},
|
||||||
|
TemplateData: x509util.TemplateData{},
|
||||||
|
options: []webhook.RequestBodyOption{webhook.WithX5CCertificate(&x509.Certificate{
|
||||||
|
PublicKey: []byte("bad"),
|
||||||
|
})},
|
||||||
|
},
|
||||||
|
req: &webhook.RequestBody{},
|
||||||
|
responses: []*webhook.ResponseBody{{Allow: false}},
|
||||||
|
expectErr: true,
|
||||||
|
expectTemplateData: x509util.TemplateData{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
@ -200,16 +245,25 @@ func TestWebhookController_Enrich(t *testing.T) {
|
||||||
t.Fatalf("Got err %v, want %v", err, test.expectErr)
|
t.Fatalf("Got err %v, want %v", err, test.expectErr)
|
||||||
}
|
}
|
||||||
assert.Equals(t, test.expectTemplateData, test.ctl.TemplateData)
|
assert.Equals(t, test.expectTemplateData, test.ctl.TemplateData)
|
||||||
|
if test.assertRequest != nil {
|
||||||
|
test.assertRequest(t, test.req)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebhookController_Authorize(t *testing.T) {
|
func TestWebhookController_Authorize(t *testing.T) {
|
||||||
|
cert, err := pemutil.ReadCertificate("testdata/certs/x5c-leaf.crt", pemutil.WithFirstBlock())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
ctl *WebhookController
|
ctl *WebhookController
|
||||||
req *webhook.RequestBody
|
req *webhook.RequestBody
|
||||||
responses []*webhook.ResponseBody
|
responses []*webhook.ResponseBody
|
||||||
expectErr bool
|
expectErr bool
|
||||||
|
assertRequest func(t *testing.T, req *webhook.RequestBody)
|
||||||
}
|
}
|
||||||
tests := map[string]test{
|
tests := map[string]test{
|
||||||
"ok/no enriching webhooks": {
|
"ok/no enriching webhooks": {
|
||||||
|
@ -240,6 +294,27 @@ func TestWebhookController_Authorize(t *testing.T) {
|
||||||
responses: []*webhook.ResponseBody{{Allow: false}},
|
responses: []*webhook.ResponseBody{{Allow: false}},
|
||||||
expectErr: false,
|
expectErr: false,
|
||||||
},
|
},
|
||||||
|
"ok/with options": {
|
||||||
|
ctl: &WebhookController{
|
||||||
|
client: http.DefaultClient,
|
||||||
|
webhooks: []*Webhook{{Name: "people", Kind: "AUTHORIZING"}},
|
||||||
|
options: []webhook.RequestBodyOption{webhook.WithX5CCertificate(cert)},
|
||||||
|
},
|
||||||
|
req: &webhook.RequestBody{},
|
||||||
|
responses: []*webhook.ResponseBody{{Allow: true}},
|
||||||
|
expectErr: false,
|
||||||
|
assertRequest: func(t *testing.T, req *webhook.RequestBody) {
|
||||||
|
key, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
|
||||||
|
assert.FatalError(t, err)
|
||||||
|
assert.Equals(t, &webhook.X5CCertificate{
|
||||||
|
Raw: cert.Raw,
|
||||||
|
PublicKey: key,
|
||||||
|
PublicKeyAlgorithm: cert.PublicKeyAlgorithm.String(),
|
||||||
|
NotBefore: cert.NotBefore,
|
||||||
|
NotAfter: cert.NotAfter,
|
||||||
|
}, req.X5CCertificate)
|
||||||
|
},
|
||||||
|
},
|
||||||
"deny": {
|
"deny": {
|
||||||
ctl: &WebhookController{
|
ctl: &WebhookController{
|
||||||
client: http.DefaultClient,
|
client: http.DefaultClient,
|
||||||
|
@ -249,6 +324,18 @@ func TestWebhookController_Authorize(t *testing.T) {
|
||||||
responses: []*webhook.ResponseBody{{Allow: false}},
|
responses: []*webhook.ResponseBody{{Allow: false}},
|
||||||
expectErr: true,
|
expectErr: true,
|
||||||
},
|
},
|
||||||
|
"fail/with options": {
|
||||||
|
ctl: &WebhookController{
|
||||||
|
client: http.DefaultClient,
|
||||||
|
webhooks: []*Webhook{{Name: "people", Kind: "AUTHORIZING"}},
|
||||||
|
options: []webhook.RequestBodyOption{webhook.WithX5CCertificate(&x509.Certificate{
|
||||||
|
PublicKey: []byte("bad"),
|
||||||
|
})},
|
||||||
|
},
|
||||||
|
req: &webhook.RequestBody{},
|
||||||
|
responses: []*webhook.ResponseBody{{Allow: false}},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
@ -267,6 +354,9 @@ func TestWebhookController_Authorize(t *testing.T) {
|
||||||
if (err != nil) != test.expectErr {
|
if (err != nil) != test.expectErr {
|
||||||
t.Fatalf("Got err %v, want %v", err, test.expectErr)
|
t.Fatalf("Got err %v, want %v", err, test.expectErr)
|
||||||
}
|
}
|
||||||
|
if test.assertRequest != nil {
|
||||||
|
test.assertRequest(t, test.req)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"go.step.sm/linkedca"
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
|
"github.com/smallstep/certificates/webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// x5cPayload extends jwt.Claims with step attributes.
|
// x5cPayload extends jwt.Claims with step attributes.
|
||||||
|
@ -215,7 +216,8 @@ func (p *X5C) AuthorizeSign(_ context.Context, token string) ([]SignOption, erro
|
||||||
// The X509 certificate will be available using the template variable
|
// The X509 certificate will be available using the template variable
|
||||||
// AuthorizationCrt. For example {{ .AuthorizationCrt.DNSNames }} can be
|
// AuthorizationCrt. For example {{ .AuthorizationCrt.DNSNames }} can be
|
||||||
// used to get all the domains.
|
// used to get all the domains.
|
||||||
data.SetAuthorizationCertificate(claims.chains[0][0])
|
x5cLeaf := claims.chains[0][0]
|
||||||
|
data.SetAuthorizationCertificate(x5cLeaf)
|
||||||
|
|
||||||
templateOptions, err := TemplateOptions(p.Options, data)
|
templateOptions, err := TemplateOptions(p.Options, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -235,10 +237,10 @@ func (p *X5C) AuthorizeSign(_ context.Context, token string) ([]SignOption, erro
|
||||||
self,
|
self,
|
||||||
templateOptions,
|
templateOptions,
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeX5C, p.Name, "").WithControllerOptions(p.ctl),
|
newProvisionerExtensionOption(TypeX5C, p.Name, ""),
|
||||||
profileLimitDuration{
|
profileLimitDuration{
|
||||||
p.ctl.Claimer.DefaultTLSCertDuration(),
|
p.ctl.Claimer.DefaultTLSCertDuration(),
|
||||||
claims.chains[0][0].NotBefore, claims.chains[0][0].NotAfter,
|
x5cLeaf.NotBefore, x5cLeaf.NotAfter,
|
||||||
},
|
},
|
||||||
// validators
|
// validators
|
||||||
commonNameValidator(claims.Subject),
|
commonNameValidator(claims.Subject),
|
||||||
|
@ -246,7 +248,12 @@ func (p *X5C) AuthorizeSign(_ context.Context, token string) ([]SignOption, erro
|
||||||
defaultPublicKeyValidator{},
|
defaultPublicKeyValidator{},
|
||||||
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
newValidityValidator(p.ctl.Claimer.MinTLSCertDuration(), p.ctl.Claimer.MaxTLSCertDuration()),
|
||||||
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
newX509NamePolicyValidator(p.ctl.getPolicy().getX509()),
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_X509),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_X509,
|
||||||
|
webhook.WithX5CCertificate(x5cLeaf),
|
||||||
|
webhook.WithAuthorizationPrincipal(x5cLeaf.Subject.CommonName),
|
||||||
|
),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +312,8 @@ func (p *X5C) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||||
// The X509 certificate will be available using the template variable
|
// The X509 certificate will be available using the template variable
|
||||||
// AuthorizationCrt. For example {{ .AuthorizationCrt.DNSNames }} can be
|
// AuthorizationCrt. For example {{ .AuthorizationCrt.DNSNames }} can be
|
||||||
// used to get all the domains.
|
// used to get all the domains.
|
||||||
data.SetAuthorizationCertificate(claims.chains[0][0])
|
x5cLeaf := claims.chains[0][0]
|
||||||
|
data.SetAuthorizationCertificate(x5cLeaf)
|
||||||
|
|
||||||
templateOptions, err := TemplateSSHOptions(p.Options, data)
|
templateOptions, err := TemplateSSHOptions(p.Options, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -325,7 +333,7 @@ func (p *X5C) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||||
return append(signOptions,
|
return append(signOptions,
|
||||||
p,
|
p,
|
||||||
// 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.
|
||||||
&sshLimitDuration{p.ctl.Claimer, claims.chains[0][0].NotAfter},
|
&sshLimitDuration{p.ctl.Claimer, x5cLeaf.NotAfter},
|
||||||
// Validate public key.
|
// Validate public key.
|
||||||
&sshDefaultPublicKeyValidator{},
|
&sshDefaultPublicKeyValidator{},
|
||||||
// Validate the validity period.
|
// Validate the validity period.
|
||||||
|
@ -335,6 +343,11 @@ func (p *X5C) AuthorizeSSHSign(_ context.Context, token string) ([]SignOption, e
|
||||||
// Ensure that all principal names are allowed
|
// Ensure that all principal names are allowed
|
||||||
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), p.ctl.getPolicy().getSSHUser()),
|
newSSHNamePolicyValidator(p.ctl.getPolicy().getSSHHost(), p.ctl.getPolicy().getSSHUser()),
|
||||||
// Call webhooks
|
// Call webhooks
|
||||||
p.ctl.newWebhookController(data, linkedca.Webhook_SSH),
|
p.ctl.newWebhookController(
|
||||||
|
data,
|
||||||
|
linkedca.Webhook_SSH,
|
||||||
|
webhook.WithX5CCertificate(x5cLeaf),
|
||||||
|
webhook.WithAuthorizationPrincipal(x5cLeaf.Subject.CommonName),
|
||||||
|
),
|
||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"go.step.sm/crypto/jose"
|
"go.step.sm/crypto/jose"
|
||||||
"go.step.sm/crypto/pemutil"
|
"go.step.sm/crypto/pemutil"
|
||||||
"go.step.sm/crypto/randutil"
|
"go.step.sm/crypto/randutil"
|
||||||
|
"go.step.sm/linkedca"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
"github.com/smallstep/certificates/api/render"
|
"github.com/smallstep/certificates/api/render"
|
||||||
|
@ -497,6 +498,8 @@ func TestX5C_AuthorizeSign(t *testing.T) {
|
||||||
assert.Equals(t, nil, v.policyEngine)
|
assert.Equals(t, nil, v.policyEngine)
|
||||||
case *WebhookController:
|
case *WebhookController:
|
||||||
assert.Len(t, 0, v.webhooks)
|
assert.Len(t, 0, v.webhooks)
|
||||||
|
assert.Equals(t, linkedca.Webhook_X509, v.certType)
|
||||||
|
assert.Len(t, 2, v.options)
|
||||||
default:
|
default:
|
||||||
assert.FatalError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
assert.FatalError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
||||||
}
|
}
|
||||||
|
@ -801,6 +804,8 @@ func TestX5C_AuthorizeSSHSign(t *testing.T) {
|
||||||
case *sshDefaultPublicKeyValidator, *sshCertDefaultValidator, sshCertificateOptionsFunc:
|
case *sshDefaultPublicKeyValidator, *sshCertDefaultValidator, sshCertificateOptionsFunc:
|
||||||
case *WebhookController:
|
case *WebhookController:
|
||||||
assert.Len(t, 0, v.webhooks)
|
assert.Len(t, 0, v.webhooks)
|
||||||
|
assert.Equals(t, linkedca.Webhook_SSH, v.certType)
|
||||||
|
assert.Len(t, 2, v.options)
|
||||||
default:
|
default:
|
||||||
assert.FatalError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
assert.FatalError(t, fmt.Errorf("unexpected sign option of type %T", v))
|
||||||
}
|
}
|
||||||
|
|
|
@ -648,7 +648,6 @@ func claimsToCertificates(c *linkedca.Claims) (*provisioner.Claims, error) {
|
||||||
pc := &provisioner.Claims{
|
pc := &provisioner.Claims{
|
||||||
DisableRenewal: &c.DisableRenewal,
|
DisableRenewal: &c.DisableRenewal,
|
||||||
AllowRenewalAfterExpiry: &c.AllowRenewalAfterExpiry,
|
AllowRenewalAfterExpiry: &c.AllowRenewalAfterExpiry,
|
||||||
DisableSmallstepExtensions: &c.DisableSmallstepExtensions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
@ -687,7 +686,6 @@ func claimsToLinkedca(c *provisioner.Claims) *linkedca.Claims {
|
||||||
|
|
||||||
disableRenewal := config.DefaultDisableRenewal
|
disableRenewal := config.DefaultDisableRenewal
|
||||||
allowRenewalAfterExpiry := config.DefaultAllowRenewalAfterExpiry
|
allowRenewalAfterExpiry := config.DefaultAllowRenewalAfterExpiry
|
||||||
disableSmallstepExtensions := config.DefaultDisableSmallstepExtensions
|
|
||||||
|
|
||||||
if c.DisableRenewal != nil {
|
if c.DisableRenewal != nil {
|
||||||
disableRenewal = *c.DisableRenewal
|
disableRenewal = *c.DisableRenewal
|
||||||
|
@ -695,14 +693,10 @@ func claimsToLinkedca(c *provisioner.Claims) *linkedca.Claims {
|
||||||
if c.AllowRenewalAfterExpiry != nil {
|
if c.AllowRenewalAfterExpiry != nil {
|
||||||
allowRenewalAfterExpiry = *c.AllowRenewalAfterExpiry
|
allowRenewalAfterExpiry = *c.AllowRenewalAfterExpiry
|
||||||
}
|
}
|
||||||
if c.DisableSmallstepExtensions != nil {
|
|
||||||
disableSmallstepExtensions = *c.DisableSmallstepExtensions
|
|
||||||
}
|
|
||||||
|
|
||||||
lc := &linkedca.Claims{
|
lc := &linkedca.Claims{
|
||||||
DisableRenewal: disableRenewal,
|
DisableRenewal: disableRenewal,
|
||||||
AllowRenewalAfterExpiry: allowRenewalAfterExpiry,
|
AllowRenewalAfterExpiry: allowRenewalAfterExpiry,
|
||||||
DisableSmallstepExtensions: disableSmallstepExtensions,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.DefaultTLSDur != nil || c.MinTLSDur != nil || c.MaxTLSDur != nil {
|
if c.DefaultTLSDur != nil || c.MinTLSDur != nil || c.MaxTLSDur != nil {
|
||||||
|
|
10
go.mod
10
go.mod
|
@ -36,14 +36,14 @@ require (
|
||||||
golang.org/x/crypto v0.11.0
|
golang.org/x/crypto v0.11.0
|
||||||
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0
|
golang.org/x/exp v0.0.0-20230310171629-522b1b587ee0
|
||||||
golang.org/x/net v0.12.0
|
golang.org/x/net v0.12.0
|
||||||
google.golang.org/api v0.131.0
|
google.golang.org/api v0.132.0
|
||||||
google.golang.org/grpc v1.56.2
|
google.golang.org/grpc v1.56.2
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.31.0
|
||||||
gopkg.in/square/go-jose.v2 v2.6.0
|
gopkg.in/square/go-jose.v2 v2.6.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.110.2 // indirect
|
cloud.google.com/go v0.110.4 // indirect
|
||||||
cloud.google.com/go/compute v1.20.1 // indirect
|
cloud.google.com/go/compute v1.20.1 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
cloud.google.com/go/iam v1.1.0 // indirect
|
cloud.google.com/go/iam v1.1.0 // indirect
|
||||||
|
@ -134,9 +134,9 @@ require (
|
||||||
golang.org/x/text v0.11.0 // indirect
|
golang.org/x/text v0.11.0 // indirect
|
||||||
golang.org/x/time v0.1.0 // indirect
|
golang.org/x/time v0.1.0 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 // indirect
|
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
20
go.sum
20
go.sum
|
@ -31,8 +31,8 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD
|
||||||
cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||||
cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||||
cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA=
|
cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
|
||||||
cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw=
|
cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||||
|
@ -1488,8 +1488,8 @@ google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtuk
|
||||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||||
google.golang.org/api v0.131.0 h1:AcgWS2edQ4chVEt/SxgDKubVu/9/idCJy00tBGuGB4M=
|
google.golang.org/api v0.132.0 h1:8t2/+qZ26kAOGSmOiHwVycqVaDg7q3JDILrNi/Z6rvc=
|
||||||
google.golang.org/api v0.131.0/go.mod h1:7vtkbKv2REjJbxmHSkBTBQ5LUGvPdAqjjvt84XAfhpA=
|
google.golang.org/api v0.132.0/go.mod h1:AeTBC6GpJnJSRJjktDcPX0QwtS8pGYZOV6MSuSCusw0=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
@ -1567,12 +1567,12 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr
|
||||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||||
google.golang.org/genproto v0.0.0-20230629202037-9506855d4529 h1:9JucMWR7sPvCxUFd6UsOUNmA5kCcWOfORaT3tpAsKQs=
|
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8=
|
||||||
google.golang.org/genproto v0.0.0-20230629202037-9506855d4529/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
|
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529 h1:s5YSX+ZH5b5vS9rnpGymvIyMpLRJizowqDlOuyjXnTk=
|
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20230629202037-9506855d4529/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
|
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130 h1:2FZP5XuJY9zQyGM5N0rtovnoXjiMUEIUMvw0m9wlpLc=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:8mL13HKkDa+IuJ8yruA3ci0q+0vsUz4m//+ottjwS5o=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||||
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
|
2
pki/testdata/helm/with-ssh-and-acme.yml
vendored
2
pki/testdata/helm/with-ssh-and-acme.yml
vendored
|
@ -23,7 +23,7 @@ inject:
|
||||||
authority:
|
authority:
|
||||||
enableAdmin: false
|
enableAdmin: false
|
||||||
provisioners:
|
provisioners:
|
||||||
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","claims":{"enableSSHCA":true,"disableRenewal":false,"allowRenewalAfterExpiry":false,"disableSmallstepExtensions":false},"options":{"x509":{},"ssh":{}}}
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","claims":{"enableSSHCA":true,"disableRenewal":false,"allowRenewalAfterExpiry":false},"options":{"x509":{},"ssh":{}}}
|
||||||
- {"type":"ACME","name":"acme"}
|
- {"type":"ACME","name":"acme"}
|
||||||
- {"type":"SSHPOP","name":"sshpop","claims":{"enableSSHCA":true}}
|
- {"type":"SSHPOP","name":"sshpop","claims":{"enableSSHCA":true}}
|
||||||
tls:
|
tls:
|
||||||
|
|
2
pki/testdata/helm/with-ssh.yml
vendored
2
pki/testdata/helm/with-ssh.yml
vendored
|
@ -23,7 +23,7 @@ inject:
|
||||||
authority:
|
authority:
|
||||||
enableAdmin: false
|
enableAdmin: false
|
||||||
provisioners:
|
provisioners:
|
||||||
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","claims":{"enableSSHCA":true,"disableRenewal":false,"allowRenewalAfterExpiry":false,"disableSmallstepExtensions":false},"options":{"x509":{},"ssh":{}}}
|
- {"type":"JWK","name":"step-cli","key":{"use":"sig","kty":"EC","kid":"zsUmysmDVoGJ71YoPHyZ-68tNihDaDaO5Mu7xX3M-_I","crv":"P-256","alg":"ES256","x":"Pqnua4CzqKz6ua41J3yeWZ1sRkGt0UlCkbHv8H2DGuY","y":"UhoZ_2ItDen9KQTcjay-ph-SBXH0mwqhHyvrrqIFDOI"},"encryptedKey":"eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjEwMDAwMCwicDJzIjoiZjVvdGVRS2hvOXl4MmQtSGlMZi05QSJ9.eYA6tt3fNuUpoxKWDT7P0Lbn2juxhEbTxEnwEMbjlYLLQ3sxL-dYTA.ven-FhmdjlC9itH0.a2jRTarN9vPd6F_mWnNBlOn6KbfMjCApmci2t65XbAsLzYFzhI_79Ykm5ueMYTupWLTjBJctl-g51ZHmsSB55pStbpoyyLNAsUX2E1fTmHe-Ni8bRrspwLv15FoN1Xo1g0mpR-ufWIFxOsW-QIfnMmMIIkygVuHFXmg2tFpzTNNG5aS29K3dN2nyk0WJrdIq79hZSTqVkkBU25Yu3A46sgjcM86XcIJJ2XUEih_KWEa6T1YrkixGu96pebjVqbO0R6dbDckfPF7FqNnwPHVtb1ACFpEYoOJVIbUCMaARBpWsxYhjJZlEM__XA46l8snFQDkNY3CdN0p1_gF3ckA.JLmq9nmu1h9oUi1S8ZxYjA","claims":{"enableSSHCA":true,"disableRenewal":false,"allowRenewalAfterExpiry":false},"options":{"x509":{},"ssh":{}}}
|
||||||
- {"type":"SSHPOP","name":"sshpop","claims":{"enableSSHCA":true}}
|
- {"type":"SSHPOP","name":"sshpop","claims":{"enableSSHCA":true}}
|
||||||
tls:
|
tls:
|
||||||
cipherSuites:
|
cipherSuites:
|
||||||
|
|
|
@ -43,8 +43,23 @@ var (
|
||||||
"acme_external_account_keyID_reference_index",
|
"acme_external_account_keyID_reference_index",
|
||||||
"acme_external_account_keyID_provisionerID_index",
|
"acme_external_account_keyID_provisionerID_index",
|
||||||
}
|
}
|
||||||
|
adminTables = []string{
|
||||||
|
"admins",
|
||||||
|
"provisioners",
|
||||||
|
"authority_policies",
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DB interface {
|
||||||
|
CreateTable([]byte) error
|
||||||
|
Set(bucket, key, value []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type dryRunDB struct{}
|
||||||
|
|
||||||
|
func (*dryRunDB) CreateTable([]byte) error { return nil }
|
||||||
|
func (*dryRunDB) Set(bucket, key, value []byte) error { return nil }
|
||||||
|
|
||||||
func usage(fs *flag.FlagSet) {
|
func usage(fs *flag.FlagSet) {
|
||||||
name := filepath.Base(os.Args[0])
|
name := filepath.Base(os.Args[0])
|
||||||
fmt.Fprintf(os.Stderr, "%s is a tool to migrate data from BadgerDB to MySQL or PostgreSQL.\n", name)
|
fmt.Fprintf(os.Stderr, "%s is a tool to migrate data from BadgerDB to MySQL or PostgreSQL.\n", name)
|
||||||
|
@ -52,14 +67,15 @@ func usage(fs *flag.FlagSet) {
|
||||||
fmt.Fprintf(os.Stderr, " %s [-v1|-v2] -dir=<path> [-value-dir=<path>] -type=type -database=<source>\n", name)
|
fmt.Fprintf(os.Stderr, " %s [-v1|-v2] -dir=<path> [-value-dir=<path>] -type=type -database=<source>\n", name)
|
||||||
fmt.Fprintln(os.Stderr, "\nExamples:")
|
fmt.Fprintln(os.Stderr, "\nExamples:")
|
||||||
fmt.Fprintf(os.Stderr, " %s -v1 -dir /var/lib/step-ca/db -type=mysql -database \"user@unix/step_ca\"\n", name)
|
fmt.Fprintf(os.Stderr, " %s -v1 -dir /var/lib/step-ca/db -type=mysql -database \"user@unix/step_ca\"\n", name)
|
||||||
fmt.Fprintf(os.Stderr, " %s -v2 -dir /var/lib/step-ca/db -type=mysql -database \"user:password@tcp(localhost:3306)/step_ca\"\n", name)
|
fmt.Fprintf(os.Stderr, " %s -v1 -dir /var/lib/step-ca/db -type=mysql -database \"user:password@tcp(localhost:3306)/step_ca\"\n", name)
|
||||||
fmt.Fprintf(os.Stderr, " %s -v2 -dir /var/lib/step-ca/db -type=postgresql -database \"user=postgres dbname=step_ca\"\n", name)
|
fmt.Fprintf(os.Stderr, " %s -v2 -dir /var/lib/step-ca/db -type=postgresql -database \"user=postgres dbname=step_ca\"\n", name)
|
||||||
|
fmt.Fprintf(os.Stderr, " %s -v2 -dir /var/lib/step-ca/db -dry-run\"\n", name)
|
||||||
fmt.Fprintln(os.Stderr, "\nOptions:")
|
fmt.Fprintln(os.Stderr, "\nOptions:")
|
||||||
fs.PrintDefaults()
|
fs.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var v1, v2 bool
|
var v1, v2, dryRun bool
|
||||||
var dir, valueDir string
|
var dir, valueDir string
|
||||||
var typ, database string
|
var typ, database string
|
||||||
var key string
|
var key string
|
||||||
|
@ -67,12 +83,13 @@ func main() {
|
||||||
fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
|
||||||
fs.BoolVar(&v1, "v1", false, "use badger v1 as the source database")
|
fs.BoolVar(&v1, "v1", false, "use badger v1 as the source database")
|
||||||
fs.BoolVar(&v2, "v2", true, "use badger v2 as the source database")
|
fs.BoolVar(&v2, "v2", false, "use badger v2 as the source database")
|
||||||
fs.StringVar(&dir, "dir", "", "badger database directory")
|
fs.StringVar(&dir, "dir", "", "badger database directory")
|
||||||
fs.StringVar(&valueDir, "value-dir", "", "badger database value directory")
|
fs.StringVar(&valueDir, "value-dir", "", "badger database value directory")
|
||||||
fs.StringVar(&typ, "type", "", "the destination database type to use")
|
fs.StringVar(&typ, "type", "", "the destination database type to use")
|
||||||
fs.StringVar(&database, "database", "", "the destination driver-specific data source name")
|
fs.StringVar(&database, "database", "", "the destination driver-specific data source name")
|
||||||
fs.StringVar(&key, "key", "", "the key used to resume the migration")
|
fs.StringVar(&key, "key", "", "the key used to resume the migration")
|
||||||
|
fs.BoolVar(&dryRun, "dry-run", false, "runs the migration scripts without writing anything")
|
||||||
fs.Usage = func() { usage(fs) }
|
fs.Usage = func() { usage(fs) }
|
||||||
fs.Parse(os.Args[1:])
|
fs.Parse(os.Args[1:])
|
||||||
|
|
||||||
|
@ -81,9 +98,9 @@ func main() {
|
||||||
fatal("flag -v1 or -v2 are required")
|
fatal("flag -v1 or -v2 are required")
|
||||||
case dir == "":
|
case dir == "":
|
||||||
fatal("flag -dir is required")
|
fatal("flag -dir is required")
|
||||||
case typ != "postgresql" && typ != "mysql":
|
case typ != "postgresql" && typ != "mysql" && !dryRun:
|
||||||
fatal(`flag -type must be "postgresql" or "mysql"`)
|
fatal(`flag -type must be "postgresql" or "mysql"`)
|
||||||
case database == "":
|
case database == "" && !dryRun:
|
||||||
fatal("flag --database required")
|
fatal("flag --database required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,13 +127,19 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := nosql.New(typ, database)
|
var db DB
|
||||||
|
if dryRun {
|
||||||
|
db = &dryRunDB{}
|
||||||
|
} else {
|
||||||
|
db, err = nosql.New(typ, database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("error opening %s database: %v", typ, err)
|
fatal("error opening %s database: %v", typ, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
allTables := append([]string{}, authorityTables...)
|
allTables := append([]string{}, authorityTables...)
|
||||||
allTables = append(allTables, acmeTables...)
|
allTables = append(allTables, acmeTables...)
|
||||||
|
allTables = append(allTables, adminTables...)
|
||||||
|
|
||||||
// Convert prefix names to badger key prefixes
|
// Convert prefix names to badger key prefixes
|
||||||
badgerKeys := make([][]byte, len(allTables))
|
badgerKeys := make([][]byte, len(allTables))
|
||||||
|
|
|
@ -68,6 +68,13 @@ func WithAttestationData(data *AttestationData) RequestBodyOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithAuthorizationPrincipal(p string) RequestBodyOption {
|
||||||
|
return func(rb *RequestBody) error {
|
||||||
|
rb.AuthorizationPrincipal = p
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func WithSSHCertificateRequest(cr sshutil.CertificateRequest) RequestBodyOption {
|
func WithSSHCertificateRequest(cr sshutil.CertificateRequest) RequestBodyOption {
|
||||||
return func(rb *RequestBody) error {
|
return func(rb *RequestBody) error {
|
||||||
rb.SSHCertificateRequest = &SSHCertificateRequest{
|
rb.SSHCertificateRequest = &SSHCertificateRequest{
|
||||||
|
@ -95,3 +102,23 @@ func WithSSHCertificate(cert *sshutil.Certificate, certTpl *ssh.Certificate) Req
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithX5CCertificate(leaf *x509.Certificate) RequestBodyOption {
|
||||||
|
return func(rb *RequestBody) error {
|
||||||
|
rb.X5CCertificate = &X5CCertificate{
|
||||||
|
Raw: leaf.Raw,
|
||||||
|
PublicKeyAlgorithm: leaf.PublicKeyAlgorithm.String(),
|
||||||
|
NotBefore: leaf.NotBefore,
|
||||||
|
NotAfter: leaf.NotAfter,
|
||||||
|
}
|
||||||
|
if leaf.PublicKey != nil {
|
||||||
|
key, err := x509.MarshalPKIXPublicKey(leaf.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rb.X5CCertificate.PublicKey = key
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/smallstep/assert"
|
"github.com/smallstep/assert"
|
||||||
|
"go.step.sm/crypto/keyutil"
|
||||||
"go.step.sm/crypto/sshutil"
|
"go.step.sm/crypto/sshutil"
|
||||||
"go.step.sm/crypto/x509util"
|
"go.step.sm/crypto/x509util"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
@ -16,6 +17,15 @@ func TestNewRequestBody(t *testing.T) {
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
t2 := t1.Add(time.Hour)
|
t2 := t1.Add(time.Hour)
|
||||||
|
|
||||||
|
key, err := keyutil.GenerateDefaultSigner()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
keyBytes, err := x509.MarshalPKIXPublicKey(key.Public())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
type test struct {
|
type test struct {
|
||||||
options []RequestBodyOption
|
options []RequestBodyOption
|
||||||
want *RequestBody
|
want *RequestBody
|
||||||
|
@ -103,6 +113,40 @@ func TestNewRequestBody(t *testing.T) {
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
"X5C Certificate": {
|
||||||
|
options: []RequestBodyOption{
|
||||||
|
WithX5CCertificate(&x509.Certificate{
|
||||||
|
Raw: []byte("some raw data"),
|
||||||
|
NotBefore: t1,
|
||||||
|
NotAfter: t2,
|
||||||
|
PublicKeyAlgorithm: x509.ECDSA,
|
||||||
|
PublicKey: key.Public(),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
want: &RequestBody{
|
||||||
|
X5CCertificate: &X5CCertificate{
|
||||||
|
Raw: []byte("some raw data"),
|
||||||
|
PublicKeyAlgorithm: "ECDSA",
|
||||||
|
NotBefore: t1,
|
||||||
|
NotAfter: t2,
|
||||||
|
PublicKey: keyBytes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
"fail/X5C Certificate": {
|
||||||
|
options: []RequestBodyOption{
|
||||||
|
WithX5CCertificate(&x509.Certificate{
|
||||||
|
Raw: []byte("some raw data"),
|
||||||
|
NotBefore: t1,
|
||||||
|
NotAfter: t2,
|
||||||
|
PublicKeyAlgorithm: x509.ECDSA,
|
||||||
|
PublicKey: []byte("fail"),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
|
@ -56,6 +56,17 @@ type AttestationData struct {
|
||||||
PermanentIdentifier string `json:"permanentIdentifier"`
|
PermanentIdentifier string `json:"permanentIdentifier"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X5CCertificate is the authorization certificate sent to webhook servers for
|
||||||
|
// enriching or authorizing webhooks when signing X509 or SSH certificates using
|
||||||
|
// the X5C provisioner.
|
||||||
|
type X5CCertificate struct {
|
||||||
|
Raw []byte `json:"raw"`
|
||||||
|
PublicKey []byte `json:"publicKey"`
|
||||||
|
PublicKeyAlgorithm string `json:"publicKeyAlgorithm"`
|
||||||
|
NotBefore time.Time `json:"notBefore"`
|
||||||
|
NotAfter time.Time `json:"notAfter"`
|
||||||
|
}
|
||||||
|
|
||||||
// RequestBody is the body sent to webhook servers.
|
// RequestBody is the body sent to webhook servers.
|
||||||
type RequestBody struct {
|
type RequestBody struct {
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
|
@ -71,4 +82,8 @@ type RequestBody struct {
|
||||||
// Only set for SCEP challenge validation requests
|
// Only set for SCEP challenge validation requests
|
||||||
SCEPChallenge string `json:"scepChallenge,omitempty"`
|
SCEPChallenge string `json:"scepChallenge,omitempty"`
|
||||||
SCEPTransactionID string `json:"scepTransactionID,omitempty"`
|
SCEPTransactionID string `json:"scepTransactionID,omitempty"`
|
||||||
|
// Only set for X5C provisioners
|
||||||
|
X5CCertificate *X5CCertificate `json:"x5cCertificate,omitempty"`
|
||||||
|
// Set for X5C, AWS, GCP, and Azure provisioners
|
||||||
|
AuthorizationPrincipal string `json:"authorizationPrincipal,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue