Initial commit: VaultMesh Skills collection
Collection of operational skills for VaultMesh infrastructure including: - backup-sovereign: Backup and recovery operations - btc-anchor: Bitcoin anchoring - cloudflare-tunnel-manager: Cloudflare tunnel management - container-registry: Container registry operations - disaster-recovery: Disaster recovery procedures - dns-sovereign: DNS management - eth-anchor: Ethereum anchoring - gitea-bootstrap: Gitea setup and configuration - hetzner-bootstrap: Hetzner server provisioning - merkle-forest: Merkle tree operations - node-hardening: Node security hardening - operator-bootstrap: Operator initialization - proof-verifier: Cryptographic proof verification - rfc3161-anchor: RFC3161 timestamping - secrets-vault: Secrets management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
29
dns-sovereign/scripts/00_preflight.sh
Normal file
29
dns-sovereign/scripts/00_preflight.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${MODE:=docker}"
|
||||
: "${PDNS_API_KEY:=}"
|
||||
: "${PDNS_DATA_DIR:=$HOME/pdns}"
|
||||
: "${PDNS_PORT:=53}"
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
|
||||
main() {
|
||||
[[ -n "$PDNS_API_KEY" ]] || die "PDNS_API_KEY is required."
|
||||
need curl
|
||||
need jq
|
||||
|
||||
if [[ "$MODE" == "docker" ]]; then
|
||||
need docker
|
||||
else
|
||||
die "MODE must be docker in v1.0.0"
|
||||
fi
|
||||
|
||||
mkdir -p "$SKILL_ROOT/outputs" "$SKILL_ROOT/outputs/backups"
|
||||
mkdir -p "$PDNS_DATA_DIR"
|
||||
|
||||
log_info "Preflight OK."
|
||||
}
|
||||
main "$@"
|
||||
21
dns-sovereign/scripts/10_pdns_plan.sh
Normal file
21
dns-sovereign/scripts/10_pdns_plan.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${PDNS_DATA_DIR:=$HOME/pdns}"
|
||||
: "${PDNS_PORT:=53}"
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
|
||||
main() {
|
||||
echo "[PLAN] $(date -Iseconds) PowerDNS Authoritative (Docker)"
|
||||
echo "[PLAN] Data dir: $PDNS_DATA_DIR"
|
||||
echo "[PLAN] DNS port: $PDNS_PORT/udp + $PDNS_PORT/tcp"
|
||||
echo "[PLAN] API/Web: 127.0.0.1:$PDNS_WEB_PORT (recommended to keep private)"
|
||||
echo "[PLAN] Outputs:"
|
||||
echo " outputs/compose.yml"
|
||||
echo " outputs/pdns.conf"
|
||||
echo "[PLAN] Next: export DRY_RUN=0 && ./scripts/11_pdns_apply.sh"
|
||||
}
|
||||
main "$@"
|
||||
71
dns-sovereign/scripts/11_pdns_apply.sh
Normal file
71
dns-sovereign/scripts/11_pdns_apply.sh
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${PDNS_API_KEY:=}"
|
||||
: "${PDNS_DATA_DIR:=$HOME/pdns}"
|
||||
: "${PDNS_PORT:=53}"
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
need docker
|
||||
[[ -n "$PDNS_API_KEY" ]] || die "PDNS_API_KEY is required."
|
||||
|
||||
local ts; ts="$(date -Iseconds | tr ':' '-')"
|
||||
local backup_dir="$SKILL_ROOT/outputs/backups/$ts"
|
||||
mkdir -p "$backup_dir"
|
||||
|
||||
# pdns.conf (mounted into container)
|
||||
cat > "$SKILL_ROOT/outputs/pdns.conf" <<EOF
|
||||
launch=gsqlite3
|
||||
gsqlite3-database=/var/lib/powerdns/pdns.sqlite3
|
||||
|
||||
api=yes
|
||||
api-key=$PDNS_API_KEY
|
||||
webserver=yes
|
||||
webserver-address=0.0.0.0
|
||||
webserver-port=8081
|
||||
|
||||
# security posture
|
||||
disable-syslog=yes
|
||||
loglevel=4
|
||||
|
||||
# allow API only from container network; bind published port to localhost in compose
|
||||
webserver-allow-from=127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
|
||||
EOF
|
||||
|
||||
# compose
|
||||
cat > "$SKILL_ROOT/outputs/compose.yml" <<EOF
|
||||
version: "3.8"
|
||||
services:
|
||||
pdns:
|
||||
image: powerdns/pdns-auth-49:latest
|
||||
container_name: pdns-auth
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "${PDNS_PORT}:53/udp"
|
||||
- "${PDNS_PORT}:53/tcp"
|
||||
- "127.0.0.1:${PDNS_WEB_PORT}:8081/tcp"
|
||||
volumes:
|
||||
- ${PDNS_DATA_DIR}:/var/lib/powerdns
|
||||
- ${SKILL_ROOT}/outputs/pdns.conf:/etc/powerdns/pdns.conf:ro
|
||||
EOF
|
||||
|
||||
cp -a "$SKILL_ROOT/outputs/pdns.conf" "$backup_dir/pdns.conf"
|
||||
cp -a "$SKILL_ROOT/outputs/compose.yml" "$backup_dir/compose.yml"
|
||||
|
||||
log_info "Starting PowerDNS..."
|
||||
cd "$SKILL_ROOT/outputs"
|
||||
$(compose_cmd) -f compose.yml up -d
|
||||
|
||||
# Probe API
|
||||
log_info "Probing PDNS API..."
|
||||
local api="http://127.0.0.1:${PDNS_WEB_PORT}/api/v1/servers/localhost"
|
||||
curl -fsS -H "X-API-Key: $PDNS_API_KEY" "$api" | jq '.' > "$SKILL_ROOT/outputs/pdns_api_probe.json"
|
||||
log_info "PDNS API probe saved: outputs/pdns_api_probe.json"
|
||||
log_info "PDNS apply complete."
|
||||
}
|
||||
main "$@"
|
||||
23
dns-sovereign/scripts/20_zone_plan.sh
Normal file
23
dns-sovereign/scripts/20_zone_plan.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${ZONE_NAME:=}"
|
||||
: "${NS1_NAME:=}"
|
||||
: "${NS2_NAME:=}"
|
||||
|
||||
main() {
|
||||
if [[ -z "$ZONE_NAME" ]]; then
|
||||
log_warn "ZONE_NAME not set; zone creation will be skipped."
|
||||
exit 0
|
||||
fi
|
||||
echo "[PLAN] $(date -Iseconds) Create zone in PowerDNS"
|
||||
echo "[PLAN] Zone: $ZONE_NAME"
|
||||
echo "[PLAN] NS1: ${NS1_NAME:-ns1.$ZONE_NAME}"
|
||||
echo "[PLAN] NS2: ${NS2_NAME:-ns2.$ZONE_NAME}"
|
||||
echo "[PLAN] Note: PowerDNS API expects trailing dot for zone operations."
|
||||
echo "[PLAN] Next: export DRY_RUN=0 && ./scripts/21_zone_apply.sh"
|
||||
}
|
||||
main "$@"
|
||||
45
dns-sovereign/scripts/21_zone_apply.sh
Normal file
45
dns-sovereign/scripts/21_zone_apply.sh
Normal file
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${PDNS_API_KEY:=}"
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
: "${ZONE_NAME:=}"
|
||||
: "${NS1_NAME:=}"
|
||||
: "${NS2_NAME:=}"
|
||||
|
||||
api() {
|
||||
local method="$1"; shift
|
||||
local url="$1"; shift
|
||||
curl -sS -X "$method" "$url" -H "X-API-Key: $PDNS_API_KEY" -H "Content-Type: application/json" "$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
[[ -n "$PDNS_API_KEY" ]] || die "PDNS_API_KEY is required."
|
||||
[[ -n "$ZONE_NAME" ]] || die "ZONE_NAME is required."
|
||||
|
||||
local zone="${ZONE_NAME%\.}."
|
||||
local ns1="${NS1_NAME:-ns1.${ZONE_NAME}}"
|
||||
local ns2="${NS2_NAME:-ns2.${ZONE_NAME}}"
|
||||
|
||||
local base="http://127.0.0.1:${PDNS_WEB_PORT}/api/v1/servers/localhost"
|
||||
# Check if zone exists
|
||||
if api GET "$base/zones/$zone" | jq -e '.name' >/dev/null 2>&1; then
|
||||
log_warn "Zone already exists: $zone"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log_info "Creating zone: $zone"
|
||||
api POST "$base/zones" --data "{
|
||||
\"name\": \"$zone\",
|
||||
\"kind\": \"Native\",
|
||||
\"masters\": [],
|
||||
\"nameservers\": [\"$ns1.\", \"$ns2.\"]
|
||||
}" | jq '.' > "$SKILL_ROOT/outputs/zone_create_result.json"
|
||||
|
||||
log_info "Zone created; output saved: outputs/zone_create_result.json"
|
||||
}
|
||||
main "$@"
|
||||
23
dns-sovereign/scripts/30_cf_plan.sh
Normal file
23
dns-sovereign/scripts/30_cf_plan.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${CF_API_TOKEN:=}"
|
||||
: "${CF_ZONE_NAME:=}"
|
||||
: "${ZONE_NAME:=}"
|
||||
|
||||
main() {
|
||||
if [[ -z "$CF_API_TOKEN" || -z "$CF_ZONE_NAME" ]]; then
|
||||
log_warn "Cloudflare mirror not configured (CF_API_TOKEN/CF_ZONE_NAME). Skipping CF plan."
|
||||
exit 0
|
||||
fi
|
||||
echo "[PLAN] $(date -Iseconds) Cloudflare DNS mirror"
|
||||
echo "[PLAN] Mirror target zone in Cloudflare: $CF_ZONE_NAME"
|
||||
echo "[PLAN] Source zone (PowerDNS): ${ZONE_NAME:-<unset>}"
|
||||
echo "[PLAN] v1 mirrors only records listed in outputs/mirror_records.json if present."
|
||||
echo "[PLAN] Create that file to define records (A/AAAA/CNAME/TXT)."
|
||||
echo "[PLAN] Next: export DRY_RUN=0 && ./scripts/31_cf_apply.sh"
|
||||
}
|
||||
main "$@"
|
||||
73
dns-sovereign/scripts/31_cf_apply.sh
Normal file
73
dns-sovereign/scripts/31_cf_apply.sh
Normal file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${CF_API_TOKEN:=}"
|
||||
: "${CF_ZONE_NAME:=}"
|
||||
|
||||
api() {
|
||||
local method="$1"; shift
|
||||
local url="$1"; shift
|
||||
curl -sS -X "$method" "$url" \
|
||||
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
[[ -n "$CF_API_TOKEN" ]] || die "CF_API_TOKEN is required."
|
||||
[[ -n "$CF_ZONE_NAME" ]] || die "CF_ZONE_NAME is required."
|
||||
need jq
|
||||
need curl
|
||||
|
||||
local mirror_file="$SKILL_ROOT/outputs/mirror_records.json"
|
||||
if [[ ! -f "$mirror_file" ]]; then
|
||||
die "Missing $mirror_file. Create it like: [{\"type\":\"A\",\"name\":\"app\",\"content\":\"1.2.3.4\",\"ttl\":120}]"
|
||||
fi
|
||||
|
||||
log_info "Resolving Cloudflare zone id for: $CF_ZONE_NAME"
|
||||
local zid; zid="$(api GET "https://api.cloudflare.com/client/v4/zones?name=$CF_ZONE_NAME" | jq -r '.result[0].id')"
|
||||
[[ -n "$zid" && "$zid" != "null" ]] || die "Unable to resolve zone id."
|
||||
|
||||
# For each record, create/update in CF
|
||||
created_ids=[]
|
||||
results=[]
|
||||
while IFS= read -r rec; do
|
||||
rtype="$(echo "$rec" | jq -r '.type')"
|
||||
rname="$(echo "$rec" | jq -r '.name')"
|
||||
rcontent="$(echo "$rec" | jq -r '.content')"
|
||||
rttl="$(echo "$rec" | jq -r '.ttl // 120')"
|
||||
|
||||
# Convert short name to FQDN if needed
|
||||
if [[ "$rname" != *"."* ]]; then
|
||||
fqdn="${rname}.${CF_ZONE_NAME}"
|
||||
else
|
||||
fqdn="$rname"
|
||||
fi
|
||||
|
||||
# check existing
|
||||
existing="$(api GET "https://api.cloudflare.com/client/v4/zones/$zid/dns_records?type=$rtype&name=$fqdn")"
|
||||
rid="$(echo "$existing" | jq -r '.result[0].id')"
|
||||
|
||||
if [[ -n "$rid" && "$rid" != "null" ]]; then
|
||||
log_info "Updating $rtype $fqdn"
|
||||
api PUT "https://api.cloudflare.com/client/v4/zones/$zid/dns_records/$rid" \
|
||||
--data "{\"type\":\"$rtype\",\"name\":\"$fqdn\",\"content\":\"$rcontent\",\"ttl\":$rttl,\"proxied\":true}" \
|
||||
| jq -e '.success==true' >/dev/null || die "Failed update for $fqdn"
|
||||
echo "$rid" >> "$SKILL_ROOT/outputs/cloudflare_record_ids.txt"
|
||||
else
|
||||
log_info "Creating $rtype $fqdn"
|
||||
resp="$(api POST "https://api.cloudflare.com/client/v4/zones/$zid/dns_records" \
|
||||
--data "{\"type\":\"$rtype\",\"name\":\"$fqdn\",\"content\":\"$rcontent\",\"ttl\":$rttl,\"proxied\":true}")"
|
||||
echo "$resp" | jq -e '.success==true' >/dev/null || die "Failed create for $fqdn"
|
||||
new_id="$(echo "$resp" | jq -r '.result.id')"
|
||||
echo "$new_id" >> "$SKILL_ROOT/outputs/cloudflare_record_ids.txt"
|
||||
fi
|
||||
done < <(jq -c '.[]' "$mirror_file")
|
||||
|
||||
log_info "Cloudflare mirror applied. IDs saved to outputs/cloudflare_record_ids.txt"
|
||||
}
|
||||
main "$@"
|
||||
52
dns-sovereign/scripts/90_verify.sh
Normal file
52
dns-sovereign/scripts/90_verify.sh
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
: "${PDNS_API_KEY:=}"
|
||||
: "${PDNS_PORT:=53}"
|
||||
|
||||
main() {
|
||||
local status="$SKILL_ROOT/outputs/status_matrix.json"
|
||||
local ok_container=false ok_api=false ok_probe=false
|
||||
|
||||
if docker ps --format '{{.Names}}' | grep -q '^pdns-auth$'; then ok_container=true; fi
|
||||
if [[ -n "${PDNS_API_KEY:-}" ]]; then
|
||||
if curl -fsS -H "X-API-Key: $PDNS_API_KEY" "http://127.0.0.1:${PDNS_WEB_PORT}/api/v1/servers/localhost" >/dev/null 2>&1; then
|
||||
ok_api=true
|
||||
fi
|
||||
fi
|
||||
[[ -f "$SKILL_ROOT/outputs/pdns_api_probe.json" ]] && ok_probe=true
|
||||
|
||||
blockers="[]"
|
||||
if [[ "$ok_container" != "true" ]]; then blockers='["pdns_container_not_running"]'
|
||||
elif [[ "$ok_api" != "true" ]]; then blockers='["pdns_api_unreachable_or_key_missing"]'
|
||||
fi
|
||||
|
||||
cat > "$status" <<EOF
|
||||
{
|
||||
"skill": "dns-sovereign",
|
||||
"timestamp": "$(date -Iseconds)",
|
||||
"checks": [
|
||||
{"name":"pdns_container_running", "ok": $ok_container},
|
||||
{"name":"pdns_api_reachable", "ok": $ok_api},
|
||||
{"name":"api_probe_saved", "ok": $ok_probe}
|
||||
],
|
||||
"blockers": $blockers,
|
||||
"warnings": [
|
||||
"PowerDNS API is bound to localhost only in compose; keep it private"
|
||||
],
|
||||
"next_steps": [
|
||||
"Create/verify zones and NS records",
|
||||
"Point domain registrar to your NS hosts when ready",
|
||||
"Optionally mirror select records to Cloudflare"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
log_info "Wrote $status"
|
||||
cat "$status"
|
||||
}
|
||||
main "$@"
|
||||
77
dns-sovereign/scripts/99_report.sh
Normal file
77
dns-sovereign/scripts/99_report.sh
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
source "$SCRIPT_DIR/_common.sh"
|
||||
|
||||
: "${PDNS_PORT:=53}"
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
: "${PDNS_DATA_DIR:=$HOME/pdns}"
|
||||
: "${ZONE_NAME:=}"
|
||||
: "${CF_ZONE_NAME:=}"
|
||||
|
||||
main() {
|
||||
mkdir -p "$SKILL_ROOT/outputs"
|
||||
local report="$SKILL_ROOT/outputs/audit_report.md"
|
||||
local status="$SKILL_ROOT/outputs/status_matrix.json"
|
||||
|
||||
cat > "$report" <<EOF
|
||||
# DNS Sovereign Audit Report
|
||||
|
||||
**Generated:** $(date -Iseconds)
|
||||
**PDNS DNS Port:** $PDNS_PORT
|
||||
**PDNS API Port (localhost):** $PDNS_WEB_PORT
|
||||
**PDNS Data Dir:** $(json_escape "$PDNS_DATA_DIR")
|
||||
**Zone (PDNS):** $(json_escape "${ZONE_NAME:-}")
|
||||
**Cloudflare Mirror Zone:** $(json_escape "${CF_ZONE_NAME:-}")
|
||||
**Skill Version:** 1.0.0
|
||||
|
||||
---
|
||||
|
||||
## Artifacts
|
||||
|
||||
| Item | Path |
|
||||
|---|---|
|
||||
| Compose | \`$SKILL_ROOT/outputs/compose.yml\` |
|
||||
| pdns.conf | \`$SKILL_ROOT/outputs/pdns.conf\` |
|
||||
| API Probe | \`$SKILL_ROOT/outputs/pdns_api_probe.json\` |
|
||||
| Status Matrix | \`$SKILL_ROOT/outputs/status_matrix.json\` |
|
||||
| Backups | \`$SKILL_ROOT/outputs/backups/\` |
|
||||
|
||||
---
|
||||
|
||||
## Status Matrix
|
||||
|
||||
$(if [[ -f "$status" ]]; then
|
||||
echo '```json'
|
||||
cat "$status"
|
||||
echo '```'
|
||||
else
|
||||
echo "_Missing status_matrix.json — run 90_verify.sh first._"
|
||||
fi)
|
||||
|
||||
---
|
||||
|
||||
## EU Compliance Declaration
|
||||
|
||||
| Aspect | Value |
|
||||
|---|---|
|
||||
| Data Residency | EU (Ireland - Dublin) |
|
||||
| Jurisdiction | Irish Law |
|
||||
| Authoritative DNS | PowerDNS on your node |
|
||||
| Mirror | Optional Cloudflare mirror |
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
- PDNS stop/remove: \`./scripts/rollback/undo_pdns.sh\`
|
||||
- Delete zone (optional): \`./scripts/rollback/undo_zone.sh\`
|
||||
- Remove CF records created by this skill: \`./scripts/rollback/undo_cloudflare.sh\`
|
||||
|
||||
EOF
|
||||
|
||||
log_info "Wrote $report"
|
||||
cat "$report"
|
||||
}
|
||||
main "$@"
|
||||
38
dns-sovereign/scripts/_common.sh
Normal file
38
dns-sovereign/scripts/_common.sh
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
log_info(){ echo "[INFO] $(date -Iseconds) $*"; }
|
||||
log_warn(){ echo "[WARN] $(date -Iseconds) $*" >&2; }
|
||||
log_error(){ echo "[ERROR] $(date -Iseconds) $*" >&2; }
|
||||
die(){ log_error "$*"; exit 1; }
|
||||
need(){ command -v "$1" >/dev/null 2>&1 || die "Missing required tool: $1"; }
|
||||
|
||||
json_escape() {
|
||||
local s="$1"
|
||||
s="${s//\\/\\\\}"
|
||||
s="${s//\"/\\\"}"
|
||||
s="${s//$'\n'/\\n}"
|
||||
s="${s//$'\r'/\\r}"
|
||||
s="${s//$'\t'/\\t}"
|
||||
printf "%s" "$s"
|
||||
}
|
||||
|
||||
confirm_gate() {
|
||||
: "${DRY_RUN:=1}"
|
||||
: "${REQUIRE_CONFIRM:=1}"
|
||||
: "${CONFIRM_PHRASE:=I UNDERSTAND THIS CAN CHANGE DNS}"
|
||||
[[ "$DRY_RUN" == "0" ]] || die "DRY_RUN=$DRY_RUN (set DRY_RUN=0)."
|
||||
if [[ "$REQUIRE_CONFIRM" == "1" ]]; then
|
||||
echo "Type to confirm:"
|
||||
echo " $CONFIRM_PHRASE"
|
||||
read -r input
|
||||
[[ "$input" == "$CONFIRM_PHRASE" ]] || die "Confirmation phrase mismatch."
|
||||
fi
|
||||
}
|
||||
|
||||
compose_cmd() {
|
||||
if command -v docker-compose >/dev/null 2>&1; then
|
||||
echo "docker-compose"
|
||||
else
|
||||
echo "docker compose"
|
||||
fi
|
||||
}
|
||||
44
dns-sovereign/scripts/rollback/undo_cloudflare.sh
Normal file
44
dns-sovereign/scripts/rollback/undo_cloudflare.sh
Normal file
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
source "$SKILL_ROOT/scripts/_common.sh"
|
||||
|
||||
: "${CF_API_TOKEN:=}"
|
||||
: "${CF_ZONE_NAME:=}"
|
||||
|
||||
api() {
|
||||
local method="$1"; shift
|
||||
local url="$1"; shift
|
||||
curl -sS -X "$method" "$url" \
|
||||
-H "Authorization: Bearer $CF_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
[[ -n "$CF_API_TOKEN" ]] || die "CF_API_TOKEN required."
|
||||
[[ -n "$CF_ZONE_NAME" ]] || die "CF_ZONE_NAME required."
|
||||
need jq
|
||||
need curl
|
||||
|
||||
local ids_file="$SKILL_ROOT/outputs/cloudflare_record_ids.txt"
|
||||
if [[ ! -f "$ids_file" ]]; then
|
||||
log_warn "No cloudflare_record_ids.txt found; nothing to undo."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local zid; zid="$(api GET "https://api.cloudflare.com/client/v4/zones?name=$CF_ZONE_NAME" | jq -r '.result[0].id')"
|
||||
[[ -n "$zid" && "$zid" != "null" ]] || die "Unable to resolve zone id."
|
||||
|
||||
while IFS= read -r rid; do
|
||||
[[ -n "$rid" ]] || continue
|
||||
log_warn "Deleting Cloudflare DNS record id: $rid"
|
||||
api DELETE "https://api.cloudflare.com/client/v4/zones/$zid/dns_records/$rid" | jq -e '.success==true' >/dev/null || log_warn "Failed delete for $rid"
|
||||
done < "$ids_file"
|
||||
|
||||
rm -f "$ids_file" || true
|
||||
log_info "Cloudflare rollback complete."
|
||||
}
|
||||
main "$@"
|
||||
17
dns-sovereign/scripts/rollback/undo_pdns.sh
Normal file
17
dns-sovereign/scripts/rollback/undo_pdns.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
source "$SKILL_ROOT/scripts/_common.sh"
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
if docker ps -a --format '{{.Names}}' | grep -q '^pdns-auth$'; then
|
||||
log_warn "Stopping/removing pdns-auth container..."
|
||||
docker rm -f pdns-auth || true
|
||||
else
|
||||
log_warn "pdns-auth container not found."
|
||||
fi
|
||||
log_info "PDNS rollback complete. Data preserved in PDNS_DATA_DIR."
|
||||
}
|
||||
main "$@"
|
||||
28
dns-sovereign/scripts/rollback/undo_zone.sh
Normal file
28
dns-sovereign/scripts/rollback/undo_zone.sh
Normal file
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
SKILL_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
source "$SKILL_ROOT/scripts/_common.sh"
|
||||
|
||||
: "${PDNS_API_KEY:=}"
|
||||
: "${PDNS_WEB_PORT:=8081}"
|
||||
: "${ZONE_NAME:=}"
|
||||
|
||||
api() {
|
||||
local method="$1"; shift
|
||||
local url="$1"; shift
|
||||
curl -sS -X "$method" "$url" -H "X-API-Key: $PDNS_API_KEY" -H "Content-Type: application/json" "$@"
|
||||
}
|
||||
|
||||
main() {
|
||||
confirm_gate
|
||||
[[ -n "$PDNS_API_KEY" ]] || die "PDNS_API_KEY required."
|
||||
[[ -n "$ZONE_NAME" ]] || die "ZONE_NAME required."
|
||||
|
||||
local zone="${ZONE_NAME%\.}."
|
||||
local base="http://127.0.0.1:${PDNS_WEB_PORT}/api/v1/servers/localhost"
|
||||
log_warn "Deleting zone: $zone"
|
||||
api DELETE "$base/zones/$zone" | jq '.' || die "Failed to delete zone."
|
||||
log_info "Zone rollback complete."
|
||||
}
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user