AWS STSを使って一時的な認証情報を扱うOpenShift on AWS環境を構築する

こんにちは、Red Hatでソリューションアーキテクトをしている北村です。

今回はAWS環境でOpenShiftを構築・利用する際の認証情報の扱いについて考えていきます。

みなさんはOpenShift上のコンテナからAWSを操作する時、どのような形で権限を付与していますか?よくあるパターンとしてIAMユーザーのアクセスキーとシークレットキーをSecretで環境変数としてアタッチすることが多いかと思います。

しかしAWSでのベストプラクティスでは、システム上での認証はIAMユーザーでなくIAMロールやAWS STS(Security Token Service)による一時的な形での付与を推奨されています。また企業のセキュリティ規約上、システムリソースにIAMユーザーを使うことを明示的にNGにしていることもあるかと思います。

そこで、OpenShift上のコンテナに対して、IAMユーザーの認証情報ではなく、STSを使った一時的な認証情報を渡す方法についてご紹介します。

まずは今回のキーコンポーネントであるCloud Credential Operatorの説明から。

Cloud Credential Operator(CCO)

Cloud Credential Operator(CCO)はOpenShiftの管理Operatorで、IPIインストールした場合はデフォルトでクラスターにインストールされています。 CCOはクラウドの認証情報をCredentialsRequestというカスタムリソースで管理しており、このリソース内に記述したIAMポリシー情報や利用先のNamespace情報を元に、自動的に該当のIAMユーザーの作成とSecretの作成を実施してくれる便利なツールです。 IAMユーザーの利用で問題なければ、上記を活用して簡単にAWSアクセス用のリソースが準備できるのでぜひご利用してみてください。

今回はSTSを使うということなのでOpenShift上のこちらの機能は活用できませんが、CCOはccoctlというバイナリツールを提供しており、それを使うことでCredentialsRequestからSTSを使う場合のSecret yamlファイルを作成することができます。 ちなみにOpenShiftのSTSによるインストールはバージョン4.8からGAになりました。

以下はCCOのgithubにある、STSを使った一時的なCredential付与の概要です。

OpenShiftは、AWS Security Token Service(STS)で様々なコンポーネントの一時的なクレデンシャルを使用するように設定できます。これにより、認証フローが有効になり、コンポーネントがIAMロールを引き受けることができるため、資格情報が短命になります。また、AWS IAM OpenID Connect(OIDC)IDプロバイダーを使用して、Credentialsのリクエストと更新を自動化します。OpenShiftは、AWS IAMによって信頼されているServiceAccountトークンに署名できます。このトークンは、Podにプロジェクションして認証に使用できます。以下は、それがどのように機能するかを示す図です。 f:id:shin7446:20220226184242p:plain

手順

今回は、以下の2つの作業を行います。

  • STSを使ってAWS IPIインストールを実施
  • Podに対してSTSで一時的な認証情報を付与してS3バケット一覧を取得する

ちなみにSTSを使ったROSAのインストール方法については、以前に弊社の石川がまとめています。STSのメリットなどもわかりやすくまとまっていますので、ぜひ参照ください。

rheb.hatenablog.com


前提条件

  • ocコマンド
  • AdministratorAcccess権限を持つIAMユーザー
  • OpenShiftサブスクリプション

STS を使ったAWS IPIインストール

まずはhttps://console.redhat.com/openshift/install/aws/installer-provisionedからpull-secretをダウンロードします。

次に今回デプロイするOpenShiftのリリースイメージから、AWS CredentialsRequestオブジェクトを抽出します。そのためにhttps://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/stable-4.9/release.txtにアクセスし、Pull From 行からイメージのIDを取得しRELEASE_IMAGEに格納します

$ export RELEASE_IMAGE="quay.io/openshift-release-dev/ocp-release@sha256:1c13f0926c37c122eb5c86afd754c007f38977c8fc32d7da090490f556945afd"

以下コマンドでAWS CredentialsRequestオブジェクトを抽出します。

0000から始まる5つのファイルが普通にIPIインストールした際にデプロイされるCredentialsRequestリソースのyamlになります。

$ mkdir credreqs ; oc adm release extract --cloud=aws --credentials-requests $RELEASE_IMAGE --to=./credreqs
$ ls credreqs/
0000_30_machine-api-operator_00_credentials-request.yaml
0000_50_cloud-credential-operator_05-iam-ro-credentialsrequest.yaml
0000_50_cluster-image-registry-operator_01-registry-credentials-request.yaml
0000_50_cluster-ingress-operator_00-ingress-credentials-request.yaml
0000_50_cluster-storage-operator_03_credentials_request_aws.yaml

次にリリースイメージからOpenShiftインストールバイナリーを抽出します。

$ oc adm release extract --command=openshift-install $RELEASE_IMAGE

install-config.yamlを作成します。

Pull Secretには先ほどダウンロードしたpull-secretの中身をペーストしてください。

$ ./openshift-install create install-config
? Platform aws
INFO Credentials loaded from the "default" profile in file "/home/username/.aws/credentials"
? Region ap-northeast-1
? Base Domain xxxx.com
? Cluster Name sts-test-cluster-1
? Pull Secret [? for help] *************************
INFO Install-Config created in: .

作成されたinstall-config.yamlに、credentialsModeをManualでデプロイするための設定を挿入します。

$ echo "credentialsMode: Manual" >> install-config.yaml

インストールマニフェストを作成します。

$ ./openshift-install create manifests 
INFO Credentials loaded from the "default" profile in file "/home/username/.aws/credentials"
INFO Consuming Install Config from target directory
INFO Manifests created in: manifests and openshift

次にOpenShift Container Platform リリースイメージ内の CCO コンテナーイメージから ccoctl バイナリーを展開します。

ここでは冒頭でダウンロードしたpull-secretを~/.pull-secretとして保存していることを前提としています。

$ CCO_IMAGE=$(oc adm release info --image-for='cloud-credential-operator' $RELEASE_IMAGE)
$ oc image extract $CCO_IMAGE --file="/usr/bin/ccoctl" -a ~/.pull-secret
$ chmod 775 ccoctl

ccoctlを使ってAWSリソースを作成します。

以下のコマンドでは次の操作を一気に実行します。

  • ServiceAccount署名キー(Private/Public)を生成
  • S3バケットを作成し、OIDC設定をバケットにアップロード
  • S3バケット設定を信頼するIAM Identity Providerを設定
  • 先述で抽出したcredreqsディレクトリ内のAWSCredentialsRequestごとにIAMロールを作成
  • インストーラーが必要とするファイルをoutputディレクトリにdump
$ ccoctl aws create-all \
--name sts-test-1 \
--region ap-northeast-1 \
--credentials-requests-dir ./credreqs \
--output-dir ./outputs

実行結果

2022/02/25 08:38:07 Generating RSA keypair
2022/02/25 08:38:16 Writing private key to outputs/serviceaccount-signer.private
2022/02/25 08:38:16 Writing public key to outputs/serviceaccount-signer.public
2022/02/25 08:38:16 Copying signing key for use by installer
2022/02/25 08:38:18 Bucket sts-test-1-oidc created
2022/02/25 08:38:19 OpenID Connect discovery document in the S3 bucket sts-test-1-oidc at .well-known/openid-configuration updated
2022/02/25 08:38:19 Reading public key
2022/02/25 08:38:19 JSON web key set (JWKS) in the S3 bucket sts-test-1-oidc at keys.json updated
2022/02/25 08:38:21 Identity Provider created with ARN: arn:aws:iam::XXXXXXXXXXXX:oidc-provider/sts-test-1-oidc.s3.ap-northeast-1.amazonaws.com
2022/02/25 08:38:21 Role arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-openshift-machine-api-aws-cloud-credentials created
2022/02/25 08:38:21 Saved credentials configuration to: outputs/manifests/openshift-machine-api-aws-cloud-credentials-credentials.yaml
2022/02/25 08:38:22 Updated Role policy for Role sts-test-1-openshift-machine-api-aws-cloud-credentials
2022/02/25 08:38:22 Role arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-openshift-cloud-credential-operator-cloud-credential- created
2022/02/25 08:38:22 Saved credentials configuration to: outputs/manifests/openshift-cloud-credential-operator-cloud-credential-operator-iam-ro-creds-credentials.yaml
2022/02/25 08:38:22 Updated Role policy for Role sts-test-1-openshift-cloud-credential-operator-cloud-credential-
2022/02/25 08:38:23 Role arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-openshift-image-registry-installer-cloud-credentials created
2022/02/25 08:38:23 Saved credentials configuration to: outputs/manifests/openshift-image-registry-installer-cloud-credentials-credentials.yaml
2022/02/25 08:38:23 Updated Role policy for Role sts-test-1-openshift-image-registry-installer-cloud-credentials
2022/02/25 08:38:24 Role arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-openshift-ingress-operator-cloud-credentials created
2022/02/25 08:38:24 Saved credentials configuration to: outputs/manifests/openshift-ingress-operator-cloud-credentials-credentials.yaml
2022/02/25 08:38:24 Updated Role policy for Role sts-test-1-openshift-ingress-operator-cloud-credentials
2022/02/25 08:38:24 Role arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-openshift-cluster-csi-drivers-ebs-cloud-credentials created
2022/02/25 08:38:24 Saved credentials configuration to: outputs/manifests/openshift-cluster-csi-drivers-ebs-cloud-credentials-credentials.yaml
2022/02/25 08:38:24 Updated Role policy for Role sts-test-1-openshift-cluster-csi-drivers-ebs-cloud-credentials

AWSのIAMを見ると、たしかにIAMユーザーでなくIAMロールが作成されていることがわかります。

f:id:shin7446:20220226185328p:plain

IAM Identity Providerも構築されていることが確認できます。

f:id:shin7446:20220226185344p:plain

outputsにdumpされたmanifestディレクトリ内のファイルを./manifest配下にコピーします。

$ cp outputs/manifests/* manifests/

同じくtlsディレクトリも直下のディレクトリにコピーします。

$ cp -a outputs/tls .

先ほどマニフェストを作成した際にinstall-config.yamlが消えてしまっている場合は、再度以下のコマンドを実行します。

入力値は.openshift_install_state.jsonに保存されているため、特に対話なしにファイルが作成されます。

$ ./openshift-install create install-config

以上の作業を行なった後、クラスターのデプロイを実施します。

$ ./openshift-install create cluster

インストール後デプロイされたAWSの認証情報のSecretを確認すると、たしかにIAMロールの情報が格納されていることがわかります。

(通常のインストールでは、この情報がIAMユーザーのものになります)

$ oc get secrets -n openshift-image-registry installer-cloud-credentials -o json | jq -r .data.credentials | base64 -d
[default]
role_arn = arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-openshift-image-registry-installer-cloud-credentials
web_identity_token_file = /var/run/secrets/openshift/serviceaccount/token

STSによる一時的な認証情報を付与してPodをデプロイ

さて、STSを使ってOpenShiftがデプロイできましたので、次はその上でデプロイするPodに対してSTSを使った一時的なCredentials付与を実施します。

実施する手順は大きく以下の4つです。

  • CredentialsRequestリソースのyamlファイルを作成
  • ccoctlで上記yamlファイルをもとにIAMロールの作成とデプロイ用マニフェスト(kind: secret)を作成
  • 作成されたSecretマニフェストをデプロイ
  • Secretとサービスアカウントのトークンを要求する設定を行なったPodをデプロイ

まずCredentialsRequestを作成します。以下はS3の一覧を取得するための権限を持つIAMを作成するためのものです。

Secretのデプロイ先を「default」に、またPodに付与するService Accountを「default」にすることを前提として記述しています。

$ mkdir -p sts-test-resource/cred-reqs
$ cd sts-test-resource
$ vi cred-reqs/s3-access-secret.yaml
apiVersion: cloudcredential.openshift.io/v1
kind: CredentialsRequest
metadata:
  name: sts-test-cr
  namespace: openshift-cloud-credential-operator
spec:
  secretRef:
    name: s3-access-secret
    namespace: default
  providerSpec:
    apiVersion: cloudcredential.openshift.io/v1
    kind: AWSProviderSpec
    statementEntries:
    - effect: Allow
      action:
      - s3:ListAllMyBuckets
      resource: "*"
  serviceAccountNames:
  - default

次にccoctlでIAMロールの作成とsecretマニフェストの作成を行います。

※identity-provider-arnにはAWSコンソールのIAM→IDプロバイダから確認してください。

$ ccoctl aws create-iam-roles \
--identity-provider-arn arn:aws:iam::XXXXXXXXXXXX:oidc-provider/sts-test-1-oidc.s3.ap-northeast-1.amazonaws.com \
--output-dir ./outputs \
--name sts-test-1 \
--region ap-northeast-1 \
--credentials-requests-dir ./cred-reqs/

上記コマンドで作成されたoutputsディレクトリ配下のマニフェストをデプロイします

$ oc create -f ./outputs/manifest/default-s3-access-secret-credentials.yaml

最後にデプロイするPodのyamlファイルを作成していきます。

  • デプロイしたSecretを/var/run/secrets/awsにVolumeMount
  • awscli実行時の認証ファイルに/var/run/secrets/aws/credentialsを使うようAWS_CONFIG_FILE環境変数を設定
  • サービスアカウントのトークンを/var/run/secrets/openshift/serviceaccountに格納するようbound-sa-tokenvolumesを設定(詳細はこちらを参照)
$ vi pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: running-aws
  name: running-aws
spec:
  containers:
  - command:
    - tail
    - -f
    - /dev/null
    image: amazon/aws-cli
    name: running-aws
    resources: {}
    env:
    - name: AWS_CONFIG_FILE
      value: /var/run/secrets/aws/credentials
    volumeMounts:
    - name: aws-credentials
      mountPath: /var/run/secrets/aws
      readOnly: true
    - name: bound-sa-token
      mountPath: /var/run/secrets/openshift/serviceaccount
      readOnly: true
  volumes:
  - name: aws-credentials
    secret:
      defaultMode: 420
      secretName: s3-access-secret
  - name: bound-sa-token
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          audience: openshift
          expirationSeconds: 3600
          path: token

上記のPodをデプロイします。

$ oc create -f pod.yaml

実際にPodにログインしてawsコマンドを叩いてみます。

$ oc rsh running-aws
sh-4.2# aws s3 ls
2022-02-25 08:38:19 sts-test-1-oidc
2022-02-25 09:13:08 sts-test-cluster-1-5rtrx-image-registry-ap-northeast-1-abcdefg

無事にS3バケットの情報が取得できました。

ちなみにマウントされたawsのCredentialsファイルを見てみましょう

sh-4.2# cat /var/run/secrets/aws/credentials
[default]
role_arn = arn:aws:iam::XXXXXXXXXXXX:role/sts-test-1-default-s3-access-secret
web_identity_token_file = /var/run/secrets/openshift/serviceaccount/token

たしかにIAMロールで認証設定されていますね。


ということで、IAMユーザーではなくSTSでの一時的な認証付与を行なったPodの作成方法についてご紹介しました。

CCOの機能を最大限活用するにはIAMユーザーでの運用が望ましいですが、セキュリティ的な側面からIAMユーザー認証が使えない際は、この手法を検討してみてはいかがでしょうか?

参考サイト

access.redhat.com

github.com

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。