DynamicUser can only see its own journald entries by default, so the
sshd + authelia journalctl acquisitions were dying with "insufficient
permissions" and exit status 1 from the spawned journalctl process.
Adding systemd-journal grants the read access journald gates on group
membership, restoring the ssh-bf / authelia-bf detection chain.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The vinanrra image's mode numbers are: 0=Install+STOP, 1=Start,
2=Update+STOP, 3=Update+Start, 4=Backup+STOP. I picked 2 thinking
it meant "Only Start", which is why the container kept exiting
cleanly after each update check. Mode 1 just starts the server,
which matches what the main 7dtd container uses.
SteamCMD anonymous install fails with "Missing configuration" on a
fresh coop dir. The main 7dtd works because its binaries were
installed long ago and LinuxGSM skips the SteamCMD step. Same trick
for coop: rsync the binaries over and start-only, no update path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Container outbound (image pulls, LinuxGSM bootstrap fetches) was
dropped by the inet filter forward chain — only eth0 and DNAT'd
WAN traffic were whitelisted. Add iifname "docker0" accept so
containers can reach the internet.
Also add the coop server's 26910/26911-26912 forwards to ports.toml
so WAN players can connect.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Second container 7dtd-coop with its own /var/lib/7dtd-coop state dir
and a configure unit that patches the server as unlisted, 2-player,
distinct world seed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The agent runs as a systemd DynamicUser and was failing the nginx
acquisition with "No matching files for pattern /var/log/nginx/access.log"
because access.log is nginx:nginx 640 — readOnlyPaths handles sandbox
visibility but not Unix perms. extraGroups = [ "nginx" ] gets it past
the group bit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Upstream nixpkgs builds only cmd/crowdsec and cmd/crowdsec-cli; the
PR #446307 module's setup script expects notification plugins at
\$package/libexec/crowdsec/plugins/notification-*, causing first-start
failure (cannot stat notification-dummy). Add the cmd/notification-*
subpackages and move the resulting binaries into the libexec layout the
module expects.
Drop this override along with the vendored modules once the PR lands —
nixpkgs will need a matching package update for the rewrite to work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The upstream NixOS crowdsec module fails on first deploy ("no API client
section in configuration") because it doesn't auto-register LAPI
credentials. The rewrite in NixOS/nixpkgs#446307 (TornaxO7's branch) adds
a setup oneshot that runs `cscli machines add --auto` if the credentials
file is missing, and handles DynamicUser StateDirectory permissions
explicitly. The bouncer rewrite gets matching auto-registration.
Vendor both module files locally and disable the upstream copies. Drop
modules/crowdsec/ and the disabledModules+imports lines once the PR
merges into nixpkgs unstable.
Config moves to the new unified `settings` API (no more separate
`localConfig`); LAPI moved to 127.0.0.1:8081 to dodge the qBit collision.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Enables the CrowdSec agent with sshd/nginx/http-cve hub collections,
acquires logs from nginx, sshd, and Authelia journald, and wires the
firewall bouncer to enforce bans via nftables. Alerts are POSTed to a
self-chosen ntfy.sh topic (URL read from /var/secrets/ntfy-url, falls
back to a placeholder so the repo stays eval-clean without the secret).
Module is self-contained — remove the file + import to uninstall; state
lives under /var/lib/crowdsec.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
LAN is 10.0.0.0/24 since the router cutover; the 192.168 range was
a leftover from the eero-bridge era and no longer matches any host.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- nginx: strip Referer on torrent.nordhammer.it so qBit's origin check
doesn't reject the post-Authelia redirect (Referer was auth.nordhammer.it,
Host was torrent.nordhammer.it → 401 loop).
- tmpfiles: collapse the nested qbittorrent `d` rules into a single
`d` + recursive `Z` so systemd re-enforces ownership/perms on every
boot. Caught Docker-migration UID drift that silently broke state
persistence and file logging.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
qBittorrent's auth logic is "no SID cookie → bypass for localhost; SID
cookie present → validate it." If the browser has a stale SID from an
earlier session, qBit fails validation and returns 401 even though the
connection is from 127.0.0.1 and bypass is enabled.
Strip both directions: drop the client's Cookie header on the way in so
qBit never sees an SID, and hide Set-Cookie on the way back so the
browser never accumulates one in the first place.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sonarr/Radarr/Bazarr default to DisabledForLocalAddresses so that requests
coming via the nginx reverse proxy (from 127.0.0.1) skip the app's own
login, leaving Authelia as the single gate. Prowlarr defaults to Enabled,
which produces a 401 behind Authelia.
Idempotent: only rewrites config.xml + restarts prowlarr when it finds
the "Enabled" value; logs a no-op otherwise. Added pkgs.systemd to PATH
so the restart call works.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Only Jellyfin and the Authelia portal itself stay unprotected externally
(Jellyfin because it's streamed to remote clients; Authelia because it
is the login gate). Everything else (sonarr, radarr, bazarr, prowlarr,
torrent/qBittorrent, games, search) now goes through Authelia forward auth.
Internal integrations (Homepage widgets, Prowlarr → Sonarr/Radarr,
Bazarr → Sonarr/Radarr, transcode-hevc qBit queries) use 127.0.0.1:PORT
directly, so they are unaffected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- trustedLegacyCidrs now empty; eno1 is strictly WAN
- AdGuard rewrite retargets nordhammer.it → 10.0.0.1 (the new router IP)
- dnsmasq pins the bedroom camera (f0:a7:31:6c:50:4b) to 10.0.0.39
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Input chain now accepts WAN traffic for every port in ports.toml so
external access (SSH, HTTP, HTTPS, game ports) works through the eero's
upstream port forwards during phase 1, and via our own DNAT in phase 2.
- Add AdGuard DNS rewrite nordhammer.it → 192.168.4.25 so LAN clients
hit the mediaserver directly instead of relying on eero hairpin NAT.
Target changes to 10.0.0.1 at phase 2 cutover.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Without this, the default-drop input policy blocked SSH and AdGuard DNS
from existing 192.168.4.x clients because they arrive on eno1 (still
acting as a client on the eero network until phase 2 cutover).
The trustedLegacyCidrs list is meant to be emptied in phase 2 when
eno1 becomes the ISP-facing WAN.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds services/router.nix with systemd-networkd (eno1=WAN via DHCP,
eth0=LAN 10.0.0.1/24), nftables (NAT + firewall, default drop on WAN
in), dnsmasq (DHCP only — AdGuard Home keeps :53 for DNS), and sysctl
IP forwarding. NetworkManager is forced off on this host.
Port forwards live in ports.toml at the repo root and are imported via
builtins.fromTOML. Supports single ports, ranges ("26901-26902"), and
"both" protocol. Initial forwards: 22, 80, 443, 26900, 26901-26902.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The quality-floor helper uses awk to compare floats (since jq output
can be 10 vs 10.0 depending on type). Without gawk on PATH, the check
failed silently and every run issued PUTs even when values already
matched.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sonarr/Radarr default minSize=0 let through tiny sub-bitrate releases
(e.g. 163 MiB for a 40-min episode = 0.8 Mbps, unwatchable). Set min to
10 MB/min (~1.3 Mbps) across HDTV/WEBDL/WEBRip/Bluray 1080p so anything
below that is rejected on grab. Idempotent: only PUTs when value differs.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- services/adguard.nix: mutableSettings = false so Nix config overrides
UI-made changes on rebuild (settings are the source of truth)
- common.nix: add busybox for its collection of handy utilities
- common.nix: remove networking.nameservers — DNS now comes purely from
per-host NetworkManager config (AdGuard as the only resolver, no leaks)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- adguard.nordhammer.it now routes through Authelia forward auth
(AdGuard Home itself has no login, so this becomes the single gate)
- Added Authelia ACL rule for the subdomain so default_policy=deny
returns 401 for redirect instead of 403
- Added AdGuard Home widget to Homepage under Infrastructure
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
DoH-only sequential upstreams made first-time lookups slow. Add plain
UDP 1.1.1.1/9.9.9.9 alongside DoH and set upstream_mode=parallel so
AdGuard queries all four simultaneously and uses the fastest response.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New services/adguard.nix runs AdGuard Home on the mediaserver with DoH
upstreams (Cloudflare + Quad9) and three default blocklists. DNS listens
on :53; web UI on 127.0.0.1:3000, reverse-proxied at adguard.nordhammer.it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Thread cap didn't move the thermals, so the real culprit is likely
dried-out thermal paste rather than concurrency. Reverting to the
unbounded default while the compound gets redone; running one stream
at a time is enough of a workaround in the meantime.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Concurrent transcodes on the 56-core mediaserver were running hot.
Limits each ffmpeg invocation to 8 threads via -threads and x265's
pools= param (libx265 ignores -threads alone). Overridable with
TRANSCODE_THREADS env var.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Overriding linuxPackagesFor doesn't affect the already-evaluated
linuxPackages_latest attribute. Extend it directly so the patched
facetimehd is picked up by boot.kernelPackages = pkgs.linuxPackages_latest.
https://claude.ai/code/session_01XEMg2HskwRaQW3nrCd1q2z
Without this rule the subdomain falls under default_policy=deny,
which returns 403 instead of the 401 that nginx needs to redirect
to the Authelia login page.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Proton-based clients (e.g. CachyOS native install hitting 7DTD via
the Proton runtime) fail EAC handshake against a Linux dedicated
server. Disabling server-side lets Proton clients join via the
"Play without EasyAntiCheat" splash option.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Lets 7DTD (and other native titles lacking built-in FSR) render
internally at a lower resolution and upscale to the 3440x1440
display via gamescope's FSR filter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>