Point of inception

This commit is contained in:
jawher 2021-03-25 12:15:35 +01:00
commit b15d869de6
7 changed files with 296 additions and 0 deletions

7
Makefile Normal file
View File

@ -0,0 +1,7 @@
INVENTORY = inventory
apply:
ansible-playbook -i "inventories/${INVENTORY}.yml" "wireguard.yml"
test:
ansible-playbook -i "inventories/${INVENTORY}.yml" "ping.yml"

111
README.md Normal file
View File

@ -0,0 +1,111 @@
# Multi server Wireguard mesh with ansible
A playbook which given an inventory file with:
* a list of hosts
* for each host a `wireguard_ip` variable with the desired host (private) Wireguard IP
* `wireguard_mask_bits` variable with the number of the wireguard (private) network mask bits
* `wireguard_port` variable with the UDP port to use
will:
* install wireguard in all hosts
* generate public/private key pairs in all hosts
* generate the pre-shared keys for all host pairs
* create a `wg0` virtual network device and a `wg0` network
optionally, when the `ufw_enabled` variable is set to `true`:
* enable ufw on all hosts
* reject everything by default
* allow ssh protocol from all sources
* allow traffic from all the inventory wireguard IPs
More details and explanation can be found in this blog post: https://jawher.me/wireguard-ansible-systemd-ubuntu/
## Example
In this example, we'll create 3 Hetzner cloud CX11 servers (~3€/month) using [Hetzner's cli](https://github.com/hetznercloud/cli),
1 in each of their 3 datacenters (Nuremberg, Falkenstein & Helsinki):
```shell
env_id=wireguard-test
server_type=cx11
image=ubuntu-20.04
args=()
for k in $(hcloud ssh-key list -o=noheader -ocolumns=name); do
args+=("--ssh-key=$k")
done
for datacenter in nbg1-dc3 fsn1-dc14 hel1-dc2; do
hcloud server create "${args[@]}" \
--datacenter="${datacenter}" \
--type="${server_type}" \
--image="${image}" \
--label=env="${env_id}" \
--name="${env_id}-${datacenter}"
done
```
### Inventory
Next you need to prepapre an inventory file with the 3 servers we created in `inventories/inventory.yml`:
Run `hcloud server list -l env=wireguard-test`:
```
ID NAME STATUS IPV4 IPV6 DATACENTER
10889123 wireguard-test-nbg1-dc3 running xxx.xx.xxx.xx 2a01:xxxx:xxxx:xxxx::/64 nbg1-dc3
10889126 wireguard-test-fsn1-dc14 running xxx.xx.xxx.xxx 2a01:xxxx:xxxx:xxxx::/64 fsn1-dc14
10889127 wireguard-test-hel1-dc2 running xx.xxx.xx.xxx 2a01:xxxx:xxxx:xxxx::/64 hel1-dc2
```
And use the server names and IPv4s to build your inventory:
```yml
all:
hosts:
host1:
pipelining: true
ansible_ssh_user: root
ansible_host: "$host1_public_ip"
ansible_ssh_port: 22
wireguard_ip: 192.168.0.1
host2:
pipelining: true
ansible_ssh_user: root
ansible_host: "$host2_public_ip"
ansible_ssh_port: 22
wireguard_ip: 192.168.0.2
host3:
pipelining: true
ansible_ssh_user: root
ansible_host: "$host3_public_ip"
ansible_ssh_port: 22
wireguard_ip: 192.168.0.3
vars:
ansible_become_method: su
wireguard_mask_bits: 24
wireguard_port: 51871
```
### Apply
Run `make apply`
### Test connectivity
Run `make test`, which will perform ping tests between the 3 servers using their wireguard private IPs.
You could also ssh to each/any host and run `ping` manually if you prefer.

33
inventories/inventory.yml Normal file
View File

@ -0,0 +1,33 @@
all:
hosts:
host1:
pipelining: true
ansible_ssh_user: root
ansible_host: "$host1_public_ip"
ansible_ssh_port: 22
wireguard_ip: 192.168.0.1
host2:
pipelining: true
ansible_ssh_user: root
ansible_host: "$host2_public_ip"
ansible_ssh_port: 22
wireguard_ip: 192.168.0.2
host3:
pipelining: true
ansible_ssh_user: root
ansible_host: "$host3_public_ip"
ansible_ssh_port: 22
wireguard_ip: 192.168.0.3
vars:
ansible_become_method: su
wireguard_mask_bits: 24
wireguard_port: 51871

7
ping.yml Normal file
View File

@ -0,0 +1,7 @@
---
- hosts: all
gather_facts: yes
tasks:
- name: ping
command: "ping -c3 {{ hostvars[item].wireguard_ip }}"
with_items: "{{ groups['all'] }}"

19
templates/systemd.netdev Normal file
View File

@ -0,0 +1,19 @@
[NetDev]
Name=wg0
Kind=wireguard
Description=WireGuard tunnel wg0
[WireGuard]
ListenPort={{ wireguard_port }}
PrivateKey={{ wireguard_private_key.stdout }}
{% for peer in groups['all'] %}
{% if peer != inventory_hostname %}
[WireGuardPeer]
PublicKey={{ hostvars[peer].wireguard_public_key.stdout }}
PresharedKey={{ wireguard_preshared_keys[peer] if inventory_hostname < peer else hostvars[peer].wireguard_preshared_keys[inventory_hostname] }}
AllowedIPs={{ hostvars[peer].wireguard_ip }}/32
Endpoint={{ hostvars[peer].ansible_host }}:{{ wireguard_port }}
{% endif %}
{% endfor %}

View File

@ -0,0 +1,5 @@
[Match]
Name=wg0
[Network]
Address={{ wireguard_ip }}/{{ wireguard_mask_bits }}

114
wireguard.yml Normal file
View File

@ -0,0 +1,114 @@
---
- hosts: all
any_errors_fatal: true
gather_facts: yes
tasks:
- name: update packages
apt:
update_cache: yes
cache_valid_time: 3600
become: yes
- name: Allow SSH in UFW
ufw:
rule: allow
port: "{{ ansible_ssh_port }}"
proto: tcp
become: yes
when: ufw_enabled
- name: Set ufw logging
ufw:
logging: "on"
become: yes
when: ufw_enabled
- name: inter-node Wireguard UFW connectivity
ufw:
rule: allow
src: "{{ hostvars[item].wireguard_ip }}"
with_items: "{{ groups['all'] }}"
become: yes
when: ufw_enabled and item != inventory_hostname
- name: Reject everything and enable UFW
ufw:
state: enabled
policy: reject
log: yes
become: yes
when: ufw_enabled
- name: Install wireguard
apt:
name: wireguard
state: present
become: yes
- name: Generate Wireguard keypair
shell: wg genkey | tee /etc/wireguard/privatekey | wg pubkey | tee /etc/wireguard/publickey
args:
creates: /etc/wireguard/privatekey
become: yes
- name: register private key
shell: cat /etc/wireguard/privatekey
register: wireguard_private_key
changed_when: false
become: yes
- name: register public key
shell: cat /etc/wireguard/publickey
register: wireguard_public_key
changed_when: false
become: yes
- name: generate Preshared keyskeypair
shell: "wg genpsk > /etc/wireguard/psk-{{ item }}"
args:
creates: "/etc/wireguard/psk-{{ item }}"
when: inventory_hostname < item
with_items: "{{ groups['all'] }}"
become: yes
- name: register preshared key
shell: "cat /etc/wireguard/psk-{{ item }}"
register: wireguard_preshared_key
changed_when: false
when: inventory_hostname < item
with_items: "{{ groups['all'] }}"
become: yes
- name: massage preshared keys
set_fact: "wireguard_preshared_keys={{ wireguard_preshared_keys|default({}) | combine( {item.item: item.stdout} ) }}"
when: item.skipped is not defined
with_items: "{{ wireguard_preshared_key.results }}"
become: yes
- name: Setup wg0 device
template:
src: ./templates/systemd.netdev
dest: /etc/systemd/network/99-wg0.netdev
owner: root
group: systemd-network
mode: 0640
become: yes
notify: systemd network restart
- name: Setup wg0 network
template:
src: ./templates/systemd.network
dest: /etc/systemd/network/99-wg0.network
owner: root
group: systemd-network
mode: 0640
become: yes
notify: systemd network restart
handlers:
- name: systemd network restart
service:
name: systemd-networkd
state: restarted
enabled: yes
become: yes