使用应用程序路由网关 API 实现保护入口流量

注释

本文介绍如何在 资源上Gateway配置 TLS 终止,方法是自行编写 SecretProviderClass,通过示例 Pod 挂载适用于 Secrets Store CSI Driver 的 Azure 密钥保管库 提供程序,并在 Gateway 侦听器的 certificateRefs 中直接引用生成的 Kubernetes Secret。 如果需要完全控制证书同步工作流,或者希望自己管理这些资源,此方法非常有用。

对于大多数用户,推荐的方式是由 Application Routing Operator 提供支持的 Azure DNS 与 Azure 密钥保管库 的自动化集成,它会根据一对监听器SecretProviderClass,为你预配并调和certificateRefs、同步的 Kubernetes Secret 以及监听器tls.options。 若要使用自动化工作流,请参阅 使用应用程序路由的 Gateway API 实现配置 Azure DNS 和 TLS

应用路由插件支持从 Azure 密钥保管库 (AKV) 同步密钥,以通过 TLS 终止来保护网关 API 的入口流量。 按照以下步骤创建证书和密钥,以在网关上终止 TLS 流量。

先决条件

所需的客户端/服务器证书和密钥

  1. 创建根证书和私钥用于为示例服务的证书签名:
mkdir httpbin_certs
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout httpbin_certs/example.com.key -out httpbin_certs/example.com.crt
  1. 生成证书和私钥,用于 httpbin.example.com
openssl req -out httpbin_certs/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin_certs/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
openssl x509 -req -sha256 -days 365 -CA httpbin_certs/example.com.crt -CAkey httpbin_certs/example.com.key -set_serial 0 -in httpbin_certs/httpbin.example.com.csr -out httpbin_certs/httpbin.example.com.crt

配置 TLS 入口网关

设置Azure 密钥保管库并将机密同步到群集

  1. 创建Azure 密钥保管库

    需要一个 Azure 密钥保管库 资源才能向应用程序路由加载项提供证书和密钥输入。

    export AKV_NAME=<azure-key-vault-resource-name>  
    az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
    
  2. 为群集启用 Secret Store CSI 驱动程序加载项的 Azure 密钥保管库 提供程序

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. 如果 密钥保管库 对权限模型使用 Azure RBAC,请按照 此处 的说明,为加载项的用户分配 密钥保管库 密钥用户的 Azure 角色。 或者,如果密钥保管库正在使用保管库访问策略权限模型,请使用访问策略授权为加载项的用户分配托管标识访问 Azure 密钥保管库 资源。

    OBJECT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query 'addonProfiles.azureKeyvaultSecretsProvider.identity.objectId' -o tsv | tr -d '\r')
    CLIENT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query 'addonProfiles.azureKeyvaultSecretsProvider.identity.clientId')
    TENANT_ID=$(az keyvault show --resource-group $RESOURCE_GROUP --name $AKV_NAME --query 'properties.tenantId')
    
    az keyvault set-policy --name $AKV_NAME --object-id $OBJECT_ID --secret-permissions get list
    
  4. 选择如何提供 TLS 证书。

    Azure 密钥保管库支持不同的对象类型。 对于本演练,请选择以下端到端选项 之一 ,然后继续执行常见验证和网关步骤。

    选项 使用此选项时 密钥保管库存储 Kubernetes 机密结果
    选项 1:将证书和密钥存储为密钥保管库机密 你有单独的 PEM 证书 (.crt) 和私钥 (.key) 文件,如本文前面生成的文件。 两个 密钥保管库 机密。 一个已同步的 Kubernetes TLS 机密命名 httpbin-credential
    选项 2:直接引用密钥保管库证书对象 证书已作为证书对象(如导入.pfx)存储在密钥保管库中。 一个密钥保管库证书对象。 一个已同步的 Kubernetes TLS 机密命名 httpbin-credential

    Important

    不要遵循这两个选项。 如果使用选项 1,请将证书和密钥文件作为密钥保管库机密上传,然后应用选项 1 SecretProviderClass 清单。 如果您使用选项 2,请跳过 密钥保管库 机密上传步骤,并应用选项 2 的 SecretProviderClass 清单。

选项 1:将证书和密钥存储为密钥保管库机密

如果具有单独的 PEM 证书(.crt)和私钥(.key)文件(如本文前面生成的文件),请使用此选项。

  1. 将证书和密钥文件作为密钥保管库机密上传:

    az keyvault secret set --vault-name $AKV_NAME --name test-httpbin-key --file httpbin_certs/httpbin.example.com.key
    az keyvault secret set --vault-name $AKV_NAME --name test-httpbin-crt --file httpbin_certs/httpbin.example.com.crt
    

    注释

    每次轮换(更改)证书或密钥时,请重新运行此步骤以将新版本作为机密上传。 若要自动将更新后的值同步到群集,请在用于 Secrets Store CSI Driver 的 Azure 密钥保管库 提供程序上启用自动轮换。 有关详细信息,请参阅 自动轮换和机密同步概述。 如果希望密钥保管库管理证书生命周期,请改用选项 2

  2. 创建引用你在 密钥保管库 中创建的两个机密的 SecretProviderClass

    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: httpbin-credential-spc
    spec:
      provider: azure
      secretObjects:
      - secretName: httpbin-credential
        type: kubernetes.io/tls
        data:
        - objectName: test-httpbin-key
          key: tls.key
        - objectName: test-httpbin-crt
          key: tls.crt
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-httpbin-key
              objectType: secret
              objectAlias: "test-httpbin-key"
            - |
              objectName: test-httpbin-crt
              objectType: secret
              objectAlias: "test-httpbin-crt"
        tenantId: $TENANT_ID
    EOF
    

选项 2:直接引用密钥保管库证书对象

当证书已作为证书对象存储在 Azure 密钥保管库 中时,请使用此选项。 无需将 .crt.key 文件作为单独的 密钥保管库 机密上传。

在此示例中,test-httpbin-cert-pfx 是Azure 密钥保管库中的证书对象的名称。 要将现有证书作为证书对象导入密钥保管库,请参阅将证书导入到密钥保管库。 有关对象参数的详细信息,请参阅 获取证书和密钥

  1. 创建引用 密钥保管库 证书对象的 SecretProviderClass

    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: httpbin-credential-spc
    spec:
      provider: azure
      secretObjects:
      - secretName: httpbin-credential
        type: kubernetes.io/tls
        data:
        - objectName: test-httpbin-key
          key: tls.key
        - objectName: test-httpbin-crt
          key: tls.crt
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-httpbin-cert-pfx  #certificate object name from keyvault
              objectType: secret
              objectAlias: "test-httpbin-key"
            - |
              objectName: test-httpbin-cert-pfx #certificate object name from keyvault
              objectType: cert
              objectAlias: "test-httpbin-crt"
        tenantId: $TENANT_ID
    EOF
    

将机密同步到群集

完成选项 1 或选项 2 后,部署示例 Pod 以将机密同步到群集。 机密存储 CSI 驱动程序需要 Pod 来引用SecretProviderClass资源,以便机密从Azure 密钥保管库同步到群集。

  1. 使用以下清单部署示例 Pod:

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: secrets-store-sync-httpbin
    spec:
      containers:
        - name: busybox
          image: mcr.azk8s.cn/oss/busybox/busybox:1.33.1
          command:
            - "/bin/sleep"
            - "10"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "httpbin-credential-spc"
    EOF
    
    • 验证 httpbin-credential 机密是否在 default SecretProviderClass 资源中定义的命名空间中创建。

      kubectl describe secret/httpbin-credential
      

      示例输出:

      Name:         httpbin-credential
      Namespace:    default
      Labels:       secrets-store.csi.k8s.io/managed=true
      Annotations:  <none>
      
      Type:  kubernetes.io/tls
      
      Data
      ====
      tls.crt:  1180 bytes
      tls.key:  1675 bytes
      

部署 TLS 网关

  1. 创建一个 Kubernetes 网关,在 TLS 配置下引用 httpbin-credential 机密。

    cat <<EOF | kubectl apply -f -
    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: httpbin-gateway
    spec:
      gatewayClassName: approuting-istio
      listeners:
      - name: https
        hostname: "httpbin.example.com"
        port: 443
        protocol: HTTPS
        tls:
          mode: Terminate
          certificateRefs:
          - name: httpbin-credential
        allowedRoutes:
          namespaces:
            from: Selector
            selector:
              matchLabels:
                kubernetes.io/metadata.name: default
    EOF
    

    然后,创建一个对应的 HTTPRoute 来配置网关的入口流量路由。

    cat <<EOF | kubectl apply -f -
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: httpbin
    spec:
      parentRefs:
      - name: httpbin-gateway
      hostnames: ["httpbin.example.com"]
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /status
        - path:
            type: PathPrefix
            value: /delay
        backendRefs:
        - name: httpbin
          port: 8000
    EOF
    

    获取网关地址和端口:

    kubectl wait --for=condition=programmed gateways.gateway.networking.k8s.io httpbin-gateway
    export INGRESS_HOST=$(kubectl get gateways.gateway.networking.k8s.io httpbin-gateway -o jsonpath='{.status.addresses[0].value}')
    export SECURE_INGRESS_PORT=$(kubectl get gateways.gateway.networking.k8s.io httpbin-gateway -o jsonpath='{.spec.listeners[?(@.name=="https")].port}')
    
  2. 发送 HTTPS 请求以访问 httpbin 服务:

    curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
    --cacert httpbin_certs/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    

    应会看到 httpbin 服务返回 418 "我是一个茶壶" 代码。