From 8559cceb220c2244bcd3c27ddcc86bb9f51c26f7 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Wed, 12 Jul 2023 16:36:42 -0400 Subject: [PATCH 1/6] Add Azure AD Workload Identity doc. --- README.md | 3 +- docs/azure_ad_workload_identity_flow.png | Bin 0 -> 84199 bytes docs/azure_workload_identity.md | 167 +++++++++++++++++++++++ docs/ccoctl.md | 100 +++++++++++++- 4 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 docs/azure_ad_workload_identity_flow.png create mode 100644 docs/azure_workload_identity.md diff --git a/README.md b/README.md index 876b1b275..c98cba3df 100644 --- a/README.md +++ b/README.md @@ -231,13 +231,14 @@ Cons: Read more about supported clouds by clicking on the links below: * [AWS](./docs/sts.md) + * [Azure](./docs/azure_workload_identity.md) * [GCP](./docs/gcp_workload_identity.md) ## Support Matrix Cloud | Mint | Mint + Remove Admin Cred | Passthrough | Manual | Token --- | --- | --- | --- | --- | --- AWS | Y | 4.4+ | Y | 4.3+ | 4.8+ -Azure | N1 | N | Y | Y | N +Azure | N1 | N | Y | Y | 4.14+ GCP | Y | 4.7+ | Y | Y | 4.10+ IBMCloud | N | N | N | Y | N KubeVirt | N | N | Y | N | N diff --git a/docs/azure_ad_workload_identity_flow.png b/docs/azure_ad_workload_identity_flow.png new file mode 100644 index 0000000000000000000000000000000000000000..b6ace649e5797ced3b2e416036259aecd582ff92 GIT binary patch literal 84199 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�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 R{3r>p*=BQ1ugtXH`9DLBYP$dc literal 0 HcmV?d00001 diff --git a/docs/azure_workload_identity.md b/docs/azure_workload_identity.md new file mode 100644 index 000000000..4fe3d82e8 --- /dev/null +++ b/docs/azure_workload_identity.md @@ -0,0 +1,167 @@ +# Azure AD Workload Identity + +### Overview +OpenShift can be configured to use temporary credentials for workload components with [Azure AD Workload Identity](https://azure.github.io/azure-workload-identity/docs/). In this model, the OpenShift cluster signs Kubernetes ServiceAccount tokens which can be trusted by an external OIDC identity provider. A component such as an operator running on the cluster can exchange the signed ServiceAccount token projected into its Pod volume for an Azure Active Directory (AD) access token using [Azure Identity SDKs](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity). This process also automates requesting and refreshing of credentials. The ServiceAccount token used by a Pod is rotated by the kubelet on the node where Pod is running when the Pod's ServiceAccount token is approaching expiry. ServiceAccount tokens are valid for one hour. + +This diagram illustrates how it works. + +![Azure AD Workload Identity Flow](azure_ad_workload_identity_flow.png) + +In order to take advantage of Azure AD Workload Identity, an OpenShift cluster must be configured with an RSA key pair with which to sign ServiceAccount tokens. We begin by creating this RSA key pair. The private key is provided to the OpenShift cluster via the OpenShift installer. The public key is stored in an Azure Blob Container along with OpenID configuration which serves as the identity provider. + +Azure User-Assigned Managed Identities are configured with the Issuer URL (public URL of the Azure Blob Container) containing the public key and OpenID configuration to allow for federation of the identity to the OIDC identity provider. Every in-cluster component receives a Kubernetes Secret containing the details of a User-Assigned Managed Identity created on the component's behalf. The Azure Identity SDK used within those components requests an Azure AD access token for the identity specified by the configuration secret and provides the signed ServiceAccount token to Azure AD. Azure AD validates that the ServiceAccount token is from a trusted identity provider before issuing an access token for the User-Assigned Managed Identity. + +### Changes in the Credentials Secret with Azure AD Workload Identity + +Without Azure AD Workload Identity, an Azure credentials secret includes an `azure_client_secret` which is essentially a password used to authenticate with Azure AD for the identity identified by `azure_client_id`. This `azure_client_secret` needs to be kept secure and is not rotated automatically. + +```yaml +apiVersion: v1 +data: + azure_client_id: + azure_client_secret: + azure_region: + azure_subscription_id: + azure_tenant_id: +kind: Secret +type: Opaque +``` + +With Azure AD Workload Identity, an Azure credentials secret contains the `azure_client_id` of the User-Assigned Managed Identity that the component will be authenticating as along with the path to the projected ServiceAccount token, `azure_federated_token_file`. The ServiceAccount token mounted at this path is refreshed hourly and thus the credentials are short-lived. + +```yaml +apiVersion: v1 +data: + azure_client_id: + azure_federated_token_file: + azure_region: + azure_subscription_id: + azure_tenant_id: +kind: Secret +type: Opaque +``` + +### Steps to install an OpenShift Cluster with Azure AD Workload Identity + +1. Set the `$RELEASE_IMAGE` environment variable. + + `$RELEASE_IMAGE` should be a recent and supported OpenShift release image that you want to deploy in your cluster. + Please refer to the [support matrix](../README.md#support-matrix) for compatibilities. + + A sample release image would be `RELEASE_IMAGE=quay.io/openshift-release-dev/ocp-release:${RHOCP_version}-${Arch}` + + Where `RHOCP_version` is the OpenShift version (e.g `4.14.0-fc.4` or `4.14.3`) and the `Arch` is the architecture type (e.g `x86_64`). + +2. Extract Azure CredentialsRequests objects from the release image. + + ``` + oc adm release extract --cloud=azure --credentials-requests $RELEASE_IMAGE --to=./credreqs + ``` + +3. Extract the `openshift-install` and `ccoctl` binaries from the release image. + + ``` + oc adm release extract --command=openshift-install $RELEASE_IMAGE + + CCO_IMAGE=$(oc adm release info --image-for='cloud-credential-operator' ${RELEASE_IMAGE}) && oc image extract ${CCO_IMAGE} --file='/usr/bin/ccoctl' --registry-config=${PULL_SECRET_PATH:-.}/pull-secret && chmod +x ccoctl + ``` + +4. Create Azure resources using the [ccoctl](./ccoctl.md) tool. You will need Azure credentials with sufficient permissions. The Azure credentials can be automatically detected after having logged into the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) `az cli login` or may be provided as environment variables (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`). + + The following command will, + * Generate public/private ServiceAccount signing RSA keys. + * Create an empty Azure ResourceGroup in which to install the cluster. This ResourceGroup is used to scope created identities must be configured as the cluster insallation ResourceGroup within the `install-config.yaml`. + * Create an Azure ResourceGroup in which to create identity resources. + * Create an Azure StorageAccount, Azure Blob Container and upload OIDC configuration to the Blob Container. + * Create User-Assigned Managed Identities for each Azure CredentialsRequest. + + Manifest files needed for cluster installation are output to the `output_dir` directory. + + ``` + ./ccoctl azure create-all --name \ + --output-dir \ + --region \ + --subscription-id \ + --credentials-requests-dir /path/to/credreqs/directory/created/in/step/2 \ + --dnszone-resource-group-name + ``` + +5. Create an install-config.yaml with the extracted OpenShift installer binary. Be sure to choose the same region as specified in step 4. + + ``` + ./openshift-install create install-config + ``` + +6. Make sure that we install the cluster in Manual mode by setting the `credentialsMode` within `install-config.yaml`. + + ``` + echo "credentialsMode: Manual" >> install-config.yaml + ``` + +7. Configure the installation Azure ResourceGroup created in step 4 within the `install-config.yaml`. By default, the installation Azure ResourceGroup will be the `` used in step 4. + + The `install-config.yaml` must be modified to add the `resourceGroupName` key within Azure platform (`.platform.azure.resourceGroupName`). We can accomplish this using [yq](https://github.com/mikefarah/yq) or by editing `install-config.yaml` directly. + + ``` + yq -i '.platform.azure.resourceGroupName = ""' install-config.yaml + ``` + +8. Create install manifests. + + ``` + ./openshift-install create manifests + ``` + +9. Copy the manifests created in the step 4 and put them in the install `manifests` directory generated by `openshift-install create manifests`. + + ``` + cp ccoctl_output_dir/manifests/* /path/to/dir/with/install/manifests/ + ``` + +10. Copy the private key for the ServiceAccount signer and put it in the same location as `install-config.yaml`. + + ``` + cp -a ccoctl_output_dir/tls /path/to/dir/with/install-config.yaml + ``` + +11. Run the OpenShift installer. + + ``` + ./openshift-install create cluster --log-level=debug + ``` + +### Post install verification + +1. Connect to the newly installed cluster and verify that the OpenShift cluster does not have `root` credentials. + + This command should return a secret not found error. + + ```yaml + oc get secrets -n kube-system azure-credentials + ``` + +2. Verify that components are assuming the `azure_client_id` specified in the secret manifests, instead of credentials passed through by the Cloud Credential Operator. The secret displayed should not contain an `azure_client_secret` key and will instead contain an `azure_federated_token_file` key. + ```yaml + oc get secrets -n openshift-image-registry cloud-credentials -o yaml + ``` + sample output of the above command + ```json + { + "azure_client_id": "cG90YXRvIHNhbGFkIGJyZWFkIGJvd2wK", + "azure_federated_token_file": "L2RyeS9ydWIvY2hpY2tlbi93aW5ncy8K", + "azure_region": "Y29ybiBicmVhZAo=", + "azure_subscription_id": "Y2hvcml6byB0YWNvcwo=", + "azure_tenant_id": "bW9sYXNzZXMgY29va2llcwo=" + } + ``` + +### Cleanup Azure resources after uninstalling the cluster + +Make sure you clean up the following resources after you uninstall your cluster. You can use the `` used in installation step 4 to identify these resources. + + ``` + ./ccoctl azure delete --name \ + --region \ + --subscription-id \ + --delete-oidc-resource-group + ``` diff --git a/docs/ccoctl.md b/docs/ccoctl.md index 48be00359..f629b2877 100644 --- a/docs/ccoctl.md +++ b/docs/ccoctl.md @@ -9,16 +9,23 @@ The `ccoctl` tool provides various commands to assist with the creating and main - [Creating IAM Roles](#creating-iam-roles) - [Creating all the required resources together](#creating-all-the-required-resources-together) - [Deleting resources](#deleting-resources) -- [GCP](#gcp) +- [Azure](#azure) - [Global flags](#global-flags-1) - [Creating RSA keys](#creating-rsa-keys-1) + - [Creating OpenID Connect Issuer](#creating-openid-connect-issuer) + - [Creating Managed Identities](#creating-managed-identities) + - [Creating all the required resources together](#creating-all-the-required-resources-together-1) + - [Deleting resources](#deleting-resources-1) +- [GCP](#gcp) + - [Global flags](#global-flags-2) + - [Creating RSA keys](#creating-rsa-keys-2) - [Creating Workload Identity Pool](#creating-workload-identity-pool) - [Creating Workload Identity Provider](#creating-workload-identity-provider) - [Creating IAM Service Accounts](#creating-iam-service-accounts) - - [Creating all the required resources together](#creating-all-the-required-resources-together-1) - - [Deleting resources](#deleting-resources-1) + - [Creating all the required resources together](#creating-all-the-required-resources-together-2) + - [Deleting resources](#deleting-resources-2) - [IBMCloud](#ibmcloud) - - [Global flags](#global-flags-2) + - [Global flags](#global-flags-3) - [Extract the Credentials Request objects from the above release image](#extract-the-credentials-request-objects-from-the-above-release-image) - [IBM Cloud](#ibm-cloud) - [IBM Cloud Power VS](#ibm-cloud-power-vs) @@ -95,7 +102,7 @@ Then you can use `ccoctl` to process all CredentialsRequest objects in the `./cr $ ccoctl aws create-all --name= --region= --credentials-requests-dir= --create-private-s3-bucket ``` -### Deleting resources +### Deleting resources To delete resources created by ccoctl, run @@ -106,6 +113,89 @@ $ ccoctl aws delete --name= --region= where `name` is the name used to tag and account any cloud resources that were created, and `region` is the aws region in which cloud resources were created. +## Azure + +### Global flags + +By default, the tool will output to the directory the command(s) were run in. To specify a directory, use the `--output-dir` flag. + +Commands which would otherwise make Azure API calls can be passed the `--dry-run` flag to display defaulted arguments and to have `ccoctl` place the OpenID and cluster authentication configuration in the output dir. + +Azure resources that allow tagging can be tagged with provided user tags by specifying `--user-tags exampletag1=examplevalue1,exampletag2=examplevalue2`. + +### Creating RSA keys + +To generate keys for use when setting up the cluster's OpenID Connect provider, run + +```bash +$ ccoctl azure create-key-pair +``` + +### Creating OpenID Connect Issuer + +To set up an OIDC Issuer in Azure, the following command will create an Azure ResourceGroup, StorageAccount, and Blob Container which will contain OpenID configuration as well as the provided public key. + +```bash +$ ccoctl azure create-oidc-issuer --name \ + --output-dir \ + --region \ + --subscription-id \ + --public-key-file /path/to/rsa/keypair/serviceaccount-signer.public \ +``` + +Note that `create-oidc-issuer` outputs an Issuer URL which is needed when creating managed identities. + +### Creating Managed Identities + +To create User-Assigned Managed Identities for each in-cluster component, you need to first extract the list of CredentialsRequest objects from the OpenShift release image. + +```bash +$ oc adm release extract --credentials-requests --cloud=azure --to=./credrequests quay.io/path/to/openshift-release:version +``` + +Then you can use `ccoctl` to process each CredentialsRequest object in the `./credrequests` directory (from the example above). + +This command will create an empty Azure ResourceGroup to serve as the installation resource group with which to scope permissions granted to the created identities. This ResourceGroup must be configured as the cluster installation group in `install-config.yaml` and the OpenShift installer requires that this resource group be previously empty. The Azure ResoureGroup containing the cluster DNS Zone must also be known for scoping and provided as `--dnszone-resource-group-name`. + +```bash +$ ccoctl azure create-managed-identities --name \ + --output-dir \ + --region \ + --subscription-id \ + --credentials-requests-dir ./credrequests \ + --issuer-url \ + --dnszone-resource-group-name +``` + +### Creating all the required resources together + +To create all the above mentioned resources in one go, run + +```bash +$ oc adm release extract --credentials-requests --cloud=azure --to=./credrequests quay.io/path/to/openshift-release:version +``` + +Then you can use `ccoctl` to process all CredentialsRequest objects in the `./credrequests` directory (from the example above). + +This command will create an empty Azure ResourceGroup to serve as the installation resource group with which to scope permissions granted to the created identities. This ResourceGroup must be configured as the cluster installation group in `install-config.yaml` and the OpenShift installer requires that this resource group be previously empty. The Azure ResoureGroup containing the cluster DNS Zone must also be known for scoping and provided as `--dnszone-resource-group-name`. + +```bash +$ ccoctl azure create-all --name \ + --output-dir \ + --region \ + --subscription-id \ + --credentials-requests-dir ./credrequests \ + --dnszone-resource-group-name +``` + +### Deleting resources + +To delete resources created by ccoctl, run + +```bash +$ ccoctl azure delete --name --region --subscription-id --delete-oidc-resource-group +``` + ## GCP ### Global flags From 67227ffa64e05959a81287fd18d2305f81d7b817 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Fri, 14 Jul 2023 10:04:00 -0400 Subject: [PATCH 2/6] Adjustments and fixes from comments. --- docs/azure_workload_identity.md | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/azure_workload_identity.md b/docs/azure_workload_identity.md index 4fe3d82e8..bea485ee2 100644 --- a/docs/azure_workload_identity.md +++ b/docs/azure_workload_identity.md @@ -21,6 +21,8 @@ data: azure_client_id: azure_client_secret: azure_region: + azure_resource_prefix: + azure_resourcegroup: azure_subscription_id: azure_tenant_id: kind: Secret @@ -61,16 +63,16 @@ type: Opaque 3. Extract the `openshift-install` and `ccoctl` binaries from the release image. ``` - oc adm release extract --command=openshift-install $RELEASE_IMAGE + oc adm release extract --command=openshift-install $RELEASE_IMAGE --registry-config ~/.pull-secret - CCO_IMAGE=$(oc adm release info --image-for='cloud-credential-operator' ${RELEASE_IMAGE}) && oc image extract ${CCO_IMAGE} --file='/usr/bin/ccoctl' --registry-config=${PULL_SECRET_PATH:-.}/pull-secret && chmod +x ccoctl + CCO_IMAGE=$(oc adm release info --image-for='cloud-credential-operator' ${RELEASE_IMAGE}) && oc image extract ${CCO_IMAGE} --file='/usr/bin/ccoctl' --registry-config ~/.pull-secret && chmod +x ccoctl ``` -4. Create Azure resources using the [ccoctl](./ccoctl.md) tool. You will need Azure credentials with sufficient permissions. The Azure credentials can be automatically detected after having logged into the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) `az cli login` or may be provided as environment variables (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`). +4. Create Azure resources using the [ccoctl](./ccoctl.md) tool. You will need Azure credentials with sufficient permissions. The Azure credentials can be automatically detected after having logged into the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) `az login` or may be provided as environment variables (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`). The following command will, * Generate public/private ServiceAccount signing RSA keys. - * Create an empty Azure ResourceGroup in which to install the cluster. This ResourceGroup is used to scope created identities must be configured as the cluster insallation ResourceGroup within the `install-config.yaml`. + * Create an empty Azure ResourceGroup in which to install the cluster. This ResourceGroup is used to scope created identities and must be configured as the cluster installation ResourceGroup within the `install-config.yaml`. * Create an Azure ResourceGroup in which to create identity resources. * Create an Azure StorageAccount, Azure Blob Container and upload OIDC configuration to the Blob Container. * Create User-Assigned Managed Identities for each Azure CredentialsRequest. @@ -106,25 +108,31 @@ type: Opaque yq -i '.platform.azure.resourceGroupName = ""' install-config.yaml ``` -8. Create install manifests. +8. Configure `install-config.yaml` to enable the tech preview featureset. + + ``` + echo "featureSet: TechPreviewNoUpgrade" >> install-config.yaml + ``` + +9. Create install manifests. ``` ./openshift-install create manifests ``` -9. Copy the manifests created in the step 4 and put them in the install `manifests` directory generated by `openshift-install create manifests`. +10. Copy the manifests created in the step 4 and put them in the install `manifests` directory generated by `openshift-install create manifests`. ``` cp ccoctl_output_dir/manifests/* /path/to/dir/with/install/manifests/ ``` -10. Copy the private key for the ServiceAccount signer and put it in the same location as `install-config.yaml`. +11. Copy the private key for the ServiceAccount signer and put it in the same location as `install-config.yaml`. ``` cp -a ccoctl_output_dir/tls /path/to/dir/with/install-config.yaml ``` -11. Run the OpenShift installer. +12. Run the OpenShift installer. ``` ./openshift-install create cluster --log-level=debug @@ -142,7 +150,7 @@ type: Opaque 2. Verify that components are assuming the `azure_client_id` specified in the secret manifests, instead of credentials passed through by the Cloud Credential Operator. The secret displayed should not contain an `azure_client_secret` key and will instead contain an `azure_federated_token_file` key. ```yaml - oc get secrets -n openshift-image-registry cloud-credentials -o yaml + oc get secrets -n openshift-image-registry installer-cloud-credentials -o yaml ``` sample output of the above command ```json From cf20a62af76fa90b18b38ed32aef4d9fd0f14d92 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Fri, 14 Jul 2023 10:30:03 -0400 Subject: [PATCH 3/6] Add a step for installing a recent OpenShift CLI. --- docs/azure_workload_identity.md | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/docs/azure_workload_identity.md b/docs/azure_workload_identity.md index bea485ee2..26854d3f7 100644 --- a/docs/azure_workload_identity.md +++ b/docs/azure_workload_identity.md @@ -45,7 +45,11 @@ type: Opaque ### Steps to install an OpenShift Cluster with Azure AD Workload Identity -1. Set the `$RELEASE_IMAGE` environment variable. +1. Obtain a recent version of the OpenShift CLI `oc`. + + Reference the [OpenShift CLI installation steps in the OpenShift documentation](https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/getting-started-cli.html). + +2. Set the `$RELEASE_IMAGE` environment variable. `$RELEASE_IMAGE` should be a recent and supported OpenShift release image that you want to deploy in your cluster. Please refer to the [support matrix](../README.md#support-matrix) for compatibilities. @@ -54,13 +58,13 @@ type: Opaque Where `RHOCP_version` is the OpenShift version (e.g `4.14.0-fc.4` or `4.14.3`) and the `Arch` is the architecture type (e.g `x86_64`). -2. Extract Azure CredentialsRequests objects from the release image. +3. Extract Azure CredentialsRequests objects from the release image. ``` oc adm release extract --cloud=azure --credentials-requests $RELEASE_IMAGE --to=./credreqs ``` -3. Extract the `openshift-install` and `ccoctl` binaries from the release image. +4. Extract the `openshift-install` and `ccoctl` binaries from the release image. ``` oc adm release extract --command=openshift-install $RELEASE_IMAGE --registry-config ~/.pull-secret @@ -68,7 +72,7 @@ type: Opaque CCO_IMAGE=$(oc adm release info --image-for='cloud-credential-operator' ${RELEASE_IMAGE}) && oc image extract ${CCO_IMAGE} --file='/usr/bin/ccoctl' --registry-config ~/.pull-secret && chmod +x ccoctl ``` -4. Create Azure resources using the [ccoctl](./ccoctl.md) tool. You will need Azure credentials with sufficient permissions. The Azure credentials can be automatically detected after having logged into the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) `az login` or may be provided as environment variables (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`). +5. Create Azure resources using the [ccoctl](./ccoctl.md) tool. You will need Azure credentials with sufficient permissions. The Azure credentials can be automatically detected after having logged into the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) `az login` or may be provided as environment variables (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`). The following command will, * Generate public/private ServiceAccount signing RSA keys. @@ -84,23 +88,23 @@ type: Opaque --output-dir \ --region \ --subscription-id \ - --credentials-requests-dir /path/to/credreqs/directory/created/in/step/2 \ + --credentials-requests-dir /path/to/credreqs/directory/created/in/step/3 \ --dnszone-resource-group-name ``` -5. Create an install-config.yaml with the extracted OpenShift installer binary. Be sure to choose the same region as specified in step 4. +6. Create an install-config.yaml with the extracted OpenShift installer binary. Be sure to choose the same region as specified in step 5. ``` ./openshift-install create install-config ``` -6. Make sure that we install the cluster in Manual mode by setting the `credentialsMode` within `install-config.yaml`. +7. Make sure that we install the cluster in Manual mode by setting the `credentialsMode` within `install-config.yaml`. ``` echo "credentialsMode: Manual" >> install-config.yaml ``` -7. Configure the installation Azure ResourceGroup created in step 4 within the `install-config.yaml`. By default, the installation Azure ResourceGroup will be the `` used in step 4. +8. Configure the installation Azure ResourceGroup created in step 5 within the `install-config.yaml`. By default, the installation Azure ResourceGroup will be the `` used in step 5. The `install-config.yaml` must be modified to add the `resourceGroupName` key within Azure platform (`.platform.azure.resourceGroupName`). We can accomplish this using [yq](https://github.com/mikefarah/yq) or by editing `install-config.yaml` directly. @@ -108,31 +112,31 @@ type: Opaque yq -i '.platform.azure.resourceGroupName = ""' install-config.yaml ``` -8. Configure `install-config.yaml` to enable the tech preview featureset. +9. Configure `install-config.yaml` to enable the tech preview featureset. ``` echo "featureSet: TechPreviewNoUpgrade" >> install-config.yaml ``` -9. Create install manifests. +10. Create install manifests. ``` ./openshift-install create manifests ``` -10. Copy the manifests created in the step 4 and put them in the install `manifests` directory generated by `openshift-install create manifests`. +11. Copy the credentials manifests created in step 5 and place them in the install `manifests` directory generated by `openshift-install create manifests`. ``` cp ccoctl_output_dir/manifests/* /path/to/dir/with/install/manifests/ ``` -11. Copy the private key for the ServiceAccount signer and put it in the same location as `install-config.yaml`. +12. Copy the private key for the ServiceAccount signer and place it in the same location as `install-config.yaml`. ``` cp -a ccoctl_output_dir/tls /path/to/dir/with/install-config.yaml ``` -12. Run the OpenShift installer. +13. Run the OpenShift installer. ``` ./openshift-install create cluster --log-level=debug @@ -165,7 +169,7 @@ type: Opaque ### Cleanup Azure resources after uninstalling the cluster -Make sure you clean up the following resources after you uninstall your cluster. You can use the `` used in installation step 4 to identify these resources. +Make sure you clean up the following resources after you uninstall your cluster. You can use the `` used in installation step 5 to identify these resources. ``` ./ccoctl azure delete --name \ From 2953e21c0fd70055aa5a5246f90d00e80f4a34a2 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Fri, 14 Jul 2023 10:40:03 -0400 Subject: [PATCH 4/6] Use ccoctl_output_dir for consistency. --- docs/azure_workload_identity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure_workload_identity.md b/docs/azure_workload_identity.md index 26854d3f7..9af3cfd73 100644 --- a/docs/azure_workload_identity.md +++ b/docs/azure_workload_identity.md @@ -81,7 +81,7 @@ type: Opaque * Create an Azure StorageAccount, Azure Blob Container and upload OIDC configuration to the Blob Container. * Create User-Assigned Managed Identities for each Azure CredentialsRequest. - Manifest files needed for cluster installation are output to the `output_dir` directory. + Manifest files needed for cluster installation are output to the `ccoctl_output_dir` directory. ``` ./ccoctl azure create-all --name \ From 9c231892069a5241ecc1065a6ca6de2166458230 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Fri, 14 Jul 2023 16:17:09 -0400 Subject: [PATCH 5/6] Swap s/projected/mounted/s The term "projected" comes from projected volumes https://kubernetes.io/docs/tasks/configure-pod-container/configure-projected-volume-storage/ but "mounted" illustrates the idea more simply without having to understand the volume type. --- docs/azure_workload_identity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/azure_workload_identity.md b/docs/azure_workload_identity.md index 9af3cfd73..895b0bd6b 100644 --- a/docs/azure_workload_identity.md +++ b/docs/azure_workload_identity.md @@ -1,7 +1,7 @@ # Azure AD Workload Identity ### Overview -OpenShift can be configured to use temporary credentials for workload components with [Azure AD Workload Identity](https://azure.github.io/azure-workload-identity/docs/). In this model, the OpenShift cluster signs Kubernetes ServiceAccount tokens which can be trusted by an external OIDC identity provider. A component such as an operator running on the cluster can exchange the signed ServiceAccount token projected into its Pod volume for an Azure Active Directory (AD) access token using [Azure Identity SDKs](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity). This process also automates requesting and refreshing of credentials. The ServiceAccount token used by a Pod is rotated by the kubelet on the node where Pod is running when the Pod's ServiceAccount token is approaching expiry. ServiceAccount tokens are valid for one hour. +OpenShift can be configured to use temporary credentials for workload components with [Azure AD Workload Identity](https://azure.github.io/azure-workload-identity/docs/). In this model, the OpenShift cluster signs Kubernetes ServiceAccount tokens which can be trusted by an external OIDC identity provider. A component such as an operator running on the cluster can exchange the signed ServiceAccount token mounted into its Pod volume for an Azure Active Directory (AD) access token using [Azure Identity SDKs](https://github.com/Azure/azure-sdk-for-go/tree/main/sdk/azidentity). This process also automates requesting and refreshing of credentials. The ServiceAccount token used by a Pod is rotated by the kubelet on the node where Pod is running when the Pod's ServiceAccount token is approaching expiry. ServiceAccount tokens are valid for one hour. This diagram illustrates how it works. @@ -29,7 +29,7 @@ kind: Secret type: Opaque ``` -With Azure AD Workload Identity, an Azure credentials secret contains the `azure_client_id` of the User-Assigned Managed Identity that the component will be authenticating as along with the path to the projected ServiceAccount token, `azure_federated_token_file`. The ServiceAccount token mounted at this path is refreshed hourly and thus the credentials are short-lived. +With Azure AD Workload Identity, an Azure credentials secret contains the `azure_client_id` of the User-Assigned Managed Identity that the component will be authenticating as along with the path to the mounted ServiceAccount token, `azure_federated_token_file`. The ServiceAccount token mounted at this path is refreshed hourly and thus the credentials are short-lived. ```yaml apiVersion: v1 From 31dbbffde8d9fad7cd11fd14525b888551a05370 Mon Sep 17 00:00:00 2001 From: Andrew Butcher Date: Thu, 20 Jul 2023 14:48:20 -0400 Subject: [PATCH 6/6] Use simpler extract command for ccoctl. --- docs/azure_workload_identity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/azure_workload_identity.md b/docs/azure_workload_identity.md index 895b0bd6b..b1880d1ae 100644 --- a/docs/azure_workload_identity.md +++ b/docs/azure_workload_identity.md @@ -69,7 +69,7 @@ type: Opaque ``` oc adm release extract --command=openshift-install $RELEASE_IMAGE --registry-config ~/.pull-secret - CCO_IMAGE=$(oc adm release info --image-for='cloud-credential-operator' ${RELEASE_IMAGE}) && oc image extract ${CCO_IMAGE} --file='/usr/bin/ccoctl' --registry-config ~/.pull-secret && chmod +x ccoctl + oc adm release extract --command=ccoctl $RELEASE_IMAGE --registry-config ~/.pull-secret ``` 5. Create Azure resources using the [ccoctl](./ccoctl.md) tool. You will need Azure credentials with sufficient permissions. The Azure credentials can be automatically detected after having logged into the [Azure CLI](https://learn.microsoft.com/en-us/cli/azure/install-azure-cli) `az login` or may be provided as environment variables (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`).