Skip to content

Commit c33b328

Browse files
hjoshi123inteonThatsMrTalbot
authored
feat: Adding wrapped issuers (#332)
* adding wrapped issuer Signed-off-by: hjoshi123 <[email protected]> * run 'make go-tidy' Signed-off-by: Tim Ramlot <[email protected]> * removing ignore issuers Signed-off-by: hjoshi123 <[email protected]> * fix: rename upstream example package to avoid workspace conflicts Signed-off-by: Adam Talbot <[email protected]> * feat: refactor example to use intree package Signed-off-by: Adam Talbot <[email protected]> * fix: go-tidy Signed-off-by: Adam Talbot <[email protected]> --------- Signed-off-by: hjoshi123 <[email protected]> Signed-off-by: Tim Ramlot <[email protected]> Signed-off-by: Adam Talbot <[email protected]> Co-authored-by: Tim Ramlot <[email protected]> Co-authored-by: Adam Talbot <[email protected]>
1 parent 1d96f63 commit c33b328

23 files changed

+1382
-25
lines changed

api/v1alpha1/issuer_interface.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package v1alpha1
2222
import (
2323
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2424
"k8s.io/apimachinery/pkg/runtime"
25+
"sigs.k8s.io/controller-runtime/pkg/client"
2526
)
2627

2728
type Issuer interface {
@@ -40,3 +41,9 @@ type Issuer interface {
4041
// with an issuerName set to eg. "simpleclusterissuers.issuer.cert-manager.io/issuer1".
4142
GetIssuerTypeIdentifier() string
4243
}
44+
45+
// WrappedIssuer is an issuer type to support objects which are not directly of the type Issuer. This could include cert-manager issuers for example.
46+
type WrappedIssuer interface {
47+
Issuer
48+
Unwrap() client.Object
49+
}

controllers/combined_controller.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func (r *CombinedController) SetupWithManager(ctx context.Context, mgr ctrl.Mana
118118
PreSetupWithManager: r.PreSetupWithManager,
119119
PostSetupWithManager: r.PostSetupWithManager,
120120
}).SetupWithManager(ctx, mgr); err != nil {
121-
return fmt.Errorf("%T: %w", issuerType, err)
121+
return fmt.Errorf("%T: %w", kubeutil.ObjectForIssuer(issuerType), err)
122122
}
123123
}
124124

controllers/issuer_controller.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func (r *IssuerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (res
9494

9595
logger.V(2).Info("Got StatusPatch result", "result", result, "patch", issuerStatusPatch, "error", reconcileError)
9696
if issuerStatusPatch != nil {
97-
cr, patch, err := ssaclient.GenerateIssuerStatusPatch(r.ForObject, req.Name, req.Namespace, issuerStatusPatch)
97+
cr, patch, err := ssaclient.GenerateIssuerStatusPatch(kubeutil.ObjectForIssuer(r.ForObject), req.Name, req.Namespace, issuerStatusPatch)
9898
if err != nil {
9999
return ctrl.Result{}, utilerrors.NewAggregate([]error{err, reconcileError})
100100
}
@@ -135,7 +135,7 @@ func (r *IssuerReconciler) reconcileStatusPatch(
135135
// calling IsInvalidated early to make sure the map is always cleared
136136
reportedError := r.EventSource.HasReportedError(forObjectGvk, req.NamespacedName)
137137

138-
if err := r.Client.Get(ctx, req.NamespacedName, issuer); err != nil && apierrors.IsNotFound(err) {
138+
if err := r.Client.Get(ctx, req.NamespacedName, kubeutil.ObjectForIssuer(issuer)); err != nil && apierrors.IsNotFound(err) {
139139
logger.V(1).Info("Issuer not found. Ignoring.")
140140
return result, nil, nil // done
141141
} else if err != nil {
@@ -154,17 +154,6 @@ func (r *IssuerReconciler) reconcileStatusPatch(
154154
return result, nil, nil // done
155155
}
156156

157-
if r.IgnoreIssuer != nil {
158-
ignore, err := r.IgnoreIssuer(ctx, issuer)
159-
if err != nil {
160-
return result, nil, fmt.Errorf("failed to check if issuer should be ignored: %v", err) // requeue with backoff
161-
}
162-
if ignore {
163-
logger.V(1).Info("IgnoreIssuer() returned true. Ignoring.")
164-
return result, nil, nil // done
165-
}
166-
}
167-
168157
// We now have a Issuer that belongs to us so we are responsible
169158
// for updating its Status.
170159
issuerStatusPatch = &v1alpha1.IssuerStatus{}
@@ -245,14 +234,14 @@ func (r *IssuerReconciler) reconcileStatusPatch(
245234

246235
// SetupWithManager sets up the controller with the Manager.
247236
func (r *IssuerReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
248-
if err := kubeutil.SetGroupVersionKind(mgr.GetScheme(), r.ForObject); err != nil {
237+
if err := kubeutil.SetGroupVersionKind(mgr.GetScheme(), kubeutil.ObjectForIssuer(r.ForObject)); err != nil {
249238
return err
250239
}
251240
forObjectGvk := r.ForObject.GetObjectKind().GroupVersionKind()
252241

253242
build := ctrl.NewControllerManagedBy(mgr).
254243
For(
255-
r.ForObject,
244+
kubeutil.ObjectForIssuer(r.ForObject),
256245
// we are only interested in changes to the .Spec part of the issuer
257246
// this also prevents us to get in fast reconcile loop when setting the
258247
// status to Pending causing the resource to update, while we only want

controllers/request_controller.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,10 @@ type RequestController struct {
9191
requestObjectHelperCreator RequestObjectHelperCreator
9292
}
9393

94-
type MatchIssuerType func(client.Object) (v1alpha1.Issuer, client.ObjectKey, error)
95-
type RequestObjectHelperCreator func(client.Object) RequestObjectHelper
94+
type (
95+
MatchIssuerType func(client.Object) (v1alpha1.Issuer, client.ObjectKey, error)
96+
RequestObjectHelperCreator func(client.Object) RequestObjectHelper
97+
)
9698

9799
type IssuerType struct {
98100
Type v1alpha1.Issuer
@@ -232,7 +234,7 @@ func (r *RequestController) reconcileStatusPatch(
232234
return result, statusPatch, nil // apply patch, done
233235
}
234236

235-
if err := r.Client.Get(ctx, issuerName, issuerObject); err != nil && apierrors.IsNotFound(err) {
237+
if err := r.Client.Get(ctx, issuerName, kubeutil.ObjectForIssuer(issuerObject)); err != nil && apierrors.IsNotFound(err) {
236238
logger.V(1).Info("Issuer not found. Waiting for it to be created")
237239
statusPatch.SetWaitingForIssuerExist(err)
238240

@@ -360,7 +362,6 @@ func (r *RequestController) setAllIssuerTypesWithGroupVersionKind(scheme *runtim
360362
Type: issuer,
361363
IsNamespaced: true,
362364
})
363-
364365
}
365366
for _, issuer := range r.ClusterIssuerTypes {
366367
issuers = append(issuers, IssuerType{
@@ -370,7 +371,7 @@ func (r *RequestController) setAllIssuerTypesWithGroupVersionKind(scheme *runtim
370371
}
371372

372373
for _, issuer := range issuers {
373-
if err := kubeutil.SetGroupVersionKind(scheme, issuer.Type); err != nil {
374+
if err := kubeutil.SetGroupVersionKind(scheme, kubeutil.ObjectForIssuer(issuer.Type)); err != nil {
374375
return err
375376
}
376377
}
@@ -474,7 +475,7 @@ func (r *RequestController) SetupWithManager(
474475
}
475476

476477
build = build.Watches(
477-
issuerType.Type,
478+
kubeutil.ObjectForIssuer(issuerType.Type),
478479
resourceHandler,
479480
builder.WithPredicates(
480481
predicate.ResourceVersionChangedPredicate{},

docs/api.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Package v1alpha1 contains API Schema definitions for the v1alpha1 API group \+ku
1515
- [type IssuerStatus](<#IssuerStatus>)
1616
- [func \(in \*IssuerStatus\) DeepCopy\(\) \*IssuerStatus](<#IssuerStatus.DeepCopy>)
1717
- [func \(in \*IssuerStatus\) DeepCopyInto\(out \*IssuerStatus\)](<#IssuerStatus.DeepCopyInto>)
18+
- [type WrappedIssuer](<#WrappedIssuer>)
1819

1920

2021
## Constants
@@ -60,7 +61,7 @@ const (
6061
```
6162

6263
<a name="Issuer"></a>
63-
## type [Issuer](<https://github.com/cert-manager/issuer-lib/blob/main/api/v1alpha1/issuer_interface.go#L27-L42>)
64+
## type [Issuer](<https://github.com/cert-manager/issuer-lib/blob/main/api/v1alpha1/issuer_interface.go#L28-L43>)
6465

6566

6667

@@ -117,4 +118,16 @@ func (in *IssuerStatus) DeepCopyInto(out *IssuerStatus)
117118

118119
DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non\-nil.
119120

121+
<a name="WrappedIssuer"></a>
122+
## type [WrappedIssuer](<https://github.com/cert-manager/issuer-lib/blob/main/api/v1alpha1/issuer_interface.go#L46-L49>)
123+
124+
WrappedIssuer is an issuer type to support objects which are not directly of the type Issuer. This could include cert\-manager issuers for example.
125+
126+
```go
127+
type WrappedIssuer interface {
128+
Issuer
129+
Unwrap() client.Object
130+
}
131+
```
132+
120133
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
Copyright 2023 The cert-manager Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controller
18+
19+
import (
20+
"context"
21+
"crypto/ecdsa"
22+
"crypto/elliptic"
23+
"crypto/rand"
24+
"crypto/x509"
25+
"crypto/x509/pkix"
26+
"encoding/pem"
27+
"fmt"
28+
"math/big"
29+
"time"
30+
31+
ctrl "sigs.k8s.io/controller-runtime"
32+
"sigs.k8s.io/controller-runtime/pkg/client"
33+
34+
"github.com/cert-manager/issuer-lib/api/v1alpha1"
35+
"github.com/cert-manager/issuer-lib/controllers"
36+
"github.com/cert-manager/issuer-lib/controllers/signer"
37+
"github.com/cert-manager/issuer-lib/intree"
38+
)
39+
40+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificaterequests,verbs=get;list;watch
41+
// +kubebuilder:rbac:groups=cert-manager.io,resources=certificaterequests/status,verbs=patch
42+
43+
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests,verbs=get;list;watch
44+
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=certificatesigningrequests/status,verbs=patch
45+
// +kubebuilder:rbac:groups=certificates.k8s.io,resources=signers,verbs=sign,resourceNames=issuers.cert-manager.io/*;clusterissuers.cert-manager.io/*
46+
47+
// +kubebuilder:rbac:groups=cert-manager.io,resources=issuers;clusterissuers,verbs=get;list;watch
48+
// +kubebuilder:rbac:groups=cert-manager.io,resources=issuers/status;clusterissuers/status,verbs=patch
49+
50+
// +kubebuilder:rbac:groups=core,resources=events,verbs=create;patch
51+
52+
type Signer struct {
53+
client client.Client
54+
}
55+
56+
func (s *Signer) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error {
57+
s.client = mgr.GetClient()
58+
59+
return (&controllers.CombinedController{
60+
IssuerTypes: intree.Issuers,
61+
ClusterIssuerTypes: intree.ClusterIssuers,
62+
63+
FieldOwner: "selfsigned.cert-manager.io",
64+
MaxRetryDuration: 1 * time.Minute,
65+
66+
// Ignore non self-signing certificate requests
67+
IgnoreIssuer: s.IgnoreIssuer,
68+
69+
Sign: s.Sign,
70+
Check: s.Check,
71+
EventRecorder: mgr.GetEventRecorderFor("selfsigned.cert-manager.io"),
72+
}).SetupWithManager(ctx, mgr)
73+
}
74+
75+
func (Signer) IgnoreIssuer(ctx context.Context, issuerObject v1alpha1.Issuer) (bool, error) {
76+
iss, ok := issuerObject.(intree.CMGenericIssuer)
77+
if !ok {
78+
return false, fmt.Errorf("issuer does not implement CMGenericIssuer")
79+
}
80+
81+
return iss.IssuerSpec().SelfSigned == nil, nil
82+
}
83+
84+
func (Signer) Check(ctx context.Context, issuerObject v1alpha1.Issuer) error {
85+
return nil
86+
}
87+
88+
func (Signer) Sign(ctx context.Context, cr signer.CertificateRequestObject, issuerObject v1alpha1.Issuer) (signer.PEMBundle, error) {
89+
// generate random ca private key
90+
caPrivateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
91+
if err != nil {
92+
return signer.PEMBundle{}, err
93+
}
94+
95+
caCRT := &x509.Certificate{
96+
SerialNumber: big.NewInt(1),
97+
Subject: pkix.Name{
98+
Organization: []string{"Acme Co"},
99+
},
100+
NotBefore: time.Now(),
101+
NotAfter: time.Now().Add(time.Hour * 24 * 180),
102+
103+
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
104+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
105+
BasicConstraintsValid: true,
106+
}
107+
108+
// load client certificate request
109+
certDetails, err := cr.GetCertificateDetails()
110+
if err != nil {
111+
return signer.PEMBundle{}, err
112+
}
113+
114+
clientCRTTemplate, err := certDetails.CertificateTemplate()
115+
if err != nil {
116+
return signer.PEMBundle{}, err
117+
}
118+
119+
// create client certificate from template and CA public key
120+
clientCRTRaw, err := x509.CreateCertificate(rand.Reader, clientCRTTemplate, caCRT, clientCRTTemplate.PublicKey, caPrivateKey)
121+
if err != nil {
122+
panic(err)
123+
}
124+
125+
clientCrt := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: clientCRTRaw})
126+
return signer.PEMBundle{
127+
ChainPEM: clientCrt,
128+
}, nil
129+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
---
2+
apiVersion: rbac.authorization.k8s.io/v1
3+
kind: ClusterRole
4+
metadata:
5+
name: issuer-controller-role
6+
rules:
7+
- apiGroups:
8+
- cert-manager.io
9+
resources:
10+
- certificaterequests
11+
verbs:
12+
- get
13+
- list
14+
- watch
15+
- apiGroups:
16+
- cert-manager.io
17+
resources:
18+
- certificaterequests/status
19+
verbs:
20+
- patch
21+
- apiGroups:
22+
- certificates.k8s.io
23+
resources:
24+
- certificatesigningrequests
25+
verbs:
26+
- get
27+
- list
28+
- watch
29+
- apiGroups:
30+
- certificates.k8s.io
31+
resources:
32+
- certificatesigningrequests/status
33+
verbs:
34+
- patch
35+
- apiGroups:
36+
- certificates.k8s.io
37+
resourceNames:
38+
- clusterissuers.cert-manager.io/*
39+
- issuers.cert-manager.io/*
40+
resources:
41+
- signers
42+
verbs:
43+
- sign
44+
- apiGroups:
45+
- ""
46+
resources:
47+
- events
48+
verbs:
49+
- create
50+
- patch
51+
- apiGroups:
52+
- cert-manager.io
53+
resources:
54+
- clusterissuers
55+
- issuers
56+
verbs:
57+
- get
58+
- list
59+
- watch
60+
- apiGroups:
61+
- cert-manager.io
62+
resources:
63+
- clusterissuers/status
64+
- issuers/status
65+
verbs:
66+
- patch
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
labels:
5+
control-plane: controller-manager
6+
name: system
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: v1
2+
kind: ServiceAccount
3+
metadata:
4+
name: controller-manager
5+
namespace: system
6+
---
7+
apiVersion: rbac.authorization.k8s.io/v1
8+
kind: ClusterRoleBinding
9+
metadata:
10+
name: controller-manager-rolebinding
11+
roleRef:
12+
apiGroup: rbac.authorization.k8s.io
13+
kind: ClusterRole
14+
name: issuer-controller-role
15+
subjects:
16+
- kind: ServiceAccount
17+
name: controller-manager
18+
namespace: system

0 commit comments

Comments
 (0)