You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							419 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							419 lines
						
					
					
						
							12 KiB
						
					
					
				
								#!/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"
							 |