Configuración del controlador CSI del almacén de secretos para habilitar el controlador de entrada NGINX con TLS

Este artículo le guiará por el proceso de protección de un controlador de entrada NGINX con TLS con un clúster de Azure Kubernetes Service (AKS) y una instancia de Azure Key Vault (AKV). Para más información, consulte TLS en Kubernetes.

Puede importar el certificado TLS de entrada al clúster mediante uno de los métodos siguientes:

  • Aplicación: el manifiesto de implementación de la aplicación declara y monta el volumen del proveedor. Solo cuando se implementa la aplicación el certificado está disponible en el clúster. Al quitar la aplicación, también se quita el secreto. Este escenario se adapta a los equipos de desarrollo responsables de la infraestructura de seguridad de la aplicación y su integración con el clúster.
  • Controlador de entrada: la implementación de entrada se modifica para declarar y montar el volumen del proveedor. El secreto se importa cuando se crean pods de entrada. Los pods de la aplicación no tienen acceso al certificado TLS. Este escenario se adapta a aquellos en los que un equipo (por ejemplo, TI) administra y crea componentes de infraestructura y redes (incluidos los certificados TLS HTTPS), y otros equipos administran el ciclo de vida de las aplicaciones.

Prerrequisitos

Generación de un certificado TLS

  • Generar un certificado TLS mediante el siguiente comando.

    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"
    

Importación del certificado en AKV

  1. Exporte el certificado a un archivo PFX mediante el siguiente comando.

    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. Importar el certificado mediante el az keyvault certificate import comando .

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

Implementación de una clase SecretProviderClass

  1. Exportar un nuevo espacio de nombres mediante el siguiente comando.

    export NAMESPACE=ingress-basic
    
  2. Crear el espacio de nombres mediante el comando kubectl create namespace.

    kubectl create namespace $NAMESPACE
    
  3. Seleccione un método para proporcionar una identidad de acceso y configurar la clase YAML secretProviderClass en consecuencia.

    • Asegúrese de usar objectType=secret, la única manera de obtener la clave privada y el certificado de AKV.
    • Establezca kubernetes.io/tls como type en la sección secretObjects.

    Consulte el ejemplo siguiente para obtener un ejemplo del aspecto que podría tener la clase SecretProviderClass:

    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. Aplicación de la clase SecretProviderClass al clúster de Kubernetes mediante el comando kubectl apply.

    kubectl apply -f secretProviderClass.yaml -n $NAMESPACE
    

Implementar el controlador de entrada

Adición del repositorio oficial de gráficos de entrada

  • Agregue el repositorio oficial de gráficos de entrada mediante los siguientes comandos helm.

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

Configuración e implementación de la entrada NGINX

En función de su escenario, puede elegir enlazar el certificado a la aplicación o al controlador de entrada. Siga las instrucciones que se indican a continuación según su selección:

Enlace del certificado a la aplicación

  • Enlazar el certificado a la aplicación mediante el comando helm install. La implementación de la aplicación hace referencia al proveedor de Azure Key Vault del controlador CSI del almacén de secretos.

    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
    

Enlace del certificado al controlador de entrada

  1. Enlazar el certificado al controlador de entrada mediante el comando helm install. La implementación del controlador de entrada hace referencia al proveedor de Azure Key Vault del controlador CSI del almacén de secretos.

    Nota:

    • Si no usa la identidad administrada por pods de Microsoft Entra como método de acceso, quite la línea con --set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME.

    • Además, es necesario enlazar SecretProviderClass a un pod para que el controlador CSI del almacén de secretos lo monte y genere el secreto de Kubernetes. Vea Contenido montado de sincronización con un secreto de Kubernetes .

    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. Compruebe que el secreto de Kubernetes se creó mediante el comando kubectl get secret.

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

Implementación de la aplicación

De nuevo, las instrucciones cambian ligeramente en función de su escenario. Siga las instrucciones correspondientes al escenario seleccionado.

Implementación de la aplicación mediante una referencia de aplicación

  1. Cree un archivo llamado aks-helloworld-one.yaml con el siguiente contenido.

    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. Cree un archivo llamado aks-helloworld-two.yaml con el siguiente contenido.

    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. Aplica los archivos YAML al clúster con el comando kubectl apply.

    kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE
    kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
    
  4. Compruebe que el secreto de Kubernetes se creó mediante el comando kubectl get secret.

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

Implementación de la aplicación mediante una referencia de controlador de entrada

  1. Cree un archivo llamado aks-helloworld-one.yaml con el siguiente contenido.

    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. Cree un archivo llamado aks-helloworld-two.yaml con el siguiente contenido.

    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. Aplica los archivos YAML al clúster con el comando kubectl apply.

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

Implementación de un recurso de entrada que haga referencia al secreto

Ahora podemos implementar un recurso de entrada de Kubernetes que haga referencia al secreto.

  1. Cree un archivo llamado hello-world-ingress.yaml con el siguiente contenido.

    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. Tome nota de la sección tls que hace referencia al secreto creado anteriormente y aplique el archivo al clúster mediante el comando kubectl apply.

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

Obtención de la dirección IP externa del controlador de entrada

  • Obtenga la dirección IP externa del controlador de entrada mediante el comando 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
    

Prueba de la entrada protegida con TLS

  1. Compruebe que la entrada está configurada correctamente con TLS mediante el siguiente comando curl. Use la dirección IP externa del paso anterior.

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

    No proporcionó ninguna ruta de acceso adicional con la dirección, por lo que el controlador de entrada se establece de manera predeterminada en la ruta /. Se devuelve la primera aplicación de demostración, tal como se muestra en la siguiente salida de ejemplo reducido:

    [...]
    <!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>
    [...]
    

    El parámetro -v en el comando curl genera información detallada, incluyendo el certificado TLS recibido. A mitad de la salida de cURL, puede comprobar que se ha utilizado su propio certificado TLS. El parámetro -k continúa cargando la página, aunque se esté usando un certificado autofirmado. El ejemplo siguiente muestra que se ha usado el certificado issuer: CN=demo.azure.com; O=ingress-aks-tls:

    [...]
     * 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. Agregue la ruta de acceso /hello-world-two a la dirección, como https://demo.azure.com/hello-world-two, y compruebe que la segunda aplicación de demostración está configurada correctamente.

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

    Se devuelve la segunda aplicación de demostración con el título personalizado, tal como se muestra en la siguiente salida de ejemplo reducido:

    [...]
    <!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>
    [...]