Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,13 @@ Dotted lines are the paths this diagram draws, and they are not the whole story.
Default deny holds for every segment except Hicks and the switch LAN, both of
which reach further than any diagram of exceptions suggests — so what a segment
can actually reach is recorded per segment in
[`network.md`](docs/network.md)'s *Reaches* column, and the enforced rules as a
list in [ADR-0013](docs/adr/0013-segment-access-as-implemented.md). A count was
the wrong instrument and this README carried the wrong count for months. Segment
colour matches the patch cable in the rack; a dashed border means egress only.
[`network.md`](docs/network.md)'s *Reaches* column, which is the document to
read for the current state.
[ADR-0013](docs/adr/0013-segment-access-as-implemented.md) holds the method and
the reasoning, and describes the ruleset as it stood on 2026-09-01; the Hicks
interface was narrowed the day after. A count was the wrong instrument and this
README carried the wrong count for months. Segment colour matches the patch
cable in the rack; a dashed border means egress only.
Full topology and data flow in [`docs/architecture.md`](docs/architecture.md).

## Stack
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ is a very different thing from an overlooked one. Full detail in
| Alertmanager webhook URL and the MokerLink SNMP community, in a local Claude Code session transcript | Found 2026-08-20 by a value-level sweep of the host. Redacted in place; mode 600, never committed or synced. The webhook topic was rotated — on the public ntfy instance the topic name *is* the credential, there is nothing to revoke — and delivery re-verified end to end. The switch community deliberately was not: rotating it means the `neo` residual above all over again. |
| Pre-purge objects still served by GitHub after the history rewrite | The 2026-08-19 rewrite (`021d2b6`) removed both secrets above from every *reachable* commit, but GitHub still serves the orphaned objects by SHA. Verified 2026-08-26: `647d90a`, `21afcad`, `efb2632` and `ee3d443` all still resolve through the API, and the tree at `21afcad` still lists `certificates/Gandalf.Gondor.Lab/ca-key.pem` and `cert-key.pem`. Garbage collection requested from GitHub Support on 2026-08-26 — **pending**; this is the [purge runbook](docs/runbooks/purge-git-history.md)'s *Afterwards* step, and it is the last one outstanding. The repository has no forks and a network count of 0, so nothing else is perpetuating them. Both credentials were rotated *before* the rewrite, so this changes nothing about their status: the old keys and the old community remain superseded and must still be treated as public. Re-check with `gh api repos/Gerrrt/HomeLab/commits/647d90a --jq .sha` — a `404` means GitHub has collected them. |
| Alertmanager published on `0.0.0.0`, letting anyone who could reach it silence an alert | Fixed 2026-08-30 — 9093 now binds to `127.0.0.1` ([#70](https://github.com/Gerrrt/HomeLab/issues/70), [ADR-0012](docs/adr/0012-publish-only-ports-with-an-off-host-consumer.md)). This was the sharpest of the three because a silence switches off monitoring and the record of it lives in the system being switched off. Nothing off-host ever used the port: silences are reached through Grafana, which proxies Alertmanager over the compose network behind a login, so closing it cost no capability. |
| Prometheus and Loki published on `0.0.0.0` with no authentication | **Accepted residual, not a fix in progress.** Anything that can route to `10.0.99.20` can read every metric and log line, inject metrics through Prometheus' remote-write receiver, and delete log ranges through Loki's delete API. Both stay published because `oracle`'s Alloy agent remote-writes to 9090 and pushes to 3100 — it is not a scrape target, so those ports are its only path. Firewall default-deny is the whole control: only Hicks (50) and Winterfell (99) can reach the host at all, and never an untrusted segment. Closing it properly means authentication in front of the ingest ports and a credential on every agent, which is a separate piece of work — see below. |
| Prometheus and Loki published on `0.0.0.0` with no authentication | **Accepted residual, not a fix in progress.** Anything that can route to `10.0.99.20:9090` or `10.0.99.20:3100` can read every metric and log line, inject metrics through Prometheus' remote-write receiver, and delete log ranges through Loki's delete API. Both stay published because `oracle`'s Alloy agent remote-writes to 9090 and pushes to 3100 — it is not a scrape target, so those ports are its only path. Firewall default-deny is the whole control, and since 2026-09-02 it is narrower than it was: the ingest ports are reachable from Winterfell (99) itself and from `10.0.30.110` on ImaginationLAN, which has an explicit pass for `Saruman`'s Alloy agent. Hicks (50) reaches `10.0.99.20` on `3000` only — a logged *Block access to Winterfell* drops the rest — and no untrusted segment reaches it at all. `docs/network.md` lists what Hicks may reach. Closing it properly means authentication in front of the ingest ports and a credential on every agent, which is a separate piece of work — see below. |
| The monitoring host's disk and swap are unencrypted | **Accepted residual, not a fix in progress** — see below. |

The switch is the honest gap, and it is a deliberate one. `neo` (10.7.7.2) is
Expand Down
70 changes: 62 additions & 8 deletions docs/network.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ above CasaBonita, which the spectrum does not. Reasoning in
| WAN | — | — | ISP-assigned | Uplink | — |
| LAN | — | — | `10.7.7.0/24` | Switch management only | Everything[^lan] |
| [Winterfell](#winterfell--vlan-99--management) | 99 | 🔴 Red | `10.0.99.0/24` | Infrastructure management | Internet |
| [Hicks](#hicks--vlan-50--trusted) | 50 | 🟠 Orange | `10.0.50.0/24` | Trusted workstations | Internet, 99, 30 |
| [Hicks](#hicks--vlan-50--trusted) | 50 | 🟠 Orange | `10.0.50.0/24` | Trusted workstations | Internet, 30, named ports on 99[^hicks] |
| [CasaBonita](#casabonita--vlan-40--media) | 40 | 🟡 Yellow | `10.0.40.0/24` | TVs and consoles | Internet |
| [ImaginationLAN](#imaginationlan--vlan-30--lab) | 30 | 🟢 Green | `10.0.30.0/24` | Hypervisor / lab | Internet |
| [Skids](#skids--vlan-20--iot) | 20 | 🔵 Blue | `10.0.20.0/24` | IoT and cameras | Internet |
Expand All @@ -33,6 +33,20 @@ above CasaBonita, which the spectrum does not. Reasoning in
one device and said "Nothing" here until
[ADR-0013](adr/0013-segment-access-as-implemented.md) read the ruleset.

[^hicks]: Hicks has the broadest path into management of any VLAN, and since
2026-09-02 that path is a list of destinations rather than the segment: ten
passes sit above a logged *Block access to Winterfell*, everything else from
50 to 99 is dropped, and the ten are enumerated in the Hicks notes below. It
is not the only way into 99 — the switch LAN reaches every segment, and two
host-scoped passes carry ImaginationLAN to `10.0.99.20`. Going the other
way, nothing blocks Hicks from ImaginationLAN, so the catch-all under those
rules still grants that segment entire —
[#228](https://github.com/Gerrrt/HomeLab/issues/228) owns that half.
[ADR-0013](adr/0013-segment-access-as-implemented.md) read the ruleset on
2026-09-01, the day before the narrowing landed, and describes the wider
state; it is left as written, per
[ADR-0001](adr/0001-record-architecture-decisions.md).

Hostnames are thematic rather than functional — `morpheus` is the firewall,
`mjolnir` the UPS, `Saruman` the hypervisor. The Role column is the source of
truth for what a box actually does.
Expand Down Expand Up @@ -94,7 +108,8 @@ truth for what a box actually does.
🔴 **Red** on the rack.

Infrastructure. The only segment that can administer other segments, and the
only one Hicks is permitted to reach for management.
only one Hicks is permitted to reach for management — on the named ports
listed under [Hicks](#hicks--vlan-50--trusted), and nothing else.

| Hostname | IP | MAC (OUI) | Device | OS | Location | Role |
| --- | --- | --- | --- | --- | --- | --- |
Expand All @@ -111,7 +126,9 @@ only one Hicks is permitted to reach for management.
built from.
- Port 3 of the main switch feeds an 8-port unmanaged switch[^tp-linkswitch]
that `prometheus` and `oracle` hang off.
- pfSense's admin UI is reachable on this interface from Hicks only.
- pfSense's admin UI is reachable on this interface from Hicks only, by a
named pass to `10.0.99.1:443`. Winterfell itself is blocked from it: the 99
interface drops HTTP and HTTPS to `10.0.99.1` above its egress rule.
- DHCP enabled, with static reservations for everything listed.
- `oracle` runs the Lemmiwinks wiki and its Postgres — it has since 2025-11-12,
and [ADR-0011](adr/0011-keep-the-wiki-internal.md) depends on it — and holds
Expand All @@ -133,7 +150,9 @@ only one Hicks is permitted to reach for management.

🟠 **Orange** on the rack.

Personal and work machines. The only segment with a path into management.
Personal and work machines. The VLAN with the broadest path into management,
though not the only one — the switch LAN reaches every segment, and
ImaginationLAN has two host-scoped passes to `10.0.99.20`.

| Hostname | IP | MAC (OUI) | Device | OS | Zone | Role |
| --- | --- | --- | --- | --- | --- | --- |
Expand All @@ -154,10 +173,45 @@ Personal and work machines. The only segment with a path into management.
### Notes

- Desktops are wired Cat6; one eero is wired as backhaul, the other two mesh.
- Corporate laptops live here but have no management access. They are treated as
untrusted endpoints that happen to sit on a trusted segment.
- Only specific hosts on this VLAN may reach Winterfell, and only on management
ports.
- **What this segment reaches on Winterfell is a list of destinations, not the
segment.** Ten passes sit above a logged *Block access to Winterfell*, and
everything else from 50 to 99 is dropped:

| Destination | Ports |
| --- | --- |
| `10.0.99.0/24` — the segment | `22/tcp`, ICMP echo |
| `10.0.99.1` — `morpheus` | `443/tcp` admin UI, `53/tcp+udp` resolver, `123/udp` NTP |
| `10.0.99.10` — `mjolnir` | `80,443/tcp` UPS card |
| `10.0.99.20` — `prometheus` | `3000/tcp` Grafana |
| `10.0.99.30` — `oracle` | `80,443/tcp` the wiki |

**The source is the segment, not named hosts.** Every one of those passes is
`vlan50 → …`, so any device on Hicks may use any of them. This note used to
say the opposite — "only specific hosts, and only on management ports" — and
had the narrowing backwards in both halves: it is by destination and port, and
never by host.
- **Corporate laptops are subject to exactly the same rules as everything else
here.** They are intended to be treated as untrusted endpoints that happen to
sit on a trusted segment, and nothing on the firewall enforces that: no alias
holds `10.0.50.69` or `10.0.50.70`, and no rule names them. It is a policy
about how they are used, and it is written here as one rather than as a
control.
- **Prometheus' and Loki's ingest ports are not on the list above.** `9090` and
`3100` are published without authentication
([#182](https://github.com/Gerrrt/HomeLab/issues/182)) and were reachable from
this segment for as long as the catch-all was the only rule between them;
*Block access to Winterfell* now drops them. Narrower, not gone:
`10.0.30.110` still has an explicit pass to both ports for `Saruman`'s Alloy
agent, and nothing stops a host already on Winterfell.
- **ImaginationLAN is still reached entire**, on every protocol and port,
because no rule blocks it and the catch-all below is reached.
[#228](https://github.com/Gerrrt/HomeLab/issues/228) is where that gets
decided. *Allow Hicks access to ImaginationLAN* now sits on **this** interface
— ADR-0013 found it on the ImaginationLAN interface, where a rule can never
match traffic that enters on Hicks — and grants nothing the catch-all was not
already granting.
- The switch LAN is blocked apart from `10.7.7.2:80`, the switch's own web UI;
the block below that pass is logged.

[^Desktop1]: [Build 1](https://pcpartpicker.com/b/KXv323)
[^Desktop2]: [Build 2](https://pcpartpicker.com/list/XgZpfd)
Expand Down
5 changes: 3 additions & 2 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ issues intact. Nothing was summarised away.
date. It is enforcing rather than decorative: the Winterfell block has dropped
22 packets, and the passes above it carry the real traffic — 834,283 through
*Allow SSH to Winterfell* alone — with only *Allow NTP* and *Allow HTTP to
Mjolnir* still at zero. `network.md`'s segment table still says Hicks reaches
"Internet, 99, 30", and that row is now wrong in the direction of too wide.
Mjolnir* still at zero. `network.md` and both security documents described the
wider state until they were corrected against this read; the description is no
longer the outstanding part, the posture is.

**ImaginationLAN is the half still open.** No rule blocks it, so the catch-all
grants the segment entire, on every protocol and port. *Allow Hicks access to
Expand Down
43 changes: 33 additions & 10 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,22 @@ segment explicitly before its egress rule, and the narrow exceptions that exist

**It does not hold for Hicks (50), and it does not hold for the switch LAN.**
Hicks blocks CasaBonita, Skids and Degens and then passes to `any`, so it reaches
all of Winterfell and all of ImaginationLAN on every protocol and port — not the
management path this section used to describe. The switch LAN carries pfSense's
stock *Default allow LAN to any* rule and reaches every segment.
**all of ImaginationLAN** on every protocol and port. Nothing denies it, and what
grants it is the catch-all rather than a decision about that segment — the one
rule that names ImaginationLAN grants nothing the catch-all was not already
granting. [#228](https://github.com/Gerrrt/HomeLab/issues/228) is where that gets
decided. The switch LAN carries pfSense's stock *Default allow LAN to any*
rule and reaches every segment.
Comment thread
Gerrrt marked this conversation as resolved.

**Winterfell is the half that has since been narrowed.** On 2026-09-02 the Hicks
interface gained ten host- and port-scoped passes into 99 and a logged *Block
access to Winterfell* under them, so Hicks now reaches management on an
enumerated list — SSH and ping to the segment, the firewall's admin UI, resolver
and NTP, the wiki, Grafana, and the UPS card — and nothing else.
[`network.md`](network.md#hicks--vlan-50--trusted) holds the list and is the
document to read for it. ADR-0013 read the ruleset the day before that landed
and describes the wider state; it is left as written, per ADR-0001, and this
section is where the current posture lives.

This section previously said "three exceptions", ADR-0002 said two, and ADR-0008
said five. All three were counts, and a count cannot express "reachable because
Expand All @@ -97,13 +110,23 @@ them silently; the restore runbook checks for them. A fourth, on ImaginationLAN,
is decided by ADR-0014 and lands with `ifrit`
([#234](https://github.com/Gerrrt/HomeLab/issues/234)).

Segmentation is doing more work here than it should have to. A workstation on
Hicks that can reach `10.0.99.20` can write to the metric and log stores without
a credential, because Prometheus and Loki publish unauthenticated ingest ports
for `oracle`'s agent to use — which is exactly the failure ADR-0002 predicted
when it recorded that "a compromised workstation reaches Winterfell". That is an
accepted residual, recorded in [`SECURITY.md`](../SECURITY.md), not a solved
problem.
Segmentation is doing more work here than it should have to. Prometheus and Loki
publish unauthenticated ingest ports for `oracle`'s agent to use, so anything
that can route to `10.0.99.20:9090` or `10.0.99.20:3100` can write to the metric
and log stores without a credential — which is exactly the failure ADR-0002
predicted when it recorded that "a compromised workstation reaches Winterfell".
That is an accepted residual, recorded in [`SECURITY.md`](../SECURITY.md), not a
solved problem.

**What has changed is who "anything" is.** A workstation on Hicks was in that
set for as long as the catch-all was the only rule in the way; since 2026-09-02
it reaches `10.0.99.20` on `3000` only and *Block access to Winterfell* drops
the ingest ports. What remains in the set is a host already on Winterfell, and
`10.0.30.110` on ImaginationLAN, which has an explicit pass to both ports for
`Saruman`'s Alloy agent. The residual narrowed by a firewall change nobody
recorded; [#182](https://github.com/Gerrrt/HomeLab/issues/182) still owns
closing it properly, because a control that depends on one un-reviewed rule
ordering is not authentication.

What has been taken off the firewall's shoulders is Alertmanager. It had no
off-host client, so it now binds to `127.0.0.1` and reaching VLAN 99 no longer
Expand Down