Skip to content

Commit

Permalink
Add scripts to create platform identities for dev (#3734)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Nicolas Ontiveros <[email protected]>
  • Loading branch information
niontive and Nicolas Ontiveros authored Aug 21, 2024
1 parent 309d82a commit fd8d3cf
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 17 deletions.
5 changes: 3 additions & 2 deletions docs/deploy-development-rp.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ It uses hacks scripts around a lot of the setup to make things easier to bootstr
SECRET_SA_ACCOUNT_NAME=rharosecretsdev make secrets
```

1. Run [msi.sh](../hack/devtools/msi.sh) to create a service principal and self-signed certificate to
mock a cluster MSI. Save the output values for `Client ID`, `Base64 Encoded Certificate`, and `Tenant`.
1. Run [msi.sh](../hack/devtools/msi.sh) to create a service principal and self-signed certificate to
mock a cluster MSI. This script will also create the platform identities, platform identity role assignments, and role assignment on mock cluster MSI to federate the platform identities. Platform identities will be created in resource group `RESOURCEGROUP` and subscription `SUBSCRIPTION`. Save the output values for cluster MSI `Client ID`, `Base64 Encoded Certificate`, and `Tenant`. Additionally, save the value for `Platform workload identity role sets`.

1. Copy, edit (if necessary) and source your environment file. The required
environment variable configuration is documented immediately below:
Expand All @@ -79,6 +79,7 @@ It uses hacks scripts around a lot of the setup to make things easier to bootstr
- `MOCK_MSI_CLIENT_ID`: Client ID for service principal that mocks cluster MSI (see previous step).
- `MOCK_MSI_CERT`: Base64 encoded certificate for service principal that mocks cluster MSI (see previous step).
- `MOCK_MSI_TENANT_ID`: Tenant ID for service principal that mocks cluster MSI (see previous step).
- `PLATFORM_WORKLOAD_IDENTITY_ROLE_SETS`: The platform workload identity role sets (see previous step or value in `local_dev_env.sh`).

1. Create your own RP database:

Expand Down
177 changes: 169 additions & 8 deletions hack/devtools/local_dev_env.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail

# Local development environment script.
# Execute this script from the root folder of the repo (ARO-RP).
Expand All @@ -7,6 +10,71 @@
# The steps here are the ones defined in docs/deploy-development-rp.md
# We recommend to use this script after you understand the steps of the process, not before.

PLATFORM_WORKLOAD_IDENTITY_ROLE_SETS='[
{
"openShiftVersion": "4.14",
"platformWorkloadIdentityRoles": [
{
"operatorName": "CloudControllerManager",
"roleDefinitionName": "Azure Red Hat OpenShift Cloud Controller Manager Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/a1f96423-95ce-4224-ab27-4e3dc72facd4",
"serviceAccounts": ["openshift-cloud-controller-manager:cloud-controller-manager"]
},
{
"operatorName": "ClusterIngressOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Cluster Ingress Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/0336e1d3-7a87-462b-b6db-342b63f7802c",
"serviceAccounts": ["openshift-ingress-operator:ingress-operator"]
},
{
"operatorName": "MachineApiOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Machine API Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/0358943c-7e01-48ba-8889-02cc51d78637",
"serviceAccounts": ["openshift-machine-api:machine-api-operator"]
},
{
"operatorName": "StorageOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Storage Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/5b7237c5-45e1-49d6-bc18-a1f62f400748",
"serviceAccounts": [
"openshift-cluster-csi-drivers:azure-disk-csi-driver-operator",
"openshift-cluster-csi-drivers:azure-disk-csi-driver-controller-sa"
]
},
{
"operatorName": "NetworkOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Network Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/be7a6435-15ae-4171-8f30-4a343eff9e8f",
"serviceAccounts": ["openshift-cloud-network-config-controller:cloud-network-config-controller"]
},
{
"operatorName": "ImageRegistryOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Image Registry Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/8b32b316-c2f5-4ddf-b05b-83dacd2d08b5",
"serviceAccounts": [
"openshift-image-registry:cluster-image-registry-operator",
"openshift-image-registry:registry"
]
},
{
"operatorName": "AzureFilesStorageOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Azure Files Storage Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/0d7aedc0-15fd-4a67-a412-efad370c947e",
"serviceAccounts": [
"openshift-cluster-csi-drivers:azure-file-csi-driver-operator",
"openshift-cluster-csi-drivers:azure-file-csi-driver-controller-sa",
"openshift-cluster-csi-drivers:azure-file-csi-driver-node-sa"
]
},
{
"operatorName": "ServiceOperator",
"roleDefinitionName": "Azure Red Hat OpenShift Service Operator Role",
"roleDefinitionId": "/providers/Microsoft.Authorization/roleDefinitions/4436bae4-7702-4c84-919b-c4069ff25ee2",
"serviceAccounts": ["openshift-azure-operator:aro-operator-master"]
}
]
}
]'

build_development_az_aro_extension() {
echo "INFO: Building development az aro extension..."
Expand All @@ -28,7 +96,7 @@ set_storage_account() {

ask_to_create_default_env_config() {
local answer
read -p "Do you want to create a default env file? (existing one will be overwritten, if any) (y / n) " answer
read -r -p "Do you want to create a default env file? (existing one will be overwritten, if any) (y / n) " answer

if [[ "$answer" == "y" || "$answer" == "Y" ]]; then
create_env_file
Expand All @@ -42,7 +110,7 @@ ask_to_create_default_env_config() {
# We use a service principal and certificate as the mock MSI object
create_mock_msi() {
appName="mock-msi-$(openssl rand -base64 9 | tr -dc 'a-zA-Z0-9' | head -c 6)"
az ad sp create-for-rbac --name $appName --create-cert --output json
az ad sp create-for-rbac --name "$appName" --create-cert --output json
}

get_mock_msi_clientID() {
Expand All @@ -55,21 +123,110 @@ get_mock_msi_tenantID() {

get_mock_msi_cert() {
certFilePath=$(echo "$1" | jq -r '.fileWithCertAndPrivateKey')
base64EncodedCert=$(base64 -w 0 $certFilePath)
rm $certFilePath
echo $base64EncodedCert
base64EncodedCert=$(base64 -w 0 "$certFilePath")
rm "$certFilePath"
echo "$base64EncodedCert"
}

create_env_file() {
local answer
read -p "Do you want to create an env file for Managed/Workload identity development? " answer
read -r -p "Do you want to create an env file for Managed/Workload identity development? (y / n) " answer
if [[ "$answer" == "y" || "$answer" == "Y" ]]; then
create_miwi_env_file
else
create_regular_env_file
fi
}

get_platform_workloadIdentity_role_sets() {
# Parse the JSON data using jq
platformWorkloadIdentityRoles=$(echo "${PLATFORM_WORKLOAD_IDENTITY_ROLE_SETS}" | jq -c '.[].platformWorkloadIdentityRoles[]')

echo "${platformWorkloadIdentityRoles}"
}

assign_role_to_identity() {
local objectId=$1
local roleId=$2
local scope="/subscriptions/${AZURE_SUBSCRIPTION_ID}/resourceGroups/${RESOURCEGROUP}"
local roles

if ! roles=$(az role assignment list --assignee "${objectId}" --role "${roleId}" --scope "${scope}" 2>/dev/null); then
# If the role assignment list fails, we assume the identity is newly created and we can proceed with the role assignment
echo "INFO: Unable to list role assignments for identity: ${objectId}"
fi

if [ "$roles" == "" ] || [ "$roles" == "[]" ] ; then
echo "INFO: Assigning role to identity: ${objectId}"
az role assignment create --assignee-object-id "${objectId}" --assignee-principal-type "ServicePrincipal" --role "${roleId}" --scope "${scope}" --output json
echo ""
else
echo "INFO: Role already assigned to identity: ${objectId}"
echo ""
fi
}

create_platform_identity_and_assign_role() {
local operatorName="${1}"
local roleDefinitionId="${2}"
local identityName="aro-${operatorName}"
local identity

if ! identity=$(az identity show --name "${identityName}" --resource-group "${RESOURCEGROUP}" --subscription "${AZURE_SUBSCRIPTION_ID}" --output json 2>/dev/null); then
echo "INFO: Creating platform identity for operator: ${operatorName}"
identity=$(az identity create --name "${identityName}" --resource-group "${RESOURCEGROUP}" --subscription "${AZURE_SUBSCRIPTION_ID}" --output json)
fi

# Extract the client ID, principal Id, resource ID and name from the result
clientID=$(jq -r .clientId <<<"${identity}")
principalId=$(jq -r .principalId <<<"${identity}")
resourceId=$(jq -r .id <<<"${identity}")
name=$(jq -r .name <<<"${identity}")

echo "Client ID: $clientID"
echo "Principal ID: $principalId"
echo "Resource ID: $resourceId"
echo "Name: $name"
echo ""

# The following operators require a role assignment since they need access to customer BYO virtual network
# RP will be responsible for other role assignments
if [[ "${operatorName}" == "MachineApiOperator" || "${operatorName}" == "NetworkOperator" \
|| "${operatorName}" == "AzureFilesStorageOperator" || "${operatorName}" == "ServiceOperator" ]]; then

assign_role_to_identity "${principalId}" "${roleDefinitionId}"
fi
}

setup_platform_identity() {
local platformWorkloadIdentityRoles

platformWorkloadIdentityRoles=$(get_platform_workloadIdentity_role_sets)

echo "INFO: Creating platform identities under RG ($RESOURCEGROUP) and Sub Id ($AZURE_SUBSCRIPTION_ID)"
echo ""

# Loop through each element under platformWorkloadIdentityRoles
while read -r role; do
operatorName=$(echo "$role" | jq -r '.operatorName')
roleDefinitionId=$(echo "$role" | jq -r '.roleDefinitionId' | awk -F'/' '{print $NF}')

create_platform_identity_and_assign_role "${operatorName}" "${roleDefinitionId}"

done <<< "$platformWorkloadIdentityRoles"
}

cluster_msi_role_assignment() {
local clusterMSIAppID="${1}"
local FEDERATED_CREDENTIAL_ROLE_ID="ef318e2a-8334-4a05-9e4a-295a196c6a6e"
local clusterMSIObjectID

clusterMSIObjectID=$(az ad sp show --id "${clusterMSIAppID}" --query '{objectId: id}' | jq -r .objectId)

echo "INFO: Assigning role to cluster MSI: ${clusterMSIAppID}"
assign_role_to_identity "${clusterMSIObjectID}" "${FEDERATED_CREDENTIAL_ROLE_ID}"
}

create_miwi_env_file() {
echo "INFO: Creating default env config file for managed/workload identity development..."

Expand All @@ -78,13 +235,17 @@ create_miwi_env_file() {
mockTenantID=$(get_mock_msi_tenantID "$mockMSI")
mockCert=$(get_mock_msi_cert "$mockMSI")

setup_platform_identity
cluster_msi_role_assignment "${mockClientID}"

cat >env <<EOF
export LOCATION=eastus
export ARO_IMAGE=arointsvc.azurecr.io/aro:latest
export RP_MODE=development # to use a development RP running at https://localhost:8443/
export MOCK_MSI_CLIENT_ID="$mockClientID"
export MOCK_MSI_TENANT_ID="$mockTenantID"
export MOCK_MSI_CERT="$mockCert"
export PLATFORM_WORKLOAD_IDENTITY_ROLE_SETS="$PLATFORM_WORKLOAD_IDENTITY_ROLE_SETS"
source secrets/env
EOF
Expand All @@ -105,7 +266,7 @@ EOF

ask_to_create_Azure_deployment() {
local answer
read -p "Create Azure deployment in the current subscription ($AZURE_SUBSCRIPTION_ID)? (y / n / l (list existing deployments)) " answer
read -p -r "Create Azure deployment in the current subscription ($AZURE_SUBSCRIPTION_ID)? (y / n / l (list existing deployments)) " answer

if [[ "$answer" == "y" || "$answer" == "Y" ]]; then
create_Azure_deployment
Expand All @@ -121,7 +282,7 @@ ask_to_create_Azure_deployment() {

list_Azure_deployment_names() {
echo "INFO: Existing deployment names in the current subscription ($AZURE_SUBSCRIPTION_ID):"
az deployment group list --resource-group $RESOURCEGROUP | jq '[ .[] | {deployment_name: ( .id ) | split("/deployments/")[1] } | .deployment_name ]'
az deployment group list --resource-group "$RESOURCEGROUP" | jq '[ .[] | {deployment_name: ( .id ) | split("/deployments/")[1] } | .deployment_name ]'
}

create_Azure_deployment() {
Expand Down
31 changes: 24 additions & 7 deletions hack/devtools/msi.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
#!/bin/bash
set -o errexit
set -o nounset
set -o pipefail

# This script creates a mock MSI object to use for local development
# We use a service principal and certificate as the mock object
# This script creates a mock cluster MSI object and platform identities to use for local development
# We use a service principal and certificate as the mock cluster MSI

if [[ -z "${AZURE_SUBSCRIPTION_ID:-}" ]]; then
echo "Error: AZURE_SUBSCRIPTION_ID is not set."
exit 1
fi

if [[ -z "${RESOURCEGROUP:-}" ]]; then
echo "Error: RESOURCEGROUP is not set."
exit 1
fi

scriptPath=$(realpath "$0")
scriptDir=$(dirname "$scriptPath")
Expand All @@ -13,11 +26,15 @@ if [[ -z "$sp" ]]; then
echo "Failed to create mock MSI object"
exit 1
fi
clientID=$(get_mock_msi_clientID "$sp")
tenantID=$(get_mock_msi_tenantID "$sp")
mockClientID=$(get_mock_msi_clientID "$sp")
mockTenantID=$(get_mock_msi_tenantID "$sp")
base64EncodedCert=$(get_mock_msi_cert "$sp")

setup_platform_identity
cluster_msi_role_assignment "${mockClientID}"

# Print the extracted values
echo "Client ID: $clientID"
echo "Tenant ID: $tenantID"
echo "Base64 Encoded Certificate: $base64EncodedCert"
echo "Cluster MSI Client ID: $mockClientID"
echo "Cluster MSI Tenant ID: $mockTenantID"
echo "Cluster MSI Base64 Encoded Certificate: $base64EncodedCert"
echo "Platform workload identity role sets: $PLATFORM_WORKLOAD_IDENTITY_ROLE_SETS"

0 comments on commit fd8d3cf

Please sign in to comment.