Complete markdown files cleanup and organization

- Organized 252 files across project
- Root directory: 187 → 2 files (98.9% reduction)
- Moved configuration guides to docs/04-configuration/
- Moved troubleshooting guides to docs/09-troubleshooting/
- Moved quick start guides to docs/01-getting-started/
- Moved reports to reports/ directory
- Archived temporary files
- Generated comprehensive reports and documentation
- Created maintenance scripts and guides

All files organized according to established standards.
This commit is contained in:
defiQUG
2026-01-06 01:46:25 -08:00
parent 1edcec953c
commit cb47cce074
1327 changed files with 217220 additions and 801 deletions

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# Alert script for tunnel failures
# Sends notifications when tunnels fail
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Configuration
ALERT_EMAIL="${ALERT_EMAIL:-}"
ALERT_WEBHOOK="${ALERT_WEBHOOK:-}"
LOG_FILE="${LOG_FILE:-/var/log/cloudflared-alerts.log}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Usage
if [ $# -lt 2 ]; then
echo "Usage: $0 <tunnel-name> <failure-type>"
echo ""
echo "Failure types: service_down, connectivity_failed, dns_failed"
exit 1
fi
TUNNEL_NAME="$1"
FAILURE_TYPE="$2"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
declare -A TUNNEL_DOMAINS=(
["ml110"]="ml110-01.d-bis.org"
["r630-01"]="r630-01.d-bis.org"
["r630-02"]="r630-02.d-bis.org"
)
DOMAIN="${TUNNEL_DOMAINS[$TUNNEL_NAME]:-unknown}"
# Create alert message
create_alert_message() {
local subject="Cloudflare Tunnel Alert: $TUNNEL_NAME"
local body="
ALERT: Cloudflare Tunnel Failure
Tunnel: $TUNNEL_NAME
Domain: $DOMAIN
Failure Type: $FAILURE_TYPE
Timestamp: $TIMESTAMP
Please check the tunnel status and logs.
To check status:
systemctl status cloudflared-${TUNNEL_NAME}
To view logs:
journalctl -u cloudflared-${TUNNEL_NAME} -f
"
echo "$body"
}
# Send email alert
send_email() {
if [ -z "$ALERT_EMAIL" ]; then
return 0
fi
local subject="Cloudflare Tunnel Alert: $TUNNEL_NAME"
local body=$(create_alert_message)
if command -v mail &> /dev/null; then
echo "$body" | mail -s "$subject" "$ALERT_EMAIL" 2>/dev/null || true
log_info "Email alert sent to $ALERT_EMAIL"
elif command -v sendmail &> /dev/null; then
{
echo "To: $ALERT_EMAIL"
echo "Subject: $subject"
echo ""
echo "$body"
} | sendmail "$ALERT_EMAIL" 2>/dev/null || true
log_info "Email alert sent to $ALERT_EMAIL"
else
log_warn "Email not configured (mail/sendmail not found)"
fi
}
# Send webhook alert
send_webhook() {
if [ -z "$ALERT_WEBHOOK" ]; then
return 0
fi
local payload=$(cat <<EOF
{
"text": "Cloudflare Tunnel Alert",
"attachments": [
{
"color": "danger",
"fields": [
{
"title": "Tunnel",
"value": "$TUNNEL_NAME",
"short": true
},
{
"title": "Domain",
"value": "$DOMAIN",
"short": true
},
{
"title": "Failure Type",
"value": "$FAILURE_TYPE",
"short": true
},
{
"title": "Timestamp",
"value": "$TIMESTAMP",
"short": true
}
]
}
]
}
EOF
)
if command -v curl &> /dev/null; then
curl -s -X POST -H "Content-Type: application/json" \
-d "$payload" "$ALERT_WEBHOOK" > /dev/null 2>&1 || true
log_info "Webhook alert sent"
else
log_warn "Webhook not sent (curl not found)"
fi
}
# Log alert
log_alert() {
local message="[$TIMESTAMP] ALERT: Tunnel $TUNNEL_NAME - $FAILURE_TYPE"
echo "$message" >> "$LOG_FILE"
log_error "$message"
}
# Main
main() {
log_alert
# Send alerts
send_email
send_webhook
# Also log to syslog if available
if command -v logger &> /dev/null; then
logger -t cloudflared-alert "Tunnel $TUNNEL_NAME failure: $FAILURE_TYPE"
fi
}
main

View File

@@ -0,0 +1,687 @@
#!/usr/bin/env bash
# Complete automation of Cloudflare setup via API
# Creates tunnels, DNS records, and Cloudflare Access applications
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Load .env file
if [[ -f "$PROJECT_ROOT/.env" ]]; then
source "$PROJECT_ROOT/.env"
log_info "Loaded credentials from .env"
else
log_error ".env file not found at $PROJECT_ROOT/.env"
exit 1
fi
# Cloudflare API configuration
CLOUDFLARE_API_TOKEN="${CLOUDFLARE_API_TOKEN:-}"
CLOUDFLARE_EMAIL="${CLOUDFLARE_EMAIL:-}"
CLOUDFLARE_API_KEY="${CLOUDFLARE_API_KEY:-}"
CLOUDFLARE_ZONE_ID="${CLOUDFLARE_ZONE_ID:-}"
CLOUDFLARE_ACCOUNT_ID="${CLOUDFLARE_ACCOUNT_ID:-}"
DOMAIN="${DOMAIN:-d-bis.org}"
# Check for required tools
if ! command -v curl >/dev/null 2>&1; then
log_error "curl is required"
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required. Install with: apt-get install jq"
exit 1
fi
# API base URLs
CF_API_BASE="https://api.cloudflare.com/client/v4"
CF_ZERO_TRUST_API="https://api.cloudflare.com/client/v4/accounts"
# Function to make Cloudflare API request
cf_api_request() {
local method="$1"
local endpoint="$2"
local data="${3:-}"
local url="${CF_API_BASE}${endpoint}"
local auth_header=""
if [[ -n "$CLOUDFLARE_API_TOKEN" ]]; then
auth_header="Authorization: Bearer ${CLOUDFLARE_API_TOKEN}"
elif [[ -n "$CLOUDFLARE_API_KEY" ]] && [[ -n "$CLOUDFLARE_EMAIL" ]]; then
auth_header="X-Auth-Email: ${CLOUDFLARE_EMAIL}"
# Note: We'll need to pass both headers, so we'll use a different approach
else
log_error "Cloudflare API credentials not found!" >&2
exit 1
fi
local response
local http_code
local temp_file=$(mktemp)
# Build curl command with timeout
if [[ -n "$CLOUDFLARE_API_TOKEN" ]]; then
if [[ -n "$data" ]]; then
http_code=$(curl -s --max-time 30 -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d "$data" 2>/dev/null)
else
http_code=$(curl -s --max-time 30 -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \
-H "Content-Type: application/json" 2>/dev/null)
fi
else
if [[ -n "$data" ]]; then
http_code=$(curl -s --max-time 30 -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" \
-d "$data" 2>/dev/null)
else
http_code=$(curl -s --max-time 30 -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" 2>/dev/null)
fi
fi
# Read response from temp file
if [[ -f "$temp_file" ]] && [[ -s "$temp_file" ]]; then
response=$(cat "$temp_file")
else
response=""
fi
rm -f "$temp_file"
# Check if response is valid JSON
if [[ -z "$response" ]] || ! echo "$response" | jq -e . >/dev/null 2>&1; then
log_error "Invalid JSON response from API (HTTP ${http_code:-unknown})" >&2
if [[ -n "$response" ]]; then
log_error "Response: $(echo "$response" | head -3)" >&2
fi
return 1
fi
local success=$(echo "$response" | jq -r '.success // false' 2>/dev/null)
if [[ "$success" != "true" ]]; then
local errors=$(echo "$response" | jq -r '.errors[]?.message // .error // "Unknown error"' 2>/dev/null | head -3)
if [[ "$http_code" != "200" ]] && [[ "$http_code" != "201" ]]; then
log_error "API request failed (HTTP $http_code): $errors" >&2
fi
# Don't return error for GET requests that might return empty results
if [[ "$method" == "GET" ]] && [[ "$http_code" == "200" ]]; then
echo "$response"
return 0
fi
return 1
fi
# Only output JSON to stdout on success
echo "$response"
}
# Get zone ID
get_zone_id() {
if [[ -n "$CLOUDFLARE_ZONE_ID" ]]; then
echo "$CLOUDFLARE_ZONE_ID"
return 0
fi
log_info "Getting zone ID for domain: $DOMAIN"
local response=$(cf_api_request "GET" "/zones?name=${DOMAIN}")
local zone_id=$(echo "$response" | jq -r '.result[0].id // empty')
if [[ -z "$zone_id" ]]; then
log_error "Zone not found for domain: $DOMAIN"
exit 1
fi
log_success "Zone ID: $zone_id"
echo "$zone_id"
}
# Get account ID
get_account_id() {
if [[ -n "$CLOUDFLARE_ACCOUNT_ID" ]]; then
echo "$CLOUDFLARE_ACCOUNT_ID"
return 0
fi
log_info "Getting account ID..."
local response=$(cf_api_request "GET" "/user/tokens/verify")
local account_id=$(echo "$response" | jq -r '.result.id // empty')
if [[ -z "$account_id" ]]; then
response=$(cf_api_request "GET" "/accounts")
account_id=$(echo "$response" | jq -r '.result[0].id // empty')
fi
if [[ -z "$account_id" ]]; then
local zone_id=$(get_zone_id)
response=$(cf_api_request "GET" "/zones/${zone_id}")
account_id=$(echo "$response" | jq -r '.result.account.id // empty')
fi
if [[ -z "$account_id" ]]; then
log_error "Could not determine account ID"
exit 1
fi
log_success "Account ID: $account_id"
echo "$account_id"
}
# Create tunnel
create_tunnel() {
local account_id="$1"
local tunnel_name="$2"
log_info "Creating tunnel: $tunnel_name"
# Check if tunnel already exists (skip check if API fails)
local response
response=$(cf_api_request "GET" "/accounts/${account_id}/cfd_tunnel" 2>/dev/null) || response=""
if [[ -n "$response" ]] && echo "$response" | jq -e '.result' >/dev/null 2>&1; then
local existing_id
existing_id=$(echo "$response" | jq -r ".result[]? | select(.name == \"${tunnel_name}\") | .id" 2>/dev/null || echo "")
if [[ -n "$existing_id" ]] && [[ "$existing_id" != "null" ]] && [[ "$existing_id" != "" ]]; then
log_warn "Tunnel $tunnel_name already exists (ID: $existing_id)"
echo "$existing_id"
return 0
fi
else
# If API call failed, log but continue (might be permission issue)
log_warn "Could not check for existing tunnels, attempting to create..."
fi
# Create tunnel
local data
data=$(jq -n \
--arg name "$tunnel_name" \
'{name: $name}' 2>/dev/null)
if [[ -z "$data" ]]; then
log_error "Failed to create tunnel data JSON"
return 1
fi
response=$(cf_api_request "POST" "/accounts/${account_id}/cfd_tunnel" "$data" 2>/dev/null)
local api_result=$?
# If creation failed, check if tunnel already exists (might have been created between check and create)
if [[ $api_result -ne 0 ]] || [[ -z "$response" ]] || ! echo "$response" | jq -e . >/dev/null 2>&1; then
# Try to get existing tunnel one more time
local check_response
check_response=$(cf_api_request "GET" "/accounts/${account_id}/cfd_tunnel" 2>/dev/null) || check_response=""
if [[ -n "$check_response" ]] && echo "$check_response" | jq -e '.result' >/dev/null 2>&1; then
local existing_id
existing_id=$(echo "$check_response" | jq -r ".result[]? | select(.name == \"${tunnel_name}\") | .id" 2>/dev/null || echo "")
if [[ -n "$existing_id" ]] && [[ "$existing_id" != "null" ]] && [[ "$existing_id" != "" ]]; then
log_warn "Tunnel $tunnel_name already exists (ID: $existing_id)"
echo "$existing_id"
return 0
fi
fi
log_error "Failed to create tunnel"
if [[ -n "$response" ]]; then
log_error "Response: $(echo "$response" | head -3)"
fi
return 1
fi
local tunnel_id
tunnel_id=$(echo "$response" | jq -r '.result.id // empty' 2>/dev/null || echo "")
if [[ -z "$tunnel_id" ]] || [[ "$tunnel_id" == "null" ]] || [[ "$tunnel_id" == "" ]]; then
log_error "Failed to create tunnel"
local errors
errors=$(echo "$response" | jq -r '.errors[]?.message // .error // "Unknown error"' 2>/dev/null | head -3)
if [[ -n "$errors" ]]; then
log_error "Errors: $errors"
fi
return 1
fi
log_success "Tunnel created: $tunnel_id"
echo "$tunnel_id"
}
# Get tunnel token (generate new token)
get_tunnel_token() {
local account_id="$1"
local tunnel_id="$2"
log_info "Generating tunnel token..."
# Note: Cloudflare API doesn't allow "getting" existing tokens, only generating new ones
# The endpoint is: POST /accounts/{account_id}/cfd_tunnel/{tunnel_id}/token
local response
response=$(cf_api_request "POST" "/accounts/${account_id}/cfd_tunnel/${tunnel_id}/token" 2>/dev/null) || response=""
if [[ -z "$response" ]]; then
log_warn "Could not generate token via API (may need manual generation)"
return 1
fi
local token
token=$(echo "$response" | jq -r '.result.token // empty' 2>/dev/null || echo "")
if [[ -z "$token" ]] || [[ "$token" == "null" ]]; then
log_warn "Token generation returned empty result"
return 1
fi
log_success "Token generated"
echo "$token"
}
# Configure tunnel routes
configure_tunnel_routes() {
local account_id="$1"
local tunnel_id="$2"
local hostname="$3"
local service="$4"
log_info "Configuring tunnel route: $hostname$service"
# Get existing config (may not exist for new tunnels)
local response
response=$(cf_api_request "GET" "/accounts/${account_id}/cfd_tunnel/${tunnel_id}/configurations" 2>/dev/null) || response=""
local existing_config="{}"
local ingress="[]"
# Check if response is valid and has config
if [[ -n "$response" ]] && echo "$response" | jq -e '.result.config' >/dev/null 2>&1; then
existing_config=$(echo "$response" | jq -r '.result.config // {}' 2>/dev/null || echo "{}")
ingress=$(echo "$existing_config" | jq -r '.ingress // []' 2>/dev/null || echo "[]")
# Check if route already exists
local route_exists
route_exists=$(echo "$ingress" | jq -r "any(.hostname == \"${hostname}\")" 2>/dev/null || echo "false")
if [[ "$route_exists" == "true" ]]; then
log_success "Route already configured for $hostname, skipping..."
return 0
fi
log_info "Found existing config, adding new route..."
else
log_info "No existing config found, creating new configuration..."
ingress="[]"
fi
# Build new ingress array - simple approach: replace entire config with just this route + catch-all
# This is simpler and more reliable than trying to merge with existing config
local config_data
config_data=$(jq -n \
--arg hostname "$hostname" \
--arg service "$service" \
'{
config: {
ingress: [
{
hostname: $hostname,
service: $service,
originRequest: {
noHappyEyeballs: true,
connectTimeout: "30s",
tcpKeepAlive: "30s",
keepAliveConnections: 100,
keepAliveTimeout: "90s",
disableChunkedEncoding: true,
noTLSVerify: true
}
},
{
service: "http_status:404"
}
]
}
}' 2>/dev/null)
if [[ -z "$config_data" ]]; then
log_error "Failed to create config JSON"
return 1
fi
response=$(cf_api_request "PUT" "/accounts/${account_id}/cfd_tunnel/${tunnel_id}/configurations" "$config_data" 2>/dev/null)
local api_result=$?
if [[ $api_result -eq 0 ]] && echo "$response" | jq -e '.success' >/dev/null 2>&1; then
log_success "Tunnel route configured"
return 0
else
log_error "Failed to configure tunnel route"
if [[ -n "$response" ]]; then
local errors
errors=$(echo "$response" | jq -r '.errors[]?.message // .error // "Unknown error"' 2>/dev/null | head -3)
log_error "Errors: $errors"
fi
return 1
fi
}
# Create DNS record
create_dns_record() {
local zone_id="$1"
local name="$2"
local target="$3"
log_info "Creating DNS record: $name$target"
# Check if record exists
local response=$(cf_api_request "GET" "/zones/${zone_id}/dns_records?name=${name}&type=CNAME" 2>/dev/null || echo "")
local record_id=$(echo "$response" | jq -r '.result[0].id // empty' 2>/dev/null || echo "")
local data=$(jq -n \
--arg name "$name" \
--arg target "$target" \
'{
type: "CNAME",
name: $name,
content: $target,
proxied: true,
ttl: 1
}')
if [[ -n "$record_id" ]]; then
log_warn "DNS record exists, updating..."
response=$(cf_api_request "PUT" "/zones/${zone_id}/dns_records/${record_id}" "$data")
else
response=$(cf_api_request "POST" "/zones/${zone_id}/dns_records" "$data")
fi
if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
log_success "DNS record configured"
return 0
else
log_error "Failed to configure DNS record"
return 1
fi
}
# Create Cloudflare Access application
create_access_application() {
local account_id="$1"
local app_name="$2"
local domain="$3"
log_info "Creating Access application: $app_name"
# Check if application exists
local response=$(cf_api_request "GET" "/accounts/${account_id}/access/apps" 2>/dev/null || echo "")
local existing_id=$(echo "$response" | jq -r ".result[] | select(.domain == \"${domain}\") | .id" 2>/dev/null || echo "")
if [[ -n "$existing_id" ]]; then
log_warn "Access application already exists (ID: $existing_id)"
echo "$existing_id"
return 0
fi
# Create application
local data=$(jq -n \
--arg name "$app_name" \
--arg domain "$domain" \
'{
name: $name,
domain: $domain,
type: "self_hosted",
session_duration: "8h"
}')
response=$(cf_api_request "POST" "/accounts/${account_id}/access/apps" "$data")
local app_id=$(echo "$response" | jq -r '.result.id // empty')
if [[ -z "$app_id" ]]; then
log_error "Failed to create Access application"
return 1
fi
log_success "Access application created: $app_id"
echo "$app_id"
}
# Create Access policy
create_access_policy() {
local account_id="$1"
local app_id="$2"
log_info "Creating Access policy for application..."
# Check if policy exists
local response=$(cf_api_request "GET" "/accounts/${account_id}/access/apps/${app_id}/policies" 2>/dev/null || echo "")
local existing_id=$(echo "$response" | jq -r '.result[0].id // empty' 2>/dev/null || echo "")
if [[ -n "$existing_id" ]]; then
log_warn "Access policy already exists"
return 0
fi
# Create policy with MFA requirement
local data=$(jq -n \
'{
name: "Allow Team Access",
decision: "allow",
include: [
{
email: {
email: "@'${DOMAIN}'"
}
}
],
require: [
{
email: {}
}
]
}')
response=$(cf_api_request "POST" "/accounts/${account_id}/access/apps/${app_id}/policies" "$data")
if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
log_success "Access policy created"
return 0
else
log_warn "Failed to create Access policy (may need manual configuration)"
return 1
fi
}
# Main execution
main() {
log_info "=========================================="
log_info " Cloudflare Automated Setup"
log_info "=========================================="
echo ""
# Validate credentials
if [[ -z "$CLOUDFLARE_API_TOKEN" ]] && [[ -z "$CLOUDFLARE_API_KEY" ]]; then
log_error "Cloudflare API credentials not found in .env"
exit 1
fi
# Get IDs
local zone_id=$(get_zone_id)
local account_id=$(get_account_id)
echo ""
log_info "Configuration:"
echo " Domain: $DOMAIN"
echo " Zone ID: $zone_id"
echo " Account ID: $account_id"
echo ""
# Tunnel configuration
declare -A TUNNELS=(
["ml110"]="ml110-01.d-bis.org:https://192.168.11.10:8006"
["r630-01"]="r630-01.d-bis.org:https://192.168.11.11:8006"
["r630-02"]="r630-02.d-bis.org:https://192.168.11.12:8006"
)
echo "=========================================="
log_info "Step 1: Creating Tunnels"
echo "=========================================="
declare -A TUNNEL_IDS=()
declare -A TUNNEL_TOKENS=()
for tunnel_name in "${!TUNNELS[@]}"; do
local full_name="tunnel-${tunnel_name}"
# First, try to get existing tunnel
local response
response=$(cf_api_request "GET" "/accounts/${account_id}/cfd_tunnel" 2>/dev/null) || response=""
local tunnel_id=""
if [[ -n "$response" ]] && echo "$response" | jq -e '.result' >/dev/null 2>&1; then
tunnel_id=$(echo "$response" | jq -r ".result[]? | select(.name == \"${full_name}\") | .id" 2>/dev/null || echo "")
fi
# If not found, try to create
if [[ -z "$tunnel_id" ]] || [[ "$tunnel_id" == "null" ]] || [[ "$tunnel_id" == "" ]]; then
log_info "Tunnel $full_name not found, creating..."
tunnel_id=$(create_tunnel "$account_id" "$full_name")
else
log_success "Using existing tunnel: $full_name (ID: $tunnel_id)"
fi
if [[ -z "$tunnel_id" ]] || [[ "$tunnel_id" == "null" ]] || [[ "$tunnel_id" == "" ]]; then
log_error "Failed to get or create tunnel $full_name"
continue
fi
TUNNEL_IDS["$tunnel_name"]="$tunnel_id"
# Get token (optional - can be generated later via cloudflared CLI)
log_info "Attempting to generate token for tunnel $full_name..."
local token
token=$(get_tunnel_token "$account_id" "$tunnel_id" 2>/dev/null) || token=""
if [[ -z "$token" ]] || [[ "$token" == "null" ]]; then
log_warn "Could not generate token for $full_name via API"
log_info "Token can be generated later via: cloudflared tunnel token <tunnel-id>"
log_info "Or use cloudflared CLI: cloudflared tunnel create $full_name"
token=""
else
log_success "Token generated for $full_name"
fi
TUNNEL_TOKENS["$tunnel_name"]="$token"
echo ""
done
echo "=========================================="
log_info "Step 2: Configuring Tunnel Routes"
echo "=========================================="
for tunnel_name in "${!TUNNELS[@]}"; do
local config="${TUNNELS[$tunnel_name]}"
local hostname="${config%%:*}"
local service="${config#*:}"
local tunnel_id="${TUNNEL_IDS[$tunnel_name]}"
configure_tunnel_routes "$account_id" "$tunnel_id" "$hostname" "$service"
echo ""
done
echo "=========================================="
log_info "Step 3: Creating DNS Records"
echo "=========================================="
for tunnel_name in "${!TUNNELS[@]}"; do
local config="${TUNNELS[$tunnel_name]}"
local hostname="${config%%:*}"
local tunnel_id="${TUNNEL_IDS[$tunnel_name]}"
local target="${tunnel_id}.cfargotunnel.com"
create_dns_record "$zone_id" "$hostname" "$target"
echo ""
done
echo "=========================================="
log_info "Step 4: Creating Cloudflare Access Applications"
echo "=========================================="
for tunnel_name in "${!TUNNELS[@]}"; do
local config="${TUNNELS[$tunnel_name]}"
local hostname="${config%%:*}"
local app_name="Proxmox ${tunnel_name}"
local app_id=$(create_access_application "$account_id" "$app_name" "$hostname")
# Create policy
create_access_policy "$account_id" "$app_id"
echo ""
done
echo "=========================================="
log_success "Setup Complete!"
echo "=========================================="
echo ""
log_info "Tunnel IDs:"
for tunnel_name in "${!TUNNEL_IDS[@]}"; do
echo " $tunnel_name:"
echo " ID: ${TUNNEL_IDS[$tunnel_name]}"
if [[ -n "${TUNNEL_TOKENS[$tunnel_name]}" ]] && [[ "${TUNNEL_TOKENS[$tunnel_name]}" != "" ]]; then
echo " Token: ${TUNNEL_TOKENS[$tunnel_name]:0:50}..."
else
echo " Token: (not generated - use cloudflared CLI to generate)"
fi
done
# Save credentials to file for easy access
local creds_file="$TUNNELS_DIR/tunnel-credentials.json"
log_info ""
log_info "Saving credentials to: $creds_file"
local json_output="{"
local first=true
for tunnel_name in "${!TUNNEL_IDS[@]}"; do
if [[ "$first" == "true" ]]; then
first=false
else
json_output+=","
fi
json_output+="\"$tunnel_name\":{"
json_output+="\"id\":\"${TUNNEL_IDS[$tunnel_name]}\","
json_output+="\"token\":\"${TUNNEL_TOKENS[$tunnel_name]}\""
json_output+="}"
done
json_output+="}"
echo "$json_output" | jq . > "$creds_file"
chmod 600 "$creds_file"
log_success "Credentials saved to $creds_file"
echo ""
log_info "Next steps:"
echo " 1. Credentials saved to: $creds_file"
echo " 2. Run: ./scripts/save-credentials-from-file.sh"
echo " 3. Or manually: ./scripts/save-tunnel-credentials.sh <name> <id> <token>"
echo " 4. Start services: systemctl start cloudflared-*"
echo " 5. Test access: curl -I https://ml110-01.d-bis.org"
}
main

View File

@@ -0,0 +1,197 @@
#!/usr/bin/env bash
# One-time health check for all Cloudflare tunnels
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Configuration
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
TUNNELS=("ml110" "r630-01" "r630-02" "r630-03" "r630-04")
declare -A TUNNEL_DOMAINS=(
["ml110"]="ml110-01.d-bis.org"
["r630-01"]="r630-01.d-bis.org"
["r630-02"]="r630-02.d-bis.org"
["r630-03"]="r630-03.d-bis.org"
["r630-04"]="r630-04.d-bis.org"
)
declare -A TUNNEL_IPS=(
["ml110"]="192.168.11.10"
["r630-01"]="192.168.11.11"
["r630-02"]="192.168.11.12"
["r630-03"]="192.168.11.13"
["r630-04"]="192.168.11.14"
)
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
# Check service status
check_service() {
local tunnel="$1"
local service="cloudflared-${tunnel}"
if exec_in_container "systemctl is-active --quiet $service 2>/dev/null"; then
return 0
fi
return 1
}
# Check service logs for errors
check_logs() {
local tunnel="$1"
local service="cloudflared-${tunnel}"
# Check for recent errors in logs
if exec_in_container "journalctl -u $service --since '5 minutes ago' --no-pager | grep -i error | head -1"; then
return 1
fi
return 0
}
# Check DNS resolution
check_dns() {
local domain="$1"
if dig +short "$domain" | grep -q .; then
return 0
fi
return 1
}
# Check HTTPS connectivity
check_https() {
local domain="$1"
local http_code
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "https://${domain}" 2>/dev/null || echo "000")
if [[ "$http_code" =~ ^(200|302|403|401)$ ]]; then
return 0
fi
return 1
}
# Check internal connectivity
check_internal() {
local ip="$1"
local port="${2:-8006}"
if timeout 5 bash -c "echo > /dev/tcp/${ip}/${port}" 2>/dev/null; then
return 0
fi
return 1
}
# Print header
echo ""
echo "=========================================="
echo " Cloudflare Tunnel Health Check"
echo "=========================================="
echo ""
# Check each tunnel
for tunnel in "${TUNNELS[@]}"; do
domain="${TUNNEL_DOMAINS[$tunnel]}"
ip="${TUNNEL_IPS[$tunnel]}"
service="cloudflared-${tunnel}"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Tunnel: $tunnel ($domain)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Check service status
if check_service "$tunnel"; then
log_success "Service is running"
else
log_error "Service is NOT running"
echo ""
continue
fi
# Check logs
if check_logs "$tunnel"; then
log_success "No recent errors in logs"
else
log_warn "Recent errors found in logs"
exec_in_container "journalctl -u $service --since '5 minutes ago' --no-pager | grep -i error | head -3"
fi
# Check DNS
if check_dns "$domain"; then
log_success "DNS resolution: OK"
dig +short "$domain" | head -1 | sed 's/^/ → /'
else
log_error "DNS resolution: FAILED"
fi
# Check HTTPS connectivity
if check_https "$domain"; then
log_success "HTTPS connectivity: OK"
else
log_error "HTTPS connectivity: FAILED"
fi
# Check internal connectivity
if check_internal "$ip" 8006; then
log_success "Internal connectivity to $ip:8006: OK"
else
log_error "Internal connectivity to $ip:8006: FAILED"
fi
# Get service uptime
uptime=$(exec_in_container "systemctl show $service --property=ActiveEnterTimestamp --value 2>/dev/null" || echo "unknown")
if [ "$uptime" != "unknown" ]; then
log_info "Service started: $uptime"
fi
echo ""
done
# Summary
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
all_healthy=true
for tunnel in "${TUNNELS[@]}"; do
if ! check_service "$tunnel"; then
all_healthy=false
break
fi
done
if [ "$all_healthy" = true ]; then
log_success "All tunnels are healthy"
else
log_error "Some tunnels are not healthy"
fi
echo ""

View File

@@ -0,0 +1,107 @@
#!/usr/bin/env bash
# Complete automated setup - runs all automation steps
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
echo ""
log_info "=========================================="
log_info " Complete Automated Cloudflare Setup"
log_info "=========================================="
echo ""
# Step 1: Verify prerequisites
log_info "Step 1: Verifying prerequisites..."
if ! "$SCRIPT_DIR/verify-prerequisites.sh"; then
log_error "Prerequisites check failed"
exit 1
fi
# Step 2: Run Cloudflare API automation
log_info ""
log_info "Step 2: Running Cloudflare API automation..."
log_info "This will create tunnels, DNS records, and Access applications"
echo ""
if ! "$SCRIPT_DIR/automate-cloudflare-setup.sh"; then
log_error "Cloudflare API automation failed"
exit 1
fi
# Step 3: Parse output and save credentials
log_info ""
log_info "Step 3: Saving tunnel credentials..."
log_warn "Note: You'll need to manually extract tunnel IDs and tokens from the output above"
log_warn "Then run: ./scripts/save-tunnel-credentials.sh <name> <id> <token>"
echo ""
# Step 4: Install systemd services
log_info ""
log_info "Step 4: Installing systemd services..."
if ! "$SCRIPT_DIR/setup-multi-tunnel.sh" --skip-credentials; then
log_warn "Service installation may need manual completion"
fi
# Step 5: Start services
log_info ""
log_info "Step 5: Starting tunnel services..."
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
if command -v pct &> /dev/null; then
for tunnel in ml110 r630-01 r630-02; do
if pct exec "$VMID" -- systemctl start "cloudflared-${tunnel}.service" 2>/dev/null; then
log_success "Started cloudflared-${tunnel}.service"
else
log_warn "Could not start cloudflared-${tunnel}.service (may need credentials first)"
fi
done
else
log_warn "Cannot start services remotely. Run manually:"
echo " ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl start cloudflared-*'"
fi
# Step 6: Health check
log_info ""
log_info "Step 6: Running health check..."
if "$SCRIPT_DIR/check-tunnel-health.sh"; then
log_success "Health check completed"
else
log_warn "Health check found issues (may be expected if credentials not saved yet)"
fi
echo ""
log_success "=========================================="
log_success " Automated Setup Complete"
log_success "=========================================="
echo ""
log_info "Next steps:"
echo ""
echo "1. Extract tunnel IDs and tokens from Step 2 output"
echo "2. Save credentials:"
echo " ./scripts/save-tunnel-credentials.sh ml110 <id> <token>"
echo " ./scripts/save-tunnel-credentials.sh r630-01 <id> <token>"
echo " ./scripts/save-tunnel-credentials.sh r630-02 <id> <token>"
echo ""
echo "3. Start services:"
echo " systemctl start cloudflared-*"
echo ""
echo "4. Verify:"
echo " ./scripts/check-tunnel-health.sh"
echo ""

View File

@@ -0,0 +1,171 @@
#!/usr/bin/env bash
# Configure Cloudflare Access policies with allowed email addresses
# Usage: ./configure-access-policies.sh [email1] [email2] [email3] ...
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Load .env
if [ -f "$TUNNELS_DIR/../../.env" ]; then
source "$TUNNELS_DIR/../../.env" 2>/dev/null || true
fi
if [[ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]] || [[ -z "${CLOUDFLARE_API_KEY:-}" ]] || [[ -z "${CLOUDFLARE_EMAIL:-}" ]]; then
log_error "Cloudflare credentials not found in .env"
exit 1
fi
# Get email addresses from command line or prompt
ALLOWED_EMAILS=("$@")
if [ ${#ALLOWED_EMAILS[@]} -eq 0 ]; then
log_info "Enter allowed email addresses (one per line, empty line to finish):"
while IFS= read -r email; do
[[ -z "$email" ]] && break
[[ "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]] || {
log_warn "Invalid email format: $email (skipping)"
continue
}
ALLOWED_EMAILS+=("$email")
done
fi
if [ ${#ALLOWED_EMAILS[@]} -eq 0 ]; then
log_error "No email addresses provided"
exit 1
fi
log_info "Allowed emails: ${ALLOWED_EMAILS[*]}"
echo ""
# Function to make API request
cf_api_request() {
local method="$1"
local endpoint="$2"
local data="${3:-}"
local url="https://api.cloudflare.com/client/v4${endpoint}"
local temp_file=$(mktemp)
local http_code
if [[ -n "$data" ]]; then
http_code=$(curl -s -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" \
-d "$data" 2>/dev/null)
else
http_code=$(curl -s -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" 2>/dev/null)
fi
local response=$(cat "$temp_file" 2>/dev/null || echo "")
rm -f "$temp_file"
if [[ "$http_code" != "200" ]] && [[ "$http_code" != "201" ]]; then
log_error "API request failed (HTTP $http_code)"
echo "$response" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "$response"
return 1
fi
echo "$response"
}
# Get Access applications
log_info "Fetching Access applications..."
APPS_RESPONSE=$(cf_api_request "GET" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps" 2>&1)
declare -A APP_IDS=()
for hostname in ml110-01.d-bis.org r630-01.d-bis.org r630-02.d-bis.org; do
app_id=$(echo "$APPS_RESPONSE" | jq -r ".result[]? | select(.domain? == \"${hostname}\") | .id" 2>/dev/null || echo "")
if [[ -n "$app_id" ]] && [[ "$app_id" != "null" ]] && [[ "$app_id" != "" ]]; then
APP_IDS["$hostname"]="$app_id"
log_success "Found app for $hostname: $app_id"
else
log_warn "No app found for $hostname"
fi
done
if [ ${#APP_IDS[@]} -eq 0 ]; then
log_error "No Access applications found"
exit 1
fi
echo ""
# Build email include array
EMAIL_INCLUDES=$(printf '%s\n' "${ALLOWED_EMAILS[@]}" | jq -R . | jq -s . | jq 'map({email: {email: .}})')
# Configure policy for each app
for hostname in "${!APP_IDS[@]}"; do
app_id="${APP_IDS[$hostname]}"
log_info "Configuring policy for $hostname..."
# Get existing policies
POLICIES_RESPONSE=$(cf_api_request "GET" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps/${app_id}/policies" 2>&1)
EXISTING_POLICY_ID=$(echo "$POLICIES_RESPONSE" | jq -r '.result[] | select(.name == "Allow Team Access") | .id' 2>/dev/null || echo "")
# Build policy data
POLICY_DATA=$(jq -n \
--argjson emails "$EMAIL_INCLUDES" \
'{
name: "Allow Team Access",
decision: "allow",
include: $emails,
require: [
{
email: {}
}
]
}')
if [[ -n "$EXISTING_POLICY_ID" ]] && [[ "$EXISTING_POLICY_ID" != "null" ]]; then
# Update existing policy
log_info " Updating existing policy..."
response=$(cf_api_request "PUT" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps/${app_id}/policies/${EXISTING_POLICY_ID}" "$POLICY_DATA" 2>&1)
else
# Create new policy
log_info " Creating new policy..."
response=$(cf_api_request "POST" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps/${app_id}/policies" "$POLICY_DATA" 2>&1)
fi
if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
log_success " ✓ Policy configured for $hostname"
else
log_error " ✗ Failed to configure policy for $hostname"
echo "$response" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null
fi
echo ""
done
log_success "=== Access Policies Configured ==="
log_info "Allowed emails:"
for email in "${ALLOWED_EMAILS[@]}"; do
echo " - $email"
done
echo ""
log_info "These emails can now access:"
for hostname in "${!APP_IDS[@]}"; do
echo " - https://$hostname"
done

View File

@@ -0,0 +1,184 @@
#!/usr/bin/env bash
# Configure tunnel-r630-02 to be ready for migration/token generation
# This ensures the tunnel has proper configuration before getting a token
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Load .env
if [ -f "$TUNNELS_DIR/../../.env" ]; then
source "$TUNNELS_DIR/../../.env" 2>/dev/null || true
fi
if [[ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]] || [[ -z "${CLOUDFLARE_API_KEY:-}" ]] || [[ -z "${CLOUDFLARE_EMAIL:-}" ]]; then
log_error "Cloudflare credentials not found in .env"
exit 1
fi
TUNNEL_ID="0876f12b-64d7-4927-9ab3-94cb6cf48af9"
HOSTNAME="r630-02.d-bis.org"
TARGET="https://192.168.11.12:8006"
log_info "=== Configuring tunnel-r630-02 for Migration ==="
echo ""
# Function to make API request
cf_api_request() {
local method="$1"
local endpoint="$2"
local data="${3:-}"
local url="https://api.cloudflare.com/client/v4${endpoint}"
local temp_file=$(mktemp)
local http_code
if [[ -n "$data" ]]; then
http_code=$(curl -s -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" \
-d "$data" 2>/dev/null)
else
http_code=$(curl -s -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" 2>/dev/null)
fi
local response=$(cat "$temp_file" 2>/dev/null || echo "")
rm -f "$temp_file"
if [[ "$http_code" != "200" ]] && [[ "$http_code" != "201" ]]; then
log_error "API request failed (HTTP $http_code)"
echo "$response" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "$response"
return 1
fi
echo "$response"
}
# Step 1: Ensure tunnel configuration exists
log_info "Step 1: Configuring tunnel route..."
CONFIG_DATA=$(jq -n \
--arg hostname "$HOSTNAME" \
--arg target "$TARGET" \
'{
config: {
ingress: [
{
hostname: $hostname,
service: $target,
originRequest: {
noHappyEyeballs: true,
connectTimeout: "30s",
tcpKeepAlive: "30s",
keepAliveConnections: 100,
keepAliveTimeout: "90s",
disableChunkedEncoding: true,
noTLSVerify: true
}
},
{
service: "http_status:404"
}
]
}
}')
response=$(cf_api_request "PUT" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}/configurations" "$CONFIG_DATA" 2>&1)
if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
log_success "Tunnel route configured"
else
log_error "Failed to configure tunnel route"
echo "$response" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null
exit 1
fi
# Step 2: Create local config file
log_info "Step 2: Creating local config file..."
CONFIG_FILE="$TUNNELS_DIR/configs/tunnel-r630-02.yml"
cat > "$CONFIG_FILE" <<EOF
# Cloudflare Tunnel Configuration for r630-02 Proxmox Host
# Tunnel Name: tunnel-r630-02
# Domain: r630-02.d-bis.org
# Target: 192.168.11.12:8006 (Proxmox UI)
tunnel: $TUNNEL_ID
credentials-file: /etc/cloudflared/credentials-r630-02.json
ingress:
# Proxmox UI - r630-02
- hostname: $HOSTNAME
service: $TARGET
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
# Allow self-signed certificates (Proxmox uses self-signed)
noTLSVerify: true
# Catch-all (must be last)
- service: http_status:404
# Metrics endpoint (optional, for monitoring)
metrics: 127.0.0.1:9093
# Logging
loglevel: info
# Grace period for shutdown
gracePeriod: 30s
EOF
log_success "Config file created: $CONFIG_FILE"
# Step 3: Check tunnel status
log_info "Step 3: Checking tunnel status..."
response=$(cf_api_request "GET" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/cfd_tunnel/${TUNNEL_ID}" 2>&1)
if echo "$response" | jq -e '.result' >/dev/null 2>&1; then
status=$(echo "$response" | jq -r '.result.status // "unknown"')
remote_config=$(echo "$response" | jq -r '.result.remote_config // false')
log_info "Tunnel status: $status"
log_info "Remote config: $remote_config"
if [[ "$status" == "healthy" ]] && [[ "$remote_config" == "true" ]]; then
log_success "Tunnel is ready for migration!"
else
log_warn "Tunnel needs to be connected to become healthy"
log_info "Once you install and start the tunnel, it will become healthy"
fi
fi
echo ""
log_success "=== Configuration Complete ==="
log_info "Next steps:"
log_info "1. Get token from Cloudflare Dashboard:"
log_info " https://one.dash.cloudflare.com/ → Zero Trust → Networks → Tunnels → tunnel-r630-02"
log_info "2. Install with: sudo cloudflared service install <token>"
log_info "3. Or use the install script once you have the token"

View File

@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# Complete deployment script - runs all setup steps
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
echo ""
echo "=========================================="
echo " Cloudflare Multi-Tunnel Deployment"
echo "=========================================="
echo ""
# Step 1: Verify prerequisites
log_info "Step 1: Verifying prerequisites..."
if "$SCRIPT_DIR/verify-prerequisites.sh"; then
log_success "Prerequisites verified"
else
log_error "Prerequisites check failed"
exit 1
fi
echo ""
log_info "Step 2: Running setup script..."
log_warn "You will be prompted for tunnel credentials"
echo ""
# Step 2: Run setup
if "$SCRIPT_DIR/setup-multi-tunnel.sh"; then
log_success "Setup completed"
else
log_error "Setup failed"
exit 1
fi
echo ""
log_info "Step 3: Running health check..."
if "$SCRIPT_DIR/check-tunnel-health.sh"; then
log_success "Health check completed"
else
log_warn "Health check found issues (may be expected if tunnels not fully configured)"
fi
echo ""
log_success "=========================================="
log_success " Deployment Complete"
log_success "=========================================="
echo ""
log_info "Next steps:"
echo ""
echo "1. Verify tunnels are running:"
echo " systemctl status cloudflared-*"
echo ""
echo "2. Configure Cloudflare Access:"
echo " See: docs/CLOUDFLARE_ACCESS_SETUP.md"
echo ""
echo "3. Start monitoring:"
echo " ./scripts/monitor-tunnels.sh --daemon"
echo ""
echo "4. Test access:"
echo " curl -I https://ml110-01.d-bis.org"
echo ""

View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bash
# Generate credentials files for existing Cloudflare Tunnels
# This script helps create the credentials JSON files needed for existing tunnels
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Load account ID from .env if available
if [ -f "$TUNNELS_DIR/../../.env" ]; then
source "$TUNNELS_DIR/../../.env" 2>/dev/null || true
fi
declare -A TUNNELS=(
["ml110"]="tunnel-ml110:ccd7150a-9881-4b8c-a105-9b4ead6e69a2"
["r630-01"]="tunnel-r630-01:4481af8f-b24c-4cd3-bdd5-f562f4c97df4"
["r630-02"]="tunnel-r630-02:0876f12b-64d7-4927-9ab3-94cb6cf48af9"
)
log_info "=== Cloudflare Tunnel Credentials Generator ==="
echo ""
log_info "For existing tunnels, you have two options:"
echo ""
echo "Option 1: Download from Cloudflare Dashboard (Recommended)"
echo " 1. Go to: https://one.dash.cloudflare.com/"
echo " 2. Navigate to: Zero Trust > Networks > Tunnels"
echo " 3. Click on each tunnel"
echo " 4. Click 'Configure' > 'Local Management' > 'Download credentials file'"
echo " 5. Save as: credentials-<tunnel-name>.json"
echo ""
echo "Option 2: Use cloudflared CLI (if you have access)"
echo " Run: cloudflared tunnel create <tunnel-name>"
echo " This will generate credentials for a new tunnel with that name"
echo " (Note: This creates a NEW tunnel, not for existing ones)"
echo ""
read -p "Do you have credentials files ready? (y/n): " has_creds
if [[ "$has_creds" != "y" && "$has_creds" != "Y" ]]; then
log_warn "Please download credentials from Cloudflare Dashboard first"
log_info "See: https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/#download-the-credentials-file"
exit 0
fi
log_info ""
log_info "For each tunnel, provide the path to the credentials JSON file"
log_info "Or press Enter to skip and configure manually later"
echo ""
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
copy_to_container() {
local src="$1"
local dst="$2"
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$src" "$dst"
else
scp "$src" "root@${PROXMOX_HOST}:/tmp/$(basename "$src")"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/$(basename "$src") $dst"
fi
}
for tunnel_key in "${!TUNNELS[@]}"; do
IFS=':' read -r tunnel_name tunnel_id <<< "${TUNNELS[$tunnel_key]}"
echo ""
log_info "=== Tunnel: $tunnel_name (ID: $tunnel_id) ==="
read -p "Path to credentials JSON file (or Enter to skip): " creds_path
if [[ -z "$creds_path" ]]; then
log_warn "Skipping $tunnel_name"
continue
fi
if [[ ! -f "$creds_path" ]]; then
log_error "File not found: $creds_path"
continue
fi
# Validate JSON structure
if ! jq -e '.AccountTag, .TunnelSecret, .TunnelID' "$creds_path" >/dev/null 2>&1; then
log_error "Invalid credentials file format"
log_info "Expected format:"
log_info ' {'
log_info ' "AccountTag": "...",'
log_info ' "TunnelSecret": "...",'
log_info ' "TunnelID": "...",'
log_info ' "TunnelName": "..."'
log_info ' }'
continue
fi
# Copy to container
log_info "Copying credentials to VMID $VMID..."
copy_to_container "$creds_path" "/etc/cloudflared/credentials-${tunnel_key}.json"
# Set permissions
exec_in_container "chmod 600 /etc/cloudflared/credentials-${tunnel_key}.json"
# Update config file to use correct credentials path
config_file="$TUNNELS_DIR/configs/tunnel-${tunnel_key}.yml"
if [ -f "$config_file" ]; then
# Update tunnel ID in config
temp_config=$(mktemp)
sed "s/<TUNNEL_ID_${tunnel_key^^}>/$tunnel_id/g" "$config_file" > "$temp_config"
sed -i "s|credentials-file:.*|credentials-file: /etc/cloudflared/credentials-${tunnel_key}.json|g" "$temp_config"
# Copy config to container
copy_to_container "$temp_config" "/etc/cloudflared/tunnel-${tunnel_key}.yml"
rm -f "$temp_config"
log_success "Credentials and config saved for $tunnel_name"
else
log_warn "Config file not found: $config_file"
fi
done
echo ""
log_success "=== Credentials Setup Complete ==="
log_info "Next steps:"
log_info "1. Verify configs: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- ls -la /etc/cloudflared/'"
log_info "2. Start services: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl start cloudflared-*'"
log_info "3. Check status: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl status cloudflared-*'"

View File

@@ -0,0 +1,158 @@
#!/usr/bin/env bash
# Install all 3 Cloudflare tunnels using tokens
# Usage: ./install-all-tunnels.sh <ml110-token> <r630-01-token> <r630-02-token>
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.12}"
VMID="${VMID:-102}"
declare -A TUNNELS=(
["ml110"]="tunnel-ml110:ccd7150a-9881-4b8c-a105-9b4ead6e69a2:ml110-01.d-bis.org:https://192.168.11.10:8006"
["r630-01"]="tunnel-r630-01:4481af8f-b24c-4cd3-bdd5-f562f4c97df4:r630-01.d-bis.org:https://192.168.11.11:8006"
["r630-02"]="tunnel-r630-02:0876f12b-64d7-4927-9ab3-94cb6cf48af9:r630-02.d-bis.org:https://192.168.11.12:8006"
)
# Check if tokens provided
if [ $# -lt 3 ]; then
log_error "Usage: $0 <ml110-token> <r630-01-token> <r630-02-token>"
echo ""
log_info "Example:"
echo " $0 \\"
echo " 'eyJhIjoi...' \\"
echo " 'eyJhIjoi...' \\"
echo " 'eyJhIjoi...'"
echo ""
log_info "Get tokens from: https://one.dash.cloudflare.com/ → Zero Trust → Networks → Tunnels"
exit 1
fi
TOKEN_ML110="$1"
TOKEN_R630_01="$2"
TOKEN_R630_02="$3"
declare -A TOKENS=(
["ml110"]="$TOKEN_ML110"
["r630-01"]="$TOKEN_R630_01"
["r630-02"]="$TOKEN_R630_02"
)
log_info "=== Installing All Cloudflare Tunnels ==="
echo ""
# Function to install a tunnel
install_tunnel() {
local tunnel_key="$1"
local token="$2"
IFS=':' read -r tunnel_name tunnel_id hostname target <<< "${TUNNELS[$tunnel_key]}"
log_info "Installing $tunnel_name..."
# Decode token
if ! TOKEN_DATA=$(echo "$token" | base64 -d 2>/dev/null); then
log_error "Invalid token format for $tunnel_key"
return 1
fi
local account_tag=$(echo "$TOKEN_DATA" | jq -r '.a')
local tunnel_secret=$(echo "$TOKEN_DATA" | jq -r '.s')
local token_tunnel_id=$(echo "$TOKEN_DATA" | jq -r '.t')
if [[ "$token_tunnel_id" != "$tunnel_id" ]]; then
log_warn "Token tunnel ID ($token_tunnel_id) doesn't match expected ($tunnel_id)"
fi
# Create credentials JSON
local creds_json=$(jq -n \
--arg account "$account_tag" \
--arg secret "$tunnel_secret" \
--arg id "$tunnel_id" \
--arg name "$tunnel_name" \
'{
AccountTag: $account,
TunnelSecret: $secret,
TunnelID: $id,
TunnelName: $name
}')
# Create config YAML
local config_yml="tunnel: $tunnel_id
credentials-file: /etc/cloudflared/credentials-${tunnel_key}.json
ingress:
- hostname: $hostname
service: $target
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
- service: http_status:404"
# Copy files to Proxmox host
echo "$creds_json" > /tmp/creds-${tunnel_key}.json
echo "$config_yml" > /tmp/config-${tunnel_key}.yml
scp /tmp/creds-${tunnel_key}.json /tmp/config-${tunnel_key}.yml root@${PROXMOX_HOST}:/tmp/ >/dev/null 2>&1
# Push to container
ssh root@${PROXMOX_HOST} "pct push $VMID /tmp/creds-${tunnel_key}.json /etc/cloudflared/credentials-${tunnel_key}.json && \
pct push $VMID /tmp/config-${tunnel_key}.yml /etc/cloudflared/tunnel-${tunnel_key}.yml && \
pct exec $VMID -- chmod 600 /etc/cloudflared/credentials-${tunnel_key}.json && \
pct exec $VMID -- mkdir -p /etc/cloudflared" 2>&1 | grep -v "failed to create file" || true
# Install service file
local service_file="$TUNNELS_DIR/systemd/cloudflared-${tunnel_key}.service"
if [ -f "$service_file" ]; then
scp "$service_file" root@${PROXMOX_HOST}:/tmp/ >/dev/null 2>&1
ssh root@${PROXMOX_HOST} "pct push $VMID /tmp/cloudflared-${tunnel_key}.service /etc/systemd/system/cloudflared-${tunnel_key}.service && \
pct exec $VMID -- systemctl daemon-reload && \
pct exec $VMID -- systemctl enable cloudflared-${tunnel_key}.service" >/dev/null 2>&1
fi
# Cleanup
rm -f /tmp/creds-${tunnel_key}.json /tmp/config-${tunnel_key}.yml
log_success "$tunnel_name installed"
}
# Install all tunnels
for tunnel_key in "${!TUNNELS[@]}"; do
install_tunnel "$tunnel_key" "${TOKENS[$tunnel_key]}"
echo ""
done
log_success "=== All Tunnels Installed ==="
echo ""
log_info "Starting services..."
ssh root@${PROXMOX_HOST} "pct exec $VMID -- systemctl start cloudflared-ml110 cloudflared-r630-01 cloudflared-r630-02" 2>&1 | grep -v "Unit.*not found" || true
echo ""
log_info "Checking service status..."
ssh root@${PROXMOX_HOST} "pct exec $VMID -- systemctl status cloudflared-* --no-pager | grep -E '(Active|tunnel-|●)' | head -10"
echo ""
log_success "Installation complete!"
log_info "Test URLs:"
log_info " - https://ml110-01.d-bis.org"
log_info " - https://r630-01.d-bis.org"
log_info " - https://r630-02.d-bis.org"

View File

@@ -0,0 +1,129 @@
#!/usr/bin/env bash
# Install and configure a single Cloudflare tunnel
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Usage
if [ $# -lt 1 ]; then
echo "Usage: $0 <tunnel-name> [tunnel-id] [credentials-file]"
echo ""
echo "Tunnel names: ml110, r630-01, r630-02"
echo ""
echo "Example:"
echo " $0 ml110"
echo " $0 ml110 abc123def456 /path/to/credentials.json"
exit 1
fi
TUNNEL_NAME="$1"
TUNNEL_ID="${2:-}"
CREDS_FILE="${3:-}"
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
# Validate tunnel name
if [[ ! "$TUNNEL_NAME" =~ ^(ml110|r630-01|r630-02)$ ]]; then
log_error "Invalid tunnel name: $TUNNEL_NAME"
log_error "Valid names: ml110, r630-01, r630-02"
exit 1
fi
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
log_info "Installing tunnel: $TUNNEL_NAME"
# Check VMID
if ! exec_in_container "true"; then
log_error "Cannot access VMID $VMID"
exit 1
fi
# Copy config file
config_file="$TUNNELS_DIR/configs/tunnel-${TUNNEL_NAME}.yml"
if [ ! -f "$config_file" ]; then
log_error "Configuration file not found: $config_file"
exit 1
fi
log_info "Copying configuration file..."
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$config_file" "/etc/cloudflared/tunnel-${TUNNEL_NAME}.yml"
else
scp "$config_file" "root@${PROXMOX_HOST}:/tmp/tunnel-${TUNNEL_NAME}.yml"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${TUNNEL_NAME}.yml /etc/cloudflared/tunnel-${TUNNEL_NAME}.yml"
fi
# Update tunnel ID if provided
if [ -n "$TUNNEL_ID" ]; then
log_info "Updating tunnel ID..."
exec_in_container "sed -i 's/<TUNNEL_ID_${TUNNEL_NAME^^}>/$TUNNEL_ID/g' /etc/cloudflared/tunnel-${TUNNEL_NAME}.yml"
log_success "Tunnel ID updated"
fi
# Copy credentials if provided
if [ -n "$CREDS_FILE" ] && [ -f "$CREDS_FILE" ]; then
log_info "Copying credentials file..."
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$CREDS_FILE" "/etc/cloudflared/tunnel-${TUNNEL_NAME}.json"
else
scp "$CREDS_FILE" "root@${PROXMOX_HOST}:/tmp/tunnel-${TUNNEL_NAME}.json"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${TUNNEL_NAME}.json /etc/cloudflared/tunnel-${TUNNEL_NAME}.json"
fi
exec_in_container "chmod 600 /etc/cloudflared/tunnel-${TUNNEL_NAME}.json"
log_success "Credentials file copied"
fi
# Install systemd service
service_file="$TUNNELS_DIR/systemd/cloudflared-${TUNNEL_NAME}.service"
if [ ! -f "$service_file" ]; then
log_error "Service file not found: $service_file"
exit 1
fi
log_info "Installing systemd service..."
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$service_file" "/tmp/cloudflared-${TUNNEL_NAME}.service"
exec_in_container "mv /tmp/cloudflared-${TUNNEL_NAME}.service /etc/systemd/system/cloudflared-${TUNNEL_NAME}.service"
else
scp "$service_file" "root@${PROXMOX_HOST}:/tmp/cloudflared-${TUNNEL_NAME}.service"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/cloudflared-${TUNNEL_NAME}.service /etc/systemd/system/cloudflared-${TUNNEL_NAME}.service"
exec_in_container "mv /tmp/cloudflared-${TUNNEL_NAME}.service /etc/systemd/system/cloudflared-${TUNNEL_NAME}.service"
fi
# Reload systemd
exec_in_container "systemctl daemon-reload"
exec_in_container "systemctl enable cloudflared-${TUNNEL_NAME}.service"
log_success "Tunnel $TUNNEL_NAME installed and enabled"
log_info "Start with: systemctl start cloudflared-${TUNNEL_NAME}.service"

View File

@@ -0,0 +1,252 @@
#!/usr/bin/env bash
# Install cloudflared services using tokens
# This is an alternative to credentials files
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
declare -A TUNNELS=(
["ml110"]="tunnel-ml110:ccd7150a-9881-4b8c-a105-9b4ead6e69a2"
["r630-01"]="tunnel-r630-01:4481af8f-b24c-4cd3-bdd5-f562f4c97df4"
["r630-02"]="tunnel-r630-02:0876f12b-64d7-4927-9ab3-94cb6cf48af9"
["r630-03"]="tunnel-r630-03:<TUNNEL_ID_R630_03>"
["r630-04"]="tunnel-r630-04:<TUNNEL_ID_R630_04>"
)
log_info "=== Cloudflare Tunnel Service Installation ==="
echo ""
log_info "This script will install cloudflared services using tokens"
log_info "You need to provide tokens for each tunnel"
echo ""
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
# Check if cloudflared is installed
log_info "Checking cloudflared installation..."
if ! exec_in_container "command -v cloudflared >/dev/null 2>&1"; then
log_error "cloudflared is not installed in VMID $VMID"
log_info "Install it first:"
log_info " ssh root@${PROXMOX_HOST} 'pct exec $VMID -- wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb'"
log_info " ssh root@${PROXMOX_HOST} 'pct exec $VMID -- dpkg -i cloudflared-linux-amd64.deb'"
exit 1
fi
log_success "cloudflared is installed"
echo ""
log_info "For each tunnel, you can either:"
log_info " 1. Provide a token (base64-encoded)"
log_info " 2. Press Enter to skip and configure manually later"
echo ""
for tunnel_key in "${!TUNNELS[@]}"; do
IFS=':' read -r tunnel_name tunnel_id <<< "${TUNNELS[$tunnel_key]}"
echo ""
log_info "=== Tunnel: $tunnel_name (ID: $tunnel_id) ==="
read -p "Enter token (or press Enter to skip): " token
if [[ -z "$token" ]]; then
log_warn "Skipping $tunnel_name"
continue
fi
# Validate token format (basic check - should be base64)
if ! echo "$token" | base64 -d >/dev/null 2>&1; then
log_error "Invalid token format (not base64)"
continue
fi
# Decode token to verify
token_data=$(echo "$token" | base64 -d 2>/dev/null || echo "")
if ! echo "$token_data" | jq -e '.a, .t' >/dev/null 2>&1; then
log_error "Invalid token format (not valid JSON)"
continue
fi
token_tunnel_id=$(echo "$token_data" | jq -r '.t' 2>/dev/null || echo "")
if [[ "$token_tunnel_id" != "$tunnel_id" ]]; then
log_warn "Token tunnel ID ($token_tunnel_id) doesn't match expected ($tunnel_id)"
read -p "Continue anyway? (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
continue
fi
fi
log_info "Installing service for $tunnel_name..."
# Create config file with token
temp_config=$(mktemp)
cat > "$temp_config" <<EOF
tunnel: $tunnel_id
credentials-file: /etc/cloudflared/credentials-${tunnel_key}.json
ingress:
EOF
# Add ingress rules based on tunnel
case "$tunnel_key" in
"ml110")
cat >> "$temp_config" <<'INGRESS'
- hostname: ml110-01.d-bis.org
service: https://192.168.11.10:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
INGRESS
;;
"r630-01")
cat >> "$temp_config" <<'INGRESS'
- hostname: r630-01.d-bis.org
service: https://192.168.11.11:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
INGRESS
;;
"r630-02")
cat >> "$temp_config" <<'INGRESS'
- hostname: r630-02.d-bis.org
service: https://192.168.11.12:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
INGRESS
;;
"r630-03")
cat >> "$temp_config" <<'INGRESS'
- hostname: r630-03.d-bis.org
service: https://192.168.11.13:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
INGRESS
;;
"r630-04")
cat >> "$temp_config" <<'INGRESS'
- hostname: r630-04.d-bis.org
service: https://192.168.11.14:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
INGRESS
;;
esac
cat >> "$temp_config" <<EOF
- service: http_status:404
EOF
# Copy config to container
log_info " Copying config to VMID $VMID..."
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$temp_config" "/etc/cloudflared/tunnel-${tunnel_key}.yml"
else
scp "$temp_config" "root@${PROXMOX_HOST}:/tmp/tunnel-${tunnel_key}.yml" >/dev/null 2>&1
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${tunnel_key}.yml /etc/cloudflared/tunnel-${tunnel_key}.yml" >/dev/null 2>&1
fi
# Convert token to credentials file format
token_json=$(echo "$token" | base64 -d | jq -r '{AccountTag: .a, TunnelSecret: .s, TunnelID: .t, TunnelName: "'"$tunnel_name"'"}')
temp_creds=$(mktemp)
echo "$token_json" > "$temp_creds"
# Copy credentials to container
log_info " Copying credentials to VMID $VMID..."
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$temp_creds" "/etc/cloudflared/credentials-${tunnel_key}.json"
else
scp "$temp_creds" "root@${PROXMOX_HOST}:/tmp/credentials-${tunnel_key}.json" >/dev/null 2>&1
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/credentials-${tunnel_key}.json /etc/cloudflared/credentials-${tunnel_key}.json" >/dev/null 2>&1
fi
# Set permissions
exec_in_container "chmod 600 /etc/cloudflared/credentials-${tunnel_key}.json"
# Update config to use correct credentials path
exec_in_container "sed -i 's|credentials-file:.*|credentials-file: /etc/cloudflared/credentials-${tunnel_key}.json|g' /etc/cloudflared/tunnel-${tunnel_key}.yml"
# Install systemd service
log_info " Installing systemd service..."
service_file="$TUNNELS_DIR/systemd/cloudflared-${tunnel_key}.service"
if [ -f "$service_file" ]; then
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$service_file" "/etc/systemd/system/cloudflared-${tunnel_key}.service"
else
scp "$service_file" "root@${PROXMOX_HOST}:/tmp/cloudflared-${tunnel_key}.service" >/dev/null 2>&1
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/cloudflared-${tunnel_key}.service /etc/systemd/system/cloudflared-${tunnel_key}.service" >/dev/null 2>&1
fi
exec_in_container "systemctl daemon-reload"
exec_in_container "systemctl enable cloudflared-${tunnel_key}.service"
log_success " ✓ Service installed and enabled"
else
log_warn " Service file not found: $service_file"
fi
rm -f "$temp_config" "$temp_creds"
log_success "$tunnel_name configured"
done
echo ""
log_success "=== Installation Complete ==="
log_info "Next steps:"
log_info "1. Start services: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl start cloudflared-*'"
log_info "2. Check status: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl status cloudflared-*'"

View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
# Continuous health monitoring for Cloudflare tunnels
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Configuration
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
TUNNELS=("ml110" "r630-01" "r630-02")
CHECK_INTERVAL="${CHECK_INTERVAL:-60}" # seconds
LOG_FILE="${LOG_FILE:-/var/log/cloudflared-monitor.log}"
ALERT_SCRIPT="$SCRIPT_DIR/alert-tunnel-failure.sh"
# Check if running as daemon
DAEMON=false
if [[ "${1:-}" == "--daemon" ]]; then
DAEMON=true
fi
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
# Check tunnel health
check_tunnel() {
local tunnel="$1"
local service="cloudflared-${tunnel}"
# Check if service is active
if exec_in_container "systemctl is-active --quiet $service"; then
# Check if service is actually running (not just enabled)
if exec_in_container "systemctl is-active $service 2>/dev/null | grep -q active"; then
return 0
fi
fi
return 1
}
# Check tunnel connectivity
check_connectivity() {
local tunnel="$1"
local domain=""
case "$tunnel" in
ml110) domain="ml110-01.d-bis.org" ;;
r630-01) domain="r630-01.d-bis.org" ;;
r630-02) domain="r630-02.d-bis.org" ;;
*) return 1 ;;
esac
# Try to connect via HTTPS (should get Cloudflare Access page or redirect)
if curl -s -o /dev/null -w "%{http_code}" --max-time 10 "https://${domain}" | grep -qE "^(200|302|403|401)"; then
return 0
fi
return 1
}
# Log message
log_message() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if [ "$DAEMON" = true ]; then
echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
else
case "$level" in
INFO) log_info "$message" ;;
SUCCESS) log_success "$message" ;;
WARN) log_warn "$message" ;;
ERROR) log_error "$message" ;;
esac
fi
}
# Monitor loop
monitor_loop() {
local failed_tunnels=()
while true; do
for tunnel in "${TUNNELS[@]}"; do
# Check service status
if ! check_tunnel "$tunnel"; then
log_message "ERROR" "Tunnel $tunnel service is not running"
# Alert if not already alerted
if [[ ! " ${failed_tunnels[@]} " =~ " ${tunnel} " ]]; then
failed_tunnels+=("$tunnel")
if [ -f "$ALERT_SCRIPT" ]; then
"$ALERT_SCRIPT" "$tunnel" "service_down"
fi
# Attempt restart
log_message "INFO" "Attempting to restart tunnel $tunnel"
exec_in_container "systemctl restart cloudflared-${tunnel}.service" || true
sleep 5
fi
else
# Service is running, check connectivity
if ! check_connectivity "$tunnel"; then
log_message "WARN" "Tunnel $tunnel service is running but connectivity check failed"
else
log_message "SUCCESS" "Tunnel $tunnel is healthy"
# Remove from failed list if it was there
failed_tunnels=("${failed_tunnels[@]/$tunnel}")
fi
fi
done
# Sleep before next check
sleep "$CHECK_INTERVAL"
done
}
# Main
main() {
log_message "INFO" "Starting tunnel monitoring"
log_message "INFO" "Monitoring tunnels: ${TUNNELS[*]}"
log_message "INFO" "Check interval: ${CHECK_INTERVAL}s"
if [ "$DAEMON" = true ]; then
log_message "INFO" "Running in daemon mode. Logs: $LOG_FILE"
# Redirect output to log file
monitor_loop >> "$LOG_FILE" 2>&1 &
echo $! > /tmp/cloudflared-monitor.pid
log_message "INFO" "Monitor started with PID: $(cat /tmp/cloudflared-monitor.pid)"
else
log_message "INFO" "Running in foreground mode. Press Ctrl+C to stop."
monitor_loop
fi
}
# Handle signals
trap 'log_message "INFO" "Monitor stopped"; exit 0' SIGINT SIGTERM
main

View File

@@ -0,0 +1,127 @@
#!/usr/bin/env bash
# Quick install using a single token (for ml110 tunnel)
# Usage: ./quick-install-token.sh <token>
set -euo pipefail
TOKEN="${1:-}"
if [[ -z "$TOKEN" ]]; then
echo "Usage: $0 <token>"
echo ""
echo "Example:"
echo " $0 eyJhIjoiNTJhZDU3YTcxNjcxYzVmYzAwOWVkZjA3NDQ2NTgxOTYiLCJ0IjoiY2NkNzE1MGEtOTg4MS00YjhjLWExMDUtOWI0ZWFkNmU2OWEyIiwicyI6IkZtems1ZDdUWDR0OW03Q0huVU9DYTYyTFdiQVFPZkZKa2duRHhQdExkZldKNGVvTHgyckw5K2szVCs5N0lFTFFoNGdHdHZLbzNacGZpNmI4TkdxdUlnPT0ifQ=="
exit 1
fi
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
# Decode token
TOKEN_DATA=$(echo "$TOKEN" | base64 -d)
TUNNEL_ID=$(echo "$TOKEN_DATA" | jq -r '.t')
ACCOUNT_TAG=$(echo "$TOKEN_DATA" | jq -r '.a')
TUNNEL_SECRET=$(echo "$TOKEN_DATA" | jq -r '.s')
echo "Token decoded:"
echo " Tunnel ID: $TUNNEL_ID"
echo " Account Tag: $ACCOUNT_TAG"
echo ""
# Determine tunnel name based on ID
case "$TUNNEL_ID" in
"ccd7150a-9881-4b8c-a105-9b4ead6e69a2")
TUNNEL_KEY="ml110"
TUNNEL_NAME="tunnel-ml110"
HOSTNAME="ml110-01.d-bis.org"
TARGET="https://192.168.11.10:8006"
;;
"4481af8f-b24c-4cd3-bdd5-f562f4c97df4")
TUNNEL_KEY="r630-01"
TUNNEL_NAME="tunnel-r630-01"
HOSTNAME="r630-01.d-bis.org"
TARGET="https://192.168.11.11:8006"
;;
"0876f12b-64d7-4927-9ab3-94cb6cf48af9")
TUNNEL_KEY="r630-02"
TUNNEL_NAME="tunnel-r630-02"
HOSTNAME="r630-02.d-bis.org"
TARGET="https://192.168.11.12:8006"
;;
*)
echo "Unknown tunnel ID: $TUNNEL_ID"
exit 1
;;
esac
echo "Installing for: $TUNNEL_NAME"
echo " Hostname: $HOSTNAME"
echo " Target: $TARGET"
echo ""
# Create credentials file
CREDS_JSON=$(jq -n \
--arg account "$ACCOUNT_TAG" \
--arg secret "$TUNNEL_SECRET" \
--arg id "$TUNNEL_ID" \
--arg name "$TUNNEL_NAME" \
'{
AccountTag: $account,
TunnelSecret: $secret,
TunnelID: $id,
TunnelName: $name
}')
# Create config file
CONFIG_YML="tunnel: $TUNNEL_ID
credentials-file: /etc/cloudflared/credentials-${TUNNEL_KEY}.json
ingress:
- hostname: $HOSTNAME
service: $TARGET
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
disableChunkedEncoding: true
noTLSVerify: true
- service: http_status:404"
# Copy to container
echo "Copying files to VMID $VMID..."
# Credentials
echo "$CREDS_JSON" > /tmp/credentials-${TUNNEL_KEY}.json
scp /tmp/credentials-${TUNNEL_KEY}.json root@${PROXMOX_HOST}:/tmp/ >/dev/null 2>&1
ssh root@${PROXMOX_HOST} "pct push $VMID /tmp/credentials-${TUNNEL_KEY}.json /etc/cloudflared/credentials-${TUNNEL_KEY}.json" >/dev/null 2>&1
ssh root@${PROXMOX_HOST} "pct exec $VMID -- chmod 600 /etc/cloudflared/credentials-${TUNNEL_KEY}.json"
# Config
echo "$CONFIG_YML" > /tmp/tunnel-${TUNNEL_KEY}.yml
scp /tmp/tunnel-${TUNNEL_KEY}.yml root@${PROXMOX_HOST}:/tmp/ >/dev/null 2>&1
ssh root@${PROXMOX_HOST} "pct push $VMID /tmp/tunnel-${TUNNEL_KEY}.yml /etc/cloudflared/tunnel-${TUNNEL_KEY}.yml" >/dev/null 2>&1
# Service file
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SERVICE_FILE="$TUNNELS_DIR/systemd/cloudflared-${TUNNEL_KEY}.service"
if [ -f "$SERVICE_FILE" ]; then
scp "$SERVICE_FILE" root@${PROXMOX_HOST}:/tmp/ >/dev/null 2>&1
ssh root@${PROXMOX_HOST} "pct push $VMID /tmp/cloudflared-${TUNNEL_KEY}.service /etc/systemd/system/cloudflared-${TUNNEL_KEY}.service" >/dev/null 2>&1
ssh root@${PROXMOX_HOST} "pct exec $VMID -- systemctl daemon-reload"
ssh root@${PROXMOX_HOST} "pct exec $VMID -- systemctl enable cloudflared-${TUNNEL_KEY}.service"
echo "✓ Service installed and enabled"
fi
# Cleanup
rm -f /tmp/credentials-${TUNNEL_KEY}.json /tmp/tunnel-${TUNNEL_KEY}.yml
echo ""
echo "✓ Installation complete for $TUNNEL_NAME"
echo ""
echo "Start service:"
echo " ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl start cloudflared-${TUNNEL_KEY}'"

View File

@@ -0,0 +1,87 @@
#!/usr/bin/env bash
# Restart a Cloudflare tunnel service
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Usage
if [ $# -lt 1 ]; then
echo "Usage: $0 <tunnel-name>"
echo ""
echo "Tunnel names: ml110, r630-01, r630-02"
exit 1
fi
TUNNEL_NAME="$1"
# Validate tunnel name
if [[ ! "$TUNNEL_NAME" =~ ^(ml110|r630-01|r630-02)$ ]]; then
log_error "Invalid tunnel name: $TUNNEL_NAME"
log_error "Valid names: ml110, r630-01, r630-02"
exit 1
fi
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
SERVICE="cloudflared-${TUNNEL_NAME}"
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
log_info "Restarting tunnel: $TUNNEL_NAME"
# Check if service exists
if ! exec_in_container "systemctl list-unit-files | grep -q $SERVICE"; then
log_error "Service $SERVICE not found"
exit 1
fi
# Restart service
log_info "Stopping service..."
exec_in_container "systemctl stop $SERVICE" || log_warn "Service was not running"
sleep 2
log_info "Starting service..."
if exec_in_container "systemctl start $SERVICE"; then
log_success "Service started"
# Wait a moment and check status
sleep 3
if exec_in_container "systemctl is-active --quiet $SERVICE"; then
log_success "Tunnel $TUNNEL_NAME is running"
else
log_error "Tunnel $TUNNEL_NAME failed to start"
log_info "Check logs: journalctl -u $SERVICE -n 50"
exit 1
fi
else
log_error "Failed to start service"
log_info "Check logs: journalctl -u $SERVICE -n 50"
exit 1
fi

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Load credentials from tunnel-credentials.json and save them
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
CREDS_FILE="$TUNNELS_DIR/tunnel-credentials.json"
if [ ! -f "$CREDS_FILE" ]; then
log_error "Credentials file not found: $CREDS_FILE"
log_info "Run ./scripts/automate-cloudflare-setup.sh first"
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
log_error "jq is required"
exit 1
fi
log_info "Loading credentials from: $CREDS_FILE"
# Load and save each tunnel's credentials
for tunnel_name in ml110 r630-01 r630-02; do
# Handle tunnel names with hyphens by using array notation
if [[ "$tunnel_name" == "r630-01" ]] || [[ "$tunnel_name" == "r630-02" ]]; then
tunnel_id=$(jq -r ".[\"${tunnel_name}\"].id // empty" "$CREDS_FILE")
tunnel_token=$(jq -r ".[\"${tunnel_name}\"].token // empty" "$CREDS_FILE")
else
tunnel_id=$(jq -r ".${tunnel_name}.id // empty" "$CREDS_FILE")
tunnel_token=$(jq -r ".${tunnel_name}.token // empty" "$CREDS_FILE")
fi
if [[ -z "$tunnel_id" ]] || [[ -z "$tunnel_token" ]]; then
log_warn "Missing credentials for $tunnel_name"
continue
fi
log_info "Saving credentials for $tunnel_name..."
if "$SCRIPT_DIR/save-tunnel-credentials.sh" "$tunnel_name" "$tunnel_id" "$tunnel_token"; then
log_success "Credentials saved for $tunnel_name"
else
log_error "Failed to save credentials for $tunnel_name"
fi
echo ""
done
log_success "All credentials saved!"
log_info "Next: Start services with: systemctl start cloudflared-*"

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env bash
# Save tunnel credentials and update config files after API automation
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Configuration
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
declare -A TUNNELS=(
["ml110"]="tunnel-ml110"
["r630-01"]="tunnel-r630-01"
["r630-02"]="tunnel-r630-02"
)
# Usage
if [ $# -lt 2 ]; then
echo "Usage: $0 <tunnel-name> <tunnel-id> <tunnel-token>"
echo ""
echo "Example:"
echo " $0 ml110 abc123def456 'eyJhIjoi...'"
exit 1
fi
TUNNEL_NAME="$1"
TUNNEL_ID="$2"
TUNNEL_TOKEN="$3"
if [[ ! "$TUNNEL_NAME" =~ ^(ml110|r630-01|r630-02)$ ]]; then
log_error "Invalid tunnel name: $TUNNEL_NAME"
exit 1
fi
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
log_info "Saving credentials for tunnel: $TUNNEL_NAME"
# Create credentials JSON
temp_creds=$(mktemp)
cat > "$temp_creds" <<EOF
{
"AccountTag": "${CLOUDFLARE_ACCOUNT_ID:-}",
"TunnelSecret": "${TUNNEL_TOKEN}",
"TunnelID": "${TUNNEL_ID}",
"TunnelName": "${TUNNELS[$TUNNEL_NAME]}"
}
EOF
# Copy to container
log_info "Copying credentials to VMID $VMID..."
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$temp_creds" "/etc/cloudflared/tunnel-${TUNNEL_NAME}.json"
else
scp "$temp_creds" "root@${PROXMOX_HOST}:/tmp/tunnel-${TUNNEL_NAME}.json"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${TUNNEL_NAME}.json /etc/cloudflared/tunnel-${TUNNEL_NAME}.json"
fi
# Set permissions
exec_in_container "chmod 600 /etc/cloudflared/tunnel-${TUNNEL_NAME}.json"
log_success "Credentials saved"
# Update config file with tunnel ID
log_info "Updating config file with tunnel ID..."
config_file="$TUNNELS_DIR/configs/tunnel-${TUNNEL_NAME}.yml"
if [ -f "$config_file" ]; then
# Update tunnel ID in config
if [ "$RUN_LOCAL" = true ]; then
sed -i "s/<TUNNEL_ID_${TUNNEL_NAME^^}>/$TUNNEL_ID/g" "$config_file"
pct push "$VMID" "$config_file" "/etc/cloudflared/tunnel-${TUNNEL_NAME}.yml"
else
sed "s/<TUNNEL_ID_${TUNNEL_NAME^^}>/$TUNNEL_ID/g" "$config_file" > "/tmp/tunnel-${TUNNEL_NAME}.yml"
scp "/tmp/tunnel-${TUNNEL_NAME}.yml" "root@${PROXMOX_HOST}:/tmp/"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${TUNNEL_NAME}.yml /etc/cloudflared/tunnel-${TUNNEL_NAME}.yml"
fi
log_success "Config file updated"
else
log_warn "Config file not found: $config_file"
fi
# Cleanup
rm -f "$temp_creds"
log_success "Credentials and config saved for tunnel-${TUNNEL_NAME}"

View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
# Simple script to set allowed email addresses for Cloudflare Access
# Usage: ./set-access-emails.sh email1@example.com email2@example.com ...
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
# Load .env
if [ -f "$TUNNELS_DIR/../../.env" ]; then
source "$TUNNELS_DIR/../../.env" 2>/dev/null || true
fi
if [[ -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]] || [[ -z "${CLOUDFLARE_API_KEY:-}" ]] || [[ -z "${CLOUDFLARE_EMAIL:-}" ]]; then
log_error "Cloudflare credentials not found in .env"
exit 1
fi
# Get emails from command line
ALLOWED_EMAILS=("$@")
if [ ${#ALLOWED_EMAILS[@]} -eq 0 ]; then
log_error "Usage: $0 email1@example.com email2@example.com ..."
echo ""
log_info "Example:"
echo " $0 admin@example.com user1@example.com user2@example.com"
exit 1
fi
log_info "Configuring Access policies for: ${ALLOWED_EMAILS[*]}"
echo ""
# App IDs (from earlier creation)
declare -A APP_IDS=(
["ml110-01.d-bis.org"]="ebc7cafa-11dc-4bfa-8347-4e6c229f4d3b"
["r630-01.d-bis.org"]="967625a2-0199-490a-9f4f-2de5c8d49243"
["r630-02.d-bis.org"]="618ab003-37bf-413e-b0fa-13963c2186c5"
)
# Function to make API request
cf_api_request() {
local method="$1"
local endpoint="$2"
local data="${3:-}"
local url="https://api.cloudflare.com/client/v4${endpoint}"
local temp_file=$(mktemp)
local http_code
if [[ -n "$data" ]]; then
http_code=$(curl -s -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" \
-d "$data" 2>/dev/null)
else
http_code=$(curl -s -o "$temp_file" -w "%{http_code}" \
-X "$method" "$url" \
-H "X-Auth-Email: ${CLOUDFLARE_EMAIL}" \
-H "X-Auth-Key: ${CLOUDFLARE_API_KEY}" \
-H "Content-Type: application/json" 2>/dev/null)
fi
local response=$(cat "$temp_file" 2>/dev/null || echo "")
rm -f "$temp_file"
if [[ "$http_code" != "200" ]] && [[ "$http_code" != "201" ]]; then
return 1
fi
echo "$response"
}
# Build email includes
EMAIL_INCLUDES=$(printf '%s\n' "${ALLOWED_EMAILS[@]}" | jq -R . | jq -s . | jq 'map({email: {email: .}})')
# Configure each app
for hostname in "${!APP_IDS[@]}"; do
app_id="${APP_IDS[$hostname]}"
log_info "Configuring $hostname..."
# Get existing policies
POLICIES=$(cf_api_request "GET" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps/${app_id}/policies" 2>&1) || POLICIES="{}"
EXISTING_ID=$(echo "$POLICIES" | jq -r '.result[]? | select(.name == "Allow Team Access") | .id' 2>/dev/null || echo "")
# Build policy (require field removed - email verification is default)
POLICY_DATA=$(jq -n \
--argjson emails "$EMAIL_INCLUDES" \
'{
name: "Allow Team Access",
decision: "allow",
include: $emails
}')
if [[ -n "$EXISTING_ID" ]] && [[ "$EXISTING_ID" != "null" ]]; then
# Update
response=$(cf_api_request "PUT" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps/${app_id}/policies/${EXISTING_ID}" "$POLICY_DATA" 2>&1)
else
# Create
response=$(cf_api_request "POST" "/accounts/${CLOUDFLARE_ACCOUNT_ID}/access/apps/${app_id}/policies" "$POLICY_DATA" 2>&1)
fi
if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
log_success "$hostname configured"
else
log_error " ✗ Failed for $hostname"
echo "$response" | jq -r '.errors[0].message // "Unknown error"' 2>/dev/null || echo "$response" | head -3
fi
done
echo ""
log_success "=== Access Policies Configured ==="
log_info "Allowed emails:"
for email in "${ALLOWED_EMAILS[@]}"; do
echo " - $email"
done

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env bash
# Automated credentials setup - checks for files and sets up everything
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
declare -A TUNNELS=(
["ml110"]="tunnel-ml110:ccd7150a-9881-4b8c-a105-9b4ead6e69a2"
["r630-01"]="tunnel-r630-01:4481af8f-b24c-4cd3-bdd5-f562f4c97df4"
["r630-02"]="tunnel-r630-02:0876f12b-64d7-4927-9ab3-94cb6cf48af9"
)
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
copy_to_container() {
local src="$1"
local dst="$2"
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$src" "$dst"
else
scp "$src" "root@${PROXMOX_HOST}:/tmp/$(basename "$src")" >/dev/null 2>&1
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/$(basename "$src") $dst" >/dev/null 2>&1
fi
}
log_info "=== Automated Credentials Setup ==="
echo ""
# Check for credentials files in current directory
MISSING_FILES=()
FOUND_FILES=()
for tunnel_key in "${!TUNNELS[@]}"; do
IFS=':' read -r tunnel_name tunnel_id <<< "${TUNNELS[$tunnel_key]}"
# Try multiple possible file names
creds_file=""
for possible_name in "credentials-${tunnel_key}.json" "credentials-${tunnel_name}.json" "${tunnel_name}.json"; do
if [ -f "$TUNNELS_DIR/$possible_name" ]; then
creds_file="$TUNNELS_DIR/$possible_name"
break
fi
if [ -f "$possible_name" ]; then
creds_file="$possible_name"
break
fi
done
if [[ -n "$creds_file" ]] && [[ -f "$creds_file" ]]; then
FOUND_FILES+=("$tunnel_key:$creds_file")
log_success "Found credentials for $tunnel_name: $creds_file"
else
MISSING_FILES+=("$tunnel_key:$tunnel_name:$tunnel_id")
log_warn "Missing credentials for $tunnel_name"
fi
done
echo ""
# If some files are missing, provide instructions
if [ ${#MISSING_FILES[@]} -gt 0 ]; then
log_error "Missing credentials files:"
for entry in "${MISSING_FILES[@]}"; do
IFS=':' read -r key name id <<< "$entry"
echo " - $name (ID: $id)"
done
echo ""
log_info "To download credentials:"
echo " 1. Go to: https://one.dash.cloudflare.com/ → Zero Trust → Networks → Tunnels"
echo " 2. Click each tunnel → Configure → Download credentials file"
echo " 3. Save as: credentials-${key}.json in $TUNNELS_DIR"
echo " 4. Run this script again"
echo ""
if [ ${#FOUND_FILES[@]} -eq 0 ]; then
log_error "No credentials files found. Cannot proceed."
exit 1
else
log_warn "Proceeding with available credentials only..."
echo ""
fi
fi
# Process found files
if [ ${#FOUND_FILES[@]} -eq 0 ]; then
log_error "No credentials files found!"
exit 1
fi
log_info "Setting up credentials for ${#FOUND_FILES[@]} tunnel(s)..."
echo ""
# Ensure /etc/cloudflared exists in container
exec_in_container "mkdir -p /etc/cloudflared"
for entry in "${FOUND_FILES[@]}"; do
IFS=':' read -r tunnel_key creds_file <<< "$entry"
IFS=':' read -r tunnel_name tunnel_id <<< "${TUNNELS[$tunnel_key]}"
log_info "Processing $tunnel_name..."
# Validate JSON structure
if ! jq -e '.AccountTag, .TunnelSecret, .TunnelID' "$creds_file" >/dev/null 2>&1; then
log_error "Invalid credentials file format: $creds_file"
log_info "Expected format: { \"AccountTag\": \"...\", \"TunnelSecret\": \"...\", \"TunnelID\": \"...\" }"
continue
fi
# Copy credentials to container
log_info " Copying credentials to VMID $VMID..."
copy_to_container "$creds_file" "/etc/cloudflared/credentials-${tunnel_key}.json"
# Set permissions
exec_in_container "chmod 600 /etc/cloudflared/credentials-${tunnel_key}.json"
# Update config file
config_file="$TUNNELS_DIR/configs/tunnel-${tunnel_key}.yml"
if [ -f "$config_file" ]; then
log_info " Updating config file..."
temp_config=$(mktemp)
# Replace tunnel ID placeholder
sed "s/<TUNNEL_ID_${tunnel_key^^}>/$tunnel_id/g" "$config_file" > "$temp_config"
# Ensure credentials path is correct
sed -i "s|credentials-file:.*|credentials-file: /etc/cloudflared/credentials-${tunnel_key}.json|g" "$temp_config"
# Copy config to container
copy_to_container "$temp_config" "/etc/cloudflared/tunnel-${tunnel_key}.yml"
rm -f "$temp_config"
log_success "$tunnel_name configured"
else
log_warn " Config file not found: $config_file"
fi
echo ""
done
log_success "=== Setup Complete ==="
echo ""
log_info "Next steps:"
log_info "1. Verify: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- ls -la /etc/cloudflared/'"
log_info "2. Start services: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl start cloudflared-*'"
log_info "3. Check status: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl status cloudflared-*'"
log_info "4. Enable on boot: ssh root@${PROXMOX_HOST} 'pct exec $VMID -- systemctl enable cloudflared-*'"

View File

@@ -0,0 +1,203 @@
#!/usr/bin/env bash
# Setup script for Cloudflare Multi-Tunnel configuration
# This script sets up separate tunnels for each Proxmox host
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Configuration
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
TUNNELS=("ml110" "r630-01" "r630-02")
# Check if running on Proxmox host or need to SSH
if command -v pct &> /dev/null; then
RUN_LOCAL=true
log_info "Running on Proxmox host directly"
else
RUN_LOCAL=false
log_info "Will execute commands via SSH to $PROXMOX_HOST"
fi
# Function to execute command (local or via SSH)
exec_cmd() {
if [ "$RUN_LOCAL" = true ]; then
eval "$@"
else
ssh "root@${PROXMOX_HOST}" "$@"
fi
}
# Function to execute command in container
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd"
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'"
fi
}
log_info "=== Cloudflare Multi-Tunnel Setup ==="
log_info "Proxmox Host: $PROXMOX_HOST"
log_info "VMID: $VMID"
log_info "Tunnels: ${TUNNELS[*]}"
echo ""
# Check if VMID 102 exists and is running
log_info "Checking VMID $VMID status..."
if ! exec_cmd "pct status $VMID 2>/dev/null | grep -q running"; then
log_error "VMID $VMID is not running. Please start it first."
exit 1
fi
log_success "VMID $VMID is running"
# Check if cloudflared is installed
log_info "Checking cloudflared installation..."
if ! exec_in_container "command -v cloudflared &> /dev/null"; then
log_warn "cloudflared not found. Installing..."
exec_in_container "
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -O /tmp/cloudflared.deb
dpkg -i /tmp/cloudflared.deb || apt-get install -f -y
rm /tmp/cloudflared.deb
cloudflared --version
"
log_success "cloudflared installed"
else
log_success "cloudflared is installed"
fi
# Create directories
log_info "Creating configuration directories..."
exec_in_container "
mkdir -p /etc/cloudflared
mkdir -p /var/log/cloudflared
"
log_success "Directories created"
# Copy configuration files
log_info "Copying configuration files..."
for tunnel in "${TUNNELS[@]}"; do
config_file="$TUNNELS_DIR/configs/tunnel-${tunnel}.yml"
if [ ! -f "$config_file" ]; then
log_error "Configuration file not found: $config_file"
exit 1
fi
# Copy to container
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$config_file" "/etc/cloudflared/tunnel-${tunnel}.yml"
else
scp "$config_file" "root@${PROXMOX_HOST}:/tmp/tunnel-${tunnel}.yml"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${tunnel}.yml /etc/cloudflared/tunnel-${tunnel}.yml"
fi
log_success "Copied config for tunnel-${tunnel}"
done
# Copy systemd service files
log_info "Installing systemd service files..."
for tunnel in "${TUNNELS[@]}"; do
service_file="$TUNNELS_DIR/systemd/cloudflared-${tunnel}.service"
if [ ! -f "$service_file" ]; then
log_error "Service file not found: $service_file"
exit 1
fi
# Copy to container
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$service_file" "/tmp/cloudflared-${tunnel}.service"
exec_in_container "mv /tmp/cloudflared-${tunnel}.service /etc/systemd/system/cloudflared-${tunnel}.service"
else
scp "$service_file" "root@${PROXMOX_HOST}:/tmp/cloudflared-${tunnel}.service"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/cloudflared-${tunnel}.service /etc/systemd/system/cloudflared-${tunnel}.service"
exec_in_container "mv /tmp/cloudflared-${tunnel}.service /etc/systemd/system/cloudflared-${tunnel}.service"
fi
log_success "Installed service for tunnel-${tunnel}"
done
# Reload systemd
log_info "Reloading systemd..."
exec_in_container "systemctl daemon-reload"
log_success "Systemd reloaded"
# Prompt for tunnel tokens
log_warn "=== IMPORTANT: Tunnel Setup Required ==="
log_warn "Before enabling services, you need to:"
log_warn "1. Create tunnels in Cloudflare Dashboard"
log_warn "2. Copy tunnel tokens/credentials"
log_warn "3. Update configuration files with tunnel IDs"
log_warn "4. Place credential files in /etc/cloudflared/"
echo ""
log_info "See docs/CLOUDFLARE_ACCESS_SETUP.md for detailed instructions"
echo ""
read -p "Have you created the tunnels and have the credentials ready? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
log_warn "Setup paused. Please create tunnels first."
log_info "Run this script again after creating tunnels."
exit 0
fi
# Prompt for each tunnel
for tunnel in "${TUNNELS[@]}"; do
echo ""
log_info "=== Setting up tunnel-${tunnel} ==="
read -p "Enter tunnel ID for tunnel-${tunnel}: " tunnel_id
read -p "Enter path to credentials JSON file (or press Enter to skip): " creds_file
if [ -n "$creds_file" ] && [ -f "$creds_file" ]; then
# Update config file with tunnel ID
exec_in_container "sed -i 's/<TUNNEL_ID_${tunnel^^}>/$tunnel_id/g' /etc/cloudflared/tunnel-${tunnel}.yml"
# Copy credentials file
if [ "$RUN_LOCAL" = true ]; then
pct push "$VMID" "$creds_file" "/etc/cloudflared/tunnel-${tunnel}.json"
else
scp "$creds_file" "root@${PROXMOX_HOST}:/tmp/tunnel-${tunnel}.json"
ssh "root@${PROXMOX_HOST}" "pct push $VMID /tmp/tunnel-${tunnel}.json /etc/cloudflared/tunnel-${tunnel}.json"
fi
exec_in_container "chmod 600 /etc/cloudflared/tunnel-${tunnel}.json"
log_success "Credentials configured for tunnel-${tunnel}"
else
log_warn "Skipping credentials for tunnel-${tunnel}. Configure manually later."
fi
done
# Enable services (but don't start yet - user should verify configs first)
log_info "Enabling systemd services..."
for tunnel in "${TUNNELS[@]}"; do
exec_in_container "systemctl enable cloudflared-${tunnel}.service"
log_success "Enabled cloudflared-${tunnel}.service"
done
echo ""
log_success "=== Setup Complete ==="
log_info "Next steps:"
log_info "1. Verify configuration files in /etc/cloudflared/"
log_info "2. Start services: systemctl start cloudflared-*"
log_info "3. Check status: systemctl status cloudflared-*"
log_info "4. Configure Cloudflare Access (see docs/CLOUDFLARE_ACCESS_SETUP.md)"
log_info "5. Set up monitoring: ./scripts/monitor-tunnels.sh --daemon"

View File

@@ -0,0 +1,165 @@
#!/usr/bin/env bash
# Verify prerequisites before deployment
set -euo pipefail
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[⚠]${NC} $1"; }
log_error() { echo -e "${RED}[✗]${NC} $1"; }
PROXMOX_HOST="${PROXMOX_HOST:-192.168.11.10}"
VMID="${VMID:-102}"
# Check if running on Proxmox host
if command -v pct &> /dev/null; then
RUN_LOCAL=true
else
RUN_LOCAL=false
fi
exec_in_container() {
local cmd="$1"
if [ "$RUN_LOCAL" = true ]; then
pct exec "$VMID" -- bash -c "$cmd" 2>/dev/null || return 1
else
ssh "root@${PROXMOX_HOST}" "pct exec $VMID -- bash -c '$cmd'" 2>/dev/null || return 1
fi
}
echo ""
echo "=========================================="
echo " Prerequisites Verification"
echo "=========================================="
echo ""
all_checks_passed=true
# Check 1: VMID 102 exists
log_info "Checking VMID $VMID exists..."
if exec_in_container "true"; then
log_success "VMID $VMID is accessible"
else
log_error "VMID $VMID is not accessible"
all_checks_passed=false
fi
# Check 2: VMID 102 is running
log_info "Checking VMID $VMID is running..."
if exec_in_container "systemctl is-system-running > /dev/null 2>&1"; then
log_success "VMID $VMID is running"
else
log_error "VMID $VMID is not running"
all_checks_passed=false
fi
# Check 3: Network connectivity to Proxmox hosts
log_info "Checking network connectivity to Proxmox hosts..."
for ip in 192.168.11.10 192.168.11.11 192.168.11.12; do
if exec_in_container "timeout 3 bash -c 'echo > /dev/tcp/${ip}/8006' 2>/dev/null"; then
log_success "Can reach ${ip}:8006"
else
log_warn "Cannot reach ${ip}:8006 (may need to test manually)"
fi
done
# Check 4: cloudflared installation
log_info "Checking cloudflared installation..."
if exec_in_container "command -v cloudflared &> /dev/null"; then
version=$(exec_in_container "cloudflared --version 2>/dev/null | head -1" || echo "unknown")
log_success "cloudflared is installed: $version"
else
log_warn "cloudflared is not installed (will be installed by setup script)"
fi
# Check 5: Configuration files exist
log_info "Checking configuration files..."
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TUNNELS_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
for tunnel in ml110 r630-01 r630-02; do
if [ -f "$TUNNELS_DIR/configs/tunnel-${tunnel}.yml" ]; then
log_success "Config file exists: tunnel-${tunnel}.yml"
else
log_error "Config file missing: tunnel-${tunnel}.yml"
all_checks_passed=false
fi
done
# Check 6: Systemd service files exist
log_info "Checking systemd service files..."
for tunnel in ml110 r630-01 r630-02; do
if [ -f "$TUNNELS_DIR/systemd/cloudflared-${tunnel}.service" ]; then
log_success "Service file exists: cloudflared-${tunnel}.service"
else
log_error "Service file missing: cloudflared-${tunnel}.service"
all_checks_passed=false
fi
done
# Check 7: Scripts are executable
log_info "Checking scripts are executable..."
for script in setup-multi-tunnel.sh install-tunnel.sh monitor-tunnels.sh check-tunnel-health.sh alert-tunnel-failure.sh restart-tunnel.sh; do
if [ -x "$SCRIPT_DIR/$script" ]; then
log_success "Script is executable: $script"
else
log_warn "Script not executable: $script (will fix)"
chmod +x "$SCRIPT_DIR/$script" 2>/dev/null || true
fi
done
# Check 8: Directories exist
log_info "Checking directory structure..."
for dir in configs systemd scripts monitoring docs; do
if [ -d "$TUNNELS_DIR/$dir" ]; then
log_success "Directory exists: $dir"
else
log_error "Directory missing: $dir"
all_checks_passed=false
fi
done
echo ""
echo "=========================================="
echo " Manual Prerequisites"
echo "=========================================="
echo ""
log_warn "The following must be done manually:"
echo ""
echo "1. Cloudflare Account:"
echo " - [ ] Cloudflare account with Zero Trust enabled"
echo " - [ ] Domain d-bis.org managed by Cloudflare"
echo ""
echo "2. Create Tunnels in Cloudflare Dashboard:"
echo " - [ ] Go to: https://one.dash.cloudflare.com"
echo " - [ ] Zero Trust → Networks → Tunnels"
echo " - [ ] Create: tunnel-ml110"
echo " - [ ] Create: tunnel-r630-01"
echo " - [ ] Create: tunnel-r630-02"
echo " - [ ] Copy tunnel tokens/credentials"
echo ""
echo "3. DNS Records (after tunnels created):"
echo " - [ ] Create CNAME: ml110-01 → <tunnel-id>.cfargotunnel.com (Proxied)"
echo " - [ ] Create CNAME: r630-01 → <tunnel-id>.cfargotunnel.com (Proxied)"
echo " - [ ] Create CNAME: r630-02 → <tunnel-id>.cfargotunnel.com (Proxied)"
echo ""
echo "=========================================="
if [ "$all_checks_passed" = true ]; then
log_success "All automated checks passed!"
echo ""
log_info "You can proceed with:"
echo " ./scripts/setup-multi-tunnel.sh"
exit 0
else
log_error "Some checks failed. Please fix issues before proceeding."
exit 1
fi