From 01e4b0efa1b2789fc2d892612a01e9aadffaccf8 Mon Sep 17 00:00:00 2001 From: ludal Date: Mon, 17 Aug 2020 11:48:37 +0200 Subject: [PATCH] initial commit --- README.md | 94 +++++++++++++++++++++++++++++++++++++++- defaults/main.yml | 31 +++++++++++++ handlers/main.yml | 13 ++++++ tasks/asserts.yml | 48 ++++++++++++++++++++ tasks/client.yml | 56 ++++++++++++++++++++++++ tasks/install.yml | 39 +++++++++++++++++ tasks/main.yml | 19 ++++++++ tasks/server.yml | 69 +++++++++++++++++++++++++++++ templates/client.ovpn.j2 | 43 ++++++++++++++++++ templates/ipp.txt.j2 | 8 ++++ templates/server.conf.j2 | 54 +++++++++++++++++++++++ templates/vars.j2 | 15 +++++++ 12 files changed, 488 insertions(+), 1 deletion(-) create mode 100644 defaults/main.yml create mode 100644 handlers/main.yml create mode 100644 tasks/asserts.yml create mode 100644 tasks/client.yml create mode 100644 tasks/install.yml create mode 100644 tasks/main.yml create mode 100644 tasks/server.yml create mode 100644 templates/client.ovpn.j2 create mode 100644 templates/ipp.txt.j2 create mode 100644 templates/server.conf.j2 create mode 100644 templates/vars.j2 diff --git a/README.md b/README.md index f58e04b..7cb430a 100644 --- a/README.md +++ b/README.md @@ -1 +1,93 @@ -# ansible-role-openvpn \ No newline at end of file +openvpn +======== + +The present role : + - installs OpenVPN and dependancies + - configures it as a server + - creates client certificates and configuration file + +It has been tested on : + - Debian 9 + - Debian 10 + +Role variables +-------------- + +| Variable | Type | Choices | Default | Comment | +|------------------------------------|---------|--------------|-------------------------------------------------------------------------------|----------------------------------------------------------------------------| +| openvpn_user | string | | nobody | Set the OpenVPN service user. | +| openvpn_group | string | | nogroup | Set the OpenVPN service group. | +| openvpn_public_ip | string | | | Set the OpenVPN IP on which it will be reachable. | +| openvpn_port | int | | 1194 | Which TCP/UDP port should OpenVPN listen on ? | +| openvpn_proto | string | | tcp | TCP or UDP server? | +| openvpn_dev | string | | tun | Set the OpenVPN type off internal network device (tun or tap). | +| openvpn_ip_range | string | | 10.8.0.0 | Set the OpenVPN internal IP range. | +| openvpn_ip_netmask | string | | 255.255.255.0 | Set the OpenVPN internal netmask. | +| openvpn_compress | string | | lz4-v2 | Set the kind of compression type. | +| openvpn_maxclients | int | | 10 | Set number of maximum clients allowed. | +| openvpn_keepalive_ping | int | | 10 | Set "keepalive" ping interval in seconds. | +| openvpn_keepalive_timeout | int | | 120 | Set "keepalive" timeout in seconds. | +| openvpn_cipher | string | | AES-256-GCM | Set "cipher" option for server and client. | +| openvpn_auth | string | | SHA384 | Set "auth" option for authentication algoritm. | +| openvpn_tls_cipher | string | | TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384 | Set "tls-cipher' option. | +| openvpn_client_to_client | boolean | true , false | false | Allow clients to "see" eachother. By default client only see the server. | +| openvpn_push_route | list | | [] | List of route to push in order to allow client access. | +| openvpn_verbosity | int | | 3 | Set the log verbosity. | +| openvpn_mute    | int | | 20 | Set the number of consecutive messages in the same category in the log. | +| openvpn_tls_auth | boolean | true , false | false | Enable or disable tls authentication. | +| openvpn_log_status | string | | /var/log/openvpn-status.log | Path of the status log file. File is truncated and rewritten evert minute. | +| openvpn_log_append | string | | /var/log/openvpn.log | Path of log file. | +| openvpn_easyrsa_req_country | string | | | Easy-RSA variable "EASYRSA_REQ_COUNTRY" | +| openvpn_easyrsa_req_province | string | | | Esay-RSA variable "EASYRSA_REQ_PROVINCE" | +| openvpn_easyrsa_req_city | string | | | Esay-RSA variable "EASYRSA_REQ_CITY" | +| openvpn_easyrsa_req_org | string | | | Esay-RSA variable "EASYRSA_REQ_ORG" | +| openvpn_easyrsa_req_email | string | | | Esay-RSA variable "EASYRSA_REQ_EMAIL" | +| openvpn_easyrsa_req_ou | string | | | Esay-RSA variable "EASYRSA_REQ_OU" | + +Dependencies +------------ + +Does not depend on any other roles. + +Example Playbook +---------------- + + - hosts: vpn + ignore_errors: "{{ ansible_check_mode }}" # ignore errors only in check mode ! + + roles: + - { role: brainsys.openvpn, tags: ['openvpn'] } + +Example variables +----------------- + + openvpn_public_ip: a.b.c.d + + openvpn_client_to_client: false + openvpn_proto: 'tcp' + openvpn_ip_range: '10.8.0.0' + openvpn_push_route: + - ip: a.b.c.d + netmask: 255.255.255.0 + openvpn_client: + - name: client1 + ip: 10.8.0.11 + - name: client2 + ip: 10.8.0.12 + + +TODO +---- + + - sets up networking / firewall + - handle delegate_to instead of lookfile when creating client configuration + +License +------- + +GPLv3 + +Author Information +------------------ + +Written by Ludovic Cartier diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..490cead --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,31 @@ +--- +openvpn_asserts: True + +openvpn_user: 'nobody' +openvpn_group: 'nogroup' + +openvpn_ip_range: '10.8.0.0' +openvpn_ip_netmask: '255.255.255.0' + +openvpn_port: 1194 +openvpn_proto: 'tcp' +openvpn_dev: 'tun' + +openvpn_compress: 'lz4-v2' + +openvpn_client_to_client: False + +openvpn_maxclients: 10 + +openvpn_keepalive_ping: 10 +openvpn_keepalive_timeout: 120 + +openvpn_tls_auth: False +openvpn_cipher: 'AES-256-GCM' +openvpn_auth: 'SHA384' +openvpn_tls_cipher: 'TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384' + +openvpn_log_verbosity: 3 +openvpn_mute: 20 +openvpn_log_status: '/var/log/openvpn-status.log' +openvpn_log_append: '/var/log/openvpn.log' diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..d0ed599 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,13 @@ +--- +- name: 'openvpn | start service' + systemd: + name: openvpn@{{ ansible_hostname }}.service + state: started + enabled: yes + tags: ['openvpn'] + +- name: 'openvpn | restart service' + systemd: + name: openvpn@{{ ansible_hostname }}.service + enabled: yes + tags: ['openvpn'] diff --git a/tasks/asserts.yml b/tasks/asserts.yml new file mode 100644 index 0000000..25fbf79 --- /dev/null +++ b/tasks/asserts.yml @@ -0,0 +1,48 @@ +--- +- name: 'openvpn | validate | ensure Easy-RSA request Country is defined' + assert: + that: openvpn_easyrsa_req_country is defined + msg: ' is mandatory and must be defined.' + when: + - openvpn_server + - openvpn_client + +- name: 'openvpn | validate | ensure Easy-RSA request Province is defined' + assert: + that: openvpn_easyrsa_req_province is defined + msg: ' is mandatory and must be defined' + when: + - openvpn_server + - openvpn_client + +- name: 'openvpn | validate | ensure Easy-RSA request City is defined' + assert: + that: openvpn_easyrsa_req_city is defined + msg: ' is mandatory and must be defined' + when: + - openvpn_server + - openvpn_client + +- name: 'openvpn | validate | ensure Easy-RSA request Organization is defined' + assert: + that: openvpn_easyrsa_req_org is defined + msg: ' is mandatory and must be defined' + when: + - openvpn_server + - openvpn_client + +- name: 'openvpn | validate | ensure Easy-RSA request Email is defined' + assert: + that: openvpn_easyrsa_req_email is defined + msg: ' is mandatory and must be defined' + when: + - openvpn_server + - openvpn_client + +- name: 'openvpn | validate | ensure Easy-RSA request OU is defined' + assert: + that: openvpn_easyrsa_req_ou is defined + msg: ' is mandatory and must be defined' + when: + - openvpn_server + - openvpn_client diff --git a/tasks/client.yml b/tasks/client.yml new file mode 100644 index 0000000..993a5bb --- /dev/null +++ b/tasks/client.yml @@ -0,0 +1,56 @@ +--- +- name: 'openvpn | create client directory' + file: + path: /etc/openvpn/client/{{ item.name }}/ + state: directory + mode: '0755' + loop: "{{ openvpn_client }}" + tags: ['openvpn', 'openvpn_client'] + +- name: 'openvpn | create client request' + command: ./easyrsa --batch --req-cn={{ item.name }} gen-req {{ item.name }} nopass + args: + chdir: /etc/openvpn/{{ ansible_hostname }}/easy-rsa + environment: + EASYRSA_BATCH: 1 + loop: "{{ openvpn_client }}" + tags: ['openvpn', 'openvpn_client'] + +- name: 'openvpn | create client certificates' + command: ./easyrsa sign-req client {{ item.name }} + args: + chdir: /etc/openvpn/{{ ansible_hostname }}/easy-rsa + environment: + EASYRSA_BATCH: 1 + loop: "{{ openvpn_client }}" + tags: ['openvpn', 'openvpn_client'] + +- name: 'openvpn | copy client certificate' + copy: + src: "/etc/openvpn/{{ ansible_hostname }}/easy-rsa/pki/issued/{{ item.name }}.crt" + dest: "/etc/openvpn/client/{{ item.name }}/{{ item.name }}.crt" + remote_src: yes + loop: "{{ openvpn_client }}" + tags: ['openvpn', 'openvpn_client'] + +- name: 'openvpn | copy client private key' + copy: + src: "/etc/openvpn/{{ ansible_hostname }}/easy-rsa/pki/private/{{ item.name }}.key" + dest: "/etc/openvpn//client/{{ item.name }}/{{ item.name }}.key" + remote_src: yes + loop: "{{ openvpn_client }}" + tags: ['openvpn', 'openvpn_client'] + +- name: 'openvpn | create client configuration file' + template: + src: "../data/openvpn/client.ovpn.j2" + dest: "/etc/openvpn/client/{{ item.name }}/{{ item.name }}.ovpn" + when: openvpn_client is defined + loop: "{{ openvpn_client }}" + vars: + loop_cert: "{{ lookup('file', '/etc/openvpn/client/' + item.name + '/' + item.name + '.crt') }}" + loop_key : "{{ lookup('file', '/etc/openvpn/client/' + item.name + '/' + item.name + '.key') }}" + loop_ca : "{{ lookup('file', '/etc/openvpn/' + ansible_hostname + '/keys/ca.crt') }}" + loop_ta : "{{ lookup('file', '/etc/openvpn/' + ansible_hostname + '/keys/ta.key') }}" + tags: ['openvpn', 'openvpn_client'] + diff --git a/tasks/install.yml b/tasks/install.yml new file mode 100644 index 0000000..4e20ffd --- /dev/null +++ b/tasks/install.yml @@ -0,0 +1,39 @@ +--- +- name: 'openvpn | update APT Cache' + apt: + update_cache: yes + cache_valid_time: 3600 + tags: ['openvpn', 'openvpn_install'] + +- name: 'openvpn | install packages' + apt: + name: + - openvpn + - openssl + - easy-rsa + state: present + tags: ['openvpn', 'openvpn_install'] + +- name: 'openvpn | create directories' + file: + path: /etc/openvpn/{{ ansible_hostname }}/keys + state: directory + mode: '0755' + tags: ['openvpn', 'openvpn_install'] + +- name: 'openvpn | copy easy-rsa' + copy: + src: /usr/share/easy-rsa + dest: /etc/openvpn/{{ ansible_hostname }} + owner: root + group: root + tags: ['openvpn', 'openvpn_install'] + +- name: 'openvpn | chmod +x easyrsa' + file: + path: /etc/openvpn/{{ ansible_hostname }}/easy-rsa/easyrsa + owner: root + group: root + mode: 0755 + tags: ['openvpn', 'openvpn_install'] + diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..c14d8e3 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- name: 'Check if ansible version >= 2.7.' + assert: + that: "ansible_version.full is version_compare(2.7, '>=')" + msg: "Une version Ansible 2.7 ou supérieur est nécessaire pour utiliser cette version du rôle." + tags: always + +- include_tasks: asserts.yml + when: openvpn_asserts + tags: always + +- include_tasks: install.yml + tags: ['openvpn','openvpn_install'] + +- include_tasks: server.yml + tags: ['openvpn', 'openvpn_server'] + +- include_tasks: client.yml + tags: ['openvpn', 'openvpn_client'] diff --git a/tasks/server.yml b/tasks/server.yml new file mode 100644 index 0000000..37eda1c --- /dev/null +++ b/tasks/server.yml @@ -0,0 +1,69 @@ +--- +- name: 'openvpn | copy vars' + template: + src: "../data/openvpn/vars.j2" + dest: "/etc/openvpn/{{ ansible_hostname }}/easy-rsa/vars" + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | cleanup everything' + command: "./easyrsa init-pki" + args: + chdir: /etc/openvpn/{{ ansible_hostname }}/easy-rsa + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | create random file' + command: "dd if=/dev/urandom of=pki/.rnd bs=256 count=1" + args: + chdir: /etc/openvpn/{{ ansible_hostname }}/easy-rsa + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | generate certificates' + command: "{{ item }}" + args: + chdir: /etc/openvpn/{{ ansible_hostname }}/easy-rsa + environment: + EASYRSA_BATCH: 1 + with_items: + - ./easyrsa build-ca nopass + - ./easyrsa gen-dh + - ./easyrsa build-server-full {{ ansible_hostname }} nopass + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | copy certificates' + copy: + src: "{{ item }}" + dest: "/etc/openvpn/{{ ansible_hostname }}/keys" + remote_src: yes + with_items: + - /etc/openvpn/{{ ansible_hostname }}/easy-rsa/pki/dh.pem + - /etc/openvpn/{{ ansible_hostname }}/easy-rsa/pki/private/{{ ansible_hostname }}.key + - /etc/openvpn/{{ ansible_hostname }}/easy-rsa/pki/issued/{{ ansible_hostname }}.crt + - /etc/openvpn/{{ ansible_hostname }}/easy-rsa/pki/ca.crt + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | generate ta.key' + command: "openvpn --genkey --secret /etc/openvpn/{{ ansible_hostname }}/keys/ta.key" + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | chmod ta.key' + file: + path: "/etc/openvpn/{{ ansible_hostname }}/keys/ta.key" + owner: root + group: root + mode: 0644 + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | configure ifconfig-pool-persist' + template: + src: "../data/openvpn/ipp.txt.j2" + dest: "/etc/openvpn/{{ ansible_hostname }}/ipp.txt" + when: openvpn_client is defined + tags: ['openvpn', 'openvpn_server'] + +- name: 'openvpn | copy server configuration' + template: + src: "../data/openvpn/server.conf.j2" + dest: "/etc/openvpn/{{ ansible_hostname }}.conf" + tags: ['openvpn', 'openvpn_server'] + notify: openvpn-restart + diff --git a/templates/client.ovpn.j2 b/templates/client.ovpn.j2 new file mode 100644 index 0000000..1c146b6 --- /dev/null +++ b/templates/client.ovpn.j2 @@ -0,0 +1,43 @@ +# alterway - openvpn client configuration + +client +dev {{ openvpn_dev }} +proto {{ openvpn_proto) }} +remote {{ openvpn_public_ip }} {{ openvpn_port }} +resolv-retry infinite + +nobind + +persist-key +persist-tun + +ca [inline] +cert [inline] +key [inline] +{% if openvpn_tls_auth is defined and openvpn_tls_auth == "true" %} +tls-auth [inline] 1 +{% endif %} + +cipher {{ openvpn_cipher }} +auth {{ openvpn_auth }} +tls-cipher {{ openvpn_tls_cipher }} + +compress {{ openvpn_compress }} + + +{{ loop_ca }} + + + +{{ loop_cert }} + + + +{{ loop_key }} + + +{% if openvpn_tls_auth is defined and openvpn_tls_auth == "true" %} + +{{ loop_ta }} + +{% endif %} diff --git a/templates/ipp.txt.j2 b/templates/ipp.txt.j2 new file mode 100644 index 0000000..511e8dc --- /dev/null +++ b/templates/ipp.txt.j2 @@ -0,0 +1,8 @@ +# ansible managed - DO NOT EDIT MANUALLY !!! +# official documentation - https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/ +# +# , + +{% for user in openvpn_client %} +{{ user.name }},{{ user.ip }} +{% endfor %} diff --git a/templates/server.conf.j2 b/templates/server.conf.j2 new file mode 100644 index 0000000..e207dfa --- /dev/null +++ b/templates/server.conf.j2 @@ -0,0 +1,54 @@ +# ansible managed - DO NOT EDIT MANUALLY !!! +# official documentation - https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/ + +user {{ openvpn_user }} +group {{ openvpn_group }} + +server {{ openvpn_ip_range }} {{ openvpn_ip_netmask }} +port {{ openvpn_port }} +proto {{ openvpn_proto }} +dev {{ openvpn_dev }} + +keepalive {{ openvpn_keepalive_ping }} {{ openvpn_keepalive_timeout }} + +ca /etc/openvpn/{{ ansible_hostname }}/keys/ca.crt +cert /etc/openvpn/{{ ansible_hostname }}/keys/{{ ansible_hostname }}.crt +key /etc/openvpn/{{ ansible_hostname }}/keys/{{ ansible_hostname }}.key +dh /etc/openvpn/{{ ansible_hostname }}/keys/dh.pem +{% if openvpn_tls_auth is defined and openvpn_tls_auth == "true" %} +tls-auth /etc/openvpn/{{ ansible_hostname }}/keys/ta.key 0 +{% endif %} + +cipher {{ openvpn_cipher }} +auth {{ openvpn_auth }} +tls-cipher {{ openvpn_tls_cipher }} + +compress {{ openvpn_compress }} +push "compress {{ openvpn_compress }}" + +max-clients {{ openvpn_maxclients }} + +ifconfig-pool-persist /etc/openvpn/{{ ansible_hostname }}/ipp.txt + +{% if openvpn_push_route is defined %} +{% for route in openvpn_push_route %} +push "route {{ route.ip }} {{ route.netmask }}" +{% endfor %} +{% endif %} + +persist-key +persist-tun + +verb {{ openvpn_log_verbosity }} +status {{ openvpn_log_status }} +log-append {{ openvpn_log_append }} + +mute {{ openvpn_mute }} + +{% if openvpn_proto is defined and openvpn_proto == "udp" %} +explicit-exit-notify 5 +{% endif %} + +{% if openvpn_client_to_client is defined and openvpn_client_to_client is sameas true %} +client-to-client +{% endif %} diff --git a/templates/vars.j2 b/templates/vars.j2 new file mode 100644 index 0000000..9d906d5 --- /dev/null +++ b/templates/vars.j2 @@ -0,0 +1,15 @@ +# ansible managed - DO NOT EDIT MANUALLY !!! + +if [ -z "$EASYRSA_CALLER" ]; then + echo "You appear to be sourcing an Easy-RSA 'vars' file." >&2 + echo "This is no longer necessary and is disallowed. See the section called" >&2 + echo "'How to use this file' near the top comments for more details." >&2 + return 1 +fi + +set_var EASYRSA_REQ_COUNTRY "{{ openvpn_easyrsa_req_country }}" +set_var EASYRSA_REQ_PROVINCE "{{ openvpn_easyrsa_req_province }}" +set_var EASYRSA_REQ_CITY "{{ openvpn_easyrsa_req_city }}" +set_var EASYRSA_REQ_ORG "{{ openvpn_easyrsa_req_org }}" +set_var EASYRSA_REQ_EMAIL "{{ openvpn_easyrsa_req_email }}" +set_var EASYRSA_REQ_OU "{{ openvpn_easyrsa_req_ou }}"