feat: add support for public DNS target IP and private DNS allowance in Cloudflare setup

This commit is contained in:
S
2026-03-02 23:27:04 -06:00
parent 14a5773a2d
commit 63f5bf6ea7
7 changed files with 242 additions and 29 deletions

View File

@@ -124,6 +124,8 @@ TLS_MODE=cloudflare # TLS mode: "cloudflare" (DNS-01 via CF API) o
CADDY_DOMAIN= # Wildcard cert base domain (e.g. privacyindesign.com → cert for *.privacyindesign.com) CADDY_DOMAIN= # Wildcard cert base domain (e.g. privacyindesign.com → cert for *.privacyindesign.com)
CADDY_DATA_PATH= # Absolute path on host for Caddy data (e.g. /mnt/nvme/caddy) CADDY_DATA_PATH= # Absolute path on host for Caddy data (e.g. /mnt/nvme/caddy)
CLOUDFLARE_API_TOKEN= # Cloudflare API token with Zone:DNS:Edit (only if TLS_MODE=cloudflare) CLOUDFLARE_API_TOKEN= # Cloudflare API token with Zone:DNS:Edit (only if TLS_MODE=cloudflare)
PUBLIC_DNS_TARGET_IP= # Phase 8 Cloudflare A-record target for GITEA_DOMAIN (public ingress IP recommended)
PHASE8_ALLOW_PRIVATE_DNS_TARGET=false # true only for LAN-only/split-DNS setups using private RFC1918 target IPs
SSL_CERT_PATH= # Absolute path to SSL cert (only if TLS_MODE=existing) SSL_CERT_PATH= # Absolute path to SSL cert (only if TLS_MODE=existing)
SSL_KEY_PATH= # Absolute path to SSL key (only if TLS_MODE=existing) SSL_KEY_PATH= # Absolute path to SSL key (only if TLS_MODE=existing)

View File

@@ -231,7 +231,7 @@ When `TLS_MODE=cloudflare`, Caddy handles certificate renewal automatically via
| MacBook | macOS, Homebrew, jq >= 1.6, curl >= 7.70, git >= 2.30, shellcheck >= 0.8, gh >= 2.0, bw >= 2.0 | | MacBook | macOS, Homebrew, jq >= 1.6, curl >= 7.70, git >= 2.30, shellcheck >= 0.8, gh >= 2.0, bw >= 2.0 |
| Unraid | Linux, Docker >= 20.0, docker-compose >= 2.0, jq >= 1.6, passwordless sudo for SSH user | | Unraid | Linux, Docker >= 20.0, docker-compose >= 2.0, jq >= 1.6, passwordless sudo for SSH user |
| Fedora | Linux with dnf, Docker CE >= 20.0, docker-compose >= 2.0, jq >= 1.6, passwordless sudo for SSH user | | Fedora | Linux with dnf, Docker CE >= 20.0, docker-compose >= 2.0, jq >= 1.6, passwordless sudo for SSH user |
| Network | MacBook can SSH to both servers, DNS A record pointing to Unraid (needed for Phase 8 TLS), Cloudflare API token (if using `TLS_MODE=cloudflare`) | | Network | MacBook can SSH to both servers; for `TLS_MODE=cloudflare`, provide `CLOUDFLARE_API_TOKEN` plus `PUBLIC_DNS_TARGET_IP` (public ingress IP recommended; private IP requires `PHASE8_ALLOW_PRIVATE_DNS_TARGET=true`) |
## Quick Start ## Quick Start

View File

@@ -31,8 +31,9 @@ Before running anything, confirm:
DNS and TLS are only needed for Phase 8 (Caddy reverse proxy). You can set these up later: DNS and TLS are only needed for Phase 8 (Caddy reverse proxy). You can set these up later:
- A DNS A record for your Gitea domain pointing to `UNRAID_IP`
- If using `TLS_MODE=cloudflare`: a Cloudflare API token with Zone:DNS:Edit permission - If using `TLS_MODE=cloudflare`: a Cloudflare API token with Zone:DNS:Edit permission
- `PUBLIC_DNS_TARGET_IP` set to your ingress IP for `GITEA_DOMAIN` (public IP recommended)
- If you intentionally use LAN-only split DNS with a private IP target, set `PHASE8_ALLOW_PRIVATE_DNS_TARGET=true`
### 2. Passwordless sudo on remote hosts ### 2. Passwordless sudo on remote hosts
@@ -316,7 +317,7 @@ Then re-run Phase 4. Already-migrated repos will be skipped.
**Symptom**: Preflight check 14 fails. **Symptom**: Preflight check 14 fails.
**Fix**: Add or update your DNS A record. If using a local DNS server or `/etc/hosts`, ensure the record points to `UNRAID_IP`. DNS propagation can take minutes to hours. **Fix**: Phase 8 can auto-upsert the Cloudflare A record for `GITEA_DOMAIN` when `TLS_MODE=cloudflare`. Set `PUBLIC_DNS_TARGET_IP` first. Use a public ingress IP for public access. For LAN-only split DNS, set `PHASE8_ALLOW_PRIVATE_DNS_TARGET=true`.
### Caddy fails to start or obtain TLS certificate in Phase 8 ### Caddy fails to start or obtain TLS certificate in Phase 8

View File

@@ -269,9 +269,9 @@ _ENV_VAR_TYPES=(
) )
# Conditional variables — validated only when TLS_MODE matches. # Conditional variables — validated only when TLS_MODE matches.
_ENV_CONDITIONAL_TLS_NAMES=(CLOUDFLARE_API_TOKEN SSL_CERT_PATH SSL_KEY_PATH) _ENV_CONDITIONAL_TLS_NAMES=(CLOUDFLARE_API_TOKEN PUBLIC_DNS_TARGET_IP PHASE8_ALLOW_PRIVATE_DNS_TARGET SSL_CERT_PATH SSL_KEY_PATH)
_ENV_CONDITIONAL_TLS_TYPES=(nonempty path path) _ENV_CONDITIONAL_TLS_TYPES=(nonempty ip bool path path)
_ENV_CONDITIONAL_TLS_WHEN=( cloudflare existing existing) _ENV_CONDITIONAL_TLS_WHEN=( cloudflare cloudflare cloudflare existing existing)
# Conditional variables — validated only when GITEA_DB_TYPE is NOT sqlite3. # Conditional variables — validated only when GITEA_DB_TYPE is NOT sqlite3.
_ENV_CONDITIONAL_DB_NAMES=(GITEA_DB_PORT GITEA_DB_NAME GITEA_DB_USER GITEA_DB_PASSWD) _ENV_CONDITIONAL_DB_NAMES=(GITEA_DB_PORT GITEA_DB_NAME GITEA_DB_USER GITEA_DB_PASSWD)

View File

@@ -513,12 +513,52 @@ if ! ssh_exec UNRAID "docker exec caddy caddy reload --config /etc/caddy/Caddyfi
fi fi
log_success "Caddy container is running with new config" log_success "Caddy container is running with new config"
probe_http_code_ok() {
local code="$1" role="$2"
if [[ "$role" == "gitea_api" ]]; then
[[ "$code" == "200" ]]
return
fi
[[ "$code" =~ ^(2|3)[0-9][0-9]$ || "$code" == "401" || "$code" == "403" ]]
}
probe_host_via_caddy() {
local host="$1" upstream="$2" role="$3"
local path="/"
if [[ "$role" == "gitea_api" ]]; then
path="/api/v1/version"
fi
local tmp_body http_code
tmp_body=$(mktemp)
http_code=$(curl -sk --resolve "${host}:443:${UNRAID_CADDY_IP}" \
-o "$tmp_body" -w "%{http_code}" "https://${host}${path}" 2>/dev/null || echo "000")
if probe_http_code_ok "$http_code" "$role"; then
log_success "Probe passed: ${host} (HTTP ${http_code})"
rm -f "$tmp_body"
return 0
fi
log_error "Probe failed: ${host} (HTTP ${http_code})"
if [[ "$http_code" == "502" || "$http_code" == "503" || "$http_code" == "504" || "$http_code" == "000" ]]; then
local upstream_probe_raw upstream_code
upstream_probe_raw=$(ssh_exec UNRAID "curl -sk -o /dev/null -w '%{http_code}' '${upstream}' || true" 2>/dev/null || true)
upstream_code=$(printf '%s' "$upstream_probe_raw" | tr -cd '0-9')
if [[ -z "$upstream_code" ]]; then
upstream_code="000"
elif [[ ${#upstream_code} -gt 3 ]]; then
upstream_code="${upstream_code:$((${#upstream_code} - 3))}"
fi
log_warn "Upstream check from Unraid: ${upstream} -> HTTP ${upstream_code}"
fi
rm -f "$tmp_body"
return 1
}
if [[ "$MODE" == "canary" ]]; then if [[ "$MODE" == "canary" ]]; then
if confirm_action "Run canary HTTPS probe for tower.sintheus.com via Caddy IP now? [y/N] "; then if confirm_action "Run canary HTTPS probe for tower.sintheus.com via Caddy IP now? [y/N] "; then
if curl -skf --resolve "tower.sintheus.com:443:${UNRAID_CADDY_IP}" \ if ! probe_host_via_caddy "tower.sintheus.com" "https://192.168.1.82:443" "generic"; then
"https://tower.sintheus.com/" >/dev/null; then
log_success "Canary probe passed: tower.sintheus.com via ${UNRAID_CADDY_IP}"
else
log_error "Canary probe failed for tower.sintheus.com via ${UNRAID_CADDY_IP}" log_error "Canary probe failed for tower.sintheus.com via ${UNRAID_CADDY_IP}"
exit 1 exit 1
fi fi
@@ -527,11 +567,12 @@ else
log_step 5 "Probing all configured hosts via Caddy IP..." log_step 5 "Probing all configured hosts via Caddy IP..."
PROBE_FAILS=0 PROBE_FAILS=0
for entry in "${SELECTED_HOST_MAP[@]}"; do for entry in "${SELECTED_HOST_MAP[@]}"; do
IFS='|' read -r host _ <<< "$entry" IFS='|' read -r host upstream _ <<< "$entry"
if curl -skf --resolve "${host}:443:${UNRAID_CADDY_IP}" "https://${host}/" >/dev/null; then role="generic"
log_success "Probe passed: ${host}" if [[ "$host" == "$GITEA_DOMAIN" ]]; then
else role="gitea_api"
log_error "Probe failed: ${host}" fi
if ! probe_host_via_caddy "$host" "$upstream" "$role"; then
PROBE_FAILS=$((PROBE_FAILS + 1)) PROBE_FAILS=$((PROBE_FAILS + 1))
fi fi
done done

View File

@@ -25,7 +25,7 @@ require_vars UNRAID_IP UNRAID_SSH_USER UNRAID_GITEA_IP UNRAID_CADDY_IP \
REPO_NAMES REPO_NAMES
if [[ "$TLS_MODE" == "cloudflare" ]]; then if [[ "$TLS_MODE" == "cloudflare" ]]; then
require_vars CLOUDFLARE_API_TOKEN require_vars CLOUDFLARE_API_TOKEN PUBLIC_DNS_TARGET_IP
elif [[ "$TLS_MODE" == "existing" ]]; then elif [[ "$TLS_MODE" == "existing" ]]; then
require_vars SSL_CERT_PATH SSL_KEY_PATH require_vars SSL_CERT_PATH SSL_KEY_PATH
else else
@@ -44,6 +44,13 @@ UNRAID_DOCKER_NETWORK_NAME="br0"
CADDY_COMPOSE_DIR="${UNRAID_COMPOSE_DIR}/caddy" CADDY_COMPOSE_DIR="${UNRAID_COMPOSE_DIR}/caddy"
PHASE8_GITEA_ROUTE_BEGIN="# BEGIN_PHASE8_GITEA_ROUTE" PHASE8_GITEA_ROUTE_BEGIN="# BEGIN_PHASE8_GITEA_ROUTE"
PHASE8_GITEA_ROUTE_END="# END_PHASE8_GITEA_ROUTE" PHASE8_GITEA_ROUTE_END="# END_PHASE8_GITEA_ROUTE"
PUBLIC_DNS_TARGET_IP="${PUBLIC_DNS_TARGET_IP:-}"
PHASE8_ALLOW_PRIVATE_DNS_TARGET="${PHASE8_ALLOW_PRIVATE_DNS_TARGET:-false}"
if ! validate_bool "${PHASE8_ALLOW_PRIVATE_DNS_TARGET}"; then
log_error "Invalid PHASE8_ALLOW_PRIVATE_DNS_TARGET='${PHASE8_ALLOW_PRIVATE_DNS_TARGET}' (must be true or false)"
exit 1
fi
wait_for_https_public() { wait_for_https_public() {
local host="$1" max_secs="${2:-30}" local host="$1" max_secs="${2:-30}"
@@ -78,6 +85,149 @@ wait_for_https_via_resolve() {
return 1 return 1
} }
check_unraid_gitea_backend() {
local raw code
raw=$(ssh_exec UNRAID "curl -sS -o /dev/null -w '%{http_code}' 'http://${UNRAID_GITEA_IP}:3000/api/v1/version' || true" 2>/dev/null || true)
code=$(printf '%s' "$raw" | tr -cd '0-9')
if [[ -z "$code" ]]; then
code="000"
elif [[ ${#code} -gt 3 ]]; then
code="${code:$((${#code} - 3))}"
fi
if [[ "$code" == "200" ]]; then
log_success "Unraid -> Gitea backend API reachable (HTTP 200)"
return 0
fi
log_error "Unraid -> Gitea backend API check failed (HTTP ${code}) at http://${UNRAID_GITEA_IP}:3000/api/v1/version"
return 1
}
is_private_ipv4() {
local ip="$1"
[[ "$ip" =~ ^10\. ]] || \
[[ "$ip" =~ ^192\.168\. ]] || \
[[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[0-1])\. ]]
}
cloudflare_api_call() {
local method="$1" path="$2" data="${3:-}"
local -a args=(
curl -sS
-X "$method"
-H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}"
-H "Content-Type: application/json"
"https://api.cloudflare.com/client/v4${path}"
)
if [[ -n "$data" ]]; then
args+=(-d "$data")
fi
"${args[@]}"
}
ensure_cloudflare_dns_for_gitea() {
local host="$1" target_ip="$2" zone_id zone_name
local allow_private="${PHASE8_ALLOW_PRIVATE_DNS_TARGET}"
if [[ -z "$target_ip" ]]; then
log_error "PUBLIC_DNS_TARGET_IP is not set"
log_error "Set PUBLIC_DNS_TARGET_IP to your public ingress IP for ${host}"
log_error "For LAN-only/split-DNS use, also set PHASE8_ALLOW_PRIVATE_DNS_TARGET=true"
return 1
fi
if ! validate_ip "$target_ip"; then
log_error "Invalid PUBLIC_DNS_TARGET_IP='${target_ip}'"
log_error "Set PUBLIC_DNS_TARGET_IP in .env to the IP that should answer ${host}"
return 1
fi
zone_name="${host#*.}"
if [[ "$zone_name" == "$host" ]]; then
log_error "GITEA_DOMAIN='${host}' is not a valid FQDN for Cloudflare zone detection"
return 1
fi
if is_private_ipv4 "$target_ip"; then
if [[ "$allow_private" != "true" ]]; then
log_error "Refusing private DNS target ${target_ip} for Cloudflare public DNS"
log_error "Set PUBLIC_DNS_TARGET_IP to public ingress IP, or set PHASE8_ALLOW_PRIVATE_DNS_TARGET=true for LAN-only split-DNS"
return 1
fi
log_warn "Using private DNS target ${target_ip} because PHASE8_ALLOW_PRIVATE_DNS_TARGET=true"
fi
local zone_resp zone_err
zone_resp=$(cloudflare_api_call GET "/zones?name=${zone_name}&status=active")
if [[ "$(jq -r '.success // false' <<< "$zone_resp")" != "true" ]]; then
zone_err=$(jq -r '(.errors // []) | map(.message // tostring) | join("; ")' <<< "$zone_resp")
log_error "Cloudflare zone lookup failed for ${zone_name}: ${zone_err:-unknown error}"
return 1
fi
zone_id=$(jq -r '.result[0].id // empty' <<< "$zone_resp")
if [[ -z "$zone_id" ]]; then
log_error "Cloudflare zone not found or not accessible for ${zone_name}"
return 1
fi
local record_resp record_err record_count record_id old_ip
record_resp=$(cloudflare_api_call GET "/zones/${zone_id}/dns_records?type=A&name=${host}")
if [[ "$(jq -r '.success // false' <<< "$record_resp")" != "true" ]]; then
record_err=$(jq -r '(.errors // []) | map(.message // tostring) | join("; ")' <<< "$record_resp")
log_error "Cloudflare DNS query failed for ${host}: ${record_err:-unknown error}"
return 1
fi
record_count=$(jq -r '.result | length' <<< "$record_resp")
if [[ "$record_count" -eq 0 ]]; then
local create_payload create_resp create_err
create_payload=$(jq -n \
--arg type "A" \
--arg name "$host" \
--arg content "$target_ip" \
--argjson ttl 120 \
--argjson proxied false \
'{type:$type, name:$name, content:$content, ttl:$ttl, proxied:$proxied}')
create_resp=$(cloudflare_api_call POST "/zones/${zone_id}/dns_records" "$create_payload")
if [[ "$(jq -r '.success // false' <<< "$create_resp")" != "true" ]]; then
create_err=$(jq -r '(.errors // []) | map(.message // tostring) | join("; ")' <<< "$create_resp")
log_error "Failed to create Cloudflare A record ${host} -> ${target_ip}: ${create_err:-unknown error}"
return 1
fi
log_success "Created Cloudflare A record: ${host} -> ${target_ip}"
return 0
fi
record_id=$(jq -r '.result[0].id // empty' <<< "$record_resp")
old_ip=$(jq -r '.result[0].content // empty' <<< "$record_resp")
if [[ -n "$old_ip" && "$old_ip" == "$target_ip" ]]; then
log_info "Cloudflare A record already correct: ${host} -> ${target_ip}"
return 0
fi
local update_payload update_resp update_err
update_payload=$(jq -n \
--arg type "A" \
--arg name "$host" \
--arg content "$target_ip" \
--argjson ttl 120 \
--argjson proxied false \
'{type:$type, name:$name, content:$content, ttl:$ttl, proxied:$proxied}')
update_resp=$(cloudflare_api_call PUT "/zones/${zone_id}/dns_records/${record_id}" "$update_payload")
if [[ "$(jq -r '.success // false' <<< "$update_resp")" != "true" ]]; then
update_err=$(jq -r '(.errors // []) | map(.message // tostring) | join("; ")' <<< "$update_resp")
log_error "Failed to update Cloudflare A record ${host}: ${update_err:-unknown error}"
return 1
fi
log_info "Updated Cloudflare A record: ${host}"
log_info " old: ${old_ip:-<empty>}"
log_info " new: ${target_ip}"
return 0
}
caddyfile_has_domain_block() { caddyfile_has_domain_block() {
local file="$1" domain="$2" local file="$1" domain="$2"
awk -v domain="$domain" ' awk -v domain="$domain" '
@@ -322,23 +472,39 @@ log_step 4 "Starting Caddy container..."
CONTAINER_STATUS=$(ssh_exec UNRAID "docker ps --filter name=caddy --format '{{.Status}}'" 2>/dev/null || true) CONTAINER_STATUS=$(ssh_exec UNRAID "docker ps --filter name=caddy --format '{{.Status}}'" 2>/dev/null || true)
if [[ "$CONTAINER_STATUS" == *"Up"* ]]; then if [[ "$CONTAINER_STATUS" == *"Up"* ]]; then
log_info "Caddy container already running" log_info "Caddy container already running"
if [[ "$CADDYFILE_UPDATED" -eq 1 ]]; then log_info "Reloading Caddy config from /etc/caddy/Caddyfile"
log_info "Caddyfile changed — restarting caddy to apply updated config" if ssh_exec UNRAID "docker exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile" >/dev/null 2>&1; then
ssh_exec UNRAID "docker restart caddy >/dev/null" log_success "Caddy config reloaded"
log_success "Caddy container restarted with new config"
else else
log_info "Caddyfile unchanged — restart not required" log_warn "Caddy reload failed; restarting caddy container"
ssh_exec UNRAID "docker restart caddy >/dev/null"
log_success "Caddy container restarted"
fi fi
else else
ssh_exec UNRAID "cd '${CADDY_COMPOSE_DIR}' && docker compose up -d 2>/dev/null || docker-compose up -d" ssh_exec UNRAID "cd '${CADDY_COMPOSE_DIR}' && docker compose up -d 2>/dev/null || docker-compose up -d"
log_success "Caddy container started" if ssh_exec UNRAID "docker exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile" >/dev/null 2>&1; then
log_success "Caddy container started and config loaded"
else
log_success "Caddy container started"
fi
fi fi
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Step 5: Wait for HTTPS to work # Step 5: Ensure DNS points Gitea domain to target ingress IP
# ---------------------------------------------------------------------------
log_step 5 "Ensuring DNS for ${GITEA_DOMAIN}..."
if [[ "$TLS_MODE" == "cloudflare" ]]; then
ensure_cloudflare_dns_for_gitea "${GITEA_DOMAIN}" "${PUBLIC_DNS_TARGET_IP}"
else
log_info "TLS_MODE=${TLS_MODE}; skipping Cloudflare DNS automation"
fi
# ---------------------------------------------------------------------------
# Step 6: Wait for HTTPS to work
# Caddy auto-obtains certs — poll until HTTPS responds. # Caddy auto-obtains certs — poll until HTTPS responds.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
log_step 5 "Waiting for HTTPS (Caddy auto-provisions cert)..." log_step 6 "Waiting for HTTPS (Caddy auto-provisions cert)..."
check_unraid_gitea_backend
if wait_for_https_public "${GITEA_DOMAIN}" 30; then if wait_for_https_public "${GITEA_DOMAIN}" 30; then
log_success "HTTPS verified through current domain routing — https://${GITEA_DOMAIN} works" log_success "HTTPS verified through current domain routing — https://${GITEA_DOMAIN} works"
else else
@@ -348,7 +514,7 @@ else
fi fi
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Step 6: Mark GitHub repos as offsite backup only # Step 7: Mark GitHub repos as offsite backup only
# Updates description + homepage to indicate Gitea is primary. # Updates description + homepage to indicate Gitea is primary.
# Disables wiki and Pages to avoid unnecessary resource usage. # Disables wiki and Pages to avoid unnecessary resource usage.
# Does NOT archive — archived repos reject pushes, which would break # Does NOT archive — archived repos reject pushes, which would break
@@ -356,7 +522,7 @@ fi
# Persists original mutable settings to a local state file for teardown. # Persists original mutable settings to a local state file for teardown.
# GitHub Actions already disabled in Phase 6 Step D. # GitHub Actions already disabled in Phase 6 Step D.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
log_step 6 "Marking GitHub repos as offsite backup..." log_step 7 "Marking GitHub repos as offsite backup..."
init_phase8_state_store init_phase8_state_store
GITHUB_REPO_UPDATE_FAILURES=0 GITHUB_REPO_UPDATE_FAILURES=0
@@ -390,10 +556,11 @@ for repo in "${REPOS[@]}"; do
--arg homepage "https://${GITEA_DOMAIN}/${GITEA_ORG_NAME}/${repo}" \ --arg homepage "https://${GITEA_DOMAIN}/${GITEA_ORG_NAME}/${repo}" \
'{description: $description, homepage: $homepage, has_wiki: false, has_projects: false}') '{description: $description, homepage: $homepage, has_wiki: false, has_projects: false}')
if github_api PATCH "/repos/${GITHUB_USERNAME}/${repo}" "$UPDATE_PAYLOAD" >/dev/null 2>&1; then if PATCH_OUT=$(github_api PATCH "/repos/${GITHUB_USERNAME}/${repo}" "$UPDATE_PAYLOAD" 2>&1); then
log_success "Marked GitHub repo as mirror: ${repo}" log_success "Marked GitHub repo as mirror: ${repo}"
else else
log_error "Failed to update GitHub repo: ${repo}" log_error "Failed to update GitHub repo: ${repo}"
log_error "GitHub API: $(printf '%s' "$PATCH_OUT" | tail -n 1)"
GITHUB_REPO_UPDATE_FAILURES=$((GITHUB_REPO_UPDATE_FAILURES + 1)) GITHUB_REPO_UPDATE_FAILURES=$((GITHUB_REPO_UPDATE_FAILURES + 1))
fi fi

View File

@@ -65,7 +65,7 @@ get_env_val() {
# Prompt function # Prompt function
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Base prompt count (56 fixed + 3 TLS conditional slots — repo/DB prompts added dynamically) # Base prompt count (56 fixed + 3 TLS conditional slots — repo/DB prompts added dynamically)
TOTAL_PROMPTS=59 TOTAL_PROMPTS=61
CURRENT_PROMPT=0 CURRENT_PROMPT=0
LAST_SECTION="" LAST_SECTION=""
@@ -374,11 +374,13 @@ prompt_var "CADDY_DATA_PATH" "Absolute path on host for Caddy data"
# Conditional TLS prompts # Conditional TLS prompts
if [[ "$COLLECTED_TLS_MODE" == "cloudflare" ]]; then if [[ "$COLLECTED_TLS_MODE" == "cloudflare" ]]; then
prompt_var "CLOUDFLARE_API_TOKEN" "Cloudflare API token (Zone:DNS:Edit)" nonempty "" "TLS / REVERSE PROXY" prompt_var "CLOUDFLARE_API_TOKEN" "Cloudflare API token (Zone:DNS:Edit)" nonempty "" "TLS / REVERSE PROXY"
prompt_var "PUBLIC_DNS_TARGET_IP" "Public DNS target IP for GITEA_DOMAIN" ip "" "TLS / REVERSE PROXY"
prompt_var "PHASE8_ALLOW_PRIVATE_DNS_TARGET" "Allow private RFC1918 DNS target (LAN-only/split-DNS)" bool "false" "TLS / REVERSE PROXY"
# Skip cert path prompts but still count them for progress # Skip cert path prompts but still count them for progress
CURRENT_PROMPT=$((CURRENT_PROMPT + 2)) CURRENT_PROMPT=$((CURRENT_PROMPT + 2))
else else
# Skip cloudflare token prompt but count it # Skip cloudflare token prompt but count it
CURRENT_PROMPT=$((CURRENT_PROMPT + 1)) CURRENT_PROMPT=$((CURRENT_PROMPT + 3))
prompt_var "SSL_CERT_PATH" "Absolute path to SSL cert" path "" "TLS / REVERSE PROXY" prompt_var "SSL_CERT_PATH" "Absolute path to SSL cert" path "" "TLS / REVERSE PROXY"
prompt_var "SSL_KEY_PATH" "Absolute path to SSL key" path "" "TLS / REVERSE PROXY" prompt_var "SSL_KEY_PATH" "Absolute path to SSL key" path "" "TLS / REVERSE PROXY"
fi fi