Guard recovery bootstrap from production host
CI / tests-and-images (pull_request) Has been cancelled

This commit is contained in:
agent committed 2026-10-11 19:16:54 +03:00
1 parent 97803ca5de
commit 16f97d5f73
7 files changed
+60 -2

No files matched your search

+2
View File
@@ -19,6 +19,8 @@ jobs:
run: python3 -m unittest discover -s tools/telegram-bot -p 'test_*.py' -v
- name: Run infrastructure backup tests
run: python3 -m unittest discover -s infra/backup -p 'test_*.py' -v
- name: Check recovery host guard
run: bash infra/bootstrap/test-host-guard.sh
- name: Validate production Compose and Caddyfile
run: |
secret_file=$(mktemp)
+3
View File
@@ -47,6 +47,9 @@ from planned configuration.
- The repository has a cold-backup archive/verification tool with staging-only
restore and unit tests. It has not run against production; do not treat this
as an active backup or verified recovery.
- Recovery bootstrap has an explicit `--recovery-target` mode that refuses the
inventoried production hostname/IP; its host-identity guard has shell tests.
The mode has not been run on a VPS.
- An isolated local Compose smoke test built the backend/OpenSearch images,
reached healthy status for OpenSearch, backend, and Caddy, and returned HTTP
200 for `/review` through Caddy. Its containers, networks, and volumes were
+3 -1
View File
@@ -15,7 +15,9 @@ configuration, and a backup and restore have been tested.
`sudo bash infra/bootstrap/bootstrap-ubuntu-24.04.sh` from a verified copy
of this branch on the VPS. The script installs Docker Engine/Compose and Git,
sets Docker log rotation, and creates root-owned paths. It refuses other OS
versions and does not change SSH, UFW, sudoers, or user groups.
versions and does not change SSH, UFW, sudoers, or user groups. Use
`--recovery-target` only on a replacement host; it refuses the known
production host identity.
2. Install the approved repository checkout at `/opt/akyldash/current` and
configure `/etc/akyldash/production.env` plus
`/etc/akyldash/review_secret` outside Git. The secret file must be
+3 -1
View File
@@ -16,7 +16,9 @@ run recovery commands against the existing production by accident.
Preserve the host's existing root SSH and firewall policy until an owner has
reviewed the change.
3. Check out the approved Git revision and run
`sudo bash infra/bootstrap/bootstrap-ubuntu-24.04.sh`. It installs Docker
`sudo bash infra/bootstrap/bootstrap-ubuntu-24.04.sh --recovery-target`. The
recovery mode refuses the inventoried production hostname/IP and writes a
root-only marker at `/etc/akyldash/recovery-target`. It installs Docker
Engine, Compose, Git, Docker log limits, and target directories.
4. Configure DNS to point through Cloudflare to the recovery host and allow the
owner to review/open TCP `80` and `443`. The production Compose Caddy service
+26
View File
@@ -1,6 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -gt 1 || ( $# -eq 1 && $1 != --recovery-target ) ]]; then
echo "Usage: $0 [--recovery-target]" >&2
exit 2
fi
recovery_target=false
if [[ ${1:-} == --recovery-target ]]; then
recovery_target=true
. "$(dirname "${BASH_SOURCE[0]}")/host-guard.sh"
recovery_hostname=$(hostname -f 2>/dev/null || true)
recovery_ipv4=$(ip -o -4 addr show 2>/dev/null | awk '{split($4, address, "/"); print address[1]}')
if [[ -z $recovery_hostname || -z $recovery_ipv4 ]]; then
echo "Cannot verify recovery host identity; refusing recovery bootstrap." >&2
exit 1
fi
if akyldash_is_production_host "$recovery_hostname" "$recovery_ipv4"; then
echo "Refusing recovery bootstrap on the current production host." >&2
exit 1
fi
fi
if [[ ${EUID} -ne 0 ]]; then
echo "Run this script as root." >&2
exit 1
@@ -47,6 +67,12 @@ install -d -o root -g root -m 0755 /opt/akyldash
install -d -o root -g root -m 0700 /etc/akyldash
install -d -o root -g root -m 0750 /var/lib/akyldash/data
install -d -o root -g root -m 0700 /var/backups/akyldash
if [[ $recovery_target == true ]]; then
install -o root -g root -m 0600 /dev/null /etc/akyldash/recovery-target
fi
systemctl enable --now docker
docker compose version
echo "Bootstrap complete. codex was not added to sudo or the docker group; SSH and firewall settings were not changed."
if [[ $recovery_target == true ]]; then
echo 'Recovery target marker created at /etc/akyldash/recovery-target.'
fi
+11
View File
@@ -0,0 +1,11 @@
akyldash_is_production_host() {
local hostname=${1:-}
local ipv4_addresses=${2:-}
if [[ -z $hostname ]]; then
hostname=$(hostname -f 2>/dev/null || true)
fi
if [[ -z $ipv4_addresses ]]; then
ipv4_addresses=$(ip -o -4 addr show 2>/dev/null | awk '{split($4, address, "/"); print address[1]}')
fi
[[ $hostname == avprogs.hlab.kz ]] || grep -Fqx '206.223.249.118' <<<"$ipv4_addresses"
}
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail
source "$(dirname "${BASH_SOURCE[0]}")/host-guard.sh"
akyldash_is_production_host avprogs.hlab.kz 127.0.0.1
akyldash_is_production_host other.example $'10.0.0.2\n206.223.249.118'
if akyldash_is_production_host recovery.example 10.0.0.2; then
echo 'FAIL unrelated recovery host identified as production' >&2
exit 1
fi
echo 'PASS recovery host identity guard'