echo6-docs/runbooks/idahomesh-bridge-setup.md
Matt Johnson e9231ac24a Migration: consolidate Echo6 docs to cortex with full infrastructure cleanup sync
- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup)
- Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing
- Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack
- Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure
- Removes 4 deprecated runbook duplicates (canonical versions live in projects/)
- Adds .gitignore for binary archives and editor temp files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 06:02:16 +00:00

9.3 KiB

IdahoMesh Bridge Setup

Build a one-way bridge between your tailnet and the IdahoMesh Meshtastic network. This lets your devices reach Nebra gateways through IdahoMesh, while preventing IdahoMesh from reaching back into your network.


Architecture

Your Tailnet (your Headscale/Tailscale)
       ↓  (one-way only)
  [Bridge Machine]  ← dual tailscaled, NAT + firewall
       ↓
IdahoMesh Tailnet (100.100.0.0/16)
       ↕
  Nebra CM3 Gateways (Meshtastic nodes)

Security model: Your devices can reach Nebras. Nothing on IdahoMesh can initiate connections back into your network. NAT masquerades your source IPs so IdahoMesh only sees the bridge's IP.


Prerequisites

Item Value
IdahoMesh Headscale URL https://vpn.idahomesh.com
IdahoMesh prefix 100.100.0.0/16
Your preauthkey Provided by IdahoMesh admin

You also need:

  1. A Linux machine on your network (VM, Pi, bare metal — anything running systemd)
  2. Root access on that machine
  3. Internet access (to reach vpn.idahomesh.com)
  4. Your own tailnet's connection details (Headscale URL, or stock Tailscale if using tailscale.com)

Step 1: Install Tailscale

curl -fsSL https://tailscale.com/install.sh | sh

This installs both tailscale and tailscaled. The default service (tailscaled.service) will handle your primary tailnet.


Step 2: Set Up Dual tailscaled

You need two Tailscale daemon instances — one for your tailnet, one for IdahoMesh. The default tailscaled service handles your tailnet. Create a second service for IdahoMesh.

Create directories

mkdir -p /var/lib/tailscale-meshtastic /var/run/tailscale-meshtastic

Create the second tailscaled service

Write /etc/systemd/system/tailscaled-meshtastic.service:

[Unit]
Description=Tailscale daemon (IdahoMesh tailnet)
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/sbin/tailscaled \
  --state=/var/lib/tailscale-meshtastic/tailscaled.state \
  --socket=/var/run/tailscale-meshtastic/tailscaled.sock \
  --port=41642 \
  --tun=tailscale1
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

Critical: The --tun=tailscale1 flag is required. Both instances cannot use the default tailscale0 TUN device — the second one will fail with "TUN device tailscale0 is busy" if you omit this.

Enable and start it:

systemctl daemon-reload
systemctl enable --now tailscaled-meshtastic

Step 3: Enable IP Forwarding

cat > /etc/sysctl.d/99-bridge.conf << 'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF

sysctl -p /etc/sysctl.d/99-bridge.conf

Step 4: Join Both Tailnets

Join your tailnet (default tailscaled)

Advertise the IdahoMesh range so your other devices can route to Meshtastic nodes through this bridge:

# If you use your own Headscale:
tailscale up \
  --login-server=https://YOUR_HEADSCALE_URL \
  --advertise-routes=100.100.0.0/16 \
  --accept-routes

# If you use stock Tailscale (tailscale.com):
tailscale up \
  --advertise-routes=100.100.0.0/16 \
  --accept-routes

After joining, you need to approve the advertised route on your tailnet's admin:

  • Headscale: headscale routes list then headscale routes enable -r <route-id>
  • Stock Tailscale: Go to admin console → Machines → your bridge → approve the 100.100.0.0/16 subnet route

Join IdahoMesh (second tailscaled)

tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock up \
  --login-server=https://vpn.idahomesh.com \
  --authkey=YOUR_IDAHOMESH_PREAUTHKEY \
  --accept-routes

Important: Do NOT advertise your tailnet's routes on IdahoMesh. The bridge is one-way — IdahoMesh should have no route back into your network.


Step 5: Configure One-Way Firewall and NAT

This is the critical security step. Your tailnet can reach IdahoMesh, but nothing on IdahoMesh can reach back into your network.

Replace YOUR_TAILNET_PREFIX below with your tailnet's IP range. Common values:

Tailnet type Prefix
Stock Tailscale 100.64.0.0/10
Custom Headscale Check your config.yamlprefixes.v4

Apply iptables rules

# NAT: Masquerade your source IPs when going to IdahoMesh
# Nebras see the bridge's IdahoMesh IP, not your real tailnet IPs
iptables -t nat -A POSTROUTING -s YOUR_TAILNET_PREFIX -d 100.100.0.0/16 -j MASQUERADE

# Allow your tailnet → IdahoMesh (outbound)
iptables -A FORWARD -s YOUR_TAILNET_PREFIX -d 100.100.0.0/16 -j ACCEPT

# Allow established/related return traffic only (responses to connections you initiated)
iptables -A FORWARD -s 100.100.0.0/16 -d YOUR_TAILNET_PREFIX -m state --state ESTABLISHED,RELATED -j ACCEPT

# DROP all new connections from IdahoMesh → your tailnet
iptables -A FORWARD -s 100.100.0.0/16 -d YOUR_TAILNET_PREFIX -j DROP

Persist rules across reboots

Do not use iptables-persistent — it hangs on install even with noninteractive mode. Use manual persistence instead:

# Save current rules
mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4

Create /etc/systemd/system/iptables-restore.service:

[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Enable it:

systemctl daemon-reload
systemctl enable iptables-restore

Verify rules

iptables -L FORWARD -v -n
iptables -t nat -L POSTROUTING -v -n

You should see:

  • MASQUERADE rule on POSTROUTING
  • ACCEPT for your prefix → 100.100.0.0/16
  • ACCEPT ESTABLISHED,RELATED for 100.100.0.0/16 → your prefix
  • DROP for 100.100.0.0/16 → your prefix

Step 6: Enable Routes on Your Other Devices

Any device on your tailnet that wants to reach IdahoMesh Nebras through the bridge needs to accept subnet routes:

# On each device that needs access
tailscale set --accept-routes

Without this, traffic to 100.100.0.x goes to the default gateway instead of through the Tailscale tunnel.


Step 7: Verify

Check both tailscaled instances

# Your tailnet
tailscale status

# IdahoMesh
tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock status

Both should show "online" with peers listed.

Ping a Nebra gateway

# From the bridge itself, via IdahoMesh socket
tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock ping burley-butte

Ping from another device on your tailnet

# From any device on your tailnet (with --accept-routes enabled)
# Use the Nebra's IdahoMesh IP
ping 100.100.0.3   # Burley Butte

Verify isolation

From an IdahoMesh device or ask the admin to test — pinging your tailnet IPs from IdahoMesh should time out / be unreachable.


Troubleshooting

Second tailscaled won't start

  • Check journalctl -u tailscaled-meshtastic -f
  • Most common: forgot --tun=tailscale1 — both instances fighting over tailscale0
  • Verify the port isn't in conflict: default uses 41641, second uses 41642

Can't reach Nebras from other devices on your tailnet

  • Verify the bridge advertises 100.100.0.0/16: tailscale status should show it as a subnet router
  • Approve the route on your tailnet admin (Headscale or Tailscale admin console)
  • Enable --accept-routes on the client device trying to reach Nebras

IdahoMesh preauthkey expired

  • Contact the IdahoMesh admin for a new key
  • Re-join: tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock up --login-server=https://vpn.idahomesh.com --authkey=NEW_KEY --accept-routes

After reboot, only one tailscaled reconnects

  • Check both services: systemctl status tailscaled and systemctl status tailscaled-meshtastic
  • Verify iptables rules survived: iptables -L FORWARD -v -n
  • If the second instance lost state, re-join IdahoMesh with a new preauthkey

Default tailscaled pointed at wrong server after force-reauth

  • If you run tailscale up --force-reauth without specifying --login-server, it may reconnect to the wrong Headscale
  • Always specify --login-server explicitly when re-authing:
    • Default instance: tailscale up --login-server=https://YOUR_HEADSCALE_URL --force-reauth
    • IdahoMesh instance: tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock up --login-server=https://vpn.idahomesh.com --force-reauth

Quick Reference

Item Value
IdahoMesh URL https://vpn.idahomesh.com
IdahoMesh prefix 100.100.0.0/16
IdahoMesh socket /var/run/tailscale-meshtastic/tailscaled.sock
IdahoMesh TUN device tailscale1
IdahoMesh port 41642
Default Tailscale socket /var/run/tailscale/tailscaled.sock
Default TUN device tailscale0
Default port 41641

Useful commands

# IdahoMesh status
tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock status

# IdahoMesh ping
tailscale --socket=/var/run/tailscale-meshtastic/tailscaled.sock ping <hostname>

# Check firewall rules
iptables -L FORWARD -v -n
iptables -t nat -L POSTROUTING -v -n

# Restart services
systemctl restart tailscaled                # Your tailnet
systemctl restart tailscaled-meshtastic     # IdahoMesh

Last updated: 2026-02-11