Skip to content

Commit

Permalink
Merge pull request #566 from abutcher/CCO-233
Browse files Browse the repository at this point in the history
CCO-233: Add Azure AD Workload Identity doc.
  • Loading branch information
openshift-merge-robot committed Jul 20, 2023
2 parents a6d615c + 31dbbff commit 1f37b78
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 6 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -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 | N<sup>1</sup> | N | Y | Y | N
Azure | N<sup>1</sup> | 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
Expand Down
Binary file added docs/azure_ad_workload_identity_flow.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
179 changes: 179 additions & 0 deletions docs/azure_workload_identity.md
@@ -0,0 +1,179 @@
# 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 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.

![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: <client id>
azure_client_secret: <client secret>
azure_region: <region>
azure_resource_prefix: <resource group prefix eg. "mycluster-az-t68n4">
azure_resourcegroup: <resource group eg. "mycluster-az-t68n4-rg">
azure_subscription_id: <subscription id>
azure_tenant_id: <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 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
data:
azure_client_id: <client id>
azure_federated_token_file: </path/to/mounted/service/account/token>
azure_region: <region>
azure_subscription_id: <subscription id>
azure_tenant_id: <tenant id>
kind: Secret
type: Opaque
```

### Steps to install an OpenShift Cluster with Azure AD Workload Identity

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.

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`).

3. Extract Azure CredentialsRequests objects from the release image.

```
oc adm release extract --cloud=azure --credentials-requests $RELEASE_IMAGE --to=./credreqs
```

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
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`).

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 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.

Manifest files needed for cluster installation are output to the `ccoctl_output_dir` directory.

```
./ccoctl azure create-all --name <azure_infra_name> \
--output-dir <ccoctl_output_dir> \
--region <azure_region> \
--subscription-id <azure_subscription_id> \
--credentials-requests-dir /path/to/credreqs/directory/created/in/step/3 \
--dnszone-resource-group-name <azure_dns_zone_resourcegroup_name>
```

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
```

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
```

8. Configure the installation Azure ResourceGroup created in step 5 within the `install-config.yaml`. By default, the installation Azure ResourceGroup will be the `<azure_infra_name>` 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.

```
yq -i '.platform.azure.resourceGroupName = "<azure_infra_name>"' install-config.yaml
```

9. Configure `install-config.yaml` to enable the tech preview featureset.

```
echo "featureSet: TechPreviewNoUpgrade" >> install-config.yaml
```

10. Create install manifests.

```
./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/
```

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
```

13. 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 installer-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 `<azure_infra_name>` used in installation step 5 to identify these resources.

```
./ccoctl azure delete --name <azure_infra_name> \
--region <azure_region> \
--subscription-id <azure_subscription_id> \
--delete-oidc-resource-group
```
100 changes: 95 additions & 5 deletions docs/ccoctl.md
Expand Up @@ -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)
Expand Down Expand Up @@ -95,7 +102,7 @@ Then you can use `ccoctl` to process all CredentialsRequest objects in the `./cr
$ ccoctl aws create-all --name=<name> --region=<aws-region> --credentials-requests-dir=<path-to-directory-with-list-of-credentials-requests> --create-private-s3-bucket
```

### Deleting resources<a name="aws-delete"></a>
### Deleting resources


To delete resources created by ccoctl, run
Expand All @@ -106,6 +113,89 @@ $ ccoctl aws delete --name=<name> --region=<aws-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 <azure_infra_name> \
--output-dir <output_dir> \
--region <azure_region> \
--subscription-id <azure_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 <azure_infra_name> \
--output-dir <output_dir> \
--region <azure_region> \
--subscription-id <azure_subscription_id> \
--credentials-requests-dir ./credrequests \
--issuer-url <issuer url generated when creating the oidc issuer> \
--dnszone-resource-group-name <azure resource group containing the dns zone of the cluster>
```

### 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 <azure_infra_name> \
--output-dir <output_dir> \
--region <azure_region> \
--subscription-id <azure_subscription_id> \
--credentials-requests-dir ./credrequests \
--dnszone-resource-group-name <azure resource group containing the dns zone of the cluster>
```

### Deleting resources

To delete resources created by ccoctl, run

```bash
$ ccoctl azure delete --name <azure_infra_name> --region <azure_region> --subscription-id <azure_subscription_id> --delete-oidc-resource-group
```

## GCP

### Global flags
Expand Down

0 comments on commit 1f37b78

Please sign in to comment.