Episode 5: NetBird mesh - control plane behind Caddy, Forgejo as IdP, services off the public internet
This commit is contained in:
parent
d5398caece
commit
8cebf425c6
3 changed files with 88 additions and 5 deletions
16
Caddyfile
16
Caddyfile
|
|
@ -21,11 +21,19 @@ status.{$DOMAIN} {
|
||||||
reverse_proxy uptime-kuma:3001
|
reverse_proxy uptime-kuma:3001
|
||||||
}
|
}
|
||||||
|
|
||||||
n8n.{$DOMAIN} {
|
# n8n has no public route any more - it is reachable over the mesh only (episode 5)
|
||||||
reverse_proxy n8n:5678
|
|
||||||
}
|
|
||||||
|
|
||||||
|
# NetBird control plane (episode 5) - dashboard, API, gRPC, relay, built-in IdP
|
||||||
vpn.{$DOMAIN} {
|
vpn.{$DOMAIN} {
|
||||||
reverse_proxy wg-easy:51821
|
# Native gRPC (needs HTTP/2 cleartext to backend)
|
||||||
|
@grpc header Content-Type application/grpc*
|
||||||
|
reverse_proxy @grpc h2c://netbird-server:80
|
||||||
|
|
||||||
|
# Combined server paths (relay, signal, management, OAuth2)
|
||||||
|
@backend path /relay* /ws-proxy/* /api/* /oauth2/*
|
||||||
|
reverse_proxy @backend netbird-server:80
|
||||||
|
|
||||||
|
# Dashboard (everything else)
|
||||||
|
reverse_proxy /* netbird-dashboard:80
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
# uptime-kuma monitoring + alerts
|
# uptime-kuma monitoring + alerts
|
||||||
# n8n automation hub
|
# n8n automation hub
|
||||||
# wg-easy WireGuard VPN (optional — start with: --profile vpn)
|
# wg-easy WireGuard VPN (optional — start with: --profile vpn)
|
||||||
# (full Netbird mesh gets its own episode)
|
# (the full NetBird mesh is in netbird/ — episode 5)
|
||||||
#
|
#
|
||||||
# Quickstart:
|
# Quickstart:
|
||||||
# 1. cp .env.example .env && edit it (domain, email, secrets)
|
# 1. cp .env.example .env && edit it (domain, email, secrets)
|
||||||
|
|
@ -22,6 +22,11 @@ name: selfhostedops-stack
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
proxy:
|
proxy:
|
||||||
|
driver_opts:
|
||||||
|
# Docker 28+ firewalls direct access to container IPs from other
|
||||||
|
# interfaces (raw PREROUTING drop per container). This lets the NetBird
|
||||||
|
# mesh interface reach the stack containers - see netbird/ (episode 5).
|
||||||
|
com.docker.network.bridge.trusted_host_interfaces: "wt0"
|
||||||
ci:
|
ci:
|
||||||
internal:
|
internal:
|
||||||
|
|
||||||
|
|
|
||||||
70
netbird/README.md
Normal file
70
netbird/README.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# NetBird - the mesh around the stack (episode 5)
|
||||||
|
|
||||||
|
Two containers: the dashboard and a combined server that carries the
|
||||||
|
management API, the signal service, a relay, STUN and a built-in identity
|
||||||
|
provider. Everything except one UDP port rides through the same Caddy as the
|
||||||
|
rest of the stack.
|
||||||
|
|
||||||
|
## Install (on the box, next to the stack)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /opt/netbird && cd /opt/netbird
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/netbirdio/netbird/main/infrastructure_files/getting-started.sh -o getting-started.sh
|
||||||
|
|
||||||
|
NETBIRD_DOMAIN=vpn.example.com \
|
||||||
|
NETBIRD_REVERSE_PROXY_TYPE=4 \
|
||||||
|
NETBIRD_EXTERNAL_PROXY_NETWORK=selfhostedops-stack_proxy \
|
||||||
|
NETBIRD_BIND_LOCALHOST_ONLY=true \
|
||||||
|
bash getting-started.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script renders `docker-compose.yml`, `config.yaml`, `dashboard.env` and
|
||||||
|
the exact Caddy block to paste (`caddyfile-netbird.txt` - the same block is
|
||||||
|
already in this repo's `Caddyfile`). Reload Caddy, then open
|
||||||
|
`https://vpn.example.com`: a one-time setup page asks for the first admin
|
||||||
|
account. That account lives in the built-in provider and is your break-glass
|
||||||
|
login - store the password in the vault from episode 4.
|
||||||
|
|
||||||
|
## Forgejo as the network's identity provider
|
||||||
|
|
||||||
|
1. Forgejo -> Site administration -> Applications -> new OAuth2 application.
|
||||||
|
Redirect URI: `https://vpn.example.com/oauth2/callback` (confidential client).
|
||||||
|
2. NetBird dashboard -> Settings -> Identity Providers -> Add Identity Provider.
|
||||||
|
Type: OIDC (Generic). Issuer: your Forgejo base URL. Paste client ID + secret.
|
||||||
|
3. The login page now shows "Continue with Forgejo". First-time users land in
|
||||||
|
*pending approval* until an admin approves them in the Team list.
|
||||||
|
|
||||||
|
## Machines join
|
||||||
|
|
||||||
|
Servers use setup keys (Settings -> Setup Keys -> Create Key):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
||||||
|
netbird up --setup-key <KEY> --management-url https://vpn.example.com:443
|
||||||
|
```
|
||||||
|
|
||||||
|
People just run `netbird up --management-url https://vpn.example.com:443`;
|
||||||
|
the CLI prints a device-code URL, and the browser side is the same
|
||||||
|
"Continue with Forgejo" button.
|
||||||
|
|
||||||
|
## Reaching the containers (and the Docker 28+ trap)
|
||||||
|
|
||||||
|
Dashboard -> Networks: add a network, add the compose subnet as a resource
|
||||||
|
(e.g. `172.18.0.0/16`), add the box itself as routing peer, add one access
|
||||||
|
policy. Then note the one line already present on the `proxy` network in
|
||||||
|
`compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
networks:
|
||||||
|
proxy:
|
||||||
|
driver_opts:
|
||||||
|
com.docker.network.bridge.trusted_host_interfaces: "wt0"
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker 28+ drops any packet to a container IP that does not arrive on
|
||||||
|
Docker's own bridge (a raw-table rule, so DOCKER-USER does not help). The
|
||||||
|
option above makes Docker write the accept rules itself. Changing it needs a
|
||||||
|
network recreate (`docker compose down && docker compose up -d`).
|
||||||
|
|
||||||
|
After that, delete a service's public block from the `Caddyfile`, reload
|
||||||
|
Caddy, and the service is reachable over the mesh only.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue