TLS를 사용하여 NGINX 수신 컨트롤러를 사용하도록 비밀 저장소 CSI 드라이버 설정

이 문서에서는 AKS(Azure Kubernetes Service) 클러스터 및 AKV(Azure Key Vault) 인스턴스에서 TLS를 사용하여 NGINX 수신 컨트롤러를 보호하는 프로세스를 안내합니다. 자세한 내용은 Kubernetes의 TLS를 참조하세요.

다음 방법 중 하나를 사용하여 수신 TLS 인증서를 클러스터로 가져올 수 있습니다.

  • 애플리케이션: 애플리케이션 배포 매니페스트는 공급자 볼륨을 선언하고 탑재합니다. 애플리케이션을 배포할 때만 클러스터에서 인증서를 사용할 수 있습니다. 애플리케이션을 제거하면 비밀도 제거됩니다. 이 시나리오는 애플리케이션의 보안 인프라 및 클러스터와의 통합을 담당하는 개발 팀에 적합합니다.
  • 수신 컨트롤러: 수신 배포는 공급자 볼륨을 선언하고 탑재하도록 수정됩니다. 수신 Pod를 만들 때 비밀을 가져옵니다. 애플리케이션의 Pod는 TLS 인증서에 액세스할 수 없습니다. 이 시나리오는 한 팀(예:, IT)이 인프라 및 네트워킹 구성 요소(HTTPS TLS 인증서 포함)를 관리하고 생성하며 다른 팀이 애플리케이션 수명 주기를 관리하는 시나리오에 적합합니다.

필수 조건

TLS 인증서 생성

  • 다음 명령을 사용하여 TLS 인증서를 생성합니다.

    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"
    

AKV로 인증서 가져오기

  1. 다음 명령을 사용하여 인증서를 PFX 파일로 내보냅니다.

    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. az keyvault certificate import 명령을 사용하여 인증서를 가져옵니다.

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

SecretProviderClass 배포

  1. 다음 명령을 사용하여 새 네임스페이스를 내보냅니다.

    export NAMESPACE=ingress-basic
    
  2. kubectl create namespace 명령을 사용하여 네임스페이스를 만듭니다.

    kubectl create namespace $NAMESPACE
    
  3. 액세스 ID를 제공하는 메서드를 선택하고 그에 따라 SecretProviderClass YAML을 구성합니다.

    • AKV에서 프라이빗 키와 인증서를 가져오는 유일한 방법인 objectType=secret을(를) 사용해야 합니다.
    • secretObjects 섹션에 있는 kubernetes.io/tls을(를) type(으)로 설정합니다.

    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. kubectl apply 명령을 사용하여 Kubernetes 클러스터에 SecretProviderClass를 적용합니다.

    kubectl apply -f secretProviderClass.yaml -n $NAMESPACE
    

수신 컨트롤러 배포

공식 수신 차트 리포지토리 추가

  • 다음 helm 명령을 사용하여 공식 수신 차트 리포지토리를 추가합니다.

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

NGINX 수신 구성 및 배포

시나리오에 따라 애플리케이션 또는 수신 컨트롤러에 인증서를 바인딩하도록 선택할 수 있습니다. 선택 영역에 따라 아래 지침을 따릅니다.

애플리케이션에 인증서 바인딩

  • helm install 명령을 사용하여 애플리케이션에 인증서를 바인딩합니다. 애플리케이션의 배포는 비밀 저장소 CSI 드라이버의 Azure Key Vault 공급자를 참조합니다.

    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
    

수신 컨트롤러에 인증서 바인딩

  1. helm install 명령을 사용하여 수신 컨트롤러에 인증서를 바인딩합니다. 수신 컨트롤러의 배포는 비밀 저장소 CSI 드라이버의 Azure Key Vault 공급자를 참조합니다.

    참고 항목

    • Microsoft Entra Pod 관리 ID를 액세스 방법으로 사용하지 않는 경우 --set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME을 사용하여 줄을 제거합니다.

    • 또한 SecretProviderClass를 Pod에 바인딩해야 비밀 저장소 CSI 드라이버에서 이를 탑재하고 Kubernetes 비밀을 생성합니다. 탑재된 콘텐츠를 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. kubectl get secret 명령을 사용하여 Kubernetes 비밀이 만들어졌는지 확인합니다.

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

애플리케이션 배포

다시 말하지만, 지침은 시나리오에 따라 약간 변경됩니다. 선택한 시나리오에 해당하는 지침을 따릅니다.

애플리케이션 참조를 사용하여 애플리케이션 배포

  1. 다음과 같은 내용으로 aks-helloworld-one.yaml라는 파일을 만듭니다.

    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. 다음과 같은 내용으로 aks-helloworld-two.yaml라는 파일을 만듭니다.

    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. kubectl apply 명령을 사용하여 클러스터에 YAML 파일을 적용합니다.

    kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE
    kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
    
  4. kubectl get secret 명령을 사용하여 Kubernetes 비밀이 만들어졌는지 확인합니다.

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

수신 컨트롤러 참조를 사용하여 애플리케이션 배포

  1. 다음과 같은 내용으로 aks-helloworld-one.yaml라는 파일을 만듭니다.

    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. 다음과 같은 내용으로 aks-helloworld-two.yaml라는 파일을 만듭니다.

    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. kubectl apply 명령을 사용하여 클러스터에 YAML 파일을 적용합니다.

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

비밀을 참조하는 수신 리소스 배포

이제 비밀을 참조하는 Kubernetes 수신 리소스를 배포할 수 있습니다.

  1. 다음 콘텐츠를 사용하여 파일 이름 hello-world-ingress.yaml을 만듭니다.

    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. 앞에서 만든 비밀을 참조하는 tls 섹션을 기록하고 kubectl apply 명령을 사용하여 클러스터에 파일을 적용합니다.

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

수신 컨트롤러의 외부 IP 주소 가져오기

  • kubectl get service 명령을 사용하여 수신 컨트롤러의 외부 IP 주소를 가져옵니다.

    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
    

TLS를 사용하여 보안이 설정된 수신 테스트

  1. 다음 curl 명령을 사용하여 수신이 TLS로 올바르게 구성되었는지 확인합니다. 이전 단계의 외부 IP를 사용해야 합니다.

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

    주소와 함께 다른 경로가 제공되지 않았으므로 수신 컨트롤러는 기본값인 / 경로로 설정됩니다. 첫 번째 데모 애플리케이션은 다음 축소된 예제 출력에 표시된 대로 반환됩니다.

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

    curl 명령의 -v 매개 변수는 수신된 TLS 인증서를 비롯한 자세한 정보를 출력합니다. curl 출력을 따라 절반 부분까지 이동하면 고유한 TLS 인증서를 사용했는지 확인할 수 있습니다. -k 매개 변수는 자체 서명된 인증서를 사용하더라도 페이지를 계속 로드합니다. 다음 예제에서는 발급자: CN=demo.azure.com; O = CN=demo.azure.com; O=aks-ingress-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. 주소에 /hello-world-two 경로(예: https://demo.azure.com/hello-world-two)를 추가하고 두 번째 데모 애플리케이션이 제대로 구성되었는지 확인합니다.

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

    사용자 지정 제목이 있는 두 번째 데모 애플리케이션은 다음 축소된 예제 출력에 표시된 대로 반환됩니다.

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