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