forked from TrueCloudLab/certificates
Remove ACME restrictions and add proper template support.
This commit is contained in:
parent
6a09f11357
commit
a7fe0104c4
4 changed files with 42 additions and 24 deletions
|
@ -18,6 +18,7 @@ type Provisioner interface {
|
||||||
AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error)
|
AuthorizeSign(ctx context.Context, token string) ([]provisioner.SignOption, error)
|
||||||
GetName() string
|
GetName() string
|
||||||
DefaultTLSCertDuration() time.Duration
|
DefaultTLSCertDuration() time.Duration
|
||||||
|
GetOptions() *provisioner.ProvisionerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// MockProvisioner for testing
|
// MockProvisioner for testing
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/authority/provisioner"
|
"github.com/smallstep/certificates/authority/provisioner"
|
||||||
|
"github.com/smallstep/certificates/x509util"
|
||||||
"github.com/smallstep/nosql"
|
"github.com/smallstep/nosql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -299,23 +300,23 @@ func (o *order) finalize(db nosql.DB, csr *x509.CertificateRequest, auth SignAut
|
||||||
orderNames = uniqueLowerNames(orderNames)
|
orderNames = uniqueLowerNames(orderNames)
|
||||||
|
|
||||||
// Validate identifier names against CSR alternative names.
|
// Validate identifier names against CSR alternative names.
|
||||||
|
//
|
||||||
|
// Note that with certificate templates we are not going to check for the no
|
||||||
|
// presence of other SANs as they will only be set if the templates allows
|
||||||
|
// them.
|
||||||
if len(csr.DNSNames) != len(orderNames) {
|
if len(csr.DNSNames) != len(orderNames) {
|
||||||
return nil, BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames))
|
return nil, BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sans := make([]x509util.SubjectAlternativeName, len(csr.DNSNames))
|
||||||
for i := range csr.DNSNames {
|
for i := range csr.DNSNames {
|
||||||
if csr.DNSNames[i] != orderNames[i] {
|
if csr.DNSNames[i] != orderNames[i] {
|
||||||
return nil, BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames))
|
return nil, BadCSRErr(errors.Errorf("CSR names do not match identifiers exactly: CSR names = %v, Order names = %v", csr.DNSNames, orderNames))
|
||||||
}
|
}
|
||||||
}
|
sans[i] = x509util.SubjectAlternativeName{
|
||||||
|
Type: x509util.DNSType,
|
||||||
if len(csr.IPAddresses) > 0 {
|
Value: csr.DNSNames[i],
|
||||||
return nil, BadCSRErr(errors.Errorf("CSR contains IP Address SANs, but should only contain DNS Names"))
|
}
|
||||||
}
|
|
||||||
if len(csr.EmailAddresses) > 0 {
|
|
||||||
return nil, BadCSRErr(errors.Errorf("CSR contains Email Address SANs, but should only contain DNS Names"))
|
|
||||||
}
|
|
||||||
if len(csr.URIs) > 0 {
|
|
||||||
return nil, BadCSRErr(errors.Errorf("CSR contains URI SANs, but should only contain DNS Names"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get authorizations from the ACME provisioner.
|
// Get authorizations from the ACME provisioner.
|
||||||
|
@ -325,6 +326,17 @@ func (o *order) finalize(db nosql.DB, csr *x509.CertificateRequest, auth SignAut
|
||||||
return nil, ServerInternalErr(errors.Wrapf(err, "error retrieving authorization options from ACME provisioner"))
|
return nil, ServerInternalErr(errors.Wrapf(err, "error retrieving authorization options from ACME provisioner"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Template data
|
||||||
|
data := x509util.NewTemplateData()
|
||||||
|
data.SetCommonName(csr.Subject.CommonName)
|
||||||
|
data.Set(x509util.SANsKey, sans)
|
||||||
|
|
||||||
|
templateOptions, err := provisioner.TemplateOptions(p.GetOptions(), data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ServerInternalErr(errors.Wrapf(err, "error creating template options from ACME provisioner"))
|
||||||
|
}
|
||||||
|
signOps = append(signOps, templateOptions)
|
||||||
|
|
||||||
// Create and store a new certificate.
|
// Create and store a new certificate.
|
||||||
certChain, err := auth.Sign(csr, provisioner.Options{
|
certChain, err := auth.Sign(csr, provisioner.Options{
|
||||||
NotBefore: provisioner.NewTimeDuration(o.NotBefore),
|
NotBefore: provisioner.NewTimeDuration(o.NotBefore),
|
||||||
|
|
|
@ -3,12 +3,10 @@ package provisioner
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"net/http"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/smallstep/certificates/errs"
|
"github.com/smallstep/certificates/errs"
|
||||||
"github.com/smallstep/certificates/x509util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ACME is the acme provisioner type, an entity that can authorize the ACME
|
// ACME is the acme provisioner type, an entity that can authorize the ACME
|
||||||
|
@ -48,6 +46,11 @@ func (p *ACME) GetEncryptedKey() (string, string, bool) {
|
||||||
return "", "", false
|
return "", "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOptions returns the configured provisioner options.
|
||||||
|
func (p *ACME) GetOptions() *ProvisionerOptions {
|
||||||
|
return p.Options
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultTLSCertDuration returns the default TLS cert duration enforced by
|
// DefaultTLSCertDuration returns the default TLS cert duration enforced by
|
||||||
// the provisioner.
|
// the provisioner.
|
||||||
func (p *ACME) DefaultTLSCertDuration() time.Duration {
|
func (p *ACME) DefaultTLSCertDuration() time.Duration {
|
||||||
|
@ -75,14 +78,7 @@ func (p *ACME) Init(config Config) (err error) {
|
||||||
// in the ACME protocol. This method returns a list of modifiers / constraints
|
// in the ACME protocol. This method returns a list of modifiers / constraints
|
||||||
// on the resulting certificate.
|
// on the resulting certificate.
|
||||||
func (p *ACME) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) {
|
func (p *ACME) AuthorizeSign(ctx context.Context, token string) ([]SignOption, error) {
|
||||||
// Certificate templates
|
|
||||||
templateOptions, err := TemplateOptions(p.Options, x509util.NewTemplateData())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errs.Wrap(http.StatusInternalServerError, err, "acme.AuthorizeSign")
|
|
||||||
}
|
|
||||||
|
|
||||||
return []SignOption{
|
return []SignOption{
|
||||||
templateOptions,
|
|
||||||
// modifiers / withOptions
|
// modifiers / withOptions
|
||||||
newProvisionerExtensionOption(TypeACME, p.Name, ""),
|
newProvisionerExtensionOption(TypeACME, p.Name, ""),
|
||||||
newForceCNOption(p.ForceCN),
|
newForceCNOption(p.ForceCN),
|
||||||
|
|
|
@ -48,6 +48,15 @@ var (
|
||||||
ExtKeyUsageMicrosoftKernelCodeSigning = convertName("MicrosoftKernelCodeSigning")
|
ExtKeyUsageMicrosoftKernelCodeSigning = convertName("MicrosoftKernelCodeSigning")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Names used and SubjectAlternativeNames types.
|
||||||
|
const (
|
||||||
|
AutoType = "auto"
|
||||||
|
DNSType = "dns"
|
||||||
|
EmailType = "email"
|
||||||
|
IPType = "ip"
|
||||||
|
URIType = "uri"
|
||||||
|
)
|
||||||
|
|
||||||
// Extension is the JSON representation of a raw X.509 extensions.
|
// Extension is the JSON representation of a raw X.509 extensions.
|
||||||
type Extension struct {
|
type Extension struct {
|
||||||
ID ObjectIdentifier `json:"id"`
|
ID ObjectIdentifier `json:"id"`
|
||||||
|
@ -106,21 +115,21 @@ type SubjectAlternativeName struct {
|
||||||
|
|
||||||
func (s SubjectAlternativeName) Set(c *x509.Certificate) {
|
func (s SubjectAlternativeName) Set(c *x509.Certificate) {
|
||||||
switch strings.ToLower(s.Type) {
|
switch strings.ToLower(s.Type) {
|
||||||
case "dns":
|
case DNSType:
|
||||||
c.DNSNames = append(c.DNSNames, s.Value)
|
c.DNSNames = append(c.DNSNames, s.Value)
|
||||||
case "email":
|
case EmailType:
|
||||||
c.EmailAddresses = append(c.EmailAddresses, s.Value)
|
c.EmailAddresses = append(c.EmailAddresses, s.Value)
|
||||||
case "ip":
|
case IPType:
|
||||||
// The validation of the IP would happen in the unmarshaling, but just
|
// The validation of the IP would happen in the unmarshaling, but just
|
||||||
// to be sure we are only adding valid IPs.
|
// to be sure we are only adding valid IPs.
|
||||||
if ip := net.ParseIP(s.Value); ip != nil {
|
if ip := net.ParseIP(s.Value); ip != nil {
|
||||||
c.IPAddresses = append(c.IPAddresses, ip)
|
c.IPAddresses = append(c.IPAddresses, ip)
|
||||||
}
|
}
|
||||||
case "uri":
|
case URIType:
|
||||||
if u, err := url.Parse(s.Value); err == nil {
|
if u, err := url.Parse(s.Value); err == nil {
|
||||||
c.URIs = append(c.URIs, u)
|
c.URIs = append(c.URIs, u)
|
||||||
}
|
}
|
||||||
case "auto", "":
|
case "", AutoType:
|
||||||
dnsNames, ips, emails, uris := SplitSANs([]string{s.Value})
|
dnsNames, ips, emails, uris := SplitSANs([]string{s.Value})
|
||||||
c.DNSNames = append(c.DNSNames, dnsNames...)
|
c.DNSNames = append(c.DNSNames, dnsNames...)
|
||||||
c.IPAddresses = append(c.IPAddresses, ips...)
|
c.IPAddresses = append(c.IPAddresses, ips...)
|
||||||
|
|
Loading…
Reference in a new issue