aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inventory.yaml.example2
-rw-r--r--main.yaml1
-rw-r--r--tasks/pve_setup.yaml9
-rw-r--r--tasks/wg_setup.yaml72
-rw-r--r--templates/manage_wg_peers.sh.j2186
-rw-r--r--templates/wg0.conf.j212
-rw-r--r--vars/main.yaml17
7 files changed, 296 insertions, 3 deletions
diff --git a/inventory.yaml.example b/inventory.yaml.example
index 6f27497..115fb2b 100644
--- a/inventory.yaml.example
+++ b/inventory.yaml.example
@@ -7,6 +7,8 @@ all:
hostname: proxmox-ve-test
nat_subnet: 10.10.10.0/24
nat_bridge_ip: 10.10.10.1
+ wg_subnet: 10.13.37.0/24
+ wg_port: 31337
children:
servers:
hosts:
diff --git a/main.yaml b/main.yaml
index 0e1f5eb..7c7e60f 100644
--- a/main.yaml
+++ b/main.yaml
@@ -8,3 +8,4 @@
- import_tasks: tasks/pve_setup.yaml
- import_tasks: tasks/pve_configure.yaml
- import_tasks: tasks/harden.yaml
+ - import_tasks: tasks/wg_setup.yaml
diff --git a/tasks/pve_setup.yaml b/tasks/pve_setup.yaml
index 7d04ff2..9fcea47 100644
--- a/tasks/pve_setup.yaml
+++ b/tasks/pve_setup.yaml
@@ -7,7 +7,7 @@
mode: '0644'
- name: create /etc/apt/sources.list.d directory
- ansible.builtin.file:
+ file:
path: /etc/apt/sources.list.d
state: directory
mode: '0755'
@@ -51,6 +51,8 @@
name: "{{ apt_packages }}"
state: present
update_cache: true
+ environment:
+ DEBIAN_FRONTEND: noninteractive
- name: reboot to activate proxmox ve kernel
reboot:
@@ -91,3 +93,8 @@
apt:
name: "{{ apt_packages_to_remove }}"
state: absent
+
+- name: remove pve-enterprise apt source
+ file:
+ path: /etc/apt/sources.list.d/pve-enterprise.list
+ state: absent
diff --git a/tasks/wg_setup.yaml b/tasks/wg_setup.yaml
new file mode 100644
index 0000000..9557a79
--- /dev/null
+++ b/tasks/wg_setup.yaml
@@ -0,0 +1,72 @@
+- name: install wireguard and dependencies
+ apt:
+ name: "{{ wireguard_packages }}"
+ state: present
+ update_cache: yes
+
+- name: update apt packages
+ apt:
+ update_cache: true
+
+- name: install apt packages
+ apt:
+ name: "{{ apt_packages }}"
+ state: present
+ update_cache: true
+ environment:
+ DEBIAN_FRONTEND: noninteractive
+
+- name: create wireguard server directory
+ file:
+ path: "{{ wireguard_server_home }}"
+ state: directory
+ mode: "0700"
+
+- name: create wireguard peers directory
+ file:
+ path: "{{ wireguard_peers_home }}"
+ state: directory
+ mode: "0700"
+
+- name: generate wireguard server keys
+ shell:
+ cmd: |
+ wg genpsk > "{{ wireguard_server_home }}/psk.key"
+ wg genkey > "{{ wireguard_server_home }}/server.key"
+ creates: "{{ wireguard_server_home }}/server.key"
+ args:
+ chdir: "{{ wireguard_server_home }}"
+
+- name: get server public key
+ shell:
+ cmd: wg pubkey < "{{ wireguard_server_home }}/server.key"
+ register: server_pubkey
+ changed_when: false
+
+- name: read wireguard server.key from remote host
+ slurp:
+ src: "{{ wireguard_server_home }}/server.key"
+ register: wg_key
+
+- name: set private key from remote file
+ set_fact:
+ private_key: "{{ wg_key.content | b64decode }}"
+
+- name: deploy {{ wireguard_server_home }}/wg0.conf
+ template:
+ src: wg0.conf.j2
+ dest: "{{ wireguard_server_home }}/wg0.conf"
+ mode: "0600"
+
+- name: deploy manage_wg_peers.sh
+ template:
+ src: manage_wg_peers.sh.j2
+ dest: /root/manage_wg_peers.sh
+ mode: "0600"
+
+- name: restart wireguard
+ systemd:
+ name: wg-quick@wg0.service
+ state: restarted
+ enabled: true
+ when: ansible_service_mgr == 'systemd'
diff --git a/templates/manage_wg_peers.sh.j2 b/templates/manage_wg_peers.sh.j2
new file mode 100644
index 0000000..ed2f800
--- /dev/null
+++ b/templates/manage_wg_peers.sh.j2
@@ -0,0 +1,186 @@
+#!/bin/bash
+set -e
+
+WG_SERVER_HOME="{{ wireguard_server_home }}"
+WG_PEERS_HOME="${WG_SERVER_HOME}/peers.d"
+IP_FILE="${WG_SERVER_HOME}/ips.txt"
+SUBNET_PREFIX="{{ wireguard_subnet_prefix }}"
+DEFAULT_PORT="{{ wireguard_port }}"
+DEFAULT_DNS="8.8.8.8"
+
+test "${EUID}" -ne 0 && printf "%s\n" "run as root" && exit 1
+umask 077
+
+if ! command -v wg &>/dev/null; then
+ printf "%s\n" "[err] wireguard not installed"
+ exit 1
+fi
+
+function usage() {
+ printf "%s\n" \
+ "" \
+ "wireguard peer management script" \
+ "usage: $(basename ${0}) <action> [-h] <options>" \
+ "" \
+ "add-peer add a peer to the wg network" \
+ "options:" \
+ "-s <server> required: wg server endpoint" \
+ "-n <name> optional: peer name, default random" \
+ "-p <port> optional: wg server port, default ${DEFAULT_PORT}" \
+ "-d <dns> optional: dns server, default ${DEFAULT_DNS}" \
+ "" \
+ "remove-peer remove peer from the wg network" \
+ "options:" \
+ "-n <name> required: peer name" \
+ "" \
+ "usage print this help message and exit" \
+ "" \
+ "configuration files:" \
+ "wg server config dir: ${WG_SERVER_HOME}" \
+ "wg peers configs dir: ${WG_PEERS_HOME}"
+ exit 1
+}
+
+function validate_ip() {
+ local ip="${1}"
+ if ! [[ "${ip}" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ printf "%s\n" "[err] invalid IP address: ${ip}"
+ exit 1
+ fi
+}
+
+function validate_port() {
+ local port="${1}"
+ if ! [[ "${port}" =~ ^[0-9]+$ ]] || [[ "${port}" -lt 1 ]] || [[ "${port}" -gt 65535 ]]; then
+ printf "%s\n" "[err] invalid port: ${port}"
+ exit 1
+ fi
+}
+
+function get_next_available_ip() {
+ (
+ flock -x 200
+ touch "${IP_FILE}"
+ for i in {2..254}; do
+ ip="${SUBNET_PREFIX}.${i}"
+ if ! grep -q "${ip}" "${IP_FILE}"; then
+ printf "%s\n" "${ip}"
+ printf "%s\n" "${ip}" >> "${IP_FILE}"
+ exit 0
+ fi
+ done
+ printf "%s\n" "[err] no available ips in range ${SUBNET_PREFIX}.2 - ${SUBNET_PREFIX}.254"
+ exit 1
+ ) 200>"${IP_FILE}.lock"
+}
+
+function add_peer() {
+ if ! test -d "${WG_SERVER_HOME}" || ! test -f "${WG_SERVER_HOME}/wg0.conf"; then
+ printf "%s\n" "[err] no wg server config found; install the server first"
+ exit 1
+ fi
+
+ if ! test "${server}"; then
+ printf "%s\n" "[err] missing -s <server>"
+ exit 1
+ fi
+
+ mkdir -p "${WG_PEERS_HOME}/${name}" &>/dev/null
+ assigned_ip=$(get_next_available_ip)
+ (
+ cd "${WG_PEERS_HOME}/${name}"
+ wg genkey | tee "${name}.key" | wg pubkey > "${name}.pub"
+
+ cat > "${name}.conf" << EOF
+# peer ${name}
+[Interface]
+PrivateKey = $(cat "${name}.key")
+Address = ${assigned_ip}/24
+DNS = ${dns}
+
+[Peer]
+PublicKey = $(wg pubkey < "${WG_SERVER_HOME}/server.key")
+PresharedKey = $(cat "${WG_SERVER_HOME}/psk.key")
+Endpoint = ${server}:${port}
+AllowedIPs = 0.0.0.0/0
+PersistentKeepalive = 25
+EOF
+ printf "%s\n" \
+ "[inf] generated peer configuration ${name} to server ${server}" \
+ "[inf] config: ${WG_PEERS_HOME}/${name}/${name}.conf"
+ )
+
+ (
+ peer_public_key=$(wg pubkey < "${WG_PEERS_HOME}/${name}/${name}.key")
+ cat >> "${WG_SERVER_HOME}/wg0.conf" << EOF
+
+# peer ${name}
+[Peer]
+PublicKey = ${peer_public_key}
+PresharedKey = $(cat "${WG_SERVER_HOME}/psk.key")
+AllowedIPs = ${assigned_ip}/32
+# peer ${name}
+EOF
+ systemctl restart wg-quick@wg0.service
+ printf "%s\n" "[inf] restarted wg-quick@wg0.service"
+ )
+}
+
+function remove_peer() {
+ if ! test "${name}"; then
+ printf "%s\n" "[err] missing -n <name>"
+ exit 1
+ fi
+
+ if grep -qi "^# peer ${name}" "${WG_SERVER_HOME}/wg0.conf"; then
+ sed -i "/# peer ${name}/,/# peer ${name}/ s/^/# /" "${WG_SERVER_HOME}/wg0.conf"
+ systemctl restart wg-quick@wg0.service
+ printf "%s\n" "[inf] removed peer ${name} and restarted wg server"
+ else
+ printf "%s\n" "[err] no such peer ${name} in ${WG_SERVER_HOME}/wg0.conf"
+ exit 1
+ fi
+}
+
+port="${DEFAULT_PORT}"
+dns="${DEFAULT_DNS}"
+
+if test "${1}"; then
+ action="${1}"
+ shift
+ case "${action}" in
+ add-peer)
+ while getopts "s:n:p:d:h" opts; do
+ case "${opts}" in
+ s) server="${OPTARG}"; validate_ip "${server}";;
+ n) name="${OPTARG}";;
+ p) port="${OPTARG}"; validate_port "${port}";;
+ d) dns="${OPTARG}"; validate_ip "${dns}";;
+ h) usage;;
+ *) usage;;
+ esac
+ done
+ rand=$(printf "%s\n" "${RANDOM}" | md5sum | fold -w4 | head -1)
+ name="${name:-${rand}}"
+ add_peer
+ ;;
+ remove-peer)
+ while getopts "n:h" opts; do
+ case "${opts}" in
+ n) name="${OPTARG}";;
+ h) usage;;
+ *) usage;;
+ esac
+ done
+ remove_peer
+ ;;
+ usage)
+ usage
+ ;;
+ *)
+ usage
+ ;;
+ esac
+else
+ usage
+fi
diff --git a/templates/wg0.conf.j2 b/templates/wg0.conf.j2
new file mode 100644
index 0000000..6b0aa34
--- /dev/null
+++ b/templates/wg0.conf.j2
@@ -0,0 +1,12 @@
+[Interface]
+PrivateKey = {{ private_key }}
+Address = {{ wireguard_subnet_prefix}}.1/24
+ListenPort = {{ wireguard_port }}
+PostUp = sysctl -w net.ipv4.ip_forward=1
+PostUp = iptables -A FORWARD -i {{ wireguard_interface }} -o %i -j ACCEPT
+PostUp = iptables -A FORWARD -i %i -j ACCEPT
+PostUp = iptables -t nat -A POSTROUTING -o {{ wireguard_interface }} -j MASQUERADE
+PostDown = sysctl -w net.ipv4.ip_forward=0
+PostDown = iptables -D FORWARD -i {{ wireguard_interface }} -o %i -j ACCEPT
+PostDown = iptables -D FORWARD -i %i -j ACCEPT
+PostDown = iptables -t nat -D POSTROUTING -o {{ wireguard_interface }} -j MASQUERADE
diff --git a/vars/main.yaml b/vars/main.yaml
index 568e185..bd079ca 100644
--- a/vars/main.yaml
+++ b/vars/main.yaml
@@ -1,5 +1,3 @@
-fail2ban_jail_dir: /etc/fail2ban/jail.d
-
apt_packages:
- curl
- ca-certificates
@@ -17,3 +15,18 @@ pve_packages:
apt_packages_to_remove:
- os-prober
+
+wireguard_packages:
+ - wireguard
+ - wireguard-tools
+ - iptables
+ - iproute2
+
+fail2ban_jail_dir: /etc/fail2ban/jail.d
+wireguard_server_home: /etc/wireguard
+wireguard_peers_home: "{{ wireguard_server_home }}/peers.d"
+wireguard_ip_file: "{{ wireguard_server_home }}/ips.txt"
+wireguard_subnet: "{{ wg_subnet }}"
+wireguard_subnet_prefix: "{{ wg_subnet.split('.')[0:3] | join('.') }}"
+wireguard_port: "{{ wg_port }}"
+wireguard_interface: "{{ ansible_default_ipv4.interface }}"