Role to deploy WireGuard Site-to-Site VPN setups.
Tested:
- Debian 11
- Raspbian 11
- Debian 12
# latest
ansible-galaxy role install git+https://github.com/ansibleguy/infra_wireguard
# from galaxy
ansible-galaxy install ansibleguy.infra_wireguard
# or to custom role-path
ansible-galaxy install ansibleguy.infra_wireguard --roles-path ./roles
# install dependencies
ansible-galaxy install -r requirements.yml
python3 -m pip install -r requirements.txt
Feel free to:
-
Open PRs
-
Start discussions
-
Open issues => after checking out the troubleshooting guide below!
-
Need professional support using Ansible or WireGuard? Contact us:
E-Mail: contact@oxl.at
Tel: +43 3115 40 900 0
Language: German or English
-
You want a simple Ansible GUI?
Check-out this Ansible WebUI
Here some detailed config examples and their results:
You can define your WireGuard topologies spanning multiple hosts or host-groups.
The role will filter the topologies to the ones the current target-host is a part of and configure those.
These peer-keys must match your ansible inventory-hostnames!
wireguard:
restart_on_change: true # allow the wg-services to be restarted on changes
topologies:
dc_nl:
type: 'single'
peers:
srv02:
Endpoint: 'srv02.wg.template.ansibleguy.net'
Address: '10.100.0.1/30'
srv03:
Endpoint: 'srv03.wg.template.ansibleguy.net'
Address: '10.100.0.2/30'
You might want to use 'ansible-vault' to encrypt the host-key files:
ansible-vault encrypt roles/ansibleguy.infra_wireguard/files/keys/some_file.key
Run the playbook:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml
Or if you have encrypted your keys:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml --ask-vault-pass
There are also some useful tags available:
- base
- config
- tunnels
- purge
If you only want to provision one of your topologies you can set the following variable at execution time:
ansible-playbook -K -D -i inventory/hosts.yml playbook.yml -e only_topo=TOPOLOGY_KEY
-
Package installation
- WireGuard
- Resolvconf (name resolution)
-
Configuration
-
Simplified configuration by the mapping of topologies
-
Supported topologies:
-
Keys
- Generating public/private key-pairs for each host in a topology (WG identifies peer by publicKey)
- Keys are written to the controller for consistency
-
Routing
- The routing is up to you to manage! You could enable the auto-added WG routes or add custom up-/down-scripts.
-
Default config:
- Saving private-key in file
- Disabling route auto-adding (anti-lockout & customization)
- Enabled syslog-logging with instance-identifiers
- Restarting wg-service on changes
-
Default opt-ins:
- Using PSK for additional security
- Purging of orphaned tunnels
-
Default opt-outs:
- Installation of 'resolvconf' for name-resolution override
- Traffic forwarding (router-like)
-
Features:
- Showing last logs if service re-/start fails
- Auto-Connect of dynamic peer
-
-
Note: this role currently only supports debian-based systems
-
Note: Most of the role's functionality can be opted in or out.
For all available options - see the default-config located in the main defaults-file!
-
Warning: Not every setting/variable you provide will be checked for validity. Bad config might break the role!
-
Warning: Be aware that the WireGuard up-/down-scripts are executed as the TUNNEL SERVICE goes up; NOT THE TUNNEL CONNECTION.
You might need to take this in consideration when planning/configuring your routes and metrics!
-
Info: You should keep your topology names short. And try not to use special characters => they will get removed automatically (except '_=+.-') so the key is a valid interface-name!
-
Info: Interfaces will get a prefix prepended: (can be changed as provided)
- single => wgs_
- star => wgx_
- mesh => wgm_
-
Info: How to run tests is described here
-
Info: The host-keys will be saved in the roles 'files' directory by default.
This key-directory can be changed using the 'controller_key_store' variable!
-
Info: If you are using OPNSense firewalls - you can use the ansibleguy.opnsense Ansible collection to manage those WireGuard tunnels.
If you encounter connectivity problems => please follow these steps to narrow down their source:
wg show all
If not:
-
The connection cannot be established - maybe a misconfiguration or some networking issue (firewall blocking traffic)
-
Check your WireGuard logs:
# 'wgs_ts2' is the WireGuard tunnel-interface in this example guy@srv:~# journalctl -u wg-quick@wgs_ts2 > -- Journal begins at Tue 2022-02-08 15:46:07 UTC, ends at Tue 2022-02-08 17:01:27 UTC. -- > Feb 08 16:12:31 test-ag-wg-s3 systemd[1]: Starting WireGuard via wg-quick(8) for wgs_ts2... > Feb 08 16:12:31 test-ag-wg-s3 wireguard_wgs_ts2[10698]: [#] ip link add wgs_ts2 type wireguard > Feb 08 16:12:31 test-ag-wg-s3 wireguard_wgs_ts2[10698]: [#] wg setconf wgs_ts2 /dev/fd/63
- Here are some common error-messages you might see when mis-configuring your tunnels:
- Error:
RTNETLINK answers: Address already in use
- Problem: each tunnel must use a unique port to listen on - you might have assigned a duplicate port (or forgot to set it to a custom one)
- Error:
failure in name resolution
- Problem: the dns-hostname the service is trying to connect to is not set (correctly) or your target-host has general problems resolving DNS
- Error: Tunnels are configured, services are running, but connection isn't up
- Problem: the connection port might be blocked by a firewall
- Error:
- Here are some common error-messages you might see when mis-configuring your tunnels:
Ping the remote WireGuard tunnel ip - in the configuration this is the 'Address'.
Important: define the source-ip to use!
# .2 is the remote WG-IP; .1 is the local one
ping 10.0.1.2 -I 10.0.1.1
If not:
-
Make sure the tunnel is really running!
-
Check if the keys match =>
wg show all
should show 'the same' public keys on both sides:guy@srv1:~# wg show all > interface: wgx_tx1 > public key: FJgEWygMdiqRcTvij3PiXOtPJNtTENQkv301l2PGhwY= > ...
guy@srv2:~# wg show all > ... > peer: FJgEWygMdiqRcTvij3PiXOtPJNtTENQkv301l2PGhwY= > ...
To re-generate mismatched keys, just remove them from the controllers 'files' directory and re-run the role on the servers.
This only applies to tunnels that are used to connect remote subnets.
We test it with another ping - this time using the local subnet (not WG-IP).
# 172.30.1.1 is the remote 'subnet'; 172.20.0.1 is the local one
ping 172.30.1.1 -I 172.20.0.1
# you can also run a traceroute to get more information about the route taken:
traceroute 172.30.1.1
If you are motivated => you can run a tcpdump on the remote host to find out if traffic is coming 'through the tunnel'.
# 'wgs_ts2' is the WireGuard tunnel-interface in this example
guy@srv:~# tcpdump -i wgs_ts2
> tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
> listening on wgs_ts2, link-type RAW (Raw IP), snapshot length 262144 bytes
> 17:00:07.336550 IP 10.0.1.2 > 10.0.1.1: ICMP echo request, id 38770, seq 1, length 64
> 17:00:07.336695 IP 10.0.1.1 > 10.0.1.2: ICMP echo reply, id 38770, seq 1, length 64
If not:
-
Check if a firewall is blocking any traffic between the hosts.
IPTables/NFTables per example need to allow incoming AND forwarding traffic.
-
Check your routing configuration for errors and compare it against the 'running config':
# show 'simple' overview ip route show all # show ALL routes ip route show table all # to remove unneccessary broadcast/local routes ip route show table all | grep -vE '^(broadcast|local)\s'
-
The hosts need to support traffic forwarding!
Make sure the setting 'wireguard.support.traffic_forwarding' is enabled.
-
You could have forgotten to add the target network to the 'AllowedIPs'.
This is necessary for star & mesh topologies!
It might be a role-problem/-bug or some other edge-case => please feel free to open a GitHub issue!
Please provide the troubleshooting results in the issue.