@@ -12,6 +12,7 @@ import (
12
12
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13
13
"k8s.io/apimachinery/pkg/util/sets"
14
14
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
15
+ "k8s.io/client-go/tools/cache"
15
16
16
17
"github.com/openshift/cluster-api/pkg/apis/machine/v1beta1"
17
18
)
@@ -21,13 +22,16 @@ const (
21
22
nodeGroup = "system:nodes"
22
23
nodeUserPrefix = nodeUser + ":"
23
24
25
+ maxPendingDelta = time .Hour
26
+ maxPendingCSRs = 100
27
+
24
28
nodeBootstrapperUsername = "system:serviceaccount:openshift-machine-config-operator:node-bootstrapper"
25
29
26
- maxMachineDelta = 10 * time .Minute
30
+ maxMachineClockSkew = 10 * time .Second
31
+ maxMachineDelta = 10 * time .Minute
27
32
28
- machineRoleKey = "machine.openshift.io/cluster-api-machine-role"
29
- machineTypeKey = "machine.openshift.io/cluster-api-machine-type"
30
- machineRoleTypeValue = "worker"
33
+ machineCSRAutoApproveKey = "machine.openshift.io/csr-auto-approver"
34
+ machineCSRAutoApproveValue = "cluster-machine-approver"
31
35
)
32
36
33
37
var nodeBootstrapperGroups = sets .NewString (
@@ -103,9 +107,21 @@ func validateCSRContents(req *certificatesv1beta1.CertificateSigningRequest, csr
103
107
return nodeAsking , nil
104
108
}
105
109
106
- // authorizeCSR authorizes the CertificateSigningRequest req for a node's server certificate.
107
- // csr should be the parsed CSR from req.Spec.Request. Names contained in the CSR are checked against addresses in the
108
- // corresponding node's machine status.
110
+ // authorizeCSR authorizes the CertificateSigningRequest req for a node's client or server certificate.
111
+ // csr should be the parsed CSR from req.Spec.Request.
112
+ //
113
+ // For client certificates:
114
+ // The only information contained in the CSR is the future name of the node. Thus we perform a best effort check:
115
+ //
116
+ // 1. User is the node bootstrapper
117
+ // 2. Node does not exist
118
+ // 3. Use machine API internal DNS to locate matching machine based on node name
119
+ // 4. Machine must not have a node ref
120
+ // 5. Machine must opt-in to CSR auto approve via label
121
+ // 6. CSR creation timestamp is very close to machine creation timestamp
122
+ //
123
+ // For server certificates:
124
+ // Names contained in the CSR are checked against addresses in the corresponding node's machine status.
109
125
func authorizeCSR (machines []v1beta1.Machine , nodes corev1client.NodeInterface , req * certificatesv1beta1.CertificateSigningRequest , csr * x509.CertificateRequest ) error {
110
126
if len (machines ) == 0 || req == nil || csr == nil {
111
127
return fmt .Errorf ("Invalid request" )
@@ -210,11 +226,11 @@ func authorizeNodeClientCSR(machines []v1beta1.Machine, nodes corev1client.NodeI
210
226
return fmt .Errorf ("machine for node %s already has node ref" , nodeName )
211
227
}
212
228
213
- if ! isWorkerMachine (nodeMachine ) {
214
- return fmt .Errorf ("machine for node %s is not a worker " , nodeName )
229
+ if ! machineWantsCSRAutoApprove (nodeMachine ) {
230
+ return fmt .Errorf ("machine for node %s is not configured for CSR auto approval " , nodeName )
215
231
}
216
232
217
- start := nodeMachine .CreationTimestamp .Add (- maxMachineDelta ) // TODO maybe this should be really small to account only for clock drift
233
+ start := nodeMachine .CreationTimestamp .Add (- maxMachineClockSkew )
218
234
end := nodeMachine .CreationTimestamp .Add (maxMachineDelta )
219
235
if ! inTimeSpan (start , end , req .CreationTimestamp .Time ) {
220
236
return fmt .Errorf ("CSR %s creation time %s not in range (%s, %s)" , req .Name , req .CreationTimestamp .Time , start , end )
@@ -247,10 +263,43 @@ func findMatchingMachineFromInternalDNS(nodeName string, machines []v1beta1.Mach
247
263
return v1beta1.Machine {}, false
248
264
}
249
265
250
- func isWorkerMachine (machine v1beta1.Machine ) bool {
251
- return machine .Labels [machineRoleKey ] == machineRoleTypeValue && machine . Labels [ machineTypeKey ] == machineRoleTypeValue
266
+ func machineWantsCSRAutoApprove (machine v1beta1.Machine ) bool {
267
+ return machine .Labels [machineCSRAutoApproveKey ] == machineCSRAutoApproveValue
252
268
}
253
269
254
270
func inTimeSpan (start , end , check time.Time ) bool {
255
271
return check .After (start ) && check .Before (end )
256
272
}
273
+
274
+ func isApproved (csr * certificatesv1beta1.CertificateSigningRequest ) bool {
275
+ for _ , condition := range csr .Status .Conditions {
276
+ if condition .Type == certificatesv1beta1 .CertificateApproved {
277
+ return true
278
+ }
279
+ }
280
+ return false
281
+ }
282
+
283
+ func recentlyPendingCSRs (indexer cache.Indexer ) int {
284
+ // assumes we are scheduled on the master meaning our clock is the same
285
+ now := time .Now ()
286
+ start := now .Add (- maxPendingDelta )
287
+ end := now .Add (maxMachineClockSkew )
288
+
289
+ var pending int
290
+
291
+ for _ , item := range indexer .List () {
292
+ csr := item .(* certificatesv1beta1.CertificateSigningRequest )
293
+
294
+ // ignore "old" CSRs
295
+ if ! inTimeSpan (start , end , csr .CreationTimestamp .Time ) {
296
+ continue
297
+ }
298
+
299
+ if ! isApproved (csr ) {
300
+ pending ++
301
+ }
302
+ }
303
+
304
+ return pending
305
+ }
0 commit comments