bastille-jail-provisioning: add FreeBSD-adapted script safety patterns
Merge in a checklist-style "provisioning script safety" section, adapted from wshobson/agents' bash-defensive-patterns skill (fetched directly from GitHub, MIT licensed) rather than trusted from its catalog summary. Covers: shell-choice caution (#!/bin/bash isn't guaranteed on FreeBSD; bash is a package at /usr/local/bin/bash, not base), a contextual error-handling philosophy instead of a blanket `set -Eeuo pipefail`, trap-based cleanup/logging for scripts interrupted mid-jail-creation (without auto-rollback, which can worsen the known destroy/create IP churn issue), jail-name/IP input validation before destructive `bastille` commands, and FreeBSD `mktemp`/`sed -i`/`date` syntax differences from GNU. Deliberately dropped the upstream's interactive `rm -rI` cleanup pattern (wrong for unattended SSH automation) and did not import retry/locking/ShellCheck-gate advice that wasn't actually present in the real upstream files. Cross-references remote-shell-quoting-safety instead of duplicating its nested-shell-quoting content. Also documents the /usr/local/bastille vs /www/bastille bastille_prefix split already present across this fleet's hosts (gringo/staging vs granja), which the new script-safety guidance assumes readers already know not to hardcode. Full worked patterns and code examples live in the new references/script-safety.md; SKILL.md keeps the load-bearing summary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -265,3 +265,63 @@ exist under that name in the FreeBSD ports tree) — and remember Valkey's
|
||||
ACL means any code connecting to it needs an explicit `AUTH` step, not just
|
||||
`connect()`; a `connect()`-only implementation will silently fail every
|
||||
subsequent command with `NOAUTH` against this fleet's standard Valkey setup.
|
||||
|
||||
## Bastille filesystem prefix varies by host — check before hardcoding a path
|
||||
|
||||
Don't assume `/usr/local/bastille/jails/<name>/root/...` — that's only
|
||||
the convention where `bastille.conf` uses the default `bastille_prefix`
|
||||
(confirmed on gringo and staging). A host with a custom
|
||||
`bastille_prefix` (e.g. `granja`, where storage was repointed at a
|
||||
dedicated `wwwpool` with `bastille_prefix=/www/bastille`) puts jails
|
||||
under `/www/bastille/jails/<name>/root/...` instead — writing to the
|
||||
`/usr/local/bastille/...` path on that host silently succeeds (creates a
|
||||
new, wrong directory) rather than erroring, since nothing stops you from
|
||||
writing to an arbitrary path. **Check the host's actual
|
||||
`bastille_prefix`** (`grep bastille_prefix /etc/rc.conf` or
|
||||
`bastille.conf`) before hardcoding either path in a script.
|
||||
|
||||
## Provisioning script safety
|
||||
|
||||
Applies to any shell script written to automate part of this
|
||||
provisioning flow (not the one-off interactive commands above). Full
|
||||
worked patterns, including FreeBSD-specific `mktemp`/`sed`/`date` syntax,
|
||||
a trap-based cleanup template, and jail-name/IP input validation, live in
|
||||
`references/script-safety.md` — read that before writing a new
|
||||
provisioning script. Summary of the load-bearing points:
|
||||
|
||||
- **Don't assume `#!/bin/bash` exists.** FreeBSD base ships `/bin/sh`
|
||||
(POSIX-ish ash), not bash — bash is a package that installs to
|
||||
`/usr/local/bin/bash`, never `/bin/bash`, and many freshly cloned jails
|
||||
won't have it installed at all. Default to `#!/bin/sh` and POSIX
|
||||
constructs unless bash-only features are genuinely needed and you've
|
||||
confirmed bash is present on the target.
|
||||
- **Error handling is contextual, not a blanket `set -Eeuo pipefail`.**
|
||||
Turn on `set -eu` for the whole script, but explicitly tolerate steps
|
||||
that are allowed to fail (e.g. an already-installed package, an
|
||||
optional service restart) with a commented `|| true` rather than either
|
||||
aborting the whole provisioning run on a non-fatal hiccup or suppressing
|
||||
errors wholesale. `-o pipefail`/`-E` are bash-only and unavailable under
|
||||
`/bin/sh`.
|
||||
- **Trap cleanup, don't trap auto-rollback.** A script that dies partway
|
||||
through jail creation should log exactly which step it reached (jail
|
||||
created? DB granted? vhost written?) so a human can clean up — see the
|
||||
create/destroy IP-alias churn gotcha above for why an automatic
|
||||
`bastille destroy` inside a failure-path trap can make things worse, not
|
||||
better.
|
||||
- **Validate jail names and IPs before they reach a destructive `bastille`
|
||||
command.** `bastille destroy <name>` has no confirmation prompt.
|
||||
Validate with plain POSIX `case`/`[` pattern matching (works under both
|
||||
`/bin/sh` and bash) before any value — especially one derived from
|
||||
outside the script — is interpolated into a command.
|
||||
|
||||
For quoting content through nested shell layers (`ssh` → `bastille cmd`
|
||||
→ `su`), use the separate `remote-shell-quoting-safety` skill — that
|
||||
problem is not duplicated here.
|
||||
|
||||
Provenance: the script-safety patterns above are adapted from
|
||||
wshobson/agents' `bash-defensive-patterns` skill
|
||||
(https://github.com/wshobson/agents,
|
||||
`plugins/shell-scripting/skills/bash-defensive-patterns`, MIT licensed,
|
||||
fetched 2026-08-15) — rewritten for FreeBSD/Bastille where upstream
|
||||
assumed GNU/Linux; see `references/script-safety.md` for exactly what was
|
||||
kept, rewritten, or dropped.
|
||||
|
||||
Reference in New Issue
Block a user