skills: fleet map, default-deny standard, modded-app and vuln-scan skills
- server-fleet-map: add gringo row - bastille-jail-provisioning: elevate default-deny-outbound to the standard for every new jail; reference gringo's live pf.conf - modded-app-update-pattern: new skill, fork/rebase pattern for locally patched apps; uses tailnetatlas on gringo as the concrete example - dependency-vuln-scanning: new skill, npm audit / pip-audit / osv-scanner cadence for deployments and monthly thereafter
This commit is contained in:
@@ -130,6 +130,33 @@ curl -H 'Host: <domain>' http://<jail-ip>/
|
||||
Only switch to testing via the host's `127.0.0.1` (or the public domain)
|
||||
once the `sites-enabled` vhost for this specific site actually exists.
|
||||
|
||||
## Default-deny outbound is the standard for every new jail
|
||||
|
||||
**This is no longer an incident workaround — it is the default posture for
|
||||
any jail created on any Bastille host in this fleet.** Most jails are
|
||||
reverse-proxied inbound-only services and do not need outbound internet
|
||||
access. Denying it by default limits blast radius if a jail is compromised
|
||||
(fewer callback/exfil paths, no unexpected update pings).
|
||||
|
||||
When you finish provisioning a new jail, it must land in the `block out`
|
||||
ruleset, not the `pass out` ruleset. Only add it to the allowed list if its
|
||||
actual function genuinely requires external reach, and document the reason
|
||||
in `pf.conf` next to the IP:
|
||||
|
||||
```
|
||||
# Jails that need real outbound internet access (function confirmed):
|
||||
table <outbound_allowed> { 10.20.0.14, 10.20.0.22, ... }
|
||||
nat on $ext_if from <outbound_allowed> to any -> ($ext_if)
|
||||
pass out quick on $ext_if from <outbound_allowed> to any keep state
|
||||
block out quick on $ext_if from 10.20.0.0/24 to any
|
||||
```
|
||||
|
||||
The live reference implementation is gringo's `/etc/pf.conf` (tracked in
|
||||
`infra/gringo/pf/pf.conf`). Before considering any new jail complete, verify
|
||||
its IP is either in the deny block or explicitly listed with a comment
|
||||
explaining why it needs outbound access. "Might need it later" is not a
|
||||
reason.
|
||||
|
||||
## Every jail needs a dedicated NPM-facing port, not just `listen 80`
|
||||
|
||||
**Confirmed real gap, 2026-08-07**: on jail hosts fronted by a shared
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: dependency-vuln-scanning
|
||||
description: Run dependency vulnerability scans on every new deployment/migration and monthly thereafter. Covers Node, Python, and general OS/library scanning.
|
||||
---
|
||||
|
||||
# Dependency Vulnerability Scanning
|
||||
|
||||
Dependency vulnerabilities are not a one-time checklist item. They should be
|
||||
scanned on every new deployment or migration into the fleet, and then re-scanned
|
||||
periodically thereafter. For this fleet size, **monthly** is a reasonable
|
||||
cadence.
|
||||
|
||||
## When to run
|
||||
|
||||
- Before marking a new jail/container/app as "live" after provisioning or
|
||||
migration.
|
||||
- Before merging a dependency update.
|
||||
- Monthly, against every actively maintained app and host.
|
||||
|
||||
This applies to Docker-host deployments too — a container image is just another
|
||||
artifact with dependencies. Cross-reference the provisioning checklist in
|
||||
`skills/bastille-jail-provisioning/SKILL.md` and add a vulnerability-scan step
|
||||
before considering any new jail complete.
|
||||
|
||||
## Tooling by stack
|
||||
|
||||
### Node.js / npm
|
||||
|
||||
```bash
|
||||
npm audit
|
||||
```
|
||||
|
||||
Use `npm audit --audit-level=moderate` to filter noise if the project is large.
|
||||
For a CI-friendly exit code, `npm audit --audit-level=high` will fail only on
|
||||
high/critical findings. Always review the full output at least once.
|
||||
|
||||
If the project uses `pnpm` or `yarn`, use their equivalents (`pnpm audit`,
|
||||
`yarn audit`). The Node ecosystem is common on this fleet — expect to use this
|
||||
one often.
|
||||
|
||||
### Python
|
||||
|
||||
```bash
|
||||
pip-audit
|
||||
```
|
||||
|
||||
Run inside the project's virtualenv so it sees the same packages the app will
|
||||
actually use. If `pip-audit` is not installed, add it to the dev tooling in the
|
||||
virtualenv rather than the system Python.
|
||||
|
||||
### General / OS / multi-ecosystem fallback
|
||||
|
||||
```bash
|
||||
osv-scanner -r /path/to/project
|
||||
```
|
||||
|
||||
`osv-scanner` is the best general fallback: it understands lockfiles from npm,
|
||||
PyPI, Go, Rust, Maven, and others, and queries the OSV database. Use it when a
|
||||
project mixes ecosystems or when you want a single scanner across a whole host.
|
||||
|
||||
## What to do with findings
|
||||
|
||||
1. **Triage, do not blindly upgrade.** A CVE in a dev-only dependency is not the
|
||||
same as one in the runtime path of a public-facing service.
|
||||
2. **Patch or pin.** Prefer a real upstream upgrade. If the upstream fix is not
|
||||
available yet and the risk is real, consider pinning, removing the dependency,
|
||||
or a temporary local patch (tracked via `skills/modded-app-update-pattern`).
|
||||
3. **Re-scan after the fix.** A green `npm audit` or `pip-audit` output is the
|
||||
only proof the finding is resolved.
|
||||
4. **Record exceptions.** If a finding is accepted (e.g. internal-only tool,
|
||||
no reachable attack path), write the justification down in the deployment
|
||||
notes or the issue tracker, not just in someone's memory.
|
||||
|
||||
## What not to do
|
||||
|
||||
- Do not treat vulnerability scanning as optional for "internal-only" tools.
|
||||
Internal tools often have the same dependencies and the same CVEs.
|
||||
- Do not run a scan and ignore the output because it is long. Parse it, triage
|
||||
it, file issues for anything that needs follow-up.
|
||||
- Do not rely solely on the language-specific scanner when the project also
|
||||
pulls in OS packages or container base-image packages; add `osv-scanner` or
|
||||
the image scanner as a second opinion.
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
name: modded-app-update-pattern
|
||||
description: How to keep a vendor app that has been locally patched update-safe, using a fork or two-branch local repo so upstream updates can be merged/rebased instead of becoming a forgotten, untracked diff.
|
||||
---
|
||||
|
||||
# Modded App Update Pattern
|
||||
|
||||
When an app or library has been custom-patched to work in this fleet, future
|
||||
upstream updates must not silently overwrite those patches, and the patches
|
||||
themselves must not become an undocumented, forgotten diff. The safe pattern is
|
||||
to keep two distinct lines of history:
|
||||
|
||||
- `upstream-vendor` — tracks exactly what was shipped by the vendor, with no
|
||||
local changes.
|
||||
- `main` / `deployed` — the running version, with local patches as clean
|
||||
commits on top of `upstream-vendor`.
|
||||
|
||||
This lets you merge or rebase the next upstream release onto `main` deliberately,
|
||||
reviewing each local patch for conflicts and relevance rather than re-applying a
|
||||
mystery diff by hand.
|
||||
|
||||
## When to use this
|
||||
|
||||
Use this whenever you modify source code, config templates, or package files
|
||||
inside an app that came from upstream (GitHub, npm, PyPI, a vendor tarball).
|
||||
Pure runtime secrets or host-specific `.env` files do not count — keep those
|
||||
outside the repo entirely. But changes like these do:
|
||||
|
||||
- Patching `requirements.txt` because the upstream pins break on FreeBSD.
|
||||
- Adding a local `run.sh` or rc.d script that the vendor does not provide.
|
||||
- Modifying source to fix a FreeBSD path, disable a broken widget, or adapt an
|
||||
API endpoint.
|
||||
|
||||
## Local-only version (no public fork)
|
||||
|
||||
If the repo is already cloned on the deployment host and you do not need a
|
||||
public fork:
|
||||
|
||||
```bash
|
||||
cd /app
|
||||
git checkout -b upstream-vendor <upstream-tag-or-commit>
|
||||
git branch -m main deployed # or keep main
|
||||
git checkout deployed
|
||||
git rebase upstream-vendor # apply local commits on top of clean vendor state
|
||||
```
|
||||
|
||||
After that:
|
||||
|
||||
1. Fetch the new upstream tag into `upstream-vendor` and reset that branch to
|
||||
it: `git checkout upstream-vendor && git reset --hard v2.3.4`.
|
||||
2. `git checkout deployed && git rebase upstream-vendor`.
|
||||
3. Resolve conflicts one patch at a time; each local change stays a separate
|
||||
commit with a clear message.
|
||||
4. Deploy and test.
|
||||
|
||||
## Public-fork version
|
||||
|
||||
If multiple hosts or people will deploy the app, create a fork on the internal
|
||||
Gitea instance:
|
||||
|
||||
1. Fork `vendor/app` to `CloudHost/app-deployed`.
|
||||
2. Add the original repo as an additional remote in your local clone:
|
||||
`git remote add vendor https://github.com/vendor/app.git`.
|
||||
3. Keep `upstream-vendor` tracking the vendor remote and `main`/`deployed`
|
||||
tracking the fork remote.
|
||||
4. Push local patches to the fork.
|
||||
|
||||
This makes the running version auditable and reusable without relying on one
|
||||
host's working copy.
|
||||
|
||||
## Concrete fleet example: tailnetatlas on gringo
|
||||
|
||||
The `tailnetatlas` jail runs `crazyhoesl/tailnet-atlas`, cloned into `/app`.
|
||||
The deployment has real local changes:
|
||||
|
||||
- `app/requirements.txt` was modified for this environment.
|
||||
- `run.sh` was added because the project ships no rc.d-friendly entrypoint.
|
||||
- `venv/` exists as an untracked build artifact.
|
||||
|
||||
If the upstream repo releases a new version, a plain `git pull` would either
|
||||
merge the local changes blindly or fail with conflicts in `requirements.txt`.
|
||||
The safer state is:
|
||||
|
||||
```
|
||||
upstream-vendor -> commit matching the upstream release that was deployed
|
||||
main/deployed -> upstream-vendor + "adapt requirements.txt for FreeBSD" +
|
||||
"add run.sh for rc.d" + "ignore venv/"
|
||||
```
|
||||
|
||||
Updating then becomes `git rebase upstream-vendor` on `main`, not a manual diff
|
||||
rescue.
|
||||
|
||||
## What not to do
|
||||
|
||||
- Do not leave local changes uncommitted in a clone and hope you remember them
|
||||
before the next update.
|
||||
- Do not `git pull` upstream into the same branch that carries local patches
|
||||
without reviewing the merge.
|
||||
- Do not commit secrets into either branch. Runtime secrets belong in root-only
|
||||
`.env` files or the secret store, never in the app repo.
|
||||
@@ -16,6 +16,7 @@ whichever host you happen to already be connected to.
|
||||
| **venus** | `100.109.251.127` (LAN `192.168.0.33`) | Corporate — internal business sites/tools, the iWP.es plugin-subscription platform, and other CloudHost-org first-party projects | Bastille jails, combined web+db (local MariaDB, no shared external DB host). New jails typically created via `bastille clone -a <known-good-base>` rather than from scratch. |
|
||||
| **granja** | `100.98.197.28` | Affiliate network — the travel/tourism site network (menorca.ro, palma.ro, and similar) | No public interface configured directly; sits behind a reverse-proxy manager (NPM) over Tailscale. See `docs/server-granja.md` for the local-curl-simulates-NPM testing trick. |
|
||||
| **funky** | `100.127.21.100` (LAN `192.168.0.218`) | Customers — sites migrated from the old Linux affiliate fleet, actual paying-customer properties (e.g. news.easycut.es, photomouse.ro) | Combined web+db+valkey, bastille jails. Migration target that absorbed several now-decommissioned Linux hosts (maagar, shoe, proton). |
|
||||
| **gringo** | `100.95.46.79` (LAN `192.168.168.68`) | Internal apps/tools host — replacement for the old Linux Docker host barky-1 | ~45 Bastille VNET jails on `10.20.0.0/24` (APIs, dashboards, Gitea, analytics, productivity tools). Default-deny outbound pf model; NPM dedicated ports `8000 + last octet`. See `docs/server-gringo.md`. |
|
||||
| **zamolxis** | `100.115.128.41` | Standalone (non-jailed) FreeBSD site host | Not part of the granja/external-fleet jail families — a single site (Newspaper/tagDiv theme, PHP 8.5) running directly on the host, not in a jail. |
|
||||
| **cabrera** | `100.100.108.19`, SSH port `79` | External Linux affiliate-network host (BTPanel-style) | Part of the older Linux fleet (alongside raptor/maagar/formentor/moonie/spunky/proton/shoe — several since decommissioned/migrated to funky). Hosts easycut.es's main WooCommerce e-commerce site directly (not jailed). |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user