在 Hashicorp Vault 中存储 Cassandra Secret

在 Hashicorp Vault 中存储 Cassandra Secret

此功能可让您在外部密钥管理器 Hashicorp Vault 中存储 Apigee Hybrid 的 Cassandra 数据库凭据。外部密钥管理器可让您管理 Secret 在 Kubernetes 中的存储方式,包括管理数据驻留和精细的访问权限控制。

在 Apigee Hybrid 1.10 版之前,为 Cassandra 用户提供密码的唯一方法是在 override.yaml 中指定密码。这些密码存储在 Kubernetes Secret 中。例如:

cassandra:
  auth:
    default:
      password: "********"
    admin:
      password: "********"
    ddl:
      password: "********"
    dml:
      password: "********"
    jmx:
      username: "jmxuser"
      password: "********"
    jolokia:
      username: "apigee"
      password: "********"

使用 Hashicorp Vault,您可以通过 Kubernetes Secrets Store CSI 驱动程序 API (SecretProviderClass) 提供这些密码。这允许 Kubernetes 装载存储在外部 Vault 中的多个 Secret、密钥和证书。

Cassandra 用户和密码

您需要为以下 Cassandra 用户创建 Secret。更改默认值以满足组织的安全政策。

Cassandra 用户 默认用户名 默认密码
管理员 admin_user "********"
DDL ddl_user "********"
默认 cassandra 注意:默认用户名必须始终为“cassandra” "********"
DML dml_user "********"
JMX "jmxuser" "********"
Jolokia "apigee" "********"

如需了解详情,请参阅 cassandra 配置属性

配置外部 Secret 集成

为 Apigee Hybrid 设置 Vault 集成包括以下步骤。

  • 在前两个步骤中,您直接与 Vault 交互。
  • 在第三步和第四步中,您将配置应用于 Kubernetes 集群。

按照以下步骤在 Vault 中创建 Secret,并使 Hybrid 安装可以访问它们。

创建 Vault Secret、政策和角色

  1. 验证当前的 Kubernetes 上下文设置为您的集群:
    kubectl config current-context
  2. 使用 Vault API、CLI 或界面创建 Cassandra Secret。您创建的 Secret 值必须与集群中当前使用的 Cassandra 用户名和密码相匹配。
    • 密钥:可以使用任何密钥(或多个密钥的组合),例如:
      secret/data/apigee/cassandra
    • Secret 数据:Apigee Hybrid 要求以下 Cassandra 用户的用户名和密码:
      Cassandra 用户
      管理员
      DDL
      默认
      DML
      JMX
      Jolokia
      这些用户名和密码值可以分布在任意数量的密钥中。
    • Vault CLI:以下命令介绍如何创建包含所有必需用户名和密码的 Secret:
      vault kv put secret/apigee/cassandra \
          adminUsername="ADMIN_USERNAME" \
          adminPassword="ADMIN_PASSWORD" \
          ddlUsername="DDL_USERNAME" \
          ddlPassword="DDL_PASSWORD" \
          defaultUsername="cassandra" \
          defaultPassword="DEFAULT_PASSWORD" \
          dmlUsername="DML_USERNAME" \
          dmlPassword="DML_PASSWORD" \
          jmxUsername="JMX_USERNAME" \
          jmxPassword="JMX_PASSWORD" \
          jolokiaUsername="JOLOKIA_USERNAME" \
          jolokiaPassword="JOLOKIA_PASSWORD"
      每个用户的默认用户名如下所示:
      Cassandra 用户 默认值
      管理员 admin_user
      DDL ddl_user
      默认 cassandra
      DML dml_user
      JMX jmxuser
      Jolokia apigee
  3. 在 Vault 中,创建政策以授予对您刚刚创建的 Secret 的访问权限。
    1. 创建一个政策文件(建议的名称:apigee-cassandra-auth.txt),其中包含以下内容:
      path "secret/data/apigee/cassandra" {
        capabilities = ["read"]
      }
      如果您创建了多个 Secret,则每个 Secret 都必须添加到政策文件中:
      path "secret/data/apigee/cassandra/admin" {
        capabilities = ["read"]
      }
      
      path "secret/data/apigee/cassandra/ddl" {
        capabilities = ["read"]
      }
    2. 将该政策应用于 Vault:
      vault policy write apigee-cassandra-auth apigee-cassandra-auth.txt

      可使用标准输入取代从文件读取来创建政策:

      echo 'path "secret/data/apigee/cassandra" { capabilities = ["read"] }' | vault policy write apigee-cassandra-auth -
  4. 将该政策绑定到 Apigee Cassandra Kubernetes 服务账号。
    1. 定义以下环境变量:
      export ORG_NAME=APIGEE_ORG_NAME
      export ENVS_LIST=LIST_OF_APIGEE-ENVS
      export APIGEE_NAMESPACE=YOUR_APIGEE_NAMESPACE
      export NAMESPACES=apigee-system,${APIGEE_NAMESPACE}

      其中:

      • ORG_NAME 是您的 Apigee 组织的名称。
      • ENVS_LIST 是您的 Apigee 环境的逗号分隔列表,例如 dev,prod
      • APIGEE_NAMESPACE 是您的 Apigee 命名空间。默认值为 apigee
      • NAMESPACES 是 Apigee、apigee-system 和您的 Apigee 命名空间的命名空间逗号分隔列表。
    2. 创建一个包含以下内容的脚本:脚本可以采用任何名称。在以下示例中,脚本的名称为 create-vault-cassandra-role.sh
      # create-vault-cassandra-role.sh
      
      ORG=ORG_NAME  # ORG name
      ENVS=ENVS_LIST # comma separated env names, for example: dev,prod
      
      org_short_name=$(echo $ORG | head -c 15)
      encode=$(echo -n $ORG | shasum -a 256 | head -c 7)
      org_encode=$(echo "$org_short_name-$encode")
      names=apigee-manager,apigee-cassandra-default,apigee-cassandra-backup-sa,apigee-cassandra-restore-sa,apigee-cassandra-schema-setup-${org_encode},apigee-cassandra-schema-val-${org_encode},apigee-cassandra-user-setup-${org_encode},apigee-mart-${org_encode},apigee-mint-task-scheduler-${org_encode}
      
      for env in ${ENVS//,/ }
      do
        env_short_name=$(echo $env | head -c 15)
        encode=$(echo -n $ORG:$env | shasum -a 256 | head -c 7)
        env_encode=$(echo "$org_short_name-$env_short_name-$encode")
        names+=,apigee-synchronizer-${env_encode},apigee-runtime-${env_encode}
      done
      
      echo $names
      
    3. 运行脚本并将输出分配给 SERVICE_ACCOUNT_NAMES 变量。这将创建 Kubernetes 服务账号名称的逗号分隔列表。
      export SERVICE_ACCOUNT_NAMES=$(./create-vault-cassandra-role)

      检查该变量是否已填充列表:

      echo $SERVICE_ACCOUNT_NAMES
    4. 使用 Vault CLI 创建将政策绑定到 Kubernetes 服务账号的角色:
      vault write auth/kubernetes/role/cassandra \
          bound_service_account_names=${SERVICE_ACCOUNT_NAMES} \
          bound_service_account_namespaces=${NAMESPACES} \
          policies=apigee-cassandra-auth \
          ttl=1m

安装 CSI 驱动程序和 Vault 提供程序

Apigee Hybrid v1.12.3 支持以下 Helm 图表版本:

软件 版本
Secret Store CSI 驱动程序 v1.3.4
保险柜 v0.25.0
  1. 按照 Secrets Store CSI 驱动程序安装说明在您的集群上安装 CSI 驱动程序。CSI 驱动程序具有用于安装的 Helm 图表。
  2. 如果您尚未安装 Vault CSI 提供程序,请按照安装 Vault CSI 提供程序中的说明操作。

创建 SecretProviderClass 对象

SecretProviderClass 资源告知 CSI 驱动程序在请求 Secret 时要通信的提供程序。Cassandra 用户的凭据必须通过此对象进行配置。下表显示了 Apigee Cassandra 预期的文件名 (objectName):

Cassandra 用户 预期的 Secret 文件名
管理员 adminUsernameadminPassword
DDL ddlUsernameddlPassword
默认 cassandradefaultPassword
DML dmlUsernamedmlPassword
JMX jmxUsernamejmxPassword
Jolokia jolokiaUsernamejolokiaPassword
  1. 为您的 SecretProviderClass 创建一个 YAML 文件。文件可以采用任何名称,例如 spc.yaml。使用以下 SecretProviderClass 模板配置此资源:
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: apigee-cassandra-auth-spc
    spec:
      provider: vault
      parameters:
        roleName: apigee-cassandra-auth  # the roleName should match the vault role you created earlier in this procedure
    
        # vaultAddress is the endpoint your Vault server is running at.
        # If Vault is running in the same cluster as Apigee, the format will generally be:
        # http://vault.<namespace>.svc.cluster.local:<vaultServicePort>
        vaultAddress: VAULT_ADDRESS
    
        # "objectName" is an alias used within the SecretProviderClass to reference
        # that specific secret. This will also be the filename containing the secret.
        # Apigee Cassandra expects these exact values so they must not be changed.
        # "secretPath" is the path in Vault where the secret should be retrieved.
        # "secretKey" is the key within the Vault secret response to extract a value from.
        # For example, if the Vault secret is located at `secret/data/apigee/cassandra`
        # and you want to specify the admin password, you would use the following:
        # - objectName: "adminPassword"
        #   secretPath: "secret/data/apigee/cassandra"
        #   secretKey: "key within Vault secret specifying the admin password"
        objects: |
          - objectName: "adminUsername"
            secretPath: ""
            secretKey: ""
          - objectName: "adminPassword"
            secretPath: ""
            secretKey: ""
          - objectName: "defaultUsername"
            secretPath: ""
            secretKey: ""
          - objectName: "defaultPassword"
            secretPath: ""
            secretKey: ""
          - objectName: "ddlUsername"
            secretPath: ""
            secretKey: ""
          - objectName: "ddlPassword"
            secretPath: ""
            secretKey: ""
          - objectName: "dmlUsername"
            secretPath: ""
            secretKey: ""
          - objectName: "dmlPassword"
            secretPath: ""
            secretKey: ""
          - objectName: "jolokiaUsername"
            secretPath: ""
            secretKey: ""
          - objectName: "jolokiaPassword"
            secretPath: ""
            secretKey: ""
          - objectName: "jmxUsername"
            secretPath: ""
            secretKey: ""
          - objectName: "jmxPassword"
            secretPath: ""
            secretKey: ""
  2. SecretProviderClass 应用于您的 apigeeapigee-system 命名空间。在以下命令中,命名空间为 apigeeapigee-system。如果您使用不同的命名空间,请替换这些值:
    kubectl -n apigee apply -f spc.yaml
    kubectl -n apigee-system apply -f spc.yaml

为 Cassandra 启用外部 Secret

  1. overrides.yaml 中添加以下配置,以便为 Cassandra 启用外部 Secret:
    cassandra:
      auth:
        secretProviderClass: apigee-cassandra-auth-spc  # The name of the SecretProviderClass created in spc.yaml.

    请参阅 cassandra.auth.secretProviderClass

  2. 使用 helm upgrade 将更改应用于 apigee-operatorapigee-datastore 组件:
    • apigee-operator 中的数据存储区控制器参与区域扩展期间的 Cassandra 停用和数据复制。这些任务需要 JMX 和 Jolokia 凭据。
      helm upgrade operator apigee-operator/ \
        --namespace apigee-system \
        --atomic \
        -f overrides.yaml
    • apigee-datastore 提供在连接到 Cassandra 时,apigee-runtime、Synchronizer 和 MART 等下游组件使用的凭据。
      helm upgrade datastore apigee-datastore/ \
        --namespace apigee \
        --atomic \
        -f overrides.yaml
  3. 验证是否使用了外部 Secret。启用外部 Secret 后,系统会添加引用这些 Secret 的新 VolumeVolume MountEnvironment Variable
    • 验证 apigee-controller-manager Deployment。

      检查名为 apigee-external-secretsVolume 是否存在,以及引用上面创建的 SecretProviderClass

      kubectl -n apigee-system get deployment apigee-controller-manager -o jsonpath='{.spec.template.spec.volumes[?(@.name=="apigee-external-secrets")]}'
      {
        "csi": {
          "driver": "secrets-store.csi.k8s.io",
          "readOnly": true,
          "volumeAttributes": {
            "secretProviderClass": "apigee-cassandra-auth-spc"
          }
        },
        "name": "apigee-external-secrets"
      }

      检查名为 apigee-external-secretsVolumeMount 是否存在:

      kubectl -n apigee-system get deployment apigee-controller-manager -o jsonpath='{.spec.template.spec.containers[?(@.name=="manager")].volumeMounts[?(@.name=="apigee-external-secrets")]}'
      {
        "mountPath": "/opt/apigee/externalsecrets",
        "name": "apigee-external-secrets",
        "readOnly": true
      }

      检查是否存在引用外部 Secret 的 Environment Variable

      kubectl -n apigee-system get deployment apigee-controller-manager -o jsonpath='{.spec.template.spec.containers[?(@.name=="manager")].env}'
      [
        ...
        {
          "name": "CASSANDRA_JOLOKIA_USERNAME_PATH",
          "value": "/opt/apigee/externalsecrets/jolokiaUsername"
        },
        {
          "name": "CASSANDRA_JOLOKIA_PASSWORD_PATH",
          "value": "/opt/apigee/externalsecrets/jolokiaPassword"
        }
      ]

回滚到 K8s Secret

  1. 如需还原为非外部 Secret,请移除 overrides.yaml 中的 secretProviderClass 配置并使用先前的配置:
    cassandra:
          auth:
            secretProviderClass: apigee-cassandra-auth-spc # remove this line
  2. 使用 helm upgrade 将更改应用于 apigee-operatorapigee-datastore 组件:
    helm upgrade operator apigee-operator/ \
      --namespace apigee-system \
      --atomic \
      -f overrides.yaml
    helm upgrade datastore apigee-datastore/ \
      --namespace apigee \
      --atomic \
      -f overrides.yaml

问题排查:创建用于调试的客户端容器

如果您使用的是 Vault,则本部分将取代问题排查部分创建用于调试的客户端容器中的说明。

本部分介绍如何创建客户端容器,您可从中访问 Cassandra 调试实用程序(如 cqlsh)。这些实用程序可让您查询 Cassandra 表,并且可用于调试。

创建客户端容器

如需创建客户端容器,请按以下步骤操作:

  1. 容器使用 apigee-cassandra-user-setup Pod 中的 TLS 证书。第一步是提取此证书名称:
    kubectl get secrets -n apigee --field-selector type=kubernetes.io/tls | grep apigee-cassandra-user-setup | awk '{print $1}'

    此命令返回证书名称。例如:apigee-cassandra-user-setup-rg-hybrid-b7d3b9c-tls

  2. 打开一个新文件,并将以下 pod 规范粘贴到其中:
    apiVersion: v1
      kind: Pod
      metadata:
        labels:
        name: CASSANDRA_CLIENT_NAME   # For example: my-cassandra-client
        namespace: apigee
      spec:
        containers:
        - name: CASSANDRA_CLIENT_NAME
          image: "gcr.io/apigee-release/hybrid/apigee-hybrid-cassandra-client:1.12.3"
          imagePullPolicy: Always
          command:
          - sleep
          - "3600"
          env:
          - name: CASSANDRA_SEEDS
            value: apigee-cassandra-default.apigee.svc.cluster.local
          - name: APIGEE_DML_USERNAME_PATH
            value: /opt/apigee/externalsecrets/dmlUsername
          - name: APIGEE_DML_PASSWORD_PATH
            value: /opt/apigee/externalsecrets/dmlPassword
          volumeMounts:
          - mountPath: /opt/apigee/ssl
            name: tls-volume
            readOnly: true
          - name: apigee-external-secrets
            mountPath: /opt/apigee/externalsecrets
            readOnly: true
        volumes:
        - name: tls-volume
          secret:
            defaultMode: 420
            secretName: apigee-cassandra-user-setup-vaibhavhybridor-8b3e61d-tls
        - name: apigee-external-secrets
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: apigee-cass-password
        serviceAccount: apigee-cassandra-default
        serviceAccountName: apigee-cassandra-default
        restartPolicy: Never
  3. 使用 .yaml 扩展名保存文件。例如:my-spec.yaml
  4. 将规范应用到您的集群:
    kubectl apply -f my-spec.yaml -n apigee
  5. 登录容器:
    kubectl exec -n CASSANDRA_CLIENT_NAME -it -- bash
  6. 使用以下命令连接到 Cassandra cqlsh 接口。输入与以下内容完全一致的命令:
    APIGEE_DML_USER=$(cat "$APIGEE_DML_USERNAME_PATH")
    export APIGEE_DML_USER
    APIGEE_DML_PASSWORD=$(cat "$APIGEE_DML_PASSNAME_PATH")
    export APIGEE_DML_PASSWORD
    cqlsh ${CASSANDRA_SEEDS} -u ${APIGEE_DML_USER} -p ${APIGEE_DML_PASSWORD} --ssl

删除客户端 pod

使用以下命令删除 Cassandra 客户端 pod:

kubectl delete pods -n apigee cassandra-client