Skip to content

Latest commit

 

History

History
365 lines (280 loc) · 16.9 KB

File metadata and controls

365 lines (280 loc) · 16.9 KB

🏠 Network

Reference for IP assignments, firewall rules, DNS configuration, Tailscale ACLs, and traffic flow.

Quick links: 🌐 IP Assignments · 🔥 Firewall Rules · 🧭 DNS · 🔒 Tailscale · 🔀 Traffic Flow

Full outline

🌐 IP Assignments

LAN (Static DHCP)

Node IP var Role
homelab-edge ip_edge Edge, DNS, Ansible control
homelab-observe ip_observe Monitoring
homelab-data-01 ip_data_01 Postgres, Redis (shared data tier)
homelab-svc-01 ip_svc_01 Camunda, n8n, Authentik, Jellyfin
homelab-svc-02 ip_svc_02 GreenTechHub

IP values and lan_subnet are defined in inventories/group_vars/all/overrides.yml. main.yml holds EDIT_BEFORE_USE placeholders; overrides.yml overrides them with real values. When IPs change, update overrides.yml only. Also configure static DHCP reservations on your router by MAC address.

Tailscale (100.x.x.x)

Node Tailscale IP
homelab-edge 100.x.x.1
homelab-observe 100.x.x.2
homelab-data-01 100.x.x.3
homelab-svc-01 100.x.x.4
homelab-svc-02 100.x.x.5

Tailscale IPs are assigned by the coordination server and stable per device. Update group_vars/all/overrides.yml if they change.

Service Ports

Service Node Port
Grafana homelab-observe 3000
Prometheus homelab-observe 9090
Alertmanager homelab-observe 9093
Uptime Kuma homelab-observe 3001
Portainer homelab-observe 9000
ntfy homelab-observe 8085
Camunda homelab-svc-01 8080
Authentik homelab-svc-01 9000
n8n homelab-svc-01 5678
Jellyfin homelab-svc-01 8096
GreenTechHub homelab-svc-02 8000
Postgres homelab-data-01 5432
Redis homelab-data-01 6379
Pi-hole admin homelab-edge 80
Infisical — Caddy HTTPS (Tailscale only) homelab-edge 8443
Semaphore — Caddy HTTPS (Tailscale only) homelab-edge 8444
Infisical direct (Tailscale only, non-browser) homelab-edge 8222
Semaphore direct (Tailscale only, non-browser) homelab-edge 3010
SSH (all nodes) all ssh_port

🔥 Firewall Rules

All nodes use ufw with a default-deny inbound policy. Rules are applied by the base_hardening role and node-specific group vars.

homelab-edge

Port Protocol Source Reason
ssh_port TCP any SSH admin access
53 TCP/UDP LAN Pi-hole DNS (LAN only)
80 TCP LAN Pi-hole admin UI (LAN only)
8443 TCP tailscale_cgnat_range Caddy HTTPS — Infisical (Tailscale)
8444 TCP tailscale_cgnat_range Caddy HTTPS — Semaphore (Tailscale)
3000 TCP tailscale_cgnat_range Caddy HTTPS — Grafana (Tailscale)
9090 TCP tailscale_cgnat_range Caddy HTTPS — Prometheus (Tailscale)
9093 TCP tailscale_cgnat_range Caddy HTTPS — Alertmanager (Tailscale)
3001 TCP tailscale_cgnat_range Caddy HTTPS — Uptime Kuma (Tailscale)
9000 TCP tailscale_cgnat_range Caddy HTTPS — Portainer (Tailscale)
8085 TCP tailscale_cgnat_range Caddy HTTPS — ntfy (Tailscale)
8222 TCP tailscale_cgnat_range (100.64.0.0/10) Infisical direct port (Tailscale only — non-browser clients)
3010 TCP tailscale_cgnat_range (100.64.0.0/10) Semaphore direct port (Tailscale only — non-browser clients)

No ports are forwarded from the router. Cloudflare Tunnel connects outbound — all external traffic enters through it.

🔒 All tailscale_cgnat_range rows above are Tailscale-only, served as https://homelab-edge.<tailnet>.ts.net:<port> via Caddy (browser-trusted Let's Encrypt cert from tailscale cert) — no Pi-hole DNS involved. Ports 8222/3010 are the equivalent direct (non-Caddy) ports for non-browser clients. Don't widen any of these beyond tailscale_cgnat_range — see group_vars/all/main.yml. Request path: Tailscale-Only Service Access.

homelab-observe

Port Protocol Source Reason
ssh_port TCP LAN / VPN SSH (edge + admin devices)
3000 TCP LAN / VPN Grafana
9090 TCP LAN / VPN Prometheus
9093 TCP LAN / VPN Alertmanager
3001 TCP LAN / VPN Uptime Kuma
9000 TCP LAN / VPN Portainer
8085 TCP LAN / VPN ntfy

No public exposure. Accessible via Tailscale from admin devices.

homelab-data-01

Port Protocol Source Reason
ssh_port TCP LAN / VPN SSH (edge only)
5432 TCP Tailnet + LAN node IPs Postgres — consumers only, not open broadly
6379 TCP Tailnet + LAN node IPs Redis — consumers only, not open broadly

homelab-svc-01

Port Protocol Source Reason
ssh_port TCP LAN / VPN SSH (edge only)
8080 TCP LAN / VPN Camunda
9000 TCP LAN / VPN Authentik
5678 TCP LAN / VPN n8n
8096 TCP LAN / VPN Jellyfin

homelab-svc-02

Port Protocol Source Reason
ssh_port TCP LAN / VPN SSH (edge only)
8000 TCP LAN / VPN GreenTechHub

Updating Rules

Firewall rules are managed by Ansible. Do not apply ufw changes manually — they will be overwritten on the next deploy. Instead, edit ufw_rules in group_vars/<node>.yml and run:

ansible-playbook playbooks/apply_firewall.yml --limit homelab-edge

🧭 DNS

🏠 Internal — Pi-hole + Unbound

Pi-hole on homelab-edge (ip_edge) is the DNS server for all LAN clients. Set it as the primary DNS on your router's DHCP config.

Internal hostnames are configured as static entries in the homelab-edge-services repo (Pi-hole's custom.list). Do not edit /etc/pihole/custom.list directly — it is overwritten on every deploy-service deploy of homelab-edge-services.

Hostname Resolves to Port
grafana.homelab.local ip_observe 3000
prometheus.homelab.local ip_observe 9090
alertmanager.homelab.local ip_observe 9093
uptime.homelab.local ip_observe 3001
portainer.homelab.local ip_observe 9000
ntfy.homelab.local ip_observe 8085
camunda.homelab.local ip_svc_01 8080
authentik.homelab.local ip_svc_01 9000
n8n.homelab.local ip_svc_01 5678
jellyfin.homelab.local ip_svc_01 8096
greentechhub.homelab.local ip_svc_02 8000

Clients access services at http://<hostname>:<port>. Traffic between nodes travels over Tailscale (encrypted), so a separate internal TLS layer is not required.

Upstream resolution is handled by Unbound running on homelab-edge, which performs recursive DNSSEC-validated lookups directly against root servers — no third-party upstream DNS.

Adding a New Internal Hostname

Caddy and Pi-hole config are static files committed in the homelab-edge-services repo, not Ansible templates — see repo_split_brief.md.

  1. In homelab-edge-services, add an entry to both:
    • configs/pihole/custom.list (hostname → ip_edge's literal current value)
    • configs/caddy/Caddyfile (reverse_proxy {env.IP_<NODE>}:<port>) Commit and push.
  2. Redeploy from homelab-edge:
    /opt/deploy-service-venv/bin/deploy-service deploy homelab-edge-services --config /opt/homelab/services.yml

☁️ External — Cloudflare

Public hostnames (*.yourdomain.com) are managed in Cloudflare DNS. TLS terminates at Cloudflare. The Cloudflare Tunnel config in host_vars/homelab-edge.yml maps each public hostname to an internal host:port.

Pi-hole has no involvement in external DNS resolution.


Tailscale

Mode

Tailscale is installed on every node individually. Each node has its own Tailscale IP and maintains a direct encrypted connection to the Tailscale coordination server, independently of all other nodes.

homelab-edge additionally runs in subnet router mode, advertising lan_subnet to the Tailscale network. This is a convenience for reaching LAN devices that do not have Tailscale installed (e.g. a router, NAS, or IoT device) — it is not a dependency for accessing homelab nodes.

Because every node has its own Tailscale connection, the edge going dark has no impact on your ability to SSH into or manage any other node over VPN. LAN access (direct IP on ssh_port) remains available regardless.

homelab-edge joins the tailnet during Phase 1 (not Phase 2, as every other node does) — bootstrap_edge.yml brings Tailscale up before Infisical, because both Infisical and Semaphore (provisioned, seeded, and brought up later in that same single-pass play — see What the Bootstrap Playbook Does) are Tailscale-only services that must be reachable for the operator by the time Phase 1 finishes. (The provisioning-and-seed step itself doesn't need Tailscale at all — it runs over loopback, pre-firewall, in the one window where 127.0.0.1:8222 is reachable unauthenticated; see roles/infisical/tasks/bootstrap_instance.yml's header comment.) deploy_edge.yml re-runs the tailscale role in Phase 2 too — idempotent, a no-op once the node is already joined.

ACLs

Tailscale ACLs are defined in the Tailscale admin console (not in this repo). Recommended policy:

Source Destination Ports Reason
homelab-edge all homelab nodes 22 Ansible SSH
homelab-observe all homelab nodes 9100, 8080 Prometheus scrape (node + cAdvisor)
tag:admin all homelab nodes 22 Admin SSH access
tag:admin homelab-observe 3000, 3001, 9000 Grafana, Uptime Kuma, Portainer
all homelab nodes homelab-observe 3100 Loki log ingestion
homelab-edge homelab-svc-01/02 8080, 8096, 8000 Cloudflare Tunnel routing (Camunda/Jellyfin on svc-01, GreenTechHub on svc-02)
svc nodes homelab-data-01 5432, 6379 Postgres/Redis (shared data tier)
deny all all Default deny

Tag admin devices in the Tailscale console as tag:admin. This gives you fine-grained per-node ACL control — tighter than a blanket subnet route allow rule.

The edge subnet router (lan_subnet) should not be granted broad access in ACLs. It exists only to reach non-Tailscale LAN devices; homelab nodes are accessed directly by their own Tailscale IPs.

Key Rotation

Tailscale auth keys are stored in inventories/group_vars/all/vault.yml. On expiry, generate a new reusable key from the Tailscale admin console and update the vault:

ansible-vault edit inventories/group_vars/all/vault.yml

Then re-run the relevant deploy playbook to apply the new key.

MagicDNS

Tailscale MagicDNS resolves node hostnames (e.g. homelab-edge) to Tailscale IPs automatically for devices on the VPN. This complements Pi-hole's .homelab.local zone — MagicDNS handles node-to-node resolution within Tailscale; Pi-hole handles service-level .homelab.local resolution for LAN clients.

Tailscale HTTPS Certificates

homelab-edge provisions a browser-trusted Let's Encrypt certificate for its MagicDNS FQDN (homelab-edge.<tailnet>.ts.net) via the tailscale cert command. Tailscale acts as the DNS-01 ACME proxy — no public port exposure required. The cert is written to /var/lib/tailscale/certs/ and mounted read-only into the Caddy container, which serves it on ports 8443/8444 for Infisical and Semaphore respectively.

This gives Tailscale-connected browsers a green padlock at https://homelab-edge.<tailnet>.ts.net:8443/8444 with no per-device trust setup. LAN access via *.homelab.local uses plain HTTP — no TLS on the LAN path (ACME cannot issue certs for .local domains and Caddy's local CA requires per-device trust installation).

The cert is provisioned by the tailscale Ansible role (tailscale_cert_enabled: true on the edge node) and renewed weekly via cron.


🔀 Traffic Flow

External Request (Public Hostname)

Client → Cloudflare DNS → Cloudflare edge (TLS termination)
    → Cloudflare Tunnel (outbound from homelab-edge)
    → cloudflared on homelab-edge
    → target service on internal host:port

No ports open on the router. No direct internet exposure of any homelab node.

Internal Request (LAN Client)

LAN client → Pi-hole DNS (<ip_edge>:53)
    → resolves service.homelab.local → node IP
    → direct connection to node:port over LAN

Tailscale-Only Service Access

Ports and services covered by this pattern are listed in Firewall Rules → homelab-edge. No Pi-hole hostname, no LAN path — the requesting device must itself be a tailnet member.

Browser (Tailscale):

Admin device (on tailnet) → https://homelab-edge.<tailnet>.ts.net:{8443,8444,...}
    → ufw allows 100.64.0.0/10 on that port → Caddy → container on homelab-edge

Non-browser / API (Tailscale):

Admin device (on tailnet) → http://<edge-tailscale-ip>:{8222,3010}
    → ufw allows 100.64.0.0/10 on that port → container on homelab-edge

Ansible Deploy (Phase 3+)

homelab-edge (homelab user)
    → SSH to target node (<ssh_port>)
    → executes tasks as homelab user with passwordless sudo

Automated Deploy (Phase 4)

GitHub push → .github/workflows/deploy.yml
    → HTTP POST to n8n/Camunda endpoint (via Cloudflare Tunnel)
    → n8n/Camunda SSHes to homelab-edge as deploy user (via Tailscale)
    → scripts/deploy.sh → ansible-playbook
    → Ansible SSHes to all target nodes