Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACME] Fix account registration on issuer secret update #81

Merged
merged 1 commit into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0

############# builder #############
FROM eu.gcr.io/gardener-project/3rd/golang:1.16.2 AS builder
FROM eu.gcr.io/gardener-project/3rd/golang:1.16.6 AS builder

WORKDIR /build
COPY . .
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ require (
github.com/miekg/dns v1.1.31
github.com/onsi/ginkgo v1.14.1
github.com/onsi/gomega v1.10.2
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.7.1
golang.org/x/lint v0.0.0-20200302205851-738671d3881b
k8s.io/api v0.20.6
Expand Down
3 changes: 1 addition & 2 deletions pkg/cert/legobridge/cakeypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"strings"
"time"

"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
)

Expand Down Expand Up @@ -79,7 +78,7 @@ func (c *TLSKeyPair) RawCertInfo() ([]byte, error) {

certInfo, err := json.Marshal(raw)
if err != nil {
return nil, errors.Wrap(err, "encoding certificate info failed")
return nil, fmt.Errorf("encoding certificate info failed: %w", err)
}
return certInfo, nil
}
6 changes: 2 additions & 4 deletions pkg/cert/legobridge/certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import (
"sync"
"time"

"github.com/pkg/errors"

"github.com/gardener/cert-management/pkg/cert/metrics"
"github.com/gardener/cert-management/pkg/cert/utils"

Expand Down Expand Up @@ -368,7 +366,7 @@ func DecodeCertificate(tlsCrt []byte) (*x509.Certificate, error) {
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("parsing certificate failed with %s", err.Error())
return nil, fmt.Errorf("parsing certificate failed: %w", err)
}
return cert, nil
}
Expand Down Expand Up @@ -415,7 +413,7 @@ func RevokeCertificate(user *RegistrationUser, cert []byte) error {
// A client facilitates communication with the CA server.
client, err := lego.NewClient(config)
if err != nil {
return errors.Wrap(err, "client creation failed")
return fmt.Errorf("client creation failed: %w", err)
}

return client.Certificate.Revoke(cert)
Expand Down
10 changes: 5 additions & 5 deletions pkg/cert/legobridge/dnscontrollerprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func newDNSControllerProvider(settings DNSControllerSettings,
certificateName resources.ObjectName, targetClass string, issuerKey utils.IssuerKey) (ProviderWithCount, error) {
itf, err := settings.Cluster.Resources().GetByExample(&dnsapi.DNSEntry{})
if err != nil {
return nil, fmt.Errorf("cannot get DNSEntry resources: %s", err.Error())
return nil, fmt.Errorf("cannot get DNSEntry resources: %w", err)
}
n := atomic.AddUint32(&index, 1)
return &dnsControllerProvider{
Expand Down Expand Up @@ -136,7 +136,7 @@ func (p *dnsControllerProvider) Present(domain, token, keyAuth string) error {
}
_, err := p.entryResources.Create(entry)
if err != nil {
return fmt.Errorf("creating DNSEntry %s/%s failed with %s", entry.Namespace, entry.Name, err.Error())
return fmt.Errorf("creating DNSEntry %s/%s failed: %w", entry.Namespace, entry.Name, err)
}
return nil
}
Expand All @@ -145,14 +145,14 @@ func (p *dnsControllerProvider) Present(domain, token, keyAuth string) error {
err := retryOnUpdateError(func() error {
obj, err := p.entryResources.Get_(entry)
if err != nil {
return fmt.Errorf("getting DNSEntry %s/%s failed with %s", entry.Namespace, entry.Name, err.Error())
return fmt.Errorf("getting DNSEntry %s/%s failed: %w", entry.Namespace, entry.Name, err)
}
entry = obj.Data().(*dnsapi.DNSEntry)
setSpec(entry)
p.logger.Infof("presenting DNSEntry %s/%s for certificate resource %s with %d values", entry.Namespace, entry.Name, p.certificateName, len(values))
_, err = p.entryResources.Update(entry)
if err != nil {
return &updateError{msg: fmt.Sprintf("updating DNSEntry %s/%s failed with %s", entry.Namespace, entry.Name, err.Error())}
return &updateError{msg: fmt.Sprintf("updating DNSEntry %s/%s failed: %s", entry.Namespace, entry.Name, err)}
}
return nil
})
Expand Down Expand Up @@ -199,7 +199,7 @@ func (p *dnsControllerProvider) CleanUp(domain, token, keyAuth string) error {
p.logger.Infof("cleanup DNSEntry %s/%s for request %s", entry.Namespace, entry.Name, p.certificateName)
err := p.entryResources.Delete(entry)
if err != nil {
return fmt.Errorf("deleting DNSEntry %s/%s failed with %s", entry.Namespace, entry.Name, err.Error())
return fmt.Errorf("deleting DNSEntry %s/%s failed: %w", entry.Namespace, entry.Name, err)
}
return nil
}
Expand Down
23 changes: 11 additions & 12 deletions pkg/cert/legobridge/pki.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"time"

"github.com/go-acme/lego/v4/certificate"
"github.com/pkg/errors"
)

var (
Expand Down Expand Up @@ -107,15 +106,15 @@ func generateKey(algo x509.PublicKeyAlgorithm, size int) (crypto.Signer, []byte,
switch algo {
case x509.RSA:
if size < RSAMinSize {
return nil, nil, errors.New("RSA key is too weak")
return nil, nil, fmt.Errorf("RSA key is too weak")
}
if size > RSAMaxSize {
return nil, nil, errors.New("RSA key size too large")
return nil, nil, fmt.Errorf("RSA key size too large")
}

key, err = rsa.GenerateKey(rand.Reader, size)
if err != nil {
return nil, nil, errors.Wrap(err, "unable to generate RSA private key")
return nil, nil, fmt.Errorf("unable to generate RSA private key: %w", err)
}
case x509.ECDSA:
var curve elliptic.Curve
Expand All @@ -127,20 +126,20 @@ func generateKey(algo x509.PublicKeyAlgorithm, size int) (crypto.Signer, []byte,
case ECCurve256:
curve = elliptic.P256()
default:
return nil, nil, errors.New("invalid elliptic curve")
return nil, nil, fmt.Errorf("invalid elliptic curve")
}

key, err = ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return nil, nil, errors.Wrap(err, "unable to generate RSA private key")
return nil, nil, fmt.Errorf("unable to generate RSA private key: %w", err)
}
default:
return nil, nil, errors.New("algorithm not supported")
return nil, nil, fmt.Errorf("algorithm not supported")
}

pem, err := privateKeyToBytes(key)
if err != nil {
return nil, nil, errors.Wrap(err, "encoding private key failed")
return nil, nil, fmt.Errorf("encoding private key failed: %w", err)
}
return key, pem, nil
}
Expand Down Expand Up @@ -185,7 +184,7 @@ func createCertReq(input ObtainInput) (*x509.CertificateRequest, error) {
func generateCSRPEM(csr *x509.CertificateRequest, privateKey crypto.Signer) ([]byte, error) {
derBytes, err := x509.CreateCertificateRequest(rand.Reader, csr, privateKey)
if err != nil {
return nil, errors.Wrap(err, "failed to create certificate request")
return nil, fmt.Errorf("failed to create certificate request: %w", err)
}

pemBytes := bytes.NewBuffer([]byte{})
Expand Down Expand Up @@ -220,7 +219,7 @@ func generateCertFromCSR(csrPEM []byte, duration time.Duration, isCA bool) (*x50

serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, errors.Wrap(err, "failed to generate serial number")
return nil, fmt.Errorf("failed to generate serial number: %w", err)
}

return &x509.Certificate{
Expand All @@ -244,7 +243,7 @@ func generateCertFromCSR(csrPEM []byte, duration time.Duration, isCA bool) (*x50
func signCert(cert, issuerCert *x509.Certificate, publicKey crypto.PublicKey, signerKey crypto.PrivateKey) ([]byte, error) {
derBytes, err := x509.CreateCertificate(rand.Reader, cert, issuerCert, publicKey, signerKey)
if err != nil {
return nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
return nil, fmt.Errorf("error creating x509 certificate: %w", err)
}

pemBytes := bytes.NewBuffer([]byte{})
Expand Down Expand Up @@ -365,7 +364,7 @@ func encodeCertPEM(out io.Writer, derBytes []byte) error {
func encodePEM(out io.Writer, b *pem.Block) error {
err := pem.Encode(out, b)
if err != nil {
return errors.Wrap(err, "error encoding certificate PEM")
return fmt.Errorf("error encoding certificate PEM: %w", err)
}
return nil
}
Expand Down
9 changes: 6 additions & 3 deletions pkg/cert/legobridge/reguser.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ func NewRegistrationUserFromEmailAndPrivateKey(issuerKey utils.IssuerKey,
func (u *RegistrationUser) ToSecretData() (map[string][]byte, error) {
privkey, err := privateKeyToBytes(u.key)
if err != nil {
return nil, fmt.Errorf("encoding private key failed: %s", err.Error())
return nil, fmt.Errorf("encoding private key failed: %w", err)
}
return map[string][]byte{KeyPrivateKey: privkey}, nil
}
Expand All @@ -149,7 +149,7 @@ func (u *RegistrationUser) ToSecretData() (map[string][]byte, error) {
func (u *RegistrationUser) RawRegistration() ([]byte, error) {
reg, err := json.Marshal(u.registration)
if err != nil {
return nil, fmt.Errorf("encoding registration failed: %s", err.Error())
return nil, fmt.Errorf("encoding registration failed: %w", err)
}
return reg, nil
}
Expand All @@ -169,7 +169,10 @@ func RegistrationUserFromSecretData(issuerKey utils.IssuerKey,
reg := &registration.Resource{}
err = json.Unmarshal(registrationRaw, reg)
if err != nil {
return nil, fmt.Errorf("unmarshalling registration json failed with %s", err.Error())
return nil, fmt.Errorf("unmarshalling registration json failed: %w", err)
}
if reg.URI == "" {
return nil, fmt.Errorf("unmarshalling registration with unexpected empty URI")
}
metrics.AddACMEAccountRegistration(issuerKey, reg.URI, email)
return &RegistrationUser{email: email, registration: reg, caDirURL: caDirURL, key: privateKey,
Expand Down
2 changes: 1 addition & 1 deletion pkg/cert/utils/utils_certificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func ExtractDomains(spec *api.CertificateSpec) ([]string, error) {
func ExtractCommonNameAnDNSNames(csr []byte) (cn *string, san []string, err error) {
certificateRequest, err := extractCertificateRequest(csr)
if err != nil {
err = fmt.Errorf("parsing CSR failed with: %s", err)
err = fmt.Errorf("parsing CSR failed: %w", err)
return
}
cnvalue := certificateRequest.Subject.CommonName
Expand Down
120 changes: 44 additions & 76 deletions pkg/controller/issuer/acme/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,123 +56,91 @@ func (r *acmeIssuerHandler) Reconcile(logger logger.LogContext, obj resources.Ob
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("missing server in ACME spec"))
}

r.support.AddIssuerDomains(obj.ClusterKey(), issuer.Spec.ACME.Domains)
r.support.AddIssuerDomains(obj.ClusterKey(), acme.Domains)

r.support.RememberIssuerSecret(obj.ClusterKey(), issuer.Spec.ACME.PrivateKeySecretRef, "")
r.support.RememberIssuerSecret(obj.ClusterKey(), acme.PrivateKeySecretRef, "")

issuerKey := r.support.ToIssuerKey(obj.ClusterKey())
var secret *corev1.Secret
var secretHash string
var err error
if acme.PrivateKeySecretRef != nil {
secret, err = r.support.ReadIssuerSecret(issuerKey, acme.PrivateKeySecretRef)
if err != nil {
if acme.AutoRegistration {
logger.Info("spec.acme.privateKeySecretRef not existing, creating new account")
} else {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("loading issuer secret failed with %s", err.Error()))
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("loading issuer secret failed: %w", err))
}
}
hash := r.support.CalcSecretHash(secret)
r.support.RememberIssuerSecret(obj.ClusterKey(), issuer.Spec.ACME.PrivateKeySecretRef, hash)
secretHash = r.support.CalcSecretHash(secret)
r.support.RememberIssuerSecret(obj.ClusterKey(), acme.PrivateKeySecretRef, secretHash)
}
if secret != nil && issuer.Status.ACME != nil && issuer.Status.ACME.Raw != nil {
eabKeyID, eabHmacKey, err := r.support.LoadEABHmacKey(issuerKey, acme)
if secret != nil {
objKey := obj.ClusterKey()
eabKeyID, eabHmacKey, err := r.support.LoadEABHmacKey(&objKey, issuerKey, acme)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("loading EAB secret failed: %s", err))
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("loading EAB secret failed: %w", err))
}
user, err := legobridge.RegistrationUserFromSecretData(issuerKey, acme.Email, acme.Server, issuer.Status.ACME.Raw,
var raw []byte
if core.IsSameExistingRegistration(issuer.Status.ACME, secretHash) {
raw = issuer.Status.ACME.Raw
} else {
user, err := legobridge.NewRegistrationUserFromEmail(issuerKey, acme.Email, acme.Server, secret.Data, eabKeyID, eabHmacKey)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("creating registration user failed: %w", err))
}
raw, err = user.RawRegistration()
if err != nil {
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("registration marshalling failed: %w", err))
}
}
user, err := legobridge.RegistrationUserFromSecretData(issuerKey, acme.Email, acme.Server, raw,
secret.Data, eabKeyID, eabHmacKey)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("extracting registration user from secret failed with %s", err.Error()))
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("extracting registration user from secret failed: %w", err))
}
if user.GetEmail() != acme.Email {
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("email of registration user from secret does not match %s != %s", user.GetEmail(), acme.Email))
}
return r.support.SucceededAndTriggerCertificates(logger, obj, &acmeType, issuer.Status.ACME.Raw)
} else if secret != nil || acme.AutoRegistration {
eabKid, eabHmacKey, err := r.prepareEAB(obj, issuer)
wrapped, err := core.WrapRegistration(raw, secretHash)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, err)
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("wrapped registration marshalling failed: %w", err))
}
var secretData map[string][]byte
if secret != nil {
secretData = secret.Data
}
user, err := legobridge.NewRegistrationUserFromEmail(issuerKey, acme.Email, acme.Server, secretData, eabKid, eabHmacKey)
return r.support.SucceededAndTriggerCertificates(logger, obj, &acmeType, wrapped)
} else if acme.AutoRegistration {
user, err := legobridge.NewRegistrationUserFromEmail(issuerKey, acme.Email, acme.Server, nil, "", "")
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("creating registration user failed with %s", err.Error()))
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("creating registration user failed: %w", err))
}

if secret != nil {
err = r.support.UpdateIssuerSecret(issuerKey, user, secret)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("updating issuer secret failed with %s", err.Error()))
}
} else {
secretRef, secret, err := r.support.WriteIssuerSecretFromRegistrationUser(issuerKey, issuer.UID, user, acme.PrivateKeySecretRef)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("writing issuer secret failed with %s", err.Error()))
}
issuer.Spec.ACME.PrivateKeySecretRef = secretRef
hash := r.support.CalcSecretHash(secret)
r.support.RememberIssuerSecret(obj.ClusterKey(), issuer.Spec.ACME.PrivateKeySecretRef, hash)
secretRef, secret, err := r.support.WriteIssuerSecretFromRegistrationUser(issuerKey, issuer.UID, user, acme.PrivateKeySecretRef)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("writing issuer secret failed: %w", err))
}
acme.PrivateKeySecretRef = secretRef
secretHash = r.support.CalcSecretHash(secret)
r.support.RememberIssuerSecret(obj.ClusterKey(), acme.PrivateKeySecretRef, secretHash)

regRaw, err := user.RawRegistration()
if err != nil {
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("registration marshalling failed with %s", err.Error()))
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("registration marshalling failed: %w", err))
}
newObj, err := r.support.GetIssuerResources(issuerKey).Update(issuer)
if err != nil {
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("updating resource failed with %s", err.Error()))
return r.failedAcmeRetry(logger, obj, api.StateError, fmt.Errorf("updating resource failed: %w", err))
}

return r.support.SucceededAndTriggerCertificates(logger, newObj, &acmeType, regRaw)
raw, err := core.WrapRegistration(regRaw, secretHash)
if err != nil {
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("wrapped registration marshalling failed: %w", err))
}
return r.support.SucceededAndTriggerCertificates(logger, newObj, &acmeType, raw)
} else {
return r.failedAcme(logger, obj, api.StateError, fmt.Errorf("neither `SecretRef` or `AutoRegistration: true` provided"))
}
}

func (r *acmeIssuerHandler) prepareEAB(obj resources.Object, issuer *api.Issuer) (eabKid, eabHmacKey string, err error) {
acme := issuer.Spec.ACME
eab := acme.ExternalAccountBinding

if eab == nil {
return
}

r.support.RememberIssuerEABSecret(obj.ClusterKey(), eab.KeySecretRef, "")

if eab.KeyID == "" {
err = fmt.Errorf("missing keyID for external account binding in ACME spec")
return
}

if eab.KeySecretRef == nil {
err = fmt.Errorf("missing keySecretRef for external account binding in ACME spec")
return
}

issuerKey := r.support.ToIssuerKey(obj.ClusterKey())
secret, err := r.support.ReadIssuerSecret(issuerKey, eab.KeySecretRef)
if err != nil {
err = fmt.Errorf("loading issuer secret for external account binding failed with %s", err.Error())
return
}
hash := r.support.CalcSecretHash(secret)
r.support.RememberIssuerEABSecret(obj.ClusterKey(), eab.KeySecretRef, hash)

hmacEncoded, ok := secret.Data[legobridge.KeyHmacKey]
if !ok {
err = fmt.Errorf("key %s not found in secret data", legobridge.KeyHmacKey)
return
}

eabKid = eab.KeyID
eabHmacKey = string(hmacEncoded)
return
}

func (r *acmeIssuerHandler) failedAcme(logger logger.LogContext, obj resources.Object, state string, err error) reconcile.Status {
return r.support.Failed(logger, obj, state, &acmeType, err, false)
}
Expand Down
Loading