The deployment shipped its payload but not the thing that installs it.54a5c09added README, knot.conf, knsctl, zone.tmpl, secrets.conf.example, the aliases and the MOTD -- but no deploy.sh, no cloud-init.yml, and no entry in automations.sh's DEPLOYMENTS. `git log --all` confirms deploy.sh was never committed and it is not gitignored, yet README.md:96 and :100 tell the operator to run it. So the documented install path did not exist. Alpine only, native, matching the README: Knot binds :53 directly, needs real client addresses for RRL and cookies, and keeps its DNSSEC key store on the host filesystem. The RHEL packaging needs EPEL, which nothing here sets up, so anything that is not Alpine dies with a clear message rather than half-installing somewhere untested. Three decisions worth recording: The include chain is stubbed. knot.conf include:s seven files the `dns` repo owns; Knot treats a missing include as a config error, so a node the pipeline has never delivered to would fail conf-check and never start. deploy.sh writes a placeholder for each one that is ABSENT -- never over a delivered file -- so the node comes up healthy serving no zones until the pipeline lands. TSIG is generated on a primary and required on a secondary. The keys must match byte for byte, so a secondary that generated its own would authenticate nothing; it now refuses to deploy without TSIG_AUTHORITIVE and TSIG_ADMIN. A primary generates both and prints them once. An existing secrets.conf is never rewritten, so a re-run cannot rotate a key out from under a running estate. PRIMARY_ADDR seeds a minimal remotes.conf on a secondary so it can bootstrap by AXFR before the pipeline runs -- written only when remotes.conf was absent, verified by re-running against a delivered file and confirming it is left untouched. Re-runs apply changes rather than freezing at first deploy, per the pattern this repo just adopted elsewhere: knot.conf is re-rendered from .env every run, env-presence is captured before the ":=" defaults, and values passed to a re-run are written back to .env with the awk-based set_env from947c899-- which matters here because a TSIG secret can contain the characters that broke the sed-based one. Two bugs caught while testing this, before it shipped: - the secrets.conf renderer used `++n` as a gsub argument, which awk evaluates on every line, not just matching ones -- both keys would have received the SAME secret, making the read-only admin key identical to the replication key. Increments on a matching line only now. - the MOTD is a pre-drawn box, so substituting values of a different width than their @TOKEN@ shifted the right border on every login. Values are now padded to the token's span, measured over an ASCII-only region so it holds under busybox awk in the C locale; an over-long value overflows rather than being truncated. Verified: knot.conf renders identity/NSID/listen and leaves the control socket alone; all seven stubs are created on a fresh node and skipped on a re-run; the remotes.conf seed fires only for a fresh secondary; .env seeds every runtime key; the MOTD renders with no leftover tokens and an aligned border. Not verified: apk, knotc and the service start, which need an actual Alpine host. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
552 lines
28 KiB
Bash
552 lines
28 KiB
Bash
#!/usr/bin/env bash
|
|
#
|
|
# deploy.sh -- deploy an authoritative Knot DNS node on Alpine, native (no
|
|
# container). Single-purpose box, runs as root.
|
|
#
|
|
# What this does:
|
|
# 1. Installs knot + knot-utils and creates the knot user/directories.
|
|
# 2. Seeds /etc/knot/.env on first run; on a re-run updates only the values
|
|
# passed that run, and re-renders knot.conf from it either way.
|
|
# 3. Renders /etc/knot/knot.conf from the skeleton (@NODE_ID@, @LISTEN@).
|
|
# 4. TSIG: ROLE=primary generates secrets.conf and prints the keys once;
|
|
# ROLE=secondary REQUIRES them to be supplied, because they must byte-match
|
|
# the primary's or no transfer will ever authenticate.
|
|
# 5. Stubs the seven policy files knot.conf includes, so conf-check passes and
|
|
# knotd starts clean on a box the `dns` repo has never delivered to.
|
|
# 6. Installs knsctl, zone.tmpl, the login aliases and the MOTD.
|
|
# 7. Opens 53/tcp + 53/udp on the host firewall.
|
|
# 8. conf-check, then enables and starts knot.
|
|
#
|
|
# What this does NOT do: manage zones or DNS policy. Those come from the `dns`
|
|
# repo's pipeline -- see the README's split table. This script never overwrites
|
|
# a policy file the pipeline owns, nor an existing secrets.conf.
|
|
#
|
|
# Idempotent: re-run to change identity/listen/peers or pull a newer knot.
|
|
#
|
|
# Self-contained: knot.conf, secrets.conf.example, knsctl, zone.tmpl,
|
|
# knotdns-aliases.sh, knot-dns.motd and .env.example are embedded as a base64
|
|
# tar.gz at the bottom. Rebuild with build.sh after editing the loose files.
|
|
#
|
|
# Usage:
|
|
# ROLE=primary NODE_ID=ANYCAST-DNS-1 bash deploy.sh
|
|
# ROLE=secondary NODE_ID=ANYCAST-DNS-3 PRIMARY_ADDR=10.1.24.64 \
|
|
# TSIG_AUTHORITIVE=... TSIG_ADMIN=... SKIP_PROMPTS=1 bash deploy.sh
|
|
|
|
set -euo pipefail
|
|
|
|
: "${CONF_DIR:=/etc/knot}"
|
|
: "${DATA_DIR:=/var/lib/knot}"
|
|
: "${SKIP_PROMPTS:=0}" # non-interactive: require values via env, no prompts
|
|
: "${FORCE:=0}"
|
|
[[ "$SKIP_PROMPTS" == "1" ]] && FORCE=1
|
|
|
|
ENV_FILE="$CONF_DIR/.env"
|
|
|
|
# Which keys arrived in this script's ENVIRONMENT has to be recorded BEFORE the
|
|
# ":=" defaults below, which make an unset variable look like an empty one --
|
|
# and a blank written over a live TSIG secret would break every transfer.
|
|
RUNTIME_KEYS=(NODE_ID ROLE LISTEN PRIMARY_ADDR DATACENTER PEERS)
|
|
KEYS_FROM_ENV=()
|
|
for _k in "${RUNTIME_KEYS[@]}"; do
|
|
if [[ -n "${!_k+x}" ]]; then KEYS_FROM_ENV+=("$_k"); fi
|
|
done
|
|
|
|
: "${NODE_ID:=}"
|
|
: "${ROLE:=}"
|
|
: "${LISTEN:=}"
|
|
: "${PRIMARY_ADDR:=}"
|
|
: "${DATACENTER:=}"
|
|
: "${PEERS:=}"
|
|
: "${TSIG_AUTHORITIVE:=}" # secondary: must match the primary's
|
|
: "${TSIG_ADMIN:=}"
|
|
|
|
LISTEN_DEFAULT='0.0.0.0@53, ::@53'
|
|
|
|
log() { printf '\033[1;32m[+]\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33m[!]\033[0m %s\n' "$*" >&2; }
|
|
die() { printf '\033[1;31m[x]\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
[[ $EUID -eq 0 ]] || die "Run as root."
|
|
|
|
# Alpine only, deliberately: Knot binds :53 directly and keeps its DNSSEC key
|
|
# store on the host filesystem, and this estate's nodes are Alpine. The RHEL
|
|
# packaging needs EPEL, which nothing in this repo sets up, so fail loudly
|
|
# rather than half-install somewhere untested.
|
|
_osid="$( [[ -r /etc/os-release ]] && . /etc/os-release 2>/dev/null && echo "${ID:-}" )"
|
|
[[ "$_osid" == alpine ]] || \
|
|
die "This deployment is Alpine-only (detected: ${_osid:-unknown}). Knot on other distros is not packaged or tested here."
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Extract embedded archive (falls back to the source dir before build.sh runs)
|
|
# ---------------------------------------------------------------------------
|
|
SCRIPT_DIR=$(mktemp -d -t knot-deploy.XXXXXX)
|
|
trap 'rm -rf "$SCRIPT_DIR"' EXIT
|
|
|
|
grep -a -A 9999999 '^__ARCHIVE_BELOW__$' "$0" \
|
|
| tail -n +2 \
|
|
| base64 -d \
|
|
| tar -xz -C "$SCRIPT_DIR" 2>/dev/null || true
|
|
|
|
if [[ ! -f "$SCRIPT_DIR/knot.conf" ]]; then
|
|
SRC=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
for f in knot.conf secrets.conf.example knsctl zone.tmpl knotdns-aliases.sh knot-dns.motd .env.example; do
|
|
[[ -f "$SRC/$f" ]] || die "Missing $f (no embedded payload and not in $SRC -- run build.sh)."
|
|
cp "$SRC/$f" "$SCRIPT_DIR/$f"
|
|
done
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# .env: seed once, then update only what this run passed
|
|
# ---------------------------------------------------------------------------
|
|
set_env() { # <KEY> <value>: update KEY in .env, or append if absent
|
|
# The value goes through the ENVIRONMENT, never interpolated into a sed
|
|
# script: a TSIG secret or a listen list containing & or the s||| delimiter
|
|
# would otherwise be silently corrupted or abort the run.
|
|
local key="$1" val="$2" tmp
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
|
|
return 0
|
|
fi
|
|
tmp="$(mktemp)"
|
|
_SE_KEY="$key" _SE_VAL="$val" awk '
|
|
BEGIN { k = ENVIRON["_SE_KEY"]; v = ENVIRON["_SE_VAL"]; seen = 0 }
|
|
!seen && index($0, k "=") == 1 { print k "=" v; seen = 1; next }
|
|
{ print }
|
|
END { if (!seen) print k "=" v }
|
|
' "$ENV_FILE" > "$tmp"
|
|
cat "$tmp" > "$ENV_FILE" # rewrite in place: keeps the original mode/owner
|
|
rm -f "$tmp"
|
|
}
|
|
|
|
install -d -m 0755 "$CONF_DIR"
|
|
|
|
# On a re-run the deployed values live in .env. Load them into any key NOT
|
|
# passed this run, so the prompts below do not demand a full retype and
|
|
# SKIP_PROMPTS=1 does not die over values the node already has.
|
|
if [[ -f "$ENV_FILE" ]]; then
|
|
_preloaded=()
|
|
for _k in "${RUNTIME_KEYS[@]}"; do
|
|
if [[ " ${KEYS_FROM_ENV[*]-} " == *" ${_k} "* ]]; then continue; fi
|
|
_v=$(sed -n "s/^${_k}=//p" "$ENV_FILE" | tail -n1)
|
|
if [[ -z "$_v" ]]; then continue; fi
|
|
printf -v "$_k" '%s' "$_v"
|
|
_preloaded+=("$_k")
|
|
done
|
|
if (( ${#_preloaded[@]} > 0 )); then
|
|
log "Reusing ${ENV_FILE} values for: ${_preloaded[*]}"
|
|
log " (pass VAR=... to change one; it is then written back to .env)"
|
|
fi
|
|
fi
|
|
|
|
prompt() {
|
|
local varname="$1" message="$2"
|
|
local -n ref="$varname"
|
|
if [[ -z "${ref:-}" ]]; then
|
|
[[ "$SKIP_PROMPTS" == "1" ]] && die "$varname required (set it in the environment; running with SKIP_PROMPTS=1)."
|
|
read -r -p "$message: " ref
|
|
[[ -n "$ref" ]] || die "$varname required."
|
|
fi
|
|
}
|
|
|
|
prompt NODE_ID "Node ID for server.identity / NSID (e.g. ANYCAST-DNS-3)"
|
|
prompt ROLE "Role: primary or secondary"
|
|
case "$ROLE" in
|
|
primary|secondary) ;;
|
|
*) die "ROLE must be 'primary' or 'secondary' (got '$ROLE')." ;;
|
|
esac
|
|
[[ -n "$LISTEN" ]] || LISTEN="$LISTEN_DEFAULT"
|
|
|
|
if [[ "$ROLE" == secondary ]]; then
|
|
prompt PRIMARY_ADDR "Address of the primary this node transfers from"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Packages, user, directories
|
|
# ---------------------------------------------------------------------------
|
|
log "Installing Knot DNS..."
|
|
apk add -q knot knot-utils
|
|
|
|
# The package normally creates knot:knot; make that certain before any chown.
|
|
getent group knot >/dev/null 2>&1 || addgroup -S knot
|
|
getent passwd knot >/dev/null 2>&1 || adduser -S -D -H -G knot -s /sbin/nologin knot
|
|
|
|
# storage/journal/kasp/timer/catalog as declared in knot.conf's database:
|
|
# section, plus the zone root knsctl writes into.
|
|
for d in database journal keys timer catalog zones; do
|
|
install -d -m 0750 -o knot -g knot "$DATA_DIR/$d"
|
|
done
|
|
install -d -m 0750 -o knot -g knot /run/knot
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# knot.conf -- re-rendered every run, since it is derived purely from .env
|
|
# ---------------------------------------------------------------------------
|
|
log "Rendering $CONF_DIR/knot.conf (NODE_ID=$NODE_ID, ROLE=$ROLE)..."
|
|
_KC_NODE_ID="$NODE_ID" _KC_LISTEN="$LISTEN" awk '
|
|
{ gsub(/@NODE_ID@/, ENVIRON["_KC_NODE_ID"]); gsub(/@LISTEN@/, ENVIRON["_KC_LISTEN"]); print }
|
|
' "$SCRIPT_DIR/knot.conf" > "$CONF_DIR/knot.conf.new"
|
|
install -m 0640 -o root -g knot "$CONF_DIR/knot.conf.new" "$CONF_DIR/knot.conf"
|
|
rm -f "$CONF_DIR/knot.conf.new"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TSIG. A secondary's keys must byte-match the primary's, so they can never be
|
|
# generated here -- a generated key would authenticate nothing.
|
|
# ---------------------------------------------------------------------------
|
|
gen_tsig() { # gen_tsig <key-id> -> base64 secret on stdout
|
|
local id="$1" out=""
|
|
if command -v keymgr >/dev/null 2>&1; then
|
|
# keymgr prints a whole key: block; take just the secret.
|
|
out="$(keymgr -t "$id" hmac-sha256 2>/dev/null \
|
|
| awk '/secret:/ { sub(/^[[:space:]]*secret:[[:space:]]*/, ""); print; exit }' || true)"
|
|
fi
|
|
[[ -n "$out" ]] || out="$(openssl rand -base64 32)"
|
|
printf '%s' "$out"
|
|
}
|
|
|
|
SECRETS="$CONF_DIR/secrets.conf"
|
|
TSIG_PRINTED=0
|
|
if [[ -f "$SECRETS" ]]; then
|
|
log "$SECRETS exists; leaving it alone (TSIG keys are never rotated by a re-run)."
|
|
elif [[ "$ROLE" == primary ]]; then
|
|
log "Generating TSIG keys for $SECRETS..."
|
|
[[ -n "$TSIG_AUTHORITIVE" ]] || TSIG_AUTHORITIVE="$(gen_tsig authortive-tsig)"
|
|
[[ -n "$TSIG_ADMIN" ]] || TSIG_ADMIN="$(gen_tsig admin-tsig)"
|
|
TSIG_PRINTED=1
|
|
else
|
|
[[ -n "$TSIG_AUTHORITIVE" && -n "$TSIG_ADMIN" ]] || die \
|
|
"A secondary's TSIG keys must match the primary's byte for byte, so they cannot be generated here.
|
|
Copy them off the primary ($CONF_DIR/secrets.conf) and re-run with:
|
|
TSIG_AUTHORITIVE=<secret> TSIG_ADMIN=<secret> bash deploy.sh"
|
|
fi
|
|
|
|
if [[ ! -f "$SECRETS" ]]; then
|
|
# Increment only on a MATCHING line: as a gsub argument, `++n` would be
|
|
# evaluated on every line of the file, so both keys would get the second
|
|
# secret -- making the read-only admin key identical to the replication key.
|
|
_TS_AUTH="$TSIG_AUTHORITIVE" _TS_ADMIN="$TSIG_ADMIN" awk '
|
|
/REPLACE_ME/ { n++; sub(/REPLACE_ME/, n == 1 ? ENVIRON["_TS_AUTH"] : ENVIRON["_TS_ADMIN"]) }
|
|
{ print }
|
|
' "$SCRIPT_DIR/secrets.conf.example" > "$SECRETS.new"
|
|
install -m 0640 -o root -g knot "$SECRETS.new" "$SECRETS"
|
|
rm -f "$SECRETS.new"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# The seven policy files knot.conf includes. The `dns` repo owns them; a fresh
|
|
# node has none, and Knot treats a missing include as a config error -- so
|
|
# conf-check would fail and knotd would never start. Stub only what is absent;
|
|
# never touch a file the pipeline has already delivered.
|
|
# ---------------------------------------------------------------------------
|
|
_stubbed=()
|
|
for f in remotes policy modules templates arpa dnssec public; do
|
|
[[ -f "$CONF_DIR/$f.conf" ]] && continue
|
|
cat > "$CONF_DIR/$f.conf" <<EOF
|
|
# $f.conf -- PLACEHOLDER written by deploy.sh.
|
|
#
|
|
# This file is owned by the \`dns\` repo and is overwritten by its pipeline.
|
|
# It exists only so knot.conf's include: chain resolves and knotd can start on
|
|
# a node the pipeline has not delivered to yet. Do not edit it here.
|
|
EOF
|
|
chown root:knot "$CONF_DIR/$f.conf"; chmod 0640 "$CONF_DIR/$f.conf"
|
|
_stubbed+=("$f.conf")
|
|
done
|
|
(( ${#_stubbed[@]} == 0 )) || log "Stubbed policy files (the dns pipeline overwrites these): ${_stubbed[*]}"
|
|
|
|
# A secondary otherwise cannot transfer anything until the pipeline first runs.
|
|
# Seed remotes.conf from PRIMARY_ADDR so the node can bootstrap by AXFR -- but
|
|
# only when we just stubbed it, so a delivered remotes.conf is never clobbered.
|
|
if [[ "$ROLE" == secondary && -n "$PRIMARY_ADDR" ]] \
|
|
&& printf '%s\n' "${_stubbed[@]-}" | grep -qx 'remotes.conf'; then
|
|
log "Seeding $CONF_DIR/remotes.conf with the primary ($PRIMARY_ADDR)..."
|
|
cat > "$CONF_DIR/remotes.conf" <<EOF
|
|
# remotes.conf -- BOOTSTRAP seed written by deploy.sh from PRIMARY_ADDR.
|
|
#
|
|
# Enough for this node to AXFR from the primary before the \`dns\` repo's
|
|
# pipeline has ever delivered here. The pipeline owns this file and will
|
|
# overwrite it; do not add remotes by hand.
|
|
|
|
remote:
|
|
- id: primary
|
|
address: ${PRIMARY_ADDR}
|
|
key: authortive-tsig
|
|
|
|
acl:
|
|
- id: acl_primary
|
|
address: ${PRIMARY_ADDR}
|
|
key: authortive-tsig
|
|
action: [ transfer, notify ]
|
|
EOF
|
|
chown root:knot "$CONF_DIR/remotes.conf"; chmod 0640 "$CONF_DIR/remotes.conf"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Operator tooling
|
|
# ---------------------------------------------------------------------------
|
|
install -m 0755 "$SCRIPT_DIR/knsctl" /usr/local/sbin/knsctl
|
|
install -m 0644 -o root -g knot "$SCRIPT_DIR/zone.tmpl" "$CONF_DIR/zone.tmpl"
|
|
install -m 0644 "$SCRIPT_DIR/knotdns-aliases.sh" /etc/profile.d/knotdns-aliases.sh
|
|
|
|
# PEERS drives `knsctl serials` / the knsers alias.
|
|
if [[ -n "$PEERS" ]]; then
|
|
printf 'export PEERS="%s"\n' "$PEERS" > /etc/profile.d/knotdns-peers.sh
|
|
chmod 0644 /etc/profile.d/knotdns-peers.sh
|
|
fi
|
|
|
|
_FQDN="$(hostname -f 2>/dev/null || hostname 2>/dev/null || echo "$NODE_ID")"
|
|
# The MOTD is a pre-drawn box, so a value wider or narrower than its @TOKEN@
|
|
# would shift the right-hand border on every login. Replace "@TOKEN@ + the run
|
|
# of spaces after it" with the value padded to that same span, which keeps the
|
|
# border put. That span is pure ASCII, so byte length == display width and this
|
|
# stays correct under busybox awk in the C locale (the box-drawing characters
|
|
# themselves are never measured). A value longer than its span is left to
|
|
# overflow rather than truncated: a clipped hostname in an ops banner is worse
|
|
# than a ragged edge.
|
|
_MD_NODE_ID="$NODE_ID" _MD_ROLE="$ROLE" _MD_DC="${DATACENTER:-unset}" _MD_FQDN="$_FQDN" awk '
|
|
function fit(line, tok, val, n, span, pad) {
|
|
while (match(line, tok " *")) {
|
|
span = RLENGTH
|
|
pad = val
|
|
while (length(pad) < span) pad = pad " "
|
|
line = substr(line, 1, RSTART - 1) pad substr(line, RSTART + RLENGTH)
|
|
}
|
|
return line
|
|
}
|
|
{
|
|
$0 = fit($0, "@NODE_ID@", ENVIRON["_MD_NODE_ID"])
|
|
$0 = fit($0, "@ROLE@", ENVIRON["_MD_ROLE"])
|
|
$0 = fit($0, "@DATACENTER@", ENVIRON["_MD_DC"])
|
|
$0 = fit($0, "@FQDN@", ENVIRON["_MD_FQDN"])
|
|
print
|
|
}
|
|
' "$SCRIPT_DIR/knot-dns.motd" > /etc/motd
|
|
chmod 0644 /etc/motd
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# .env: seed on first run, else update only what was passed this run
|
|
# ---------------------------------------------------------------------------
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
log "Seeding $ENV_FILE..."
|
|
install -m 0640 -o root -g knot "$SCRIPT_DIR/.env.example" "$ENV_FILE"
|
|
for _k in "${RUNTIME_KEYS[@]}"; do set_env "$_k" "${!_k}"; done
|
|
else
|
|
# knot.conf is re-rendered from .env above, so letting .env drift from what
|
|
# was passed would silently redeploy the old identity on the next run.
|
|
log ".env exists; keeping it (only values passed this run are updated)."
|
|
for _k in ${KEYS_FROM_ENV[@]+"${KEYS_FROM_ENV[@]}"}; do
|
|
_cur=$(sed -n "s/^${_k}=//p" "$ENV_FILE" | tail -n1)
|
|
if [[ "$_cur" == "${!_k}" ]]; then continue; fi
|
|
set_env "$_k" "${!_k}"
|
|
log " ${_k}: ${_cur:-<unset>} -> ${!_k:-<empty>}"
|
|
done
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Firewall: authoritative DNS answers on 53, TCP and UDP both.
|
|
# ---------------------------------------------------------------------------
|
|
if [[ -d /etc/firewall/ports.d && -x /usr/local/sbin/firewall-apply ]]; then
|
|
log "Registering 53/tcp + 53/udp with the host firewall..."
|
|
printf '53/tcp\n53/udp\n' > /etc/firewall/ports.d/knot-dns.rule
|
|
/usr/local/sbin/firewall-apply
|
|
else
|
|
warn "No host firewall found (scripts/harden-firewall.sh not installed); 53/tcp+udp not registered."
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Validate, then start. -b so a rejected config fails here, not silently.
|
|
# ---------------------------------------------------------------------------
|
|
log "Validating configuration..."
|
|
knotc -b conf-check || die "knotc conf-check failed -- not starting. Fix $CONF_DIR/knot.conf and re-run."
|
|
|
|
rc-update add knot default >/dev/null 2>&1 || true
|
|
if rc-service knot status >/dev/null 2>&1; then
|
|
log "Reloading knot..."
|
|
knotc -b reload || warn "Reload failed; check 'rc-service knot status' and the syslog."
|
|
else
|
|
log "Starting knot..."
|
|
rc-service knot start || die "knot failed to start -- check the syslog (log target is syslog, zone at info)."
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
================================================================
|
|
DEPLOYED ${NODE_ID} (${ROLE})
|
|
|
|
Config: ${CONF_DIR}/knot.conf (re-rendered from ${ENV_FILE} every run)
|
|
Secrets: ${SECRETS} (0640 root:knot, never in git)
|
|
Data: ${DATA_DIR}
|
|
Admin: knsctl -- 'knsctl list', 'knsctl status'; aliases in a login shell
|
|
|
|
This node serves NO ZONES yet. Zone data and DNS policy come from the \`dns\`
|
|
repo's pipeline; the policy files here are placeholders until it delivers.
|
|
EOF
|
|
|
|
if [[ "$ROLE" == secondary ]]; then
|
|
cat <<EOF
|
|
|
|
Secondary bootstrap: remotes.conf points at ${PRIMARY_ADDR:-<unset>} with
|
|
authortive-tsig. Add this node's address to the PRIMARY's remotes.conf (or let
|
|
the catalog zone do it) before it can transfer.
|
|
EOF
|
|
fi
|
|
|
|
if [[ "$TSIG_PRINTED" == "1" ]]; then
|
|
cat <<EOF
|
|
|
|
>> TSIG KEYS -- SHOWN ONCE. Every secondary needs these byte for byte:
|
|
TSIG_AUTHORITIVE=${TSIG_AUTHORITIVE}
|
|
TSIG_ADMIN=${TSIG_ADMIN}
|
|
Store them out of band. They are in ${SECRETS} on this node.
|
|
EOF
|
|
fi
|
|
|
|
cat <<EOF
|
|
|
|
>> BACK UP THE KASP DATABASE (${DATA_DIR}/keys). It replicates nowhere, and
|
|
losing it makes every signed domain go bogus until each registrar is updated
|
|
by hand:
|
|
knotc -b zone-backup +backupdir /var/backups/knot/\$(date -u +%FT%H) +journal
|
|
EOF
|
|
__ARCHIVE_BELOW__
|
|
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
|
|
ohWtaP8r7Xext/XSAHgAAA==
|