Skip to content

Security: Gerrrt/HomeLab

SECURITY.md

Security policy

This repository documents and configures a private home network. It is not a product and has no users other than its owner, so "supported versions" does not really apply — main is the only branch that means anything, and it is what the lab runs.

What is useful here is a clear answer to two questions: what to do if you spot a problem, and what is already known.

Reporting something

If you find a misconfiguration, a leaked credential, or a weakness in what is published here, please report it privately rather than opening a public issue:

  • GitHub Security Advisoriesopen a draft advisory (preferred; it stays private until fixed)
  • Failing that, a GitHub issue without details, asking for a contact.

Please do not open a public issue containing a working credential, a capture, or anything that would let someone else reach the network before it can be fixed.

This is a personal project, so there is no SLA. Realistically: acknowledgement within a few days, and credential exposure treated as urgent.

Please don't

The lab is a home network, not a bug bounty target. Scanning, probing or attempting to reach any host described in docs/network.md is unwelcome and not authorised. Everything worth reviewing is in this repository — review the configuration, not the running system.

Known exposure

Documented rather than quietly fixed, because a known and written-down exposure is a very different thing from an overlooked one. Full detail in docs/security.md.

What Status
SNMP community committed in plaintext, shared across firewall, switch, UPS and BMC Removed from HEAD and replaced with four distinct per-device SOPS-encrypted values. Rotated on all four devices; each answers to its own new community. The firewall, the UPS and the BMC additionally refuse the old one. The switch still accepts its previous community alongside the new one — see below. The original shared string has been purged from git history, though it must still be treated as public — it was reachable in a public repository and cannot be un-seen.
Grafana admin/admin with anonymous Admin access enabled Fixed — anonymous auth off, password from SOPS
Passphrase-encrypted TLS private keys under certificates/ Removed from HEAD and purged from history. A new CA and leaf have been generated with scripts/gen-certs.sh; the old keys are superseded and should be treated as compromised wherever they were ever trusted.
Decrypted secrets in editor undo files, written by make secrets-edit Found 2026-08-20: three files under ~/.local/state/nvim/undodir/ holding the live pfSense, APC and iLO SNMP communities in plaintext, mode 664. Shredded. make secrets-edit now hardens $EDITOR before handing it plaintext, so it cannot recur. Never committed and never left the host, so those three communities were not rotated on that basis.
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.
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 rotated and polling, but it also still accepts the community it held before the rotation, verified after a reboot so the result reflects its saved configuration rather than a stale agent. Its firmware does not persist a deletion from the SNMP community table: the row can be removed, applied and saved, and the entry is still there after a restart. Each attempt also drops the SNMP agent until the switch is rebooted, and it is the switch the whole network runs through.

The residual risk is accepted rather than overlooked. The community is read-only, and reaching UDP/161 on 10.7.7.2 requires both a foothold on the management VLAN and the specific pfSense rule that permits 10.0.99.20 to reach it — it is not exposed beyond the management segment. The way to close it without fighting the firmware is to overwrite that row with a fresh value rather than delete it, on some future pass when the switch is already being taken down for something else.

Remediation is tracked in docs/roadmap.md, with procedures in docs/runbooks/rotate-snmp-community.md and docs/runbooks/purge-git-history.md.

There is no .gitleaksignore. There was one, enumerating nine historical findings so the full-history scan stayed meaningful; the purge removed what it acknowledged, so it was deleted. CI now scans the whole history with no exceptions, which is the only way to know the purge worked — an ignore file large enough to cover real findings can also hide new ones.

The unencrypted disk is a different kind of entry from the rest of that table. The others each happened once and have a date; this one is a standing property of the host. Measured on prometheus (10.0.99.20): /dev/mapper holds only control and the LVM logical volume, so there is no LUKS anywhere — the root filesystem is plain ext4 on LVM — and /swap.img is 4 GiB, unencrypted, on that same filesystem.

So ~/.config/sops/age/keys.txt and the rendered artefacts under snmp-exporter/.rendered/, alertmanager/.rendered/ and stacks/observability/.env — which hold plaintext by design — are all mode 600 and owned by robo, and file permissions are the only thing protecting them. Permissions mean nothing to someone holding the disk.

This is accepted rather than tracked as work. The threat model in docs/security.md already excludes an attacker with physical access to the rack, and this is that same exclusion stated at the filesystem level: recycled hardware, a home lab, and full-disk encryption on a headless host that must boot unattended after a power cut is a trade with its own failure mode. It is written down because the undo-file row above was only serious because of this — an undo file at mode 664 on an encrypted disk is a much smaller problem, and the two facts are easy to lose track of separately.

What this repository will not contain

Deliberate omissions, so their absence is not mistaken for an oversight:

  • Full MAC addresses. Truncated to the vendor OUI, which keeps the useful half and drops the unique identifier.
  • Owner-linked device names, and no room labelled as a child's.
  • Camera-to-room mapping. That there are cameras is fine; which one covers which door is not.
  • The WAN address, firewall rule bodies, and Wi-Fi configuration.
  • Any plaintext credential. Secrets are SOPS + age encrypted; the private key never enters the repository. See secrets/README.md.

Controls in CI

Every push and pull request runs:

  • gitleaks over the working tree and full history, with rules for SNMP communities, inline Grafana passwords, PEM private keys and age secret keys.
  • An assertion that every secrets/*.sops.yaml is genuinely encrypted, which needs no ability to decrypt.
  • An assertion that no rendered or decrypted artefact — .env, .rendered/, .purge-secrets.txt — is ever a tracked file.
  • Verification that every container image is pinned by tag and digest, so a moved tag cannot silently change what is deployed.

See docs/security.md for the threat model and segmentation rationale.

There aren't any published security advisories