Einrichten des CSI-Treibers für den Geheimnisspeicher zum Aktivieren des NGINX-Eingangsdatencontrollers mit TLS

Dieser Artikel führt Sie schrittweise durch den Prozess zum Sichern eines NGINX-Eingangsdatencontrollers mit TLS mit einem AKS-Cluster (Azure Kubernetes Service) und einer Azure Key Vault-Instanz (AKV). Weitere Informationen finden Sie unter TLS in Kubernetes.

Sie können das eingehende TLS-Zertifikat mit einer der folgenden Methoden in den Cluster importieren:

  • Anwendung: Das Anwendungsbereitstellungsmanifest deklariert das Anbietervolume und bindet es ein. Nur wenn Sie die Anwendung bereitstellen, wird das Zertifikat im Cluster verfügbar gemacht. Wenn Sie die Anwendung entfernen, wird auch das Geheimnis entfernt. Dieses Szenario eignet sich für Bereitstellungsteams, die für die Sicherheitsinfrastruktur der Anwendung und ihre Integration in den Cluster verantwortlich sind.
  • Eingangsdatencontroller: Die Eingangsbereitstellung wird so geändert, dass das Anbietervolume deklariert und eingebunden wird. Das Geheimnis wird importiert, wenn Eingangspods erstellt werden. Die Pods der Anwendung besitzen keinen Zugriff auf das TLS-Zertifikat. Dieses Szenario eignet sich für Situationen, in denen ein Team (z. B. die IT-Abteilung) Infrastruktur- und Netzwerkkomponenten (einschließlich HTTPS-TLS-Zertifikate) verwaltet und erstellt, während andere Teams den Anwendungslebenszyklus verwalten.

Voraussetzungen

Generieren eines TLS-Zertifikats

  • Generieren Sie mit dem folgenden Befehl ein TLS-Zertifikat.

    export CERT_NAME=aks-ingress-cert
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
        -out aks-ingress-tls.crt \
        -keyout aks-ingress-tls.key \
        -subj "/CN=demo.azure.com/O=aks-ingress-tls"
    

Importieren des Zertifikats in AKV

  1. Exportieren Sie das Zertifikat mit folgendem Befehl in eine PFX-Datei:

    export AKV_NAME="[YOUR AKV NAME]"
    openssl pkcs12 -export -in aks-ingress-tls.crt -inkey aks-ingress-tls.key  -out $CERT_NAME.pfx
    # skip Password prompt
    
  2. Importieren Sie das Zertifikat mit dem Befehl az keyvault certificate import.

    az keyvault certificate import --vault-name $AKV_NAME -n $CERT_NAME -f $CERT_NAME.pfx
    

Bereitstellen einer SecretProviderClass

  1. Exportieren Sie einen neuen Namespace mit dem folgenden Befehl.

    export NAMESPACE=ingress-basic
    
  2. Erstellen Sie den Namespace mit dem Befehl kubectl create namespace.

    kubectl create namespace $NAMESPACE
    
  3. Wählen Sie eine Methode aus, um eine Zugriffsidentität bereitzustellen, und konfigurieren Sie Ihre SecretProviderClass-YAML-Datei entsprechend.

    • Achten Sie darauf, objectType=secret zu verwenden, da dies die einzige Möglichkeit ist, den privaten Schlüssel und das Zertifikat aus AKV abzurufen.
    • Legen Sie kubernetes.io/tls als type in Ihrem Abschnitt secretObjects fest.

    Im Folgenden finden Sie ein Beispiel, wie Ihre SecretProviderClass aussehen kann:

    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: azure-tls
    spec:
      provider: azure
      secretObjects:                            # secretObjects defines the desired state of synced K8s secret objects
        - secretName: ingress-tls-csi
          type: kubernetes.io/tls
          data: 
            - objectName: $CERT_NAME
              key: tls.key
            - objectName: $CERT_NAME
              key: tls.crt
      parameters:
        usePodIdentity: "false"
        useVMManagedIdentity: "true"
        userAssignedIdentityID: <client id>
        keyvaultName: $AKV_NAME                 # the name of the AKV instance
        objects: |
          array:
            - |
              objectName: $CERT_NAME
              objectType: secret
        tenantId: $TENANT_ID                    # the tenant ID of the AKV instance
    
  4. Wenden Sie die SecretProviderClass mit dem Befehl kubectl apply auf Ihren Kubernetes-Cluster an.

    kubectl apply -f secretProviderClass.yaml -n $NAMESPACE
    

Bereitstellen des Eingangsdatencontrollers

Hinzufügen des offiziellen Repositorys für das Eingangsdiagramm

  • Fügen Sie das offizielle Repository für Eingangsdiagramme mithilfe der folgenden helm-Befehle hinzu.

    helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
    helm repo update
    

Konfigurieren und Bereitstellen des NGINX-Eingangsdatencontrollers

Je nach Szenario können Sie das Zertifikat entweder an die Anwendung oder an den Eingangsdatencontroller binden. Befolgen Sie die Anweisungen unten gemäß Ihrer Auswahl:

Binden eines Zertifikats an eine Anwendung

  • Binden Sie das Zertifikat mithilfe des Befehls helm install an die Anwendung. Die Bereitstellung der Anwendung verweist auf den Azure Key Vault-Anbieter des CSI-Treibers des Geheimnisspeichers.

    helm install ingress-nginx/ingress-nginx --generate-name \
        --namespace $NAMESPACE \
        --set controller.replicaCount=2 \
        --set controller.nodeSelector."kubernetes\.io/os"=linux \
        --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
        --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux
    

Binden eines Zertifikats an den Eingangsdatencontroller

  1. Binden Sie das Zertifikat mithilfe des Befehls helm install an den Eingangscontroller. Die Bereitstellung des Eingangsdatencontrollers verweist auf den Azure Key Vault-Anbieter des CSI-Treibers des Geheimnisspeichers.

    Hinweis

    • Wenn Sie die vom Pod verwaltete Microsoft Entra-Identität nicht als Zugriffsmethode verwenden, entfernen Sie die Zeile mit --set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME.

    • Außerdem ist eine Bindung der SecretProviderClass an einen Pod erforderlich, damit der Secrets Store CSI-Treiber den Pod einbindet und das Kubernetes-Geheimnis generiert. Weitere Informationen finden Sie unter Synchronisieren von eingebundenen Inhalten mit einem Kubernetes-Geheimnis.

    helm install ingress-nginx/ingress-nginx --generate-name \
        --namespace $NAMESPACE \
        --set controller.replicaCount=2 \
        --set controller.nodeSelector."kubernetes\.io/os"=linux \
        --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
        --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz \
        --set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME \
        -f - <<EOF
    controller:
      extraVolumes:
          - name: secrets-store-inline
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "azure-tls"
      extraVolumeMounts:
          - name: secrets-store-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
    EOF
    
  2. Überprüfen Sie mithilfe des Befehls kubectl get secret, ob das Kubernetes-Geheimnis erstellt wurde.

    kubectl get secret -n $NAMESPACE
    
    NAME                                             TYPE                                  DATA   AGE
    ingress-tls-csi                                  kubernetes.io/tls                     2      1m34s
    

Bereitstellen der Anwendung

Auch hier ändern sich die Anweisungen je nach Szenario geringfügig. Befolgen Sie die Anweisungen für das Szenario, das Sie ausgewählt haben.

Bereitstellen der Anwendung mithilfe eines Anwendungsverweises

  1. Erstellen Sie eine Datei namens aks-helloworld-one.yaml mit folgendem Inhalt.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-one  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-one
      template:
        metadata:
          labels:
            app: aks-helloworld-one
        spec:
          containers:
          - name: aks-helloworld-one
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "Welcome to Azure Kubernetes Service (AKS)"
            volumeMounts:
            - name: secrets-store-inline
              mountPath: "/mnt/secrets-store"
              readOnly: true
          volumes:
          - name: secrets-store-inline
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "azure-tls"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-one  
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-one
    
  2. Erstellen Sie eine Datei namens aks-helloworld-two.yaml mit folgendem Inhalt.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-two  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-two
      template:
        metadata:
          labels:
            app: aks-helloworld-two
        spec:
          containers:
          - name: aks-helloworld-two
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "AKS Ingress Demo"
            volumeMounts:
            - name: secrets-store-inline
              mountPath: "/mnt/secrets-store"
              readOnly: true
          volumes:
          - name: secrets-store-inline
            csi:
              driver: secrets-store.csi.k8s.io
              readOnly: true
              volumeAttributes:
                secretProviderClass: "azure-tls"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-two
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-two
    
  3. Wenden Sie mithilfe des Befehls kubectl apply den die YAML-Dateien auf Ihren Cluster an.

    kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE
    kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
    
  4. Überprüfen Sie mithilfe des Befehls kubectl get secret, ob das Kubernetes-Geheimnis erstellt wurde.

    kubectl get secret -n $NAMESPACE
    
    NAME                                             TYPE                                  DATA   AGE
    ingress-tls-csi                                  kubernetes.io/tls                     2      1m34s
    

Bereitstellen der Anwendung mithilfe eines Eingangsdatencontroller-Verweises

  1. Erstellen Sie eine Datei namens aks-helloworld-one.yaml mit folgendem Inhalt.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-one  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-one
      template:
        metadata:
          labels:
            app: aks-helloworld-one
        spec:
          containers:
          - name: aks-helloworld-one
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "Welcome to Azure Kubernetes Service (AKS)"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-one
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-one
    
  2. Erstellen Sie eine Datei namens aks-helloworld-two.yaml mit folgendem Inhalt.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: aks-helloworld-two  
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: aks-helloworld-two
      template:
        metadata:
          labels:
            app: aks-helloworld-two
        spec:
          containers:
          - name: aks-helloworld-two
            image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
            ports:
            - containerPort: 80
            env:
            - name: TITLE
              value: "AKS Ingress Demo"
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: aks-helloworld-two  
    spec:
      type: ClusterIP
      ports:
      - port: 80
      selector:
        app: aks-helloworld-two
    
  3. Wenden Sie mithilfe des Befehls kubectl apply den die YAML-Dateien auf Ihren Cluster an.

    kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE
    kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
    

Bereitstellen einer Eingangsressource, die auf das Geheimnis verweist

Sie können nun eine Kubernetes-Eingangsressource bereitstellen, die auf das Geheimnis verweist.

  1. Erstellen Sie eine Datei namens hello-world-ingress.yaml mit folgendem Inhalt.

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ingress-tls
      annotations:
        nginx.ingress.kubernetes.io/rewrite-target: /$2
    spec:
      ingressClassName: nginx
      tls:
      - hosts:
        - demo.azure.com
        secretName: ingress-tls-csi
      rules:
      - host: demo.azure.com
        http:
          paths:
          - path: /hello-world-one(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
          - path: /hello-world-two(/|$)(.*)
            pathType: Prefix      
            backend:
              service:
                name: aks-helloworld-two
                port:
                  number: 80
          - path: /(.*)
            pathType: Prefix      
            backend:
              service:
                name: aks-helloworld-one
                port:
                  number: 80
    
  2. Notieren Sie sich den Abschnitt tls, der auf das zuvor erstellte Geheimnis verweist, und wenden Sie die Datei mit dem Befehl kubectl apply auf Ihren Cluster an.

    kubectl apply -f hello-world-ingress.yaml -n $NAMESPACE
    

Abrufen der externen IP-Adresse des Eingangsdatencontrollers

  • Ermitteln Sie die externe IP-Adresse des Eingangsdatencontrollers mit dem Befehl kubectl get service.

    kubectl get service --namespace $NAMESPACE --selector app.kubernetes.io/name=ingress-nginx
    
    NAME                                       TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                      AGE
    nginx-ingress-1588032400-controller        LoadBalancer   10.0.255.157   EXTERNAL_IP      80:31293/TCP,443:31265/TCP   19m
    nginx-ingress-1588032400-default-backend   ClusterIP      10.0.223.214   <none>           80/TCP                       19m
    

Testen des mit TLS gesicherten Eingangs

  1. Überprüfen Sie mit dem Befehl curl, ob Ihr Eingang ordnungsgemäß mit TLS konfiguriert ist. Stellen Sie sicher, dass Sie die externe IP-Adresse aus dem vorherigen Schritt verwenden.

    curl -v -k --resolve demo.azure.com:443:EXTERNAL_IP https://demo.azure.com
    

    Da mit der Adresse kein anderer Pfad angegeben wurde, verendet der Eingangsdatencontroller standardmäßig die Route /. Die erste Demoanwendung wird zurückgegeben, wie in der folgenden verkürzten Beispielausgabe gezeigt:

    [...]
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" type="text/css" href="/static/default.css">
        <title>Welcome to Azure Kubernetes Service (AKS)</title>
    [...]
    

    Durch den Parameter -v im curl-Befehl werden ausführliche Informationen ausgegeben, einschließlich des erhaltenen TLS-Zertifikats. Während der CURL-Ausgabe können Sie überprüfen, ob Ihr eigenes TLS-Zertifikat verwendet wurde. Durch den Parameter -k wird die Seite auch dann weiterhin geladen, wenn wir ein selbstsigniertes Zertifikat verwenden. Das folgende Beispiel zeigt, dass das Zertifikat issuer: CN=demo.azure.com; O=aks-ingress-tls verwendet wurde:

    [...]
     * Server certificate:
     *  subject: CN=demo.azure.com; O=aks-ingress-tls
     *  start date: Oct 22 22:13:54 2021 GMT
     *  expire date: Oct 22 22:13:54 2022 GMT
     *  issuer: CN=demo.azure.com; O=aks-ingress-tls
     *  SSL certificate verify result: self signed certificate (18), continuing anyway.
    [...]
    
  2. Fügen Sie der Adresse den Pfad /hello-world-two hinzu, z. B. https://demo.azure.com/hello-world-two, und vergewissern Sie sich, dass die zweite Demoanwendung ordnungsgemäß konfiguriert ist.

    curl -v -k --resolve demo.azure.com:443:EXTERNAL_IP https://demo.azure.com/hello-world-two
    

    Die zweite Demoanwendung wird mit dem benutzerdefinierten Titel zurückgegeben, wie in der folgenden verkürzten Beispielausgabe gezeigt:

    [...]
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" type="text/css" href="/static/default.css">
        <title>AKS Ingress Demo</title>
    [...]