connaisseur icon indicating copy to clipboard operation
connaisseur copied to clipboard

CertManager integration for the AdmissionController certificate

Open andreee94 opened this issue 2 years ago • 1 comments

Describe the feature The Kubernetes validating and mutating admission webhooks require a self signed certificate. Until now the certificate is generated automatically when the helm chart is installed but the lifecycle has to be managed manually.

In case the cluster have already installed the cert-manager utility, the certificate management can be delegated to it.

Cert manager can that inject the caBundle inside the MutatingWebhookConfiguration.

Optional: Is your feature request related to a problem? Please describe. <A clear and concise description of what the problem is.>

Optional: Implementation ideas

certificate_webhook-conf.yaml file:

{{- $k8sMinor := (include "k8s-version-minor" .) -}}
{{- $altNames := list -}}
{{- $altNames = append $altNames (printf "%s-svc" .Chart.Name) -}}
{{- $altNames = append $altNames (printf "%s-svc.%s" .Chart.Name .Release.Namespace) -}}
{{- $altNames = append $altNames (printf "%s-svc.%s.svc" .Chart.Name .Release.Namespace) -}}
{{- $altNames = append $altNames (printf "%s-svc.%s.svc.cluster.local" .Chart.Name .Release.Namespace) -}}
{{- $certs := genSelfSignedCert (printf "%s-svc.%s.svc" .Chart.Name .Release.Namespace) nil $altNames 36500 -}}

{{- if .Values.admissionWebhook.certManager.useCertManager }}
  {{- if .Values.admissionWebhook.certManager.newCertificate }}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: {{ .Chart.Name }}-certificate
  namespace: {{ .Release.Namespace }}
spec:
  commonName: "{{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc"
  dnsNames:
    - {{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc.cluster.local
    - {{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc
  issuerRef:
    kind: {{ .Values.admissionWebhook.certManager.newCertificate.issuerRef.kind }}
    name: {{ .Values.admissionWebhook.certManager.newCertificate.issuerRef.name }}
  secretName: {{ .Chart.Name }}-tls
  {{- end }}
{{- else }}
---
apiVersion: v1
kind: Secret
metadata:
  name: {{ .Chart.Name }}-tls
  namespace: {{ .Release.Namespace }}
  labels:
    {{- include "helm.labels" . | nindent 4 }}
type: Opaque
data:
  tls.crt: {{ default ($certs.Cert | b64enc) (include "getInstalledTLSCert" .) }}
  tls.key: {{ default ($certs.Key | b64enc) (include "getInstalledTLSKey" .) }}
{{- end}}
---
{{ if lt ($k8sMinor | int) 17 }}
apiVersion: admissionregistration.k8s.io/v1beta1
{{ else }}
apiVersion: admissionregistration.k8s.io/v1
{{ end }}
kind: MutatingWebhookConfiguration
metadata:
  name: {{ .Chart.Name }}-webhook
  labels:
    {{- include "helm.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": post-delete
    "helm.sh/hook-delete-policy": before-hook-creation, hook-succeeded, hook-failed
    {{- if .Values.admissionWebhook.certManager.useCertManager }}
      {{- if .Values.admissionWebhook.certManager.existingCertificate }}
    cert-manager.io/inject-ca-from: {{ .Values.admissionWebhook.certManager.existingCertificate.namespace }}/{{ .Values.admissionWebhook.certManager.existingCertificate.name }}
      {{- else }} # new certificate
    cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Chart.Name }}-certificate
      {{- end }}
    {{- end }}
webhooks:
  - name: {{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc
    failurePolicy: Ignore
    reinvocationPolicy: {{ .Values.deployment.reinvocationPolicy | default "Never" }}
    clientConfig:
      service:
        name: {{ .Chart.Name }}-svc
        namespace: {{ .Release.Namespace }}
        path: /mutate
      {{- if not .Values.admissionWebhook.certManager.useCertManager }}
      caBundle: {{ default ($certs.Cert | b64enc) (include "getInstalledTLSCert" .) }}
      {{- end }}
    rules: []
    sideEffects: None
    {{- if lt ($k8sMinor | int) 17 }}
    admissionReviewVersions: ["v1beta1"]
    {{- else }}
    admissionReviewVersions: ["v1"]
    {{- end }}
---
{{ if lt ($k8sMinor | int) 17 -}}
apiVersion: admissionregistration.k8s.io/v1beta1
{{ else -}}
apiVersion: admissionregistration.k8s.io/v1
{{ end -}}
kind: MutatingWebhookConfiguration
metadata:
  name: {{ .Chart.Name }}-webhook
  labels:
    {{- include "helm.labels" . | nindent 4 }}
  annotations:
    "helm.sh/hook": post-install, post-upgrade, post-rollback
    {{- if .Values.admissionWebhook.certManager.useCertManager }}
      {{- if .Values.admissionWebhook.certManager.existingCertificate }}
    cert-manager.io/inject-ca-from: {{ .Values.admissionWebhook.certManager.existingCertificate.namespace }}/{{ .Values.admissionWebhook.certManager.existingCertificate.name }}
      {{- else }} # new certificate
    cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/{{ .Chart.Name }}-certificate
      {{- end }}
    {{- end }}
webhooks:
  - name: {{ .Chart.Name }}-svc.{{ .Release.Namespace }}.svc
    failurePolicy: {{ .Values.deployment.failurePolicy | default "Fail" }}
    reinvocationPolicy: {{ .Values.deployment.reinvocationPolicy | default "Never" }}
    clientConfig:
      service:
        name: {{ .Chart.Name }}-svc
        namespace: {{ .Release.Namespace }}
        path: /mutate
      {{- if not .Values.admissionWebhook.certManager.useCertManager }}
      caBundle: {{ default ($certs.Cert | b64enc) (include "getInstalledTLSCert" .) }}
      {{- end }}
    rules:
      - operations: ["CREATE", "UPDATE"]
        apiGroups: ["*"]
        apiVersions: ["*"]
        {{- if .Values.automaticChildApproval }}
        {{- if .Values.automaticChildApproval.enabled }}
        resources: ["pods", "deployments", "replicationcontrollers", "replicasets", "daemonsets", "statefulsets", "jobs", "cronjobs"]
        {{- else }}
        resources: ["pods"]
        {{- end }}
        {{- else }}
        resources: ["pods", "deployments", "replicationcontrollers", "replicasets", "daemonsets", "statefulsets", "jobs", "cronjobs"]
        {{- end }}
    sideEffects: None
    {{- if gt ($k8sMinor | int) 13 }}
    timeoutSeconds: 30
    {{- end }}
    {{- if lt ($k8sMinor | int) 17 }}
    admissionReviewVersions: ["v1beta1"]
    {{- else }}
    admissionReviewVersions: ["v1"]
    {{- end }}
    {{- if .Values.namespacedValidation }}
    {{- if .Values.namespacedValidation.enabled }}
    namespaceSelector:
      matchExpressions:
      - key: securesystemsengineering.connaisseur/webhook
    {{- if not .Values.namespacedValidation.mode}}
        operator: NotIn
        values:
          - ignore
    {{- else if eq .Values.namespacedValidation.mode "ignore"}}
        operator: NotIn
        values:
          - ignore
    {{- else if eq .Values.namespacedValidation.mode "validate"}}
        operator: In
        values:
          - validate
   {{- end }}
   {{- end }}
   {{- end }}

Example of values.yaml structure:

admissionWebhook:
  certManager:
    useCertManager: true
    # existingCertificate:
    #   name: validating-webhook-cert
    #   namespace: kube-system
    newCertificate:
      issuerRef:
        kind: ClusterIssuer
        name: ca-issuer

This requires and already installed CA. This is an example of a self signed ca created with cert manager:

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: root-ca-certificate
  namespace: cert-manager
spec:
  isCA: true
  commonName: connaisseur.test.com
  secretName: root-ca-secret
  privateKey:
    algorithm: ECDSA
    size: 256
  issuerRef:
    name: selfsigned-issuer
    kind: ClusterIssuer
    group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: ca-issuer
spec:
  ca:
    secretName: root-ca-secret

Optional: Additional context <Add any other context or screenshots about the feature request here.>

Let me know if you think that this can be useful so that I will create a pull request. Thank you.

andreee94 avatar Mar 05 '22 18:03 andreee94

@andreee94 I think this is a great idea that somehow relates to the earlier issue #225 to allow specifying your own cert. I am not a pro in certManager, but would think that externalizing the management of TLS certs would be a great feature. We'd probably have to make sure that cert renewal restarts Connaisseur and does not break the install (we have experienced issues in the past), but I'd be up for implementing this. Would have to understand the details a bit better though... @phbelitz might have more feedback on this.

thanks for the awesome idea :pray:

xopham avatar Mar 16 '22 07:03 xopham