Skip to content

Commit 251b014

Browse files
authored
Merge pull request #18 from JoshVanL/cert-manager-v1.3
2 parents 443b921 + eeba5c7 commit 251b014

16 files changed

+1504
-275
lines changed

.golangci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,7 @@ linters:
7474
- unused
7575
- varcheck
7676
- whitespace
77+
issues:
78+
# Exclude scope checks in tests: "Using the variable on range scope `test` in function literal"
79+
exclude:
80+
- Using the variable on range scope `test` in function literal

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ A KMSIssuer resource configures a new [Cert-Manager external issuer](https://cer
170170
| spec.duration | duration | Certificate default Duration. (optional, default=26280h aka 3 years) |
171171
| spec.renewBefore | duration | The amount of time before the certificate’s notAfter time that the issuer will begin to attempt to renew the certificate. If this value is greater than the total duration of the certificate (i.e. notAfter - notBefore), it will be automatically renewed 2/3rds of the way through the certificate’s duration. <br> <br> The `NotBefore` field on the certificate is set to the current time rounded down by the renewal interval. For example, if the certificate is renewed every hour, the `NotBefore` field is set to the beggining of the hour. If the certificate is renewed every day, the `NotBefore` field is set to the beggining of the day. This allows the generation of consistent certificates regardless of when it has been generated during the renewal period, or recreate the same certificate after a backup/restore of your kubernetes cluster. For more details on the computation, check the [time.Truncate](https://golang.org/pkg/time/#Time.Truncate) function. |
172172

173+
## Disable Approval Check
174+
175+
The KMS Issuer will wait for CertificateRequests to have an [approved condition
176+
set](https://cert-manager.io/docs/concepts/certificaterequest/#approval) before
177+
signing. If using an older version of cert-manager (pre v1.3), you can disable
178+
this check by supplying the command line flag `-enable-approved-check=false` to
179+
the Issuer Deployment.
180+
173181
## Contributing
174182

175183
Kms-Issuer is built using the [Kubebuilder](https://book.kubebuilder.io/) framework. See the [official documentation](https://book.kubebuilder.io/quick-start.html) to get started and check [CONTRIBUTING.md](CONTRIBUTING.md) for more details.

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/cert-manager.io_certificaterequests.yaml

Lines changed: 613 additions & 57 deletions
Large diffs are not rendered by default.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# permissions to approve all cert-manager.skyscanner.net requests
2+
apiVersion: rbac.authorization.k8s.io/v1
3+
kind: ClusterRole
4+
metadata:
5+
name: cert-manager-controller-approve:cert-manager-skyscanner-net
6+
rules:
7+
- apiGroups:
8+
- cert-manager.io
9+
resources:
10+
- signers
11+
verbs:
12+
- approve
13+
resourceNames:
14+
- kmsissuers.cert-manager.skyscanner.net/*
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# bind the cert-manager internal approver to approve
2+
# cert-manager.skyscanner.net CertificateRequests
3+
apiVersion: rbac.authorization.k8s.io/v1
4+
kind: ClusterRoleBinding
5+
metadata:
6+
name: cert-manager-controller-approve:cert-manager-skyscanner-net
7+
roleRef:
8+
apiGroup: rbac.authorization.k8s.io
9+
kind: ClusterRole
10+
name: cert-manager-controller-approve:cert-manager-skyscanner-net
11+
subjects:
12+
- kind: ServiceAccount
13+
name: cert-manager
14+
namespace: cert-manager

config/rbac/kustomization.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ resources:
1010
- auth_proxy_role.yaml
1111
- auth_proxy_role_binding.yaml
1212
- auth_proxy_client_clusterrole.yaml
13+
# Comment the following 2 lines if you don't wish for the internal cert-manager
14+
# approver to approve all cert-manager.skyscanner.net CertificateRequests by default.
15+
- cert_manager_controller_approver_clusterrole.yaml
16+
- cert_manager_controller_approver_clusterrolebinding.yaml

controllers/certificaterequest_controller.go

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,32 @@ import (
2626
kmsca "github.com/Skyscanner/kms-issuer/pkg/kmsca"
2727
"github.com/go-logr/logr"
2828
apiutil "github.com/jetstack/cert-manager/pkg/api/util"
29-
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
29+
cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
3030
cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
3131
pkiutil "github.com/jetstack/cert-manager/pkg/util/pki"
3232
core "k8s.io/api/core/v1"
3333
apierrors "k8s.io/apimachinery/pkg/api/errors"
34+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
"k8s.io/apimachinery/pkg/types"
3536
"k8s.io/client-go/tools/record"
37+
"k8s.io/utils/clock"
3638
ctrl "sigs.k8s.io/controller-runtime"
3739
"sigs.k8s.io/controller-runtime/pkg/client"
3840
)
3941

42+
const (
43+
levelDebug = 4
44+
)
45+
4046
// CertificateRequestReconciler reconciles a StepIssuer object.
4147
type CertificateRequestReconciler struct {
4248
client.Client
4349
Log logr.Logger
4450
Recorder record.EventRecorder
4551
KMSCA *kmsca.KMSCA
52+
53+
Clock clock.Clock
54+
CheckApprovedCondition bool
4655
}
4756

4857
// Annotation for generating RBAC role for writing Events
@@ -54,8 +63,7 @@ type CertificateRequestReconciler struct {
5463
// Reconcile will read and validate a KMSIssuer resource associated to the
5564
// CertificateRequest resource, and it will sign the CertificateRequest with the
5665
// provisioner in the KMSIssuer.
57-
func (r *CertificateRequestReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
58-
ctx := context.Background()
66+
func (r *CertificateRequestReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
5967
log := r.Log.WithValues("certificaterequests", req.NamespacedName)
6068

6169
// Fetch the CertificateRequest resource being reconciled.
@@ -77,6 +85,11 @@ func (r *CertificateRequestReconciler) Reconcile(req ctrl.Request) (ctrl.Result,
7785
return ctrl.Result{}, nil
7886
}
7987

88+
shouldProcess, err := r.requestShouldBeProcessed(ctx, log, cr)
89+
if err != nil || !shouldProcess {
90+
return ctrl.Result{}, err
91+
}
92+
8093
// If the certificate data is already set then we skip this request as it
8194
// has already been completed in the past.
8295
if len(cr.Status.Certificate) > 0 {
@@ -96,15 +109,15 @@ func (r *CertificateRequestReconciler) Reconcile(req ctrl.Request) (ctrl.Result,
96109
Namespace: req.Namespace,
97110
Name: cr.Spec.IssuerRef.Name,
98111
}
99-
if err := r.Client.Get(ctx, issNamespaceName, &issuer); err != nil {
112+
if err = r.Client.Get(ctx, issNamespaceName, &issuer); err != nil {
100113
log.Error(err, "failed to retrieve KMSIssuer resource", "namespace", req.Namespace, "name", cr.Spec.IssuerRef.Name)
101114
_ = r.setStatus(ctx, cr, cmmeta.ConditionFalse, cmapi.CertificateRequestReasonPending, "Failed to retrieve KMSIssuer resource %s: %v", issNamespaceName, err)
102115
return ctrl.Result{}, err
103116
}
104117

105118
// Check if the KMSIssuer resource has been marked Ready
106119
if !issuer.Status.IsReady() {
107-
err := fmt.Errorf("resource %s is not ready", issNamespaceName)
120+
err = fmt.Errorf("resource %s is not ready", issNamespaceName)
108121
log.Error(err, "failed to retrieve StepIssuer resource", "namespace", req.Namespace, "name", cr.Spec.IssuerRef.Name)
109122
_ = r.setStatus(ctx, cr, cmmeta.ConditionFalse, cmapi.CertificateRequestReasonPending, "StepIssuer resource %s is not Ready", issNamespaceName)
110123
return ctrl.Result{}, err
@@ -161,6 +174,64 @@ func (r *CertificateRequestReconciler) SetupWithManager(mgr ctrl.Manager) error
161174
// return false
162175
// }
163176

177+
// requestShouldBeProcessed will return false if the conditions on the request
178+
// mean that it should not be processed. If the request has been denied, it
179+
// will set the request failure time and add a Ready=False condition.
180+
func (r *CertificateRequestReconciler) requestShouldBeProcessed(ctx context.Context, log logr.Logger, cr *cmapi.CertificateRequest) (bool, error) {
181+
dbg := log.V(levelDebug)
182+
183+
// Ignore CertificateRequest if it is already Ready
184+
if apiutil.CertificateRequestHasCondition(cr, cmapi.CertificateRequestCondition{
185+
Type: cmapi.CertificateRequestConditionReady,
186+
Status: cmmeta.ConditionTrue,
187+
}) {
188+
dbg.Info("CertificateRequest is Ready. Ignoring.")
189+
return false, nil
190+
}
191+
// Ignore CertificateRequest if it is already Failed
192+
if apiutil.CertificateRequestHasCondition(cr, cmapi.CertificateRequestCondition{
193+
Type: cmapi.CertificateRequestConditionReady,
194+
Status: cmmeta.ConditionFalse,
195+
Reason: cmapi.CertificateRequestReasonFailed,
196+
}) {
197+
dbg.Info("CertificateRequest is Failed. Ignoring.")
198+
return false, nil
199+
}
200+
// Ignore CertificateRequest if it already has a Denied Ready Reason
201+
if apiutil.CertificateRequestHasCondition(cr, cmapi.CertificateRequestCondition{
202+
Type: cmapi.CertificateRequestConditionReady,
203+
Status: cmmeta.ConditionFalse,
204+
Reason: cmapi.CertificateRequestReasonDenied,
205+
}) {
206+
dbg.Info("CertificateRequest already has a Ready condition with Denied Reason. Ignoring.")
207+
return false, nil
208+
}
209+
210+
// If CertificateRequest has been denied, mark the CertificateRequest as
211+
// Ready=Denied and set FailureTime if not already.
212+
if apiutil.CertificateRequestIsDenied(cr) {
213+
dbg.Info("CertificateRequest has been denied. Marking as failed.")
214+
215+
if cr.Status.FailureTime == nil {
216+
nowTime := metav1.NewTime(r.Clock.Now())
217+
cr.Status.FailureTime = &nowTime
218+
}
219+
220+
message := "The CertificateRequest was denied by an approval controller"
221+
return false, r.setStatus(ctx, cr, cmmeta.ConditionFalse, cmapi.CertificateRequestReasonDenied, message)
222+
}
223+
224+
if r.CheckApprovedCondition {
225+
// If CertificateRequest has not been approved, exit early.
226+
if !apiutil.CertificateRequestIsApproved(cr) {
227+
dbg.Info("certificate request has not been approved")
228+
return false, nil
229+
}
230+
}
231+
232+
return true, nil
233+
}
234+
164235
func (r *CertificateRequestReconciler) setStatus(ctx context.Context, cr *cmapi.CertificateRequest, status cmmeta.ConditionStatus, reason, message string, args ...interface{}) error {
165236
completeMessage := fmt.Sprintf(message, args...)
166237
apiutil.SetCertificateRequestCondition(cr, cmapi.CertificateRequestConditionReady, status, reason, completeMessage)

0 commit comments

Comments
 (0)