dejima-devbox-hardener made a devbox report what changed. Everything it
reports goes to a log nobody reads: the node's firewall log, and the audit's
own output in a journal. A signal you never see is not detection, and calling
it one would be the same mistake as reporting a timeout as a pass.
This is the part that reads those reports and wakes somebody up.
ITS OWN REPOSITORY because it runs on neither of that project's two machines.
Shipping it inside the hardener would have put a program that must not run on
the devbox in the repository whose whole subject is the devbox.
THE INVERSION IS THE DESIGN. The guard reaches in; the devbox never reaches
out. That falls out of the firewall the devbox already has - inbound SSH from
the admin subnet is permitted, so no new rule is needed - and it buys three
things:
- the baseline lives here, so root on the devbox can lie about the current
state but cannot alter what this machine recorded last week;
- the alert leaves from here, so a compromised devbox can neither suppress
it by going quiet nor drown it by shouting;
- silence becomes a signal. A reporter running on the devbox and switched
off by whoever took it causes nothing to happen, forever. A collection
that stops arriving is itself the alarm, and it is the only mechanism
here a compromised machine cannot defeat by doing nothing.
Not a service: no port, no daemon, nothing inbound. A timer runs it, it makes
a few outbound connections, writes a file and exits - which is why it needs
no machine of its own, and why "another thing to harden" is not the trade.
NOTHING THE DEVBOX CONTROLS REACHES THE PHONE. Alert bodies are composed here
from a fixed vocabulary and counts. A compromised devbox controls every
filename and unit name, and interpolating one into a push hands it a channel
to the operator's phone - the thing dejima's own notification guarantee
exists to prevent, defeated one layer out. Detail stays in the local report.
The documented path is ntfy for alerts and an Uptime Kuma push monitor for
the dead-man's switch, both self-hosted. The alert topic is deliberately not
the one dejima publishes to: that URL lives on the devbox, so root there can
drown the topic or forge an all-clear.
Two bugs the tests found rather than review: `printf '%s\n' ""` writes a
newline, so an audit returning nothing passed a -s check and would have been
recorded as a baseline; and die() inside a command substitution ends the
subshell rather than the program, so a missing host produced an empty result
and the cycle carried on as though the box had answered.
`bats test/` 16 passing.
83 lines
3.4 KiB
Bash
83 lines
3.4 KiB
Bash
#!/usr/bin/env bats
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
# Copyright (C) 2026 Brad Anderson
|
|
#
|
|
# Every ```sh block in the documentation parses as shell.
|
|
#
|
|
# The runbooks are the product, and a command in one is not an illustration -
|
|
# it is something an operator pastes into a root shell on a hypervisor while
|
|
# following the page. A block that does not parse is a defect in the
|
|
# deliverable, exactly as a dangling link is.
|
|
#
|
|
# It exists because of a near-miss: restructuring prose around a fenced block
|
|
# turned a `\` line continuation into `\\`, which renders identically, reads
|
|
# as prose in a diff, and breaks for whoever pastes it. Nothing else here looks
|
|
# inside a fence, and nobody counts backslashes at end of line.
|
|
#
|
|
# IT PARSES AND DOES NOT EXECUTE, which is the only safe thing to do with a
|
|
# file full of `sudo iptables`. So it checks syntax, not whether a command is
|
|
# correct or does what the prose around it claims.
|
|
#
|
|
# PLACEHOLDERS ARE SUBSTITUTED FIRST. `<name>` is this repository's idiom, not
|
|
# shell, and shell reads it as redirection - so whether a block parsed would
|
|
# otherwise depend on how many angle brackets landed on a line.
|
|
# `try <forge-ip> 443` parses by accident as two redirects; `ssh <you>@<vm-ip>`
|
|
# does not, because the trailing `>` has nothing after it. The pattern requires
|
|
# a letter after the `<`, which leaves `3<>/dev/tcp/...` and `<<EOF` intact -
|
|
# both real shell, and both things a naive substitution would corrupt into a
|
|
# passing test.
|
|
|
|
setup() {
|
|
REPO="$(cd "$BATS_TEST_DIRNAME/.." && pwd)"
|
|
}
|
|
|
|
@test "every fenced sh block parses as shell" {
|
|
work="$BATS_TEST_TMPDIR/snippets"
|
|
mkdir -p "$work"
|
|
|
|
# Extracted to files rather than piped, so bash -n reports a line number
|
|
# inside the block and the .loc file turns it back into a place in the docs.
|
|
while IFS= read -r f; do
|
|
safe="$(printf '%s' "$f" | tr '/.' '__')"
|
|
awk -v dir="$work" -v pfx="$safe" -v file="$f" '
|
|
/^```sh$/ {
|
|
inblock = 1; n++
|
|
out = dir "/" pfx "_" n ".sh"
|
|
print file ":" NR > (out ".loc"); close(out ".loc")
|
|
next
|
|
}
|
|
/^```$/ { if (inblock) { close(out); inblock = 0 } ; next }
|
|
inblock { print >> out }
|
|
' "$REPO/$f"
|
|
done < <(cd "$REPO" && git ls-files '*.md')
|
|
|
|
checked=0
|
|
fails=""
|
|
for snippet in "$work"/*.sh; do
|
|
[ -e "$snippet" ] || continue
|
|
checked=$((checked + 1))
|
|
sed -i -E 's/<[a-zA-Z][^<>]*>/PLACEHOLDER/g' "$snippet"
|
|
if ! err="$(bash -n "$snippet" 2>&1)"; then
|
|
fails="$fails\n $(cat "$snippet.loc")\n$(printf '%s' "$err" | sed 's/^/ /')"
|
|
fi
|
|
done
|
|
|
|
# A pass that checked nothing is the failure this suite exists to avoid, and
|
|
# an extractor that silently stops matching is exactly how it would happen.
|
|
[ "$checked" -gt 0 ] || { echo "no sh blocks found - the extractor is broken, not the docs" >&2; false; }
|
|
|
|
if [ -n "$fails" ]; then
|
|
printf 'fenced sh blocks that do not parse:%b\n' "$fails" >&2
|
|
false
|
|
fi
|
|
}
|
|
|
|
@test "the guard fails when a block is broken, rather than passing quietly" {
|
|
# The same shape as links.bats's own self-check. A guard that cannot fail is
|
|
# indistinguishable from one that always passes, and this one runs over
|
|
# files nobody expects to break.
|
|
printf 'x() { echo a \\\\\n && echo b; }\n' > "$BATS_TEST_TMPDIR/broken.sh"
|
|
run bash -n "$BATS_TEST_TMPDIR/broken.sh"
|
|
[ "$status" -ne 0 ]
|
|
}
|