diff --git a/openvpn/defaults/main.yml b/openvpn/defaults/main.yml new file mode 100644 index 00000000..f5c8f4b4 --- /dev/null +++ b/openvpn/defaults/main.yml @@ -0,0 +1,49 @@ +--- +openvpn_enabled: True +openvpn_enable_system_forward: True +openvpn_pkg_state: latest +openvpn_pkgs: + - openvpn + +openvpn_radius_auth: False +openvpn_radius_pkg: + - openvpn-auth-radius + +openvpn_ldap_auth: False +openvpn_ldap_pkg: + - openvpn-auth-ldap + +openvpn_conf_dir: /etc/openvpn +openvpn_conf_name: openvpn.conf + +openvpn_mode: server +openvpn_dev: tun +openvpn_port: 1194 +openvpn_protocol: udp +openvpn_server_net: '192.168.254.0 255.255.255.0' +openvpn_push_routes: + - '192.168.253.0 255.255.255.0' + +openvpn_tls_server: True +openvpn_dh: /etc/openvpn/dh2048.pem +openvpn_tls_auth: '/etc/openvpn/ta.key 0' +openvpn_install_alternative_ca: False +openvpn_alternative_ca_name: ca.pem +openvpn_ca: '/var/lib/acme/live/{{ ansible_fqdn }}/chain' +openvpn_cert: '/var/lib/acme/live/{{ ansible_fqdn }}/cert' +openvpn_key: '/var/lib/acme/live/{{ ansible_fqdn }}/privkey' + +openvpn_compression_enabled: False +openvpn_keepalive: '10 120' + +openvpn_cert_auth_enabled: True +openvpn_username_pam_auth: False + +openvpn_max_clients: 50 +openvpn_run_unprivileged: True +openvpn_unprivileged_user: nobody +openvpn_unprivileged_group: nogroup +openvpn_letsencrypt_managed: True + +openvpn_verbosity_log: 3 +openvpn_mute_after: 20 diff --git a/openvpn/files/ca.pem b/openvpn/files/ca.pem new file mode 100644 index 00000000..1df06a0e --- /dev/null +++ b/openvpn/files/ca.pem @@ -0,0 +1,101 @@ +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDczCCAlugAwIBAgIBADANBgkqhkiG9w0BAQUFADAuMQswCQYDVQQGEwJJVDEN +MAsGA1UEChMESU5GTjEQMA4GA1UEAxMHSU5GTiBDQTAeFw0wNjEwMDMxNDE2NDda +Fw0xNjEwMDMxNDE2NDdaMC4xCzAJBgNVBAYTAklUMQ0wCwYDVQQKEwRJTkZOMRAw +DgYDVQQDEwdJTkZOIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA +zpWODoOVnUKpyikjyrdj+QpJuoJeKkqF4fbd6LrqeQL0dqAiluVR8D4y/T2Mqvsd +H/fg0s3EYZUDQZimcAmC3ammTX3rqXOz34GWLGpXoXAmUVKWPNFJo6rAEwhw3Sja +a8mEjMiZE/JigHN5RI8K6taKtjL/jE4XUTZOGbvlKsROxzJPM6bO4GJdYO+qhK9E +5HsbV699DYyukBfUB6ChtD6GDbcdPKUKwheni5j0v6smFjiBEb3VQg4O+uBWTHMP +116L9kPY+I7ojzXLuayMTd+6TXzunR33+v6h8AtLChcQRt4vj7oG/scTg3eSnFsq +oEO4D4IF9v481GJJwg58LwIDAQABo4GbMIGYMA8GA1UdEwEB/wQFMAMBAf8wDgYD +VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTRYvOzd3LILvvyeRpvN04nnxPVIDBWBgNV +HSMETzBNgBTRYvOzd3LILvvyeRpvN04nnxPVIKEypDAwLjELMAkGA1UEBhMCSVQx +DTALBgNVBAoTBElORk4xEDAOBgNVBAMTB0lORk4gQ0GCAQAwDQYJKoZIhvcNAQEF +BQADggEBAHjX0z+3P3JyQGIBI5aAXOS3NuDEf0MdqCLFIGsXjtvIm2kDSMSGQOg5 +uZnJLTAhaT+gX5eNkDdzhuuJEgW1FPGDy2If6zgD4T4EsS50E+L5BTNOG78UzF4H +9DGBlbrkD8VEug9RpxGusSweGGlnO6CT/U1Tb3XY5ZjIrMubh09UwmjK9nEIe3vC +RPInAkbmamteezpKOqC5Knj0ZpqU+CnWkuyYnjslX1e9O5lbupLTp5NOqZRCFn1i +iTjpoNefgqLE3sHedgb2P1vS8lO+EIhRnWgfN9qAHSqkQ+ZObxIfPJFdcluu8d/K +tXsFkKmmFuEHd0SrYpBh9ZCLDgq2x9Y= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow +PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD +Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O +rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq +OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b +xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw +7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD +aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV +HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG +SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 +ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr +AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz +R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 +JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo +Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ +-----END CERTIFICATE----- diff --git a/openvpn/files/openvpn-letsencrypt-acme.sh b/openvpn/files/openvpn-letsencrypt-acme.sh new file mode 100644 index 00000000..d531c3e0 --- /dev/null +++ b/openvpn/files/openvpn-letsencrypt-acme.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +LE_SERVICES_SCRIPT_DIR=/usr/lib/acme/hooks +LE_CERTS_DIR=/var/lib/acme/live/$HOSTNAME +LE_LOG_DIR=/var/log/letsencrypt +DATE=$( date ) + +[ ! -d $LE_LOG_DIR ] && mkdir $LE_LOG_DIR +echo "$DATE" >> $LE_LOG_DIR/openvpn.log + +if [ -f /etc/default/letsencrypt ] ; then + . /etc/default/letsencrypt +else + echo "No letsencrypt default file" >> $LE_LOG_DIR/openvpn.log +fi + +echo "Reload the openvpn service" >> $LE_LOG_DIR/openvpn.log +if [ -x /bin/systemctl ] ; then + systemctl reload openvpn >> $LE_LOG_DIR/openvpn.log 2>&1 +else + service openvpn reload >> $LE_LOG_DIR/openvpn.log 2>&1 +fi + +echo "Done." >> $LE_LOG_DIR/openvpn.log + +exit 0 + diff --git a/openvpn/handlers/main.yml b/openvpn/handlers/main.yml new file mode 100644 index 00000000..fb4be8af --- /dev/null +++ b/openvpn/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Reload OpenVPN + service: name=openvpn state=reloaded + when: openvpn_enabled + diff --git a/openvpn/tasks/letsencrypt-openvpn.yml b/openvpn/tasks/letsencrypt-openvpn.yml new file mode 100644 index 00000000..fd6ddc17 --- /dev/null +++ b/openvpn/tasks/letsencrypt-openvpn.yml @@ -0,0 +1,14 @@ +--- +- name: Create the acme hooks directory if it does not yet exist + file: dest={{ letsencrypt_acme_services_scripts_dir }} state=directory owner=root group=root + when: + - openvpn_letsencrypt_managed + - letsencrypt_acme_install + tags: [ 'openvpn', 'letsencrypt' ] + +- name: Install a script that fix the letsencrypt certificate for openvpn and then reload the service + copy: src=openvpn-letsencrypt-acme.sh dest={{ letsencrypt_acme_services_scripts_dir }}/openvpn owner=root group=root mode=4555 + when: + - openvpn_letsencrypt_managed + - letsencrypt_acme_install + tags: [ 'openvpn', 'letsencrypt' ] diff --git a/openvpn/tasks/main.yml b/openvpn/tasks/main.yml new file mode 100644 index 00000000..5eac2106 --- /dev/null +++ b/openvpn/tasks/main.yml @@ -0,0 +1,4 @@ +--- +- include: openvpn.yml +- include: letsencrypt-openvpn.yml + when: openvpn_letsencrypt_managed diff --git a/openvpn/tasks/openvpn.yml b/openvpn/tasks/openvpn.yml new file mode 100644 index 00000000..341d4621 --- /dev/null +++ b/openvpn/tasks/openvpn.yml @@ -0,0 +1,84 @@ +--- +- name: Install the OpenVPN main packages + apt: pkg={{ item }} state={{ openvpn_pkg_state }} update_cache=yes + with_items: '{{ openvpn_pkgs }}' + tags: openvpn + +- name: Install the OpenVPN radius auth plugin package + apt: pkg={{ item }} state={{ openvpn_pkg_state }} + with_items: '{{ openvpn_radius_pkg }}' + when: openvpn_radius_auth + tags: openvpn + +- name: Install the OpenVPN ldap auth plugin package + apt: pkg={{ item }} state={{ openvpn_pkg_state }} + with_items: '{{ openvpn_ldap_pkg }}' + when: openvpn_ldap_auth + tags: openvpn + +- name: Install the OpenVPN PAM auth plugin + shell: cp /usr/lib/openvpn/openvpn-plugin-auth-pam.so {{ openvpn_conf_dir }}/openvpn-plugin-auth-pam.so + args: + creates: '{{ openvpn_conf_dir }}/openvpn-plugin-auth-pam.so' + when: openvpn_username_pam_auth + tags: openvpn + +- name: Remove the OpenVPN PSM auth plugin + file: dest={{ openvpn_conf_dir }}/openvpn-plugin-auth-pam.so state=absent + when: not openvpn_username_pam_auth + tags: openvpn + +- name: Create the ipp and status subdirs + file: dest={{ openvpn_conf_dir }}/{{ item }} state=directory + with_items: + - ipp + - status + tags: openvpn + +- name: Install the main OpenVPN configuration file + template: src=openvpn.conf.j2 dest={{ openvpn_conf_dir }}/{{ openvpn_conf_name }} owner=root group=root mode=0444 + notify: Reload OpenVPN + tags: openvpn + +- name: Create the dh file + shell: openssl dhparam -out {{ openvpn_conf_dir }}/dh2048.pem 2048 ; chmod 444 {{ openvpn_conf_dir }}/dh2048.pem + args: + creates: '{{ openvpn_conf_dir }}/dh2048.pem' + tags: openvpn + +- name: Create the ta key + shell: cd {{ openvpn_conf_dir }} ; openvpn --genkey --secret ta.key ; chmod 400 {{ openvpn_conf_dir }}/ta.key + args: + creates: '{{ openvpn_conf_dir }}/ta.key' + tags: openvpn + +- name: Install the alternate CA file + copy: src=ca.pem dest={{ openvpn_conf_dir }}/{{ openvpn_alternative_ca_name }} + when: openvpn_install_alternative_ca + tags: openvpn + +- name: Ensure that the OpenVPN service is enabled and running + service: name=openvpn state=started enabled=yes + when: openvpn_enabled + tags: openvpn + +- name: Ensure that the OpenVPN service is stopped and disabled + service: name=openvpn state=stopped enabled=no + when: not openvpn_enabled + tags: openvpn + +- name: Enable kernel forwarding + sysctl: name={{ item }} value=1 reload=yes state=present + with_items: + - net.ipv4.ip_forward +# - net.ipv6.conf.all.forwarding + when: openvpn_enable_system_forward + tags: openvpn + +- name: Disable kernel forwarding + sysctl: name={{ item }} value=0 reload=yes state=present + with_items: + - net.ipv4.ip_forward +# - net.ipv6.conf.all.forwarding + when: not openvpn_enable_system_forward + tags: openvpn diff --git a/openvpn/templates/openvpn.conf.j2 b/openvpn/templates/openvpn.conf.j2 new file mode 100644 index 00000000..ac782b14 --- /dev/null +++ b/openvpn/templates/openvpn.conf.j2 @@ -0,0 +1,53 @@ +mode {{ openvpn_mode }} +dev {{ openvpn_dev }} + +server {{ openvpn_server_net }} +ifconfig-pool-persist ipp/ipp.txt +{% for route in openvpn_push_routes %} +push "route {{ route }}" +{% endfor %} + +port {{ openvpn_port }} +proto {{ openvpn_protocol }} + +{% if openvpn_tls_server %} +tls-server +{% endif %} + +dh {{ openvpn_dh }} +ca {{ openvpn_ca }} +cert {{ openvpn_cert }} +key {{ openvpn_key }} +tls-auth {{ openvpn_tls_auth }} + +{% if openvpn_compression_enabled %} +comp-lzo +{% endif %} + +keepalive {{ openvpn_keepalive }} + +{% if not openvpn_cert_auth_enabled %} +# Disable cert-auth +client-cert-not-required +{% endif %} + +{% if openvpn_username_pam_auth %} +username-as-common-name +# PAM login +plugin /etc/openvpn/openvpn-plugin-auth-pam.so login +{% endif %} + +max-clients {{ openvpn_max_clients }} + +persist-tun +persist-key + +status status/openvpn-status.log + +{% if openvpn_run_unprivileged %} +user {{ openvpn_unprivileged_user }} +group {{ openvpn_unprivileged_group }} +{% endif %} + +verb {{ openvpn_verbosity_log }} +mute {{ openvpn_mute_after }}