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>
使用 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
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 资源。
流程
生成一个私钥来为
ServiceAccount
对象签名:$ openssl genrsa -out sa-signer 4096
生成
ServiceAccount
对象公钥:$ openssl rsa -in sa-signer -pubout -out sa-signer.pub
创建 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
参数。保留 S3 存储桶 URL:
OPENID_BUCKET_URL="https://<oidc_bucket_name>.s3.<aws_region>.amazonaws.com"
构建 OIDC 配置:
创建一个名为
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 中可以使用的字符。
创建一个名为
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" ] }
上传 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 存储桶。允许 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
创建 AWS IAM OIDC 身份提供程序:
从托管 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}'
在链根目录计算证书的指纹:
$ export BUCKET_FINGERPRINT=$(openssl x509 -in cert<number>.pem -fingerprint -noout | sed -e 's/.*Fingerprint=//' -e 's/://g')
其中
<number>
为保存的文件中的最高数字。例如,如果2
是保存文件中的最高数字,则使用cert2.pem
。创建身份提供程序:
$ aws iam create-open-id-connect-provider --url $OPENID_BUCKET_URL --thumbprint-list $BUCKET_FINGERPRINT --client-id-list openshift sts.amazonaws.com
-
保留新创建的身份提供程序返回的 ARN。这个 ARN 稍后被称为
<aws_iam_openid_arn>
。
生成 IAM 角色:
针对您要部署到的云,找到此发行版本镜像中的所有
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 版本对应的数字。对于每个
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。要进一步限制角色,例如只有特定集群的
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" ] } }
对于每个 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 } } } ] } }
准备运行 OpenShift Container Platform 安装程序:
创建
install-config.yaml
文件:$ ./openshift-install create install-config
将集群配置使用 CCO 以手动模式安装:
$ echo "credentialsMode: Manual" >> install-config.yaml
创建安装清单:
$ ./openshift-install create manifests
创建
tls
目录,复制之前生成的私钥:注意目标文件名必须是
./tls/bound-service-account-signing-key.key
。$ mkdir tls ; cp <path_to_service_account_signer> ./tls/bound-service-account-signing-key.key
使用名为
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
对于从发行镜像中提取的每个
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. 验证安装
- 连接到 OpenShift Container Platform 集群。
验证集群没有
root
凭证:$ oc get secrets -n kube-system aws-creds
输出应类似于:
Error from server (NotFound): secrets "aws-creds" not found
验证组件是否假定 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