# Automatic L2 Wireguard Mesh Deployment
This is a small and opinionated tool that assists with the bring-up and
management of L2-bridged Wireguard mesh networks using only SSH.
It is intended to be used within IaC systems, CI pipelines, or other
automation sources able to provide state management & idempotent deployments.
Personally, I've developed this package for use with Pulumi and RKE to create my infrastructure network,
which spans across two LANs and multiple (Linode) cloud nodes.
I wanted to minimize configuration requirements to the bare minimum, so the only thing needed to get a
mesh going with this tool is a `mesh.yaml` file defining a minimal set of parameters for each node.
Otherwise, users are only expected to pre-configure SSH connectivity and any necessary NAT UDP ports mappings for Wireguard.
## Features
- Fully-connected topology or user-defined peering.
- SSH-based peer configuration.
- Automatic public, private and pre-shared key generation.
- The configuring node only needs remote root/sudo access, and does not store private keys or any additional state.
- Peerwise `gretap`/`ip6gretap` L2 links over Wireguard.
- `iproute2`-based bridging with STP enabled and configurable priorities.
### Non-Features
What this doesn't do:
- Dynamically add/remove nodes or rotate keys without bringing down the mesh.
- Configure IP forwarding, Internet routing, or DNS.
- Support clients, gateways/egress/ingress, or anything other than a fully- or mostly-connected mesh of servers.
### TODO
- [ ] Add `--dry-run` flag to preview changes.
- [ ] Add more accessibility to Wireguard config options.
- [ ] Support some method of manual insecure interface bridging, e.g. for VPCs/VLANs.
## How It Works
Because Wireguard doesn't support L2 or have the ability to route the same subnet across AllowedIPs for multiple peers,
creating a fully connected mesh at L2 requires a GRE tunnel to each peer, and a bridge for all peer tunnels on each node.
```
+-------------------------+ +-------------------------+
| node1: | | nodeN: |
| +-------------+ | | +-------------+ |
| | bridge | | | | bridge | |
| | +---------+ | +----+ | | +----+ | +---------+ | |
| | | gretap1 |<-->| | | | | |<-->| gretap1 | | |
| | +---------+ | | | | | | | | +---------+ | |
| | ... | | wg |<---->| wg | | ... | |
| | +---------+ | | | | | | | | +---------+ | |
| | | gretapN |<-->| | | | | |<-->| gretapN | | |
| | +---------+ | +----+ | | +----+ | +---------+ | |
| +-------------+ | | +-------------+ |
+-------------------------+ +-------------------------+
\ /
\--------- ssh ---------/
|
+----------------------+
| wireguard-mesh host |
+----------------------+
```
In this diagram, the bridge on each node has an assigned IP address within the defined network, and
STP prevents routing loops and advertisement floods that would otherwise be caused by multiple redundant links.
The Wireguard peer addresses are randomly chosen by default and only used for GRE tunneling.
### Example `mesh.yaml`
Configuration files can also be provided from `stdin`, and JSON-formatting is available with the `-j` flag.
```yaml
name: test
network: fd00:0:0:1::/64
full: false # Optional: default is true. Set to false for manual peering.
nodes:
mesh0:
addr: fd00:0:0:1::1/64
ssh: test0
endpoint: lan1.example.com
peers: # When unset or empty and full=false, all other nodes are peered.
- mesh1
- mesh2
- mesh3
mesh1:
addr: fd00:0:0:1::2/64
ssh: test1
endpoint: lan1.example.com:51821
mesh2:
addr: fd00:0:0:1::3/64
ssh: test2
endpoint: lan2.example.com
listen_port: 51850
mesh3:
addr: fd00:0:0:1::4/64
ssh: test3
endpoint: test3.cloud.example.com
prio: 100
```
Prior versions supported auto-assignment of addresses, but this has been removed in favor of explicit configuration.
Each node's `addr` field must be a unique interface address within the network.
# Usage
Create a `mesh.yaml` as shown above, and then run:
```shell
mesh up
```
Show mesh info:
```shell
mesh info
```
Bring down the mesh and remove configs:
```shell
mesh down -r
```
### Full Usage
```commandline
usage: mesh [-h] [-j] [-J] [-f FILE] [-q] COMMAND ...
Wireguard mesh network manager.
options:
-h, --help show this help message and exit
-j, --json Input JSON instead of YAML (default: use file ext).
-J, --json-out Output JSON instead of YAML.
-f FILE, --file FILE Mesh configuration file or - for stdin (default: mesh.yaml).
-q, --quiet Suppress output.
commands:
COMMAND Command:
up - Bring up mesh.
down - Bring down mesh.
sync - Sync mesh.
show - Show mesh Wireguard info.
info - Show mesh network info.
-------------------------------
usage: mesh up [-h] [-i]
options:
-h, --help show this help message and exit
-i, --info Show mesh info after bringing up.
-------------------------------
usage: mesh down [-h] [-r]
options:
-h, --help show this help message and exit
-r, --remove Remove Wireguard interface configs.
```
## System Requirements
Before deploying, the following is expected:
- This package is installed on the configuring host.
- Root or sudo access is available via SSH on all nodes.
- Typically defined as entries in `~/.ssh/config`.
- Specified by a host/connection string or keyword argument dictionary for [`fabric.Connection`](https://docs.fabfile.org/en/latest/api/connection.html).
- Wireguard and `wg-quick` from `wireguard-tools` are installed on all nodes.
- `iproute2`: `ip6gretap` and `bridge` capability
- Optional: `netcat` and shell `/dev/udp` capability for reachability testing.
### Network/Firewall:
This tool assumes that we have:
- SSH accessibility from the configuring host to every node
- Wireguard Endpoint UDP NAT ports mapped to LAN nodes
## Notes
A _static_ mesh is defined here as a configuration that does not change between creation and
deletion. This tool is capable of discerning the mesh state from the configuration
and has some ability to handle new peers, but deconfiguring/removing peers will leave dangling configuration files.
Hence, the recommended method to handle mesh changes is to perform a full replacement:
bring down the mesh from the existing configuration, and recreate from the new
configuration. This has the side effect of generating all new private and pre-shared keys as well.
### Scalability
For a mesh of `N` fully-connected nodes, each node requires one Wireguard server with `N-1` peers,
`N-1` GRE tunnels, and one bridge interface for the tunnels.
#### Considerations:
- STP will likely be very slow to learn/adapt with large `N` if links change state frequently.
- Sometimes, STP doesn't find the best path between LAN-local nodes and ends up
adding unnecessary multi-hop latency, even with only 3 nodes.
- The newly added `prio` node field allows setting of bridge priorities. Defaults are mapped to node indices, which lessens
the likelihood of arbitrarily bad routing decisions by ensuring that the 0-indexed node is the most likely root node (out of every 16).
- There are better dynamic routing options, but STP is the simplest to enable.
- Kernel limitations might prevent a large number of `ip6gretap` links?
#### Performance:
- Links take a slight MTU hit for the GRE tunnels, on top of Wireguard's 80 bytes.
- Latency differences in ping times were under 500 microseconds on average,
when comparing the same LAN vs meshed-over-LAN links.
- LAN-local peers can sometimes find local endpoints for clients, even when the original endpoints
referred to the same NAT gateway.
- STP can't provide any minimum latency or best-path guarantees.
- `iperf` measurements TBD.
### Security
Every Wireguard peering is automatically assigned a preshared key when the mesh is created, and each node's
private keys are automatically generated as well.
Keys are never saved anywhere other than each nodes `/etc/wireguard/` config, and only loaded into memory when working
with meshes that were previously configured.
However, access to all keys is necessarily possible via SSH when configured for this tool,
so SSH identities should be properly protected within production environments.
## Reference
### Articles
- [Wireguard L2 gist](https://gist.github.com/zOrg1331/a2a7ffb3cfe3b3b821d45d6af00cb8f6)
- [Comparison of Wireguard mesh tools](https://github.com/HarvsG/WireGuardMeshes)
### Tools & Libraries
- [`wireguard-tools`](https://github.com/cmusatyalab/wireguard-tools)
- Python library used here for configuration management.
- Netlink-capable: Useful for development of active Wireguard management solutions.
- [`wg-meshconf`](https://github.com/k4yt3x/wg-meshconf)
- (Python) CSV-based mesh management.
Raw data
{
"_id": null,
"home_page": "https://github.com/sitbon/wireguard-mesh",
"name": "wireguard-mesh",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.11,<4.0",
"maintainer_email": "",
"keywords": "wireguard,mesh,vpn,networking",
"author": "Phillip Sitbon",
"author_email": "phillip.sitbon@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/32/fb/b3e666f99c57f0c626e547b5815de7d341a5fbc21b0abe32c2460dc2e190/wireguard_mesh-0.2.5.tar.gz",
"platform": null,
"description": "# Automatic L2 Wireguard Mesh Deployment\n\nThis is a small and opinionated tool that assists with the bring-up and\nmanagement of L2-bridged Wireguard mesh networks using only SSH.\nIt is intended to be used within IaC systems, CI pipelines, or other\nautomation sources able to provide state management & idempotent deployments.\n\nPersonally, I've developed this package for use with Pulumi and RKE to create my infrastructure network,\nwhich spans across two LANs and multiple (Linode) cloud nodes.\n\nI wanted to minimize configuration requirements to the bare minimum, so the only thing needed to get a \nmesh going with this tool is a `mesh.yaml` file defining a minimal set of parameters for each node.\n\nOtherwise, users are only expected to pre-configure SSH connectivity and any necessary NAT UDP ports mappings for Wireguard.\n\n## Features\n- Fully-connected topology or user-defined peering.\n- SSH-based peer configuration.\n - Automatic public, private and pre-shared key generation.\n - The configuring node only needs remote root/sudo access, and does not store private keys or any additional state.\n- Peerwise `gretap`/`ip6gretap` L2 links over Wireguard.\n- `iproute2`-based bridging with STP enabled and configurable priorities.\n\n### Non-Features\nWhat this doesn't do:\n- Dynamically add/remove nodes or rotate keys without bringing down the mesh.\n- Configure IP forwarding, Internet routing, or DNS.\n- Support clients, gateways/egress/ingress, or anything other than a fully- or mostly-connected mesh of servers.\n\n### TODO\n\n- [ ] Add `--dry-run` flag to preview changes.\n- [ ] Add more accessibility to Wireguard config options.\n- [ ] Support some method of manual insecure interface bridging, e.g. for VPCs/VLANs.\n\n## How It Works\n\nBecause Wireguard doesn't support L2 or have the ability to route the same subnet across AllowedIPs for multiple peers,\ncreating a fully connected mesh at L2 requires a GRE tunnel to each peer, and a bridge for all peer tunnels on each node.\n```\n+-------------------------+ +-------------------------+\n| node1: | | nodeN: |\n| +-------------+ | | +-------------+ |\n| | bridge | | | | bridge | |\n| | +---------+ | +----+ | | +----+ | +---------+ | |\n| | | gretap1 |<-->| | | | | |<-->| gretap1 | | |\n| | +---------+ | | | | | | | | +---------+ | |\n| | ... | | wg |<---->| wg | | ... | |\n| | +---------+ | | | | | | | | +---------+ | |\n| | | gretapN |<-->| | | | | |<-->| gretapN | | |\n| | +---------+ | +----+ | | +----+ | +---------+ | |\n| +-------------+ | | +-------------+ |\n+-------------------------+ +-------------------------+\n \\ /\n \\--------- ssh ---------/\n |\n +----------------------+\n | wireguard-mesh host |\n +----------------------+\n```\nIn this diagram, the bridge on each node has an assigned IP address within the defined network, and\nSTP prevents routing loops and advertisement floods that would otherwise be caused by multiple redundant links.\nThe Wireguard peer addresses are randomly chosen by default and only used for GRE tunneling.\n\n### Example `mesh.yaml`\nConfiguration files can also be provided from `stdin`, and JSON-formatting is available with the `-j` flag.\n\n```yaml\nname: test\nnetwork: fd00:0:0:1::/64\nfull: false # Optional: default is true. Set to false for manual peering.\nnodes:\n mesh0:\n addr: fd00:0:0:1::1/64\n ssh: test0\n endpoint: lan1.example.com\n peers: # When unset or empty and full=false, all other nodes are peered.\n - mesh1\n - mesh2\n - mesh3\n mesh1:\n addr: fd00:0:0:1::2/64\n ssh: test1\n endpoint: lan1.example.com:51821\n mesh2:\n addr: fd00:0:0:1::3/64\n ssh: test2\n endpoint: lan2.example.com\n listen_port: 51850\n mesh3:\n addr: fd00:0:0:1::4/64\n ssh: test3\n endpoint: test3.cloud.example.com\n prio: 100\n```\n\nPrior versions supported auto-assignment of addresses, but this has been removed in favor of explicit configuration.\n\nEach node's `addr` field must be a unique interface address within the network.\n\n# Usage\n\nCreate a `mesh.yaml` as shown above, and then run:\n```shell\nmesh up\n```\n\nShow mesh info:\n```shell\nmesh info\n```\n\nBring down the mesh and remove configs:\n```shell\nmesh down -r\n```\n\n### Full Usage\n```commandline\nusage: mesh [-h] [-j] [-J] [-f FILE] [-q] COMMAND ...\n\nWireguard mesh network manager.\n\noptions:\n -h, --help show this help message and exit\n -j, --json Input JSON instead of YAML (default: use file ext).\n -J, --json-out Output JSON instead of YAML.\n -f FILE, --file FILE Mesh configuration file or - for stdin (default: mesh.yaml).\n -q, --quiet Suppress output.\n\ncommands:\n COMMAND Command:\n up - Bring up mesh.\n down - Bring down mesh.\n sync - Sync mesh.\n show - Show mesh Wireguard info.\n info - Show mesh network info.\n \n-------------------------------\nusage: mesh up [-h] [-i]\n\noptions:\n -h, --help show this help message and exit\n -i, --info Show mesh info after bringing up.\n \n-------------------------------\nusage: mesh down [-h] [-r]\n\noptions:\n -h, --help show this help message and exit\n -r, --remove Remove Wireguard interface configs.\n```\n\n## System Requirements\nBefore deploying, the following is expected:\n- This package is installed on the configuring host.\n- Root or sudo access is available via SSH on all nodes.\n - Typically defined as entries in `~/.ssh/config`.\n - Specified by a host/connection string or keyword argument dictionary for [`fabric.Connection`](https://docs.fabfile.org/en/latest/api/connection.html).\n- Wireguard and `wg-quick` from `wireguard-tools` are installed on all nodes.\n- `iproute2`: `ip6gretap` and `bridge` capability\n- Optional: `netcat` and shell `/dev/udp` capability for reachability testing.\n\n### Network/Firewall:\nThis tool assumes that we have:\n - SSH accessibility from the configuring host to every node\n - Wireguard Endpoint UDP NAT ports mapped to LAN nodes\n\n## Notes\n\nA _static_ mesh is defined here as a configuration that does not change between creation and\ndeletion. This tool is capable of discerning the mesh state from the configuration\nand has some ability to handle new peers, but deconfiguring/removing peers will leave dangling configuration files.\n\nHence, the recommended method to handle mesh changes is to perform a full replacement:\nbring down the mesh from the existing configuration, and recreate from the new\nconfiguration. This has the side effect of generating all new private and pre-shared keys as well.\n\n### Scalability\n\nFor a mesh of `N` fully-connected nodes, each node requires one Wireguard server with `N-1` peers,\n`N-1` GRE tunnels, and one bridge interface for the tunnels. \n\n#### Considerations:\n- STP will likely be very slow to learn/adapt with large `N` if links change state frequently.\n - Sometimes, STP doesn't find the best path between LAN-local nodes and ends up\n adding unnecessary multi-hop latency, even with only 3 nodes.\n - The newly added `prio` node field allows setting of bridge priorities. Defaults are mapped to node indices, which lessens\n the likelihood of arbitrarily bad routing decisions by ensuring that the 0-indexed node is the most likely root node (out of every 16).\n - There are better dynamic routing options, but STP is the simplest to enable.\n- Kernel limitations might prevent a large number of `ip6gretap` links?\n\n#### Performance:\n- Links take a slight MTU hit for the GRE tunnels, on top of Wireguard's 80 bytes.\n- Latency differences in ping times were under 500 microseconds on average,\n when comparing the same LAN vs meshed-over-LAN links.\n - LAN-local peers can sometimes find local endpoints for clients, even when the original endpoints\n referred to the same NAT gateway.\n - STP can't provide any minimum latency or best-path guarantees.\n- `iperf` measurements TBD.\n\n### Security\n\nEvery Wireguard peering is automatically assigned a preshared key when the mesh is created, and each node's\nprivate keys are automatically generated as well.\n\nKeys are never saved anywhere other than each nodes `/etc/wireguard/` config, and only loaded into memory when working\nwith meshes that were previously configured.\n\nHowever, access to all keys is necessarily possible via SSH when configured for this tool,\nso SSH identities should be properly protected within production environments.\n\n## Reference\n\n### Articles\n- [Wireguard L2 gist](https://gist.github.com/zOrg1331/a2a7ffb3cfe3b3b821d45d6af00cb8f6)\n- [Comparison of Wireguard mesh tools](https://github.com/HarvsG/WireGuardMeshes)\n\n\n### Tools & Libraries\n- [`wireguard-tools`](https://github.com/cmusatyalab/wireguard-tools)\n - Python library used here for configuration management.\n - Netlink-capable: Useful for development of active Wireguard management solutions.\n- [`wg-meshconf`](https://github.com/k4yt3x/wg-meshconf)\n - (Python) CSV-based mesh management. \n",
"bugtrack_url": null,
"license": "AGPLv3",
"summary": "A Wireguard L2 Mesh Network Automation Tool",
"version": "0.2.5",
"project_urls": {
"Homepage": "https://github.com/sitbon/wireguard-mesh",
"Repository": "https://github.com/sitbon/wireguard-mesh"
},
"split_keywords": [
"wireguard",
"mesh",
"vpn",
"networking"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2e8a2d6266f30728223b82a9dbf07025905b9ac53b8f9c4287c890f531a01511",
"md5": "c20c11a0fae15c353d8676313c5cdd56",
"sha256": "b5ea16905c2dc2c8326b16ff5c9be58d9773bce813e63656602ce74a5c0a59a9"
},
"downloads": -1,
"filename": "wireguard_mesh-0.2.5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "c20c11a0fae15c353d8676313c5cdd56",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11,<4.0",
"size": 17689,
"upload_time": "2023-12-11T04:37:39",
"upload_time_iso_8601": "2023-12-11T04:37:39.187251Z",
"url": "https://files.pythonhosted.org/packages/2e/8a/2d6266f30728223b82a9dbf07025905b9ac53b8f9c4287c890f531a01511/wireguard_mesh-0.2.5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "32fbb3e666f99c57f0c626e547b5815de7d341a5fbc21b0abe32c2460dc2e190",
"md5": "abc8d22e3838ec61d0e6889a4637d839",
"sha256": "a5e0bf365a2265d12108808d9bd042103150c07969c652c9af47e23239406c91"
},
"downloads": -1,
"filename": "wireguard_mesh-0.2.5.tar.gz",
"has_sig": false,
"md5_digest": "abc8d22e3838ec61d0e6889a4637d839",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11,<4.0",
"size": 14669,
"upload_time": "2023-12-11T04:37:41",
"upload_time_iso_8601": "2023-12-11T04:37:41.089892Z",
"url": "https://files.pythonhosted.org/packages/32/fb/b3e666f99c57f0c626e547b5815de7d341a5fbc21b0abe32c2460dc2e190/wireguard_mesh-0.2.5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-12-11 04:37:41",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "sitbon",
"github_project": "wireguard-mesh",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "wireguard-mesh"
}