×

OpenShift Container Platform控制平面包含一个内置的OAuth服务器。您可以获取OAuth访问令牌以对OpenShift Container Platform API进行身份验证。创建托管集群后,您可以通过指定身份提供程序来配置OAuth。

使用CLI为托管集群配置OAuth服务器

您可以使用OpenID Connect身份提供程序 (oidc) 为托管集群配置内部OAuth服务器。

您可以为以下受支持的身份提供程序配置OAuth

  • oidc

  • htpasswd

  • keystone

  • ldap

  • basic-authentication

  • request-header

  • github

  • gitlab

  • google

在OAuth配置中添加任何身份提供程序都会删除默认的kubeadmin用户提供程序。

配置身份提供程序时,必须提前在托管集群中配置至少一个NodePool副本。DNS解析的流量通过工作节点发送。对于htpasswdrequest-header身份提供程序,无需提前配置NodePool副本。

先决条件
  • 您已创建托管集群。

步骤
  1. 通过运行以下命令编辑托管集群上的HostedCluster自定义资源 (CR)

    $ oc edit <hosted_cluster_name> -n <hosted_cluster_namespace>
  2. 使用以下示例在HostedCluster CR中添加OAuth配置

    apiVersion: hypershift.openshift.io/v1alpha1
    kind: HostedCluster
    metadata:
      name: <hosted_cluster_name> (1)
      namespace: <hosted_cluster_namespace> (2)
    spec:
      configuration:
        oauth:
          identityProviders:
          - openID: (3)
              claims:
                email: (4)
                  - <email_address>
                name: (5)
                  - <display_name>
                preferredUsername: (6)
                  - <preferred_username>
              clientID: <client_id> (7)
              clientSecret:
                name: <client_id_secret_name> (8)
              issuer: https://example.com/identity (9)
            mappingMethod: lookup (10)
            name: IAM
            type: OpenID
    1 指定托管集群名称。
    2 指定托管集群命名空间。
    3 此提供程序名称作为身份声明的值的前缀,以形成身份名称。提供程序名称也用于构建重定向 URL。
    4 定义用作电子邮件地址的属性列表。
    5 定义用作显示名称的属性列表。
    6 定义用作首选用户名的一系列属性。
    7 定义在OpenID提供程序中注册的客户端的ID。您必须允许客户端重定向到https://oauth-openshift.apps.<cluster_name>.<cluster_domain>/oauth2callback/<idp_provider_name> URL。
    8 定义在OpenID提供程序中注册的客户端的密钥。
    9 OpenID规范中描述的颁发者标识符。您必须使用不带查询或片段组件的https
    10 定义控制如何在此提供程序的身份和User对象之间建立映射的映射方法。
  3. 保存文件以应用更改。

使用Web控制台为托管集群配置OAuth服务器

您可以使用OpenShift Container Platform Web控制台为托管集群配置内部OAuth服务器。

您可以为以下受支持的身份提供程序配置OAuth

  • oidc

  • htpasswd

  • keystone

  • ldap

  • basic-authentication

  • request-header

  • github

  • gitlab

  • google

在OAuth配置中添加任何身份提供程序都会删除默认的kubeadmin用户提供程序。

配置身份提供程序时,必须提前在托管集群中配置至少一个NodePool副本。DNS解析的流量通过工作节点发送。对于htpasswdrequest-header身份提供程序,无需提前配置NodePool副本。

先决条件
  • 您已以具有cluster-admin权限的用户身份登录。

  • 您已创建托管集群。

步骤
  1. 导航到主页API资源管理器

  2. 使用按类型筛选框搜索您的HostedCluster资源。

  3. 单击要编辑的HostedCluster资源。

  4. 单击实例选项卡。

  5. 单击托管集群名称条目旁边的选项菜单kebab,然后单击编辑HostedCluster

  6. 在YAML文件中添加OAuth配置

    spec:
      configuration:
        oauth:
          identityProviders:
          - openID: (1)
              claims:
                email: (2)
                  - <email_address>
                name: (3)
                  - <display_name>
                preferredUsername: (4)
                  - <preferred_username>
              clientID: <client_id> (5)
              clientSecret:
                name: <client_id_secret_name> (6)
              issuer: https://example.com/identity (7)
            mappingMethod: lookup (8)
            name: IAM
            type: OpenID
    1 此提供程序名称作为身份声明的值的前缀,以形成身份名称。提供程序名称也用于构建重定向 URL。
    2 定义用作电子邮件地址的属性列表。
    3 定义用作显示名称的属性列表。
    4 定义用作首选用户名的一系列属性。
    5 定义在OpenID提供程序中注册的客户端的ID。您必须允许客户端重定向到https://oauth-openshift.apps.<cluster_name>.<cluster_domain>/oauth2callback/<idp_provider_name> URL。
    6 定义在OpenID提供程序中注册的客户端的密钥。
    7 OpenID规范中描述的颁发者标识符。您必须使用不带查询或片段组件的https
    8 定义控制如何在此提供程序的身份和User对象之间建立映射的映射方法。
  7. 单击保存

其他资源

使用AWS上的托管集群中的CCO为组件分配IAM角色

您可以使用 Amazon Web Services (AWS) 托管集群中的 Cloud Credential Operator (CCO) 为组件分配提供短期、有限权限安全凭据的 IAM 角色。默认情况下,CCO 在托管控制平面中运行。

CCO 仅支持 AWS 托管集群的手动模式。默认情况下,托管集群配置为手动模式。管理集群可能使用除手动模式以外的其他模式。

验证 AWS 托管集群中 CCO 的安装

您可以验证 Cloud Credential Operator (CCO) 是否在您的托管控制平面中正确运行。

先决条件
  • 您已在 Amazon Web Services (AWS) 上配置了托管集群。

步骤
  1. 通过运行以下命令,验证 CCO 是否以手动模式在您的托管集群中配置

    $ oc get cloudcredentials <hosted_cluster_name> -n <hosted_cluster_namespace> -o=jsonpath={.spec.credentialsMode}
    预期输出
    Manual
  2. 通过运行以下命令,验证serviceAccountIssuer资源的值是否不为空

    $ oc get authentication cluster --kubeconfig <hosted_cluster_name>.kubeconfig -o jsonpath --template '{.spec.serviceAccountIssuer }'
    示例输出
    https://aos-hypershift-ci-oidc-29999.s3.us-east-2.amazonaws.com/hypershift-ci-29999

启用 Operator 以支持使用 AWS STS 的基于 CCO 的工作流

作为设计您的项目以在 Operator Lifecycle Manager (OLM) 上运行的 Operator 作者,您可以通过自定义您的项目以支持 Cloud Credential Operator (CCO),使您的 Operator 能够对启用 STS 的 OpenShift Container Platform 集群上的 AWS 进行身份验证。

使用此方法,Operator 负责并需要 RBAC 权限才能创建CredentialsRequest对象和读取生成的Secret对象。

默认情况下,与 Operator 部署相关的 Pod 会挂载serviceAccountToken卷,以便可以在生成的Secret对象中引用服务帐户令牌。

先决条件
  • OpenShift Container Platform 4.14 或更高版本

  • 处于 STS 模式下的集群

  • 基于 OLM 的 Operator 项目

步骤
  1. 更新您的 Operator 项目的ClusterServiceVersion (CSV) 对象

    1. 确保您的 Operator 具有创建CredentialsRequests对象的 RBAC 权限

      clusterPermissions列表示例
      # ...
      install:
        spec:
          clusterPermissions:
          - rules:
            - apiGroups:
              - "cloudcredential.openshift.io"
              resources:
              - credentialsrequests
              verbs:
              - create
              - delete
              - get
              - list
              - patch
              - update
              - watch
    2. 添加以下注释以声明支持此使用 AWS STS 的基于 CCO 的工作流的方法

      # ...
      metadata:
       annotations:
         features.operators.openshift.io/token-auth-aws: "true"
  2. 更新您的 Operator 项目代码

    1. 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"
    2. 确保您已准备好要修补和应用的CredentialsRequest对象。例如

      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 项目代码的一部分),您可以以不同的方式处理它

      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对象。

    3. 在 Operator 初始化期间,将角色 ARN 和 Web 身份令牌路径添加到凭据请求并应用它

      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)
         }
      }
    4. 确保您的 Operator 可以等待 CCO 从凭据请求中显示Secret对象,如下例所示,它与您在 Operator 中协调的其他项目一起调用

      等待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 为什么尚未访问云资源的集群管理员创建自定义反馈。
    5. 通过读取 CCO 从凭据请求创建的密钥并创建包含该密钥数据的 AWS 配置文件来设置 AWS 配置

      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
      }

      假设密钥存在,但在使用此密钥时,您的 Operator 代码应等待并重试,以便为 CCO 创建密钥留出时间。

      此外,等待时间最终应该超时并警告用户 OpenShift Container Platform 集群版本(以及 CCO)可能是一个较早的版本,不支持使用 STS 检测的CredentialsRequest对象工作流。在这种情况下,请指示用户必须使用其他方法添加密钥。

    6. 配置 AWS SDK 会话,例如

      AWS SDK 会话配置示例
      sharedCredentialsFile, err := SharedCredentialsFileFromSecret(secret)
      if err != nil {
         // handle error
      }
      options := session.Options{
         SharedConfigState: session.SharedConfigEnable,
         SharedConfigFiles: []string{sharedCredentialsFile},
      }