204 lines
8.2 KiB
Markdown
204 lines
8.2 KiB
Markdown
---
|
|
schema_version: 1
|
|
id: home-server.networking
|
|
type: reference
|
|
scope: [jupiter, mercury, dns, ingress, wireguard, cloudflare]
|
|
sensitivity: private-infrastructure
|
|
last_reviewed: "2026-10-06"
|
|
sources:
|
|
- kind: owner-report
|
|
reference: "Network topology, Mercury nginx/iptables/fail2ban output, LAN DNS, 2026-10-06"
|
|
- kind: owner-report
|
|
reference: "Cloudflare ingress YAML and client SSH configurations, 2026-10-06"
|
|
- kind: owner-report
|
|
reference: "CGNAT rationale, Docker network and container inspection, 2026-10-06"
|
|
- kind: repository
|
|
repository: jupiter-stacks
|
|
revision: 610e325ef6a98850511ce7a089b0b9a77bfecf15
|
|
paths:
|
|
- stacks/traefik/docker-compose.yml
|
|
- stacks/cloudflared/docker-compose.yml
|
|
- stacks/gitea/docker-compose.yml
|
|
- stacks/jellyfin/docker-compose.yml
|
|
- stacks/audiobookshelf/docker-compose.yml
|
|
related: [home-server.reference, home-server.host, home-server.mercury, home-server.seedbox, home-server.authentication, home-server.operations]
|
|
update_triggers: [dns-change, proxy-change, tunnel-change, firewall-change, certificate-change]
|
|
unknowns:
|
|
- public-dns-record-types-and-cloudflare-proxy-status
|
|
- cloudflare-access-applications-and-login-policies
|
|
- wireguard-allowedips-and-keepalive
|
|
- jupiter-firewall-policy
|
|
- traefik-forwarded-header-trust-configuration
|
|
---
|
|
|
|
# Networking
|
|
|
|
## HTTPS ingress
|
|
|
|
```mermaid
|
|
---
|
|
config:
|
|
themeCSS: |
|
|
.node rect { rx: 8px; ry: 8px; stroke-width: 1.5px; }
|
|
.cluster rect { fill: var(--infra-zone) !important; stroke: var(--infra-border) !important; stroke-dasharray: 5 5; rx: 12px; ry: 12px; }
|
|
.label, .nodeLabel, .cluster-label { color: var(--infra-text) !important; }
|
|
.edgeLabel, .edgeLabel p { background-color: var(--infra-zone) !important; color: var(--infra-text) !important; }
|
|
.flowchart-link { stroke: var(--infra-line) !important; }
|
|
marker path { fill: var(--infra-line) !important; stroke: var(--infra-line) !important; }
|
|
.infraClient rect { fill: var(--infra-client-fill) !important; stroke: var(--infra-client-stroke) !important; }
|
|
.infraEdge rect { fill: var(--infra-edge-fill) !important; stroke: var(--infra-edge-stroke) !important; }
|
|
.infraService rect { fill: var(--infra-service-fill) !important; stroke: var(--infra-service-stroke) !important; }
|
|
flowchart:
|
|
curve: basis
|
|
nodeSpacing: 35
|
|
rankSpacing: 55
|
|
---
|
|
flowchart TB
|
|
Internet["<b>Public clients</b>"]
|
|
|
|
subgraph VPS["MERCURY / PUBLIC EDGE"]
|
|
Nginx["<b>nginx</b><br/>185.230.217.66<br/>WireGuard 10.0.0.1"]
|
|
Uptime["<b>Uptime service</b><br/>127.0.0.1:3001"]
|
|
Nginx -->|"uptime.umbra.mom / HTTP"| Uptime
|
|
end
|
|
|
|
subgraph Home["JUPITER / HOME SERVER"]
|
|
LAN["<b>LAN clients</b><br/>Jellyfin / Bookshelf local DNS"]
|
|
Traefik["<b>Traefik</b><br/>192.168.1.56<br/>WireGuard 10.0.0.2"]
|
|
Apps["<b>Application containers</b><br/>Docker network: traefik"]
|
|
LAN -->|"HTTPS :443"| Traefik
|
|
Traefik -->|"Application routing"| Apps
|
|
end
|
|
|
|
Internet -->|"HTTPS :443"| Nginx
|
|
Nginx -->|"HTTPS :443 over WireGuard"| Traefik
|
|
|
|
class Internet,LAN infraClient
|
|
class Nginx,Traefik infraEdge
|
|
class Apps,Uptime infraService
|
|
```
|
|
|
|
## Cloudflare Tunnel
|
|
|
|
```mermaid
|
|
---
|
|
config:
|
|
themeCSS: |
|
|
.node rect { rx: 8px; ry: 8px; stroke-width: 1.5px; }
|
|
.cluster rect { fill: var(--infra-zone) !important; stroke: var(--infra-border) !important; stroke-dasharray: 5 5; rx: 12px; ry: 12px; }
|
|
.label, .nodeLabel, .cluster-label { color: var(--infra-text) !important; }
|
|
.edgeLabel, .edgeLabel p { background-color: var(--infra-zone) !important; color: var(--infra-text) !important; }
|
|
.flowchart-link { stroke: var(--infra-line) !important; }
|
|
marker path { fill: var(--infra-line) !important; stroke: var(--infra-line) !important; }
|
|
.infraClient rect { fill: var(--infra-client-fill) !important; stroke: var(--infra-client-stroke) !important; }
|
|
.infraCloud rect { fill: var(--infra-cloud-fill) !important; stroke: var(--infra-cloud-stroke) !important; }
|
|
.infraEdge rect { fill: var(--infra-edge-fill) !important; stroke: var(--infra-edge-stroke) !important; }
|
|
.infraService rect { fill: var(--infra-service-fill) !important; stroke: var(--infra-service-stroke) !important; }
|
|
flowchart:
|
|
curve: basis
|
|
nodeSpacing: 35
|
|
rankSpacing: 45
|
|
---
|
|
flowchart TB
|
|
Shell["<b>SSH client</b><br/>Host jupiter / user jay"]
|
|
Git["<b>Git over SSH</b><br/>Host git.umbra.mom / user git"]
|
|
Cloud["<b>Cloudflare Tunnel edge</b><br/>Access policy unconfirmed"]
|
|
|
|
subgraph Home["JUPITER / HOME SERVER"]
|
|
Connector["<b>cloudflared</b><br/>Locally configured ingress"]
|
|
SSH["<b>Host sshd</b><br/>192.168.1.56:22"]
|
|
Gitea["<b>Gitea SSH</b><br/>192.168.1.56:2222"]
|
|
Connector -->|"ssh.umbra.mom / SSH :22"| SSH
|
|
Connector -->|"git.umbra.mom / SSH :2222"| Gitea
|
|
end
|
|
|
|
Shell -->|"cloudflared access ssh / ssh.umbra.mom"| Cloud
|
|
Git -->|"cloudflared access ssh / git.umbra.mom"| Cloud
|
|
Cloud <-->|"Tunnel traffic / initiated outbound by Jupiter"| Connector
|
|
|
|
class Shell,Git infraClient
|
|
class Cloud infraCloud
|
|
class Connector infraEdge
|
|
class SSH,Gitea infraService
|
|
```
|
|
|
|
Client `cloudflared` transports SSH through Cloudflare; Jupiter's connector opens
|
|
the outbound tunnel. Origins still perform SSH authentication. No Mercury,
|
|
WireGuard, or Traefik hop on this path; no home-router port forwarding.
|
|
|
|
| Ingress hostname | Origin | Client SSH user |
|
|
| --- | --- | --- |
|
|
| `ssh.umbra.mom` | `ssh://192.168.1.56:22` | `jay` |
|
|
| `git.umbra.mom` | `ssh://192.168.1.56:2222` | `git` |
|
|
| Unmatched | `http_status:404` | N/A |
|
|
|
|
Both client configurations use `IdentityFile ~/.ssh/id_ed25519`.
|
|
|
|
```sshconfig
|
|
Host jupiter
|
|
User jay
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
ProxyCommand cloudflared access ssh --hostname ssh.umbra.mom
|
|
|
|
Host git.umbra.mom
|
|
User git
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
ProxyCommand cloudflared access ssh --hostname %h
|
|
```
|
|
|
|
Local tunnel configuration is separate from Cloudflare Access applications.
|
|
Access login/provider/allow rules remain unconfirmed; `ProxyCommand` alone
|
|
does not establish enforcement.
|
|
|
|
Ingress source: host `/data/cloudflared/config.yaml`, mounted at
|
|
`/etc/cloudflared/config.yaml`.
|
|
|
|
## DNS / routes
|
|
|
|
| DNS / route | Destination |
|
|
| --- | --- |
|
|
| Public service ingress | Mercury `185.230.217.66` |
|
|
| `jellyfin.local.umbra.mom` | Jupiter `192.168.1.56` |
|
|
| `bookshelf.local.umbra.mom` | Jupiter `192.168.1.56` |
|
|
| `ssh.umbra.mom`, `git.umbra.mom` | Cloudflare Tunnel |
|
|
| `uptime.umbra.mom` | Mercury nginx -> `http://127.0.0.1:3001` |
|
|
|
|
## Mercury
|
|
|
|
[Mercury VPS](mercury.md): native nginx; WireGuard server `10.0.0.1/24`;
|
|
Jupiter client `10.0.0.2/24`. Hosting, TLS, firewall and fail2ban details are
|
|
maintained on that page.
|
|
|
|
Home ISP uses CGNAT. Mercury supplies a static public IPv4 and forwards over
|
|
WireGuard; owner preference is to avoid public ingress via the residential IP.
|
|
|
|
## Observed Docker networks
|
|
|
|
Snapshot from supplied inspection, not fixed-address configuration. Container
|
|
IPs may change; use Docker service names.
|
|
|
|
| Network | IPv4 subnet | Internal | Observed role |
|
|
| --- | --- | --- | --- |
|
|
| `traefik` | `172.18.0.0/16` | No | Reverse proxy and application ingress |
|
|
| `gitea_network` | `172.23.0.0/16` | No | Gitea, runner, notes builder, Havox sync |
|
|
| `cloudflared_cloudflare` | `172.26.0.0/16` | No | Tunnel connector |
|
|
| `gitea_data` | `192.168.96.0/20` | Yes | Gitea and PostgreSQL |
|
|
| `paperless_data` | `192.168.112.0/20` | Yes | Paperless, PostgreSQL, Redis |
|
|
| `dozzle_dozzle` | `172.27.0.0/16` | Yes | Dozzle and socket proxy |
|
|
|
|
Cloudflared's observed `172.26.0.2` matches the supplied proxied SSH login source.
|
|
An internal network alone does not isolate a multi-network container from
|
|
egress through its other networks. Empty network entries do not establish
|
|
whether they are obsolete or safe to remove.
|
|
|
|
## Jupiter: repository configuration
|
|
|
|
- Traefik publishes TCP `80,443`; HTTP redirects to HTTPS.
|
|
- Docker provider: opt-in `traefik.enable=true`; external network `traefik`.
|
|
- File provider: `/data/traefik/dynamic`.
|
|
- ACME: Let's Encrypt; Cloudflare DNS-01; `/data/traefik/acme/acme.json`;
|
|
apex + `*.umbra.mom` certificate requested.
|
|
- HTTPS fallback: priority `1`; unmatched non-apex hosts redirect to `https://umbra.mom`.
|
|
- `cloudflared`: `/data/cloudflared` -> `/etc/cloudflared`;
|
|
stack-local bridge `cloudflare`; no published ports.
|