AWS EKS 部署

本文探讨了如何根据特定于 AWS 和弹性 Kubernetes 服务 (Elastic Kubernetes Service - EKS) 调整您的 Bitwarden 自托管 Helm Chart 部署

请注意,本文中记录的某些附加组件要求您的 EKS 集群至少已启动一个节点。

入口控制器

默认情况下, my-values.yaml 中定义了一个 nginx 控制器,并且需要一个 AWS 网络负载均衡器。目前不推荐使用 AWS 应用负载均衡器 (Application Load Balancer - ALB),因为它们不支持路径重写和基于路径的路由。

以下假设您在 AWS 证书管理器中保存有一个 SSL 证书,因为您将需要证书 Amazon 资源名称 (ARN)。

您的集群中还必须至少有一个节点正在运行。

要将网络负载均衡器连接到您的集群:

1、按照这些说明创建一个 IAM 策略和角色,然后在集群中安装 AWS 负载均衡器控制器。

2、运行以下命令为您的集群设置一个入口控制器。这将创建一个 AWS 网络负载均衡器。请注意,在此示例命令中,有些值必须替换,以配置为满足您的需求:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm upgrade ingress-nginx ingress-nginx/ingress-nginx -i \
  --namespace kube-system \
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-backend-protocol'="ssl" \
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-cross-zone-load-balancing-enabled'="true" \
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-type'="external" \
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-nlb-target-type'="instance" \
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-scheme'="internet-facing" \
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-ssl-cert'="arn:aws:acm:REPLACEME:REPLACEME:certificate/REPLACEME" \ #Replace with the ARN for your certificate
  --set-string controller.service.annotations.'service\.beta\.kubernetes\.io/aws-load-balancer-ssl-ports'="443" \
  --set controller.service.externalTrafficPolicy="Local"

3、根据以下示例更新您的 my-values.yaml 文件,确保替换所有 REPLACE 占位符:

general:
  domain: "REPLACEME.com"
  ingress:
    enabled: true
    className: "nginx"
     ## - Annotations to add to the Ingress resource
    annotations:
      nginx.ingress.kubernetes.io/ssl-redirect: "true"
      nginx.ingress.kubernetes.io/use-regex: "true"
      nginx.ingress.kubernetes.io/rewrite-target: /$1
    ## - Labels to add to the Ingress resource
    labels: {}
    # Certificate options
    tls:
      # TLS certificate secret name
      name: # Handled via the NLB defined in the ingress controller
      # Cluster cert issuer (ex. Let's Encrypt) name if one exists
      clusterIssuer:
    paths:
      web:
        path: /(.*)
        pathType: ImplementationSpecific
      attachments:
        path: /attachments/(.*)
        pathType: ImplementationSpecific
      api:
        path: /api/(.*)
        pathType: ImplementationSpecific
      icons:
        path: /icons/(.*)
        pathType: ImplementationSpecific
      notifications:
        path: /notifications/(.*)
        pathType: ImplementationSpecific
      events:
        path: /events/(.*)
        pathType: ImplementationSpecific
      scim:
        path: /scim/(.*)
        pathType: ImplementationSpecific
      sso:
        path: /(sso/.*)
        pathType: ImplementationSpecific
      identity:
        path: /(identity/.*)
        pathType: ImplementationSpecific
      admin:
        path: /(admin/?.*)
        pathType: ImplementationSpecific

创建存储类

部署需要您提供的共享存储类,该存储类必须支持 ReadWriteMany。以下是如何创建符合要求的存储类的示例:

以下假设您已创建了一个 AWS 弹性文件系统 (Elastic File System - EFS)。如果还没有,请立即创建一个。无论哪种情况,请记下您的 EFS 文件系统 ID,在此过程中您将需要用到它。

1、获取适用于您的 EKS 集群的 Amazon EFS CSI 驱动程序插件。这将要求您为您的集群创建一个 OIDC 提供程序,并为驱动程序创建一个 IAM 角色

2、在 AWS CloudShell 中,替换以下脚本中的 file_system_id= "REPLACE" 变量并在 AWS CloudShell 中运行它:

以下只是一个说明性示例,请务必根据您自己的安全需求分配权限。

file_system_id="REPLACE"

cat << EOF | kubectl apply -n bitwarden -f -
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: shared-storage
provisioner: efs.csi.aws.com
parameters:
  provisioningMode: efs-ap
  fileSystemId: $file_system_id
  directoryPerms: "777" # Change for your use case
  uid: "2000" # Change for your use case
  gid: "2000" # Change for your use case
  basePath: "/dyn1"
  subPathPattern: "\${.PVC.name}"
  ensureUniqueDirectory: "false"
  reuseAccessPoint: "false"
mountOptions:
  - iam
  - tls
EOF

3、将 my-values.yaml 中的 sharedStorageClassName 的值设置为您在 metadata.name: 中指定的类的名称,例如:

sharedStorageClassName: "shared-storage"

使用 AWS Secrets Manager

部署需要使用 Kubernetes secrets 对象来设置敏感值。虽然可以使用 kubectl create secret 命令来设置 secrets,但 AWS 客户可能倾向于使用 AWS Secrets Manager 和适用于 Kubernetes Secrets Store CSI 驱动程序的 AWS Secrets 和 Configuration Provider (ACSP)。

您需要将以下机密存储在 AWS Secrets Manager 中。请注意,您可以更改此处使用的密钥,但如果您这样做,还必须对后续步骤进行更改:

1、安全存储您的机密后,安装 ACSP

2、创建权限策略以允许访问您的机密。该策略必须授予 secretsmanager:GetSecretValuesecretsmanager:DescribeSecret 权限,例如:

{
   "Version": "2012-10-17",
   "Statement": {
     "Effect": "Allow",
     "Action": [
       "secretsmanager:DescribeSecret",
       "secretsmanager:GetSecretValue"
     ],
     "Resource": "arn:aws:secretsmanager:REPLACEME:REPLACEME:secret:REPLACEME"
   }
}

3、创建一个可以通过已创建的权限策略访问您的机密的服务账户,例如:

CLUSTER_NAME="REPLACE"
ACCOUNT_ID="REPLACE" # replace with your AWS account ID
ROLE_NAME="REPLACE" # name of a role that will be created in IAM
POLICY_NAME="REPLACE" # the name of the policy you created earlier
eksctl create iamserviceaccount \
  --cluster=$CLUSTER_NAME \
  --namespace=bitwarden \
  --name=bitwarden-sa \
  --role-name $ROLE_NAME \
  --attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/$POLICY_NAME \
  --approve

4、接下来,创建 SecretProviderClass,如以下示例所示。请务必将 region 替换为您所在的区域,并将objectName 替换为您创建的 Secrets Manager 机密的名称(步骤 1):

cat <<EOF | kubectl apply -n bitwarden -f -
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: bitwarden-secrets-manager-csi
  labels:
    app.kubernetes.io/component: secrets
  annotations:
spec:
  provider: aws
  parameters:
    region: REPLACE
    objects: |
      - objectName: "REPLACE"
        objectType: "secretsmanager"
        objectVersionLabel: "AWSCURRENT"
        jmesPath:
          - path: installationid
            objectAlias: installationid
          - path: installationkey
            objectAlias: installationkey
          - path: smtpusername
            objectAlias: smtpusername
          - path: smtppassword
            objectAlias: smtppassword
          - path: yubicoclientid
            objectAlias: yubicoclientid
          - path: yubicokey
            objectAlias: yubicokey
          - path: hibpapikey
             objectAlias: hibpapikey
          - path: sapassword #-OR- dbconnectionstring if external SQL
            objectAlias: sapassword #-OR- dbconnectionstring if external SQL
  secretObjects:
  - secretName: "bitwarden-secret"
    type: Opaque
    data:
    - objectName: installationid
       key: globalSettings__installation__id
    - objectName: installationkey
       key: globalSettings__installation__key
    - objectName: smtpusername
       key: globalSettings__mail__smtp__username
    - objectName: smtppassword
       key: globalSettings__mail__smtp__password
    - objectName: yubicoclientid
       key: globalSettings__yubico__clientId
    - objectName: yubicokey
       key: globalSettings__yubico__key
    - objectName: hibpapikey
       key: globalSettings__hibpApiKey
    - objectName: sapassword #-OR- dbconnectionstring if external SQL
       key: SA_PASSWORD #-OR- globalSettings__sqlServer__connectionString if external SQL
EOF

5、在您的 my-values.yaml 文件中,设置以下值:

  • secrets.secretName :设置为 SecretProviderClass 中定义的 secretName (步骤 3)。

  • secrets.secretProviderClass :设置为 SecretProviderClass 中定义的 metedata.name (第 3 步)。

  • component.admin.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.api.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.attachments.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.events.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.icons.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.identity.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.notifications.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.scim.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.sso.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • component.web.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • database.podServiceAccount :设置为为您的服务账户定义的名称(步骤 2)。

  • serviceAccount.name :设置为为您的服务账户定义的名称(步骤 2)。

  • serviceAccount.deployRolesOnly :设置为 true

最后更新于