diff --git a/docs/v1-redeploy-changelog.md b/docs/v1-redeploy-changelog.md index 76f8aab..e76b485 100644 --- a/docs/v1-redeploy-changelog.md +++ b/docs/v1-redeploy-changelog.md @@ -1387,3 +1387,16 @@ ### Next-free numbers Design decision: D-069. Doc fix: DOCFIX-065 (unchanged). Bundle fix: BUNDLEFIX-008 (007 ASSIGNED above; repo tree grep showed 002-004 but 001-006 exist in history, so 007 is the first safe free). + +### 2026-07-02 (session 2, addendum) -- tenant-onboard.sh Option-3 keypair defect fixed pre-run + +Code-review catch before the first clean-room run: stage3 created the nova keypair as -svc while +stage6 creates the cluster as -cluster. Nova keypairs are USER-scoped; magnum validates the keypair +in the REQUEST context (magnum 18.0.0 attr_validator.validate_keypair -> cli.nova().keypairs.get, +invoked from cluster.py:545 at cluster create) and node boot resolves it as the TRUSTOR (the +per-cluster trust app-cred impersonates the trustor). A -svc-owned key is invisible to -cluster -> +deterministic KeyPairNotFound 400 at stage6. Option-3-specific: all prior validations used a single +identity that owned its own key. Fix: keypair created by -cluster (stage3, second subshell); +template create (stage5) drops --keypair (keypair_id optional, default=None -- verified in source); +stage6 already supplies --keypair as -cluster. Rejected alternative: creating the template as +-cluster (blurs the Option-3 svc/cluster division for no gain). diff --git a/scripts/tenant-onboard.sh b/scripts/tenant-onboard.sh index cfcf837..8523c88 100644 --- a/scripts/tenant-onboard.sh +++ b/scripts/tenant-onboard.sh @@ -2,6 +2,11 @@ # tenant-onboard.sh -- Option-3 multi-tenant onboarding (D-066), Omega Cloud v1 # STATUS: DRAFT 2026-07-02. Stages 0-5 validated live (tenant acme). Stage 6 gate CLEARED: # D-067 (barbican<->Vault) FIXED + validated live 2026-07-02 (see D-067 amendment / BUNDLEFIX-007). +# KEYPAIR FIX (2026-07-02 pre-first-clean-room-run): nova keypairs are USER-scoped and magnum +# validates + boots the keypair in the CLUSTER-CREATOR/trustor context (magnum 18.0.0 +# attr_validator.validate_keypair via cluster.py:545; trust app-cred impersonates the trustor). +# So the keypair is created by -cluster (stage3), and the template (stage5) omits --keypair +# (keypair_id optional, default=None); stage6 supplies it. A -svc-owned key would 400 at stage6. # # Model (D-066): operator creates domain + manager; manager creates project + -cluster (password, # trust-capable, cluster lifecycle) + -svc (unrestricted app cred, non-trust automation). Cluster @@ -88,7 +93,7 @@ ) || die "stage2 failed" } -stage3(){ # -svc mints unrestricted app cred; -cluster keeps password; keypair +stage3(){ # -svc mints unrestricted app cred; -cluster owns the keypair (trustor-owned) admin_env local DOM; DOM=$(openstack domain show "$CLIENT" -f value -c id &1) local SF="$OUT/${CLIENT}-svc-cred.txt"; local PID; PID=$(awk -F= '/^project_id=/{print $2}' "$SF") @@ -102,9 +107,18 @@ openstack application credential create "${CLIENT}-svc-cred" --unrestricted --description "$CLIENT non-trust automation" -f shell "$ACF" 2>&1 grep -qE '^id=' "$ACF" || { echo "appcred FAIL"; cat "$ACF"; exit 1; }; chmod 600 "$ACF" echo " app cred -> $ACF (unrestricted; secret len $(awk -F'"' '/^secret=/{print length($2)}' "$ACF"))" + ) || die "stage3 (svc app cred) failed" + # keypair as -CLUSTER: magnum validates it in the cluster-creator nova context at cluster + # create, and node boot resolves it as the trustor -- both are the -cluster identity. + local CF="$OUT/${CLIENT}-cluster-cred.txt"; [ -s "$CF" ] || die "run stage2 first (no cluster cred)" + ( for v in $(env|awk -F= '/^OS_/{print $1}'); do unset "$v"; done + export OS_AUTH_URL="$AUTH_URL" OS_IDENTITY_API_VERSION=3 OS_CACERT="$CA" + export OS_USERNAME="${CLIENT}-cluster" OS_USER_DOMAIN_ID="$DOM" OS_PROJECT_ID="$PID" + export OS_PASSWORD="$(awk -F= '/^password=/{print $2}' "$CF")" + [ "$(openstack token issue -f value -c project_id &1)" = "$PID" ] || { echo "cluster-user auth FAIL"; exit 1; } local KF="$OUT/${CLIENT}-key.pem"; umask 077; openstack keypair create "${CLIENT}-key" "$KF" 2>&1 - head -1 "$KF" | grep -q 'PRIVATE KEY' && { chmod 600 "$KF"; echo " keypair -> $KF"; } || { echo "keypair FAIL"; cat "$KF"; exit 1; } - ) || die "stage3 failed" + head -1 "$KF" | grep -q 'PRIVATE KEY' && { chmod 600 "$KF"; echo " keypair -> $KF (owner: ${CLIENT}-cluster)"; } || { echo "keypair FAIL"; cat "$KF"; exit 1; } + ) || die "stage3 (cluster keypair) failed" } stage4(){ # tenant L3 via app cred @@ -137,10 +151,12 @@ export OS_APPLICATION_CREDENTIAL_ID="$(awk -F'"' '/^id=/{print $2}' "$ACF")" export OS_APPLICATION_CREDENTIAL_SECRET="$(awk -F'"' '/^secret=/{print $2}' "$ACF")" openstack coe cluster template show "${CLIENT}-k8s" -f value -c uuid /dev/null 2>&1 && openstack coe cluster template delete "${CLIENT}-k8s" &1 || true + # NO --keypair here: template is -svc-created but the key is -cluster-owned; keypair_id is + # optional (magnum default=None) and stage6 supplies --keypair in the -cluster context. openstack coe cluster template create "${CLIENT}-k8s" --image "$IMG" --external-network provider-ext \ --master-flavor gp.mid --flavor capi.node --coe kubernetes --network-driver calico \ --docker-storage-driver overlay2 --master-lb-enabled --floating-ip-enabled \ - --fixed-network "${CLIENT}-net" --fixed-subnet "${CLIENT}-subnet" --keypair "${CLIENT}-key" &1 + --fixed-network "${CLIENT}-net" --fixed-subnet "${CLIENT}-subnet" &1 [[ "$(openstack coe cluster template show "${CLIENT}-k8s" -f value -c uuid &1)" =~ ^[0-9a-f-]{36}$ ]] && echo " template ${CLIENT}-k8s ok" || { echo "template FAIL"; exit 1; } ) || die "stage5 failed" }