Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
7479c77
Initial e2e testing for external auth for ARO HCP.
Aug 5, 2025
c3eb0cf
Fix formatting and label usage for external auth API test
Aug 5, 2025
f4dda64
Add license header to external_auth_cluster.go
Aug 5, 2025
96bdc7b
updating bicep to param files.
Aug 5, 2025
ede1ac5
adding fix update-bicep-json.sh .
Aug 5, 2025
aa18e0b
Some breadcrumbs
mociarain Aug 6, 2025
c330c9b
adding external auth demo scripts.
Aug 6, 2025
efdf928
Fix: clean external auth config and update bicep artifacts
Aug 6, 2025
09901c7
Resolve merge conflict in per_invocation_framework.go
Aug 6, 2025
a2f74fc
add missing depends on steps to housekeeping steps (#2375)
geoberle Aug 5, 2025
e38511b
Fix create mock identities
janboll Aug 5, 2025
e7498fc
add image registry disablement test
deads2k Aug 4, 2025
5906b0e
Additional permissions for ev2
janboll Aug 5, 2025
00c662b
Add endpoints
mociarain Jul 15, 2025
145ed17
Add the basic models
mociarain Jul 18, 2025
6d4891b
Tidy up internal tests
mociarain Jul 18, 2025
489258a
Fix enum
mociarain Jul 18, 2025
2b0d2e8
Add more internals
mociarain Jul 21, 2025
4555534
Add VersionedHCPOpenShiftClusterExternalAuth
mociarain Jul 21, 2025
9095524
Comment out some pieces
mociarain Jul 21, 2025
95e8b1b
Linting fixes
mociarain Jul 21, 2025
36c5985
Fix typo
mociarain Jul 21, 2025
0e18d46
Fix typos
mociarain Jul 22, 2025
fdbe16a
Drop unneeded TODO
mociarain Jul 22, 2025
163212c
WIP
mociarain Jul 22, 2025
4922ad7
Add ocm clients and tests
mociarain Jul 22, 2025
1cf9c28
WIP
mociarain Jul 22, 2025
ffbc67a
Tidy up CSClient
mociarain Jul 23, 2025
dc692eb
Add ValidateStatic method to ExternalAuth
mociarain Jul 23, 2025
c9310f1
Wire up create and update
mociarain Jul 23, 2025
1d764ed
Explicitly check for nil
mociarain Jul 24, 2025
0955192
Remove duplicate import
mociarain Jul 24, 2025
7e296d8
Add testing internals
mociarain Jul 24, 2025
15536a7
Add createOrUpdate test
mociarain Jul 24, 2025
839a428
Remove old comment
mociarain Jul 25, 2025
0651f30
Regenerate mocks
mociarain Jul 25, 2025
61d8c59
Update a comment
mociarain Jul 25, 2025
569a6d3
Tidy up JSON validation
mociarain Jul 31, 2025
f47b6bf
Update comments
mociarain Jul 31, 2025
afdb84c
Use better name
mociarain Jul 31, 2025
ebb5262
Turn back the clock
mociarain Aug 1, 2025
29510fc
Move the routes around
mociarain Aug 1, 2025
06790ab
Add the list endpoint
mociarain Aug 1, 2025
1613644
Remove unnecessary tags
mociarain Aug 1, 2025
1165301
Fix typo
mociarain Aug 1, 2025
c2f5efd
Fix typo
mociarain Aug 1, 2025
3d213b6
Tidy up the JSON validation logic
mociarain Aug 1, 2025
fb7c37b
Fix validation logic
mociarain Aug 5, 2025
8a584e4
Handle Groups as a pointer
mociarain Aug 5, 2025
638137b
Fix InternalId pattern
mociarain Aug 5, 2025
a454ad4
Add Type to DB ResourceDocument Types
mociarain Aug 5, 2025
4df34eb
Support GET and DELETE
mociarain Aug 5, 2025
8af31b3
Add cURL commands
mociarain Aug 5, 2025
d9b345e
Fix test
mociarain Aug 5, 2025
dc2a6e5
Update ArmDeploymentPreflight
mociarain Aug 5, 2025
3907884
Always set ExternalAuthDocs to Succeeded
mociarain Aug 5, 2025
0f01b8f
Set the correct label
mociarain Aug 5, 2025
413b2c9
fix: remove admin env context from adminCertName
bennerv Aug 5, 2025
36c1490
use the shared_dir for backup cleanup from e2e
deads2k Aug 4, 2025
a62b17e
denyassignment env variable for CS
geoberle Aug 5, 2025
a52b557
Use correct ev2 role
janboll Aug 6, 2025
32f66c5
bump cluster services image to 089791c74a681b9173025d0c72aad621e97e5f…
ziccardi Aug 5, 2025
cbb0ffd
Make kv role configurable
janboll Aug 6, 2025
c516108
Remove old access block
janboll Aug 6, 2025
bf123ae
e2e setup: improve fallback and env var handling for cluster creation
patriksuba Jul 30, 2025
27454c0
Refactor E2E test artifact handling and Bicep build integration
patriksuba Jul 31, 2025
8b86c0e
Create directory generated-test-artifacts if not already present.
patriksuba Jul 31, 2025
49e7f49
Mention Fallback to bicep setup in README
patriksuba Jul 31, 2025
71668da
Fix error message typos for teardown validation test case
patriksuba Jul 31, 2025
d6d019c
Fix extraction of managed identities from infra-only bicep deployment
patriksuba Aug 1, 2025
c99f0e6
Create a new helper function to return bytes from bicep deployment.
patriksuba Aug 4, 2025
d31b578
Update fallback bicep creation with resource group helper from framew…
patriksuba Aug 5, 2025
1ab7ed8
Update location of ARM templates. Build ARM templates into container.
patriksuba Aug 5, 2025
593c0dd
fix up compile due to two passing PRs
deads2k Aug 6, 2025
bddd8a7
config: Bump RP images
Aug 5, 2025
8e12657
add config parameters for postgres backup settings
jfchevrette Jul 29, 2025
2458113
Set the default of enabled
mociarain Aug 6, 2025
b17ad12
Fix the OCM layer
mociarain Aug 6, 2025
e48a831
Add James and David to test dir owners
jharrington22 Aug 6, 2025
91ae4df
bump HO in MSFT envs
geoberle Aug 6, 2025
81c3ed9
Force centraluseuap to be non-zonal
tsatam Aug 6, 2025
7bc2266
Update eastus2euap to use zones 1,3,4
tsatam Aug 6, 2025
4d131fd
Update dev-infrastructure/modules/common.bicep
tsatam Aug 6, 2025
75a1493
update demo cluster creation bicep
geoberle Aug 6, 2025
a64b405
also handle nodepools and non bicep
geoberle Aug 6, 2025
8cc6f94
update e2e
geoberle Aug 6, 2025
7e74034
tooling: bump ARO-Tools to latest revision
stevekuznetsov Aug 6, 2025
c97ca86
*: adapt to new ARO-Tools
stevekuznetsov Aug 6, 2025
0487834
update monitoring documentation
tony-schndr Jul 29, 2025
4362728
A Monumental One-Letter Namespace Correction That Will Echo Through t…
swiencki Aug 6, 2025
489d9d1
tooling: bump ARO-Tools to get execution constraints
stevekuznetsov Aug 6, 2025
016b6b8
pipelines: constrain global resource groups to uksouth
stevekuznetsov Aug 6, 2025
abfa7e0
bump cs image to efd4998. Fixes tls certs and CPO image override hard…
bennerv Aug 7, 2025
0c23f5e
Revert "pipelines: constrain global resource groups to uksouth"
geoberle Aug 7, 2025
3df837a
Revert "tooling: bump ARO-Tools to latest revision"
geoberle Aug 7, 2025
0a9a3d7
Initial e2e testing for external auth for ARO HCP.
Aug 5, 2025
6a74cda
Resolve merge conflict in per_invocation_framework.go
Aug 6, 2025
a96e04c
ExternalAuth E2E: add complete cluster test and supporting demo paylo…
Aug 7, 2025
8adead9
Merge branch 'main' into ARO-20092-ARO-externalauth-tests
Nanyte25 Aug 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions demo/bicep/external_auth.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
param name string
param location string = resourceGroup().location
param currentTime string = utcNow()
param consoleCallbackUrl string // e.g., https://console-openshift-console.apps.example.com/auth/callback

resource script 'Microsoft.Resources/deploymentScripts@2019-10-01-preview' = {
name: name
location: location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${resourceId("app-reg-automation", "Microsoft.ManagedIdentity/userAssignedIdentities", "AppRegCreator")}': {}
}
}
properties: {
azPowerShellVersion: '5.0'
arguments: '-resourceName "${name}" -consoleCallback "${consoleCallbackUrl}"'
scriptContent: '''
param([string] $resourceName, [string] $consoleCallback)

$token = (Get-AzAccessToken -ResourceUrl https://graph.microsoft.com).Token
$headers = @{'Content-Type' = 'application/json'; 'Authorization' = 'Bearer ' + $token}

$template = @{
displayName = $resourceName
signInAudience = "AzureADMyOrg"
web = @{
redirectUris = @($consoleCallback)
}
requiredResourceAccess = @(
@{
resourceAppId = "00000003-0000-0000-c000-000000000000"
resourceAccess = @(
@{
id = "e1fe6dd8-ba31-4d61-89e7-88639da4683d"
type = "Scope"
}
)
}
)
}

# Upsert App registration
$app = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/applications?filter=displayName eq '$($resourceName)'").value
$principal = @{}
if ($app) {
$ignore = Invoke-RestMethod -Method Patch -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)" -Body ($template | ConvertTo-Json -Depth 10)
$principal = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/servicePrincipals?filter=appId eq '$($app.appId)'").value
} else {
$app = (Invoke-RestMethod -Method Post -Headers $headers -Uri "https://graph.microsoft.com/beta/applications" -Body ($template | ConvertTo-Json -Depth 10))
$principal = Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/servicePrincipals" -Body (@{ "appId" = $app.appId } | ConvertTo-Json)
}

# Regenerate client secret
$app = (Invoke-RestMethod -Method Get -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)")
foreach ($password in $app.passwordCredentials) {
$body = @{ "keyId" = $password.keyId }
$ignore = Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)/removePassword" -Body ($body | ConvertTo-Json)
}

$body = @{ "passwordCredential" = @{ "displayName"= "Client Secret" } }
$secret = (Invoke-RestMethod -Method POST -Headers $headers -Uri "https://graph.microsoft.com/beta/applications/$($app.id)/addPassword" -Body ($body | ConvertTo-Json)).secretText

$DeploymentScriptOutputs = @{
objectId = $app.id
clientId = $app.appId
clientSecret = $secret
principalId = $principal.id
redirectUri = $consoleCallback
}
'''
cleanupPreference: 'OnSuccess'
retentionInterval: 'P1D'
forceUpdateTag: currentTime
}
}

output objectId string = script.properties.outputs.objectId
output clientId string = script.properties.outputs.clientId
output clientSecret string = script.properties.outputs.clientSecret
output principalId string = script.properties.outputs.principalId
output redirectUri string = script.properties.outputs.redirectUri
6 changes: 6 additions & 0 deletions demo/external-auth-payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"clientId": "00000000-0000-0000-0000-000000000000",
"clientSecret": "your-secret-value",
"issuer": "https://login.microsoftonline.com/your-tenant-id/v2.0",
"callbackUrl": "https://console-openshift.example.com/oauth2callback"
}
261 changes: 261 additions & 0 deletions demo/main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@

show_info_box() {
gum style --border double --margin "1" --padding "1 2" --border-foreground 99 \
"📘 External Entra Auth Prerequisites:" "" \
"• A fully created ARO-HCP cluster" \
"• A Hosted Control Plane (HCP) running" \
"• A NodePool created and provisioned" \
"• RP is port-forwarded and reachable on localhost:8443" \
"• Azure CLI is authenticated and target subscription is selected"
echo ""
}

#!/usr/bin/env bash
set -euo pipefail

# Icons
K8S="☸️"
AZ="🔷"
GIT="🌳"
CHECK="✅"
PENDING="⏳"

# Dependencies check
install_if_missing() {
cmd=$1
pkg=$2
icon=$3
if ! command -v "$cmd" &>/dev/null; then
echo "$icon Installing $cmd..."
sudo dnf install -y "$pkg"
fi
}

check_deps() {
install_if_missing az azure-cli "$AZ"
install_if_missing kubectl kubectl "$K8S"
install_if_missing jq jq "🧩"
install_if_missing gum gum "💎"
install_if_missing git git "$GIT"
}

# Verify az login
verify_az_login() {
echo "$AZ Verifying Azure login..."
az account show &>/dev/null || az login
}

# Print step with checkboxes
declare -A STATUS
STATUS=(
[entra]="⏳"
[group]="⏳"
[callback]="⏳"
[update_uri]="⏳"
[apply_rp]="⏳"
[test]="⏳"
)

print_status() {
clear
echo "🔐 External Auth Entra Setup Progress:"
echo "${STATUS[entra]} Create Entra App"
echo "${STATUS[group]} Create AD Group & Add User"
echo "${STATUS[callback]} Get Cluster Callback URL"
echo "${STATUS[update_uri]} Update Entra Redirect URI"
echo "${STATUS[apply_rp]} Apply Config via RP"
echo "${STATUS[test]} Test Redirect"
echo ""
}

create_entra_app() {
print_status
echo "$AZ Creating Entra App..."
app_name="ARO-HCP-Auth-$(date +%s)"
app_info=$(az ad app create --display-name "$app_name" --query '{appId: appId, id: id}' -o json)
client_id=$(echo "$app_info" | jq -r '.appId')
app_obj_id=$(echo "$app_info" | jq -r '.id')
secret_info=$(az ad app credential reset --id "$client_id" --append --display-name "AROSecret" -o json)
client_secret=$(echo "$secret_info" | jq -r '.password')
echo "client_id=$client_id" > demo_env.sh
echo "client_secret=$client_secret" >> demo_env.sh
echo "app_obj_id=$app_obj_id" >> demo_env.sh
echo "app_name=$app_name" >> demo_env.sh
STATUS[entra]="✅"
}

create_ad_group() {
print_status
echo "👥 Creating AD Group..."
source demo_env.sh
group_name="ARO-HCP-Admins"
az ad group create --display-name "$group_name" --mail-nickname "$group_name" &>/dev/null || true
read -p "Enter user email to add: " user_email
user_id=$(az ad user show --id "$user_email" --query id -o tsv)
az ad group member add --group "$group_name" --member-id "$user_id"
STATUS[group]="✅"
}

get_callback_url() {
print_status
echo "$K8S Fetching callback URL..."
echo ""
echo "🔐 Ensure you're authenticated to the correct cluster (management or hosted control plane)."
echo "If you encounter x509 certificate errors:"
echo " - Run ./request-admin-credential.sh to create break-glass credentials"
echo " - export KUBECONFIG=./kubeconfig"
echo " - Use --insecure-skip-tls-verify for kubectl commands"
echo ""

hcp_ns=$(gum input --placeholder "Enter Hypershift namespace:")
hcp_name=$(gum input --placeholder "Enter Hypershift cluster name:")

echo ""
echo "🔍 Attempting to retrieve callback URL from HostedCluster..."
callback_url=$(kubectl get hostedcluster "$hcp_name" -n "$hcp_ns" -o jsonpath="{.status.oauthCallbackURL}" 2>/dev/null || echo "")

if [[ -z "$callback_url" ]]; then
echo "HostedCluster callback URL not available. Attempting to get OpenShift console route..."
callback_url=$(kubectl get route console -n openshift-console --insecure-skip-tls-verify -o jsonpath="{.spec.host}" 2>/dev/null || echo "")

if [[ -z "$callback_url" ]]; then
echo "⚠️ Failed to retrieve callback URL from both HostedCluster and OpenShift console route."
echo "↩️ Returning to menu without setting callback URL."
return
else
echo "✅ Found fallback callback URL from OpenShift console route: https://$callback_url"
callback_url="https://$callback_url"
fi
else
echo "✅ Callback URL from HostedCluster: $callback_url"
fi

echo "callback_url=$callback_url" >> demo_env.sh
STATUS[callback]="✅"
}


update_app_redirect_uri() {
print_status
echo "$AZ Updating redirect URI..."
source demo_env.sh

# Ensure the callback URL is present
if [[ -z "${callback_url:-}" ]]; then
echo "❌ callback_url is not set. Please run 'Get Callback URL' step first."
return
fi

# Ensure it ends with /oauth/callback
redirect_uri="${callback_url%/}/oauth/callback"
echo "🔗 Setting redirect URI to: $redirect_uri"

az ad app update --id "$client_id" --web-redirect-uris "$redirect_uri"

STATUS[update_uri]="✅"
}

apply_idp_config_via_rp() {
print_status
echo "📡 Preparing to send external auth config to RP frontend..."

echo ""
echo "💡 Ensure RP is forwarded:"
echo " kubectl port-forward svc/aro-hcp-frontend -n aro-hcp 8443:8443"
echo ""

source demo_env.sh

# Check if IDP is already configured
echo "🔍 Checking existing IDPs in the HostedCluster..."
if kubectl get authentication cluster --insecure-skip-tls-verify -o json | jq -e '.spec.identityProviders | length > 0' >/dev/null; then
echo "⚠️ Identity Provider already configured in the cluster."
gum confirm "Return to menu?" && return
else
echo "✅ No existing IDP found."
fi

# Get Entra app name or ID
read -p "Enter the external auth ID (e.g., entra): " external_auth_id

# Get access token
ACCESS_TOKEN=$(az account get-access-token --query accessToken -o tsv 2>/dev/null || true)
if [[ -z "$ACCESS_TOKEN" ]]; then
read -p "Could not auto-acquire access token. Please enter it manually: " ACCESS_TOKEN
else
echo "🔑 Azure access token acquired."
fi

# Get subscription/RG/cluster name
default_sub=$(az account show --query id -o tsv 2>/dev/null || echo "")
read -p "Enter your subscription ID [default: $default_sub]: " subscription_id
subscription_id=${subscription_id:-$default_sub}
read -p "Enter your resource group name: " resource_group
read -p "Enter your cluster name: " cluster_name

# Build RP URL
rp_url="http://localhost:8443/subscriptions/$subscription_id/resourceGroups/$resource_group/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/$cluster_name/externalAuths/$external_auth_id?api-version=2024-06-10-preview"
created_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)

echo ""
echo "🔗 RP Endpoint: $rp_url"
echo "🚀 Sending PUT request with payload..."

# Execute request
curl -s -w "%{http_code}" --fail-with-body -o rp_response.log -X PUT "$rp_url" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Ms-Identity-Url: https://dummy.identity.azure.net" \
-H "X-Ms-Arm-Resource-System-Data: {\"createdBy\": \"dev-user\", \"createdByType\": \"User\", \"createdAt\": \"$created_at\"}" \
--data-binary @external-auth-payload.json || echo "error" >> rp_response.log

echo ""
echo "Logging RP pod logs (top 30 lines)..."

echo "Switching to Service Cluster for logs..."
export KUBECONFIG=$(make infra.svc.aks.kubeconfigfile 2>/dev/null)

if [[ -z "$KUBECONFIG" ]]; then
echo "Could not switch to service cluster. Skipping pod log capture." >> rp_response.log
else
echo "Capturing logs from RP frontend pod..."
{
echo ""
echo "================ RP FRONTEND POD LOGS (top 30) ================"
kubectl logs deployment/aro-hcp-frontend -c aro-hcp-frontend -n aro-hcp --tail=30 2>&1 || echo "Failed to get logs"
} >> rp_response.log
fi

echo ""
if grep -q '"status": *"Succeeded"' rp_response.log; then
echo "✅ Successfully applied external auth config to RP."
STATUS[apply_rp]="✅"
else
echo "❌ Failed to apply config or confirm success."
echo "📄 See full logs in rp_response.log"
gum confirm "Retry apply to RP?" && apply_idp_config_via_rp || echo "↩️ Returning to main menu."
fi
}


test_login_redirect() {
print_status
source demo_env.sh
gum confirm "Open callback URL in browser?" && xdg-open "$callback_url"
STATUS[test]="✅"
}

run_flow() {
check_deps
verify_az_login
create_entra_app
create_ad_group
get_callback_url
update_app_redirect_uri
apply_idp_config_via_rp
test_login_redirect
print_status
echo "🎉 All tasks completed."
}

run_flow
6 changes: 6 additions & 0 deletions demo/rp_response.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"error": {
"code": "InternalServerError",
"message": "Internal server error."
}
}
1 change: 1 addition & 0 deletions frontend/pkg/frontend/external_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@

var dummyURL = "Spain"
var dummyAudiences = []string{"audience1"}
var dummyClaim = "openshift-v4.18.0"

Check failure on line 52 in frontend/pkg/frontend/external_auth_test.go

View workflow job for this annotation

GitHub Actions / lint

other declaration of dummyClaim (typecheck)
var dummyClaim = "4.18.0"

Check failure on line 53 in frontend/pkg/frontend/external_auth_test.go

View workflow job for this annotation

GitHub Actions / lint

dummyClaim redeclared in this block

func TestCreateExternalAuth(t *testing.T) {
clusterResourceID, _ := azcorearm.ParseResourceID(api.TestClusterResourceID)
Expand Down
Loading
Loading