# 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` - ``: `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 — ``` SetHandler application/x-httpd-php ``` The regex `\.* ` (zero or more literal dots, unanchored) matches nearly any filename, including ones with zero dots. Applied globally (outside any `` 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 `` PHP handler that's wider than it probably needs to be. Still open — not yet addressed.