Files
matrix-setup/start.sh
gyurix 1134fd915c
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
Enhance Dockerfile and start.sh: add curl, openssl, and jq; update HOMESERVER_URL to use HTTPS; improve admin user registration logic and error handling
2025-09-01 16:08:08 +02:00

185 lines
6.1 KiB
Bash

#!/bin/sh
DB_TYPE="${DB_TYPE:-pyscopg2}" # Default to PostgreSQL
POSTGRES_DB="${POSTGRES_DB:-matrix}"
POSTGRES_USER="${POSTGRES_USER:-matrix}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-matrix}"
POSTGRES_HOST=matrixpostgres-db
DB_PORT="5432"
SYNAPSE_HOST="${SYNAPSE_SERVER_NAME:-localhost}"
ADMIN_USERNAME="${ADMIN_USERNAME:-admin}"
ADMIN_PASSWORD="${ADMIN_PASSWORD:-changeme}"
HOMESERVER_URL="${HOMESERVER_URL:-https://$SYNAPSE_HOST}"
prepare_postgres () {
if [ -f /data/homeserver.yaml ]; then
DB_TYPE=$(yq '.database.name' /data/homeserver.yaml 2>/dev/null )
if [ "$DB_TYPE" != "psycopg2" ]; then
echo "Preparing PostgreSQL database..."
YAML='.server_name = "'$SYNAPSE_HOST'" |
.database.name = "psycopg2" |
.database.args.user = "'$POSTGRES_USER'" |
.database.args.password = "'$POSTGRES_PASSWORD'" |
.database.args.database = "'$POSTGRES_DB'" |
.database.args.host = "'$POSTGRES_HOST'" |
.database.args.port = "'$DB_PORT'" |
.database.args.cp_min = 5 |
.database.args.cp_max = 10'
yq eval "$YAML" /data/homeserver.yaml > /data/homeserver.yaml.tmp && mv /data/homeserver.yaml.tmp /data/homeserver.yaml
exit 0
fi
fi
}
# Get registration shared secret from template.json
get_registration_secret() {
if [ -f "/data/homeserver.yaml" ]; then
SHARED_SECRET=$(yq '.registration_shared_secret' /data/homeserver.yaml 2>/dev/null)
echo "$SHARED_SECRET"
if [ -n "$SHARED_SECRET" ] && [ "$SHARED_SECRET" != "null" ]; then
return 0
fi
fi
}
# Wait for Synapse to be ready
wait_for_synapse() {
echo "Waiting for Synapse to be ready..."
local max_attempts=30
local attempt=1
while [ $attempt -le $max_attempts ]; do
if curl -sk "$HOMESERVER_URL/_matrix/client/versions" >/dev/null 2>&1; then
echo "Synapse is ready!"
return 0
fi
echo "Attempt $attempt/$max_attempts - Synapse not ready yet..."
sleep 2
attempt=$((attempt + 1))
done
echo "Error: Synapse did not become ready within expected time"
return 1
}
# Register admin user using shared secret
register_admin_user() {
local SHARED_SECRET=$1
echo "Registering admin user: $ADMIN_USERNAME"
# Derive localpart (strip leading @ and :domain) and lowercase
local localpart
localpart=$(printf '%s' "$ADMIN_USERNAME" | sed -e 's/^@//' -e 's/:.*$//' | tr '[:upper:]' '[:lower:]')
# Fetch server-issued nonce
local url nonce
url="${HOMESERVER_URL%/}/_synapse/admin/v1/register"
nonce=$(curl -kfsS "$url" | jq -r '.nonce')
if [ -z "$nonce" ] || [ "$nonce" = "null" ]; then
echo "Failed to fetch nonce from $url"
return 1
fi
# Admin flag and user_type
local admin_word admin_json user_type user_type_json
user_type="${USER_TYPE:-}"
case "${ADMIN:-true}" in
true|1|yes|y|True|TRUE) admin_word="admin"; admin_json=true ;;
*) admin_word="notadmin"; admin_json=false ;;
esac
if [ -n "$user_type" ]; then
user_type_json=$(printf '"%s"' "$user_type")
else
user_type_json=null
fi
# Compute MAC exactly like Python (no trailing NUL unless user_type present)
local mac
if [ -n "$user_type" ]; then
mac=$(printf '%s\0%s\0%s\0%s\0%s' "$nonce" "$localpart" "$ADMIN_PASSWORD" "$admin_word" "$user_type" \
| openssl dgst -sha1 -hmac "$SHARED_SECRET" -binary | od -An -tx1 | tr -d ' \n')
else
mac=$(printf '%s\0%s\0%s\0%s' "$nonce" "$localpart" "$ADMIN_PASSWORD" "$admin_word" \
| openssl dgst -sha1 -hmac "$SHARED_SECRET" -binary | od -An -tx1 | tr -d ' \n')
fi
# Build payload and register
local payload response
payload=$(printf '{"nonce":"%s","username":"%s","password":"%s","admin":%s,"user_type":%s,"mac":"%s"}' \
"$nonce" "$localpart" "$ADMIN_PASSWORD" "$admin_json" "$user_type_json" "$mac")
response=$(curl -kfsS -X POST "$url" -H 'Content-Type: application/json' -d "$payload" || true)
if echo "$response" | grep -q '"user_id"\|"access_token"'; then
echo "Admin user created successfully!"
return 0
else
echo "Failed to create admin user. Response: $response"
return 1
fi
}
# Check if admin exists by trying to login
check_admin_by_login() {
local response=$(curl -sk -X POST "$HOMESERVER_URL/_matrix/client/r0/login" \
-H "Content-Type: application/json" \
-d "{
\"type\": \"m.login.password\",
\"user\": \"$ADMIN_USERNAME\",
\"password\": \"$ADMIN_PASSWORD\"
}" 2>/dev/null)
if echo "$response" | grep -q access_token; then
echo "Successfully logged in as admin user"
yq eval '.registration_shared_secret = ""' /data/homeserver.yaml > /data/homeserver.yaml.tmp && mv /data/homeserver.yaml.tmp /data/homeserver.yaml
return 0 # User exists and password is correct
fi
return 1 # User doesn't exist or wrong password
}
# Main execution
main() {
echo "Starting Synapse admin initialization..."
if ! wait_for_synapse; then
exit 1
fi
# Get registration shared secret
local SHARED_SECRET=$(get_registration_secret)
if [ -z "$SHARED_SECRET" ]; then
echo "Error: Could not find registration_shared_secret"
echo "Make sure template.json contains the encoded configuration"
exit 1
fi
echo "Found registration shared secret"
# Check if admin already exists by trying to login
if check_admin_by_login; then
echo "Admin user '$ADMIN_USERNAME' already exists and is accessible"
exit 0
fi
echo "No accessible admin user found. Creating admin user..."
# Try to register admin user
if ! register_admin_user "$SHARED_SECRET"; then
echo "Primary registration method failed, trying alternative..."
fi
}
# Validate environment
if [ -z "$ADMIN_PASSWORD" ] || [ "$ADMIN_PASSWORD" = "changeme" ]; then
echo "Warning: Please set a secure ADMIN_PASSWORD"
fi
prepare_postgres
main "$@"