Skip to content
Snippets Groups Projects
Commit 6e4d8cc8 authored by Mariano Cano's avatar Mariano Cano
Browse files

Add support for identity certificates.

Extract a subject from sans if common name is empty.
parent 4816e2e8
No related branches found
No related tags found
No related merge requests found
......@@ -20,7 +20,7 @@ var collection = new(sync.Map)
// Step implements a Step JWK provisioners in charge of signing certificate
// requests using step certificates.
type Step struct {
uid types.UID
name string
provisioner *ca.Provisioner
}
......@@ -35,9 +35,22 @@ func New(iss *api.StepIssuer, password []byte) (*Step, error) {
if err != nil {
return nil, err
}
return &Step{
p := &Step{
name: iss.Name + "." + iss.Namespace,
provisioner: provisioner,
}, nil
}
// Request identity certificate if required.
if version, err := provisioner.Version(); err == nil {
if version.RequireClientAuthentication {
if err := p.createIdentityCertificate(); err != nil {
return nil, err
}
}
}
return p, nil
}
// Load returns a Step provisioner by NamespacedName.
......@@ -55,6 +68,30 @@ func Store(namespacedName types.NamespacedName, provisioner *Step) {
collection.Store(namespacedName, provisioner)
}
func (s *Step) createIdentityCertificate() error {
csr, pk, err := ca.CreateCertificateRequest(s.name)
if err != nil {
return err
}
token, err := s.provisioner.Token(s.name)
if err != nil {
return err
}
resp, err := s.provisioner.Sign(&capi.SignRequest{
CsrPEM: *csr,
OTT: token,
})
if err != nil {
return err
}
tr, err := s.provisioner.Client.Transport(context.Background(), resp, pk)
if err != nil {
return err
}
s.provisioner.Client.SetTransport(tr)
return nil
}
// Sign sends the certificate requests to the Step CA and returns the signed
// certificate.
func (s *Step) Sign(ctx context.Context, cr *certmanager.CertificateRequest) ([]byte, []byte, error) {
......@@ -80,15 +117,17 @@ func (s *Step) Sign(ctx context.Context, cr *certmanager.CertificateRequest) ([]
return nil, nil, err
}
var sans []string
for _, dns := range csr.DNSNames {
sans = append(sans, dns)
}
sans := append([]string{}, csr.DNSNames...)
for _, ip := range csr.IPAddresses {
sans = append(sans, ip.String())
}
token, err := s.provisioner.Token(csr.Subject.CommonName, sans...)
subject := csr.Subject.CommonName
if subject == "" {
subject = generateSubject(sans)
}
token, err := s.provisioner.Token(subject, sans...)
if err != nil {
return nil, nil, err
}
......@@ -151,3 +190,19 @@ func encodeX509(cert *x509.Certificate) ([]byte, error) {
}
return caPem.Bytes(), nil
}
// generateSubject returns the first SAN that is not 127.0.0.1 or localhost. The
// CSRs generated by the Certificate resource have always those SANs. If no SANs
// are available `step-issuer-certificate` will be used as a subject is always
// required.
func generateSubject(sans []string) string {
if len(sans) == 0 {
return "step-issuer-certificate"
}
for _, s := range sans {
if s != "127.0.0.1" && s != "localhost" {
return s
}
}
return sans[0]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment