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