5.10.2. AWS STS を使用した OLM 管理 Operator 向けの CCO ベースのワークフロー
AWS 上で実行している OpenShift Container Platform クラスターが Security Token Service (STS) モードである場合、クラスターは AWS と OpenShift Container Platform の機能を利用して、アプリケーションレベルで IAM ロールを使用していることを意味します。STS を使用すると、アプリケーションは IAM ロールを引き受けることができる JSON Web トークン (JWT) を提供できます。
JWT には、サービスアカウントに一時的に許可されるパーミッションを許可する sts:AssumeRoleWithWebIdentity IAM アクションの Amazon Resource Name (ARN) が含まれています。JWT には、AWS IAM が検証できる ProjectedServiceAccountToken の署名キーが含まれています。署名されたサービスアカウントトークン自体は、AWS ロールを引き受けるために必要な JWT として使用されます。
Cloud Credential Operator (CCO) は、クラウドプロバイダー上で実行している OpenShift Container Platform クラスターにデフォルトでインストールされる Cluster Operator です。CCO は STS のために次の機能を提供します。
- CCO が STS 対応のクラスター上で実行されているかどうかを検出する
-
CredentialsRequestオブジェクトをチェックして、Operator に AWS リソースへのアクセスを許可するために必要な情報を提供するフィールドが存在するかどうかを確認する
CCO は、手動モードの場合でもこの検出を実行します。適切に設定されている場合、CCO は必要なアクセス情報を含む Secret オブジェクトを Operator namespace に投影します。
OpenShift Container Platform 4.14 以降、CCO は、STS ワークフローに必要な情報を含む Secrets の作成を要求できる CredentialsRequest オブジェクトの使用を拡張することで、このタスクを半自動化できるようになりました。ユーザーは、Web コンソールまたは CLI から Operator をインストールするときにロール ARN を指定できます。
更新の自動承認を使用したサブスクリプションは推奨されません。更新前に権限の変更が必要な場合があるためです。更新の手動承認を使用したサブスクリプションであれば、管理者が新しいバージョンの権限を確認し、必要な手順を実行してから更新できます。
OpenShift Container Platform 4.14 以降の更新された CCO と一緒に使用するために Operator を準備する Operator 作成者は、STS トークン認証の処理に加えて、ユーザーに指示し、以前の CCO バージョンからの相違を処理するコードを追加する必要があります (Operator がまだ STS 未対応の場合)。推奨される方法は、STS 関連のフィールドを正しく入力した CredentialsRequest オブジェクトを用意して、CCO に Secret を作成させることです。
バージョン 4.14 より前の OpenShift Container Platform クラスターをサポートする予定がる場合は、CCO ユーティリティー (ccoctl) を使用して STS 対応情報を含むシークレットを手動で作成する方法をユーザーに提供することを検討してください。以前の CCO バージョンはクラスター上の STS モードを認識しないため、シークレットを作成できません。
コードでは、決して表示されないシークレットをチェックし、提供したフォールバック手順に従うようにユーザーに警告する必要があります。詳細は、「代替方法」サブセクションを参照してください。
5.10.2.1. Operator が AWS STS を使用した CCO ベースのワークフローをサポートできるようにする リンクのコピーリンクがクリップボードにコピーされました!
Operator Lifecycle Manager (OLM) で実行するプロジェクトを設計する Operator 作成者は、Cloud Credential Operator (CCO) をサポートするようにプロジェクトをカスタマイズすることで、STS 対応の OpenShift Container Platform クラスター上で Operator が AWS に対して認証できるようにすることができます。
この方法では、Operator が CredentialsRequest オブジェクトを作成します。Operator には、オブジェクトを作成するための RBAC 権限と、生成される Secret オブジェクトを読み取るための RBAC 権限が必要です。
デフォルトでは、Operator デプロイメントに関連する Pod は、生成される Secret オブジェクトでサービスアカウントトークンを参照できるように、serviceAccountToken ボリュームをマウントします。
前提条件
- OpenShift Container Platform 4.14 以降
- STS モードのクラスター
- OLM ベースの Operator プロジェクト
手順
Operator プロジェクトの
ClusterServiceVersion(CSV) オブジェクトを更新します。Operator が
CredentialsRequestsオブジェクトを作成する RBAC 権限を持っていることを確認します。例5.16
clusterPermissionsリストの例# ... install: spec: clusterPermissions: - rules: - apiGroups: - "cloudcredential.openshift.io" resources: - credentialsrequests verbs: - create - delete - get - list - patch - update - watchAWS STS を使用したこの CCO ベースのワークフロー方式のサポートを要求するために、次のアノテーションを追加します。
# ... metadata: annotations: features.operators.openshift.io/token-auth-aws: "true"
Operator プロジェクトコードを更新します。
Subscriptionオブジェクトによって Pod に設定された環境変数からロール ARN を取得します。以下に例を示します。// Get ENV var roleARN := os.Getenv("ROLEARN") setupLog.Info("getting role ARN", "role ARN = ", roleARN) webIdentityTokenPath := "/var/run/secrets/openshift/serviceaccount/token"パッチを適用して適用できる
CredentialsRequestオブジェクトがあることを確認してください。以下に例を示します。例5.17
CredentialsRequestオブジェクトの作成例import ( minterv1 "github.com/openshift/cloud-credential-operator/pkg/apis/cloudcredential/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) var in = minterv1.AWSProviderSpec{ StatementEntries: []minterv1.StatementEntry{ { Action: []string{ "s3:*", }, Effect: "Allow", Resource: "arn:aws:s3:*:*:*", }, }, STSIAMRoleARN: "<role_arn>", } var codec = minterv1.Codec var ProviderSpec, _ = codec.EncodeProviderSpec(in.DeepCopyObject()) const ( name = "<credential_request_name>" namespace = "<namespace_name>" ) var CredentialsRequestTemplate = &minterv1.CredentialsRequest{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: "openshift-cloud-credential-operator", }, Spec: minterv1.CredentialsRequestSpec{ ProviderSpec: ProviderSpec, SecretRef: corev1.ObjectReference{ Name: "<secret_name>", Namespace: namespace, }, ServiceAccountNames: []string{ "<service_account_name>", }, CloudTokenPath: "", }, }あるいは、YAML 形式の
CredentialsRequestオブジェクトから開始している場合 (たとえば、Operator プロジェクトコードの一部として)、別の方法で処理することもできます。例5.18 YAML フォームでの
CredentialsRequestオブジェクト作成の例// CredentialsRequest is a struct that represents a request for credentials type CredentialsRequest struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` Metadata struct { Name string `yaml:"name"` Namespace string `yaml:"namespace"` } `yaml:"metadata"` Spec struct { SecretRef struct { Name string `yaml:"name"` Namespace string `yaml:"namespace"` } `yaml:"secretRef"` ProviderSpec struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` StatementEntries []struct { Effect string `yaml:"effect"` Action []string `yaml:"action"` Resource string `yaml:"resource"` } `yaml:"statementEntries"` STSIAMRoleARN string `yaml:"stsIAMRoleARN"` } `yaml:"providerSpec"` // added new field CloudTokenPath string `yaml:"cloudTokenPath"` } `yaml:"spec"` } // ConsumeCredsRequestAddingTokenInfo is a function that takes a YAML filename and two strings as arguments // It unmarshals the YAML file to a CredentialsRequest object and adds the token information. func ConsumeCredsRequestAddingTokenInfo(fileName, tokenString, tokenPath string) (*CredentialsRequest, error) { // open a file containing YAML form of a CredentialsRequest file, err := os.Open(fileName) if err != nil { return nil, err } defer file.Close() // create a new CredentialsRequest object cr := &CredentialsRequest{} // decode the yaml file to the object decoder := yaml.NewDecoder(file) err = decoder.Decode(cr) if err != nil { return nil, err } // assign the string to the existing field in the object cr.Spec.CloudTokenPath = tokenPath // return the modified object return cr, nil }注記CredentialsRequestオブジェクトを Operator バンドルに追加することは現在サポートされていません。ロール ARN と Web ID トークンパスを認証情報リクエストに追加し、Operator の初期化中に適用します。
例5.19 Operator の初期化中に
CredentialsRequestオブジェクトを適用する例// apply CredentialsRequest on install credReq := credreq.CredentialsRequestTemplate credReq.Spec.CloudTokenPath = webIdentityTokenPath c := mgr.GetClient() if err := c.Create(context.TODO(), credReq); err != nil { if !errors.IsAlreadyExists(err) { setupLog.Error(err, "unable to create CredRequest") os.Exit(1) } }次の例に示すように、Operator が CCO から
Secretオブジェクトが表示されるのを待機できるようにします。これは、Operator で調整している他の項目とともに呼び出されます。例5.20
Secretオブジェクトを待機する例// WaitForSecret is a function that takes a Kubernetes client, a namespace, and a v1 "k8s.io/api/core/v1" name as arguments // It waits until the secret object with the given name exists in the given namespace // It returns the secret object or an error if the timeout is exceeded func WaitForSecret(client kubernetes.Interface, namespace, name string) (*v1.Secret, error) { // set a timeout of 10 minutes timeout := time.After(10 * time.Minute)1 // set a polling interval of 10 seconds ticker := time.NewTicker(10 * time.Second) // loop until the timeout or the secret is found for { select { case <-timeout: // timeout is exceeded, return an error return nil, fmt.Errorf("timed out waiting for secret %s in namespace %s", name, namespace) // add to this error with a pointer to instructions for following a manual path to a Secret that will work on STS case <-ticker.C: // polling interval is reached, try to get the secret secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { // secret does not exist yet, continue waiting continue } else { // some other error occurred, return it return nil, err } } else { // secret is found, return it return secret, nil } } } }- 1
timeout値は、CCO が追加されたCredentialsRequestオブジェクトを検出してSecretオブジェクトを生成する速度の推定に基づいています。Operator がまだクラウドリソースにアクセスしていない理由を疑問に思っているクラスター管理者のために、時間を短縮するか、カスタムフィードバックを作成することを検討してください。
認証情報リクエストから CCO によって作成されたシークレットを読み取り、そのシークレットのデータを含む AWS 設定ファイルを作成することで、AWS 設定をセットアップします。
例5.21 AWS 設定の作成例
func SharedCredentialsFileFromSecret(secret *corev1.Secret) (string, error) { var data []byte switch { case len(secret.Data["credentials"]) > 0: data = secret.Data["credentials"] default: return "", errors.New("invalid secret for aws credentials") } f, err := ioutil.TempFile("", "aws-shared-credentials") if err != nil { return "", errors.Wrap(err, "failed to create file for shared credentials") } defer f.Close() if _, err := f.Write(data); err != nil { return "", errors.Wrapf(err, "failed to write credentials to %s", f.Name()) } return f.Name(), nil }重要シークレットは存在すると想定されますが、このシークレットを使用する場合、CCO がシークレットを作成する時間を与えるために、Operator コードは待機して再試行する必要があります。
さらに、待機期間は最終的にタイムアウトになり、OpenShift Container Platform クラスターのバージョン、つまり CCO が、STS 検出による
CredentialsRequestオブジェクトのワークフローをサポートしていない以前のバージョンである可能性があることをユーザーに警告します。このような場合は、別の方法を使用してシークレットを追加する必要があることをユーザーに指示します。AWS SDK セッションを設定します。以下に例を示します。
例5.22 AWS SDK セッション設定の例
sharedCredentialsFile, err := SharedCredentialsFileFromSecret(secret) if err != nil { // handle error } options := session.Options{ SharedConfigState: session.SharedConfigEnable, SharedConfigFiles: []string{sharedCredentialsFile}, }