3.5. Operator が AWS STS を使用した CCO ベースのワークフローをサポートできるようにする
Operator Lifecycle Manager (OLM) で実行するプロジェクトを設計する Operator 作成者は、Cloud Credential Operator (CCO) をサポートするようにプロジェクトをカスタマイズすることで、STS 対応の OpenShift Container Platform クラスター上で Operator が AWS に対して認証できるようにすることができます。
このメソッドでは、Operator が CredentialsRequest
オブジェクトの作成を担当します。つまり、Operator がこれらのオブジェクトを作成するには RBAC 権限が必要です。次に、Operator は、結果として得られる Secret
オブジェクトを読み取ることができなければなりません。
デフォルトでは、Operator デプロイメントに関連する Pod は、結果として得られる Secret
オブジェクトでサービスアカウントトークンを参照できるように、serviceAccountToken
ボリュームをマウントします。
前提条件
- OpenShift Container Platform 4.14 以降
- STS モードのクラスター
- OLM ベースの Operator プロジェクト
手順
Operator プロジェクトの
ClusterServiceVersion
(CSV) オブジェクトを更新します。Operator が
CredentialsRequests
オブジェクトを作成する RBAC 権限を持っていることを確認します。例3.1
clusterPermissions
リストの例# ... install: spec: clusterPermissions: - rules: - apiGroups: - "cloudcredential.openshift.io" resources: - credentialsrequests verbs: - create - delete - get - list - patch - update - watch
AWS 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
オブジェクトがあることを確認してください。以下に例を示します。例3.2
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 プロジェクトコードの一部として)、別の方法で処理することもできます。例3.3 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 の初期化中に適用します。
例3.4 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 で調整している他の項目とともに呼び出されます。例3.5
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 設定をセットアップします。
例3.6 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 セッションを設定します。以下に例を示します。
例3.7 AWS SDK セッション設定の例
sharedCredentialsFile, err := SharedCredentialsFileFromSecret(secret) if err != nil { // handle error } options := session.Options{ SharedConfigState: session.SharedConfigEnable, SharedConfigFiles: []string{sharedCredentialsFile}, }