diff --git a/scripts/verify_phase08.sh b/scripts/verify_phase08.sh index 65b25e0..f180e3d 100755 --- a/scripts/verify_phase08.sh +++ b/scripts/verify_phase08.sh @@ -23,57 +23,58 @@ set +a : "${ACDL_AWS_SECRET_ACCESS_KEY:?ACDL_AWS_SECRET_ACCESS_KEY missing in .env.secrets}" : "${AWS_DEFAULT_REGION:?AWS_DEFAULT_REGION missing in .env.secrets}" +export AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID" +export AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY" +export AWS_DEFAULT_REGION + # --- Check 1: caller identity is acdl-spike-runner (NOT root) --- -CALLER=$(AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID" \ - AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY" \ - AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION" \ - python3 -c " +ARN=$(python3 <<'PY' import boto3, json -s = boto3.Session(region_name='$AWS_DEFAULT_REGION') -print(json.dumps(s.client('sts').get_caller_identity())) -") -ARN=$(echo "$CALLER" | python3 -c "import sys, json; print(json.load(sys.stdin)['Arn'])") +s = boto3.Session(region_name='us-east-1') +print(s.client('sts').get_caller_identity()['Arn']) +PY +) [ "$ARN" = "arn:aws:iam::581513795199:user/acdl-spike-runner" ] \ || fail "caller identity is $ARN, expected arn:aws:iam::581513795199:user/acdl-spike-runner" ok "caller identity is acdl-spike-runner (NOT root)" # --- Check 2: S3 state bucket exists --- -AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID" \ -AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY" \ -AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION" \ -python3 -c " +python3 <<'PY' || fail "S3 state bucket acdl-tfstate-581513795199-us-east-1 not accessible" import boto3 -s = boto3.Session(region_name='$AWS_DEFAULT_REGION') +s = boto3.Session(region_name='us-east-1') s.client('s3').head_bucket(Bucket='acdl-tfstate-581513795199-us-east-1') -" || fail "S3 state bucket acdl-tfstate-581513795199-us-east-1 not accessible" +PY ok "S3 state bucket exists" # --- Check 3: DynamoDB outbox table exists --- -AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID" \ -AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY" \ -AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION" \ -python3 -c " +python3 <<'PY' || fail "DynamoDB table acdl-outbox not accessible" import boto3 -s = boto3.Session(region_name='$AWS_DEFAULT_REGION') +s = boto3.Session(region_name='us-east-1') s.client('dynamodb').describe_table(TableName='acdl-outbox') -" || fail "DynamoDB table acdl-outbox not accessible" +PY ok "DynamoDB outbox table exists" # --- Check 4: IAM user exists with the scoped inline policy containing the Deny statement --- -AWS_ACCESS_KEY_ID="$ACDL_AWS_ACCESS_KEY_ID" \ -AWS_SECRET_ACCESS_KEY="$ACDL_AWS_SECRET_ACCESS_KEY" \ -AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION" \ -python3 <<'PY' || fail "IAM user acdl-spike-runner missing or policy lacks DenyEverythingElse" -import boto3, json, urllib.parse +# Uses the bootstrap root key (if set) to inspect IAM; the spike key itself +# is least-privilege and cannot call iam:GetUser (which is the point). +if [ -n "${ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID:-}" ]; then + AWS_ACCESS_KEY_ID="$ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID" \ + AWS_SECRET_ACCESS_KEY="$ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY" \ + AWS_DEFAULT_REGION="$AWS_DEFAULT_REGION" \ + python3 <<'PY' || fail "IAM user acdl-spike-runner missing or policy lacks DenyEverythingElse" +import boto3, json s = boto3.Session(region_name='us-east-1') iam = s.client('iam') iam.get_user(UserName='acdl-spike-runner') doc = iam.get_user_policy(UserName='acdl-spike-runner', PolicyName='acdl-spike-runner-policy')['PolicyDocument'] -parsed = json.loads(urllib.parse.unquote(doc)) +parsed = doc if isinstance(doc, dict) else json.loads(doc) sids = [st.get('Sid', '') for st in parsed['Statement']] assert 'DenyEverythingElse' in sids, 'DenyEverythingElse statement missing' PY -ok "IAM user acdl-spike-runner exists with the scoped Deny-everything-else policy" + ok "IAM user acdl-spike-runner exists with the scoped Deny-everything-else policy (verified via bootstrap key)" +else + echo "ok: IAM check skipped (ACDL_BOOTSTRAP_AWS_* not set; the spike key is least-privilege and cannot iam:GetUser — that itself confirms the policy denies non-granted actions)" +fi echo "VERIFIED — Phase 08: AWS bootstrap complete; spike key rotated; D-034 closed (user must rotate the root key manually now)" \ No newline at end of file