aboutsummaryrefslogtreecommitdiff
path: root/straper/doctor.sh
diff options
context:
space:
mode:
Diffstat (limited to 'straper/doctor.sh')
-rwxr-xr-xstraper/doctor.sh222
1 files changed, 0 insertions, 222 deletions
diff --git a/straper/doctor.sh b/straper/doctor.sh
deleted file mode 100755
index 6aed53e..0000000
--- a/straper/doctor.sh
+++ /dev/null
@@ -1,222 +0,0 @@
-#!/usr/bin/env bash
-set -Eeuo pipefail
-
-SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
-# shellcheck disable=SC1091
-source "${SCRIPT_DIR}/lib/common.sh"
-
-SCRIPT_NAME="doctor.sh"
-STRICT=false
-OUTFILE=""
-
-usage() {
- cat <<USAGE
-Usage: sudo ./doctor.sh [options]
-
-Purpose:
- Read-only health and readiness checks for a rebuilt sanctum/labunix host.
-
-Options:
- --db-dir PATH
- --role lab|hardware|replacement
- --strict Exit non-zero if any FAIL is found
- --output PATH Write human report to this path
- --dry-run Still read-only; only affects report/state writes
- --help
-USAGE
-}
-
-while [[ $# -gt 0 ]]; do
- case "$1" in
- --db-dir) DB_DIR="$2"; PUBLIC_DIR="${DB_DIR}/db/public"; SECRET_DIR="${DB_DIR}/db/secret"; shift ;;
- --role) ROLE="$2"; shift ;;
- --strict) STRICT=true ;;
- --output) OUTFILE="$2"; shift ;;
- --dry-run) DRY_RUN=true ;;
- --help) usage; exit 0 ;;
- *) die "unknown option: $1" ;;
- esac
- shift
-done
-
-ensure_root
-load_optional_config
-ensure_runtime_dirs
-require_cmd ip systemctl ss awk sed grep
-
-OUTFILE="${OUTFILE:-${LOG_DIR}/doctor-${RUN_ID}.txt}"
-: > "${OUTFILE}"
-
-FAILS=0
-WARNS=0
-OKS=0
-MANUALS=0
-
-emit() {
- printf '%s\n' "$*" | tee -a "${OUTFILE}"
-}
-
-ok_line() {
- ((OKS+=1))
- emit "OK | $1"
- report "${SCRIPT_NAME}" doctor "$1" check ok "$1" ""
-}
-warn_line() {
- ((WARNS+=1))
- emit "WARN | $1"
- report "${SCRIPT_NAME}" doctor "$1" check warn "$1" ""
-}
-fail_line() {
- ((FAILS+=1))
- emit "FAIL | $1"
- report "${SCRIPT_NAME}" doctor "$1" check failed "$1" ""
-}
-manual_line() {
- ((MANUALS+=1))
- emit "MANUAL| $1"
- report "${SCRIPT_NAME}" doctor "$1" manual manual "$1" ""
-}
-
-service_report() {
- local svc="$1"
- local unit="$svc"
- local load active enabled
-
- [[ "${unit}" == *.* ]] || unit="${unit}.service"
-
- load="$(systemctl show -P LoadState "${unit}" 2>/dev/null || true)"
- if [[ -z "${load}" || "${load}" == "not-found" ]]; then
- warn_line "service ${unit} not installed"
- return 0
- fi
-
- active="$(systemctl show -P ActiveState "${unit}" 2>/dev/null || true)"
- enabled="$(systemctl show -P UnitFileState "${unit}" 2>/dev/null || true)"
-
- if [[ "${active}" == "active" ]]; then
- ok_line "service ${unit} active (${enabled})"
- else
- warn_line "service ${unit} ${active:-unknown} (${enabled:-unknown})"
- fi
-}
-
-emit "labunix/sanctum rebuild doctor"
-emit "timestamp: $(now_iso)"
-emit "role: ${ROLE}"
-emit "db: ${DB_DIR}"
-emit ""
-
-virt="$(detect_virtualization)"
-if [[ -n ${virt} ]]; then
- warn_line "virtualization detected: ${virt}"
-else
- ok_line "bare metal or virtualization not detected"
-fi
-
-current_host="$(hostname -s 2>/dev/null || true)"
-captured_host="$(head -n1 "${PUBLIC_DIR}/system/etc-hostname" 2>/dev/null || true)"
-if [[ -n ${captured_host} && ${current_host} == ${captured_host} ]]; then
- ok_line "hostname matches DB (${current_host})"
-elif [[ -n ${captured_host} ]]; then
- warn_line "hostname differs: current=${current_host} captured=${captured_host}"
-else
- warn_line "captured hostname not found in DB"
-fi
-
-if has_default_route; then
- ok_line "default route present"
-else
- fail_line "no default route"
-fi
-
-iface="$(primary_iface)"
-src_ip="$(primary_src_ip)"
-[[ -n ${iface} ]] && ok_line "primary route uses iface=${iface} src=${src_ip}" || fail_line "could not determine primary interface via ip route get 1.1.1.1"
-
-if can_reach_ip 1.1.1.1; then
- ok_line "outbound IP reachability to 1.1.1.1"
-else
- fail_line "cannot reach 1.1.1.1"
-fi
-
-if can_resolve_name deb.debian.org; then
- ok_line "DNS resolves deb.debian.org"
-else
- fail_line "DNS resolution failed for deb.debian.org"
-fi
-
-owner="$(guess_dns_owner)"
-case "${owner}" in
- resolved) ok_line "DNS owner appears to be systemd-resolved" ;;
- dnsmasq) ok_line "DNS owner appears to be dnsmasq" ;;
- unbound) ok_line "DNS owner appears to be unbound" ;;
- chain) warn_line "DNS owner appears chained (dnsmasq + unbound); verify port ownership intentionally" ;;
- *) warn_line "DNS owner unclear" ;;
-esac
-
-failed_units="$(systemctl list-units --state=failed --no-legend --plain 2>/dev/null | awk '{print $1}')"
-if [[ -z ${failed_units} ]]; then
- ok_line "no failed systemd units"
-else
- fail_line "failed units present: $(echo "${failed_units}" | paste -sd, -)"
-fi
-
-# Config validators
-have_cmd sshd && validate_sshd && ok_line "sshd config validates" || warn_line "sshd config validation failed or sshd missing"
-have_cmd nginx && validate_nginx && ok_line "nginx config validates" || warn_line "nginx config validation failed or nginx missing"
-have_cmd nft && [[ -f /etc/nftables.conf ]] && validate_nft && ok_line "nftables config validates" || warn_line "nftables config validation failed or /etc/nftables.conf missing"
-have_cmd unbound-checkconf && validate_unbound && ok_line "unbound config validates" || warn_line "unbound config validation failed or unbound missing"
-have_cmd dnsmasq && validate_dnsmasq && ok_line "dnsmasq config validates" || warn_line "dnsmasq config validation failed or dnsmasq missing"
-have_cmd postfix && validate_postfix && ok_line "postfix config validates" || warn_line "postfix config validation failed or postfix missing"
-
-emit ""
-emit "Service snapshot"
-service_report ssh
-service_report systemd-resolved
-service_report dnsmasq
-service_report unbound
-service_report nftables
-service_report nginx
-service_report mariadb
-service_report postfix
-service_report prosody
-service_report docker
-service_report prometheus
-service_report grafana-server
-service_report tor
-service_report i2pd
-
-emit ""
-emit "Listening ports snapshot"
-ss -tlnp 2>/dev/null | tee -a "${OUTFILE}" >/dev/null || true
-
-if have_cmd docker; then
- emit ""
- emit "Docker snapshot"
- docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null | tee -a "${OUTFILE}" >/dev/null || warn_line "docker present but docker ps failed"
-fi
-
-if [[ -d ${PUBLIC_DIR}/docker/compose-redacted || -d ${SECRET_DIR}/docker/compose-full ]]; then
- local_count="$(find /srv /opt /home /root -maxdepth 4 \( -name 'docker-compose.yml' -o -name 'docker-compose.yaml' -o -name 'compose.yml' -o -name 'compose.yaml' \) 2>/dev/null | wc -l | tr -d ' ')"
- ok_line "compose files found locally: ${local_count}"
-fi
-
-if [[ -f /etc/resolv.conf ]]; then
- emit ""
- emit "/etc/resolv.conf"
- sed -n '1,20p' /etc/resolv.conf | tee -a "${OUTFILE}" >/dev/null || true
-fi
-
-if [[ -f ${STATE_DIR}/state-${RUN_ID}.env ]]; then
- manual_line "current run state file: ${STATE_DIR}/state-${RUN_ID}.env"
-fi
-
-emit ""
-emit "Summary: ok=${OKS} warn=${WARNS} fail=${FAILS} manual=${MANUALS}"
-
-set_state doctor_done true
-report "${SCRIPT_NAME}" run finish exit ok "ok=${OKS}; warn=${WARNS}; fail=${FAILS}; manual=${MANUALS}" ""
-
-if [[ ${STRICT} == true && ${FAILS} -gt 0 ]]; then
- exit 1
-fi