From f24386d74c19503c089be019d2f2f0fa41e3dbfa Mon Sep 17 00:00:00 2001 From: Fulvio Galeazzi <fulvio.galeazzi@garr.it> Date: Wed, 25 Mar 2020 22:34:09 +0000 Subject: [PATCH] 2020-03-25: FG; Completed client VPN file generation. --- README.md | 11 +- group_vars/openvpn | 8 + inventory.yml.sample | 7 +- playbooks/clientsetup.yml | 9 ++ roles/clientvpn/README.md | 38 +++++ roles/clientvpn/defaults/main.yml | 2 + roles/clientvpn/handlers/main.yml | 2 + roles/clientvpn/meta/main.yml | 57 +++++++ roles/clientvpn/tasks/main.yml | 148 ++++++++++++++++++ .../templates/openvpn.client.conf.j2 | 26 +++ roles/clientvpn/tests/inventory | 2 + roles/clientvpn/tests/test.yml | 5 + roles/clientvpn/vars/main.yml | 5 + roles/setup/tasks/configOpenvpnService.yml | 46 +----- roles/setup/tasks/main.yml | 2 +- roles/setup/templates/openvpn.server.conf.j2 | 27 ++++ 16 files changed, 349 insertions(+), 46 deletions(-) create mode 100644 group_vars/openvpn create mode 100644 playbooks/clientsetup.yml create mode 100644 roles/clientvpn/README.md create mode 100644 roles/clientvpn/defaults/main.yml create mode 100644 roles/clientvpn/handlers/main.yml create mode 100644 roles/clientvpn/meta/main.yml create mode 100644 roles/clientvpn/tasks/main.yml create mode 100644 roles/clientvpn/templates/openvpn.client.conf.j2 create mode 100644 roles/clientvpn/tests/inventory create mode 100644 roles/clientvpn/tests/test.yml create mode 100644 roles/clientvpn/vars/main.yml create mode 100644 roles/setup/templates/openvpn.server.conf.j2 diff --git a/README.md b/README.md index e078c09..303b88c 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,10 @@ These instructions assume that `openvpn` and `authca` were both installed with n direct `root` access but rather with a generic user (`ubuntu`, in this tutorial) with `sudoer` capability: login to these servers is only allowed by using a SSH key (same one, on both servers). - -Note that `authca` should only be accessed from `openvpn`: obviously, it should not be -configured with a public IP address. +We also assume that: + * `openvpn` has a public IP address (`1.2.3.4`) as well as a private address + `10.9.8.8` + * `authca` has only a private address `10.9.8.7` To avoid storing the installation SSH key to `openvpn`, we will use `ssh-agent` to enable safer login to `authca`. @@ -101,5 +102,9 @@ Author Information Originator: Fulvio Galeazzi, Consortium GARR, CSD Department +Acknowledgements: + https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-18-04 + https://openvpn.net/community-resources/how-to/#creating-configuration-files-for-server-and-clients + Developers: `<add your name here>` diff --git a/group_vars/openvpn b/group_vars/openvpn new file mode 100644 index 0000000..668c18f --- /dev/null +++ b/group_vars/openvpn @@ -0,0 +1,8 @@ +clientconfig: + cfghome: /home/ubuntu/client-configs + cfghomeusr: ubuntu + cfghomegrp: ubuntu + clients: + - piopio: + nameshort: piopioSrv + CommonName: '"Server di Piopio"' \ No newline at end of file diff --git a/inventory.yml.sample b/inventory.yml.sample index 692ff1e..00cce97 100644 --- a/inventory.yml.sample +++ b/inventory.yml.sample @@ -6,9 +6,12 @@ ansible_user=ansible openvpnDNstring='"My org OpenVPN server"' vpnproto=tcp vpnport=443 -vpnnetwork=10.0.0.0/8 +lannetwork=10.111.0.0 +lannetmask=255.255.0.0 +vpnnetwork=10.10.10.0 +vpnnetmask=255.255.255.0 vpninterface=ens3 - +vpnpublicip=1.2.3.4 [authca] 5.6.7.8 diff --git a/playbooks/clientsetup.yml b/playbooks/clientsetup.yml new file mode 100644 index 0000000..66934b7 --- /dev/null +++ b/playbooks/clientsetup.yml @@ -0,0 +1,9 @@ +--- +# Install softwares, Very first operations to be possibly done right after installation + +- hosts: all + become: True + roles: + - clientvpn + + diff --git a/roles/clientvpn/README.md b/roles/clientvpn/README.md new file mode 100644 index 0000000..225dd44 --- /dev/null +++ b/roles/clientvpn/README.md @@ -0,0 +1,38 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: + + - hosts: servers + roles: + - { role: username.rolename, x: 42 } + +License +------- + +BSD + +Author Information +------------------ + +An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/roles/clientvpn/defaults/main.yml b/roles/clientvpn/defaults/main.yml new file mode 100644 index 0000000..57f2aa6 --- /dev/null +++ b/roles/clientvpn/defaults/main.yml @@ -0,0 +1,2 @@ +--- +# defaults file for clientvpn \ No newline at end of file diff --git a/roles/clientvpn/handlers/main.yml b/roles/clientvpn/handlers/main.yml new file mode 100644 index 0000000..ff33522 --- /dev/null +++ b/roles/clientvpn/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for clientvpn \ No newline at end of file diff --git a/roles/clientvpn/meta/main.yml b/roles/clientvpn/meta/main.yml new file mode 100644 index 0000000..7223799 --- /dev/null +++ b/roles/clientvpn/meta/main.yml @@ -0,0 +1,57 @@ +galaxy_info: + author: your name + description: your description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Some suggested licenses: + # - BSD (default) + # - MIT + # - GPLv2 + # - GPLv3 + # - Apache + # - CC-BY + license: license (GPLv2, CC-BY, etc) + + min_ansible_version: 1.2 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + # Optionally specify the branch Galaxy will use when accessing the GitHub + # repo for this role. During role install, if no tags are available, + # Galaxy will use this branch. During import Galaxy will access files on + # this branch. If Travis integration is configured, only notifications for this + # branch will be accepted. Otherwise, in all cases, the repo's default branch + # (usually master) will be used. + #github_branch: + + # + # platforms is a list of platforms, and each platform has a name and a list of versions. + # + # platforms: + # - name: Fedora + # versions: + # - all + # - 25 + # - name: SomePlatform + # versions: + # - all + # - 1.0 + # - 7 + # - 99.99 + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. \ No newline at end of file diff --git a/roles/clientvpn/tasks/main.yml b/roles/clientvpn/tasks/main.yml new file mode 100644 index 0000000..6fca5a1 --- /dev/null +++ b/roles/clientvpn/tasks/main.yml @@ -0,0 +1,148 @@ +--- +# tasks file for clientvpn + +# all variables are defined under group_vars and this role is meant to be executed on OpenVPN server only + +- name: prepare request + block: + + - name: create directory to store client certificates + file: + state: directory + path: "{{ item }}" + mode: 01700 + owner: "{{ clientconfig.cfghomeusr }}" + group: "{{ clientconfig.cfghomegrp }}" + with_items: + - "{{ clientconfig.cfghome }}" + - "{{ clientconfig.cfghome }}/reqs" + - "{{ clientconfig.cfghome }}/keys" + - "{{ clientconfig.cfghome }}/crts" + - "{{ clientconfig.cfghome }}/files" + delegate_to: localhost + + - name: copy ca.crt and ta.key to clientconfig + fetch: + src: /etc/openvpn/{{ item }} + dest: "{{ clientconfig.cfghome }}/" + fail_on_missing: yes + flat: yes + with_items: + - ca.crt + - ta.key + + - name: create CA client request + shell: ./easyrsa gen-req {{ item.nameshort }} nopass + args: + chdir: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}" + creates: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}/pki/reqs/{{ item.nameshort }}.req" + stdin: "{{ item.CommonName | default('Client Name') }}" + with_items: + - "{{ clientconfig.clients }}" + + - name: transfer reqs to localhost + fetch: + src: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}/pki/reqs/{{ item.nameshort }}.req" + dest: "{{ clientconfig.cfghome }}/reqs/" + flat: yes + fail_on_missing: yes + with_items: + - "{{ clientconfig.clients }}" + + - name: transfer keys to localhost + fetch: + src: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}/pki/private/{{ item.nameshort }}.key" + dest: "{{ clientconfig.cfghome }}/keys/" + flat: yes + fail_on_missing: yes + with_items: + - "{{ clientconfig.clients }}" + + when: + - inventory_hostname in groups['openvpn'] + +##### + +- name: create temporary directory, will need it later + tempfile: + state: directory + suffix: req + register: authcaTempdir + when: + - inventory_hostname in groups['authca'] + +- name: transfer reqs and keys and sign them, then copy to localhost + block: + + - name: transfer req files from localhost to authca + copy: + src: "{{ hostvars[groups['openvpn'][0]]['clientconfig']['cfghome'] }}/reqs/{{ item.nameshort }}.req" + dest: "{{ authcaTempdir.path }}/" + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + - name: transfer key files from localhost to authca + copy: + src: "{{ hostvars[groups['openvpn'][0]]['clientconfig']['cfghome'] }}/keys/{{ item.nameshort }}.key" + dest: "{{ authcaTempdir.path }}/" + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + + - name: execute import-req + shell: ./easyrsa import-req "{{ authcaTempdir.path }}/{{ item.nameshort }}.req" "{{ item.nameshort }}" + args: + chdir: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}" + creates: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}/pki/reqs/{{ item.nameshort }}.req" + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + + - name: execute sign-req + shell: ./easyrsa sign-req client {{ item.nameshort }} + args: + chdir: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}" + creates: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}/pki/issued/{{ item.nameshort }}.crt" + stdin: "yes" + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + + - name: transfer crt files to localhost + fetch: + src: "{{ easyrsaBasedir }}/{{ easyrsaSubdir }}/pki/issued/{{ item.nameshort }}.crt" + dest: "{{ hostvars[groups['openvpn'][0]]['clientconfig']['cfghome'] }}/crts/" + fail_on_missing: yes + flat: yes + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + + when: + - inventory_hostname in groups['authca'] + +- name: produce final configuration file + block: + + - name: configure server.conf file + template: + src: openvpn.client.conf.j2 + dest: "{{ hostvars[groups['openvpn'][0]]['clientconfig']['cfghome'] }}/files/{{ item.nameshort }}.ovpn.part" + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + - name: append other certificates to ovpn client config + shell: cat ./files/{{ item.nameshort }}.ovpn.part + <(echo -e '<ca>') + ./ca.crt + <(echo -e '</ca>\n<cert>') + ./crts/{{ item.nameshort }}.crt + <(echo -e '</cert>\n<key>') + ./keys/{{ item.nameshort }}.key + <(echo -e '</key>\n<tls-auth>') + ./ta.key + <(echo -e '</tls-auth>') + > ./files/{{ item.nameshort }}.ovpn + args: + chdir: "{{ hostvars[groups['openvpn'][0]]['clientconfig']['cfghome'] }}" + creates: "{{ hostvars[groups['openvpn'][0]]['clientconfig']['cfghome'] }}/files/{{ item.nameshort }}.ovpn" + executable: /bin/bash + with_items: + - "{{ hostvars[groups['openvpn'][0]]['clientconfig']['clients'] }}" + + when: + - inventory_hostname in groups['openvpn'] diff --git a/roles/clientvpn/templates/openvpn.client.conf.j2 b/roles/clientvpn/templates/openvpn.client.conf.j2 new file mode 100644 index 0000000..d73fc93 --- /dev/null +++ b/roles/clientvpn/templates/openvpn.client.conf.j2 @@ -0,0 +1,26 @@ +client +dev tun +proto {{ vpnproto }} +remote {{ vpnpublicip }} {{ vpnport }} + +cipher AES-256-CBC +auth SHA256 +key-direction 1 + +resolv-retry infinite +nobind +user nobody +group nogroup +persist-key +persist-tun +remote-cert-tls server + +#comp-lzo + +# Set log file verbosity. +verb 3 + +### for Linux only, if using update-resolv-conf +# script-security 2 +# up /etc/openvpn/update-resolv-conf +# down /etc/openvpn/update-resolv-conf diff --git a/roles/clientvpn/tests/inventory b/roles/clientvpn/tests/inventory new file mode 100644 index 0000000..878877b --- /dev/null +++ b/roles/clientvpn/tests/inventory @@ -0,0 +1,2 @@ +localhost + diff --git a/roles/clientvpn/tests/test.yml b/roles/clientvpn/tests/test.yml new file mode 100644 index 0000000..0be5ede --- /dev/null +++ b/roles/clientvpn/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - clientvpn \ No newline at end of file diff --git a/roles/clientvpn/vars/main.yml b/roles/clientvpn/vars/main.yml new file mode 100644 index 0000000..62e1efd --- /dev/null +++ b/roles/clientvpn/vars/main.yml @@ -0,0 +1,5 @@ +--- +# vars file for clientvpn + +easyrsaBasedir: /home/easyrsa +easyrsaSubdir: easyrsa3 diff --git a/roles/setup/tasks/configOpenvpnService.yml b/roles/setup/tasks/configOpenvpnService.yml index a5717a0..463ce67 100644 --- a/roles/setup/tasks/configOpenvpnService.yml +++ b/roles/setup/tasks/configOpenvpnService.yml @@ -1,42 +1,8 @@ --- -# config OpenVpn service, enable and start it +# config OpenVpn service -- name: copy example config file - shell: cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/ - args: - creates: /etc/openvpn/server.conf.gz -- name: gunzip file - shell: gzip -d -c /etc/openvpn/server.conf.gz > /etc/openvpn/server.conf - args: - creates: /etc/openvpn/server.conf - -- name: update configuration file, replace existing lines - lineinfile: - path: /etc/openvpn/server.conf - regexp: "{{ item.regex }}" - state: "{{ item.state }}" - line: "{{ item.line }}" - backrefs: yes - with_items: - - { state: "present", line: 'tls-auth ta.key 0 # This file is secret', regex: ';?tls-auth'} - - { state: "present", line: 'cipher AES-256-CBC', regex: ';?cipher '} - - { state: "present", line: 'ca ca.crt', regex: '^;?ca '} - - { state: "present", line: 'cert server.crt', regex: '^;?cert '} - - { state: "present", line: 'key server.key', regex: '^;?key '} - - { state: "present", line: 'dh dh.pem', regex: '^;?dh '} - - { state: "present", line: 'user nobody', regex: '^;?\s*user '} - - { state: "present", line: 'group nogroup', regex: '^;?\s*group '} - - { state: "present", line: '\1', regex: ';?(.*redirect-gateway.*$)'} - - { state: "present", line: '\1', regex: ';?(.*dhcp-option.*$)'} - - { state: "present", line: 'port {{ vpnport }}', regex: '^;?\s*port '} - - { state: "present", line: 'proto {{ vpnproto }}', regex: '^;?\s*proto '} - - { state: "present", line: 'explicit-exit-notify {{ "0" if vpnproto == "tcp" else "1" }}', regex: '^;?\s*explicit-exit-notify '} - -- name: update configuration file, add new lines - lineinfile: - path: /etc/openvpn/server.conf - state: "{{ item.state }}" - insertafter: "{{ item.after }}" - line: "{{ item.line }}" - with_items: - - { state: "present", line: 'auth SHA256', after: 'cipher ' } +- name: configure server.conf file + template: + src: openvpn.server.conf.j2 + dest: /etc/openvpn/server.conf + backup: yes diff --git a/roles/setup/tasks/main.yml b/roles/setup/tasks/main.yml index 0418f42..79b636f 100644 --- a/roles/setup/tasks/main.yml +++ b/roles/setup/tasks/main.yml @@ -245,7 +245,7 @@ - name: start openvpn service service: name: openvpn@server - state: started + state: restarted enabled: yes when: diff --git a/roles/setup/templates/openvpn.server.conf.j2 b/roles/setup/templates/openvpn.server.conf.j2 new file mode 100644 index 0000000..a84d05e --- /dev/null +++ b/roles/setup/templates/openvpn.server.conf.j2 @@ -0,0 +1,27 @@ +port {{ vpnport }} +proto {{ vpnproto }} +server {{ vpnnetwork }} {{ vpnnetmask }} +push "route {{ lannetwork }} {{ lannetmask }}" +# ifconfig +explicit-exit-notify {{ "0" if vpnproto == "tcp" else "1" }} + +dev tun +ca ca.crt +cert server.crt +key server.key +dh dh.pem +keepalive 10 120 +tls-auth ta.key 0 +cipher AES-256-CBC +auth SHA256 +user nobody +group nogroup +persist-key +persist-tun +status /var/log/openvpn/openvpn-status.log +verb 3 +ifconfig-pool-persist /var/log/openvpn/ipp.txt +push "redirect-gateway def1 bypass-dhcp" +push "dhcp-option DNS 208.67.222.222" +push "dhcp-option DNS 208.67.220.220" + -- GitLab