10.5. AWS STS를 사용하여 운영자가 CCO 기반 워크플로를 지원하도록 지원
프로젝트를 Operator Lifecycle Manager(OLM)에서 실행하도록 설계하는 Operator 작성자는 프로젝트를 사용자 지정하여 Cloud Credential Operator(CCO)를 지원함으로써 STS 지원 OpenShift Container Platform 클러스터에서 AWS에 대해 Operator가 인증하도록 할 수 있습니다.
이 방법을 사용하면 운영자는 CredentialsRequest 객체를 생성하고 결과 Secret 객체를 읽기 위한 RBAC 권한이 필요하며 이에 대한 책임이 있습니다.
기본적으로 Operator 배포와 관련된 Pod는 serviceAccountToken 볼륨을 마운트하여 서비스 계정 토큰을 결과 Secret 객체에서 참조할 수 있도록 합니다.
필수 조건
- OpenShift Container Platform 4.14 이상
- STS 모드의 클러스터
- OLM 기반 운영자 프로젝트
프로세스
Operator 프로젝트의
ClusterServiceVersion(CSV) 객체를 업데이트하세요.운영자가
CredentialsRequests객체를 생성할 수 있는 RBAC 권한이 있는지 확인하세요.예 10.1.
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개체가 준비되었는지 확인하세요. 예를 들면 다음과 같습니다.예 10.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 프로젝트 코드의 일부로) 다음과 같이 다르게 처리할 수 있습니다.예 10.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 }참고현재 Operator 번들에
CredentialsRequest객체를 추가하는 것은 지원되지 않습니다.자격 증명 요청에 역할 ARN과 웹 ID 토큰 경로를 추가하고 운영자 초기화 중에 적용합니다.
예 10.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에서 조정 중인 다른 항목과 함께 호출됩니다.예 10.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
시간 초과값은 CCO가 추가된CredentialsRequest객체를 얼마나 빨리 감지하고Secret객체를 생성하는지에 대한 추정치에 따라 결정됩니다. 운영자가 아직 클라우드 리소스에 액세스하지 못하는 이유가 무엇인지 궁금해하는 클러스터 관리자를 위해 시간을 줄이거나 맞춤형 피드백을 만드는 것을 고려해 볼 수 있습니다.
CCO가 자격 증명 요청에서 생성한 비밀을 읽고 해당 비밀의 데이터가 포함된 AWS 구성 파일을 만들어 AWS 구성을 설정합니다.
예 10.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가 비밀을 생성할 시간을 얻을 수 있습니다.
또한 대기 기간은 결국 시간 초과되어 사용자에게 OpenShift Container Platform 클러스터 버전과 CCO가 STS 감지를 통한
CredentialsRequest개체 워크플로를 지원하지 않는 이전 버전일 수 있다는 경고를 보냅니다. 이런 경우에는 사용자에게 다른 방법을 사용하여 비밀번호를 추가해야 한다고 알려주십시오.AWS SDK 세션을 구성합니다(예:
예 10.7. AWS SDK 세션 구성 예시
sharedCredentialsFile, err := SharedCredentialsFileFromSecret(secret) if err != nil { // handle error } options := session.Options{ SharedConfigState: session.SharedConfigEnable, SharedConfigFiles: []string{sharedCredentialsFile}, }