From c751db56180b065268766667536e615649730318 Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Sat, 9 May 2020 11:24:06 +0200 Subject: [PATCH 01/11] Duplicate VPNator and rewrite it for Wireguard Many modifications remain to be made, targets have to be cleaned a bit more, and routing does not work quite yet. Still working on this. This playbook will, however, deploy functional wg tunneling for a Debian10 server provided that you have full-upgrade'd it and rebooted before. --- .../{vpNator.yml => vpNator-ovpn.yml} | 0 ansible_scripts/vpNator-wireguard.yml | 330 ++++++++++++++++++ 2 files changed, 330 insertions(+) rename ansible_scripts/{vpNator.yml => vpNator-ovpn.yml} (100%) create mode 100644 ansible_scripts/vpNator-wireguard.yml diff --git a/ansible_scripts/vpNator.yml b/ansible_scripts/vpNator-ovpn.yml similarity index 100% rename from ansible_scripts/vpNator.yml rename to ansible_scripts/vpNator-ovpn.yml diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator-wireguard.yml new file mode 100644 index 0000000..4546155 --- /dev/null +++ b/ansible_scripts/vpNator-wireguard.yml @@ -0,0 +1,330 @@ +--- + + +#FIXME: Le script tunnel.sh ne focntionne pas car le path du .ovpn est hardcodé dedans + + +# On vérifie si aucun tag n'a été défini, dans ce cas on affiche une invite pour choisir l'action a effectuer. +# Le résultat de l'user input est stocké dans hostvars.localhost.pause_result.user_input. Il vaut un string ("1", "2", etc...) si aucun tag a été défini, ou est undefined si au moins un tag a été précisé. Cette variable est TREES CHIANTE à récupérer puisqu'elle ests sur l'host 'localhost' + +# On a: hostvars.localhost.pause_result.user_input qui contient la réponse (ou qui est undefined si les tags sont utilisés) + +- hosts: localhost + gather_facts: no + tasks: + - name: Check args + pause: + prompt: "WARNING: It looks like you didn't used any tag.\n + You can use the argument '--tags tag1,tag2,...' to sepcify one or more action to run\n + What do you want to do now?\n\n + Locally:\n + \t[1]: Install (--tags local_install)\n + \t[2]: Start (--tags local_start)\n + \t[3]: Stop (--tags local_stop)\n + \t[4]: Uninstall (--tags local_uninstall)\n + \t[5]: Uninstall & Install (--tags local_uninstall,local_install)\n + Remotely:\n + \t[6]: Install Wireguard and other tools (--tags install)\n + \t[7]: Start Wireguard and FireQOS (--tags start)\n + \t[8]: Stop Wireguard and FireQOS (--tags stop)\n + \t[9]: Remove Wireguard and other tools (--tags uninstall)\n + \t[A]: Uninstall & Install (--tags install,uninstall)\n + Agnostically:\n + [B]: Full uninstall & install (--tags local_install,install,local_uninstall,uninstall)\n + Please select exactly one" + register: pause_result + + - name: === Local Stop === + block: + - name: Bring wg0 interface down + become: true + become_method: sudo + command: ip link set wg0 down + ignore_errors: true + when: hostvars.localhost.pause_result is not defined or (hostvars.localhost.pause_result.user_input in ["3", "4", "5", "B"]) + tags: [ local_uninstall, local_stop ] + + + - name: === Local Uninstall === + block: + - name: Delete local wireguard interface + become: true + become_method: sudo + shell: ip link del wg0 + ignore_errors: true + + - name: Remove local wireguad private key + become: true + become_method: sudo + file: + state: absent + path: /tmp/wireguard_private + ignore_errors: true + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["4", "5", "B"] + tags: [ local_uninstall ] + + - name: Check presence of localhost wireguard key + become: true + become_method: sudo + stat: + path: /tmp/wireguard_private + register: localhost_wireguard_private + + - name: === Local Install === + block: + - name: Generate localhost wireguard key + become: true + become_method: sudo + shell: wg genkey > /tmp/wireguard_private + + - name: Create local wireguard interface + become: true + become_method: sudo + shell: ip link add wg0 type wireguard + + - name: Give localhost wireguard interface address "10.0.1.100" + become: true + become_method: sudo + shell: ip addr add 10.0.1.100/24 dev wg0 + + - name: Start localhost wireguard interface + become: true + become_method: sudo + shell: ip link set wg0 up + + - name: Set localhost private key + become: true + become_method: sudo + shell: wg set wg0 private-key /tmp/wireguard_private #TODO: Stop hardcoding stuff + + when: not localhost_wireguard_private.stat.exists or hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "5", "B"] + tags: [ local_install ] + + - name: Obtain localhost public key + become: true + become_method: sudo + shell: wg pubkey < /tmp/wireguard_private + register: localhost_wireguard_public + when: hostvars.localhost.pause_result is not defined or not hostvars.localhost.pause_result.user_input == "4" + tags: [ install, uninstall ] + +# Et maintenant on s'occupe vraiment des VPNs +- hosts: vpn + remote_user: debian + gather_facts: no + vars: + tunnel_address: "{{ hostvars[inventory_hostname].addr }}" + port: 5010 + # FIXME: Find an elegant way to pick the right version of the headers + packages: ['bc', 'linux-headers-4.19.0-8-cloud-amd64', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] + localhost_wireguard_public: "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" + tasks: # ****************************** UNINSTALL ************************************* + + - name: === STOP === + block: + - name: Bring remote wireguard interface down + become: true + become_method: sudo + command: ip link set wg0 down + ignore_errors: true + + - name: Stop FireQOS on remote + become: true + become_method: sudo + command: fireqos stop + + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["8", "9", "A", "B"] + tags: [ uninstall, stop ] + + - name: === Uninstall === + block: + - name: Remove Additional Tools Config (FireQOS) + become: true + become_method: sudo + file: + state: absent + path: /etc/firehol/fireqos.conf + + - name: Remove IP Table rule for NAT + become: true + become_method: sudo + command: iptables -t nat -D POSTROUTING -o en+ -j MASQUERADE + ignore_errors: true + + #- name: Uninstall additional tools + #become: true + #become_method: sudo + #apt: + # state: absent + # purge: true + # name: "{{ packages }}" + # + # As it turns out, none of the tools we have really need to be + # removed that badly. We can let them sit there until the VPS + # expires. Also, this part is dangerous to use in testing on + # machines where these other packages are dependencies of + # working services, which configuration will get wiped. + # Source : the apache2 server I accidentally wiped clean from my VPS + + # Uninstall script begins here + - name: Delete remote wireguard interface + become: true + become_method: sudo + shell: ip link del wg0 + ignore_errors: true + # We don't care if it doesn't exist + + - name: Obtain remote public key + become: true + become_method: sudo + shell: wg pubkey < /root/wireguard_private + register: remote_public_wireguard + ignore_errors: true + + - name: Remove peer from localhost + become: true + become_method: sudo + shell: wg set wg0 peer "{{ remote_public_wireguard.stdout }}" remove + delegate_to: localhost + ignore_errors: true + + - name: Uninstall and purge wireguard + become: true + become_method: sudo + apt: + name: wireguard + purge: true + state: absent + + - name: Remove wireguard private key file + become: true + become_method: sudo + file: + state: absent + path: /root/wireguard_private + + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["9", "A", "B"] + tags: [ uninstall ] + + + # ************************ INSTALL ***************************************** + - name: === Install === + block: + - name: Install packages and utilities + become: true + become_method: sudo + apt: + name: "{{ packages }}" + state: present + install_recommends: false # DO NOT LET IT INSTALL FIREHOL, DO NOT. (cf. Readme) + update_cache: yes + + - name: Apt full upgrade + become: true + become_method: sudo + apt: + upgrade: full + state: latest + install_recommends: false + + - name: Figure out kernel version + shell: uname -r + register: remote_kernel_version + + # For some reason this makes ansible crash + #- name: Install appropriate linux headers + #become: true + #become_method: sudo + #apt: + #name: "linux-headers-{{ remote_kernel_version }}" + #state: present + #install_recommends: false + + - name: Install wireguard and compile DKMS module + become: true + become_method: sudo + apt: + name: "wireguard" + state: present + install_recommends: false + + - name: Create remote interface + become: true + become_method: sudo + shell: ip link add wg0 type wireguard + + - name: Give remote interface an address + become: true + become_method: sudo + shell: ip addr add "{{ tunnel_address }}/24" dev wg0 + + - name: Set remote wireguard port + become: true + become_method: sudo + shell: wg set wg0 listen-port 5010 + + - name: Create wireguard private key + become: true + become_method: sudo + shell: wg genkey > /root/wireguard_private + + - name: Set wireguard private key + become: true + become_method: sudo + shell: wg set wg0 private-key /root/wireguard_private + + - name: Add localhost as peer + become: true + become_method: sudo + shell: wg set wg0 peer "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" allowed-ips 10.0.1.100/0 # Only allow server + + - name: Obtain peer public key + become: true + become_method: sudo + shell: wg pubkey < /root/wireguard_private + register: remote_public_wireguard + + - name: Add peer to localhost + become: true + become_method: sudo + shell: wg set wg0 peer "{{ remote_public_wireguard.stdout }}" allowed-ips 0.0.0.0/0 endpoint "{{ inventory_hostname }}:{{ port }}" # Allow to route everything + delegate_to: localhost + + - name: Copy FireQoS config + become: true + become_method: sudo + template: + src: templates/fireqos_vpn.conf + dest: /etc/firehol/fireqos.conf + + - name: Enable IP Forward + become: true + become_method: sudo + shell: echo 1 > /proc/sys/net/ipv4/ip_forward + + - name: IP Table Rule + become: true + become_method: sudo + command: iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE + + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["6", "7", "A", "B"] + tags: [ install ] + + # ****************** MOUNT ********************** + - name: === Install === + block: + - name: Bring wg0 interface up + become: true + become_method: sudo + shell: ip link set wg0 up + + - name: Start FireQOS + become: true + become_method: sudo + command: "fireqos start" + + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["7", "A", "B"] + tags: [ install, start ] + + + From 6271825e2b9ac6e94aa9e356fbe5be4b746008a4 Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Sat, 9 May 2020 18:42:00 +0200 Subject: [PATCH 02/11] Functional wireguard playbook - Remove whitespaces - Look for localhost wireguard private key even when restricting to `local_install` tag - Target remote install also launches remote start, but no longer the opposite - Don't install a specific header package, find the kernel version and retrieve the appropriate package - Substitute `wg0` for `tun0` in fireqos configuration template --- ansible_scripts/vpNator-wireguard.yml | 59 +++++++++++++++------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator-wireguard.yml index 4546155..5570c69 100644 --- a/ansible_scripts/vpNator-wireguard.yml +++ b/ansible_scripts/vpNator-wireguard.yml @@ -13,7 +13,7 @@ gather_facts: no tasks: - name: Check args - pause: + pause: prompt: "WARNING: It looks like you didn't used any tag.\n You can use the argument '--tags tag1,tag2,...' to sepcify one or more action to run\n What do you want to do now?\n\n @@ -69,6 +69,7 @@ stat: path: /tmp/wireguard_private register: localhost_wireguard_private + tags: [ local_install ] - name: === Local Install === block: @@ -97,7 +98,7 @@ become_method: sudo shell: wg set wg0 private-key /tmp/wireguard_private #TODO: Stop hardcoding stuff - when: not localhost_wireguard_private.stat.exists or hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "5", "B"] + when: not localhost_wireguard_private.stat.exists or hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "5", "B"] tags: [ local_install ] - name: Obtain localhost public key @@ -106,7 +107,7 @@ shell: wg pubkey < /tmp/wireguard_private register: localhost_wireguard_public when: hostvars.localhost.pause_result is not defined or not hostvars.localhost.pause_result.user_input == "4" - tags: [ install, uninstall ] + tags: [ install ] # Et maintenant on s'occupe vraiment des VPNs - hosts: vpn @@ -116,10 +117,10 @@ tunnel_address: "{{ hostvars[inventory_hostname].addr }}" port: 5010 # FIXME: Find an elegant way to pick the right version of the headers - packages: ['bc', 'linux-headers-4.19.0-8-cloud-amd64', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] + packages: ['bc', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] localhost_wireguard_public: "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" tasks: # ****************************** UNINSTALL ************************************* - + - name: === STOP === block: - name: Bring remote wireguard interface down @@ -127,15 +128,16 @@ become_method: sudo command: ip link set wg0 down ignore_errors: true - + - name: Stop FireQOS on remote become: true become_method: sudo command: fireqos stop - + ignore_errors: true + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["8", "9", "A", "B"] tags: [ uninstall, stop ] - + - name: === Uninstall === block: - name: Remove Additional Tools Config (FireQOS) @@ -165,7 +167,7 @@ # machines where these other packages are dependencies of # working services, which configuration will get wiped. # Source : the apache2 server I accidentally wiped clean from my VPS - + # Uninstall script begins here - name: Delete remote wireguard interface become: true @@ -203,10 +205,10 @@ state: absent path: /root/wireguard_private - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["9", "A", "B"] + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["9", "A", "B"] tags: [ uninstall ] - - + + # ************************ INSTALL ***************************************** - name: === Install === block: @@ -232,13 +234,13 @@ register: remote_kernel_version # For some reason this makes ansible crash - #- name: Install appropriate linux headers - #become: true - #become_method: sudo - #apt: - #name: "linux-headers-{{ remote_kernel_version }}" - #state: present - #install_recommends: false + - name: Install appropriate linux headers + become: true + become_method: sudo + apt: + name: "linux-headers-{{ remote_kernel_version.stdout }}" + state: present + install_recommends: false - name: Install wireguard and compile DKMS module become: true @@ -289,7 +291,7 @@ become_method: sudo shell: wg set wg0 peer "{{ remote_public_wireguard.stdout }}" allowed-ips 0.0.0.0/0 endpoint "{{ inventory_hostname }}:{{ port }}" # Allow to route everything delegate_to: localhost - + - name: Copy FireQoS config become: true become_method: sudo @@ -297,6 +299,11 @@ src: templates/fireqos_vpn.conf dest: /etc/firehol/fireqos.conf + - name: Substitute 'tun0' for 'wg0' + become: true + become_method: sudo + shell: sed -i "s/tun0/wg0/" /etc/firehol/fireqos.conf + - name: Enable IP Forward become: true become_method: sudo @@ -307,9 +314,9 @@ become_method: sudo command: iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["6", "7", "A", "B"] + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["6", "A", "B"] tags: [ install ] - + # ****************** MOUNT ********************** - name: === Install === block: @@ -317,14 +324,14 @@ become: true become_method: sudo shell: ip link set wg0 up - + - name: Start FireQOS become: true become_method: sudo command: "fireqos start" - - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["7", "A", "B"] + + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["6", "7", "A", "B"] tags: [ install, start ] - + From 976f322288ddc685abc837a231a8ada7469f004d Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Sat, 9 May 2020 20:25:49 +0200 Subject: [PATCH 03/11] Rewrite VPNator-wireguard for multiple connections --- ansible_scripts/vpNator-wireguard.yml | 173 ++++++++++---------------- 1 file changed, 68 insertions(+), 105 deletions(-) diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator-wireguard.yml index 5570c69..aeb1c97 100644 --- a/ansible_scripts/vpNator-wireguard.yml +++ b/ansible_scripts/vpNator-wireguard.yml @@ -17,106 +17,24 @@ prompt: "WARNING: It looks like you didn't used any tag.\n You can use the argument '--tags tag1,tag2,...' to sepcify one or more action to run\n What do you want to do now?\n\n - Locally:\n - \t[1]: Install (--tags local_install)\n - \t[2]: Start (--tags local_start)\n - \t[3]: Stop (--tags local_stop)\n - \t[4]: Uninstall (--tags local_uninstall)\n - \t[5]: Uninstall & Install (--tags local_uninstall,local_install)\n - Remotely:\n - \t[6]: Install Wireguard and other tools (--tags install)\n - \t[7]: Start Wireguard and FireQOS (--tags start)\n - \t[8]: Stop Wireguard and FireQOS (--tags stop)\n - \t[9]: Remove Wireguard and other tools (--tags uninstall)\n - \t[A]: Uninstall & Install (--tags install,uninstall)\n - Agnostically:\n - [B]: Full uninstall & install (--tags local_install,install,local_uninstall,uninstall)\n + \t[1]: Install Wireguard and other tools (--tags install)\n + \t[2]: Start Wireguard and FireQOS (--tags start)\n + \t[3]: Stop Wireguard and FireQOS (--tags stop)\n + \t[4]: Remove Wireguard and other tools (--tags uninstall)\n + \t[5]: Uninstall & Install (--tags install,uninstall)\n Please select exactly one" register: pause_result - - name: === Local Stop === - block: - - name: Bring wg0 interface down - become: true - become_method: sudo - command: ip link set wg0 down - ignore_errors: true - when: hostvars.localhost.pause_result is not defined or (hostvars.localhost.pause_result.user_input in ["3", "4", "5", "B"]) - tags: [ local_uninstall, local_stop ] - - - - name: === Local Uninstall === - block: - - name: Delete local wireguard interface - become: true - become_method: sudo - shell: ip link del wg0 - ignore_errors: true - - - name: Remove local wireguad private key - become: true - become_method: sudo - file: - state: absent - path: /tmp/wireguard_private - ignore_errors: true - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["4", "5", "B"] - tags: [ local_uninstall ] - - - name: Check presence of localhost wireguard key - become: true - become_method: sudo - stat: - path: /tmp/wireguard_private - register: localhost_wireguard_private - tags: [ local_install ] - - - name: === Local Install === - block: - - name: Generate localhost wireguard key - become: true - become_method: sudo - shell: wg genkey > /tmp/wireguard_private - - - name: Create local wireguard interface - become: true - become_method: sudo - shell: ip link add wg0 type wireguard - - - name: Give localhost wireguard interface address "10.0.1.100" - become: true - become_method: sudo - shell: ip addr add 10.0.1.100/24 dev wg0 - - - name: Start localhost wireguard interface - become: true - become_method: sudo - shell: ip link set wg0 up - - - name: Set localhost private key - become: true - become_method: sudo - shell: wg set wg0 private-key /tmp/wireguard_private #TODO: Stop hardcoding stuff - - when: not localhost_wireguard_private.stat.exists or hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "5", "B"] - tags: [ local_install ] - - - name: Obtain localhost public key - become: true - become_method: sudo - shell: wg pubkey < /tmp/wireguard_private - register: localhost_wireguard_public - when: hostvars.localhost.pause_result is not defined or not hostvars.localhost.pause_result.user_input == "4" - tags: [ install ] - # Et maintenant on s'occupe vraiment des VPNs - hosts: vpn remote_user: debian gather_facts: no vars: - tunnel_address: "{{ hostvars[inventory_hostname].addr }}" + peer_number: "{{ hostvars[inventory_hostname].id }}" + tunnel_address: "10.8.{{ peer_number }}.1" + local_address: "10.8.{{ peer_number }}.2" + localhost_wg_interface: "wg{{ peer_number }}" port: 5010 - # FIXME: Find an elegant way to pick the right version of the headers packages: ['bc', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] localhost_wireguard_public: "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" tasks: # ****************************** UNINSTALL ************************************* @@ -135,7 +53,14 @@ command: fireqos stop ignore_errors: true - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["8", "9", "A", "B"] + - name: Bring local wg interface down + become: true + become_method: sudo + command: ip link set "{{ localhost_wg_interface }}" down + delegate_to: localhost + ignore_errors: true + + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["3", "4", "5"] tags: [ uninstall, stop ] - name: === Uninstall === @@ -150,7 +75,7 @@ - name: Remove IP Table rule for NAT become: true become_method: sudo - command: iptables -t nat -D POSTROUTING -o en+ -j MASQUERADE + command: iptables -t nat -D POSTROUTING -o e+ -j MASQUERADE #FIXME catch both eth+ and en+, but better ignore_errors: true #- name: Uninstall additional tools @@ -169,6 +94,7 @@ # Source : the apache2 server I accidentally wiped clean from my VPS # Uninstall script begins here + # Note: destroying the interface removes the peer remotely - name: Delete remote wireguard interface become: true become_method: sudo @@ -176,19 +102,21 @@ ignore_errors: true # We don't care if it doesn't exist - - name: Obtain remote public key + - name: Delete local wireguard interface become: true become_method: sudo - shell: wg pubkey < /root/wireguard_private - register: remote_public_wireguard + shell: "ip link del {{ localhost_wg_interface }}" ignore_errors: true + delegate_to: localhost - - name: Remove peer from localhost + - name: Remove local wireguad private key become: true become_method: sudo - shell: wg set wg0 peer "{{ remote_public_wireguard.stdout }}" remove - delegate_to: localhost + file: + state: absent + path: "/tmp/wireguard_private_{{ peer_number }}" ignore_errors: true + delegate_to: localhost - name: Uninstall and purge wireguard become: true @@ -205,13 +133,42 @@ state: absent path: /root/wireguard_private - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["9", "A", "B"] + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["4", "5"] tags: [ uninstall ] # ************************ INSTALL ***************************************** - name: === Install === block: + - name: === Localhost Install === + block: + - name: Generate localhost wireguard key + become: true + become_method: sudo + shell: "wg genkey > /tmp/wireguard_private_{{ peer_number }}" + + - name: Create localhost interface + become: true + become_method: sudo + shell: "ip link add {{ localhost_wg_interface }} type wireguard" + + - name: Give localhost wireguard interface address 2 + become: true + become_method: sudo + shell: "ip addr add {{ local_address }}/24 dev {{ localhost_wg_interface }}" + + - name: Set localhost private key + become: true + become_method: sudo + shell: "wg set {{ localhost_wg_interface }} private-key /tmp/wireguard_private_{{ peer_number }}" + + - name: Obtain localhost public key + become: true + become_method: sudo + shell: "wg pubkey < /tmp/wireguard_private_{{ peer_number }}" + register: localhost_wireguard_public + delegate_to: localhost + - name: Install packages and utilities become: true become_method: sudo @@ -278,7 +235,7 @@ - name: Add localhost as peer become: true become_method: sudo - shell: wg set wg0 peer "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" allowed-ips 10.0.1.100/0 # Only allow server + shell: wg set wg0 peer "{{ localhost_wireguard_public.stdout }}" allowed-ips 10.0.1.100/0 # Only allow server - name: Obtain peer public key become: true @@ -289,7 +246,7 @@ - name: Add peer to localhost become: true become_method: sudo - shell: wg set wg0 peer "{{ remote_public_wireguard.stdout }}" allowed-ips 0.0.0.0/0 endpoint "{{ inventory_hostname }}:{{ port }}" # Allow to route everything + shell: wg set "{{ localhost_wg_interface }}" peer "{{ remote_public_wireguard.stdout }}" allowed-ips 0.0.0.0/0 endpoint "{{ inventory_hostname }}:{{ port }}" # Allow to route everything delegate_to: localhost - name: Copy FireQoS config @@ -312,14 +269,20 @@ - name: IP Table Rule become: true become_method: sudo - command: iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE + command: iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["6", "A", "B"] + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "5"] tags: [ install ] # ****************** MOUNT ********************** - name: === Install === block: + - name: Start localhost wireguard interface + become: true + become_method: sudo + shell: "ip link set {{ localhost_wg_interface }} up" + delegate_to: localhost + - name: Bring wg0 interface up become: true become_method: sudo @@ -330,7 +293,7 @@ become_method: sudo command: "fireqos start" - when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["6", "7", "A", "B"] + when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "2", "5"] tags: [ install, start ] From d5fc7cd4275a5c34135375dd8913a5f5d3297f7f Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Sun, 10 May 2020 11:14:21 +0200 Subject: [PATCH 04/11] Use tunX instead of wgX for compatibility's sake --- ansible_scripts/vpNator-wireguard.yml | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator-wireguard.yml index aeb1c97..abdd385 100644 --- a/ansible_scripts/vpNator-wireguard.yml +++ b/ansible_scripts/vpNator-wireguard.yml @@ -33,7 +33,7 @@ peer_number: "{{ hostvars[inventory_hostname].id }}" tunnel_address: "10.8.{{ peer_number }}.1" local_address: "10.8.{{ peer_number }}.2" - localhost_wg_interface: "wg{{ peer_number }}" + localhost_wg_interface: "tun{{ peer_number }}" port: 5010 packages: ['bc', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] localhost_wireguard_public: "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" @@ -44,7 +44,7 @@ - name: Bring remote wireguard interface down become: true become_method: sudo - command: ip link set wg0 down + command: ip link set tun0 down ignore_errors: true - name: Stop FireQOS on remote @@ -98,7 +98,7 @@ - name: Delete remote wireguard interface become: true become_method: sudo - shell: ip link del wg0 + shell: ip link del tun0 ignore_errors: true # We don't care if it doesn't exist @@ -210,17 +210,17 @@ - name: Create remote interface become: true become_method: sudo - shell: ip link add wg0 type wireguard + shell: ip link add tun0 type wireguard - name: Give remote interface an address become: true become_method: sudo - shell: ip addr add "{{ tunnel_address }}/24" dev wg0 + shell: ip addr add "{{ tunnel_address }}/24" dev tun0 - name: Set remote wireguard port become: true become_method: sudo - shell: wg set wg0 listen-port 5010 + shell: wg set tun0 listen-port 5010 - name: Create wireguard private key become: true @@ -230,12 +230,12 @@ - name: Set wireguard private key become: true become_method: sudo - shell: wg set wg0 private-key /root/wireguard_private + shell: wg set tun0 private-key /root/wireguard_private - name: Add localhost as peer become: true become_method: sudo - shell: wg set wg0 peer "{{ localhost_wireguard_public.stdout }}" allowed-ips 10.0.1.100/0 # Only allow server + shell: wg set tun0 peer "{{ localhost_wireguard_public.stdout }}" allowed-ips 10.0.1.100/0 # Only allow server - name: Obtain peer public key become: true @@ -256,10 +256,10 @@ src: templates/fireqos_vpn.conf dest: /etc/firehol/fireqos.conf - - name: Substitute 'tun0' for 'wg0' - become: true - become_method: sudo - shell: sed -i "s/tun0/wg0/" /etc/firehol/fireqos.conf + #- name: Substitute 'tun0' for 'wg0' + #become: true + #become_method: sudo + #shell: sed -i "s/tun0/wg0/" /etc/firehol/fireqos.conf - name: Enable IP Forward become: true @@ -283,10 +283,10 @@ shell: "ip link set {{ localhost_wg_interface }} up" delegate_to: localhost - - name: Bring wg0 interface up + - name: Bring tun0 interface up become: true become_method: sudo - shell: ip link set wg0 up + shell: ip link set tun0 up - name: Start FireQOS become: true From 9197ada438668c916859d03f48ac20662090ed7f Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Thu, 14 May 2020 15:24:51 +0200 Subject: [PATCH 05/11] Documentation for VPNator-WireGuard --- README.md => README-openvpn.md | 4 +- README-wireguard.md | 183 ++++++++++++++++++++++++++ ansible_scripts/vpNator-wireguard.yml | 1 + 3 files changed, 186 insertions(+), 2 deletions(-) rename README.md => README-openvpn.md (99%) create mode 100644 README-wireguard.md diff --git a/README.md b/README-openvpn.md similarity index 99% rename from README.md rename to README-openvpn.md index 862ff31..4c0a6ff 100644 --- a/README.md +++ b/README-openvpn.md @@ -4,7 +4,7 @@ The purpose of this playbook, affectionately referred to as "VPNator", is to hel When run naively, ```bash -ansible-playbook vpNator.yml +ansible-playbook vpNator-ovpn.yml ``` this playbook prompts you for an action. You can : @@ -57,7 +57,7 @@ Various aspects of the playbook can be modified on a global level. All of these variables can be set either by modifying the playbook, or by providing extra arguments (which will override those set in the playbook) using the `-e` or `--extra-vars` option of `ansible-playbook`, followed by pairs of `key=val` strings. For example, you can change the `extra_args` variable to provide additional parameters to openvpn at launch by using ```bash -ansible-playbook vpNator.yml -e extra_args='--local 127.0.0.1' +ansible-playbook vpNator-ovpn.yml -e extra_args='--local 127.0.0.1' ``` Using additional external arguments is recommended for one-time uses. When long-term persistent modifications are needed, modify the playbook/host inventory files themselves. diff --git a/README-wireguard.md b/README-wireguard.md new file mode 100644 index 0000000..7795119 --- /dev/null +++ b/README-wireguard.md @@ -0,0 +1,183 @@ +# Ansible playbook for remote Wireguard deployment + +The purpose of this playbook, affectionately referred to as "VPNator", is to help with the remote installation, deployment, stoppage and removal of OpenVPN on Debian based systems. Currently, only up-to-date Debian 10 is sure to be supported. + +When run naively, +```bash +ansible-playbook vpNator-wireguard.yml +``` +this playbook prompts you for an action. You can : + + 1. : Install WireGuard's package (usually, to compile the associated kernel module), set up and configure + 2. : Start WireGuard and FireQOS + 3. : Stop WireGuard and FireQOS + 4. : Remove WireGuard and the other tools. + 5. : Uninstall and install everything (do a complete round of purge, install and launch) + +## Technical Details + +This part of the document contains more explicit and detailed explanations of what our playbook does, why it is written that way, and how to navigate its code. It assumes that the reader is already familiar with the basics of YAML file structures and common Ansible modules. Feel free to read the Ansible modules documentation alongside this document if needed. Some useful actions available using simple Ansible options are brought to the attention of the reader. + +### Technical generalities + +The way VPNator is typically run at INSALan, a simple call to `ansible-playbook` is enough. However, if your remote user happens to need a sudo password, or the remote SSH connection requires a password (or keys you haven't loaded), then : + - Remember to load any keys you need to before you start the playbook + ```bash + ssh-add $PATH_TO_KEY/my_key + ``` + - Launch Ansible with the `-K` or `--ask-pass` option. Ansible will prompt you for the password to use for the privilege ascension mechanism used (here, that is sudo). + +#### Adding remote hosts + +Our version of VPNator connects to any remote machine under the `vpn` inventory with the login `debian`, and no password. Remote hosts are listed in the inventory `/etc/ansible/hosts` in the following fashion : + +``` +[vpn] + +vpn1 id=1 +vpn2 id=3 +ovh1 id=100 ansible_user=bidule +``` + +The subnet used within a tunnel is computed using that id. It must be an integer between `1` and `254`. The local host will take ip address 1 on that subnet on the interface corresponding with the VPN connection, and the VPN will take address 2. + +Note that, using Ansible's terminology, `vpn1` and so on are host names (which should ideally be aliased to ip addresses/resolvable domain names) list under the same "inventory". + +The `ansible_user` host variable is a documented ansible variable that indicates what user to log in as on that specific host. It is typically set to "debian", and when undefined that is the value taken. + +#### Modifying specific variables + +Various aspects of the playbook can be modified on a global level. + - The port on which WireGuard should listen for incoming connections is modified around line 37 in the `port` variable + - The address of the remote server on the tunnel is computed using their peer id in the pattern `10.8.id.1`. The local address on that tunnel is also computed around line 35. It is `10.8.id.2`. + - The local WireGuard interface is called `tunid` where `id` is the host id, and that can be changed in the `localhost_wg_interface` interface. + +All of these variables can be set either by modifying the playbook, or by providing extra arguments (which will override those set in the playbook) using the `-e` or `--extra-vars` option of `ansible-playbook`, followed by pairs of `key=val` strings. For example, you can change the `extra_args` variable to change the port WireGuard listens on +```bash +ansible-playbook vpNator-wireguard.yml -e port=5010 +``` + +Using additional external arguments is recommended for one-time uses. When long-term persistent modifications are needed, modify the playbook/host inventory files themselves. + +#### Limiting playbook runs + +It is also possible to limit the playbook's run to a particular machine among the group `vpn` (or any other you have chosen to use instead), using the `-l` option and providing individual hostnames or any pattern. Ansible will match individuals in the original pool of hosts and their data against that pattern, and only run the playbook for those successfully matched. + +***BE CAREFUL : When limiting ansible to a set of hosts that does not contain localhost, the menu asking you which operation you want to execute will be bypassed. This can be fixed by adding 'localhost' in the list of restricted hosts, or by providing activity tags.*** + +### Structure of the Playbook + +This Ansible playbook is designed such that actions requested by the user will always follow the logical order they should be executed in. + +When run without tags, the playbook launches a local play that prompts the user for an action to take (i.e. what flags to set). This play sits almost at the top of the playbook in order to be executed first when needed. + +Once appropriate flags are set, the playbook begins. In order, it can + 1. Bring remote and local WireGuard interfaces down, destroying all connections + 2. Stop FireQOS + 3. Uninstall and purge FireQOS + 4. Remove the forwarding networking rules + 5. Uninstalling WireGuard and purging its configuration + 6. Remove the interfaces we created for WireGuard, destroying their configuration, and removing the copies of the private keys we generated + 7. Install additional packages typically used in production to probe and operate on the VPS + 8. Generating and storing local and remote encryption keys on the hard drive + 9. Installing the linux headers and DKMS modules for WireGuard, along with the utility called `wg` + 10. Creating and setting up network interfaces on both ends + 11. Set up addresses on both ends + 12. Let both ends know about each other + 13. Enable IP forwarding on the system + 14. Add IP table rules to masquerade outbound traffic + 15. Bring the WireGuard interfaces up on the VPS, and locally + 16. Start FireQOS + +You may notice that operations 1-2 correspond to a typical "stop", operations 3-6 correspond to an uninstallation, 7-14 to an installation, and 15-16 to a start. This way, when the user requests one of these operations, Ansible simply examines the conditions between the groups of actions above and skips over those that do not correspond. When an operation like a "stop & purge" is requested, everything beyond operation 6 is skipped. This way, none of the typical operations needed to handle VPN deployment and maintenance requires more than a single run of the playbook. + +In the next subsection, we go over the technical details of each of these actions the user can take and how they are realized. + +### Available actions + +#### Stopping + + - **What does it do?** To be quite explicit, this action brings the remote and local WireGuard networking interfaces on the VPS and local host, and disables FireQOS. + + - **How is it done?** After calling `ip link set X down`, the interfaces still exist, and their configuration remains, but all connections are suddenly killed. + FireQOS has a command called `stop` that simply disables it. + + - **How do I know it worked?** On both the VPS and the local host, you should observe that the virtual network devices `tunX` (or whatever you're calling it) associated with your VPN are no longer 'Up'. You can check that with `ip -c a` or `ip link`. There is no specific way to detect whether FireQOS is stopped or not (since it's not a running process), but we have never observed any failure with `fireqos stop` unless FireQOS wasn't started in the first place. + +#### Uninstallation + + - **What does it do?** This action removes the `wireguard` package, additional tools and any other file we may have transferred to run our VPN. + + - **How is it done?** Most of the file deletion operations are run using Ansible's `file` module. FireQOS' configuration file is the first to go. + Then the IP rule we added in order to enable Native Address Translation (or NAT) is removed using `iptables -t nat -D ...` (since our rule is present in the `nat` table; the omitted part contains the exact copy of a rule explained in details in the Installation operation). + Until recently, most of the tools typically used by people at INSALan were removed and purged alongside WireGuard's packet using the `apt` module. This has caused issues in testing since some of these are dependencies for other programs, and caused massive interference (especially since a purge of configuration and data was requested). Notably, `openssl` is listed in the `packages` variable, and is a dependency for many IM server daemons. + + - **How do I know it worked?** If this operation worked, `apt search wireguard` should show you that the package `wireguard` is no longer installed. You should see none of the nodes listed above in the file system. Running `sudo iptables-save` should not show you the iptable line used to enable NAT. + +#### Installation + + - **What does it do?** This action installs our typically useful programs, then WireGuard. It copies some configuration files over to the VPS, for FireQOS. Some network rules are set as well. + + - **How is it done?** First and foremost the `apt` module is used to install all of the packages in the `packages` variable, after an update of the package list is done. This is typically very slow, especially if it is run for the first time on a VPS. + + ***An important note on our call to the apt module*** : We pass the option `install_recommends` and set it to `false` since one of the recommended packages of `fireqos`, `firehol` (but not `firehol-common`), has a habit of ***locking us outside of the VPS***. We prevent this package from being installed, and especially run, by asking ansible not to install recommended packages alongside the ones we ask it to install. It also makes things run a little faster. + + The configuration file `template/fireqos_vpn.conf` is sent over to the server in `/etc/firehold/`. Note that the remote interface name is hardcoded in order to be compatible with the OpenVPN version of this playbook. + + ***Building the kernel module*** + WireGuard works using a kernel module that is responsible for handling very specific types of network interfaces. One can create these interfaces with + ```sh + ip link add wg0 type wireguard + ``` + And this what we do. But in order for this to work, the type `wireguard` must be known to the Linux kernel at run-time. A kernel module called `wireguard.ko` must be loaded. You can check whether that is the case using + ```sh + lsmod | grep wireguard + ``` + Installing the `wireguard` apt package does not necessarily install said module. With versions of Debian that have a linux kernel above version `5.6.0`, the module is shipped along with your installation of the kernel. If that is not the case (and currently for Debian 10 stable without backports it isn't), the `wireguard` package depends on another one called `wireguard-dkms`. When it is unpacked, it compiles the source code for the `wireguard` kernel module, installs it and loads it. Note that `bc` is a required dependency, that is why it is listed among the needed tools. + + Since kernel module compilation requires a version of the linux kernel headers that matches the kernel currently running, it is your job when deploying WireGuard to know whether the headers corresponding to the currently running version can be installed. Our playbook does attempt to use `uname -r` to figure out the kernel version and install the appropriate headers. If you cannot, it will be impossible to run WireGuard without excruciating pain. + + + ***Network Forwarding Rules*** + + IP forwarding is enabled by the kernel after the value 1 is "cat-ed" into `/proc/sys/net/ipv4/ip_forward`. Only IPv4 forwarding is enabled, but this is not (yet) an issue. + + Finally, an IP table rule is added to the IP table `nat` in the `POSTROUTING` chain to enable masquerading (i.e. substitution of actual sender address in IP headers with the machine's IP and the opposite for inbound traffic) whenever a packet leaves through the external network interface (usually called `en*` or `eth*` in Debian). + + - **How do I know it worked?** Typically when everything here worked out, Ansible will not show any error, and you'll notice that every file mentioned above is at the right place. Running `iptables-save` will show the IP table rule line mentioned (at least once). + +#### Launching + + - **What does it do?** The last action brings both WireGuard interfaces up and starts FireQOS on both the local and remote hosts. + - **How is it done?** The following command brings the interface `wg69` up + ```bash + ip link set wg69 up + ``` + This is done on both local and remote hosts, such that connections can be initiated using both interfaces. + + And finally FireQOS is started by simply using `fireqos start` on the remote host. Remember that FireQOS does not actually run a process, so `fireqos start` simply enables the tools we use for monitoring on remote systems. + + - **How do I know it worked?** The network interfaces `tunX` (or whatever you called it) should be 'UP' on both ends of the tunnel. You can check using `ip -c a` (or any command to display the list of network interfaces). You should be able to log onto the remote host by using the following command where `
` stands for the IPv4 address assigned to the `tunX` network interface of said remote host + ```bash + ssh debian@
+ ``` + If this works you have successfully logged onto the remote host through the VPN tunnel. Further testing is usually done by trying to access the global internet (typically websites such as your favorite search engine) in order to check that the VPN actually forwards network traffic. + +### An explanation of the IP table rule + +As described in the documentation of `iptables`, the `nat` table is consulted whenever a packet creates a new connection. When NAT is enabled at that stage, the remainder of the connection is done under the impression of the external peer (i.e. not our VPS) that it is legitimately talking to the VPS itself, when, in actuality, some packages are emitted by players in the LAN, rise into our VPN tunnels, exit at the VPS, undergo masquerade, and then leave. + +Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that openvpn, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through OpenVPN*, it makes sense to only masquerade those packets that will leave for the `en+` interface. + +All of these requirements explain the following line of IP table rule : +```bash +iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE +``` + +What is not explained here is simple `iptables` syntax : the `-t` parameter gives the table (here it's `nat`), the `-A` parameter indicates that we should append to the chain provided (here `POSTROUTING`) and the rest is the rule. Whenever the requirements (that a packet establishing a connection be about to leave for the outside world, the action listed after `-j` is + +## Common troubleshooting + +The following is a short list of typical errors that are encountered while using VPNator. + - Skipped errors during stop operation : VPNator tries to `ip link set down` on the remote host when they don't exist. If WireGuard is not running (either because you haven't started it yet or because you manually killed it, or maybe it crashed but we've never seen that yet), this will fail, and Ansible will catch that failure. To prevent the playbook from failing when that happens, this error is ignored. The same thing is done for `fireqos stop`. + - No traffic goes through one or more of the VPNs but the tunnels work : this should not happen any more, but do check that both the IP table rule and IP forwarding are set as expected. diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator-wireguard.yml index abdd385..3fbdec2 100644 --- a/ansible_scripts/vpNator-wireguard.yml +++ b/ansible_scripts/vpNator-wireguard.yml @@ -35,6 +35,7 @@ local_address: "10.8.{{ peer_number }}.2" localhost_wg_interface: "tun{{ peer_number }}" port: 5010 + # bc is required to build wireguard-dkms, but not an apt dependency packages: ['bc', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] localhost_wireguard_public: "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" tasks: # ****************************** UNINSTALL ************************************* From c32cf44a17336ee4a8a66ac0504dc96a429772ca Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Thu, 14 May 2020 20:30:34 +0200 Subject: [PATCH 06/11] Missed some "OpenVPN"s in doc --- README-wireguard.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README-wireguard.md b/README-wireguard.md index 7795119..30fecb3 100644 --- a/README-wireguard.md +++ b/README-wireguard.md @@ -1,6 +1,6 @@ # Ansible playbook for remote Wireguard deployment -The purpose of this playbook, affectionately referred to as "VPNator", is to help with the remote installation, deployment, stoppage and removal of OpenVPN on Debian based systems. Currently, only up-to-date Debian 10 is sure to be supported. +The purpose of this playbook, affectionately referred to as "VPNator", is to help with the remote installation, deployment, stoppage and removal of WireGuard on Debian based systems. Currently, only up-to-date Debian 10 is sure to be supported. When run naively, ```bash @@ -167,7 +167,7 @@ In the next subsection, we go over the technical details of each of these action As described in the documentation of `iptables`, the `nat` table is consulted whenever a packet creates a new connection. When NAT is enabled at that stage, the remainder of the connection is done under the impression of the external peer (i.e. not our VPS) that it is legitimately talking to the VPS itself, when, in actuality, some packages are emitted by players in the LAN, rise into our VPN tunnels, exit at the VPS, undergo masquerade, and then leave. -Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that openvpn, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through OpenVPN*, it makes sense to only masquerade those packets that will leave for the `en+` interface. +Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that WireGuard, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through WireGuard*, it makes sense to only masquerade those packets that will leave for the `en+` interface. All of these requirements explain the following line of IP table rule : ```bash From 0e5c694112234221ff31681e182406700dce7e3a Mon Sep 17 00:00:00 2001 From: NathanPERIER <47391572+NathanPERIER@users.noreply.github.com> Date: Wed, 29 Sep 2021 11:56:08 +0200 Subject: [PATCH 07/11] Fixed name of ansible task group --- ansible_scripts/vpNator-wireguard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator-wireguard.yml index 3fbdec2..14154ec 100644 --- a/ansible_scripts/vpNator-wireguard.yml +++ b/ansible_scripts/vpNator-wireguard.yml @@ -276,7 +276,7 @@ tags: [ install ] # ****************** MOUNT ********************** - - name: === Install === + - name: === Start === block: - name: Start localhost wireguard interface become: true From 5fa8272da9f21333d9f7a29dd146564d5b415c32 Mon Sep 17 00:00:00 2001 From: Lymkwi Date: Mon, 4 Oct 2021 10:10:07 +0200 Subject: [PATCH 08/11] Fix 'eth*' not caught in OVPN iptables rule --- README-openvpn.md | 4 ++-- README-wireguard.md | 6 +++--- ansible_scripts/vpNator-ovpn.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README-openvpn.md b/README-openvpn.md index 4c0a6ff..9dd0306 100644 --- a/README-openvpn.md +++ b/README-openvpn.md @@ -173,11 +173,11 @@ In the next subsection, we go over the technical details of each of these action As described in the documentation of `iptables`, the `nat` table is consulted whenever a packet creates a new connection. When NAT is enabled at that stage, the remainder of the connection is done under the impression of the external peer (i.e. not our VPS) that it is legitimately talking to the VPS itself, when, in actuality, some packages are emitted by players in the LAN, rise into our VPN tunnels, exit at the VPS, undergo masquerade, and then leave. -Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that openvpn, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through OpenVPN*, it makes sense to only masquerade those packets that will leave for the `en+` interface. +Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that openvpn, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through OpenVPN*, it makes sense to only masquerade those packets that will leave for the `e+` interface. All of these requirements explain the following line of IP table rule : ```bash -iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE +iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE ``` What is not explained here is simple `iptables` syntax : the `-t` parameter gives the table (here it's `nat`), the `-A` parameter indicates that we should append to the chain provided (here `POSTROUTING`) and the rest is the rule. Whenever the requirements (that a packet establishing a connection be about to leave for the outside world, the action listed after `-j` is diff --git a/README-wireguard.md b/README-wireguard.md index 30fecb3..b323ae9 100644 --- a/README-wireguard.md +++ b/README-wireguard.md @@ -8,7 +8,7 @@ ansible-playbook vpNator-wireguard.yml ``` this playbook prompts you for an action. You can : - 1. : Install WireGuard's package (usually, to compile the associated kernel module), set up and configure + 1. : Install WireGuard's package (usually, to compile the associated kernel module), set up and configure 2. : Start WireGuard and FireQOS 3. : Stop WireGuard and FireQOS 4. : Remove WireGuard and the other tools. @@ -167,11 +167,11 @@ In the next subsection, we go over the technical details of each of these action As described in the documentation of `iptables`, the `nat` table is consulted whenever a packet creates a new connection. When NAT is enabled at that stage, the remainder of the connection is done under the impression of the external peer (i.e. not our VPS) that it is legitimately talking to the VPS itself, when, in actuality, some packages are emitted by players in the LAN, rise into our VPN tunnels, exit at the VPS, undergo masquerade, and then leave. -Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that WireGuard, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through WireGuard*, it makes sense to only masquerade those packets that will leave for the `en+` interface. +Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that WireGuard, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through WireGuard*, it makes sense to only masquerade those packets that will leave for the `e+` interface. All of these requirements explain the following line of IP table rule : ```bash -iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE +iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE ``` What is not explained here is simple `iptables` syntax : the `-t` parameter gives the table (here it's `nat`), the `-A` parameter indicates that we should append to the chain provided (here `POSTROUTING`) and the rest is the rule. Whenever the requirements (that a packet establishing a connection be about to leave for the outside world, the action listed after `-j` is diff --git a/ansible_scripts/vpNator-ovpn.yml b/ansible_scripts/vpNator-ovpn.yml index 5f734f8..aa34cbb 100644 --- a/ansible_scripts/vpNator-ovpn.yml +++ b/ansible_scripts/vpNator-ovpn.yml @@ -77,7 +77,7 @@ - name: Remove IP Table rule for NAT become: true become_method: sudo - command: iptables -t nat -D POSTROUTING -o en+ -j MASQUERADE + command: iptables -t nat -D POSTROUTING -o e+ -j MASQUERADE ignore_errors: true #- name: Uninstall additional tools From 32dfeef9dcb147a09e12605a6fc62d7536e45f8b Mon Sep 17 00:00:00 2001 From: Solanum Date: Sun, 29 Sep 2024 22:26:33 +0200 Subject: [PATCH 09/11] Working with wireguard/nftables + Removed OpenVPN --- README-openvpn.md | 204 --------------- README-wireguard.md => README.md | 0 ansible_scripts/vpNator-ovpn.yml | 240 ------------------ .../{vpNator-wireguard.yml => vpNator.yml} | 13 +- 4 files changed, 9 insertions(+), 448 deletions(-) delete mode 100644 README-openvpn.md rename README-wireguard.md => README.md (100%) delete mode 100644 ansible_scripts/vpNator-ovpn.yml rename ansible_scripts/{vpNator-wireguard.yml => vpNator.yml} (95%) diff --git a/README-openvpn.md b/README-openvpn.md deleted file mode 100644 index 9dd0306..0000000 --- a/README-openvpn.md +++ /dev/null @@ -1,204 +0,0 @@ -# Ansible playbook for remote OpenVPN deployment - -The purpose of this playbook, affectionately referred to as "VPNator", is to help with the remote installation, deployment, stoppage and removal of OpenVPN on Debian based systems. Currently, only Debian 9 is sure to be supported. - -When run naively, -```bash -ansible-playbook vpNator-ovpn.yml -``` -this playbook prompts you for an action. You can : - - 1. : Install OpenVPN's package, copy the configuration files required over to the server, launch and mount the tunnels. - 2. : Remove OpenVPN and wipe the configuration clean. - 3. : Stop, remove, install, and restart OpenVPN (doing step 2 then 3). - 4. : Stop OpenVPN without uninstall it. - 5. : Start OpenVPN (requires prior installation) - -## Technical Details - -This part of the document contains more explicit and detailed explanations of what our playbook does, why it is written that way, and how to navigate its code. It assumes that the reader is already familiar with the basics of YAML file structures and common Ansible modules. Feel free to read the Ansible modules documentation alongside this document if needed. Some useful actions available using simple Ansible options are brought to the attention of the reader. - -### Technical generalities - -The way VPNator is typically run at INSALan, a simple call to `ansible-playbook`, passing any flags if needed, is enough. However, if your remote user happens to need a sudo password, or the remote SSH connection requires a password (or keys you haven't loaded), then : - - Remember to load any keys you need to before you start the playbook - ```bash - ssh-add $PATH_TO_KEY/my_key - ``` - - Launch Ansible with the `-k` or `--ask-pass` option. Ansible will prompt you for the password to use for the privilege ascension mechanism used (here, that is sudo). - -#### Adding remote hosts - -Our version of VPNator connects to any remote machine under the `vpn` inventory with the login `debian`, and no password. Remote hosts are listed in the inventory `/etc/ansible/hosts` in the following fashion : - -``` -[vpn] - -vpn1 subnet=$SUBNET1 fournisseur=vpn1 ansible_user=debian -vpn2 subnet=$SUBNET2 fournisseur=vpn2 ansible_user=debian -ovh1 subnet=$SUBNET3 fournisseur=ovh1 ansible_user=debian -``` - -where `$SUBNETX` is a subnet prefix in the likes of `10.8.2.0`. The local host will take ip address 1 on that subnet on the interface corresponding with the VPN connection, and the VPN will take address 2. - -Note that, using Ansible's terminology, `vpn1` and so on are host names (which should ideally be aliased to ip addresses/resolvable domain names) list under the same "inventory". - -The `fournisseur` key is later only referred to as `ovpnNumber` in the playbook or "VPN number" in this document. - -The `ansible_user` host variable is a documented ansible variable that indicates what user to log in as on that specific host. It is typically set to "debian", and when undefined that is the value taken. - -#### Modifying specific variables - -Various aspects of the playbook can be modified on a global level. - - The port on which OpenVPN should listen for incoming connections is modified around line 35 in the `port` variable - - The protocol used by OpenVPN to transmit data is determined by the variable `protocol` - - The variable `ovpnNumber` contains a unique identifier used to distinguish files from a specific VPS and its associated VPN - - Default arguments passed to OpenVPN are defined by the variable `default_args`. Additional arguments should be written in `extra_args`. - -All of these variables can be set either by modifying the playbook, or by providing extra arguments (which will override those set in the playbook) using the `-e` or `--extra-vars` option of `ansible-playbook`, followed by pairs of `key=val` strings. For example, you can change the `extra_args` variable to provide additional parameters to openvpn at launch by using -```bash -ansible-playbook vpNator-ovpn.yml -e extra_args='--local 127.0.0.1' -``` - -Using additional external arguments is recommended for one-time uses. When long-term persistent modifications are needed, modify the playbook/host inventory files themselves. - -#### Limiting playbook runs - -It is also possible to limit the playbook's run to a particular machine among the group `vpn` (or any other you have chosen to use instead), using the `-l` option and providing individual hostnames or any pattern. Ansible will match individuals in the original pool of hosts and their data against that pattern, and only run the playbook for those successfully matched. - -***BE CAREFUL : When limiting ansible to a set of hosts that does not contain localhost, the menu asking you which operation you want to execute will be bypassed. This can be fixed by adding 'localhost' in the list of restricted hosts.*** - -### Structure of the Playbook - -This Ansible playbook is designed such that actions requested by the user will always follow the logical order they should be executed in. - -When run without flags, the playbook launches a local play that prompts the user for an action to take (i.e. what flags to set). This play sits almost at the top of the playbook in order to be executed first when needed. - -Once appropriate flags are set, the playbook begins. In order, it can - 1. Kill OpenVPN on both remote and local hosts - 2. Stop FireQOS from running - 3. Uninstall tools and their configuration files (mostly FireQOS) - 4. Remove the iptable rules that masquerades outbound traffic - 5. Uninstalling OpenVPN and purging its configuration - 6. Remove more directories in which we store OpenVPN files (.ovpn) or the certificate generation script, or more configuration files - 7. Install additional packages typically used in production to probe and operate on the VPS - 8. Create a folder dedicated to storing VPN files - 9. Copy and run a certificate generation script - 10. Customize and copy OpenVPN configuration files - 11. Repatriate the customized OpenVPN files from the VPS - 12. Enable IP forwarding on the system - 13. Add IP table rules to masquerade outbound traffic - 14. Start OpenVPN on the VPS, and locally - 15. Start FireQOS - -You may notice that operations 1-2 correspond to a typical "stop", operations 3-6 correspond to an uninstallation, 7-13 to an installation, and 14-15 to a start. This way, when the user requests one of these operations, Ansible simply examines the conditions between the groups of actions above and skips over those that do not correspond. When an operation like a "stop & purge" is requested, everything beyond operation 6 is skipped. This way, none of the typical operations needed to handle VPN deployment and maintenance requires more than a single run of the playbook. - -In the next subsection, we go over the technical details of each of these actions the user can take and how they are realized. - -### Available actions - -#### Stopping - - - **What does it do?** To be quite explicit, this action kills any process called `openvpn` on the VPS and local host, and disables FireQOS. - - - **How is it done?** The killing of openvpn on remote hosts is simply done using `pkill openvpn`. It sens a `SIGTERM` signal to the openvpn process, which handles it as a shutdown request. - - Locally, since a single openvpn process deals with a single VPN connection, we restrict the killing process by matching a pattern against the full command line that launched openvpn, containing the name (and VPN number) of the configuration file for the specific connection we want to terminate. - - FireQOS has a command called `stop` that simply disables it. - - - **How do I know it worked?** On both the VPS and the local host, you should observe that the virtual network devices `tunX` associated with your VPN are no longer present. A `pgrep openvpn` on the VPS should yield nothing. There is no specific way to detect whether FireQOS is stopped or not (since it's not a running process), but we have never observed any failure with `fireqos stop`. - -#### Uninstallation - - - **What does it do?** This action removes the `openvpn` package, configuration files, additional tools and any other file we may have transferred to run our VPN. - - - **How is it done?** Most of the file deletion operations are run using Ansible's `file` module. FireQOS' configuration file is the first to go. - Then the IP rule we added in order to enable Native Address Translation (or NAT) is removed using `iptables -t nat -D ...` (since our rule is present in the `nat` table; the omitted part contains the exact copy of a rule explained in details in the Installation operation). - Until recently, most of the tools typically used by people at INSALan were removed and purged alongside OpenVPN's packet using the `apt` module. This has caused issues in testing since some of these are dependencies for other programs, and caused massive interference (especially since a purge of configuration and data was requested). Notably, `openssl` is listed in the `packages` variable, and is a dependency for many IM server daemons. That being said, OpenVPN is still removed (but not purged), and later more files we sent over to run it are removed : - - `/etc/openvpn` which contains all of the configuration OpenVPN uses - - `/etc/sysctl.d/30-openvpn-forward.conf` which contains (we assume) system rules related to the forwarding of packets to and from openvpn. - - `/home/vpn` where the certificate generation script is copied and run - - `/home/client-{{ ovpnNumber }}` which is the OpenVPN configuration file for the specific VPN with number `ovpnNumber` - - - **How do I know it worked?** If this operation worked, `apt search openvpn` should show you that the package `openvpn` is not installed. You should see none of the nodes listed above in the file system. Running `sudo iptables-save` should not show you the iptable line used to enable NAT (although if it does it is not a problem). - -#### Installation - - - **What does it do?** This action installs our typically useful programs, then OpenVPN. It copies a lot of configuration files over to the VPS, and a bash script to generate a certificate. Some network rules are set as well. - - - **How is it done?** First and foremost the `apt` module is used to install all of the packages in the `packages` variable, after an update of the package list is done. This is typically very slow, especially if it is run for the first time on a VPS. - - ***An important note on our call to the apt module*** : We pass the option `install_recommends` and set it to `false` since one of the recommended packages of `fireqos`, `firehol` (but not `firehol-common`), has a habit of ***locking us outside of the VPS***. We prevent this package from being installed, and especially run, by asking ansible not to install recommended packages alongside the ones we ask it to install. It also makes things run a little faster. - - A directory called `vpn` is created in the `home` directory using the `file` module. Then, a certificate generation script is customized and copied over to the VPS where it is run. More files are copied as well : - - `server.conf.j2` is customized and copied to `/etc/openvpn` to act as the configuration file for the openvpn server - - `client.conf.j2` is customized and copied to `/home/vpn` to act as the configuration file for a specific openvpn client - - `dh.pem` is copied to `/etc/openvpn`. It is a typical Diffie-Hellman parameters file which seems to contain a certificate. For our purposes, we do not care about its strength or privacy. - - When we talk about "customizing", the reader should understand that specific fields delineated by `{{...}}` tags are detected in the files mentioned for the value stored in the variable `...` by Ansible. This way, for example, the `client.conf.j2` file can take the name `client-?.ovpn` where `?` is the VPN number corresponding to a specific instance. - - Once all of this is in place, the `.ovpn` client configuration file is modified once more so that the `tun` device name takes on a unique number at the end. That number corresponds to the place of a specific instance in the inventory of hosts listed in the `vpn` group. When this modification is done, that `.ovpn` file is copied back onto the local host into a folder called `openvpn_files` (which is created when it does not exist), located above the directory where Ansible is run. - - IP forwarding is enabled by the kernel after the value 1 is "cat-ed" into `/proc/sys/net/ipv4/ip_forward`. Only IPv4 forwarding is enabled, but this is not (yet) an issue. - - Finally, an IP table rule is added to the IP table `nat` in the `POSTROUTING` chain to enable masquerading (i.e. substitution of actual sender address in IP headers with the machine's IP and the opposite for inbound traffic) whenever a packet leaves through the external network interface (usually called `en*` in Debian). - - - **How do I know it worked?** Typically when everything here worked out, Ansible will not show any error, and you'll notice that every file mentioned above is at the right place. Running `iptables-save` will show the IP table rule line mentioned (at least once), and the `.ovpn` files will all be present in `openvpn_files` above the directory where you ran the playbook. - -#### Launching - - - **What does it do?** The last action launches OpenVPN and FireQOS on both the local and remote hosts. - - **How is it done?** On the remote host, OpenVPN is started by running the OpenVPN binary. The only argument provided is the path to the server configuration file uploaded during installation. - ```bash - /usr/sbin/openvpn --config /etc/openvpn/server.conf --daemon - ``` - The `daemon` option ensures that `openvpn` forks and runs in the background, detached from the shell access granted to the Ansible 'command' module. - - Locally, OpenVPN is launched with a couple of different options. Among the default arguments are : - - The path to the client configuration file `client-{{ ovpnNumber }}.ovpn` for obvious reasons - - `route-noexec` is a flag that prevents OpenVPN from setting IP routes automatically on the client to redirect network traffic. Other scripts in the deployment procedure at INSALan handle IP rules and IP routes in exactly the way we want to. - - `daemon` such that OpenVPN runs in the background. - - Supplementary arguments can be added by writing them in the `extra_args` variable in the beginning of the playbook. - - And finally FireQOS is started by simply using `fireqos start` on the remote host. Remember that FireQOS does not actually run a process, so `fireqos start` simply enables the tools we use for monitoring on remote systems. - - **How do I know it worked?** A `pgrep` on both the remote and local hosts should yield at least one process ID on each machine (and hopefully only one). The network interfaces `tunX` should be visible on both ends of the tunnel using `ip -c a` (or any command to display the list of network interfaces). You should be able to log onto the remote host by using the following command where `
` stands for the IPv4 address assigned to the `tunX` network interface of said remote host - ```bash - ssh debian@
- ``` - If this works you have successfully logged onto the remote host through the VPN tunnel. Further testing is usually done by trying to access the global internet (typically websites such as your favorite search engine) in order to check that the VPN actually forwards network traffic. - -### An explanation of the IP table rule - -As described in the documentation of `iptables`, the `nat` table is consulted whenever a packet creates a new connection. When NAT is enabled at that stage, the remainder of the connection is done under the impression of the external peer (i.e. not our VPS) that it is legitimately talking to the VPS itself, when, in actuality, some packages are emitted by players in the LAN, rise into our VPN tunnels, exit at the VPS, undergo masquerade, and then leave. - -Specifically, the `POSTROUTING` chain is called just as packets are about to leave. This makes sense, since we only wish to masquerade packets that openvpn, running on the VPS, emits to the outside world, stemming from data circulating in the tunnels. Moreover, those packets should only be masqueraded whenever they leave the VPS and try and contact the outside world. Since *we are always supposed to make first contact in any connection that transits through OpenVPN*, it makes sense to only masquerade those packets that will leave for the `e+` interface. - -All of these requirements explain the following line of IP table rule : -```bash -iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE -``` - -What is not explained here is simple `iptables` syntax : the `-t` parameter gives the table (here it's `nat`), the `-A` parameter indicates that we should append to the chain provided (here `POSTROUTING`) and the rest is the rule. Whenever the requirements (that a packet establishing a connection be about to leave for the outside world, the action listed after `-j` is - -### The certificate generation script - -During the installation procedure, a shell script called `certgen.sh` is copied remotely, and later on executed. Its inner workings are detailed in this section. - -This script is the last standing piece of shell script left from the original "Roadwarrior" OpenVPN script from which we created the ansible playbook. Both authors of the playbook decided to keep all of the operations related to certificate generation in that script, and to launch it remotely, in order to keep the playbook relatively clear. - -The script starts by downloading and inflating an archive from OpenVPN's GitHub repository for the `easy-rsa` program. The contents of that archive are then copied over to `/etc/openvpn/easyrsa` where we operate. - -Easy-RSA is used at first to generate what is called a 'PKI', or 'Public Key Infrastructure'. This is a system that wherein a Certificate Authority (CA) can receive certificate signing requests (CSRs) and generate certificates and certificate revocation lists (CRLs). These certificates are what interest us here. - -A CA is built before the script generates two certificates (one for the server and one for the client), along with a CRL. Once the CA and both certificates have been built, they are copied into `/etc/openvpn` and their content is copied into the `.ovpn` client configuration file (between appropriate tags), so that the client has copies of these certificates too. - -## Common troubleshooting - -The following is a short list of typical errors that are encountered while using VPNator. - - Skipped errors during stop operation : VPNator tries to `pkill openvpn` on the remote host. If OpenVPN is not running (either because you haven't started it yet or because you manually killed it, or maybe it crashed), this will fail, and Ansible will catch that failure. To prevent the playbook from failing when that happens, this error is ignored. The same thing is done for `fireqos stop`. - - No traffic goes through one or more of the VPNs but the tunnels work : this should not happen any more, but do check that both the IP table rule and IP forwarding are set as expected. - -## To-do list - - Upgrade Easy-RSA in the certificate generation script to version 3.0.6. diff --git a/README-wireguard.md b/README.md similarity index 100% rename from README-wireguard.md rename to README.md diff --git a/ansible_scripts/vpNator-ovpn.yml b/ansible_scripts/vpNator-ovpn.yml deleted file mode 100644 index aa34cbb..0000000 --- a/ansible_scripts/vpNator-ovpn.yml +++ /dev/null @@ -1,240 +0,0 @@ ---- - - -#FIXME: Le script tunnel.sh ne focntionne pas car le path du .ovpn est hardcodé dedans - - -# On vérifie si aucun tag n'a été défini, dans ce cas on affiche une invite pour choisir l'action a effectuer. -# Le résultat de l'user input est stocké dans hostvars.localhost.pause_result.user_input. Il vaut un string ("1", "2", etc...) si aucun tag a été défini, ou est undefined si au moins un tag a été précisé. Cette variable est TREES CHIANTE à récupérer puisqu'elle ests sur l'host 'localhost' - -# On a: hostvars.localhost.pause_result.user_input qui contient la réponse (ou qui est undefined si les tags sont utilisés) - -- hosts: localhost - gather_facts: no - tasks: - - name: Check args - pause: - prompt: "WARNING: It looks like you didn't used any tag.\n - You can use the argument '--tags tag1,tag2,...' to sepcify one or more action to run\n - What do you want to do now?\n\n - [1]: Install OpenVPN (and configure it) (--tags install)\n - [2]: Remove OpenVPN (--tags uninstall)\n - [3]: Uninstall and reinstall OpenVPN (--tags install,uninstall)\n - [4]: Stop OpenVPN (--tags stop)\n - [5]: Start the tunnels and FireqOS (--tags start)\n - [6]: Just change current config (--tags reconf)" - register: pause_result - - - - -# Et maintenant on s'occupe vraiment des VPNs -- hosts: vpn - remote_user: debian - vars: - subnet: "{{ hostvars[inventory_hostname].subnet }}" - port: 5010 - protocol: tcp - ovpnNumber: "{{ hostvars[inventory_hostname].fournisseur }}" - packages: ['openvpn', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] - # OPENVPN - default_args: --route-noexec --daemon - extra_args: - tasks: # ****************************** UNINSTALL ************************************* - - - name: === STOP === - block: - - name: Kill OpenVPN on remote - become: true - become_method: sudo - command: pkill openvpn - ignore_errors: true - - - name: Kill OpenVPN on localhost # FIXME : Figure out whether you can only kill for one tunnel - become: true - become_method: sudo - command: pkill -f "openvpn --config ../openvpn_files/client-{{ ovpnNumber }}.ovpn" - delegate_to: localhost - ignore_errors: true - - - name: Stop FireQOS on remote - become: true - become_method: sudo - command: fireqos stop - - when: (hostvars.localhost.pause_result is defined and (hostvars.localhost.pause_result.user_input == "2" or hostvars.localhost.pause_result.user_input == "3" or hostvars.localhost.pause_result.user_input == "4" or hostvars.localhost.pause_result.user_input == "6") ) or hostvars.localhost.pause_result is not defined - tags: [ uninstall, stop, reconf ] - - - name: === Uninstall === - block: - - name: Remove Additional Tools Config (FireQOS) - become: true - become_method: sudo - file: - state: absent - path: /etc/firehol/fireqos.conf - - - name: Remove IP Table rule for NAT - become: true - become_method: sudo - command: iptables -t nat -D POSTROUTING -o e+ -j MASQUERADE - ignore_errors: true - - #- name: Uninstall additional tools - #become: true - #become_method: sudo - #apt: - # state: absent - # purge: true - # name: "{{ packages }}" - # - # As it turns out, none of the tools we have really need to be - # removed that badly. We can let them sit there until the VPS - # expires. Also, this part is dangerous to use in testing on - # machines where these other packages are dependencies of - # working services, which configuration will get wiped. - # Source : the apache2 server I accidentally wiped clean from my VPS - - # Uninstall script begins here - - name: Uninstall and purge OpenVPN - become: true - become_method: sudo - apt: - name: openvpn - purge: True - state: absent - - - name: Remove OVPN config files and VPN directory - become: true - become_method: sudo - file: - state: absent - path: "{{ item }}" - loop: - - /etc/openvpn - - /etc/sysctl.d/30-openvpn-forward.conf - - /home/vpn - - /home/client-{{ ovpnNumber }} - - when: (hostvars.localhost.pause_result is defined and (hostvars.localhost.pause_result.user_input == "2" or hostvars.localhost.pause_result.user_input == "3")) or hostvars.localhost.pause_result is not defined - tags: [ uninstall ] - - - # ************************ INSTALL ***************************************** - - name: Install packages and utilities - become: true - become_method: sudo - apt: - name: "{{ packages }}" - state: present - install_recommends: false # DO NOT LET IT INSTALL FIREHOL, DO NOT. (cf. Readme) - update_cache: yes - when: (hostvars.localhost.pause_result is defined and (hostvars.localhost.pause_result.user_input == "1" or hostvars.localhost.pause_result.user_input == "3" )) or hostvars.localhost.pause_result is not defined - tags: [ install ] - - - name: === Install === - block: - - name: Create a folder for the VPN software - become: true - become_method: sudo - file: - path: /home/vpn - state: directory - - - name: Copy the key generation script - become: true - become_method: sudo - template: src=templates/certgen.sh.j2 dest=/home/vpn/certgen.sh - - - name: Copy the server.conf to the remote server - become: true - become_method: sudo - template: src=templates/server.conf.j2 dest=/etc/openvpn/server.conf - #owner=root - #mode=0777 - - - name: Copy the client.ovpn file to the server to apply the template - become: true - become_method: sudo - template: - src: templates/client.conf.j2 - dest: "/home/vpn/client-{{ ovpnNumber }}.ovpn" - - - name: Copy FireQoS config - become: true - become_method: sudo - template: - src: templates/fireqos_vpn.conf - dest: /etc/firehol/fireqos.conf - - - name: Generate the keys - become: true - become_method: sudo - command: bash /home/vpn/certgen.sh - - - name: Send dh.pem - become: true - become_method: sudo - template: - src: templates/dh.pem - dest: "/etc/openvpn/dh.pem" - - - name: Rename dev tun - become: true - become_method: sudo - lineinfile: - path: "/home/vpn/client-{{ ovpnNumber }}.ovpn" - regexp: '^dev tun' - line: "dev tun{{groups['vpn'].index(inventory_hostname) + 1}}" - - - name: Repatriate .ovpn files - become: true - become_method: sudo - fetch: - src: "/home/vpn/client-{{ ovpnNumber }}.ovpn" - dest: ../openvpn_files/ - flat: true - - - name: Enable IP Forward - become: true - become_method: sudo - shell: echo 1 > /proc/sys/net/ipv4/ip_forward - - - name: IP Table Rule - become: true - become_method: sudo - command: iptables -t nat -A POSTROUTING -o en+ -j MASQUERADE - - when: (hostvars.localhost.pause_result is defined and (hostvars.localhost.pause_result.user_input == "1" or hostvars.localhost.pause_result.user_input == "3" or hostvars.localhost.pause_result.user_input == "6")) or hostvars.localhost.pause_result is not defined - tags: [ install, reconf ] - - # ****************** MOUNT ********************** - - name: === Mounting === - block: - - name: Start openvpn on the remote server - become: true - become_method: sudo - command: - cmd: "/usr/sbin/openvpn --config /etc/openvpn/server.conf --daemon" - chdir: /etc/openvpn - #command: systemctl restart openvpn - #@server.service) - - - name: Deploy tunnels on the local host - become: true - become_method: sudo - command: "/usr/sbin/openvpn --config ../openvpn_files/client-{{ ovpnNumber }}.ovpn {{ default_args }} {{ extra_args}}" - # WARNING : If you modify the beginning of this line (between openvpn and .ovpn), remember to modify the openvpn pkill - # line for local host up above - delegate_to: localhost - - - name: Start FireQOS - become: true - become_method: sudo - command: "fireqos start" - - when: (hostvars.localhost.pause_result is defined and (hostvars.localhost.pause_result.user_input == "1" or hostvars.localhost.pause_result.user_input == "3" or hostvars.localhost.pause_result.user_input == "5" or hostvars.localhost.pause_result.user_input == "6")) or hostvars.localhost.pause_result is not defined - tags: [ install, start, reconf] - - - diff --git a/ansible_scripts/vpNator-wireguard.yml b/ansible_scripts/vpNator.yml similarity index 95% rename from ansible_scripts/vpNator-wireguard.yml rename to ansible_scripts/vpNator.yml index 14154ec..29a76fc 100644 --- a/ansible_scripts/vpNator-wireguard.yml +++ b/ansible_scripts/vpNator.yml @@ -36,7 +36,7 @@ localhost_wg_interface: "tun{{ peer_number }}" port: 5010 # bc is required to build wireguard-dkms, but not an apt dependency - packages: ['bc', 'iptables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] + packages: ['bc', 'nftables', 'openssl', 'htop', 'tmux', 'screen', 'neovim', 'hexedit', 'vim', 'emacs', 'fireqos', 'iftop', 'bmon', 'curl', 'ca-certificates', 'iperf3'] localhost_wireguard_public: "{{ hostvars.localhost.localhost_wireguard_public.stdout }}" tasks: # ****************************** UNINSTALL ************************************* @@ -76,7 +76,7 @@ - name: Remove IP Table rule for NAT become: true become_method: sudo - command: iptables -t nat -D POSTROUTING -o e+ -j MASQUERADE #FIXME catch both eth+ and en+, but better + command: nft delete chain insalan masquerading #FIXME catch both eth+ and en+, but better ignore_errors: true #- name: Uninstall additional tools @@ -221,7 +221,7 @@ - name: Set remote wireguard port become: true become_method: sudo - shell: wg set tun0 listen-port 5010 + shell: wg set tun0 listen-port "{{ port}}" - name: Create wireguard private key become: true @@ -270,7 +270,12 @@ - name: IP Table Rule become: true become_method: sudo - command: iptables -t nat -A POSTROUTING -o e+ -j MASQUERADE + shell: | + nft add table ip insalan + nft -- add chain insalan masquerading "{ type nat hook postrouting priority 100; }" + nft add rule insalan masquerading oifname "e*" masquerade + args: + executable: /bin/bash when: hostvars.localhost.pause_result is not defined or hostvars.localhost.pause_result.user_input in ["1", "5"] tags: [ install ] From 8f64e19d11728abf083c47481ad64e0e965ddc22 Mon Sep 17 00:00:00 2001 From: pixup1 Date: Mon, 30 Sep 2024 08:09:00 +0200 Subject: [PATCH 10/11] Moved vpn_register.py to VPNator --- README.md | 2 ++ vpn_register.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100755 vpn_register.py diff --git a/README.md b/README.md index b323ae9..4440a16 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ ovh1 id=100 ansible_user=bidule The subnet used within a tunnel is computed using that id. It must be an integer between `1` and `254`. The local host will take ip address 1 on that subnet on the interface corresponding with the VPN connection, and the VPN will take address 2. +This file can be generated beforehand by [vpn_register.py](vpn_register.py), which will also register the VPNs to other useful locations, like in `/etc/hosts`. + Note that, using Ansible's terminology, `vpn1` and so on are host names (which should ideally be aliased to ip addresses/resolvable domain names) list under the same "inventory". The `ansible_user` host variable is a documented ansible variable that indicates what user to log in as on that specific host. It is typically set to "debian", and when undefined that is the value taken. diff --git a/vpn_register.py b/vpn_register.py new file mode 100755 index 0000000..b8f636b --- /dev/null +++ b/vpn_register.py @@ -0,0 +1,45 @@ +#!/bin/python3 +import subprocess +etc_hosts_filename = "/etc/hosts" +etc_ansible_hosts_filename = "/etc/ansible/hosts" +etc_firehol_vpn_list_filename = "/etc/firehol/vpn_list" +vpn_list = [] +while True : + a=input("Entrez l'adresse d'un vpn (touche A pour finir) \n") + if a=="A": + break + vpn_list.append(a) + +file = open(etc_hosts_filename, "r") +file_content = file.readlines() +file_right_content = [] +for line in file_content: + if "vpn" not in line : + file_right_content.append(line) + +file.close() + +file = open(etc_hosts_filename, "w") +for line in file_right_content : + file.write(line) +i = 1 +for ip_vpn in vpn_list : + file.write(ip_vpn + " vpn" + str(i) + "\n") + i+=1 +file.close() + +file = open(etc_ansible_hosts_filename, "w") +file.write("[vpn] \n\n") +for i in range (len(vpn_list)) : + file.write("vpn" + str(i+1) + " id=" + str(i+1) + "\n") +file.close() + +file = open(etc_firehol_vpn_list_filename, "w") +for i in range(len(vpn_list)) : + file.write("vpn" + str(i+1) + "=" + vpn_list[i] +"\n") +file.close() + +for i in range(len(vpn_list)) : + s = "ssh-keygen -R vpn" + str(i+1) + subprocess.run(args = s, shell = True) + From 46c269d2349ef5117ad3cf60a9c46dc5de1be706 Mon Sep 17 00:00:00 2001 From: Claptrap Date: Sat, 14 Dec 2024 11:15:39 +0100 Subject: [PATCH 11/11] Add persistent-keepalive --- ansible_scripts/vpNator.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible_scripts/vpNator.yml b/ansible_scripts/vpNator.yml index 29a76fc..acbc611 100644 --- a/ansible_scripts/vpNator.yml +++ b/ansible_scripts/vpNator.yml @@ -247,7 +247,7 @@ - name: Add peer to localhost become: true become_method: sudo - shell: wg set "{{ localhost_wg_interface }}" peer "{{ remote_public_wireguard.stdout }}" allowed-ips 0.0.0.0/0 endpoint "{{ inventory_hostname }}:{{ port }}" # Allow to route everything + shell: wg set "{{ localhost_wg_interface }}" peer "{{ remote_public_wireguard.stdout }}" persistent-keepalive 30 allowed-ips 0.0.0.0/0 endpoint "{{ inventory_hostname }}:{{ port }}" # Allow to route everything delegate_to: localhost - name: Copy FireQoS config