Fold the Dankclaw and Dankserver vulnerability passes into
fleet-security-audit-2026-10-21.html: the stub "Advisory cross-reference (in
progress)" section becomes the full matrix (host advisories, GHSA coverage,
image-vulnerability matrix, no-advisory census, observed-without-credentials
rows for the two livingroom hosts) and the header links the two working files,
which move into omp-docs/reports/ with an index of their own.
Correct three figures the audit had wrong, all found by re-probing: the
mutable-tag census (17 -> 19 Dankclaw manifests / 17 unique references, +4 on
Dankserver), the Dankserver OpenSSH advisory count (0 -> 5 unpatched CVEs; the
first OSV query omitted the version epoch, and OSV answers "0 vulns" rather
than "unknown" for a malformed purl), and the Plex version ("12.1.0" is
Jellyfin's - the live container reports 1.43.5.11029-6bf8af3f0-ls329). Also
repair a malformed <code> tag in F1's evidence cell.
kb-lint and kb-coverage both report 0 issues; index.md gains the Reports
section and log.md the matching entry.
501 lines
56 KiB
HTML
501 lines
56 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Fleet Security Audit — 2026-10-21</title>
|
||
<style>
|
||
:root{
|
||
--primary:#2563eb; --secondary:#64748b; --success:#16a34a; --warning:#ca8a04;
|
||
--danger:#dc2626; --background:#f8fafc; --card:#ffffff; --border:#e2e8f0;
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Ubuntu,sans-serif;
|
||
line-height:1.6;background:var(--background);color:#1e293b}
|
||
header{background:linear-gradient(135deg,var(--primary),#1e40af);color:#fff;padding:2rem 1rem;text-align:center}
|
||
header h1{font-size:1.75rem;font-weight:600;margin-bottom:.5rem}
|
||
header .subtitle{opacity:.9;font-size:.95rem}
|
||
header .date{margin-top:.75rem;font-size:.85rem;opacity:.8}
|
||
.container{max-width:1080px;margin:0 auto;padding:1.5rem}
|
||
section{background:var(--card);border-radius:12px;padding:1.5rem;margin-bottom:1.5rem;box-shadow:0 1px 3px rgba(0,0,0,.08)}
|
||
section h2{color:var(--primary);font-size:1.25rem;margin-bottom:1rem;padding-bottom:.5rem;border-bottom:2px solid var(--border)}
|
||
h3{font-size:1rem;color:#334155;margin:1.25rem 0 .75rem;font-weight:600}
|
||
h3:first-child{margin-top:0}
|
||
p{margin-bottom:.75rem;color:#475569}
|
||
ul,ol{margin:.75rem 0 .75rem 1.25rem;color:#475569}
|
||
li{margin-bottom:.5rem}
|
||
code{font-family:'SF Mono',Monaco,'Cascadia Code',monospace;background:#f1f5f9;padding:.15rem .4rem;border-radius:4px;font-size:.9em;color:#0f172a}
|
||
pre{background:#1e293b;color:#e2e8f0;padding:1rem;border-radius:8px;overflow-x:auto;margin:.75rem 0;font-size:.85rem;line-height:1.5}
|
||
pre code{background:transparent;padding:0;color:inherit}
|
||
table{width:100%;border-collapse:collapse;margin:1rem 0;font-size:.9rem}
|
||
th,td{padding:.7rem .75rem;text-align:left;border-bottom:1px solid var(--border);vertical-align:top}
|
||
th{background:#f1f5f9;font-weight:600;color:#334155;font-size:.8rem;text-transform:uppercase;letter-spacing:.04em}
|
||
tbody tr:hover{background:#f8fafc}
|
||
.sev{font-size:.72rem;font-weight:700;padding:.2rem .55rem;border-radius:20px;text-transform:uppercase;letter-spacing:.04em;white-space:nowrap}
|
||
.sev.critical{background:#fee2e2;color:#991b1b}
|
||
.sev.high{background:#ffedd5;color:#9a3412}
|
||
.sev.medium{background:#fef3c7;color:#92400e}
|
||
.sev.low{background:#dbeafe;color:#1e40af}
|
||
.sev.info{background:#e2e8f0;color:#475569}
|
||
.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:1rem;margin:1rem 0}
|
||
.card{background:var(--background);border-radius:8px;padding:1rem;border-left:4px solid var(--secondary)}
|
||
.card.critical{border-left-color:var(--danger)}
|
||
.card.ok{border-left-color:var(--success)}
|
||
.card.warning{border-left-color:var(--warning)}
|
||
.card h4{font-size:.75rem;text-transform:uppercase;letter-spacing:.05em;color:var(--secondary);margin-bottom:.4rem}
|
||
.card .value{font-size:1.35rem;font-weight:700;color:#1e293b}
|
||
.card .value.error{color:var(--danger)}
|
||
.card .value.warn{color:var(--warning)}
|
||
.card .value.good{color:var(--success)}
|
||
.tag{display:inline-block;font-size:.72rem;padding:.1rem .45rem;border-radius:4px;background:#eef2ff;color:#3730a3;margin-right:.25rem}
|
||
.tag.pin{background:#dcfce7;color:#166534}
|
||
.tag.mutable{background:#fee2e2;color:#991b1b}
|
||
.tag.loop{background:#e2e8f0;color:#475569}
|
||
.note{background:#fffbeb;border-left:4px solid var(--warning);padding:.9rem 1rem;border-radius:6px;margin:1rem 0;font-size:.9rem;color:#78350f}
|
||
.verdict{background:#f0f9ff;border-left:4px solid var(--primary);padding:.9rem 1rem;border-radius:6px;margin-bottom:1rem;font-size:.95rem}
|
||
figure{margin:1rem 0}
|
||
figcaption{font-size:.8rem;color:var(--secondary);margin-top:.4rem;text-align:center}
|
||
footer{text-align:center;font-size:.8rem;color:var(--secondary);padding:1rem 0 2rem}
|
||
@media print{body{background:#fff}section{box-shadow:none;border:1px solid var(--border)}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>Fleet Security Audit</h1>
|
||
<div class="subtitle">blankpad · Dankclaw · Dankserver · LAN edge (192.168.0.0/24)</div>
|
||
<div class="date">Audited 2026-10-21 · Comrade Chief Designer, Soviet Computer Systems of Frank</div>
|
||
<div class="date">Per-host vulnerability passes: <a href="vuln-dankclaw.md">vuln-dankclaw.md</a> · <a href="vuln-dankserver.md">vuln-dankserver.md</a></div>
|
||
</header>
|
||
<div class="container">
|
||
|
||
<section id="verdict">
|
||
<h2>Verdict</h2>
|
||
<div class="verdict">
|
||
No compromise indicators. The fleet is not patched-and-probed enough to notice a breach, and four
|
||
findings are one config line each: a shared SSH password that is also in plaintext everywhere it
|
||
was echoed, <strong>23 container images on floating tags</strong>, Gitea accepting
|
||
<strong>self-registration with public read</strong>, and no HSTS/CSP on the edge proxy.
|
||
</div>
|
||
<div class="grid">
|
||
<div class="card critical"><h4>Critical</h4><div class="value error">1</div></div>
|
||
<div class="card warning"><h4>High</h4><div class="value warn">2</div></div>
|
||
<div class="card warning"><h4>Medium</h4><div class="value warn">4</div></div>
|
||
<div class="card"><h4>Low / info</h4><div class="value">3</div></div>
|
||
<div class="card ok"><h4>Rootkits</h4><div class="value good">none</div></div>
|
||
</div>
|
||
<p><strong>Coverage.</strong> Three boxes audited with credentials (blankpad, Dankclaw, Dankserver);
|
||
four more observed without access (router <code>.1</code>, <code>.40</code>/<code>.215</code> =
|
||
danklivingroom, <code>.75</code> = Apple device). Every claim below carries the command that produced
|
||
it; three claims inherited from an earlier survey were <strong>refuted</strong> and are listed in
|
||
<a href="#caveats">Caveats</a> rather than repeated.</p>
|
||
</section>
|
||
|
||
<section id="method">
|
||
<h2>Scope and method</h2>
|
||
<table>
|
||
<thead><tr><th>Instrument</th><th>Version</th><th>What it proved</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>nmap (NSE)</td><td>7.98</td><td>TCP/UDP service and banner census, TLS certs, <code>ssh2-enum-algos</code>, <code>sshvulns.nse</code> on every reachable address; <code>-sn</code> sweep of 192.168.0.0/24.</td></tr>
|
||
<tr><td>masscan</td><td>1.3.2</td><td>Full 1–65535 sweep of all live hosts (source-pinned to the Wi-Fi NIC), 65 535 ports per host, 78 open TCP ports total.</td></tr>
|
||
<tr><td>fping</td><td>5.3</td><td>27 live addresses from ARP+ICMP; reconciled against the KB network page.</td></tr>
|
||
<tr><td>lynis</td><td>3.1.5</td><td>Host audit on blankpad (71 warnings, see <a href="#blankpad">blankpad</a>).</td></tr>
|
||
<tr><td>checksec</td><td>2.7.1</td><td>Binary hardening of <code>sshd</code>, <code>ssh</code>, <code>sudo</code> on blankpad.</td></tr>
|
||
<tr><td>OSV API</td><td>—</td><td>Advisory lookup for pinned versions (distro ecosystems + Go/npm/PyPI for containers). In progress, see <a href="#cve">Cross-reference</a>.</td></tr>
|
||
<tr><td>direct inspection</td><td>—</td><td><code>sshd -T</code>, <code>authorized_keys</code> counts, host-key dates, quadlet manifests, live service configs, package/update state, and <code>ss -tlnp</code> as root on every box (root-owned sockets are invisible to an unprivileged <code>ss</code>).</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<h3>In scope / out of scope</h3>
|
||
<ul>
|
||
<li><strong>Audited with credentials:</strong> blankpad (CachyOS laptop, the repo's home), Dankclaw (Ubuntu 26.04 services box), Dankserver (Ubuntu 26.04 + 2× R9700 inference box).</li>
|
||
<li><strong>Observed only:</strong> <code>.40</code>/<code>.215</code> (danklivingroom, Ubuntu 24.04.4 — no key installed, banner-level only), <code>.75</code> (Apple device), router <code>.1</code> (ARRIS admin UI).</li>
|
||
<li><strong>Deliberately untouched:</strong> no service restarts, no package installs on the servers, no <code>/etc</code> edits anywhere — this pass is read-only; fixes are proposed in <a href="#fixes">Remediation</a> and land as their own changes.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section id="findings">
|
||
<h2>Findings register</h2>
|
||
<table>
|
||
<thead><tr><th>Sev</th><th>Where</th><th>Finding</th><th>Evidence</th><th>Fix</th></tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span class="sev critical">critical</span></td>
|
||
<td>all 3 boxes</td>
|
||
<td><strong>One SSH/sudo password for the whole fleet, and it is in plaintext in shell history, agent transcripts and command lines.</strong> All three boxes still accept it as an authentication factor (<code>PasswordAuthentication yes</code>), so a leaked transcript downgrades key-only SSH to a guessed secret.</td>
|
||
<td><code>sshd -T</code> on blankpad, Dankclaw, Dankserver → <code>passwordauthentication yes</code>, <code>pubkeyauthentication yes</code>, <code>permitrootlogin prohibit-password</code>, <code>maxauthtries 6</code>. Every privileged probe in this audit ran <code>printf '<pw>' | sudo -S</code>, which also puts the secret in the process table (<code>/proc/<pid>/cmdline</code> is world-readable) — the same class of leak that previously exposed an HF token.</td>
|
||
<td>Rotate the password once, then set <code>PasswordAuthentication no</code> in a drop-in on all three boxes and switch agent-side privilege escalation to <code>sudo -n</code> (already passwordless on the servers) or <code>sudo -S < ~/.secrets/sudo-pass</code> with mode <code>600</code>. Verify keys in a second session before closing the door.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev high">high</span></td>
|
||
<td>Dankclaw, Dankserver</td>
|
||
<td><strong>23 container images run mutable tags</strong> — 19 of the 33 quadlet manifests on Dankclaw (17 unique references: 15 × <code>:latest</code>, 1 × <code>:main</code>, 1 × <code>:nightly</code>) and 4 of the 12 on Dankserver. The deployed version cannot be read from the manifest, a restart can silently upgrade a production service, and a CVE lookup is impossible without inspecting each container.</td>
|
||
<td><code>grep -h '^Image=' ~/.config/containers/systemd/*.container</code> on both hosts. Dankclaw, recounted from the manifests: 33 <code>Image=</code> lines — 19 mutable (15 × <code>:latest</code>, 1 × <code>:main</code>, 1 × <code>:nightly</code>, the rest duplicate declarations of <code>localhost/hermes-agent:latest</code>), 10 exact-tag, 4 digest-pinned. The Dankclaw working page reports 24 there because it also counts rolling release tags (<code>postgres:16-alpine</code> ×2, <code>valkey:9-alpine</code>, <code>comfyui-rocm:gfx1151</code>) and <code>open_notebook:v1-latest@sha256…</code> as mutable. Dankserver: 4 on <code>:latest</code> plus the rolling <code>mariadb:11</code> and <code>redis:7-alpine</code>. Four Dankclaw images <em>do</em> pin digests (<code>open_notebook:v1-latest@sha256:54b2025…</code>, <code>surrealdb:v2@sha256:e5d7387…</code>, <code>chromedp/headless-shell@sha256:2d349b5…</code>, <code>ghcr.io/goauthentik/ldap@sha256:2e7b259…</code>), so the policy exists but is applied inconsistently. Live probes disagree with the tags: <code>gitea:latest</code> → 1.27.3, <code>grafana:latest</code> → 13.2.2 (commit <code>1bea008</code>), <code>jellyfin:latest</code> → 12.1.0, <code>pihole:latest</code> → Core v6.4.3 (vendor v6.5).</td>
|
||
<td>Pin every <code>Image=</code> to <code>tag@sha256:digest</code> (or at minimum an exact tag) in the quadlet manifests, one commit per host; see <a href="#images">Image census</a>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev high">high</span></td>
|
||
<td>Dankclaw</td>
|
||
<td><strong>Gitea allows self-registration and unauthenticated read.</strong> The instance carries the fleet's infrastructure repositories, so any device on the LAN (or anything that can reach <code>192.168.0.143:3000</code>) can create an account and browse them.</td>
|
||
<td>Live <code>/data/gitea/conf/app.ini</code>: <code>DISABLE_REGISTRATION = false</code>, <code>REQUIRE_SIGNIN_VIEW = false</code>; API version 1.27.3; published <code>0.0.0.0:3000</code> and <code>2222:22</code>.</td>
|
||
<td><code>DISABLE_REGISTRATION = true</code> (Authentik is the identity provider; keep the OAuth path) and audit per-repository visibility — the secret-gate doctrine in <code>AGENTS.md</code> assumes these repos are not world-readable.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev medium">medium</span></td>
|
||
<td>Dankclaw (Caddy)</td>
|
||
<td><strong>No HSTS anywhere, no CSP on the authenticated vhosts.</strong> The apex redirect drops the one header that pins clients to TLS; the app vhosts differ from each other, so policy is per-vhost accident rather than intent.</td>
|
||
<td><code>https://franksdank.cloud</code> → <code>HTTP/2 308, server: Caddy</code> and nothing else. <code>gitea.franksdank.cloud</code> → <code>x-content-type-options: nosniff</code>, <code>x-frame-options: SAMEORIGIN</code>. <code>grafana.franksdank.cloud</code> → bare <code>HTTP/2 200</code>.</td>
|
||
<td>Add <code>Strict-Transport-Security: max-age=31536000; includeSubDomains</code> to the Caddy site blocks (and fix the pre-existing <code>max-age=600</code> on the router-side listener), then stage a <code>Content-Security-Policy</code> starting in report-only mode.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev medium">medium</span></td>
|
||
<td>Dankserver</td>
|
||
<td><strong>NFS client helpers exposed on every interface with nothing exported.</strong> <code>rpcbind</code> on <code>0.0.0.0:111</code> plus <code>rpc.statd</code> on <code>0.0.0.0:51651</code>, while <code>/etc/exports</code> contains only comments — attack surface with no consumer.</td>
|
||
<td>Root <code>ss -tlnp</code> on both servers; <code>cat /etc/exports</code> → comments only. Dankclaw has the same two listeners.</td>
|
||
<td><code>systemctl disable --now rpcbind nfs-common</code> on both Ubuntu boxes (re-enable if an export is ever added).</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev medium">medium</span></td>
|
||
<td>Dankserver, .40/.215</td>
|
||
<td><strong>Patch lag and OpenSSH skew.</strong> The Ubuntu boxes are 73 packages behind (6 security) and the two livingroom hosts still run OpenSSH 10.0 while the audited boxes run 10.2/10.6 — three different SSH stacks on one LAN.</td>
|
||
<td>Dankserver: <code>apt-get -s upgrade</code> → 73 pending, 6 from <code>*-security*</code>, reboot not required. <code>.40</code>/<code>.215</code>: nmap banner <code>OpenSSH_10.0p1 Ubuntu-2ubuntu12.1</code> on Ubuntu 24.04.4 LTS. blankpad: <code>OpenSSH_10.6p1, OpenSSL 3.6.5</code>; Dankclaw/Dankserver: <code>OpenSSH_10.2p1 Ubuntu-2ubuntu3.8, OpenSSL 3.5.5</code>.</td>
|
||
<td>Upgrade the Ubuntu pair in a window (both boxes, same procedure, reboot not required on Dankserver); plan the livingroom hosts into the same key rollout rather than leaving them on an old stack.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev medium">medium</span></td>
|
||
<td>Dankserver</td>
|
||
<td><strong>Host keys predate the installed system and two live addresses are undocumented.</strong> <code>/etc/ssh/ssh_host_*_key</code> are dated 2024-11-29 on a box whose userland is Ubuntu 26.04-era — the keys were carried across a reinstall and never rotated. <code>.247</code> answers alongside <code>.235</code> with the same service set, and the KB names only <code>.235</code>.</td>
|
||
<td><code>ls -l --time-style=long-iso /etc/ssh/ssh_*_key</code> → 2024-11-29 (ecdsa/ed25519/rsa, no post-quantum key); ARP: <code>.235 b0:82:e2:a6:39:f3</code>, <code>.247 b0:82:e2:a6:39:f4</code> (same OUI, adjacent NIC) → [INFERENCE] second NIC of the same chassis. blankpad's keys (2026-09-09) already include <code>ssh_host_mldsa44_ed25519_key</code>; Dankclaw's (2026-07-21) and Dankserver's do not.</td>
|
||
<td>Rotate Dankserver's host keys after pinning <code>known_hosts</code> on the clients, and record both addresses in <code>omp-docs/system/dankserver.md</code>.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev low">low</span></td>
|
||
<td>Dankclaw, blankpad</td>
|
||
<td><strong>Listeners that no manifest declares.</strong> On Dankclaw a container serves <code>0.0.0.0:8787</code> without a <code>PublishPort=</code> line (host-network), and two <code>PublishPort</code> entries omit the bind address so podman publishes them on all interfaces where their neighbours are explicit.</td>
|
||
<td>Root <code>ss -tlnp</code>: <code>0.0.0.0:8787</code> → <code>/opt/hermes/.venv/bin/python /webui/server.py</code> (uid 109999); <code>192.168.0.143:18093</code> → <code>signal-relay.py</code>; <code>192.168.0.143:9401</code> + <code>127.0.0.1:9401</code> → <code>gpu-exporter.py</code>; <code>127.0.0.1:12345</code> → alloy; <code>127.0.0.1:33471</code> → containerd. Manifest grep shows <code>PublishPort=2222:22</code> and <code>PublishPort=3005:8080</code> with no address. blankpad likewise runs <code>python3</code> on <code>0.0.0.0:8899</code> and <code>bun</code> on <code>127.0.0.1:8787</code>.</td>
|
||
<td>Write the missing <code>PublishPort=127.0.0.1:…</code> lines so intent is on disk, and adopt the loose host scripts into quadlet/systemd units — every listener should be owned by a unit.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev low">low</span></td>
|
||
<td>.40/.215</td>
|
||
<td><strong>LLMNR enabled on the livingroom hosts</strong> (UDP/TCP 5355) — a legacy name-resolution protocol that answers broadcasts and is routinely poisoned; and their other open ports (<code>8081</code>, <code>27036</code>, <code>32000</code>) have no owner record anywhere.</td>
|
||
<td>nmap service probes on <code>.40</code> and <code>.215</code>; identical port sets on both addresses, which are two separate physical boxes (different OUIs: <code>84:47:09:…</code>, <code>4c:23:38:…</code>).</td>
|
||
<td><code>LLMRR=no</code> → <code>LLMNR=no</code> in <code>/etc/systemd/resolved.conf</code>; document what runs on those hosts once key access exists.</td>
|
||
</tr>
|
||
<tr>
|
||
<td><span class="sev info">info</span></td>
|
||
<td>all 3 boxes</td>
|
||
<td><strong>No rootkit or integrity indicators.</strong> NSE <code>rootkit-check</code>, <code>http-malware-host</code>, <code>http-sql-vulnwrapper</code>, <code>http-wordpress-enum</code> and <code>ssl-ccs-injection</code> ran against every reachable web service with no positives; blankpad binaries carry full RELRO, stack canary, Intel CET (SHSTK&IBT), NX and PIE.</td>
|
||
<td>nmap NSE results per host; <code>checksec file /usr/bin/sshd</code> → Full RELRO / Canary / CFI SHSTK&IBT / NX / PIE / FORTIFY 7 of 10, same profile for <code>ssh</code>, and <code>sudo</code> 5 of 9 with <code>RUNPATH [/usr/bin/sudo→/usr/lib/sudo]</code>.</td>
|
||
<td>Nothing to do; recorded so the next audit has a baseline.</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="hosts">
|
||
<h2>Per-host detail</h2>
|
||
|
||
<h3 id="blankpad">blankpad — dev workstation (192.168.0.140, .104)</h3>
|
||
<table>
|
||
<thead><tr><th>Field</th><th>Observed</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>OS / kernel</td><td>CachyOS, <code>7.2.9-1-cachyos</code></td></tr>
|
||
<tr><td>SSH</td><td><code>OpenSSH_10.6p1, OpenSSL 3.6.5</code>; unit <code>sshd</code> active + enabled, listening <code>0.0.0.0:22</code> and <code>[::]:22</code></td></tr>
|
||
<tr><td>sshd policy</td><td><code>passwordauthentication yes</code> · <code>pubkeyauthentication yes</code> · <code>permitrootlogin prohibit-password</code> · <code>maxauthtries 6</code> · <code>kbdinteractiveauthentication no</code> · <code>usepam yes</code>; ciphers <code>chacha20-poly1305, aes128-gcm, aes256-gcm, aes128-ctr, aes192-ctr, aes256-ctr</code></td></tr>
|
||
<tr><td>sshd config files</td><td><code>/etc/ssh/sshd_config.d/20-systemd-userdb.conf</code> (symlink from <code>/usr/lib/systemd</code>), <code>99-archlinux.conf</code> — the drop-ins, not a hand-edited config</td></tr>
|
||
<tr><td>Host keys</td><td>2026-09-09: <code>ed25519</code>, <code>ecdsa</code>, <code>rsa</code>, <strong><code>mldsa44_ed25519</code></strong> (post-quantum) — the only box in the fleet offering a PQ host key</td></tr>
|
||
<tr><td>Binary hardening</td><td><code>sshd</code>/<code>ssh</code>: Full RELRO, canary, CFI SHSTK&IBT, NX, PIE, no RPATH, FORTIFY 7/10. <code>sudo</code>: same, FORTIFY 5/9, <code>RUNPATH [/usr/lib/sudo]</code></td></tr>
|
||
<tr><td>Users with login shells</td><td>none besides <code>root</code> — no stale accounts</td></tr>
|
||
<tr><td>TCP listeners (13)</td><td><code>0.0.0.0:22</code> sshd · <code>0.0.0.0:8899</code> python3 · <code>127.0.0.53:53</code> resolved · <code>127.0.0.1:8787</code> bun · <code>127.0.0.1:44179</code> omp · <code>127.0.0.1:6463</code> Discord · <code>127.0.0.1:35507</code> chrome · <code>127.0.0.1:5433</code> passt (rest loopback)</td></tr>
|
||
<tr><td>Local tooling</td><td><code>lynis</code> + <code>checksec</code> installed; <code>rkhunter</code>/<code>chkrootkit</code> absent (Arch has no <code>debsecan</code> equivalent — <code>pacman</code> advisories come from the security tracker feed)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3 id="dankclaw">Dankclaw — services box (192.168.0.143)</h3>
|
||
<table>
|
||
<thead><tr><th>Field</th><th>Observed</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>SSH</td><td><code>OpenSSH_10.2p1 Ubuntu-2ubuntu3.8, OpenSSL 3.5.5</code>; <code>passwordauthentication yes</code>, <code>permitrootlogin prohibit-password</code>, <code>maxauthtries 6</code>, <code>authenticationmethods any</code>, <code>kbdinteractiveauthentication no</code>; MAC list still offers <code>umac-64-etm</code>, <code>hmac-sha1-etm</code>, <code>umac-64</code>, <code>hmac-sha1</code></td></tr>
|
||
<tr><td>Authorised keys</td><td><code>/home/dank/.ssh/authorized_keys</code> 9 entries, <code>/root/.ssh/authorized_keys</code> 3 entries</td></tr>
|
||
<tr><td>Host keys</td><td>2026-07-21 (ecdsa, ed25519, rsa)</td></tr>
|
||
<tr><td>Containers</td><td>33 quadlet manifests in <code>~/.config/containers/systemd</code>; 15 on <code>:latest</code>, 1 on <code>:main</code>, 1 on <code>:nightly</code> (19 manifest lines / 17 unique references), 4 digest-pinned, 10 exact-tag</td></tr>
|
||
<tr><td>Published on all interfaces</td><td><code>0.0.0.0:80</code>, <code>0.0.0.0:443</code> (Caddy) · <code>0.0.0.0:53</code> tcp+udp and <code>0.0.0.0:8083→80</code> (Pi-hole DNS + admin) · <code>0.0.0.0:3000</code>, <code>2222:22</code> (Gitea) · <code>0.0.0.0:8188</code>, <code>0.0.0.0:8502</code>, <code>3005:8080</code> · <code>389→3389</code>, <code>636→6636</code> (Authentik LDAP/LDAPS) · <code>7772:7772</code> (embedding proxy) · <code>8081:8080</code>, <code>8082:8085</code>, <code>9119:9119</code> · <code>0.0.0.0:8787</code> (unmanifested, see findings)</td></tr>
|
||
<tr><td>Published loopback-only</td><td><code>127.0.0.1:3002</code> Grafana · <code>127.0.0.1:3100</code> Loki · <code>127.0.0.1:9090</code> Prometheus · <code>127.0.0.1:9093</code> Alertmanager · <code>127.0.0.1:9000</code> Authentik · <code>127.0.0.1:5055</code>, <code>127.0.0.1:5056→5055</code>, <code>127.0.0.1:8080</code> + <code>192.168.0.143:8080</code></td></tr>
|
||
<tr><td>Grafana</td><td>13.2.2 commit <code>1bea008f7e4e858b6824c9e364d608bd4d10b13a</code> via <code>/api/health</code>; live <code>/etc/grafana/grafana.ini</code> has <code>;enabled = false</code> under <code>[auth.anonymous]</code> — anonymous access is <strong>off</strong>; env file mode <code>600</code>, contains only <code>GRAFANA_OAUTH_CLIENT_SECRET</code></td></tr>
|
||
<tr><td>Caddy</td><td><code>franksdank/caddy-porkbun:latest</code>; Caddyfile fronts both hosts, guarded vhosts route through Anubis → Authentik, apps with their own auth exempt by construction</td></tr>
|
||
<tr><td>Pi-hole</td><td><code>pihole/pihole:latest</code>; env <code>ServerIP=192.168.0.143</code>, <code>DNS1=1.1.1.1</code>, <code>DNS2=1.0.0.1</code>, <code>WEBPASSWORD=<set></code> — the admin UI is on <code>0.0.0.0:8083</code> rather than behind Caddy</td></tr>
|
||
<tr><td>Host processes</td><td><code>signal-relay.py</code> (18093), <code>gpu-exporter.py --serve</code> (9401), <code>alloy</code> (12345), <code>containerd</code> devv socket (33471) — scripts from <code>~/serverAppConfigs/scripts</code>, not declared as quadlets</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3 id="dankserver">Dankserver — main model box (192.168.0.235, .247)</h3>
|
||
<table>
|
||
<thead><tr><th>Field</th><th>Observed</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>SSH</td><td><code>OpenSSH_10.2p1 Ubuntu-2ubuntu3.8, OpenSSL 3.5.5</code>; <code>passwordauthentication yes</code>, <code>permitrootlogin prohibit-password</code>, <code>maxauthtries 6</code>, <code>usepam yes</code></td></tr>
|
||
<tr><td>Authorised keys</td><td><code>/home/dank/.ssh/authorized_keys</code> 7 entries; <code>/root/.ssh/authorized_keys</code> not readable by <code>dank</code> (unprivileged <code>cat</code> → permission denied)</td></tr>
|
||
<tr><td>Host keys</td><td>2024-11-29 — predates the current Ubuntu userland; no post-quantum host key</td></tr>
|
||
<tr><td>Packages</td><td>73 upgradable, 6 from <code>*-security</code>; reboot not required</td></tr>
|
||
<tr><td>Containers</td><td>12 quadlet manifests; 4 on <code>:latest</code> (<code>prom/node-exporter</code>, <code>9001/copyparty-ac</code>, <code>jellyfin/jellyfin</code> → 12.1.0 live, <code>rommapp/romm</code>); the rest pinned, including <code>linuxserver/plex:1.43.5.11029-6bf8af3f0-ls329</code> and <code>linuxserver/calibre-web:0.6.27-ls402</code></td></tr>
|
||
<tr><td>Host services</td><td><code>radiance</code> inference on <code>*:7770</code> (native process, not a container), <code>syncthing</code> <code>*:8384</code> (user unit enabled), <code>gnome-remote-de</code> RDP on <code>*:3389</code> (user unit active, system unit disabled), <code>rpcbind</code> <code>0.0.0.0:111</code>, <code>rpc.statd</code> <code>0.0.0.0:51651</code></td></tr>
|
||
<tr><td>NFS</td><td><code>/etc/exports</code> contains comments only — nothing exported, so the two NFS helper daemons serve nothing</td></tr>
|
||
<tr><td>Second address</td><td><code>.247</code> (ARP <code>b0:82:e2:a6:39:f4</code>) presents the same service set as <code>.235</code> (<code>b0:82:e2:a6:39:f3</code>) — [INFERENCE] second NIC of the same chassis; undocumented in the KB</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3 id="observed">Observed without credentials</h3>
|
||
<table>
|
||
<thead><tr><th>Address</th><th>Identity</th><th>Evidence</th><th>Action</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>.40</code> and <code>.215</code></td><td>danklivingroom — two distinct boxes (OUIs <code>84:47:09</code>, <code>4c:23:38</code>) with identical port profiles</td><td>SSH auth probe returns <code>Ubuntu 24.04.4 LTS</code> + <code>Permission denied (publickey,password)</code>; nmap banner <code>OpenSSH_10.0p1 Ubuntu-2ubuntu12.1</code>; open ports <code>22, 5355, 8081, 27036, 32000</code> on both</td><td>Install the fleet key (no password auth needed), then patch and document; see the LLMNR finding</td></tr>
|
||
<tr><td><code>.75</code></td><td>Apple device</td><td>Open ports <code>853, 49152, 62078, 62742, 62805</code> — <code>62078</code> is the Apple mobile-sync service; mDNS name present</td><td>Managed handset/tablet: nothing to patch from here; record it in the network page so it stops showing up as an unknown</td></tr>
|
||
<tr><td><code>.112</code></td><td>dankstation</td><td>Seen by the earlier discovery pass (mDNS + SSH, 7 ports via nmap); <strong>absent</strong> from the 2026-10-21 fping/masscan sweep</td><td>Confirm whether it is asleep or was renamed — the KB has no current entry</td></tr>
|
||
<tr><td><code>.1</code></td><td>ARRIS gateway (ISP router, Wi-Fi password holder)</td><td>Open ports <code>80, 111, 443, 8181, 49152, 50011, 50012, 51515</code>; HTTP banner <code>server: ARRIS</code>; auth probe reports password auth (no key accepted)</td><td>Admin UI is LAN-reachable; change the default credential and disable HTTP if the vendor build allows</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="network">
|
||
<h2>LAN exposure map</h2>
|
||
<figure>
|
||
<svg viewBox="0 0 1000 470" role="img" aria-label="Fleet attack surface" style="width:100%;height:auto;color:#1e293b">
|
||
<defs>
|
||
<marker id="a" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 z" fill="var(--secondary)"/></marker>
|
||
</defs>
|
||
<rect x="0" y="0" width="1000" height="470" fill="none"/>
|
||
<!-- internet -->
|
||
<g>
|
||
<rect x="20" y="30" width="150" height="56" rx="10" fill="none" stroke="var(--secondary)" stroke-width="1.5"/>
|
||
<text x="95" y="54" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">Internet</text>
|
||
<text x="95" y="72" text-anchor="middle" font-size="11" fill="var(--secondary)">WAN</text>
|
||
</g>
|
||
<!-- router -->
|
||
<g>
|
||
<rect x="20" y="140" width="150" height="86" rx="10" fill="none" stroke="var(--warning)" stroke-width="1.5"/>
|
||
<text x="95" y="164" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">.1 ARRIS</text>
|
||
<text x="95" y="182" text-anchor="middle" font-size="11" fill="var(--secondary)">80 · 443 · 8181</text>
|
||
<text x="95" y="198" text-anchor="middle" font-size="11" fill="var(--secondary)">111 · 49152</text>
|
||
<text x="95" y="214" text-anchor="middle" font-size="11" fill="var(--danger)">admin UI on LAN</text>
|
||
</g>
|
||
<line x1="95" y1="86" x2="95" y2="138" stroke="var(--secondary)" stroke-width="1.5" marker-end="url(#a)"/>
|
||
<!-- lan bus -->
|
||
<g>
|
||
<rect x="20" y="380" width="960" height="60" rx="10" fill="none" stroke="var(--primary)" stroke-width="1.5"/>
|
||
<text x="500" y="405" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">LAN 192.168.0.0/24 — 27 live hosts (fping), 78 open TCP ports (masscan, 1–65535)</text>
|
||
<text x="500" y="424" text-anchor="middle" font-size="11" fill="var(--secondary)">named: .1 router · .104/.140 blankpad · .143 Dankclaw · .235/.247 Dankserver · .40/.215 danklivingroom · .75 Apple · .112 dankstation (absent 2026-10-21)</text>
|
||
</g>
|
||
<line x1="95" y1="226" x2="95" y2="378" stroke="var(--secondary)" stroke-width="1.5" marker-end="url(#a)"/>
|
||
<!-- blankpad -->
|
||
<g>
|
||
<rect x="200" y="30" width="230" height="110" rx="10" fill="none" stroke="var(--secondary)" stroke-width="1.5"/>
|
||
<text x="315" y="54" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">blankpad</text>
|
||
<text x="315" y="72" text-anchor="middle" font-size="11" fill="var(--secondary)">CachyOS · OpenSSH 10.6p1</text>
|
||
<text x="315" y="90" text-anchor="middle" font-size="11" fill="var(--danger)">0.0.0.0:22 sshd · 0.0.0.0:8899 python3</text>
|
||
<text x="315" y="108" text-anchor="middle" font-size="11" fill="var(--secondary)">10 other listeners, loopback only</text>
|
||
<text x="315" y="126" text-anchor="middle" font-size="11" fill="var(--success)">PQ host key (mldsa44) · CET on sshd/sudo</text>
|
||
</g>
|
||
<!-- dankclaw -->
|
||
<g>
|
||
<rect x="460" y="30" width="250" height="130" rx="10" fill="none" stroke="var(--danger)" stroke-width="1.5"/>
|
||
<text x="585" y="54" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">Dankclaw</text>
|
||
<text x="585" y="72" text-anchor="middle" font-size="11" fill="var(--secondary)">Ubuntu 26.04 · 33 quadlets</text>
|
||
<text x="585" y="90" text-anchor="middle" font-size="11" fill="var(--danger)">10 published on 0.0.0.0</text>
|
||
<text x="585" y="108" text-anchor="middle" font-size="11" fill="var(--secondary)">80 443 53 8083 3000 8188 8502 3005 7772 8787</text>
|
||
<text x="585" y="126" text-anchor="middle" font-size="11" fill="var(--warning)">17 images on :latest</text>
|
||
<text x="585" y="144" text-anchor="middle" font-size="11" fill="var(--secondary)">Caddy → Anubis → Authentik</text>
|
||
</g>
|
||
<!-- dankserver -->
|
||
<g>
|
||
<rect x="740" y="30" width="240" height="130" rx="10" fill="none" stroke="var(--warning)" stroke-width="1.5"/>
|
||
<text x="860" y="54" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">Dankserver</text>
|
||
<text x="860" y="72" text-anchor="middle" font-size="11" fill="var(--secondary)">Ubuntu 26.04 · 12 quadlets</text>
|
||
<text x="860" y="90" text-anchor="middle" font-size="11" fill="var(--danger)">73 pkgs behind (6 security)</text>
|
||
<text x="860" y="108" text-anchor="middle" font-size="11" fill="var(--secondary)">radiance :7770 · 4 images :latest</text>
|
||
<text x="860" y="126" text-anchor="middle" font-size="11" fill="var(--warning)">rpcbind :111 + rpc.statd, no exports</text>
|
||
<text x="860" y="144" text-anchor="middle" font-size="11" fill="var(--secondary)">host keys 2024-11-29</text>
|
||
</g>
|
||
<!-- livingroom -->
|
||
<g>
|
||
<rect x="460" y="190" width="250" height="110" rx="10" fill="none" stroke="var(--secondary)" stroke-width="1.5" stroke-dasharray="5 4"/>
|
||
<text x="585" y="214" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">danklivingroom</text>
|
||
<text x="585" y="232" text-anchor="middle" font-size="11" fill="var(--secondary)">.40 + .215 · Ubuntu 24.04.4</text>
|
||
<text x="585" y="250" text-anchor="middle" font-size="11" fill="var(--danger)">OpenSSH 10.0p1 · key auth refused</text>
|
||
<text x="585" y="268" text-anchor="middle" font-size="11" fill="var(--secondary)">22 5355 8081 27036 32000</text>
|
||
<text x="585" y="286" text-anchor="middle" font-size="11" fill="var(--warning)">LLMNR enabled</text>
|
||
</g>
|
||
<!-- apple -->
|
||
<g>
|
||
<rect x="740" y="190" width="240" height="94" rx="10" fill="none" stroke="var(--secondary)" stroke-width="1.5" stroke-dasharray="5 4"/>
|
||
<text x="860" y="214" text-anchor="middle" font-size="14" font-weight="600" fill="currentColor">.75 Apple device</text>
|
||
<text x="860" y="232" text-anchor="middle" font-size="11" fill="var(--secondary)">853 49152 62078 62742 62805</text>
|
||
<text x="860" y="250" text-anchor="middle" font-size="11" fill="var(--secondary)">mobile-sync exposed on Wi-Fi</text>
|
||
<text x="860" y="268" text-anchor="middle" font-size="11" fill="var(--secondary)">managed endpoint, out of band</text>
|
||
</g>
|
||
<line x1="315" y1="140" x2="315" y2="378" stroke="var(--secondary)" stroke-width="1.2" marker-end="url(#a)"/>
|
||
<line x1="585" y1="160" x2="585" y2="378" stroke="var(--secondary)" stroke-width="1.2" marker-end="url(#a)"/>
|
||
<line x1="860" y1="160" x2="860" y2="378" stroke="var(--secondary)" stroke-width="1.2" marker-end="url(#a)"/>
|
||
<line x1="585" y1="300" x2="585" y2="378" stroke="var(--secondary)" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#a)"/>
|
||
<line x1="860" y1="284" x2="860" y2="378" stroke="var(--secondary)" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#a)"/>
|
||
<text x="200" y="176" font-size="11" fill="var(--secondary)">solid = audited with credentials · dashed = observed via scan only</text>
|
||
</svg>
|
||
<figcaption>Audited hosts and every port each one answers on, 2026-10-21.</figcaption>
|
||
</figure>
|
||
</section>
|
||
|
||
<section id="images">
|
||
<h2>Image census — the pinning table</h2>
|
||
<p>The same repo writes both styles on the same host. <span class="tag mutable">mutable</span> means a
|
||
restart can change the running binary; <span class="tag pin">pinned</span> means the manifest states what
|
||
is running. Verified versions were read from the live services, not the tags.</p>
|
||
<h3>Dankclaw — 33 manifests, 17 on <code>:latest</code></h3>
|
||
<table>
|
||
<thead><tr><th>Image</th><th>Status</th><th>Live version</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>docker.io/gitea/gitea:latest</code></td><td><span class="tag mutable">mutable</span></td><td>1.27.3 (API)</td></tr>
|
||
<tr><td><code>docker.io/grafana/grafana:latest</code></td><td><span class="tag mutable">mutable</span></td><td>13.2.2 / <code>1bea008f7e4e858b6824c9e364d608bd4d10b13a</code></td></tr>
|
||
<tr><td><code>franksdank/caddy-porkbun:latest</code></td><td><span class="tag mutable">mutable</span></td><td>serves 80/443, <code>server: Caddy</code></td></tr>
|
||
<tr><td><code>docker.io/pihole/pihole:latest</code></td><td><span class="tag mutable">mutable</span></td><td>DNS 53 + admin 8083</td></tr>
|
||
<tr><td><code>docker.io/prom/prometheus:latest</code>, <code>prom/alertmanager:latest</code>, <code>prom/node-exporter:latest</code></td><td><span class="tag mutable">mutable</span></td><td>loopback-published</td></tr>
|
||
<tr><td><code>ghcr.io/searxng/searxng:latest</code>, <code>ghcr.io/manualdousuario/marreta:latest</code>, <code>ghcr.io/open-webui/open-webui:main</code>, <code>docker.io/qmcgaw/gluetun:latest</code>, <code>docker.io/binwiederhier/ntfy:latest</code></td><td><span class="tag mutable">mutable</span></td><td>—</td></tr>
|
||
<tr><td><code>localhost/franksdank/frank-tts:latest</code>, <code>frank-tts-engine:latest</code>, <code>localhost/hermes-agent:latest</code> (×3)</td><td><span class="tag mutable">mutable</span></td><td>hermes webui answers on <code>0.0.0.0:8787</code></td></tr>
|
||
<tr><td><code>docker.io/lightpanda/browser:nightly</code></td><td><span class="tag mutable">mutable</span></td><td>—</td></tr>
|
||
<tr><td><code>ghcr.io/goauthentik/server:2026.8.3</code> (×3), <code>ghcr.io/manualdousuario/marreta</code>, <code>ghcr.io/seerr-team/seerr:v3.4.1</code>, <code>techarohq/anubis:v1.27.0</code>, <code>gcr.io/cadvisor/cadvisor:v0.49.1</code>, <code>docker.io/library/postgres:16-alpine</code> (×2), <code>docker.io/valkey/valkey:9-alpine</code>, <code>localhost/comfyui-rocm:gfx1151</code></td><td><span class="tag pin">pinned</span></td><td>—</td></tr>
|
||
<tr><td><code>lfnovo/open_notebook:v1-latest@sha256:54b2025a…</code>, <code>surrealdb/surrealdb:v2@sha256:e5d7387e…</code>, <code>chromedp/headless-shell@sha256:2d349b54…</code>, <code>ghcr.io/goauthentik/ldap@sha256:2e7b259e…</code></td><td><span class="tag pin">digest-pinned</span></td><td>the pattern every manifest should follow</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<h3>Dankserver — 12 manifests, 4 on <code>:latest</code></h3>
|
||
<table>
|
||
<thead><tr><th>Image</th><th>Status</th><th>Live version</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>ghcr.io/jellyfin/jellyfin:latest</code></td><td><span class="tag mutable">mutable</span></td><td>12.1.0 (<code>/System/Info/Public</code>)</td></tr>
|
||
<tr><td><code>ghcr.io/rommapp/romm:latest</code>, <code>ghcr.io/9001/copyparty-ac:latest</code>, <code>docker.io/prom/node-exporter:latest</code></td><td><span class="tag mutable">mutable</span></td><td>—</td></tr>
|
||
<tr><td><code>localhost/radiance-vllm:0.13.0-e060895</code>, <code>lscr.io/linuxserver/plex:1.43.5.11029-6bf8af3f0-ls329</code>, <code>linuxserver/calibre:9.15.0</code>, <code>linuxserver/calibre-web:0.6.27-ls402</code>, <code>linuxserver/tautulli:v2.18.1-ls245</code>, <code>docker.io/library/mariadb:11</code>, <code>redis:7-alpine</code>, <code>gcr.io/cadvisor/cadvisor:v0.49.1</code></td><td><span class="tag pin">pinned</span></td><td>—</td></tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="note"><strong>Why this matters more than the tag itself:</strong> an unpinned service cannot be
|
||
tied to an advisory. Half of Dankclaw's stack is pinned by someone's habit and half is not, so a
|
||
<code>git diff</code> no longer tells you what code is running — the assumption the whole
|
||
"the checkout is the deployment" doctrine rests on.</div>
|
||
</section>
|
||
|
||
<section id="cve">
|
||
<h2>Advisory cross-reference</h2>
|
||
<p>Versions come from live sources only — OSV <code>POST /v1/query</code> in the correct ecosystem
|
||
(<code>Ubuntu</code>/<code>Alpine</code> for distro packages, <code>Go</code>/<code>PyPI</code>/<code>crates.io</code>
|
||
for application code), the Ubuntu Security Tracker, the Alpine secdb, GitHub repository advisories, and
|
||
<code>podman exec … --version</code> inside each container. The complete per-host tables live beside this
|
||
report: <a href="vuln-dankclaw.md">vuln-dankclaw.md</a> (33 Dankclaw images + host) and
|
||
<a href="vuln-dankserver.md">vuln-dankserver.md</a> (Dankserver, blankpad, danklivingroom). The tables below
|
||
are the cross-host summary; <span class="tag">re-verified</span> marks a version this session re-probed
|
||
directly from blankpad on 2026-10-21.</p>
|
||
|
||
<h3>Ubuntu hosts — Dankclaw and Dankserver</h3>
|
||
<table>
|
||
<thead><tr><th>Component</th><th>Installed</th><th>Severity</th><th>Advisory / fix</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>openssh</code> (source package), both servers</td><td><code>1:10.2p1-2ubuntu3.8</code> <span class="tag">re-verified</span></td><td><span class="sev medium">medium</span></td><td><a href="https://ubuntu.com/security/CVE-2026-110923">CVE-2026-110923</a> plus CVE-2026-110924, -110925, -110926, -110927 — <strong>5 unpatched, no Ubuntu fix released</strong> (<code>apt-cache policy</code>: candidate == installed; tracker status <code>needs-triage</code>, CVSS 5.5–6.5). An earlier pass recorded "0 open advisories" here: the OSV purl-only query shape returns nothing for that build, the <code>name</code>+<code>ecosystem</code>+<code>version</code> form returns the five.</td></tr>
|
||
<tr><td><code>gnome-remote-desktop</code> (Dankserver)</td><td><code>50.2-0ubuntu0.1</code></td><td><span class="sev medium">medium</span></td><td>5 CVEs (CVE-2025-5024, CVE-2026-109832, CVE-2026-109833, CVE-2026-18358, CVE-2026-96541), none fixed for 26.04 — the RDP listener sits on <code>*:3389</code></td></tr>
|
||
<tr><td><code>rpcbind</code> (Dankserver)</td><td><code>1.2.7-1build2</code></td><td><span class="sev medium">medium</span></td><td>3 CVEs (CVE-2026-16277, CVE-2026-16461, CVE-2026-94640), none fixed for 26.04; the daemon serves nothing (<a href="#hosts">NFS exports are empty</a>) yet answers on <code>0.0.0.0:111</code></td></tr>
|
||
<tr><td><code>openssl</code>/<code>libssl3t64</code>, <code>nfs-utils</code>, <code>samba</code>, <code>syncthing</code></td><td>3.5.5-1ubuntu3.7, 1:2.8.5-1ubuntu1, 2:4.23.6+dfsg-1ubuntu2.2, 1.29.5~ds1-3</td><td><span class="sev info">clean</span></td><td>OSV <code>Ubuntu</code> returns no applicable advisory at the installed versions (<code>syncthing</code>: 3 records, all patched before 1.29.5)</td></tr>
|
||
<tr><td>kernel (Dankserver)</td><td><code>7.0.0-38.38</code></td><td><span class="sev info">untriaged</span></td><td>35 OSV records list the installed kernel as affected with no <code>fixed</code> event — <code>needs-triage</code>, not confirmed vulnerable</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Dankclaw containers <span class="tag">re-verified</span></h3>
|
||
<table>
|
||
<thead><tr><th>Container / image</th><th>Running version</th><th>Severity</th><th>Advisory / fix</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>open-notebook-surrealdb</code><br><code>surrealdb/surrealdb:v2@sha256:e5d7387…</code></td><td>2.7.0 (<code>/surreal version</code>)</td><td><span class="sev high">high</span></td><td>25 advisories, 6 HIGH (<a href="https://github.com/surrealdb/surrealdb/security/advisories/GHSA-848m-r628-vrxw">CVE-2026-63735</a>, GHSA-4vgr-h27g-cf9p, GHSA-5qfp-32cf-69jh, GHSA-cc8f-fcx3-gpjr, CVE-2026-63760, GHSA-wjjj-24cx-f28g). <strong>Every fix is on the 3.x line</strong> — the pinned v2 tag is end-of-line, so this is a major-version migration, not a rebuild.</td></tr>
|
||
<tr><td><code>gitea-server</code><br><code>gitea/gitea:latest</code></td><td>1.27.3 (<code>/api/v1/version</code>)</td><td><span class="sev info">patched</span></td><td><a href="https://github.com/go-gitea/gitea/security/advisories/GHSA-rcr6-4jqh-j84m">CVE-2026-60004</a> (CRITICAL, RCE through the diffpatch git hook) and GHSA-cm61-xmth-gqwh (cacheable session) were both fixed in 1.27.1 — the running image carries the fix. The exposure here is F3 (open registration + public read), not the build.</td></tr>
|
||
<tr><td><code>pihole</code><br><code>pihole/pihole:latest</code></td><td>Core v6.4.3 · Web v6.6 · FTL v6.7.1 (<code>pihole -v</code>)</td><td><span class="sev high">high</span></td><td>12 FTL + 3 web advisories published on/after the image build (2026-09-19), incl. GHSA-2h7g-g549-hf74 (authenticated file read + fix bypass), GHSA-2794-hrj8-5jg9 (<code>webserver.advancedOpts</code> RCE → root), GHSA-ww5x-xx4x-qvjr (<code>misc.dnsmasq_lines</code> RCE); vendor is at FTL v6.8 / Core v6.5 / Web v6.6.1. Its Alpine shell measures <code>musl 1.2.5-r2</code> + <code>libssl3 3.5.6-r0</code> → <a href="https://secdb.alpinelinux.org/v3.24/main.json">CVE-2026-36448 (CVSS 9.8)</a>, CVE-2026-31974, CVE-2026-39171.</td></tr>
|
||
<tr><td><code>searxng-valkey</code><br><code>valkey/valkey:9-alpine@sha256:31e788e7…</code></td><td>9.1.2 (<code>valkey-server --version</code>)</td><td><span class="sev high">high</span></td><td>4 vendor advisories — <a href="https://github.com/valkey-io/valkey/security/advisories/GHSA-mvcj-73cw-22m4">CVE-2026-63639</a>, CVE-2026-56684, GHSA-jcj7-v34w-v9vv, GHSA-fq2f-crmw-q97r — applicability unconfirmed (the Alpine secdb stops at <code>9.0.4-r0</code>; the image is a from-source build). Reachable only from the searxng pod network.</td></tr>
|
||
<tr><td><code>gitea-postgres</code>, <code>authentik-postgres</code><br><code>postgres:16-alpine@sha256:06f9702…</code></td><td>16.15 (<code>postgres --version</code>)</td><td><span class="sev info">unverifiable</span></td><td>PostgreSQL server CVEs are published on postgresql.org and are not in OSV/GHSA; <code>postgresql16</code> is absent from the Alpine secdb (from-source build), so no tracker in the query set covers 16.15.</td></tr>
|
||
<tr><td><code>caddy</code><br><code>franksdank/caddy-porkbun:latest</code></td><td>v2.11.7 (<code>caddy version</code>) → <code>docker.io/library/caddy:v2.11.7_alpine@sha256:684242074367…</code></td><td><span class="sev info">clean</span></td><td>No OSV record for <code>github.com/caddyserver/caddy@2.11.7</code>. The mutable local tag hides the upstream pin (F2) — record the digest in the manifest.</td></tr>
|
||
<tr><td>Alpine 3.24 shells — <code>gitea-server</code>, <code>grafana</code>, <code>ntfy</code>, <code>authentik-postgres</code>, <code>frank-tts</code></td><td><code>musl 1.2.6-r2</code>, <code>libssl3 3.5.7-r0</code></td><td><span class="sev medium">medium</span></td><td><a href="https://secdb.alpinelinux.org/v3.24/main.json">musl → CVE-2026-107891 (fixed <code>1.2.6-r3</code>)</a>; libssl3 → 10 CVEs fixed at <code>3.5.8-r0</code>. Shells on 3.23 (gluetun, caddy, seerr) were not fetched — backport status unknown.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Dankserver containers <span class="tag">re-verified</span></h3>
|
||
<table>
|
||
<thead><tr><th>Container / image</th><th>Running version</th><th>Severity</th><th>Advisory / fix</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>plex</code><br><code>lscr.io/linuxserver/plex:1.43.5.11029-6bf8af3f0-ls329</code></td><td>1.43.5.11029 (<code>cat /build_version</code>, build-date 2026-10-20)</td><td><span class="sev high">high</span></td><td>Plex publishes no CVE feed and has no OSV ecosystem; upstream is at <strong>1.43.6.11150</strong>, a security release, so the pin is one minor behind. The earlier pass attributed "12.1.0" to Plex — that string is Jellyfin's version (see the note at the end of this section).</td></tr>
|
||
<tr><td><code>copyparty</code><br><code>ghcr.io/9001/copyparty:v1.26.2</code> (image <code>2284b21e4259</code>)</td><td>1.20.24 (image label)</td><td><span class="sev medium">medium</span></td><td><a href="https://github.com/9001/copyparty/security/advisories/GHSA-3c88-p299-7hvr">GHSA-3c88-p299-7hvr</a> (fixed 1.20.25) and GHSA-hg99-jqfp-rf76 (low, fixed 1.20.26) are both newer than the running build — the tag moved without the container being rebuilt.</td></tr>
|
||
<tr><td><code>redis</code><br><code>docker.io/library/redis:7-alpine@sha256:2ed3d0c…</code></td><td>v=7.4.11 (<code>redis-server --version</code>)</td><td><span class="sev high">high</span></td><td>12 vendor advisories (<a href="https://github.com/redis/redis/security/advisories/GHSA-93m2-935m-8rj3">CVE-2026-23479</a>, CVE-2026-25243, CVE-2025-46817, CVE-2025-48367, CVE-2025-32023, CVE-2025-21605 …) carry <code>patched: TBD</code> upstream — no fixed 7.4.x release exists to move to. CVE-2025-49844 is fixed at 7.4.6, so it does not apply.</td></tr>
|
||
<tr><td><code>romm-mariadb</code><br><code>docker.io/library/mariadb:11@sha256:943995c7…</code></td><td>11.8.9 (<code>mariadb --version</code>)</td><td><span class="sev info">unverifiable</span></td><td>Upstream MariaDB has no OSV ecosystem; the 310 name-only records belong to Debian/Ubuntu/Alpine builds and cannot be attributed to this image. The 4 security fixes released for the 11.8.6 line were recorded against a "11.8.5" reading in the earlier pass — the installed build is 11.8.9.</td></tr>
|
||
<tr><td><code>romm</code><br><code>ghcr.io/rommapp/romm@sha256:eab97d88d9d7…</code></td><td>5.3.1 (<code>cat /backend/__version__.py</code>)</td><td><span class="sev info">clean</span></td><td>None of the 15 repository advisories applies (all patched earlier), but upstream is at 5.9.0 — 12 releases behind, which is where future exposure comes from.</td></tr>
|
||
<tr><td><code>jellyfin</code><br><code>ghcr.io/jellyfin/jellyfin@sha256:6370ab0005b4…</code></td><td>12.1.0 (<code>/System/Info/Public</code>)</td><td><span class="sev info">clean</span></td><td>None of the published repository advisories applies at 12.1.0 — all patched ranges end at ≤ 12.0, including the earlier pass's GHSA-vrc3-37vx-82q5 / CVE-2026-44208 row. The three newest advisories (GHSA-cf3c-8m59-2vhx SVG SSRF, GHSA-6828-c7cx-hvqm virtual-folder traversal, GHSA-4vx8-xhc9-qg6x session control) fix later releases.</td></tr>
|
||
<tr><td><code>calibre-web</code> / <code>calibre</code></td><td>0.6.27-ls402 / 9.15.0 (image tags)</td><td><span class="sev info">unverifiable</span></td><td>No OSV record in the PyPI ecosystem at calibre-web 0.6.27 and none for calibre 9.15.0; the earlier pass's CVE-2026-42811 (fixed 0.6.28) came from the Ubuntu tracker for a different build line.</td></tr>
|
||
<tr><td><code>tautulli</code><br><code>lscr.io/linuxserver/tautulli:v2.18.1-ls245</code></td><td>2.18.1 (tag)</td><td><span class="sev info">clean</span></td><td>18 repository advisories, all patched ≤ 2.17.2</td></tr>
|
||
<tr><td><code>cadvisor</code> / <code>cadvisor-test</code></td><td>v0.49.1 (images built 2024-03-02, Alpine 3.19 / 3.18.6 bases)</td><td><span class="sev medium">medium</span></td><td>No application advisory; the exposure is the base image — 63 GHSA on the Alpine 3.19 shell (61 fix-available), and v0.49.1 is 2.5 years old.</td></tr>
|
||
<tr><td><code>frank-archon</code> (native radiance, image <code>localhost/radiance-vllm:0.13.0-e060895</code>)</td><td>0.13.0 — tag-derived only: the running container is <code>docker.io/stilldeadcode/radiance@sha256:d590e300…</code> and ships no <code>python3</code> to confirm the embedded vLLM</td><td><span class="sev critical">critical</span></td><td>Assessed against vLLM 0.13.0: <a href="https://github.com/vllm-project/vllm/security/advisories/GHSA-4r2x-xpjr-7cvv">GHSA-4r2x-xpjr-7cvv</a> and GHSA-94f4-hr76-p5j6 (CRITICAL, fixed 0.14.1 / 0.22.0) plus 48 more. If the fork carries a different vLLM base the whole row set changes — flagged unverifiable in the working page.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Observed without credentials</h3>
|
||
<table>
|
||
<thead><tr><th>Host</th><th>Component</th><th>Severity</th><th>Advisory / fix</th></tr></thead>
|
||
<tbody>
|
||
<tr><td>danklivingroom <code>.40</code> / <code>.215</code></td><td>OpenSSH <code>10.0p1</code> — banner verified by probe; OS reported <code>Ubuntu 24.04.4 LTS</code>, package version <code>1:10.0p1-2ubuntu12.1</code></td><td><span class="sev medium">medium</span></td><td>8 CVEs fixed for the 10.0p1 source line (CVE-2025-61984, CVE-2025-61985, CVE-2026-3497, CVE-2026-35385, -35386, -35387, -35388, -35414 — <a href="https://ubuntu.com/security/CVE-2026-35385">USN-8222-1</a>, USN-8090-1). Nothing else on the box is assessable: key auth refused, so no inventory (<a href="#caveats">caveat</a>).</td></tr>
|
||
<tr><td>blankpad (Arch)</td><td>1,359 installed packages</td><td><span class="sev info">clean</span></td><td>0 packages match an Arch advisory with a newer fix version (2,446-advisory feed matched on <code>pkgbase</code>, control-tested against deliberately old versions so the matcher is not vacuous). Two advisories stay open upstream without a fixed version: AVG-2882 (openssl CVE-2025-4575, low), AVG-2893 (systemd CVE-2025-4598, medium). The <code>linux-cachyos</code> kernel is not in the tracker at all.</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="note"><strong>Working-file reconciliation.</strong> Three claims in the in-progress pass were corrected while folding the per-host pages into this report: (1) <strong>OpenSSH on Ubuntu</strong> — "0 open advisories" came from an OSV query shape that returns nothing for that purl; the corrected query and the Ubuntu tracker both report five unpatched CVEs. (2) <strong>Plex "12.1.0"</strong> — that version belongs to Jellyfin; the live Plex build is <code>1.43.5.11029-6bf8af3f0-ls329</code>. (3) <strong>Dankserver mariadb "11.8.5"</strong> — the running container reports 11.8.9. The Dankclaw mutable-tag census also differs by definition (19 manifests here, 24 in the working page, which counts rolling release tags as mutable); both counts are shown in <a href="#findings">F2</a>.</div>
|
||
</section>
|
||
|
||
<section id="fixes">
|
||
<h2>Remediation</h2>
|
||
<h3>P0 — this week, one change set</h3>
|
||
<pre><code># 1. rotate the fleet password once, then close the door on all three boxes
|
||
# (verify keys in a second session first)
|
||
printf 'PasswordAuthentication no\n' | sudo tee /etc/ssh/sshd_config.d/99-audit.conf
|
||
sudo sshd -t && sudo systemctl reload sshd
|
||
|
||
# 2. stop echoing the secret in every command line
|
||
# servers already allow `sudo -n` for systemctl; blankpad keeps its sudoers entry
|
||
# 3. Gitea: stop self-registration, keep the Authentik path
|
||
DISABLE_REGISTRATION = true # /data/gitea/conf/app.ini</code></pre>
|
||
<h3>P1 — next change set</h3>
|
||
<ul>
|
||
<li>Pin all 21 mutable images to <code>tag@sha256:digest</code> in the quadlet manifests, one commit per host; the repo already digest-pins four images, so this is finishing an existing pattern, not inventing one.</li>
|
||
<li>Caddy: <code>Strict-Transport-Security: max-age=31536000; includeSubDomains</code> on every TLS site (the router-side listener currently sends <code>max-age=600</code>), then CSP in report-only.</li>
|
||
<li>Bind <code>PublishPort=2222:22</code> and <code>3005:8080</code> explicitly (<code>127.0.0.1:</code>) and give <code>0.0.0.0:8787</code> (hermes webui) a manifest line so the socket matches the repo.</li>
|
||
</ul>
|
||
<h3>P2 — with the next maintenance window</h3>
|
||
<ul>
|
||
<li><code>apt-get upgrade</code> on Dankserver (73 packages, 6 security, no reboot flag) and the equivalent on Dankclaw.</li>
|
||
<li><code>systemctl disable --now rpcbind nfs-common</code> on both Ubuntu boxes — nothing is exported.</li>
|
||
<li>Rotate Dankserver's SSH host keys (dated 2024-11-29) after pinning them in client <code>known_hosts</code>; consider shipping a post-quantum host key there and on Dankclaw the way blankpad already does.</li>
|
||
<li>danklivingroom: install the fleet key on <code>.40</code>/<code>.215</code>, patch OpenSSH 10.0, set <code>LLMNR=no</code>.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section id="caveats">
|
||
<h2>Caveats and refutations</h2>
|
||
<ul>
|
||
<li><strong>Refuted — Grafana anonymous access.</strong> Carried forward as a finding by an earlier
|
||
survey (<code>[auth.anonymous] enabled=true, org_role=1</code>). The live container config reads
|
||
<code>;enabled = false</code> — a commented default, i.e. anonymous access is off. The earlier claim
|
||
came from a checkout path on the dev box, not from Dankclaw.</li>
|
||
<li><strong>Refuted — "Grafana bound to <code>127.0.0.1:3000</code>".</strong> The manifest publishes
|
||
<code>127.0.0.1:3002:3000</code>; port 3000 on that host belongs to Gitea.</li>
|
||
<li><strong>Refuted — "Pi-hole / SearXNG / Open WebUI quadlets missing".</strong> All three exist in
|
||
<code>~/.config/containers/systemd</code>; the survey had read a stale worktree path.</li>
|
||
<li><strong>Not verifiable this pass:</strong> configuration state of <code>.40</code>/<code>.215</code>
|
||
(key auth refused), <code>.75</code> and the router — banner and port evidence only; whether
|
||
<code>.247</code> is a second NIC or a second chassis is marked <code>[INFERENCE]</code> from ARP data;
|
||
and <code>/root/.ssh/authorized_keys</code> on Dankserver is not readable without a root shell.</li>
|
||
<li><strong>Method note.</strong> An unprivileged <code>ss</code> on a rootless-podman host hides most
|
||
listeners (they belong to <code>rootlessport</code>/<code>pasta</code>); every socket census here ran as
|
||
root, and the scan toolchain was installed on the audit box rather than the servers.</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<footer>
|
||
Audit 2026-10-21 · read-only probes from blankpad against blankpad, Dankclaw, Dankserver and the LAN edge ·
|
||
part of <code>dank/command-center</code> · <code>omp-docs/reports/fleet-security-audit-2026-10-21.html</code>
|
||
</footer>
|
||
</div>
|
||
</body>
|
||
</html>
|