#!/bin/bash echo "#############################################################" echo "# " echo "# WARNING: This script will REPLACE ALL DNS records at " echo "# the target domain ($CF_DOMAIN)! " echo "# " echo "# All existing DNS records for the listed subdomains will " echo "# be deleted and replaced with new CNAME records. " echo "# " echo "#############################################################" echo "" echo "-------------------------------------------------------------" echo "Cloudflare Credentials Required" echo "Please ensure your .env file contains the following variables:" echo " CF_API_TOKEN=your_cloudflare_api_token" echo " CF_ZONE_ID=your_cloudflare_zone_id" echo " CF_TUNNEL_ID=your_cloudflared_tunnel_id" echo " CF_DOMAIN=yourdomain.com" echo "" echo "You can find these values in your Cloudflare dashboard:" echo " - API Token: https://dash.cloudflare.com/profile/api-tokens (Create a token with Zone:DNS:Edit and Access:Apps:Edit permissions for your domain)" echo " - Zone ID: On your domain's overview page" echo " - Tunnel ID: In the Zero Trust dashboard under Access > Tunnels" echo " - Domain: The domain you want to use for your services" echo "" echo "-------------------------------------------------------------" echo "" read -p "Type 'y' to continue or any other key to abort: " consent if [[ "$consent" != "y" && "$consent" != "Y" ]]; then echo "Aborted by user." exit 1 fi # Source environment variables from the .env file in the same directory ENV_FILE="$(dirname "$0")/.env" if [ -f "$ENV_FILE" ]; then export $(grep -v '^#' "$ENV_FILE" | xargs) else echo "Error: .env file not found at $ENV_FILE" exit 1 fi # Check if required Cloudflare variables are set if [ -z "$CF_API_TOKEN" ] || [ -z "$CF_ZONE_ID" ] || [ -z "$CF_TUNNEL_ID" ] || [ -z "$CF_DOMAIN" ]; then echo "Error: One or more required Cloudflare environment variables (CF_API_TOKEN, CF_ZONE_ID, CF_TUNNEL_ID, CF_DOMAIN) are not set in $ENV_FILE." exit 1 fi # Check if jq is installed if ! command -v jq &> /dev/null; then echo "Error: jq is required but not installed. Please install jq to continue." echo "On Debian/Ubuntu: sudo apt-get install jq" echo "On RHEL/CentOS: sudo yum install jq" exit 1 fi # Array of subdomains based on docker-compose.yml services SUBDOMAINS=( "homepage" "excalidraw" "listmonk" "monica" "flatnotes" "code-server" "ollama" "open-webui" "gitea" "mini-qr" "ferdium" "answer" "nocodb" "n8n" "convertx" "rocket" "live" ) # First, remove existing DNS records for these subdomains echo "Removing existing DNS records..." for subdomain in "${SUBDOMAINS[@]}"; do echo "Checking for existing records for $subdomain.$CF_DOMAIN..." # Get all DNS records for this subdomain RECORDS=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records?name=$subdomain.$CF_DOMAIN" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json") # Extract record IDs RECORD_IDS=$(echo $RECORDS | jq -r '.result[].id') # Delete each record for record_id in $RECORD_IDS; do echo "Deleting record $record_id for $subdomain.$CF_DOMAIN..." curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records/$record_id" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" done done echo "All existing records have been removed." # Add CNAME records for each subdomain for subdomain in "${SUBDOMAINS[@]}"; do echo "Adding CNAME record for $subdomain.$CF_DOMAIN..." curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{ \"type\": \"CNAME\", \"name\": \"$subdomain\", \"content\": \"$CF_TUNNEL_ID.cfargotunnel.com\", \"ttl\": 1, \"proxied\": true }" echo -e "\n" done echo "All CNAME records have been added." # Add root domain record echo "Adding root domain (@ record)..." curl -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/dns_records" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{ \"type\": \"CNAME\", \"name\": \"@\", \"content\": \"$CF_TUNNEL_ID.cfargotunnel.com\", \"ttl\": 1, \"proxied\": true }" echo -e "\n" echo "Root domain CNAME record has been added." # Prompt for admin email echo "Please enter the admin email address that should have access to protected services:" read ADMIN_EMAIL # Validate email format if [[ ! "$ADMIN_EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then echo "Error: Invalid email format. Please provide a valid email address." exit 1 fi # Now create the Cloudflare Access applications echo "Creating Cloudflare Access applications..." # Create access applications only for specific services PROTECTED_SERVICES=("homepage" "live" "ferdium" "convertx" "mini-qr" "ollama") for service in "${PROTECTED_SERVICES[@]}"; do echo "Creating access application for $service.$CF_DOMAIN..." SERVICE_APP_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{ \"name\": \"$service $CF_DOMAIN\", \"domain\": \"$service.$CF_DOMAIN\", \"type\": \"self_hosted\", \"session_duration\": \"24h\", \"app_launcher_visible\": true, \"skip_interstitial\": true }") # Extract the application ID from the response SERVICE_APP_ID=$(echo $SERVICE_APP_RESPONSE | jq -r '.result.id') if [ -z "$SERVICE_APP_ID" ] || [ "$SERVICE_APP_ID" == "null" ]; then echo "Error creating $service access application. Response: $SERVICE_APP_RESPONSE" else echo "Successfully created $service access application with ID: $SERVICE_APP_ID" # Create policy for admin email echo "Creating admin email policy for $service application..." POLICY_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$SERVICE_APP_ID/policies" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{ \"name\": \"Allow Admin Email\", \"decision\": \"allow\", \"include\": [{ \"email\": { \"email\": \"$ADMIN_EMAIL\" } }], \"require\": [], \"exclude\": [], \"precedence\": 1, \"purpose\": \"Admin Authentication\", \"session_duration\": \"24h\" }") # Check if policy creation was successful POLICY_SUCCESS=$(echo $POLICY_RESPONSE | jq -r '.success') if [ "$POLICY_SUCCESS" == "true" ]; then POLICY_ID=$(echo $POLICY_RESPONSE | jq -r '.result.id') echo "Admin email policy for $service created successfully with ID: $POLICY_ID" else ERROR_MSG=$(echo $POLICY_RESPONSE | jq -r '.errors[0].message') echo "Error creating admin email policy for $service: $ERROR_MSG" fi fi done # 2. Create specific access application for Gitea echo "Creating access application for gitea.$CF_DOMAIN..." GITEA_APP_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{ \"name\": \"Gitea $CF_DOMAIN\", \"domain\": \"gitea.$CF_DOMAIN\", \"type\": \"self_hosted\", \"app_launcher_visible\": true, \"skip_interstitial\": true }") # Extract the application ID from the response GITEA_APP_ID=$(echo $GITEA_APP_RESPONSE | jq -r '.result.id') if [ -z "$GITEA_APP_ID" ] || [ "$GITEA_APP_ID" == "null" ]; then echo "Error creating Gitea access application. Response: $GITEA_APP_RESPONSE" else echo "Successfully created Gitea access application with ID: $GITEA_APP_ID" # Create bypass policy for everyone - Updated format echo "Creating bypass policy for Gitea application..." POLICY_RESPONSE=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$CF_ZONE_ID/access/apps/$GITEA_APP_ID/policies" \ -H "Authorization: Bearer $CF_API_TOKEN" \ -H "Content-Type: application/json" \ --data "{ \"name\": \"Bypass for Everyone\", \"decision\": \"bypass\", \"include\": [{ \"everyone\": {} }], \"require\": [], \"exclude\": [] }") # Check if policy creation was successful POLICY_SUCCESS=$(echo $POLICY_RESPONSE | jq -r '.success') if [ "$POLICY_SUCCESS" == "true" ]; then POLICY_ID=$(echo $POLICY_RESPONSE | jq -r '.result.id') echo "Bypass policy for Gitea created successfully with ID: $POLICY_ID" else ERROR_MSG=$(echo $POLICY_RESPONSE | jq -r '.errors[0].message') echo "Error creating bypass policy for Gitea: $ERROR_MSG" echo "Full response: $POLICY_RESPONSE" fi fi echo "Cloudflare Access applications setup complete."