Guard recovery bootstrap from production host
CI / tests-and-images (pull_request) Has been cancelled
CI / tests-and-images (pull_request) Has been cancelled
This commit is contained in:
1 parent
97803ca5de
commit
16f97d5f73
7 files changed
+60
-2
No files matched your search
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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'
|
||||
Reference in new issue
Block a user