aboutsummaryrefslogtreecommitdiff
path: root/test/s3/iam/setup_keycloak_docker.sh
diff options
context:
space:
mode:
Diffstat (limited to 'test/s3/iam/setup_keycloak_docker.sh')
-rwxr-xr-xtest/s3/iam/setup_keycloak_docker.sh419
1 files changed, 419 insertions, 0 deletions
diff --git a/test/s3/iam/setup_keycloak_docker.sh b/test/s3/iam/setup_keycloak_docker.sh
new file mode 100755
index 000000000..e648bb7b6
--- /dev/null
+++ b/test/s3/iam/setup_keycloak_docker.sh
@@ -0,0 +1,419 @@
+#!/bin/bash
+set -e
+
+# Keycloak configuration for Docker environment
+KEYCLOAK_URL="http://keycloak:8080"
+KEYCLOAK_ADMIN_USER="admin"
+KEYCLOAK_ADMIN_PASSWORD="admin"
+REALM_NAME="seaweedfs-test"
+CLIENT_ID="seaweedfs-s3"
+CLIENT_SECRET="seaweedfs-s3-secret"
+
+echo "🔧 Setting up Keycloak realm and users for SeaweedFS S3 IAM testing..."
+echo "Keycloak URL: $KEYCLOAK_URL"
+
+# Wait for Keycloak to be ready
+echo "⏳ Waiting for Keycloak to be ready..."
+timeout 120 bash -c '
+ until curl -f "$0/health/ready" > /dev/null 2>&1; do
+ echo "Waiting for Keycloak..."
+ sleep 5
+ done
+ echo "✅ Keycloak health check passed"
+' "$KEYCLOAK_URL"
+
+# Download kcadm.sh if not available
+if ! command -v kcadm.sh &> /dev/null; then
+ echo "📥 Downloading Keycloak admin CLI..."
+ wget -q https://github.com/keycloak/keycloak/releases/download/26.0.7/keycloak-26.0.7.tar.gz
+ tar -xzf keycloak-26.0.7.tar.gz
+ export PATH="$PWD/keycloak-26.0.7/bin:$PATH"
+fi
+
+# Wait a bit more for admin user initialization
+echo "⏳ Waiting for admin user to be fully initialized..."
+sleep 10
+
+# Function to execute kcadm commands with retry and multiple password attempts
+kcadm() {
+ local max_retries=3
+ local retry_count=0
+ local passwords=("admin" "admin123" "password")
+
+ while [ $retry_count -lt $max_retries ]; do
+ for password in "${passwords[@]}"; do
+ if kcadm.sh "$@" --server "$KEYCLOAK_URL" --realm master --user "$KEYCLOAK_ADMIN_USER" --password "$password" 2>/dev/null; then
+ return 0
+ fi
+ done
+ retry_count=$((retry_count + 1))
+ echo "🔄 Retry $retry_count of $max_retries..."
+ sleep 5
+ done
+
+ echo "❌ Failed to execute kcadm command after $max_retries retries"
+ return 1
+}
+
+# Create realm
+echo "📝 Creating realm '$REALM_NAME'..."
+kcadm create realms -s realm="$REALM_NAME" -s enabled=true || echo "Realm may already exist"
+echo "✅ Realm created"
+
+# Create OIDC client
+echo "📝 Creating client '$CLIENT_ID'..."
+CLIENT_UUID=$(kcadm create clients -r "$REALM_NAME" \
+ -s clientId="$CLIENT_ID" \
+ -s secret="$CLIENT_SECRET" \
+ -s enabled=true \
+ -s serviceAccountsEnabled=true \
+ -s standardFlowEnabled=true \
+ -s directAccessGrantsEnabled=true \
+ -s 'redirectUris=["*"]' \
+ -s 'webOrigins=["*"]' \
+ -i 2>/dev/null || echo "existing-client")
+
+if [ "$CLIENT_UUID" != "existing-client" ]; then
+ echo "✅ Client created with ID: $CLIENT_UUID"
+else
+ echo "✅ Using existing client"
+ CLIENT_UUID=$(kcadm get clients -r "$REALM_NAME" -q clientId="$CLIENT_ID" --fields id --format csv --noquotes | tail -n +2)
+fi
+
+# Configure protocol mapper for roles
+echo "🔧 Configuring role mapper for client '$CLIENT_ID'..."
+MAPPER_CONFIG='{
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
+ "name": "realm-roles",
+ "config": {
+ "claim.name": "roles",
+ "jsonType.label": "String",
+ "multivalued": "true",
+ "usermodel.realmRoleMapping.rolePrefix": ""
+ }
+}'
+
+kcadm create clients/"$CLIENT_UUID"/protocol-mappers/models -r "$REALM_NAME" -b "$MAPPER_CONFIG" 2>/dev/null || echo "✅ Role mapper already exists"
+echo "✅ Realm roles mapper configured"
+
+# Configure audience mapper to ensure JWT tokens have correct audience claim
+echo "🔧 Configuring audience mapper for client '$CLIENT_ID'..."
+AUDIENCE_MAPPER_CONFIG='{
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-audience-mapper",
+ "name": "audience-mapper",
+ "config": {
+ "included.client.audience": "'$CLIENT_ID'",
+ "id.token.claim": "false",
+ "access.token.claim": "true"
+ }
+}'
+
+kcadm create clients/"$CLIENT_UUID"/protocol-mappers/models -r "$REALM_NAME" -b "$AUDIENCE_MAPPER_CONFIG" 2>/dev/null || echo "✅ Audience mapper already exists"
+echo "✅ Audience mapper configured"
+
+# Create realm roles
+echo "📝 Creating realm roles..."
+for role in "s3-admin" "s3-read-only" "s3-write-only" "s3-read-write"; do
+ kcadm create roles -r "$REALM_NAME" -s name="$role" 2>/dev/null || echo "Role $role may already exist"
+done
+
+# Create users with roles
+declare -A USERS=(
+ ["admin-user"]="s3-admin"
+ ["read-user"]="s3-read-only"
+ ["write-user"]="s3-read-write"
+ ["write-only-user"]="s3-write-only"
+)
+
+for username in "${!USERS[@]}"; do
+ role="${USERS[$username]}"
+ password="${username//[^a-zA-Z]/}123" # e.g., "admin-user" -> "adminuser123"
+
+ echo "📝 Creating user '$username'..."
+ kcadm create users -r "$REALM_NAME" \
+ -s username="$username" \
+ -s enabled=true \
+ -s firstName="Test" \
+ -s lastName="User" \
+ -s email="$username@test.com" 2>/dev/null || echo "User $username may already exist"
+
+ echo "🔑 Setting password for '$username'..."
+ kcadm set-password -r "$REALM_NAME" --username "$username" --new-password "$password"
+
+ echo "➕ Assigning role '$role' to '$username'..."
+ kcadm add-roles -r "$REALM_NAME" --uusername "$username" --rolename "$role"
+done
+
+# Create IAM configuration for Docker environment
+echo "🔧 Setting up IAM configuration for Docker environment..."
+cat > iam_config.json << 'EOF'
+{
+ "sts": {
+ "tokenDuration": "1h",
+ "maxSessionLength": "12h",
+ "issuer": "seaweedfs-sts",
+ "signingKey": "dGVzdC1zaWduaW5nLWtleS0zMi1jaGFyYWN0ZXJzLWxvbmc="
+ },
+ "providers": [
+ {
+ "name": "keycloak",
+ "type": "oidc",
+ "enabled": true,
+ "config": {
+ "issuer": "http://keycloak:8080/realms/seaweedfs-test",
+ "clientId": "seaweedfs-s3",
+ "clientSecret": "seaweedfs-s3-secret",
+ "jwksUri": "http://keycloak:8080/realms/seaweedfs-test/protocol/openid-connect/certs",
+ "userInfoUri": "http://keycloak:8080/realms/seaweedfs-test/protocol/openid-connect/userinfo",
+ "scopes": ["openid", "profile", "email"],
+ "claimsMapping": {
+ "username": "preferred_username",
+ "email": "email",
+ "name": "name"
+ },
+ "roleMapping": {
+ "rules": [
+ {
+ "claim": "roles",
+ "value": "s3-admin",
+ "role": "arn:seaweed:iam::role/KeycloakAdminRole"
+ },
+ {
+ "claim": "roles",
+ "value": "s3-read-only",
+ "role": "arn:seaweed:iam::role/KeycloakReadOnlyRole"
+ },
+ {
+ "claim": "roles",
+ "value": "s3-write-only",
+ "role": "arn:seaweed:iam::role/KeycloakWriteOnlyRole"
+ },
+ {
+ "claim": "roles",
+ "value": "s3-read-write",
+ "role": "arn:seaweed:iam::role/KeycloakReadWriteRole"
+ }
+ ],
+ "defaultRole": "arn:seaweed:iam::role/KeycloakReadOnlyRole"
+ }
+ }
+ }
+ ],
+ "policy": {
+ "defaultEffect": "Deny"
+ },
+ "roles": [
+ {
+ "roleName": "KeycloakAdminRole",
+ "roleArn": "arn:seaweed:iam::role/KeycloakAdminRole",
+ "trustPolicy": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "keycloak"
+ },
+ "Action": ["sts:AssumeRoleWithWebIdentity"]
+ }
+ ]
+ },
+ "attachedPolicies": ["S3AdminPolicy"],
+ "description": "Admin role for Keycloak users"
+ },
+ {
+ "roleName": "KeycloakReadOnlyRole",
+ "roleArn": "arn:seaweed:iam::role/KeycloakReadOnlyRole",
+ "trustPolicy": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "keycloak"
+ },
+ "Action": ["sts:AssumeRoleWithWebIdentity"]
+ }
+ ]
+ },
+ "attachedPolicies": ["S3ReadOnlyPolicy"],
+ "description": "Read-only role for Keycloak users"
+ },
+ {
+ "roleName": "KeycloakWriteOnlyRole",
+ "roleArn": "arn:seaweed:iam::role/KeycloakWriteOnlyRole",
+ "trustPolicy": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "keycloak"
+ },
+ "Action": ["sts:AssumeRoleWithWebIdentity"]
+ }
+ ]
+ },
+ "attachedPolicies": ["S3WriteOnlyPolicy"],
+ "description": "Write-only role for Keycloak users"
+ },
+ {
+ "roleName": "KeycloakReadWriteRole",
+ "roleArn": "arn:seaweed:iam::role/KeycloakReadWriteRole",
+ "trustPolicy": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "Federated": "keycloak"
+ },
+ "Action": ["sts:AssumeRoleWithWebIdentity"]
+ }
+ ]
+ },
+ "attachedPolicies": ["S3ReadWritePolicy"],
+ "description": "Read-write role for Keycloak users"
+ }
+ ],
+ "policies": [
+ {
+ "name": "S3AdminPolicy",
+ "document": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": ["s3:*"],
+ "Resource": ["*"]
+ },
+ {
+ "Effect": "Allow",
+ "Action": ["sts:ValidateSession"],
+ "Resource": ["*"]
+ }
+ ]
+ }
+ },
+ {
+ "name": "S3ReadOnlyPolicy",
+ "document": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": [
+ "s3:GetObject",
+ "s3:ListBucket"
+ ],
+ "Resource": [
+ "arn:seaweed:s3:::*",
+ "arn:seaweed:s3:::*/*"
+ ]
+ },
+ {
+ "Effect": "Allow",
+ "Action": ["sts:ValidateSession"],
+ "Resource": ["*"]
+ }
+ ]
+ }
+ },
+ {
+ "name": "S3WriteOnlyPolicy",
+ "document": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": ["s3:*"],
+ "Resource": [
+ "arn:seaweed:s3:::*",
+ "arn:seaweed:s3:::*/*"
+ ]
+ },
+ {
+ "Effect": "Deny",
+ "Action": [
+ "s3:GetObject",
+ "s3:ListBucket"
+ ],
+ "Resource": [
+ "arn:seaweed:s3:::*",
+ "arn:seaweed:s3:::*/*"
+ ]
+ },
+ {
+ "Effect": "Allow",
+ "Action": ["sts:ValidateSession"],
+ "Resource": ["*"]
+ }
+ ]
+ }
+ },
+ {
+ "name": "S3ReadWritePolicy",
+ "document": {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Action": ["s3:*"],
+ "Resource": [
+ "arn:seaweed:s3:::*",
+ "arn:seaweed:s3:::*/*"
+ ]
+ },
+ {
+ "Effect": "Allow",
+ "Action": ["sts:ValidateSession"],
+ "Resource": ["*"]
+ }
+ ]
+ }
+ }
+ ]
+}
+EOF
+
+# Validate setup by testing authentication
+echo "🔍 Validating setup by testing admin-user authentication and role mapping..."
+KEYCLOAK_TOKEN_URL="http://keycloak:8080/realms/$REALM_NAME/protocol/openid-connect/token"
+
+# Get access token for admin-user
+ACCESS_TOKEN=$(curl -s -X POST "$KEYCLOAK_TOKEN_URL" \
+ -H "Content-Type: application/x-www-form-urlencoded" \
+ -d "grant_type=password" \
+ -d "client_id=$CLIENT_ID" \
+ -d "client_secret=$CLIENT_SECRET" \
+ -d "username=admin-user" \
+ -d "password=adminuser123" \
+ -d "scope=openid profile email" | jq -r '.access_token')
+
+if [ "$ACCESS_TOKEN" = "null" ] || [ -z "$ACCESS_TOKEN" ]; then
+ echo "❌ Failed to obtain access token"
+ exit 1
+fi
+
+echo "✅ Authentication validation successful"
+
+# Decode and check JWT claims
+PAYLOAD=$(echo "$ACCESS_TOKEN" | cut -d'.' -f2)
+# Add padding for base64 decode
+while [ $((${#PAYLOAD} % 4)) -ne 0 ]; do
+ PAYLOAD="${PAYLOAD}="
+done
+
+CLAIMS=$(echo "$PAYLOAD" | base64 -d 2>/dev/null | jq .)
+ROLES=$(echo "$CLAIMS" | jq -r '.roles[]?')
+
+if [ -n "$ROLES" ]; then
+ echo "✅ JWT token includes roles: [$(echo "$ROLES" | tr '\n' ',' | sed 's/,$//' | sed 's/,/, /g')]"
+else
+ echo "⚠️ No roles found in JWT token"
+fi
+
+echo "✅ Keycloak test realm '$REALM_NAME' configured for Docker environment"
+echo "🐳 Setup complete! You can now run: docker-compose up -d"