Set up the mesh¶
This playbook connects a node to the Junk Net
Nebula mesh, giving it a
permanent private IP (e.g. 10.42.1.11) that works from any home,
behind any router. The mesh architecture page
explains why it works; this page is the how.
Who signs what
Nodes generate their own keys, but certificates are signed by the project CA, which lives offline with the maintainer. In practice: you run the keygen step, send the public key over, and get a signed certificate back. The CA key never touches a node. (For a from-scratch community cluster, the CA creation step is included below.)
Install Nebula¶
Grab the latest release for your architecture from Nebula's releases page:
curl -LO https://github.com/slackhq/nebula/releases/latest/download/nebula-linux-amd64.tar.gz
tar -xzf nebula-linux-amd64.tar.gz
sudo mv nebula /usr/local/bin/
sudo mv nebula-cert /usr/local/bin/
sudo chown root:root /usr/local/bin/nebula /usr/local/bin/nebula-cert
sudo chmod 755 /usr/local/bin/nebula /usr/local/bin/nebula-cert
sudo mkdir -p /etc/nebula
One-time: create the CA (cluster founders only)¶
Done once per community, on a machine that is not a node, and the
resulting ca.key is kept offline:
nebula-cert ca -name "Junk Net Brisbane" -duration 87600h # 10 years
# Produces ca.crt (public — distributed to every node)
# and ca.key (PRIVATE — offline, guarded, never on a node)
One-time: stand up a lighthouse¶
The lighthouse is a tiny Nebula node on a stable public IP — the
cheapest VPS you can find is plenty (it coordinates; it doesn't carry
storage traffic). Sign it a certificate in the lighthouse range and
give it a config with am_lighthouse: true:
pki:
ca: /etc/nebula/ca.crt
cert: /etc/nebula/lighthouse-1.crt
key: /etc/nebula/lighthouse-1.key
static_host_map: {}
lighthouse:
am_lighthouse: true
listen:
host: 0.0.0.0
port: 4242 # open UDP 4242 in the VPS firewall
punchy:
punch: true
relay:
am_relay: true # fallback path for hostile NATs
firewall:
outbound:
- port: any
proto: any
host: any
inbound:
- port: any
proto: icmp
host: any
Per-node: keys and certificate¶
On the new node, generate a keypair; the CA signs the public half with the node's permanent mesh IP and its group:
# On the node — the private key never leaves this machine
nebula-cert keygen -out-key node.key -out-pub node.pub
# On the CA machine, against the received node.pub
# (IP allocated from the node range; groups drive the firewall)
nebula-cert sign -ca-crt ca.crt -ca-key ca.key \
-name "node-annerley-thinkpad" -ip "10.42.1.11/16" \
-groups "node" -in-pub node.pub
Install the three PKI files on the node:
sudo mv node.key node-annerley-thinkpad.crt ca.crt /etc/nebula/
sudo chown root:root /etc/nebula/*
sudo chmod 600 /etc/nebula/node.key
sudo chmod 644 /etc/nebula/*.crt
Per-node: configuration¶
pki:
ca: /etc/nebula/ca.crt
cert: /etc/nebula/node-annerley-thinkpad.crt
key: /etc/nebula/node.key
# Where to find the lighthouse in the real world
static_host_map:
"10.42.0.1": ["<lighthouse-public-ip>:4242"]
lighthouse:
am_lighthouse: false
interval: 60
hosts:
- "10.42.0.1"
listen:
host: 0.0.0.0
port: 0 # random port — kinder to home NATs
punchy:
punch: true # keep NAT mappings alive
respond: true # punch back for hard NATs
relay:
relays:
- 10.42.0.1 # fall back to the lighthouse if punching fails
use_relays: true
tun:
dev: nebula1
firewall:
conntrack:
tcp_timeout: 12m
udp_timeout: 3m
default_timeout: 10m
outbound:
- port: any
proto: any
host: any
inbound:
# Anyone on the mesh may ping us (debugging sanity)
- port: any
proto: icmp
host: any
# Fellow storage nodes may reach Garage's inter-node RPC
- port: 3901
proto: tcp
groups:
- node
# Gateways may reach the S3 API
- port: 3900
proto: tcp
groups:
- gateway
# Admin machines may reach everything Garage exposes
- port: 3900-3903
proto: tcp
groups:
- admin
That firewall is the whole security posture in one block: Garage's ports are reachable only by certificate-holding members of the right groups, and nothing else is reachable at all.
Run it as a service¶
sudo tee /etc/systemd/system/nebula.service > /dev/null <<'EOF'
[Unit]
Description=Nebula mesh (Junk Net)
Documentation=https://github.com/slackhq/nebula
Wants=network-online.target
After=network-online.target
[Service]
Type=notify
ExecStart=/usr/local/bin/nebula -config /etc/nebula/config.yml
Restart=on-failure
RestartSec=5
SyslogIdentifier=nebula
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now nebula.service
Order matters at boot: Garage binds its RPC address on the mesh, so make Garage start after Nebula by adding to the garage unit:
Verify¶
# The mesh interface exists and has our IP?
ip addr show nebula1
# Can we reach the lighthouse across the mesh?
ping -c 3 10.42.0.1
# Can we reach another node? (ask an operator for a live node IP)
ping -c 3 10.42.1.12
# Logs, if anything is unhappy
sudo journalctl -u nebula.service -f
First ping to a new peer may take a beat (that's the hole-punch handshake); after that, traffic flows directly between homes.
Point Garage at the mesh¶
The payoff — in /etc/garage.toml, the node advertises its Nebula IP:
From here the node is reachable by its peers and ready to join the cluster.
Troubleshooting¶
nebula1missing — the service didn't start; checkjournalctl -u nebula.servicefor cert path or YAML errors.- Can ping lighthouse but not peers — usually a hard NAT; confirm
punchy.respond: trueon both ends and that relays are configured. Traffic falling back to relay shows in the logs. - Cert errors — name, IP, and CA must all match what was signed;
nebula-cert print -path /etc/nebula/<node>.crtshows what a cert actually says. - Garage can't bind after reboot — Garage raced Nebula; confirm
the
After=nebula.servicedrop-in from above is in place.