max / alloy
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
1 file changed,
+20 insertions,
-0 deletions
| @@ -373,6 +373,26 @@ | |||
| 373 | 373 | Building with `TRIM=keep` keeps the documentation and the translations both. Measured | |
| 374 | 374 | and argued in [IMAGE.md](IMAGE.md#what-was-cut-2026-08-17). | |
| 375 | 375 | ||
| 376 | + | ## Firewall: **firewalld**, ruled 2026-08-22 | |
| 377 | + | ||
| 378 | + | **firewalld, on both profiles, with the stock `public` zone on the client and an Alloy-owned zone on the server.** Until this ruling the image had no host firewall at all: `firewalld` was not installed and no nftables ruleset shipped, so every listener the preset enables was reachable from whatever network the machine was on. On a laptop behind NAT that is survivable. On the server profile it is not, and converting the Hetzner boxes to Alloy is a live plan (wiki `host-base-images`). | |
| 379 | + | ||
| 380 | + | Measured on `localhost/alloy:hardening` before picking. firewalld is 2.0 MiB and pulls eight dependencies for 7.3 MiB total, and none of it is a new stack: `python3` and `nftables` are already in the image, so the Python daemon everyone cites as firewalld's cost is a library load here rather than an interpreter. Against a 5 GB image that is not a size argument in either direction, which leaves the decision to the model rather than the byte count. | |
| 381 | + | ||
| 382 | + | The model is what decides it. Fedora's stock `public` zone allows exactly `ssh`, `mdns` and `dhcpv6-client`, and that is precisely the set `50-alloy.preset` enables on a client: sshd on every install, avahi for the `<name>.local` path the headless flow depends on, and nothing else inbound. cups is a unix socket plus `localhost:631` and needs no rule, because loopback is not filtered. So the pick is not a policy someone has to write and maintain against the preset; it is the zone Fedora already ships, matching a listener set Alloy already documents. | |
| 383 | + | ||
| 384 | + | **`FedoraWorkstation` is explicitly not the zone**, and it is the reason to state the zone rather than leave it at "firewalld". That zone opens `1025-65535` on both tcp and udp, which is most of what a firewall is for, and it is the default on Fedora's desktop spin. | |
| 385 | + | ||
| 386 | + | **The server profile gets `alloy-server`: ssh and dhcpv6-client, no mdns.** The listener sets diverge by exactly one entry, and it is the entry that matters on a public IP. avahi stays installed and running on that profile because a headless box on a LAN is found by name, but a machine with an internet-facing address should not answer 5353 to the internet to buy that. Discovery does not depend on it alone: NetworkManager sends the same hostname in DHCP option 12, so the box appears in the router's lease table, and the baked hostname means the name is known in advance anyway. Granting it back on a LAN server is `firewall-cmd --permanent --add-service=mdns --reload`, and the manual says so. | |
| 387 | + | ||
| 388 | + | **`tailscale0` is pinned to the `trusted` zone**, on both profiles, and this is the part that would silently break if it were left out. tailscaled writes its own nftables tables (`ts-input`, `ts-forward`, `ts-postrouting`) and has no firewalld awareness whatsoever: zero matching strings in the 1.102.3 binary in this image. Under nftables every base chain returns its own verdict, so firewalld rejecting a packet in its table is final no matter what tailscale accepted in its own, and the whole tree is addressed by tailnet name. Trusting the tailnet is not a new position: Tailscale SSH already bypasses `authorized_keys` on these hosts, so the ACL is the access control and has been for as long as the tailnet has existed. | |
| 389 | + | ||
| 390 | + | Rejected: **a hand-written nftables ruleset in the image.** Smaller and more legible on the page, and it owns every interaction firewalld already handles. tailscale and netavark both write rules; NetworkManager knows about firewalld zones and knows nothing about a file; and every future service that needs a port becomes an image rebuild instead of a command. Alloy ships no hand-written ruleset anywhere else either, so this would be a bespoke mechanism with exactly one user. | |
| 391 | + | ||
| 392 | + | Rejected: **a firewall on the server profile only.** It is the cheaper half of the work and it defends the machine that is already the least exposed to the case a firewall is for, which is an untrusted LAN. A laptop is on more networks than a server is. | |
| 393 | + | ||
| 394 | + | Not settled here, and filed as its own work: whether `alloy net` should show the zone alongside the interface. firewalld is a NetworkManager-integrated daemon and `alloy net` is an nmcli front, so the state is one `nmcli` field away, but showing it is a console design question rather than part of the pick. | |
| 395 | + | ||
| 376 | 396 | ## VPN | |
| 377 | 397 | ||
| 378 | 398 | **WireGuard, through NetworkManager, with no plugin.** NetworkManager 1.54 speaks WireGuard natively, and the plugin packages a search turns up (`NetworkManager-wireguard` and its `-gnome` variant) do not exist in Fedora 43. What is missing without help is key generation: `nmcli` will import a peer config but will not mint one. `wireguard-tools` supplies `wg` and `wg-quick` and is the whole of what this needed. |