This repository contains the Go source code for a fully autonomous agent that creates and maintains a self-healing WireGuard mesh network. It is designed to run in a Docker container and uses a private Git repository as its single source of truth, following GitOps principles.
The agent removes the need for manual peer configuration. Each node in the network runs this agent, which allows it to automatically register itself, discover other peers, and heal the network by updating its public IP address as it changes.
- Zero-Touch Peer Provisioning: A new server, once launched, automatically generates its keys and registers itself in the central Git repository.
- Automatic IP Healing: The agent periodically checks its public IP address and pushes updates to Git, ensuring all peers can always find it.
- Dynamic Peer Discovery: Each agent regularly syncs with the Git repository, automatically adding, updating, and removing peers from its configuration.
- GitOps-Driven Configuration: The Git repository provides a version-controlled, auditable history of your entire network's state.
- Intelligent Endpoint Selection: The agent is smart enough to determine the best way to connect to a peer, whether it's on the public internet, on the same local network, or even in another container on the same test machine.
Before deploying the agent, you need two GitHub repositories and two sets of credentials.
This is the private repository that will store your network's state.
- Create a new private GitHub repository (e.g.,
backbone-config). - Initialize it with a
README.md. - In the repository, create an empty directory named
peer_datawith a.gitkeepfile inside it. The agents will populate this directory.
The agent needs an SSH key to be able to write to the backbone-config repository. A Deploy Key is the best practice as its permissions are limited to a single repository.
-
Generate a new SSH key pair on your local machine. This key will be shared by all your agents.
# Use ed25519 for modern security and shorter keys # The -f flag prevents overwriting your default key ssh-keygen -t ed25519 -C "server1-wg-manager" -f ./server1-wg-manager
When prompted for a passphrase, press Enter twice to leave it empty. This is required for automation. This will create two files:
server1-wg-manager(private) andserver1-wg-manager.pub(public). -
Add the Public Key to GitHub:
- Go to your
backbone-configrepository on GitHub. - Navigate to Settings > Deploy Keys.
- Click "Add deploy key".
- Title: Give it a clear name, like
Server1 Wireguard Agent. - Key: Copy the contents of your public key file and paste it here.
# On your Mac/Linux cat ./server1-wg-manager.pub
[!IMPORTANT] Check the box for "Allow write access". The agent needs this permission to register itself and update its IP address.
- Click "Add key".
- Go to your
-
Secure the Private Key: The
server1-wg-manager(the private key) is the file you will securely place on your servers. Treat this file like a password.
This repository includes a Makefile to simplify building the agent and publishing it to the GitHub Container Registry (GHCR).
To log in and push to GHCR, you need a Personal Access Token with the correct permissions.
- On GitHub, go to your Settings > Developer settings > Personal access tokens > Tokens (classic).
- Click "Generate new token (classic)".
- Note: Give your token a descriptive name (e.g., "GHCR Publish Token").
- Expiration: Set an expiration date as a security best practice.
- Scopes: CRITICAL: Check the box for
write:packages. This permission is required to upload container images. - Click "Generate token" and copy the token immediately. You will not see it again.
Place the provided Makefile in the root of this wireguard-manager repository.
-
Set Environment Variables: Before running
make, you must export your GitHub username and the PAT you just created (can add to your shell profile for convenience).# Replace with your actual GitHub username and token export CR_USERNAME="jcbasso" export CR_PAT="ghp_..."
-
Run the Commands:
make help: Shows a list of all available commands.make login: Tests your credentials by logging into GHCR.make build: Builds the Docker image locally.make push: Pushes the locally built image to GHCR.make publish: The all-in-one command. It will log in, build, and push the image.
The agent determines the best network path to connect to each peer by following these rules in order:
-
Different Public IPs? (WAN Scenario)
- If a peer's
public_ipis different from the agent's own public IP, it will always use the peer'spublic_ipfor the endpoint.
- If a peer's
-
Same Public IP? (LAN / Local Test Scenario)
- If a peer's
public_ipis the same, the agent then checks theSERVER_IDENTIFIER. - Are the
SERVER_IDENTIFIERs the same?- This means both agents are running on the same host machine.
- If the
DOCKER_CONTAINERvariable is set, it will use the peer's Docker container name as the endpoint (e.g.,wireguard-jc2). This is for local testing.
- Are the
SERVER_IDENTIFIERs different?- This means the peers are on different host machines but on the same LAN.
- The agent will use the peer's
public_ip. This requires the router to have correct port forwarding rules and NAT Hairpinning/Loopback support.
- If a peer's
The agent is configured entirely through environment variables.
| Variable | Required? | Example | Description |
|---|---|---|---|
PEER_NAME |
Yes | wireguard-server-1 |
A unique name for this node. For local testing, this should match the container name. |
BACKBONE_IP |
Yes | 10.100.0.1 |
The private IP address this node will use within the WireGuard backbone network. |
WIREGUARD_PORT |
Yes | 51820 |
The public-facing UDP port that this node will listen on. |
GIT_REPO_URL |
Yes | git@github.com:user/backbone-config.git |
The SSH URL of your private Git repository. |
GIT_SSH_KEY_PATH |
Yes | /run/secrets/server1-wg-manager |
The path inside the container to the mounted private SSH deploy key. |
SERVER_IDENTIFIER |
Yes | my-macbook-pro or aws-us-east-1a |
A manually set, unique identifier for the host machine this agent is running on. |
DOCKER_CONTAINER |
Optional | wireguard-jc1 |
The Docker container name. Used only for same-host endpoint logic. |
This example shows how to deploy a backbone node on a production server using the secure file-based secret method.
On your server, create the following structure:
/opt/backbone-node/
├── docker-compose.yml
├── .env
└── secrets/
└── server1-wg-manager # Your private SSH deploy key file
Place your private key file inside the secrets directory.
[!CRITICAL] Set strict permissions on the key file:
bash sudo chown root:root /opt/backbone-node/secrets/server1-wg-manager sudo chmod 600 /opt/backbone-node/secrets/server1-wg-manager
docker-compose.yml File
services:
wireguard:
image: ghcr.io/jcbasso/wireguard-manager:latest
container_name: wireguard
restart: unless-stopped
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
volumes:
- ./wireguard-config:/etc/wireguard
- ./secrets:/run/secrets:ro
ports:
- "${WIREGUARD_PORT}:${WIREGUARD_PORT}/udp"
environment:
- PEER_NAME
- BACKBONE_IP
- GIT_REPO_URL
- GIT_SSH_KEY_PATH
- WIREGUARD_PORT
- SERVER_IDENTIFIER
# DOCKER_CONTAINER is omitted in production
postgres:
image: postgres:15-alpine
container_name: postgres
restart: unless-stopped
network_mode: "service:wireguard"
volumes:
- ./postgres-data:/var/lib/postgresql/data
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
depends_on:
- wireguard.env File
# --- WireGuard Agent Configuration ---
PEER_NAME=aws-node-1
BACKBONE_IP=10.100.0.1
WIREGUARD_PORT=51820
# A unique name for the physical/virtual server
SERVER_IDENTIFIER=my-macbook-pro
# --- Git Configuration ---
GIT_REPO_URL=git@github.com:jcbasso/backbone-config.git
GIT_SSH_KEY_PATH=/run/secrets/server1-wg-manager
# --- PostgreSQL Configuration ---
POSTGRES_USER=myuser
POSTGRES_PASSWORD=my-secure-password
POSTGRES_DB=mydbLaunch Command:
docker compose up -d