18.5. 在 STS 中使用手动模式

使用 STS 的手动模式作为 Amazon Web Services(AWS)的技术预览提供。

重要

对 AWS 安全令牌服务(STS)的支持只是一个技术预览功能。技术预览功能不被红帽产品服务等级协议 (SLA) 支持,且可能在功能方面有缺陷。红帽不推荐在生产环境中使用它们。这些技术预览功能可以使用户提早试用新的功能,并有机会在开发阶段提供反馈意见。

有关红帽技术预览功能支持范围的详情,请参阅 https://access.redhat.com/support/offerings/techpreview/

注意

此凭证策略只支持新的 OpenShift Container Platform 集群,且必须在安装过程中配置。您无法重新配置使用不同的凭证策略使用此功能的现有集群。

在带有 STS 的手动模式中,各个 OpenShift Container Platform 集群组件使用 AWS Secure Token Service(STS)来分配提供简短、有限权限安全凭证的组件 IAM 角色。这些凭证与特定于发布 AWS API 调用的每个组件的 IAM 角色关联。

使用适当配置的 AWS IAM OpenID Connect(OIDC)身份提供程序以及 AWS IAM 角色会自动请求新的和刷新的凭证。OpenShift Container Platform 为服务帐户令牌签名,这些令牌由 AWS IAM 信任,并可投射到 pod 中并用于身份验证。令牌会在一小时后刷新。

图 18.1. STS 验证流程

使用带有 STS 的手动模式更改为各个 OpenShift Container Platform 组件提供的 AWS 凭证的内容。

使用长期凭证的 AWS secret 格式

apiVersion: v1
kind: Secret
metadata:
  namespace: <target-namespace> 1
  name: <target-secret-name> 2
data:
  aws_access_key_id: <base64-encoded-access-key-id>
  aws_secret_access_key: <base64-encoded-secret-access-key>

1
组件的命名空间。
2
组件 secret 的名称。

使用 STS 的 AWS secret 格式

apiVersion: v1
kind: Secret
metadata:
  namespace: <target-namespace> 1
  name: <target-secret-name> 2
stringData:
  credentials: |-
    [default]
    role_name: <operator-role-name> 3
    web_identity_token_file: <path-to-token> 4

1
组件的命名空间。
2
组件 secret 的名称。
3
组件的 IAM 角色。
4
pod 中服务帐户令牌的路径。通常这是 OpenShift Container Platform 组件的 /var/run/secrets/openshift/serviceaccount/token

18.5.1. 使用 STS 为手动模式安装 OpenShift Container Platform 集群

安装配置为以手动模式与 OpenShift Container Platform 版本 4.7 中的 STS 搭配使用 CCO 的集群:

18.5.1.1. 手动创建 AWS 资源

要安装配置为在带有 STS 的手动模式中使用 CCO 的 OpenShift Container Platform 集群,您必须首先手动创建所需的 AWS 资源。

流程

  1. 生成一个私钥来为 ServiceAccount 对象签名:

    $ openssl genrsa -out sa-signer 4096
  2. 生成 ServiceAccount 对象公钥:

    $ openssl rsa -in sa-signer -pubout -out sa-signer.pub
  3. 创建 S3 存储桶以容纳 OIDC 配置:

    $ aws s3api create-bucket --bucket <oidc_bucket_name> --region <aws_region> --create-bucket-configuration LocationConstraint=<aws_region>
    注意

    如果 <aws_region> 的值是 us-east-1,不要指定 LocationConstraint 参数。

  4. 保留 S3 存储桶 URL:

    OPENID_BUCKET_URL="https://<oidc_bucket_name>.s3.<aws_region>.amazonaws.com"
  5. 构建 OIDC 配置:

    1. 创建一个名为 key.json 的文件,其包含以下信息:

      {
          "keys": [
              {
                  "use": "sig",
                  "kty": "RSA",
                  "kid": "<public_signing_key_id>",
                  "alg": "RS256",
                  "n": "<public_signing_key_modulus>",
                  "e": "<public_signing_key_exponent>"
              }
          ]
      }

      其中:

      • <public_signing_key_id> 是从公钥生成的:

        $ openssl rsa -in sa-signer.pub -pubin --outform DER | openssl dgst -binary -sha256 | openssl base64 | tr '/+' '_-' | tr -d '='

        该命令将公钥转换成 DER 格式,对其二进制代码执行 SHA-256 checksum,使用 base64 编码对数据进行编码,然后将 base64 编码的输出改为 base64URL 编码。

      • <public_signing_key_modulus> 是从公钥生成的:

        $ openssl rsa -pubin -in sa-signer.pub -modulus -noout | sed  -e 's/Modulus=//' | xxd -r -p | base64 -w0 | tr '/+' '_-' | tr -d '='

        该命令打印公钥的 modulus,提取 modulus 的十六进制代码,将 ASCII 十六进制转换为二进制代码,使用 base64 编码对数据进行编码,然后将 base64 编码的输出改为 base64URL 编码。

      • <public_signing_key_exponent> 是从公钥生成的:

        $ printf "%016x" $(openssl rsa -pubin -in sa-signer.pub -noout -text | grep Exponent | awk '{ print $2 }') |  awk '{ sub(/(00)+/, "", $1); print $1 }' | xxd -r -p | base64 -w0 | tr '/+' '_-' | tr -d '='

        该命令提取公钥 exponent 的十进制代码,如果需要会使用 0 填充的十六进制代码表示,移除开始的 00 对,将 ASCII 十六进制转换为二进制代码,使用 base64 编码对数据进行编码,然后将 base64 编码的输出修改为只使用 URL 中可以使用的字符。

    2. 创建一个名为 openid-configuration 的 文件,其包含以下信息:

      {
      	"issuer": "$OPENID_BUCKET_URL",
      	"jwks_uri": "${OPENID_BUCKET_URL}/keys.json",
          "response_types_supported": [
              "id_token"
          ],
          "subject_types_supported": [
              "public"
          ],
          "id_token_signing_alg_values_supported": [
              "RS256"
          ],
          "claims_supported": [
              "aud",
              "exp",
              "sub",
              "iat",
              "iss",
              "sub"
          ]
      }
  6. 上传 OIDC 配置:

    $ aws s3api put-object --bucket <oidc_bucket_name> --key keys.json --body ./keys.json
    $ aws s3api put-object --bucket <oidc_bucket_name> --key '.well-known/openid-configuration' --body ./openid-configuration

    其中 <oidc_bucket_name> 是为保存 OIDC 配置而创建的 S3 存储桶。

  7. 允许 AWS IAM OpenID Connect(OIDC)身份提供程序读取这些文件:

    $ aws s3api put-object-acl --bucket <oidc_bucket_name> --key keys.json --acl public-read
    $ aws s3api put-object-acl --bucket <oidc_bucket_name> --key '.well-known/openid-configuration' --acl public-read
  8. 创建 AWS IAM OIDC 身份提供程序:

    1. 从托管 OIDC 配置的服务器获取证书链:

      $ echo | openssl s_client -servername $<oidc_bucket_name>.s3.$<aws_region>.amazonaws.com -connect $<oidc_bucket_name>.s3.$<aws_region>.amazonaws.com:443 -showcerts 2>/dev/null | awk '/BEGIN/,/END/{ if(/BEGIN/){a++}; out="cert"a".pem"; print >out}'
    2. 在链根目录计算证书的指纹:

      $ export BUCKET_FINGERPRINT=$(openssl x509 -in cert<number>.pem -fingerprint -noout | sed -e 's/.*Fingerprint=//' -e 's/://g')

      其中 <number> 为保存的文件中的最高数字。例如,如果 2 是保存文件中的最高数字,则使用 cert2.pem

    3. 创建身份提供程序:

      $ aws iam create-open-id-connect-provider --url $OPENID_BUCKET_URL --thumbprint-list $BUCKET_FINGERPRINT --client-id-list openshift sts.amazonaws.com
    4. 保留新创建的身份提供程序返回的 ARN。这个 ARN 稍后被称为 <aws_iam_openid_arn>
  9. 生成 IAM 角色:

    1. 针对您要部署到的云,找到此发行版本镜像中的所有 CredentialsRequest CR:

      $ oc adm release extract quay.io/openshift-release-dev/ocp-release:4.<y>.<z>-x86_64 --credentials-requests --cloud=aws

      其中 <y><z> 是与您要安装的 OpenShift Container Platform 版本对应的数字。

    2. 对于每个 CredentialsRequest CR,创建一个类型为 Web identity 的 IAM 角色,使用以前创建的 IAM 身份供应商,授予必要的权限,并建立一个信任关系,以与之前创建的身份提供程序建立信任。

      例如,对于 0000_30_machine-api-operator_00_credentials-request.yaml 中的 CredentialsRequest CR,创建一个 IAM 角色,它可以接受来自为集群创建的 OIDC 供应商中的身份,类似如下:

      {
          "Role": {
              "Path": "/",
              "RoleName": "openshift-machine-api-aws-cloud-credentials",
              "RoleId": "ARSOMEROLEID",
              "Arn": "arn:aws:iam::123456789012:role/openshift-machine-api-aws-cloud-credentials",
              "CreateDate": "2021-01-06T15:54:13Z",
              "AssumeRolePolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                      {
                          "Effect": "Allow",
                          "Principal": {
                              "Federated": "<aws_iam_openid_arn>"
                          },
                          "Action": "sts:AssumeRoleWithWebIdentity",
                          "Condition": {
                              "StringEquals": {
                                  "<oidc_bucket_name>.s3.<aws_region>.amazonaws.com/$BUCKET_NAME:aud": "openshift"
                              }
                          }
                      }
                  ]
              },
              "Description": "OpenShift role for openshift-machine-api/aws-cloud-credentials",
              "MaxSessionDuration": 3600,
              "RoleLastUsed": {
                  "LastUsedDate": "2021-02-03T02:51:24Z",
                  "Region": "<aws_region>"
              }
          }
      }

      其中 <aws_iam_openid_arn> 是新创建的身份提供程序返回的 ARN。

    3. 要进一步限制角色,例如只有特定集群的 ServiceAccount 对象可以假设该角色,将 .Role.AssumeRolePolicyDocument.Statement[].Condition 字段更新至每个组件的特定 ServiceAccount 对象来修改各个角色的信任关系。

      • 修改 cluster-image-registry-operator 角色的信任关系,使其具有以下条件:

        "Condition": {
          "StringEquals": {
            "<oidc_bucket_name>.s3.<aws_region>.amazonaws.com:sub": [
              "system:serviceaccount:openshift-image-registry:registry",
              "system:serviceaccount:openshift-image-registry:cluster-image-registry-operator"
            ]
          }
        }
      • 修改 openshift-ingress-operator 的信任关系,使其具有以下条件:

        "Condition": {
          "StringEquals": {
            "<oidc_bucket_name>.s3.<aws_region>.amazonaws.com:sub": [
              "system:serviceaccount:openshift-ingress-operator:ingress-operator"
            ]
          }
        }
      • openshift-cluster-csi-drivers 的信任关系修改为具有以下条件:

        "Condition": {
          "StringEquals": {
            "<oidc_bucket_name>.s3.<aws_region>.amazonaws.com:sub": [
              "system:serviceaccount:openshift-cluster-csi-drivers:aws-ebs-csi-driver-operator",
              "system:serviceaccount:openshift-cluster-csi-drivers:aws-ebs-csi-driver-controller-sa"
            ]
          }
        }
      • 修改 openshift-machine-api 的信任关系,使其具有以下条件:

        "Condition": {
          "StringEquals": {
            "<oidc_bucket_name>.s3.<aws_region>.amazonaws.com:sub": [
              "system:serviceaccount:openshift-machine-api:machine-api-controllers"
            ]
          }
        }
  10. 对于每个 IAM 角色,请将 IAM 策略附加到角色,从对应的 CredentialsRequest 对象中反映所需的权限。

    例如,对于 openshift-machine-api,请附加类似如下的 IAM 策略:

    {
        "RoleName": "openshift-machine-api-aws-cloud-credentials",
        "PolicyName": "openshift-machine-api-aws-cloud-credentials",
        "PolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": [
                        "ec2:CreateTags",
                        "ec2:DescribeAvailabilityZones",
                        "ec2:DescribeDhcpOptions",
                        "ec2:DescribeImages",
                        "ec2:DescribeInstances",
                        "ec2:DescribeSecurityGroups",
                        "ec2:DescribeSubnets",
                        "ec2:DescribeVpcs",
                        "ec2:RunInstances",
                        "ec2:TerminateInstances",
                        "elasticloadbalancing:DescribeLoadBalancers",
                        "elasticloadbalancing:DescribeTargetGroups",
                        "elasticloadbalancing:RegisterInstancesWithLoadBalancer",
                        "elasticloadbalancing:RegisterTargets",
                        "iam:PassRole",
                        "iam:CreateServiceLinkedRole"
                    ],
                    "Resource": "*"
                },
                {
                    "Effect": "Allow",
                    "Action": [
                        "kms:Decrypt",
                        "kms:Encrypt",
                        "kms:GenerateDataKey",
                        "kms:GenerateDataKeyWithoutPlainText",
                        "kms:DescribeKey"
                    ],
                    "Resource": "*"
                },
                {
                    "Effect": "Allow",
                    "Action": [
                        "kms:RevokeGrant",
                        "kms:CreateGrant",
                        "kms:ListGrants"
                    ],
                    "Resource": "*",
                    "Condition": {
                        "Bool": {
                            "kms:GrantIsForAWSResource": true
                        }
                    }
                }
            ]
        }
    }
  11. 准备运行 OpenShift Container Platform 安装程序:

    1. 创建 install-config.yaml 文件:

      $ ./openshift-install create install-config
    2. 将集群配置使用 CCO 以手动模式安装:

      $ echo "credentialsMode: Manual" >> install-config.yaml
    3. 创建安装清单:

      $ ./openshift-install create manifests
    4. 创建 tls 目录,复制之前生成的私钥:

      注意

      目标文件名必须是 ./tls/bound-service-account-signing-key.key

      $ mkdir tls ; cp <path_to_service_account_signer> ./tls/bound-service-account-signing-key.key
    5. 使用名为 cluster-authentication-02-config.yaml 的文件创建自定义 Authentication CR:

      $ cat << EOF > manifests/cluster-authentication-02-config.yaml
      apiVersion: config.openshift.io/v1
      kind: Authentication
      metadata:
        name: cluster
      spec:
        serviceAccountIssuer: $OPENID_BUCKET_URL
      EOF
    6. 对于从发行镜像中提取的每个 CredentialsRequest CR,创建一个带有每个 CredentialsRequest 中指示的目标命名空间和目标名称的 secret,替换之前为每个组件创建的 AWS IAM 角色 ARN:

      openshift-machine-api 的 secret 清单示例:

      $ cat manifests/openshift-machine-api-aws-cloud-credentials-credentials.yaml
      apiVersion: v1
      stringData:
        credentials: |-
          [default]
          role_arn = arn:aws:iam::123456789012:role/openshift-machine-api-aws-cloud-credentials
          web_identity_token_file = /var/run/secrets/openshift/serviceaccount/token
      kind: Secret
      metadata:
        name: aws-cloud-credentials
        namespace: openshift-machine-api
      type: Opaque

18.5.1.2. 运行安装程序

  • 运行 OpenShift Container Platform 安装程序:

    $ ./openshift-install create cluster

18.5.1.3. 验证安装

  1. 连接到 OpenShift Container Platform 集群。
  2. 验证集群没有 root 凭证:

    $ oc get secrets -n kube-system aws-creds

    输出应类似于:

    Error from server (NotFound): secrets "aws-creds" not found
  3. 验证组件是否假定 secret 清单中指定的 IAM 角色,而不是使用由 CCO 创建的凭证:

    带有 Image Registry Operator 的命令示例

    $ oc get secrets -n openshift-image-registry installer-cloud-credentials -o json | jq -r .data.credentials | base64 --decode

    输出应显示组件使用的角色和 Web 身份令牌,如下所示:

    带有 Image Registry Operator 的输出示例

    [default]
    role_arn = arn:aws:iam::123456789:role/openshift-image-registry-installer-cloud-credentials
    web_identity_token_file = /var/run/secrets/openshift/serviceaccount/token