Skip to content

Commit

Permalink
introducing CA spec, refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinWeindel authored and lcavajani committed Sep 29, 2020
1 parent 62d4fda commit efbe18d
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 23 deletions.
19 changes: 19 additions & 0 deletions examples/20-issuer-ca.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Secret
metadata:
name: issuer-ca-secret
namespace: default
type: Opaque
data:
foo: Vk13YXJlMSFWTXdhcmUxIQ==
---
apiVersion: cert.gardener.cloud/v1alpha1
kind: Issuer
metadata:
name: issuer-ca
namespace: default
spec:
ca:
privateKeySecretRef:
name: issuer-ca-secret
namespace: default
19 changes: 19 additions & 0 deletions examples/30-cert-ca.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: cert.gardener.cloud/v1alpha1
kind: Certificate
metadata:
annotations:
# class annotation only needed if cert-controller-manager is started with --cert-class=myclass
#cert.gardener.cloud/class: myclass
name: cert-ca
namespace: default
spec:
commonName: cert1.mydomain.com
dnsNames:
- cert1.my-other-domain.com
# if issuer is not specified, the default issuer is used
issuerRef:
name: issuer-ca
# optionally specify secret to store certificate
secretRef:
name: cert-ca-secret
namespace: default
12 changes: 12 additions & 0 deletions pkg/apis/cert/v1alpha1/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type IssuerSpec struct {
// ACME is the ACME protocol specific spec.
// +optional
ACME *ACMESpec `json:"acme,omitempty"`
// CA is the CA specific spec.
// +optional
CA *CASpec `json:"ca,omitempty"`
// RequestsPerDayQuota is the maximum number of certificate requests per days allowed for this issuer
// +optional
RequestsPerDayQuota *int `json:"requestsPerDayQuota,omitempty"`
Expand All @@ -58,6 +61,13 @@ type ACMESpec struct {
PrivateKeySecretRef *corev1.SecretReference `json:"privateKeySecretRef,omitempty"`
}

// CASpec is the CA specific part of the spec.
type CASpec struct {
// PrivateKeySecretRef is the secret ref to the CA secret.
// +optional
PrivateKeySecretRef *corev1.SecretReference `json:"privateKeySecretRef,omitempty"`
}

// IssuerStatus is the status of the issuer.
type IssuerStatus struct {
// ObservedGeneration is the observed generation of the spec.
Expand All @@ -70,6 +80,8 @@ type IssuerStatus struct {
Type *string `json:"type"`
// ACME is the ACME specific status.
ACME *runtime.RawExtension `json:"acme,omitempty"`
// CA is the CA specific status.
CA *runtime.RawExtension `json:"ca,omitempty"`
// RequestsPerDayQuota is the actual maximum number of certificate requests per days allowed for this issuer
RequestsPerDayQuota int `json:"requestsPerDayQuota,omitempty"`
}
7 changes: 2 additions & 5 deletions pkg/controller/issuer/acme/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ import (
"github.com/gardener/cert-management/pkg/controller/issuer/core"
)

// ACMEType is the type name for ACME.
const ACMEType = "acme"

var acmeType = ACMEType
var acmeType = core.ACMEType

// NewACMEIssuerHandler creates an ACME IssuerHandler.
func NewACMEIssuerHandler(support *core.Support) (core.IssuerHandler, error) {
Expand All @@ -37,7 +34,7 @@ type acmeIssuerHandler struct {
}

func (r *acmeIssuerHandler) Type() string {
return ACMEType
return core.ACMEType
}

func (r *acmeIssuerHandler) CanReconcile(issuer *api.Issuer) bool {
Expand Down
88 changes: 88 additions & 0 deletions pkg/controller/issuer/ca/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2019 SAP SE or an SAP affiliate company. All rights reserved. ur file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use ur file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
*
*/

package ca

import (
"fmt"

corev1 "k8s.io/api/core/v1"

"github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile"
"github.com/gardener/controller-manager-library/pkg/logger"
"github.com/gardener/controller-manager-library/pkg/resources"

api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1"
"github.com/gardener/cert-management/pkg/controller/issuer/core"
)

var caType = core.CAType

// NewCAIssuerHandler creates an ACME IssuerHandler.
func NewCAIssuerHandler(support *core.Support) (core.IssuerHandler, error) {
return &caIssuerHandler{
support: support,
}, nil
}

type caIssuerHandler struct {
support *core.Support
}

func (r *caIssuerHandler) Type() string {
return core.CAType
}

func (r *caIssuerHandler) CanReconcile(issuer *api.Issuer) bool {
return issuer != nil && issuer.Spec.CA != nil
}

func (r *caIssuerHandler) Reconcile(logger logger.LogContext, obj resources.Object, issuer *api.Issuer) reconcile.Status {
logger.Infof("reconciling")

ca := issuer.Spec.CA
if ca == nil {
return r.failedCA(logger, obj, api.StateError, fmt.Errorf("missing CA spec"))
}

r.support.RememberIssuerSecret(obj.ObjectName(), ca.PrivateKeySecretRef, "")

var secret *corev1.Secret
var err error
if ca.PrivateKeySecretRef != nil {
secret, err = r.support.ReadIssuerSecret(ca.PrivateKeySecretRef)
if err != nil {
return r.failedCA(logger, obj, api.StateError, fmt.Errorf("loading issuer secret failed with %s", err.Error()))
}
hash := r.support.CalcSecretHash(secret)
r.support.RememberIssuerSecret(obj.ObjectName(), ca.PrivateKeySecretRef, hash)
}
if secret != nil && issuer.Status.CA != nil && issuer.Status.CA.Raw != nil {
// TODO check secret?
return r.support.SucceededAndTriggerCertificates(logger, obj, &caType, issuer.Status.CA.Raw)
} else if secret != nil {
// TODO check secret
regRaw := []byte("{\"foo\":\"bar\"}")

return r.support.SucceededAndTriggerCertificates(logger, obj, &caType, regRaw)
} else {
return r.failedCA(logger, obj, api.StateError, fmt.Errorf("`SecretRef` not provided"))
}
}

func (r *caIssuerHandler) failedCA(logger logger.LogContext, obj resources.Object, state string, err error) reconcile.Status {
return r.support.Failed(logger, obj, state, &caType, err)
}
36 changes: 32 additions & 4 deletions pkg/controller/issuer/certificate/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,26 @@ func (r *certReconciler) obtainCertificateAndPending(logger logger.LogContext, o
cert := obj.Data().(*api.Certificate)
logger.Infof("obtain certificate")

reguser, server, err := r.restoreRegUser(cert)
issuer, err := r.loadIssuer(cert)
if err != nil {
return r.failed(logger, obj, api.StateError, err)
}

if issuer.Spec.ACME != nil && issuer.Spec.CA != nil {
return r.failed(logger, obj, api.StateError, fmt.Errorf("invalid issuer spec: only ACME or CA can be set, but not both"))
}
if issuer.Spec.ACME != nil {
return r.obtainCertificateAndPendingACME(logger, obj, renewSecret, cert, issuer)
}
if issuer.Spec.CA != nil {
return r.obtainCertificateCA(logger, obj, renewSecret, cert, issuer)
}
return r.failed(logger, obj, api.StateError, fmt.Errorf("incomplete issuer spec (ACME or CA section must be provided)"))
}

func (r *certReconciler) obtainCertificateAndPendingACME(logger logger.LogContext, obj resources.Object,
renewSecret *corev1.Secret, cert *api.Certificate, issuer *api.Issuer) reconcile.Status {
reguser, server, err := r.restoreRegUser(issuer)
if err != nil {
return r.failed(logger, obj, api.StateError, err)
}
Expand Down Expand Up @@ -376,15 +395,24 @@ func (r *certReconciler) obtainCertificateAndPending(logger logger.LogContext, o
return r.pending(logger, obj)
}

func (r *certReconciler) restoreRegUser(crt *api.Certificate) (*legobridge.RegistrationUser, string, error) {
func (r *certReconciler) obtainCertificateCA(logger logger.LogContext, obj resources.Object,
renewSecret *corev1.Secret, cert *api.Certificate, issuer *api.Issuer) reconcile.Status {
// TODO create certificate
return r.succeeded(logger, obj)
}

func (r *certReconciler) loadIssuer(crt *api.Certificate) (*api.Issuer, error) {
// fetch issuer
issuerObjectName := r.issuerObjectName(&crt.Spec)
issuer := &api.Issuer{}
_, err := r.support.GetIssuerResources().GetInto(issuerObjectName, issuer)
if err != nil {
return nil, "", errors.Wrap(err, "fetching issuer failed")
return nil, errors.Wrap(err, "fetching issuer failed")
}
return issuer, nil
}

func (r *certReconciler) restoreRegUser(issuer *api.Issuer) (*legobridge.RegistrationUser, string, error) {
// fetch issuer secret
secretRef := issuer.Spec.ACME.PrivateKeySecretRef
if secretRef == nil {
Expand All @@ -401,7 +429,7 @@ func (r *certReconciler) restoreRegUser(crt *api.Certificate) (*legobridge.Regis
}
issuerSecretObjectName := resources.NewObjectName(secretRef.Namespace, secretRef.Name)
issuerSecret := &corev1.Secret{}
_, err = r.support.GetIssuerSecretResources().GetInto(issuerSecretObjectName, issuerSecret)
_, err := r.support.GetIssuerSecretResources().GetInto(issuerSecretObjectName, issuerSecret)
if err != nil {
return nil, "", errors.Wrap(err, "fetching issuer secret failed")
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/controller/issuer/core/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

package core

const (
// ACMEType is the type name for ACME.
ACMEType = "acme"
// CAType is the type name for CA.
CAType = "ca"
)

const (
// OptDefaultIssuer is the default-issuer command line option.
OptDefaultIssuer = "default-issuer"
Expand Down
40 changes: 27 additions & 13 deletions pkg/controller/issuer/core/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,24 @@ import (
"bytes"
"crypto/sha256"
"fmt"
"github.com/gardener/cert-management/pkg/cert/metrics"
"sort"
"strings"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sort"
"strings"

api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1"
"github.com/gardener/cert-management/pkg/cert/legobridge"
"github.com/gardener/cert-management/pkg/cert/utils"
ctrl "github.com/gardener/cert-management/pkg/controller"
"github.com/gardener/cert-management/pkg/cert/metrics"

"github.com/gardener/controller-manager-library/pkg/controllermanager/controller"
"github.com/gardener/controller-manager-library/pkg/controllermanager/controller/reconcile"
"github.com/gardener/controller-manager-library/pkg/logger"
"github.com/gardener/controller-manager-library/pkg/resources"

api "github.com/gardener/cert-management/pkg/apis/cert/v1alpha1"
"github.com/gardener/cert-management/pkg/cert/legobridge"
"github.com/gardener/cert-management/pkg/cert/utils"
ctrl "github.com/gardener/cert-management/pkg/controller"
)

// Enqueuer is an interface to allow enqueue a key
Expand Down Expand Up @@ -290,19 +293,30 @@ func (s *Support) SucceededAndTriggerCertificates(logger logger.LogContext, obj
s.triggerCertificates(logger, obj.ObjectName())

mod, status := s.prepareUpdateStatus(obj, api.StateReady, itype, nil)
if itype != nil {
switch *itype {
case ACMEType:
updateTypeStatus(mod, &status.ACME, regRaw)
case CAType:
updateTypeStatus(mod, &status.CA, regRaw)
}
}
s.updateStatus(mod)

return reconcile.Succeeded(logger)
}

func updateTypeStatus(mod *resources.ModificationState, status **runtime.RawExtension, regRaw []byte) {
changedRegistration := false
if status.ACME == nil || status.ACME.Raw == nil {
if *status == nil || (*status).Raw == nil {
changedRegistration = regRaw != nil
} else {
changedRegistration = !bytes.Equal(status.ACME.Raw, regRaw)
changedRegistration = !bytes.Equal((*status).Raw, regRaw)
}
if changedRegistration {
status.ACME = &runtime.RawExtension{Raw: regRaw}
*status = &runtime.RawExtension{Raw: regRaw}
mod.Modify(true)
}
s.updateStatus(mod)

return reconcile.Succeeded(logger)
}

// AddCertificate adds a certificate
Expand Down
3 changes: 2 additions & 1 deletion pkg/controller/issuer/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package issuer

import (
"github.com/gardener/cert-management/pkg/controller/issuer/acme"
"github.com/gardener/cert-management/pkg/controller/issuer/ca"
"github.com/gardener/cert-management/pkg/controller/issuer/certificate"
"github.com/gardener/cert-management/pkg/controller/issuer/core"
corev1 "k8s.io/api/core/v1"
Expand All @@ -21,7 +22,7 @@ import (
)

func newCompoundReconciler(c controller.Interface) (reconcile.Interface, error) {
handler, support, err := core.NewHandlerSupport(c, acme.NewACMEIssuerHandler)
handler, support, err := core.NewHandlerSupport(c, acme.NewACMEIssuerHandler, ca.NewCAIssuerHandler)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit efbe18d

Please sign in to comment.