Initial docs: alpha.jayfield.org server documentation

Records the host's service inventory, setup runbook, mail account list,
sync plan, package diff vs. clean install, and hardening TODOs, including
today's SSH hardening (key-only, no root login) and the fail2ban sshd
findtime fix for a low-and-slow brute-force evasion pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RdTyvEJkfNLDVAWt8WQ6X9
This commit is contained in:
2026-07-19 23:09:27 +02:00
co-authored by Claude Sonnet 5
commit 92d871f2da
6 changed files with 1583 additions and 0 deletions
+220
View File
@@ -0,0 +1,220 @@
# alpha.jayfield.org — Diff vs. a Clean Ubuntu 22.04 Install
Generated 2026-07-19. Methodology and caveats up front; findings below,
organized in three tiers of decreasing mechanical certainty and increasing
manual judgment.
**Update, same day**: three of the findings below (RTMP/`nginx`, the
`wireguard` tunnel, and `samba`/`cifs-utils`) were removed shortly after
this diff was run — see the annotations inline and `README.md`'s known
quirks for the current state. Left in place here as a record of what was
found and why it was removed; this file is a point-in-time investigation,
not a live inventory (`README.md` is the live inventory).
**Baseline used**: the official `jammy-server-cloudimg-amd64` manifest
(`cloud-images.ubuntu.com/jammy/current/`) for package comparisons, and
freshly-downloaded `.deb`s from the exact same Ubuntu archive this host
uses (matching installed versions exactly — e.g. `bind9 9.18.39-0ubuntu0.22.04.4`,
`postfix 3.6.4-1ubuntu1.4`) for config-file comparisons. One honest caveat:
several signs suggest this host was actually provisioned from the
interactive Server ISO installer, not the cloud image (`installation-report`,
`tasksel`, `popularity-contest`, and `language-pack-en` are all manually
installed — none of these appear on a cloud-init-provisioned instance).
The cloud image manifest is still the best available reference point, but
treat tier 1's line count as approximate, not exact.
---
## Tier 1 — Package diff
968 packages installed vs. 600 in the baseline manifest. Most of that gap
is transitive dependencies (installing `apache2` alone pulls in a dozen
supporting libraries nobody explicitly chose). Filtering to packages that
are both (a) not in the baseline and (b) marked `apt-mark showmanual`
(i.e. an actual administrative decision, not a dependency) gives **122
packages** — the real, meaningful list. Grouped by what they're for:
| Purpose | Packages |
|---|---|
| DNS | `bind9` |
| Mail | `postfix`, `postfix-mysql`, `dovecot-core`, `dovecot-imapd`, `dovecot-lmtpd`, `dovecot-managesieved`, `dovecot-mysql`, `dovecot-sieve`, `rspamd`, `mailutils`, `libmailutils6` |
| Database | `mariadb-server` (+ `mariadb-server-10.3`/`galera-3` — stale leftovers, see note below) |
| Web | `apache2`, `php8.0` + extensions, `libapache2-mod-php7.4`, `libapache2-mod-php8.0`, ~~`nginx`~~ (see Tier 2 — was **not** a web server here; **removed** same day, was unused RTMP) |
| TLS | `certbot`, `python3-certbot-apache` |
| Docker | `docker-ce`, `docker-ce-cli`, `docker-buildx-plugin`, `docker-compose-plugin`, `containerd.io` |
| Security | `fail2ban` |
| File sharing | ~~`samba`, `cifs-utils`~~ (→ the `vlda-01` CIFS mount; **removed** same day along with the dead tunnel below) |
| **VPN — not previously documented** | ~~`wireguard`~~ (**removed** same day — dormant, root-caused the CIFS failure, config backed up to `/root/removed-configs-backup/`) |
| Utilities | `at`, `net-tools`, `unzip`, `lz4`, `ltrace`, `bsdmainutils`, `resolvconf`, `qemu-guest-agent` |
| ISO-installer artifacts | `installation-report`, `tasksel`, `popularity-contest`, `language-pack-en`, `language-selector-common` |
**Stale/leftover packages worth a cleanup pass**: `mariadb-server-10.3`,
`mariadb-client-10.3`, `galera-3`, `libssl1.1`, `libicu66`, `php8.0` (vs.
the active `php7.4` module), `libperl5.30` — all remnants of the
18.04→20.04→22.04 upgrade history already noted in `README.md` (same
lineage as the `python3.8`/certbot issue documented there). Not actively
harmful, just disk/attack-surface bloat from packages no longer in active
use. Candidate for a future `apt autoremove` pass — worth checking what's
actually still linked against before purging.
**24 packages "missing" vs. baseline are not removals** — they're
expected absences for a BIOS-booted QEMU VM with no physical hardware:
`grub-efi-*`, `shim-signed`, `mokutil`, `efibootmgr` (this host boots via
legacy BIOS/`bios_grub`, confirmed in the disk-resize work — never needed
EFI tooling), plus `libtss2-*` (TPM), `fuse3`/`libntfs-3g89` (physical
filesystem drivers), `systemd-hwe-hwdb` (hardware enablement) — none of
this is relevant to a cloud VM regardless of who administers it.
---
## Tier 2 — Config file diff (live file vs. pristine package default)
Real `diff -u` output against genuinely pristine files pulled from the
matching package version in Ubuntu's archive — not reconstructed from
memory. Only the files with a real static default to diff against are
listed; Postfix's `main.cf`/`master.cf` and Dovecot's `dovecot.conf` are
**not** static conffiles (Postfix generates them via debconf at install
time; Dovecot's shipped file is a first-install template only), so "diff"
for those is a semantic comparison, covered in `SETUP.md` instead.
### `/etc/bind/named.conf.options`
Exactly what's documented in `TODO.md`/`README.md``version "unknown"`,
the `rate-limit` block, and the `forwarders` block removed in favor of
full recursion (with an inline comment explaining why). Plus pre-existing
(2022) additions: `allow-transfer`, `allow-query { any; }`,
`allow-recursion { trusted; }`, `querylog yes`, the `trusted` ACL.
### `/etc/ssh/sshd_config`
- `Port 10022` (from `#Port 22`)
- `PasswordAuthentication yes` and `PermitRootLogin yes` **explicitly set**
the shipped default leaves both commented out (relying on OpenSSH's own
safer compiled-in defaults). Flagged already in `SETUP.md` §1 as a
hardening gap worth reconsidering.
- `ChallengeResponseAuthentication no` — uses the pre-8.7 directive name;
current templates use `KbdInteractiveAuthentication`. Cosmetic, but a
dating signal (this file predates a later OpenSSH directive rename).
- **New finding**: an entire `Match Group sftp` block — chrooted,
no-shell SFTP-only access (`ChrootDirectory /var/sftp/%u`, tunneling and
forwarding all disabled). Three accounts use it: `ftpuser`, `ftp_jens`,
`ftp_alex` (`/var/sftp/{ftpuser,ftp_jens,ftp_alex}/`). This is a
legitimate, separate access mechanism from `jens`'s full shell account —
not documented anywhere before this diff.
### `/etc/systemd/resolved.conf`
Exactly the `DNS=127.0.0.1` change documented in `README.md`'s resolver
section, plus pre-existing `DNSSEC=allow-downgrade`, `LLMNR=no`, etc.
### `/etc/apache2/apache2.conf`
- `<Directory /var/www>`: `AllowOverride all` (default: `None`) — permits
`.htaccess` files to override server config anywhere under `/var/www`.
Broader than the stock posture; worth checking whether any `.htaccess`
files actually exist and rely on this before tightening it.
- A custom `fail2ban` `LogFormat` — ties into the `apache-auth` jail.
- **New finding, worth a closer look**: a global block appended at the
end of the file —
```
<FilesMatch "\.*">
SetHandler application/x-httpd-php
</FilesMatch>
```
The regex `\.* ` (zero or more literal dots, unanchored) matches nearly
any filename, including ones with zero dots. Applied globally (outside
any `<Directory>` scope), this routes essentially all requests through
the PHP handler, not just `.php` files. Whether this has real
consequences depends on what mod_php does with non-PHP content (likely
passes it through as literal text), but it's a broad, unusual, and
previously-undocumented directive — worth understanding the original
intent before deciding whether to scope it down to `\.php$`.
### `/etc/fail2ban/jail.conf`
This host edits `jail.conf` directly rather than using `jail.local` for
overrides — the intended pattern is to leave the vendor file untouched
and override in `jail.local` so future package upgrades don't silently
clobber local changes. Real customizations found: `bantime` 10m→**4h**,
`findtime` 10m→**30m**, `maxretry` 5→**3**, `port = 10022` on the
`sshd`/`dropbear`/`selinux-ssh` jails (matching the moved SSH port),
`sender = root@jayfield.org`, and several jails explicitly `enabled = true`
(`sshd`, `postfix`, `postfix-rbl`, `dovecot`, `sieve`, `pam-generic`, and
others) that ship disabled by default. The remaining diff noise (action-
syntax `%(banaction)s[...]` reformatting, comment typo fixes) is from
fail2ban's own upstream template evolving between minor releases, not a
local change.
### `/etc/nginx/nginx.conf` — **removed 2026-07-19, same day as this diff**
Confirmed Tier 1's finding cleanly: the entire `http {}` block from the
stock config was **removed** — no web serving capability at all — and
`include /etc/nginx/rtmp.conf;` was the only addition. This nginx instance
existed solely to run `libnginx-mod-rtmp` as a live-streaming ingest server
(`rtmp.conf`: `listen 1935; application live { live on; record off; }`,
no publish authentication configured). Entirely separate from the
Docker `web` (nginx) container and from Apache — no overlap, no conflict,
just a second, unrelated nginx process that had gone uncatalogued. No
active connections or recent use found before removal; purged
(`nginx nginx-core nginx-common libnginx-mod-rtmp`) along with `/etc/nginx`.
### `/etc/apache2/ports.conf`
No diff — matches stock exactly (Apache's own `Listen 80`/mod_ssl's
`Listen 443` addition).
---
## Tier 3 — Changes with no package-level "default" to diff against
Not mechanically diffable — either data (not config), or infrastructure
this host's package manager doesn't track at all.
- **Disk partitioning**: root partition was 9.7GB of a 172GB disk for
years (see `README.md`) — grown to 157GB this session via
`growpart`/`resize2fs`. No "default" partition layout to diff against;
this is provisioning-time state.
- **DNS zone content** (`/etc/bind/db.jayfield.org`): all records —
there's no baseline zone file to diff against, it's 100% custom data.
- **MySQL `vmail` database**: schema + data for mail accounts/aliases/domains
(see `SETUP.md` §3 for the schema). Data, not config.
- **TLS certificates**: 7 Let's Encrypt certs, all custom to this domain.
- **Docker layer**: `portainer`, `web` (nginx), and the Nextcloud stack
(`nextcloud`, `nextcloud-db`, `nextcloud-redis`) — none of this exists
in a base install; it's entirely additive infrastructure documented in
`README.md`.
- **WireGuard — removed 2026-07-19, same day as this diff**
(`/etc/wireguard/alpha_at_moon.conf`): a client tunnel named
"alpha at moon" — `Address = 10.8.0.8/24`, peer endpoint
`vpn.dyndns.jayfield.org:51820`, `AllowedIPs = 10.8.0.0/24,
192.168.22.0/24`. Was inactive (`wg-quick@wg0` not running) at the time
of this diff. Almost certainly *why* the `mnt-vlda-01.mount` failure
documented in `README.md` happened — `vlda-01` (the CIFS target) was
presumably only reachable via this same `192.168.22.0/24` range.
Decision made: rather than reviving the tunnel, removed it along with
the dead CIFS mount and unused Samba packages. Config backed up to
`/root/removed-configs-backup/alpha_at_moon.conf.bak` on the server
(`600 root:root`) in case the tunnel is wanted again later.
- **SFTP chroot jail** (`/var/sftp/`): three accounts (`ftpuser`,
`ftp_jens`, `ftp_alex`) in the `sftp` group, each chrooted to their own
directory with no shell — a controlled file-drop mechanism separate
from `jens`'s admin SSH access. Purpose/owners of these accounts not
established from config alone.
- **Users, groups, sudoers**: not diffed — no baseline "clean install"
user set to compare against beyond the default `ubuntu`/first-boot user,
which this host doesn't appear to still have.
- **Cron jobs**: `certbot renew` (weekly), Nextcloud's `cron.php` (every
5 min) — both documented in `README.md`, both additive, no baseline.
---
## Summary: what this diff actually surfaced
Beyond confirming the changes already tracked in `README.md`/`TODO.md`,
this exercise found **four previously-undocumented things**:
1. ~~A live RTMP streaming server (`nginx` + `libnginx-mod-rtmp` on
`:1935`)~~ — **removed** same day; unused, no auth on the publish
endpoint, no reason found to keep it.
2. ~~A dormant WireGuard tunnel to a home network~~ — **removed** same
day, along with the CIFS mount it was almost certainly gating and the
Samba packages that supported it. Config backed up first.
3. A chrooted SFTP-only access path for three accounts, separate from the
main admin login. Left as-is — appears to be legitimate, ongoing
infrastructure, not a stray leftover.
4. Two Apache config items worth a second look: `AllowOverride all` on
`/var/www`, and a broad `<FilesMatch "\.*">` PHP handler that's wider
than it probably needs to be. Still open — not yet addressed.