Private CA for mTLS with cert-manager

第一步,安装cert-manager:

https://cert-manager.io/docs/installation/kubernetes/

第二步,安装自签名ClusterIssuer(所有Namespace共用):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: FooBarSelfSign
spec:
  selfSigned: {}

第三步,签发CA证书(Application Namespace自用):

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
 name: FooBarCACertificate
spec:
 secretName: FooBarCACertificate
 isCA: true
 issuerRef:
   name: FooBarSelfSign
   kind: ClusterIssuer
 commonName: "FooBar CA"

第四步,安装CA(Application Namespace自用):

apiVersion: cert-manager.io/v1alpha2
kind: Issuer
metadata:
 name: FooBarCA
spec:
 ca:
   secretName: FooBarCACertificate

第五步,生成随机JKS密码(Application Namespace自用):

NOTE:如果不需要JKS可以省略这步。

apiVersion: v1
kind: Secret
metadata:
  name: FooBarJKSPassword
data:
  Password: {{ uuidv4 | b64enc }}

第六步,使用CA签发Application证书(Application Namespace自用):

NOTE:如果不需要JKS,可以省略keystores的部分

---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: FooBarServerCertificate
spec:
  secretName: FooBarServerCertificate
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  keySize: 2048
  keyAlgorithm: rsa
  keyEncoding: pkcs1
  commonName: server.foobar.com
  keystores:
    jks:
      create: true
      passwordSecretRef:
        name: FooBarJKSPassword
        key: Password
  issuerRef:
    name: FooBarCA
    kind: Issuer
    group: cert-manager.io
---
apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: FooBarClientCertificate
spec:
  secretName: FooBarClientCertificate
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  keySize: 2048
  keyAlgorithm: rsa
  keyEncoding: pkcs1
  commonName: "FooBar Client"
  keystores:
    jks:
      create: true
      passwordSecretRef:
        name: FooBarJKSPassword
        key: Password
  issuerRef:
    name: FooBarCA
    kind: Issuer
    group: cert-manager.io

生成的FooBarServerCertificateFooBarClientCertificate包含以下内容:

  • tls.key – 证书私钥
  • tls.crt – 证书
  • ca.crt – CA证书
  • keystore.jks
  • truststore.jks

第七步,安装签发的证书到应用:

示例1 – Server Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: FooBarServer
spec:
  template:
    spec:
      containers:
        - name: FooBarServer
          volumeMounts:
            - name: tls
              mountPath: /var/ssl/private
              readOnly: true
      volumes:
        - name: tls
          secret:
            secretName: FooBarServerCertificate
            items:
              - key: truststore.jks
                path: foobar-truststore.jks
              - key: keystore.jks
                path: foobar-keystore.jks
...

示例2 – Ingress Client:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: FooBarIngress
  annotations:
    kubernetes.io/ingress.class: nginx

    # enable mTLS for frontend
    nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
    nginx.ingress.kubernetes.io/auth-tls-verify-depth: 2
    nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "false"
    # read the ca.crt part of the certificate
    nginx.ingress.kubernetes.io/auth-tls-secret: <another certificate for frontend>
    
    # enable mTLS for backend
    nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
    nginx.ingress.kubernetes.io/proxy-ssl-secret: FooBarClientCertificate
spec:
  rules:
    - host: server.foobar.com
      http:
        paths:
          - path: /xxx
            backend:
              serviceName: FooBarAppService
              servicePort: xxx
  tls:
    - hosts:
        - server.foobar.com
      # read the tls.key and tls.crt parts of the certificate
      secretName: <another certificate for frontend>
...

Base Template in Helm

有时我们需要使用Base Template来同时生成两个稍微有点差别的manifest文件。

如果你只需要更改Map里面的内容,一种很容易的方式是直接合并生成好的YAML。但对于Containers以及Volumes这种数组类型,合并YAML就不是很容易了。

另外一种方式,是生成本地修改的.Values,具体内容如下:

templates/_util.tpl:

{{- /*
chart.util.merge will merge a YAML value file with context,
use it to generate another YAML file and output the result.
This takes an array of three values:
- the top context
- the name of the value overrides
- the name of the target template
*/ -}}
{{- define "chart.util.merge" -}}
{{- $top := first . -}}
{{- $overrides := fromYaml (include (index . 1) $top) | default (dict ) -}}
{{- include (index . 2) (merge $overrides $top) -}}
{{- end -}}

templates/_parent.yaml:

{{/*
Parent Base Template
*/}}
{{- define "chart.parent.tpl" -}}
Current Value: {{ .Values.test.key.value }}
{{- end -}}
{{- define "chart.parent" -}}
{{- include "chart.util.merge" (append . "chart.parent.tpl") -}}
{{- end -}}

templates/child1.yaml:

{{- include "chart.parent" (list . "chart.parent.child1") -}}
{{- define "chart.parent.child1" -}}
Values:
  test:
    key:
      value: child1
{{- end -}}

templates/child2.yaml:

{{- include "chart.parent" (list . "chart.parent.child2") -}}
{{- define "chart.parent.child2" -}}
Values:
  test:
    key:
      value: child2
{{- end -}}

values.yaml:

test:
  key: {}
    # This value is provided by child1.yaml or child2.yaml
    #value: parent

最后结果如下:

$ helm template test test
---
# Source: test/templates/child1.yaml
Current Value: child1
---
# Source: test/templates/child2.yaml
Current Value: child2