blob: 25987985d65556b3d03ea7650fae3d5d8747e00d [file] [log] [blame]
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +02001# Deploy a per-cluster cert-manager
2
3local kube = import "../../../kube/kube.libsonnet";
4
5{
6 local cm = self,
7 Environment: {
8 local env = self,
9 local cfg = env.cfg,
10
11 cfg:: {
12 namespace: "cert-manager",
Piotr Dobrowolski2afe6042019-04-02 14:43:34 +020013 enableWebhook: false,
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +020014 },
15
16 metadata:: {
17 namespace: cfg.namespace,
18 },
19
20 namespace: kube.Namespace(cfg.namespace) {
21 metadata+: {
22 labels: { "certmanager.k8s.io/disable-validation": "true" },
23 },
24 },
25
26 crds: {
27 certificates: kube.CustomResourceDefinition("certmanager.k8s.io", "v1alpha1", "Certificate") {
28 spec+: {
29 additionalPrinterColumns: [
30 { name: "Ready", type: "string", JSONPath: ".status.conditions[?(@.type==\"Ready\")].status" },
31 { name: "Secret", type: "string", JSONPath: ".spec.secretName" },
32 { name: "Issuer", type: "string", JSONPath: ".spec.issuerRef.name", priority: 1 },
33 { name: "Status", type: "string", JSONPath: ".status.conditions[?(@.type==\"Ready\")].message", priority: 1 },
34 { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
35 ],
36 names+: {
37 shortNames+: ["cert", "certs"],
38 },
39 scope: "Namespaced",
40 validation: {
41 # Converted from official YAML
42 "openAPIV3Schema": {
43 "properties": {
44 "apiVersion": {
45 "description": "APIVersion defines the versioned schema of this representation\nof an object. Servers should convert recognized schemas to the latest\ninternal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
46 "type": "string"
47 },
48 "kind": {
49 "description": "Kind is a string value representing the REST resource this\nobject represents. Servers may infer this from the endpoint the client\nsubmits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
50 "type": "string"
51 },
52 "metadata": {
53 "type": "object"
54 },
55 "spec": {
56 "properties": {
57 "acme": {
58 "description": "ACME contains configuration specific to ACME Certificates.\nNotably, this contains details on how the domain names listed on this\nCertificate resource should be 'solved', i.e. mapping HTTP01 and DNS01\nproviders to DNS names.",
59 "properties": {
60 "config": {
61 "items": {
62 "properties": {
63 "domains": {
64 "description": "Domains is the list of domains that this SolverConfig\napplies to.",
65 "items": {
66 "type": "string"
67 },
68 "type": "array"
69 }
70 },
71 "required": [
72 "domains"
73 ],
74 "type": "object"
75 },
76 "type": "array"
77 }
78 },
79 "required": [
80 "config"
81 ],
82 "type": "object"
83 },
84 "commonName": {
85 "description": "CommonName is a common name to be used on the Certificate",
86 "type": "string"
87 },
88 "dnsNames": {
89 "description": "DNSNames is a list of subject alt names to be used on the\nCertificate",
90 "items": {
91 "type": "string"
92 },
93 "type": "array"
94 },
95 "duration": {
96 "description": "Certificate default Duration",
97 "type": "string"
98 },
99 "ipAddresses": {
100 "description": "IPAddresses is a list of IP addresses to be used on the\nCertificate",
101 "items": {
102 "type": "string"
103 },
104 "type": "array"
105 },
106 "isCA": {
107 "description": "IsCA will mark this Certificate as valid for signing. This\nimplies that the 'signing' usage is set",
108 "type": "boolean"
109 },
110 "issuerRef": {
111 "description": "IssuerRef is a reference to the issuer for this certificate.\nIf the 'kind' field is not set, or set to 'Issuer', an Issuer resource\nwith the given name in the same namespace as the Certificate will\nbe used. If the 'kind' field is set to 'ClusterIssuer', a ClusterIssuer\nwith the provided name will be used. The 'name' field in this stanza\nis required at all times.",
112 "properties": {
113 "kind": {
114 "type": "string"
115 },
116 "name": {
117 "type": "string"
118 }
119 },
120 "required": [
121 "name"
122 ],
123 "type": "object"
124 },
125 "keyAlgorithm": {
126 "description": "KeyAlgorithm is the private key algorithm of the corresponding\nprivate key for this certificate. If provided, allowed values are\neither \"rsa\" or \"ecdsa\" If KeyAlgorithm is specified and KeySize is\nnot provided, key size of 256 will be used for \"ecdsa\" key algorithm\nand key size of 2048 will be used for \"rsa\" key algorithm.",
127 "enum": [
128 "rsa",
129 "ecdsa"
130 ],
131 "type": "string"
132 },
133 "keySize": {
134 "description": "KeySize is the key bit size of the corresponding private\nkey for this certificate. If provided, value must be between 2048\nand 8192 inclusive when KeyAlgorithm is empty or is set to \"rsa\",\nand value must be one of (256, 384, 521) when KeyAlgorithm is set\nto \"ecdsa\".",
135 "format": "int64",
136 "type": "integer"
137 },
138 "organization": {
139 "description": "Organization is the organization to be used on the Certificate",
140 "items": {
141 "type": "string"
142 },
143 "type": "array"
144 },
145 "renewBefore": {
146 "description": "Certificate renew before expiration duration",
147 "type": "string"
148 },
149 "secretName": {
150 "description": "SecretName is the name of the secret resource to store\nthis secret in",
151 "type": "string"
152 }
153 },
154 "required": [
155 "secretName",
156 "issuerRef"
157 ],
158 "type": "object"
159 },
160 "status": {
161 "properties": {
162 "conditions": {
163 "items": {
164 "properties": {
165 "lastTransitionTime": {
166 "description": "LastTransitionTime is the timestamp corresponding\nto the last status change of this condition.",
167 "format": "date-time",
168 "type": "string"
169 },
170 "message": {
171 "description": "Message is a human readable description of the details\nof the last transition, complementing reason.",
172 "type": "string"
173 },
174 "reason": {
175 "description": "Reason is a brief machine readable explanation for\nthe condition's last transition.",
176 "type": "string"
177 },
178 "status": {
179 "description": "Status of the condition, one of ('True', 'False',\n'Unknown').",
180 "enum": [
181 "True",
182 "False",
183 "Unknown"
184 ],
185 "type": "string"
186 },
187 "type": {
188 "description": "Type of the condition, currently ('Ready').",
189 "type": "string"
190 }
191 },
192 "required": [
193 "type",
194 "status",
195 "lastTransitionTime",
196 "reason",
197 "message"
198 ],
199 "type": "object"
200 },
201 "type": "array"
202 },
203 "lastFailureTime": {
204 "format": "date-time",
205 "type": "string"
206 },
207 "notAfter": {
208 "description": "The expiration time of the certificate stored in the secret\nnamed by this resource in spec.secretName.",
209 "format": "date-time",
210 "type": "string"
211 }
212 },
213 "type": "object"
214 }
215 }
216 }
217 }
218 },
219 },
220 challenges: kube.CustomResourceDefinition("certmanager.k8s.io", "v1alpha1", "Challenge") {
221 spec+: {
222 additionalPrinterColumns: [
223 { name: "State", type: "string", JSONPath: ".status.state" },
224 { name: "Domain", type: "string", JSONPath: ".spec.dnsName" },
225 { name: "Reason", type: "string", JSONPath: ".status.reason", priority: 1 },
226 { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
227 ],
228 validation: {
229 # ...
230 },
231 },
232 },
233 clusterissuers: kube.CustomResourceDefinition("certmanager.k8s.io", "v1alpha1", "ClusterIssuer") {
234 spec+: {
235 validation: {
236 # ...
237 },
238 scope: "Cluster",
239 },
240 },
241 issuers: kube.CustomResourceDefinition("certmanager.k8s.io", "v1alpha1", "Issuer") {
242 spec+: {
243 validation: {
244 # ...
245 },
246 scope: "Namespaced",
247 },
248 },
249 orders: kube.CustomResourceDefinition("certmanager.k8s.io", "v1alpha1", "Order") {
250 spec+: {
251 additionalPrinterColumns: [
252 { name: "State", type: "string", JSONPath: ".status.state" },
253 { name: "Issuer", type: "string", JSONPath: ".spec.issuerRef.name", priority: 1 },
254 { name: "Reason", type: "string", JSONPath: ".status.reason", priority: 1 },
255 { name: "Age", type: "date", JSONPath: ".metadata.creationTimestamp" },
256 ],
257 validation: {
258 # ...
259 },
260 scope: "Namespaced",
261 },
262 },
263 },
264
265 sas: {
266 cainjector: kube.ServiceAccount("cert-manager-cainjector") {
267 metadata+: env.metadata,
268 },
269 webhook: kube.ServiceAccount("cert-manager-webhook") {
270 metadata+: env.metadata,
271 },
272 certmanager: kube.ServiceAccount("cert-manager") {
273 metadata+: env.metadata,
274 },
275 },
276
277 crs: {
278 cainjector: kube.ClusterRole("cert-manager-cainjector") {
279 rules: [
280 {
281 apiGroups: ["certmanager.k8s.io"],
282 resources: ["certificates"],
283 verbs: ["get", "list", "watch"],
284 },
285 {
286 apiGroups: [""],
287 resources: ["secrets"],
288 verbs: ["get", "list", "watch"],
289 },
290 {
291 apiGroups: [""],
292 resources: ["configmaps", "events"],
293 verbs: ["*"],
294 },
295
296 {
297 apiGroups: ["admissionregistration.k8s.io"],
298 resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"],
299 verbs: ["*"],
300 },
301 {
302 apiGroups: ["apiregistration.k8s.io"],
303 resources: ["apiservices"],
304 verbs: ["*"],
305 },
306 ],
307 },
308 certmanager: kube.ClusterRole("cert-manager") {
309 rules: [
310 {
311 apiGroups: ["certmanager.k8s.io"],
312 resources: ["certificates", "certificates/finalizers", "issuers", "clusterissuers", "orders", "orders/finalizers", "challenges"],
313 verbs: ["*"],
314 },
315 {
316 apiGroups: [""],
317 resources: ["configmaps", "secrets", "events", "services", "pods"],
318 verbs: ["*"],
319 },
320 {
321 apiGroups: ["extensions"],
322 resources: ["ingresses"],
323 verbs: ["*"],
324 },
325 ],
326 },
327 certmanagerView: kube.ClusterRole("cert-manager-view") {
328 rules: [
329 {
330 apiGroups: ["certmanager.k8s.io"],
331 resources: ["certificates", "issuers"],
332 verbs: ["get", "list", "watch"],
333 },
334 ],
335 },
336 certmanagerEdit: kube.ClusterRole("cert-manager-edit") {
337 rules: [
338 {
339 apiGroups: ["certmanager.k8s.io"],
340 resources: ["certificates", "issuers"],
341 verbs: ["create", "delete", "deletecollection", "patch", "update"],
342 },
343 ],
344 },
345 webhookRequester: kube.ClusterRole("cert-manager-webhook:webhook-requester") {
346 rules: [
347 {
348 apiGroups: ["admission.certmanager.k8s.io"],
349 resources: ["certificates", "issuers", "clusterissuers"],
350 verbs: ["create"],
351 },
352 ],
353 },
354 },
355 rbs: {
356 cainjector: kube.ClusterRoleBinding("cert-manager-cainjector") {
357 roleRef_: env.crs.cainjector,
358 subjects_: [ env.sas.cainjector ],
359 },
360 certmanager: kube.ClusterRoleBinding("cert-manager") {
361 roleRef_: env.crs.certmanager,
362 subjects_: [ env.sas.certmanager ],
363 },
364 webhookAuthDelegator: kube.ClusterRoleBinding("cert-manager-webhook:auth-delegator") {
365 roleRef_: {
366 kind: "ClusterRole",
367 metadata: { name: "system:auth-delegator" },
368 },
369 subjects_: [ env.sas.webhook ],
370 },
371 webhookAuthReader: kube.RoleBinding("cert-manager-webhook:webhook-authentication-reader") {
372 metadata+: {
373 namespace: "kube-system",
374 },
375 roleRef_: {
376 kind: "Role",
377 metadata: { name: "extension-apiserver-authentication-reader" },
378 },
379 subjects_: [ env.sas.webhook ],
380 },
381 },
382 deployments: {
383 cainjector: kube.Deployment("cert-manager-cainjector") {
384 metadata+: env.metadata,
385 spec+: {
386 replicas: 1,
387 template+: {
388 spec+: {
389 serviceAccountName: env.sas.cainjector.metadata.name,
390 containers_: {
391 cainjector: kube.Container("cainjector") {
392 image: "quay.io/jetstack/cert-manager-cainjector:v0.7.0",
393 args: [
394 "--leader-election-namespace=%s" % [cfg.namespace],
395 ],
396 env_: {
397 POD_NAMESPACE: kube.FieldRef("metadata.namespace"),
398 },
399 },
400 },
401 },
402 },
403 },
404 },
405 webhook: kube.Deployment("cert-manager-webhook") {
406 metadata+: env.metadata {
407 labels: {
408 app: "webhook",
409 },
410 },
411 spec+: {
412 replicas: 1,
413 template+: {
414 spec+: {
415 serviceAccountName: env.sas.webhook.metadata.name,
416 containers_: {
417 webhook: kube.Container("webhook") {
418 image: "quay.io/jetstack/cert-manager-webhook:v0.7.0",
419 args: [
420 "--v=12",
421 "--secure-port=6443",
422 "--tls-cert-file=/certs/tls.crt",
423 "--tls-private-key-file=/certs/tls.key",
424 ],
425 env_: {
426 POD_NAMESPACE: kube.FieldRef("metadata.namespace"),
427 },
428 ports_: { // changed
429 https: { containerPort: 6443 },
430 },
431 volumeMounts_: {
432 certs: { mountPath: "/certs" },
433 },
434 },
435 },
436 volumes_: {
437 certs: {
438 secret: { secretName: env.certificates.webhookTLS.spec.secretName },
439 },
440 // kube.SecretVolume(env.secrets.webhook_tls),
441 },
442 },
443 },
444 },
445 },
446 certmanager: kube.Deployment("cert-manager") {
447 metadata+: env.metadata,
448 spec+: {
449 replicas: 1,
450 template+: {
451 spec+: {
452 serviceAccountName: env.sas.certmanager.metadata.name,
453 containers_: {
454 webhook: kube.Container("cert-manager") {
455 image: "quay.io/jetstack/cert-manager-controller:v0.7.0",
456 args: [
457 "--cluster-resource-namespace=%s" % [cfg.namespace],
458 "--leader-election-namespace=%s" % [cfg.namespace],
459 ],
460 env_: {
461 POD_NAMESPACE: kube.FieldRef("metadata.namespace"),
462 },
463 ports_: {
464 metrics: { containerPort: 9402 },
465 },
466 resources: {
467 requests: {
468 cpu: "10m",
469 memory: "32Mi",
470 },
471 },
472 },
473 },
474 },
475 },
476 },
477 },
478 },
479 service: kube.Service("cert-manager-webhook") {
480 metadata+: env.metadata,
481 target_pod:: env.deployments.webhook.spec.template,
482 spec+: {
483 type: "ClusterIP",
484 ports: [
485 { name: "https", port: 443, targetPort: 6443, protocol: "TCP" },
486 ],
487 },
488 },
Piotr Dobrowolskifc514a92019-05-05 12:12:13 +0200489 apiservice: if cfg.enableWebhook then kube._Object("apiregistration.k8s.io/v1beta1", "APIService", "v1beta1.admission.certmanager.k8s.io") {
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200490 spec+: {
491 version: "v1beta1",
492 group: "admission.certmanager.k8s.io",
493 groupPriorityMinimum: 1000,
494 versionPriority: 15,
495 service: {
496 name: env.service.metadata.name,
497 namespace: cfg.namespace,
498 },
499 },
500 },
Piotr Dobrowolskifc514a92019-05-05 12:12:13 +0200501
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200502 issuers: {
503 webhookSelfsign: cm.Issuer("cert-manager-webhook-selfsign") {
504 metadata+: env.metadata,
505 spec: {
506 selfSigned: {},
507 },
508 },
509 webhookCA: cm.Issuer("cert-manager-webhook-ca") {
510 metadata+: env.metadata,
511 spec: {
512 ca: {
513 secretName: env.certificates.webhookCA.spec.secretName,
514 },
515 },
516 },
517 },
518 certificates: {
519 webhookCA: cm.Certificate("cert-manager-webhook-ca") {
520 metadata+: env.metadata,
521 spec: {
522 secretName: "cert-manager-webhook-ca",
Piotr Dobrowolski2afe6042019-04-02 14:43:34 +0200523 duration: "43800h0m0s", // 5 years
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200524 issuerRef: {
525 name: env.issuers.webhookSelfsign.metadata.name,
526 },
527 commonName: "ca.webhook.cert-manager",
528 isCA: true,
529 },
530 },
531 webhookTLS: cm.Certificate("cert-manager-webhook-webhook-tls") {
532 metadata+: env.metadata,
533 spec: {
534 secretName: "cert-manager-webhook-webhook-tls",
Piotr Dobrowolski2afe6042019-04-02 14:43:34 +0200535 duration: "8760h0m0s", // 1 year
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200536 issuerRef: {
537 name: env.issuers.webhookSelfsign.metadata.name,
538 },
539 dnsNames: [
540 "cert-manager-webhook",
541 "cert-manager-webhook.cert-manager",
542 "cert-manager-webhook.cert-manager.svc",
543 ],
544 },
545 },
546 },
Piotr Dobrowolskifc514a92019-05-05 12:12:13 +0200547 admission: if cfg.enableWebhook then kube._Object("admissionregistration.k8s.io/v1beta1", "ValidatingWebhookConfiguration", "cert-manager-webhook") {
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200548 metadata+: {
549 annotations: {
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200550 },
551 },
Piotr Dobrowolski2afe6042019-04-02 14:43:34 +0200552 // Copied from official yaml
Piotr Dobrowolskifc514a92019-05-05 12:12:13 +0200553 webhooks: [
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200554 {
555 "name": "certificates.admission.certmanager.k8s.io",
556 "namespaceSelector": {
557 "matchExpressions": [
558 {
559 "key": "certmanager.k8s.io/disable-validation",
560 "operator": "NotIn",
561 "values": [
562 "true"
563 ]
564 },
565 {
566 "key": "name",
567 "operator": "NotIn",
568 "values": [
569 "cert-manager"
570 ]
571 }
572 ]
573 },
574 "rules": [
575 {
576 "apiGroups": [
577 "certmanager.k8s.io"
578 ],
579 "apiVersions": [
580 "v1alpha1"
581 ],
582 "operations": [
583 "CREATE",
584 "UPDATE"
585 ],
586 "resources": [
587 "certificates"
588 ]
589 }
590 ],
591 "failurePolicy": "Fail",
592 "clientConfig": {
593 "service": {
594 "name": "kubernetes",
595 "namespace": "default",
596 "path": "/apis/admission.certmanager.k8s.io/v1beta1/certificates"
597 },
598 "caBundle": "",
599 }
600 },
601 {
602 "name": "issuers.admission.certmanager.k8s.io",
603 "namespaceSelector": {
604 "matchExpressions": [
605 {
606 "key": "certmanager.k8s.io/disable-validation",
607 "operator": "NotIn",
608 "values": [
609 "true"
610 ]
611 },
612 {
613 "key": "name",
614 "operator": "NotIn",
615 "values": [
616 "cert-manager"
617 ]
618 }
619 ]
620 },
621 "rules": [
622 {
623 "apiGroups": [
624 "certmanager.k8s.io"
625 ],
626 "apiVersions": [
627 "v1alpha1"
628 ],
629 "operations": [
630 "CREATE",
631 "UPDATE"
632 ],
633 "resources": [
634 "issuers"
635 ]
636 }
637 ],
638 "failurePolicy": "Fail",
639 "clientConfig": {
640 "service": {
641 "name": "kubernetes",
642 "namespace": "default",
643 "path": "/apis/admission.certmanager.k8s.io/v1beta1/issuers"
644 },
645 "caBundle": "",
646 }
647 },
648 {
649 "name": "clusterissuers.admission.certmanager.k8s.io",
650 "namespaceSelector": {
651 "matchExpressions": [
652 {
653 "key": "certmanager.k8s.io/disable-validation",
654 "operator": "NotIn",
655 "values": [
656 "true"
657 ]
658 },
659 {
660 "key": "name",
661 "operator": "NotIn",
662 "values": [
663 "cert-manager"
664 ]
665 }
666 ]
667 },
668 "rules": [
669 {
670 "apiGroups": [
671 "certmanager.k8s.io"
672 ],
673 "apiVersions": [
674 "v1alpha1"
675 ],
676 "operations": [
677 "CREATE",
678 "UPDATE"
679 ],
680 "resources": [
681 "clusterissuers"
682 ]
683 }
684 ],
685 "failurePolicy": "Fail",
686 "clientConfig": {
687 "service": {
688 "name": "kubernetes",
689 "namespace": "default",
690 "path": "/apis/admission.certmanager.k8s.io/v1beta1/clusterissuers"
691 },
692 "caBundle": "",
693 }
694 }
Piotr Dobrowolskifc514a92019-05-05 12:12:13 +0200695 ],
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200696 },
697 },
698
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200699 Issuer(name): kube._Object("certmanager.k8s.io/v1alpha1", "Issuer", name) {
700 spec: error "spec must be specified",
701 },
702
Piotr Dobrowolski2afe6042019-04-02 14:43:34 +0200703 ClusterIssuer(name): kube._Object("certmanager.k8s.io/v1alpha1", "ClusterIssuer", name) {
704 spec: error "spec must be specified",
705 },
706
Piotr Dobrowolski79ddbc52019-04-02 13:20:15 +0200707 Certificate(name): kube._Object("certmanager.k8s.io/v1alpha1", "Certificate", name) {
708 spec: error "spec must be specified",
709 },
710}