设置机密存储 CSI 驱动程序,以启用使用 TLS 的 NGINX 入口控制器
本文指导你完成借助 Azure Kubernetes 服务 (AKS) 群集和 Azure 密钥保管库 (AKV) 实例使用 TLS 保护 NGINX 入口控制器的过程。 有关详细信息,请参阅 Kubernetes 中的 TLS。
可使用以下方法之一将入口 TLS 证书导入群集:
- 应用程序:应用程序部署清单声明并装载提供程序卷。 只有在部署应用程序时,群集中才提供证书。 移除应用程序时,也一并移除机密。 此方案适用于负责应用程序的安全基础结构及其与群集的集成的开发团队。
- 入口控制器:修改入口部署以声明并装载提供程序卷。 创建入口 Pod 时,将导入机密。 应用程序的 Pod 无权访问 TLS 证书。 此方案适用于管理和创建基础结构和网络组件(包括 HTTPS TLS 证书)的团队(例如 IT 团队),以及管理应用程序生命周期的其他团队。
先决条件
- 如果没有 Azure 订阅,请在开始前创建一个试用版订阅。
- 在开始之前,请确保 Azure CLI 版本 >=
2.30.0
,或安装最新版本。 - 配置了机密存储 CSI 驱动程序的 AKS 群集。
- Azure Key Vault 实例。
生成 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
使用以下命令将证书导出到 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
使用
az keyvault certificate import
命令导入证书。az keyvault certificate import --vault-name $AKV_NAME --name $CERT_NAME --file $CERT_NAME.pfx
部署 SecretProviderClass
使用以下命令导出新的命名空间。
export NAMESPACE=ingress-basic
使用
kubectl create namespace
命令创建命名空间。kubectl create namespace $NAMESPACE
选择一种提供访问标识的方法,并相应地配置 SecretProviderClass YAML。
- 请确保使用
objectType=secret
,这是从 AKV 获取私钥和证书的唯一方法。 - 在
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
- 请确保使用
使用
kubectl apply
命令将 SecretProviderClass 应用到 Kubernetes 群集。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
将证书绑定到入口控制器
使用
helm install
命令将证书绑定到入口控制器。 入口控制器的部署会引用机密存储 CSI 驱动程序的 Azure Key Vault 提供程序。注意
如果不使用 Microsoft Entra Pod 托管标识作为访问方法,请删除包含
--set controller.podLabels.aadpodidbinding=$AAD_POD_IDENTITY_NAME
的行。此外,机密存储 CSI 驱动程序需要将 SecretProviderClass 绑定到 Pod,这样才能装载 Pod 并生成 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
使用
kubectl get secret
命令验证是否创建了 Kubernetes 机密。kubectl get secret -n $NAMESPACE NAME TYPE DATA AGE ingress-tls-csi kubernetes.io/tls 2 1m34s
部署应用程序
同样,根据你的方案,说明将略有变化。 按照所选方案对应的说明进行操作。
使用应用程序引用部署应用程序
创建包含以下内容的名为
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.azk8s.cn/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
创建包含以下内容的名为
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.azk8s.cn/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
使用
kubectl apply
命令将 YAML 文件应用到群集。kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
使用
kubectl get secret
命令验证是否创建了 Kubernetes 机密。kubectl get secret -n $NAMESPACE NAME TYPE DATA AGE ingress-tls-csi kubernetes.io/tls 2 1m34s
使用入口控制器引用部署应用程序
创建包含以下内容的名为
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.azk8s.cn/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
创建包含以下内容的名为
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.azk8s.cn/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
使用
kubectl apply
命令将 YAML 文件应用到群集。kubectl apply -f aks-helloworld-one.yaml -n $NAMESPACE kubectl apply -f aks-helloworld-two.yaml -n $NAMESPACE
部署引用机密的入口资源
现在,可以部署引用机密的 Kubernetes 入口资源。
创建名为
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
记下
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 保护的测试入口
使用以下
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 参数也会继续加载页面。 以下示例显示使用了 issuer: 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. [...]
将 /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> [...]