Wireguard VPN Server Installer Role
ds-wireguard
This Ansible role can deploy a Wireguard VPN server on Debian-based hosts.
The role is designed for on-premises, self-hosted deployments.
It is idempotent.
Stack components:
- Wireguard kernel module and user tools
- Systemd for service management
- UFW firewall integration
- Optional support for vault-managed private keys
- NAT / IP forwarding configuration
- Configurable peers via Ansible variables
The role aims for simplicity, security, and maintainability - no manual configuration steps required after initial deployment.
Dependencies
ds_ufw: Set up the incoming firewall rules separately. The role handles only the postrouting and routing rules.
Example:
ufw_rules:
- { rule: allow, port: "51820", proto: udp, comment: "Wireguard VPN" }
- { rule: allow, port: "22", proto: tcp, src: "10.100.200.0/24", comment: "SSH from VPN" }
Requirements
- Debian 12+ (Bookworm) or compatible.
- Sudo access.
- Ports open on UDP
{{ wg_vpn_port }}(default 51820).
Role Behavior
- Installs Wireguard package.
- Ensures
/etc/wireguarddirectory exists with proper permissions. - Checks for existing private key file.
- Generates a new key pair if no key exists and no vault-provided key is defined.
- Writes vault-provided private key if defined.
- Enables IPv4 forwarding in the kernel and UFW.
- Configures UFW NAT postrouting and routing rules.
- Reads the private key and deploys a Wireguard configuration from a Jinja2 template.
- Enables and starts the Wireguard service via Systemd.
Variables
The following variables must be defined, typically in group_vars or your inventory:
| Variable | Description |
|---|---|
wg_vpn_network |
VPN network/subnet for Wireguard (e.g. 10.200.200.1/24). |
wg_vpn_interface |
Wireguard interface name (default: wg0). |
wg_vpn_port |
UDP port Wireguard listens on (default: 51820). |
wg_network_interface |
Physical interface for NAT (e.g. enp1s0). |
vault_wg_private_key |
Optional SOPS/vault-provided server private key. |
wg_peers |
Dictionary of peers with keys public_key and allowed_ips. |
Example peer definition:
wg_peers:
iron:
public_key: e2V40zdPiX43lqOamcoEI8J10uKaXWBeKwf+spWDWgc=
allowed_ips: 10.200.200.2/32
Example Playbook
- name: Deploy Wireguard VPN server
hosts: vpn-servers
become: true
roles:
- role: ds-wireguard
vars:
wg_vpn_network: 10.100.200.1/24
wg_vpn_interface: wg0
wg_vpn_port: 51820
wg_network_interface: enp1s0
vault_wg_private_key: "{{ vault_wg_private_key }}" # Optional
wg_peers:
iron:
public_key: e2V40zdPiX43lqOamcoEI8J10uKaXWBeKwf+spWDWgc=
allowed_ips: 10.100.200.2/32
Idempotence
The role is fully idempotent.
Re-running it will:
- Skip key generation if a private key exists.
- Skip firewall rules if already applied.
- Update Wireguard configuration only if variables or template change.
- Restart Wireguard only when the configuration changes.
Operational Notes
- Ensure the physical network interface for NAT (
wg_network_interface) is correct. - UFW must be installed and enabled on the host.
- Vault-managed keys provide deterministic server identity across deployments.
- Peer definitions automatically generate the Wireguard [Peer] blocks in the configuration.
- IPv4 forwarding must be enabled for routing between VPN and LAN.
License
MIT
[ Fear the Silence. Fear the Switch. ]