homelab

DNS infrastructure, secure remote access and secret management

Secure access infrastructure: Vaultwarden password vault, NPM wildcard SSL, AdGuard DoT DNS filtering, Tailscale VPN and split-horizon DNS.

Production IP addresses and domain names have been replaced with generic labels for security reasons.

Context

A cross-cutting project combining three core components of homelab security: secret management (Vaultwarden), secure service exposure (NPM + SSL + DNS), and remote access without public exposure (Tailscale VPN). Together they form a coherent architecture where no secret travels in plaintext and no service is directly exposed to the Internet.

1 — DNS Architecture (AdGuard Home + DoT)

AdGuard Home is deployed as an LXC on PVE-02 (lxc-adguard-01, VLAN 10 MGMT). It acts as the primary DNS resolver for the entire homelab:

Clients (all VLANs) → AdGuard Home :53 (VLAN 10 MGMT)
                             │
             ┌───────────────┴──────────────────┐
             │                                  │
   Internal zone (Rewrites)            DNS upstream (DoT)
   *.[my-domain.fr] → [NPM]            Cloudflare  1.1.1.1:853
   *.homelab.lan → Unbound pfSense     Quad9       9.9.9.9:853

Key decisions:

2 — HTTPS Exposure (NPM + Let's Encrypt wildcard)

Nginx Proxy Manager (lxc-npm-01, VLAN 10 MGMT) is the reverse proxy for all exposed services. It holds the wildcard certificate *.[my-domain.fr] issued by Let's Encrypt via the DNS-01 Cloudflare challenge — no permanent inbound port 80/443 exposure required.

Browser → https://service.[my-domain.fr]
                     │
              NPM (VLAN 10 MGMT :443)
              │  Wildcard SSL *.[my-domain.fr]
              │  Force SSL, HSTS
              │  Access list : 172.16.0.0/16 + Tailscale CGNAT
              ▼
         Internal service (HTTP) → response

Exposed services (excerpt):

FQDN Backend Notes
vaultwarden.[my-domain.fr] Vaultwarden :8080 Never publicly exposed (NXDOMAIN on public DNS)
grafana.[my-domain.fr] Grafana :3000 Accessible via Tailscale only
zabbix.[my-domain.fr] Zabbix :80 pfSense NAT port-forward
n8n.[my-domain.fr] n8n :5678 Internal access only

3 — Vaultwarden (self-hosted password vault)

Vaultwarden is an open-source implementation of the Bitwarden protocol deployed as an LXC on PVE-02 (lxc-vaultwarden-01, VLAN 10 MGMT), using Docker-in-LXC with a pinned image.

Security decisions

Decision Rationale
Tailscale-only No public port-forward; NXDOMAIN on public DNS → unreachable from the Internet
Docker-in-LXC CVE updates with a single command (docker pull of new tag)
SQLite (not MariaDB) Single file to back up, trivial restore
Pinned image No silent version change
Monthly KeePassXC export Offline encrypted backup (break-glass when unavailable)
# Docker deployment
docker run -d \
  --name vaultwarden \
  --restart unless-stopped \
  -e DOMAIN="https://vaultwarden.[my-domain.fr]" \
  -e SIGNUPS_ALLOWED=false \
  -v /opt/vaultwarden/data:/data \
  -p 8080:80 \
  vaultwarden/server:<pinned-version>

SIGNUPS_ALLOWED=false: no account creation possible except by explicit invitation.

Critical fix — SameSite Cookie

Symptom: login accepted but looping back to the login page indefinitely.
Cause: cookie_samesite = none combined with cookie_secure = false — a combination rejected by modern browsers (SameSite=None requires Secure).
Fix: set cookie_secure = true in config + enforce HTTPS-only access → issue resolved.

4 — Tailscale VPN (remote access without open ports)

Tailscale is installed on pfsense-vm (pfSense mirror VM on PVE-02). It acts as a subnet router for the VLAN 10 MGMT network: any authorised device can reach internal services without a single inbound port open on the internet router.

iPhone / MacBook (anywhere in the world)
       │ Tailscale (WireGuard underneath)
       ▼
pfsense-vm — Tailscale subnet router (VLAN 10 MGMT)
       │ subnet route 172.16.10.0/24
       ▼
VLAN 10 services (Vaultwarden, Grafana, Zabbix, NPM…)

Global Architecture

         ┌─────────────────────────────────────────────────────┐
         │          INTERNET                                   │
         │  vaultwarden.[my-domain.fr] = NXDOMAIN public ✗   │
         └──────────────┬──────────────────────────────────────┘
                        │ Tailscale only
                        ▼
         iPhone / MacBook (Tailscale client)
                        │ WireGuard encrypted
                        ▼
         pfsense-vm (subnet router → VLAN 10 MGMT)
                        │
                        ▼
         AdGuard Home (VLAN 10) ── DNS split-horizon
         ↓ DoT                     *.[my-domain.fr] → NPM
         Cloudflare 1.1.1.1:853    *.homelab.lan → Unbound
                        │
                        ▼
         NPM (VLAN 10) ── wildcard SSL *.[my-domain.fr]
                        │
                        ▼
         Vaultwarden (VLAN 10 :8080) ── SQLite, pinned image

Skills Demonstrated

This project covers personal data and secret protection (B3.1 — Vaultwarden vault, end-to-end Bitwarden encryption), digital identity preservation (B3.2 — VPN-only access, public NXDOMAIN, strong authentication), device and usage hardening (B3.3 — DNS over TLS, no secrets in plaintext, NPM HSTS), and infrastructure cybersecurity (B3.5 — Tailscale zero-trust, AdGuard ACLs), as well as online presence development (B1.3 — wildcard FQDN *.[my-domain.fr], automated SSL) and service availability (B1.5 — Vaultwarden, NPM, DNS available 24/7 with no public exposure).