diff --git a/base/pretask_debian.yml b/base/pretask_debian.yml index 56900fa..031108b 100644 --- a/base/pretask_debian.yml +++ b/base/pretask_debian.yml @@ -3,6 +3,8 @@ name: - sendmail - openssh-server + - git + - vim state: present update_cache: yes autoremove: yes diff --git a/roles/mderasse.firewall/tasks/main.yml b/roles/mderasse.firewall/tasks/main.yml index 1e39528..a5a149c 100644 --- a/roles/mderasse.firewall/tasks/main.yml +++ b/roles/mderasse.firewall/tasks/main.yml @@ -41,6 +41,10 @@ mode: 0644 when: "ansible_service_mgr == 'systemd'" +- name: Firewall - Launch iptables manually first + shell: /etc/firewall/firewall.sh & + run_once: yes + - name: Firewall - Manage firewall service service: name: firewall diff --git a/roles/mderasse.firewall/templates/firewall.sh.j2 b/roles/mderasse.firewall/templates/firewall.sh.j2 index 91de81a..f7b6caf 100755 --- a/roles/mderasse.firewall/templates/firewall.sh.j2 +++ b/roles/mderasse.firewall/templates/firewall.sh.j2 @@ -23,15 +23,16 @@ iptables -t mangle -F iptables -t mangle -X # ---------- Basic Policies ---------- -# Apply default policies -iptables -P INPUT {{ firewall_default_input }} -iptables -P FORWARD {{ firewall_default_forward }} -iptables -P OUTPUT {{ firewall_default_output }} # Let's keep current connection iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +# Apply default policies +iptables -P INPUT {{ firewall_default_input }} +iptables -P FORWARD {{ firewall_default_forward }} +iptables -P OUTPUT {{ firewall_default_output }} + # Accept traffic from loopback interface (localhost). iptables -t filter -A INPUT -i lo -j ACCEPT iptables -t filter -A OUTPUT -o lo -j ACCEPT diff --git a/roles/stouts.openvpn/.bumpversion.cfg b/roles/stouts.openvpn/.bumpversion.cfg new file mode 100644 index 0000000..294bb51 --- /dev/null +++ b/roles/stouts.openvpn/.bumpversion.cfg @@ -0,0 +1,6 @@ +[bumpversion] +commit = True +current_version = 2.4.1 +tag = True +tag_name = {new_version} + diff --git a/roles/stouts.openvpn/.travis.yml b/roles/stouts.openvpn/.travis.yml new file mode 100644 index 0000000..70a2ed8 --- /dev/null +++ b/roles/stouts.openvpn/.travis.yml @@ -0,0 +1,18 @@ +--- + +language: python +python: "2.7" + +env: + - MOLECULE_DISTRO=ubuntu1404 + - MOLECULE_DISTRO=ubuntu1604 + # - MOLECULE_DISTRO=ubuntu1804 + - MOLECULE_DISTRO=debian8 MOLECULE_INIT_CMD=/lib/systemd/systemd + - MOLECULE_DISTRO=debian9 MOLECULE_INIT_CMD=/lib/systemd/systemd + # - MOLECULE_DISTRO=centos6 MOLECULE_INIT_CMD=/lib/systemd/systemd + - MOLECULE_DISTRO=centos7 MOLECULE_INIT_CMD=/lib/systemd/systemd +install: + - pip install docker molecule + +script: + - molecule test diff --git a/roles/stouts.openvpn/CONTRIBUTORS b/roles/stouts.openvpn/CONTRIBUTORS new file mode 100644 index 0000000..5cfd33b --- /dev/null +++ b/roles/stouts.openvpn/CONTRIBUTORS @@ -0,0 +1,20 @@ +Contributors: + +* Adam Kunicki (https://github.com/kunickiaj) +* Adham Helal (https://github.com/ahelal) +* Daniel Miranda (https://github.com/danielkza) +* Elias (https://github.com/viderizer) +* Federico Capoano (https://github.com/nemesisdesign) +* JP Viljoen (https://github.com/froztbyte) +* Jason Bond Pratt (https://github.com/juniorplenty) +* Kirill Klenov (http://klen.github.io/) +* Kurt Sussman (https://github.com/neophiliac) +* Mahmoud Abdelkader (https://github.com/mahmoudimus) +* Marcelo Anton (https://github.com/mbanton) +* Maxim Kravets (https://github.com/zeelax) +* Michael Frister (https://github.com/meeee) +* Mohammed Naser (https://github.com/mnaser) +* Roland von Ohlen (https://github.com/RockingRolli) +* Sebastien Badia (https://github.com/sbadia) +* Thomas Krille (https://github.com/tkrille) +* https://github.com/teadur diff --git a/roles/stouts.openvpn/LICENSE b/roles/stouts.openvpn/LICENSE new file mode 100644 index 0000000..a89b814 --- /dev/null +++ b/roles/stouts.openvpn/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Stouts + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/roles/stouts.openvpn/Makefile b/roles/stouts.openvpn/Makefile new file mode 100644 index 0000000..658035a --- /dev/null +++ b/roles/stouts.openvpn/Makefile @@ -0,0 +1,20 @@ +.PHONY: release major minor patch + +VERSION?=minor +release: + @bumpversion $(VERSION) + @git checkout master + @git merge develop + @git checkout develop + @git push --all + @git push --tags + @git checkout develop + +major: + make release VERSION=major + +minor: + make release VERSION=minor + +patch: + make release VERSION=patch diff --git a/roles/stouts.openvpn/README.md b/roles/stouts.openvpn/README.md new file mode 100644 index 0000000..3927aed --- /dev/null +++ b/roles/stouts.openvpn/README.md @@ -0,0 +1,55 @@ +# Stouts.openvpn + +[![Build Status](http://img.shields.io/travis/Stouts/Stouts.openvpn.svg?style=flat-square)](https://travis-ci.org/Stouts/Stouts.openvpn) +[![Galaxy](http://img.shields.io/badge/galaxy-Stouts.openvpn-blue.svg?style=flat-square)](https://galaxy.ansible.com/Stouts/openvpn/) + +Ansible role that installs an openvpn server + +* Install and setup OpenVPN server +* Create/revoke client's configurations and certificates +* Setup authentication with PAM (System, passwd files) + +## Requirements + +None. + +## Supported platforms + +- Ubuntu 14.04 +- Ubuntu 16.04 +- Debian 8 +- Debian 9 +- Centos 7 + +## Variables + +For a complete variable reference, see the [`defaults/main.yml`](defaults/main.yml) file. + +## Example playbook + +```yaml + +- hosts: all + vars: + openvpn_use_pam: true + openvpn_download_clients: true + openvpn_download_dir: /home/me/Projects + openvpn_clients: + - client1 + - client2 + openvpn_use_pam_users: + - name: user1 + password: password1 + - name: user2 + password: password2 + roles: + - Stouts.openvpn +``` + +## License + +Licensed under the MIT License. See the LICENSE file for details. + +## Feedback, bug-reports, requests, ... + +...are [welcome](https://github.com/Stouts/Stouts.openvpn/issues)! diff --git a/roles/stouts.openvpn/defaults/main.yml b/roles/stouts.openvpn/defaults/main.yml new file mode 100644 index 0000000..0fb7f21 --- /dev/null +++ b/roles/stouts.openvpn/defaults/main.yml @@ -0,0 +1,104 @@ +--- + +# Set to 'false' to completely disable the role +openvpn_enabled: true + +openvpn_etcdir: /etc/openvpn +openvpn_keydir: "{{ openvpn_etcdir }}/keys" + +# Set to 'true' to install openvpn from the upstream repo +openvpn_use_external_repo: false + +# Set to 'true' to install EasyRSA from system packages +openvpn_use_system_easyrsa: false + +# Set to true to make the vpn server NAT incoming traffic +openvpn_route_traffic: false + +# The interface that traffic will come in from (WARNING default: any) +openvpn_interface: '' + +# Default settings (See OpenVPN documentation) +openvpn_host: "{{ inventory_hostname }}" + +openvpn_port: 1194 +openvpn_proto: udp +openvpn_dev: tun +openvpn_server: 10.8.0.0 255.255.255.0 +openvpn_bridge: {} +openvpn_max_clients: 100 +openvpn_log: /var/log/openvpn.log +openvpn_keepalive: "10 120" +openvpn_ifconfig_pool_persist: ipp.txt +openvpn_comp_lzo: true +openvpn_cipher: BF-CBC +openvpn_status: openvpn-status.log +openvpn_verb: 3 +openvpn_tls_auth: false +openvpn_tls_key: "ta.key" +openvpn_user: nobody +openvpn_group: nogroup +openvpn_resolv_retry: infinite +openvpn_client_to_client: true + + +# Network ranges that the connecting clients should try to reach using the VPN +# connection. Example: +# openvpn_route_ranges: +# - 192.168.0.0 255.255.255.0 +# - 192.168.10.0 255.255.255.0 +openvpn_route_ranges: [] + +# DNS servers to push to the connecting client to avoid leaks via DNS queries +openvpn_dns_servers: [] + +openvpn_server_options: [] +# Additional server options +# openvpn_server_options: +# - dev-node MyTap +# - client-to-client + +openvpn_client_options: [] +# Additional client options +# openvpn_client_options: +# - dev-node MyTap +# - client-to-client + +openvpn_key_country: US +openvpn_key_province: CA +openvpn_key_city: SanFrancisco +openvpn_key_org: Fort-Funston +openvpn_key_email: me@myhost.mydomain +openvpn_key_size: 1024 + +# Make clients certificate +openvpn_clients: + - client + +# Revoke clients certificates +openvpn_clients_revoke: [] + +# Use PAM authentication +openvpn_use_pam: true +openvpn_use_pam_users: [] +# Only for Debian-based, CentOS requires 'pam_pwdfile' from third party repos. +# If empty use system users +# otherwise use users from the option +# openvpn_use_pam_users: +# - { name: user, password: password } + +# LDAP authentication and configuration (optional) +openvpn_use_ldap: false +openvpn_ldap_tlsenable: 'false' +openvpn_ldap_follow_referrals: 'false' + +# Use simple authentication (default is disabled) +openvpn_simple_auth: false +openvpn_simple_auth_password: "" + +# Whether to embed CA, cert, and key info inside client OVPN config file. +openvpn_unified_client_profiles: false + +# Download the created client credentials to the specified directory +openvpn_download_clients: false +openvpn_download_dir: "client_credentials/" diff --git a/roles/stouts.openvpn/files/easy-rsa.tar.gz b/roles/stouts.openvpn/files/easy-rsa.tar.gz new file mode 100644 index 0000000..38cefb7 Binary files /dev/null and b/roles/stouts.openvpn/files/easy-rsa.tar.gz differ diff --git a/roles/stouts.openvpn/handlers/main.yml b/roles/stouts.openvpn/handlers/main.yml new file mode 100644 index 0000000..7eec5fe --- /dev/null +++ b/roles/stouts.openvpn/handlers/main.yml @@ -0,0 +1,40 @@ +--- + +- name: Restart networking (Debian) + service: + name: networking + state: restarted + listen: restart networking debian + +- name: Restart networking (RedHat) + service: + name: network + state: restarted + listen: restart networking redhat + +- name: Save iptables rules + block: + - name: Save iptable rules (RedHat) + command: iptables-save + when: ansible_os_family == "RedHat" + listen: openvpn save iptables + + - name: Save iptable rules (Ubuntu Trusty) + command: /etc/init.d/iptables-persistent save + when: + - ansible_os_family == "Debian" + - ansible_lsb.codename == "trusty" + listen: openvpn save iptables + + - name: Save the rules (Ubuntu) + command: netfilter-persistent save + when: + - ansible_os_family == "Debian" + - ansible_lsb.codename != "trusty" + listen: openvpn save iptables + +- name: Restart OpenVPN service + service: + name: "{{ openvpn_service }}" + state: restarted + listen: openvpn restart diff --git a/roles/stouts.openvpn/meta/main.yml b/roles/stouts.openvpn/meta/main.yml new file mode 100644 index 0000000..30ac0b0 --- /dev/null +++ b/roles/stouts.openvpn/meta/main.yml @@ -0,0 +1,8 @@ +--- +galaxy_info: + author: klen + company: stouts + description: Manage OpenVPN server + min_ansible_version: 2.4 + license: MIT +dependencies: [] diff --git a/roles/stouts.openvpn/molecule/default/Dockerfile.j2 b/roles/stouts.openvpn/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..0a60553 --- /dev/null +++ b/roles/stouts.openvpn/molecule/default/Dockerfile.j2 @@ -0,0 +1,14 @@ +# Molecule managed + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python2-dnf bash && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi diff --git a/roles/stouts.openvpn/molecule/default/INSTALL.rst b/roles/stouts.openvpn/molecule/default/INSTALL.rst new file mode 100644 index 0000000..b42edf5 --- /dev/null +++ b/roles/stouts.openvpn/molecule/default/INSTALL.rst @@ -0,0 +1,16 @@ +******* +Docker driver installation guide +******* + +Requirements +============ + +* General molecule dependencies (see https://molecule.readthedocs.io/en/latest/installation.html) +* Docker Engine +* docker-py +* docker + +Install +======= + + $ sudo pip install docker-py diff --git a/roles/stouts.openvpn/molecule/default/molecule.yml b/roles/stouts.openvpn/molecule/default/molecule.yml new file mode 100644 index 0000000..7711c19 --- /dev/null +++ b/roles/stouts.openvpn/molecule/default/molecule.yml @@ -0,0 +1,30 @@ +--- + +dependency: + name: galaxy +driver: + name: docker +lint: + name: yamllint + options: + config-file: molecule/default/yamllint.yml +platforms: + - name: "instance-${MOLECULE_DISTRO:-ubuntu1604}" + image: "geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu1604}-ansible:latest" + command: ${MOLECULE_INIT_CMD-""} + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + privileged: true + pre_build_image: true +provisioner: + name: ansible + lint: + name: ansible-lint +scenario: + name: default +verifier: + name: testinfra + enabled: false + lint: + name: flake8 + diff --git a/roles/stouts.openvpn/molecule/default/playbook.yml b/roles/stouts.openvpn/molecule/default/playbook.yml new file mode 100644 index 0000000..dff6cc8 --- /dev/null +++ b/roles/stouts.openvpn/molecule/default/playbook.yml @@ -0,0 +1,8 @@ +--- +- name: Converge + hosts: all + vars: + openvpn_download_clients: true + openvpn_download_dir: /tmp/ + roles: + - role: Stouts.openvpn diff --git a/roles/stouts.openvpn/molecule/default/tests/__pycache__/test_default.cpython-37-PYTEST.pyc b/roles/stouts.openvpn/molecule/default/tests/__pycache__/test_default.cpython-37-PYTEST.pyc new file mode 100644 index 0000000..e82135d Binary files /dev/null and b/roles/stouts.openvpn/molecule/default/tests/__pycache__/test_default.cpython-37-PYTEST.pyc differ diff --git a/roles/stouts.openvpn/molecule/default/tests/__pycache__/test_default.cpython-37.pyc b/roles/stouts.openvpn/molecule/default/tests/__pycache__/test_default.cpython-37.pyc new file mode 100644 index 0000000..feef873 Binary files /dev/null and b/roles/stouts.openvpn/molecule/default/tests/__pycache__/test_default.cpython-37.pyc differ diff --git a/roles/stouts.openvpn/molecule/default/tests/test_default.py b/roles/stouts.openvpn/molecule/default/tests/test_default.py new file mode 100644 index 0000000..eedd64a --- /dev/null +++ b/roles/stouts.openvpn/molecule/default/tests/test_default.py @@ -0,0 +1,14 @@ +import os + +import testinfra.utils.ansible_runner + +testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') + + +def test_hosts_file(host): + f = host.file('/etc/hosts') + + assert f.exists + assert f.user == 'root' + assert f.group == 'root' diff --git a/roles/stouts.openvpn/molecule/default/yamllint.yml b/roles/stouts.openvpn/molecule/default/yamllint.yml new file mode 100644 index 0000000..4cf2673 --- /dev/null +++ b/roles/stouts.openvpn/molecule/default/yamllint.yml @@ -0,0 +1,56 @@ +--- + +rules: + braces: + min-spaces-inside: 1 + max-spaces-inside: 1 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + brackets: + min-spaces-inside: 0 + max-spaces-inside: 0 + min-spaces-inside-empty: 0 + max-spaces-inside-empty: 0 + colons: + max-spaces-before: 0 + max-spaces-after: 1 + commas: + max-spaces-before: 0 + min-spaces-after: 1 + max-spaces-after: 1 + comments: + require-starting-space: true + min-spaces-from-content: 2 + comments-indentation: enable + document-end: disable + document-start: + present: true + empty-lines: + max: 2 + max-start: 0 + max-end: 1 + empty-values: + forbid-in-block-mappings: true + forbid-in-flow-mappings: true + hyphens: + max-spaces-after: 1 + indentation: + spaces: 2 + indent-sequences: true + check-multi-line-strings: false + key-duplicates: enable + key-ordering: disable + line-length: + max: 80 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: false + new-line-at-end-of-file: enable + new-lines: + type: unix + octal-values: + forbid-implicit-octal: true + forbid-explicit-octal: false + trailing-spaces: enable + truthy: + level: warning + diff --git a/roles/stouts.openvpn/tasks/Debian/install.yml b/roles/stouts.openvpn/tasks/Debian/install.yml new file mode 100644 index 0000000..0855387 --- /dev/null +++ b/roles/stouts.openvpn/tasks/Debian/install.yml @@ -0,0 +1,51 @@ +--- + +- name: Add OpenVPN repo GPG key + apt_key: + id: E158C569 + url: https://swupdate.openvpn.net/repos/repo-public.gpg + when: openvpn_use_external_repo | bool + +- name: Add OpenVPN repo sources + apt_repository: + filename: openvpn + repo: deb http://swupdate.openvpn.net/apt "{{ ansible_lsb.codename }}" main + when: openvpn_use_external_repo | bool + +- name: Install requirements + apt: + name: "{{ requirements }}" + update_cache: true + force: true + vars: + requirements: + - openvpn + - udev + - openssl + - zip + +- name: Install PAM dependencies + apt: + name: "{{ dependencies }}" + force: true + when: openvpn_use_pam_users | length > 0 + vars: + dependencies: + - libpam-pwdfile + - python-passlib + +- name: Install easy-rsa package + apt: + name: easy-rsa + when: openvpn_use_system_easyrsa | bool + +- name: Install LDAP dependencies + apt: + name: openvpn-auth-ldap + force: true + when: openvpn_use_ldap | bool + +- name: Install bridge dependencies + apt: + name: bridge-utils + when: openvpn_bridge | bool diff --git a/roles/stouts.openvpn/tasks/Debian/setup-bridge.yml b/roles/stouts.openvpn/tasks/Debian/setup-bridge.yml new file mode 100644 index 0000000..6de6541 --- /dev/null +++ b/roles/stouts.openvpn/tasks/Debian/setup-bridge.yml @@ -0,0 +1,14 @@ +--- + +- name: Setup bridge + template: + src: bridge-interface.deb.j2 + dest: "/etc/network/interfaces.d/{{ openvpn_dev }}" + when: openvpn_bridge | bool + notify: restart networking debian + +- name: Remove interface configuration for "{{ openvpn_dev }}" + file: + path: "/etc/network/interfaces.d/{{ openvpn_dev }}" + state: absent + when: not openvpn_bridge | bool diff --git a/roles/stouts.openvpn/tasks/RedHat/install.yml b/roles/stouts.openvpn/tasks/RedHat/install.yml new file mode 100644 index 0000000..fddcfd5 --- /dev/null +++ b/roles/stouts.openvpn/tasks/RedHat/install.yml @@ -0,0 +1,31 @@ +--- + +- name: Install requirements + yum: + name: "{{ requirements }}" + update_cache: true + vars: + requirements: + - openvpn + - openssl + - zip + +- name: Install PAM dependencies + yum: + name: python-passlib + when: openvpn_use_pam_users | length > 0 + +- name: Install easy-rsa package + yum: + name: easy-rsa + when: openvpn_use_system_easyrsa | bool + +- name: Install LDAP dependencies + yum: + name: openvpn-auth-ldap + when: openvpn_use_ldap | bool + +- name: Install bridge dependencies + yum: + name: bridge-utils + when: openvpn_bridge| bool diff --git a/roles/stouts.openvpn/tasks/RedHat/setup-bridge.yml b/roles/stouts.openvpn/tasks/RedHat/setup-bridge.yml new file mode 100644 index 0000000..824caee --- /dev/null +++ b/roles/stouts.openvpn/tasks/RedHat/setup-bridge.yml @@ -0,0 +1,28 @@ +--- + +- name: Setup up script + when: openvpn_bridge | bool + template: + src: up.sh.j2 + dest: /etc/openvpn/up.sh + mode: o+x + +- name: Setup down script + template: + src: down.sh.j2 + dest: /etc/openvpn/down.sh + mode: o+x + when: openvpn_bridge | bool + +- name: Setup bridge + template: + src: bridge-interface.rh.j2 + dest: "/etc/sysconfig/network-scripts/ifcfg-br-{{ openvpn_dev }}" + when: openvpn_bridge | bool + notify: restart networking redhat + +- name: Remove interface configuration for "{{ openvpn_dev }}" + file: + path: "/etc/sysconfig/network-scripts/ifcfg-br-{{ openvpn_dev }}" + state: absent + when: not openvpn_bridge | bool diff --git a/roles/stouts.openvpn/tasks/assertions.yml b/roles/stouts.openvpn/tasks/assertions.yml new file mode 100644 index 0000000..08d431a --- /dev/null +++ b/roles/stouts.openvpn/tasks/assertions.yml @@ -0,0 +1,7 @@ +--- + +- name: Check OS is supported + assert: + that: + - "ansible_os_family in ['Debian', 'RedHat']" + msg: "{{ ansible_os_family }} is not supported" diff --git a/roles/stouts.openvpn/tasks/clients.yml b/roles/stouts.openvpn/tasks/clients.yml new file mode 100644 index 0000000..fac8ef2 --- /dev/null +++ b/roles/stouts.openvpn/tasks/clients.yml @@ -0,0 +1,94 @@ +--- + +- name: Generate scripts + template: + src: "{{ item }}.j2" + dest: "{{ openvpn_etcdir }}/{{ item }}" + mode: 0o700 + loop: + - vars + - build-server.sh + - build-client.sh + - revoke-client.sh + +- name: Generate Server keys + command: "{{ openvpn_etcdir }}/build-server.sh" + args: + creates: "{{ openvpn_keydir }}" + +- name: Generate tls-auth key + command: + openvpn --genkey --secret "{{ openvpn_keydir }}/{{ openvpn_tls_key }}" + args: + creates: "{{ openvpn_keydir }}/{{ openvpn_tls_key }}" + when: openvpn_tls_auth + +- name: Generate Clients keys + command: "{{ openvpn_etcdir }}/build-client.sh {{ item }}" + args: + creates: "{{ openvpn_keydir }}/{{ item }}.crt" + loop: "{{ openvpn_clients }}" + +- name: Revoke Clients keys + command: "{{ openvpn_etcdir }}/revoke-client.sh {{ item }}" + args: + removes: "{{ openvpn_keydir }}/{{ item }}.crt" + loop: "{{ openvpn_clients_revoke }}" + +- include: read-client-files.yml + when: openvpn_unified_client_profiles + +- name: Create client configuration directory if requested + file: + path: "{{ openvpn_client_config_dir }}" + mode: 0o755 + state: directory + when: openvpn_client_config_dir is defined + +- name: Check if crl.pem exists + stat: + path: "{{ openvpn_keydir }}/crl.pem" + register: crl_pem_file + +- name: Generate Clients configurations + template: + src: client.conf.j2 + dest: "{{ openvpn_keydir }}/{{ item }}.ovpn" + loop: "{{ openvpn_clients }}" + register: openvpn_clients_changed + +- name: Pack clients + command: >- + zip -j + {{ item.item }}.zip + {{ item.item }}.crt + {{ item.item }}.key + {{ item.item }}.ovpn + ca.crt + {{ openvpn_tls_key if openvpn_tls_auth else '' }} + loop: "{{ openvpn_clients_changed.results }}" + args: + chdir: "{{ openvpn_keydir }}" + creates: "{{ item.item }}.zip" + +- name: Download client credentials (zip) + fetch: + src: "{{ openvpn_keydir }}/{{ item.item }}.zip" + dest: "{{ openvpn_download_dir }}" + flat: true + validate_checksum: true + when: + - openvpn_download_clients | bool + - not openvpn_unified_client_profiles | bool + loop: "{{ openvpn_clients_changed.results }}" + +- name: Download client credentials (ovpn) + fetch: + src: "{{ openvpn_keydir }}/{{ item.item }}.ovpn" + dest: "{{ openvpn_download_dir }}" + flat: true + validate_checksum: true + when: + - openvpn_download_clients | bool + - openvpn_unified_client_profiles | bool + loop: "{{ openvpn_clients_changed.results }}" diff --git a/roles/stouts.openvpn/tasks/configure.yml b/roles/stouts.openvpn/tasks/configure.yml new file mode 100644 index 0000000..a6f1a84 --- /dev/null +++ b/roles/stouts.openvpn/tasks/configure.yml @@ -0,0 +1,52 @@ +--- + +- name: Setup PAM + template: + src: openvpn.pam.j2 + dest: /etc/pam.d/openvpn + when: openvpn_use_pam | bool + +- name: Configure users + htpasswd: + path: "{{ openvpn_etcdir }}/users" + name: "{{ item.name }}" + password: "{{ item.password }}" + crypt_scheme: des_crypt + loop: "{{ openvpn_use_pam_users }}" + +- name: Setup LDAP + template: + src: auth-ldap.conf.j2 + dest: /etc/openvpn/auth-ldap.conf + when: openvpn_use_ldap | bool + +- name: Setup simple authentication + template: + src: auth-client.sh.j2 + dest: "{{ openvpn_etcdir }}/auth-client.sh" + mode: 0o755 + when: + - openvpn_simple_auth | bool + - openvpn_simple_auth_password | bool + notify: openvpn restart + +- name: Configure server + template: + src: server.conf.j2 + dest: "{{ openvpn_etcdir }}/server.conf" + notify: openvpn restart + +- name: Ensure openvpn key dir has the right permission + file: + path: "{{ openvpn_keydir }}" + state: directory + mode: 0o700 + owner: "{{ openvpn_user }}" + +- name: Set ip forwarding in the sysctl file and reload if necessary + sysctl: + name: net.ipv4.ip_forward + value: '1' + sysctl_set: true + state: present + reload: true diff --git a/roles/stouts.openvpn/tasks/easy-rsa.yml b/roles/stouts.openvpn/tasks/easy-rsa.yml new file mode 100644 index 0000000..704fdeb --- /dev/null +++ b/roles/stouts.openvpn/tasks/easy-rsa.yml @@ -0,0 +1,45 @@ +--- + +- name: Check if easy-rsa 2.x is available from the system + stat: + path: /usr/share/easy-rsa/build-ca + register: openvpn_easyrsa_check + when: openvpn_use_system_easyrsa | bool + +- name: Fail if system rsa is not present + fail: + msg: >- + EasyRSA 2.x is not present in /usr/share/easy-rsa. + Ensure the easy-rsa package is installed or disable the + `openvpn_use_system_easyrsa` option. + when: + - openvpn_use_system_easyrsa | bool + - not openvpn_easyrsa_check.stat.exists | bool + +- name: Extract easy-rsa files + unarchive: + src: easy-rsa.tar.gz + dest: "{{ openvpn_etcdir }}" + when: not openvpn_use_system_easyrsa | bool + +# TODO revisit this in relation to +# https://github.com/Stouts/Stouts.openvpn/pull/107 +- name: Fix openssl.cnf path on Debian Stretch / Ubuntiu Bionic + file: + src: "{{ openvpn_etcdir + '/easy-rsa/openssl-1.0.0.cnf' }}" + dest: "{{ openvpn_etcdir + '/easy-rsa/openssl.cnf' }}" + state: link + when: + - ansible_distribution_release == 'stretch' or + ansible_distribution_release == 'bionic' + - not openvpn_use_system_easyrsa | bool + +- name: Fix openssl.cnf path on Debian Stretch / Ubuntiu Bionic with system easyrsa + file: + src: /usr/share/easy-rsa/openssl-1.0.0.cnf + dest: /usr/share/easy-rsa/openssl.cnf + state: link + when: + - ansible_distribution_release == 'stretch' or + ansible_distribution_release == 'bionic' + - openvpn_use_system_easyrsa | bool diff --git a/roles/stouts.openvpn/tasks/firewall.yml b/roles/stouts.openvpn/tasks/firewall.yml new file mode 100644 index 0000000..9065854 --- /dev/null +++ b/roles/stouts.openvpn/tasks/firewall.yml @@ -0,0 +1,77 @@ +--- + +- name: Install firewall dependencies (Debian) + apt: + name: "{{ packages }}" + when: ansible_os_family == "Debian" + vars: + packages: + - iptables + - iptables-persistent + - module-init-tools + +- name: Read existing iptable rules + shell: iptables -L + tags: + # Ansible Lint complains about using shell instead of the iptables_module + - skip_ansible_lint + register: iptables_rules + changed_when: false + +- name: Read existing iptable rules (nat table) + shell: iptables -L -t nat + tags: + # Ansible Lint complains about using shell instead of the iptables_module + - skip_ansible_lint + register: iptables_nat_rules + changed_when: false + +- name: Allow connections to the OpenVPN server + iptables: + chain: INPUT + in_interface: "{{ openvpn_interface | default(omit, true) }}" + ctstate: NEW + protocol: udp + destination_port: "{{ openvpn_port }}" + jump: ACCEPT + comment: incoming_openvpn + notify: openvpn save iptables + when: iptables_rules.stdout.find("incoming_openvpn") == -1 + +- name: Allow forwarding from tun/tap to interface + iptables: + chain: FORWARD + in_interface: "{{ openvpn_dev }}+" + out_interface: "{{ openvpn_interface | default(omit, true) }}" + jump: ACCEPT + comment: forward_tun_tap + notify: openvpn save iptables + when: iptables_rules.stdout.find("forward_tun_tap") == -1 + +- name: Allow forwarding from interface to tun/tap + iptables: + chain: FORWARD + in_interface: "{{ openvpn_interface | default(omit, true) }}" + out_interface: "{{ openvpn_dev }}+" + jump: ACCEPT + comment: forward_reverse + notify: openvpn save iptables + when: iptables_rules.stdout.find("forward_reverse") == -1 + +- name: Allow NATing outgoing vpn traffic + iptables: + table: nat + chain: POSTROUTING + source: >- + {{ + openvpn_server + | regex_replace('^(?P.+)\s+(?P.+)$', '\g/\g') + | ipaddr('net') + }} + out_interface: "{{ openvpn_interface | default(omit, true) }}" + jump: MASQUERADE + comment: vpn_masquerade + notify: openvpn save iptables + when: + - iptables_nat_rules.stdout.find("vpn_masquerade") == -1 + - openvpn_server | length > 0 diff --git a/roles/stouts.openvpn/tasks/main.yml b/roles/stouts.openvpn/tasks/main.yml new file mode 100644 index 0000000..f2a1f42 --- /dev/null +++ b/roles/stouts.openvpn/tasks/main.yml @@ -0,0 +1,5 @@ +--- + +- import_tasks: openvpn.yml + when: openvpn_enabled | bool + tags: openvpn diff --git a/roles/stouts.openvpn/tasks/openvpn.yml b/roles/stouts.openvpn/tasks/openvpn.yml new file mode 100644 index 0000000..6b767ba --- /dev/null +++ b/roles/stouts.openvpn/tasks/openvpn.yml @@ -0,0 +1,20 @@ +--- + +- include_tasks: variables.yml + +- include_tasks: assertions.yml + +- include_tasks: "{{ ansible_os_family }}/install.yml" + +- include_tasks: easy-rsa.yml + +- include_tasks: clients.yml + +- include_tasks: configure.yml + +- include_tasks: "{{ ansible_os_family }}/setup-bridge.yml" + +- include: firewall.yml + when: openvpn_route_traffic | bool + +- include_tasks: service.yml diff --git a/roles/stouts.openvpn/tasks/read-client-files.yml b/roles/stouts.openvpn/tasks/read-client-files.yml new file mode 100644 index 0000000..ae97c35 --- /dev/null +++ b/roles/stouts.openvpn/tasks/read-client-files.yml @@ -0,0 +1,52 @@ +--- +- name: Read CA file. + command: cat "{{ openvpn_keydir }}/ca.crt" + no_log: true + register: openvpn_read_ca_file_results + changed_when: false + +- name: Read TLS-auth key + slurp: + src: "{{ openvpn_keydir }}/{{ openvpn_tls_key }}" + no_log: true + register: openvpn_read_tlsauth_file_results + changed_when: false + when: openvpn_tls_auth + +- name: Read client cert files. + command: >- + sed -n + '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' + {{ openvpn_keydir }}/{{ item }}.crt + args: + warn: false + no_log: true + changed_when: false + register: openvpn_read_client_cert_files_results + loop: "{{ openvpn_clients }}" + +- name: Read client key files. + command: cat {{ openvpn_keydir }}/{{ item }}.key + no_log: true + changed_when: false + register: openvpn_read_client_key_files_results + loop: "{{ openvpn_clients }}" + +- name: Set client cert and CA info as fact. + set_fact: + openvpn_ca_file_contents: + "{{ openvpn_read_ca_file_results.stdout }}" + openvpn_client_cert_output: + "{{ openvpn_read_client_cert_files_results.results }}" + openvpn_client_keys_output: + "{{ openvpn_read_client_key_files_results.results }}" + +- name: Set tls auth file contents as fact. + set_fact: + openvpn_tls_auth_file_contents: >- + {{ + openvpn_read_tlsauth_file_results['content'] + | b64decode + | default('') + }} + when: openvpn_tls_auth diff --git a/roles/stouts.openvpn/tasks/service.yml b/roles/stouts.openvpn/tasks/service.yml new file mode 100644 index 0000000..a4507bb --- /dev/null +++ b/roles/stouts.openvpn/tasks/service.yml @@ -0,0 +1,7 @@ +--- + +- name: Ensure OpenVPN is started + service: + name: "{{ openvpn_service }}" + state: started + enabled: true diff --git a/roles/stouts.openvpn/tasks/variables.yml b/roles/stouts.openvpn/tasks/variables.yml new file mode 100644 index 0000000..b08e713 --- /dev/null +++ b/roles/stouts.openvpn/tasks/variables.yml @@ -0,0 +1,15 @@ +--- + +- name: Include OS-specific variables. + include_vars: + file: "{{ lookup('first_found', params) }}" + vars: + params: + files: + - "{{ ansible_distribution }}.{{ + ansible_lsb.codename | default(omit) }}.yml" + - "{{ ansible_distribution }}.yml" + - "{{ ansible_os_family }}.yml" + - "Common-default.yml" + paths: + - 'vars/os' diff --git a/roles/stouts.openvpn/templates/auth-client.sh.j2 b/roles/stouts.openvpn/templates/auth-client.sh.j2 new file mode 100644 index 0000000..fbfab3e --- /dev/null +++ b/roles/stouts.openvpn/templates/auth-client.sh.j2 @@ -0,0 +1,9 @@ +#!/bin/bash + +if [[ "$password" == "{{ openvpn_simple_auth_password }}" ]]; then + R="0" +else + R="1" +fi + +exit $R diff --git a/roles/stouts.openvpn/templates/auth-ldap.conf.j2 b/roles/stouts.openvpn/templates/auth-ldap.conf.j2 new file mode 100644 index 0000000..0eb79c6 --- /dev/null +++ b/roles/stouts.openvpn/templates/auth-ldap.conf.j2 @@ -0,0 +1,49 @@ + + # LDAP server URL + URL ldap://{{ openvpn_ldap_server }} + + # Bind DN (If your LDAP server doesn't support anonymous binds) + # e.g. cn=administrator,cn=users,dc=ctc,dc=local + BindDN {{ openvpn_ldap_bind_dn }} + + # Bind Password + Password {{ openvpn_ldap_bind_password }} + + # Network timeout (in seconds) + Timeout 15 + + # Enable Start TLS + TLSEnable {{ openvpn_ldap_tlsenable }} + + # Follow LDAP Referrals (anonymously) + FollowReferrals {{ openvpn_ldap_follow_referrals }} + + # TLS CA Certificate File + TLSCACertFile /etc/ssl/ca-cert.pem + + # TLS CA Certificate Directory + TLSCACertDir /etc/ssl/certs + + # Client Certificate and key + # If TLS client authentication is required + TLSCertFile /etc/ssl/client-cert.pem + TLSKeyFile /etc/ssl/client-key.pem + + # Cipher Suite + # The defaults are usually fine here + # TLSCipherSuite ALL:!ADH:@STRENGTH + + + + # e.g. "dc=ctc,dc=local" + BaseDN {{ openvpn_ldap_base_dn }} + # e.g. "sAMAccountName=%u" + SearchFilter {{ openvpn_ldap_search_filter }} + RequireGroup true + + BaseDN {{ openvpn_ldap_base_dn }} + # e.g. "cn=OpenVPNUsers" + SearchFilter {{ openvpn_ldap_group_search_filter }} + MemberAttribute Member + + diff --git a/roles/stouts.openvpn/templates/bridge-interface.deb.j2 b/roles/stouts.openvpn/templates/bridge-interface.deb.j2 new file mode 100644 index 0000000..bca1033 --- /dev/null +++ b/roles/stouts.openvpn/templates/bridge-interface.deb.j2 @@ -0,0 +1,19 @@ +# DO NOT EDIT THIS FILE BECAUSE IT IS AUTOMATICALLY GENERATED FROM ANSIBLE + +# OpenVPN interface +auto {{ openvpn_dev }} +iface {{ openvpn_dev }} inet manual + pre-up openvpn --mktun --dev {{ openvpn_dev }} --dev-type tap + post-down openvpn --rmtun --dev {{ openvpn_dev }} --dev-type tap + up ifconfig $IFACE 0.0.0.0 up + down ifconfig $IFACE down + +# Bridge +auto br-{{ openvpn_dev }} +iface br-{{ openvpn_dev }} inet static + bridge_ports {{ openvpn_dev }} + bridge_stp off + address {{openvpn_bridge.address}} + netmask {{openvpn_bridge.netmask}} + network {{openvpn_bridge.network}} + broadcast {{openvpn_bridge.broadcast}} diff --git a/roles/stouts.openvpn/templates/bridge-interface.rh.j2 b/roles/stouts.openvpn/templates/bridge-interface.rh.j2 new file mode 100644 index 0000000..8cb7339 --- /dev/null +++ b/roles/stouts.openvpn/templates/bridge-interface.rh.j2 @@ -0,0 +1,13 @@ +# DO NOT EDIT THIS FILE BECAUSE IT IS AUTOMATICALLY GENERATED FROM ANSIBLE + +DEVICE=br-{{ openvpn_dev }} +TYPE=Bridge +IPADDR={{openvpn_bridge.address}} +NETMASK={{openvpn_bridge.netmask}} +#NETWORK={{openvpn_bridge.network}} +#BROADCAST={{openvpn_bridge.broadcast}} +ONBOOT=yes +BOOTPROTO=none +NM_CONTROLLED=no +DELAY=0 +STP=off \ No newline at end of file diff --git a/roles/stouts.openvpn/templates/build-client.sh.j2 b/roles/stouts.openvpn/templates/build-client.sh.j2 new file mode 100644 index 0000000..7f9e029 --- /dev/null +++ b/roles/stouts.openvpn/templates/build-client.sh.j2 @@ -0,0 +1,9 @@ +#!/bin/sh + +# {{ ansible_managed }} + +. {{openvpn_etcdir}}/vars + +[ $# -lt 1 ] && echo "Usage: $0 " + +$EASY_RSA/pkitool $@ diff --git a/roles/stouts.openvpn/templates/build-server.sh.j2 b/roles/stouts.openvpn/templates/build-server.sh.j2 new file mode 100644 index 0000000..ee61ab3 --- /dev/null +++ b/roles/stouts.openvpn/templates/build-server.sh.j2 @@ -0,0 +1,23 @@ +#!/bin/sh + +# {{ ansible_managed }} + +. {{openvpn_etcdir}}/vars + +# Prepare keys directory +[ -d "$KEY_DIR" ] || { + + mkdir "$KEY_DIR" && \ + chmod go-rwx "$KEY_DIR" && \ + touch "$KEY_DIR/index.txt" && \ + echo 01 >"$KEY_DIR/serial" + +} + +$EASY_RSA/pkitool --initca && \ +$EASY_RSA/pkitool --server server && \ +$OPENSSL dhparam -out ${KEY_DIR}/dh${KEY_SIZE}.pem ${KEY_SIZE} + +{% for client in openvpn_clients %} +$EASY_RSA/pkitool {{ client }} +{% endfor %} diff --git a/roles/stouts.openvpn/templates/client.conf.j2 b/roles/stouts.openvpn/templates/client.conf.j2 new file mode 100644 index 0000000..2881f6c --- /dev/null +++ b/roles/stouts.openvpn/templates/client.conf.j2 @@ -0,0 +1,94 @@ +# {{ ansible_managed }} + +{%- set client=item -%} + +# Specify that we are a client and that we will be pulling certain config file +# directives from the server. +client + +# Use the same setting as you are using on the server. On most systems, the VPN +# will not function unless you partially or fully disable the firewall for the +# TUN/TAP interface. +dev {{ openvpn_dev }} + +# Are we connecting to a TCP or UDP server? Use the same setting as on the +# server. +proto {{ openvpn_proto }} + +# Encrypt packets with cipher algorithm +cipher {{ openvpn_cipher }} + +# The hostname/IP and port of the server. You can have multiple remote entries +# to load balance between the servers. +remote {{openvpn_host}} {{openvpn_port}} + +# Keep trying indefinitely to resolve the host name of the OpenVPN server. +# Very useful on machines which are not permanently connected to the internet +# such as laptops. +resolv-retry {{ openvpn_resolv_retry }} + +# Most clients don't need to bind to a specific local port number. +nobind + +# Try to preserve some state across restarts. +persist-key +persist-tun + +# SSL/TLS parms. See the server config file for more description. It's best to +# use a separate .crt/.key file pair for each client. A single ca file can be +# used for all clients. +{% if openvpn_unified_client_profiles %} + +{{ openvpn_ca_file_contents }} + + +{{ openvpn_client_cert_output |default([{'item':client,'stdout':''}])|selectattr('item', 'match', client)|map(attribute='stdout')|list|first }} + + +{{ openvpn_client_keys_output |default([{'item':client,'stdout':''}])|selectattr('item', 'match', client)|map(attribute='stdout')|list|first }} + +{% if openvpn_tls_auth %} +key-direction 1 + +{{ openvpn_tls_auth_file_contents | default('') }} + +{% endif %} + +{% else %} +ca ca.crt +cert {{client}}.crt +key {{client}}.key +{% endif %} + +# Verify server certificate by checking that the certicate has the nsCertType +# field set to "server". This is an important precaution to protect against a +# potential attack discussed here: http://openvpn.net/howto.html#mitm +# +# To use this feature, you will need to generate your server certificates with +# the nsCertType field set to "server". The build-key-server script in the +# easy-rsa folder will do this. +ns-cert-type server + +{% if openvpn_tls_auth and not openvpn_unified_client_profiles -%} +# Use a static pre-shared key (PSK) +tls-auth {{openvpn_tls_key}} 1 +{% endif %} + +# Enable compression on the VPN link. Don't enable this unless it is also +# enabled in the server config file. +{% if openvpn_comp_lzo -%} +comp-lzo +{% else -%} +;comp-lzo +{% endif %} + +# Set log file verbosity. +verb {{openvpn_verb}} + +{% if openvpn_use_pam or openvpn_use_ldap %} +auth-user-pass +{% endif %} + +{% for option in openvpn_client_options %} +{{option}} +{% endfor %} diff --git a/roles/stouts.openvpn/templates/down.sh.j2 b/roles/stouts.openvpn/templates/down.sh.j2 new file mode 100644 index 0000000..d8ebf9a --- /dev/null +++ b/roles/stouts.openvpn/templates/down.sh.j2 @@ -0,0 +1,6 @@ +#!/bin/sh +PATH=/sbin:/usr/sbin:/bin:/usr/bin +BR=$1 +DEV=$2 +brctl delif $BR $DEV +ip link set "$DEV" down \ No newline at end of file diff --git a/roles/stouts.openvpn/templates/openvpn.pam.j2 b/roles/stouts.openvpn/templates/openvpn.pam.j2 new file mode 100644 index 0000000..2aa43ba --- /dev/null +++ b/roles/stouts.openvpn/templates/openvpn.pam.j2 @@ -0,0 +1,12 @@ +# {{ ansible_managed }} + +{% if openvpn_use_pam_users and ansible_os_family == 'Debian' %} +auth required pam_pwdfile.so pwdfile={{openvpn_etcdir}}/users +account required pam_permit.so +{% elif ansible_os_family == 'Debian' %} +@include common-auth +@include common-account +{% elif ansible_os_family == 'RedHat' %} +auth include system-auth +account include system-auth +{% endif %} diff --git a/roles/stouts.openvpn/templates/revoke-client.sh.j2 b/roles/stouts.openvpn/templates/revoke-client.sh.j2 new file mode 100644 index 0000000..a586ee1 --- /dev/null +++ b/roles/stouts.openvpn/templates/revoke-client.sh.j2 @@ -0,0 +1,13 @@ +#!/bin/sh + +# {{ ansible_managed }} + +. {{openvpn_etcdir}}/vars + +[ $# -lt 1 ] && echo "Usage: $0 " + +$EASY_RSA/revoke-full $@ + +export CLIENT=$1 + +rm -rf $KEY_DIR/$CLIENT* diff --git a/roles/stouts.openvpn/templates/server.conf.j2 b/roles/stouts.openvpn/templates/server.conf.j2 new file mode 100644 index 0000000..16d44fa --- /dev/null +++ b/roles/stouts.openvpn/templates/server.conf.j2 @@ -0,0 +1,190 @@ +# {{ ansible_managed }} + +# Which local IP address should OpenVPN listen on? (optional) +{% if openvpn_local is defined -%} +local {{ openvpn_local }} +{% else -%} +;local a.b.c.d {% endif %} + +# Which TCP/UDP port should OpenVPN listen on? If you want to run multiple +# OpenVPN instances on the same machine, use a different port number for each +# one. You will need to open up this port on your firewall. +port {{ openvpn_port }} + +# TCP or UDP server? +proto {{ openvpn_proto }} + +{% if openvpn_portshare is defined %} +# Port sharing +port-share 127.0.0.1 {{ openvpn_portshare }} +{% endif %} + +# Encrypt packets with cipher algorithm +cipher {{ openvpn_cipher }} + +# "dev tun" will create a routed IP tunnel, "dev tap" will create an ethernet +# tunnel. Use "dev tap0" if you are ethernet bridging and have precreated a +# tap0 virtual interface and bridged it with your ethernet interface. If you +# want to control access policies over the VPN, you must create firewall rules +# for the the TUN/TAP interface. On non-Windows systems, you can give an +# explicit unit number, such as tun0. On Windows, use "dev-node" for this. On +# most systems, the VPN will not function unless you partially or fully disable +# the firewall for the TUN/TAP interface. +dev {{ openvpn_dev }} + +# SSL/TLS root certificate (ca), certificate (cert), and private key (key). +# Each client and the server must have their own cert and key file. The server +# and all clients will use the same ca file. +# +# See the "easy-rsa" directory for a series of scripts for generating RSA +# certificates and private keys. Remember to use a unique Common Name for the +# server and each of the client certificates. +# +# Any X509 key management system can be used. OpenVPN can also use a PKCS #12 +# formatted key file (see "pkcs12" directive in man page). +ca {{openvpn_keydir}}/ca.crt +cert {{openvpn_keydir}}/server.crt +key {{openvpn_keydir}}/server.key # This file should be kept secret + +# Diffie hellman parameters. Generate your own with: openssl dhparam -out +# dh1024.pem 1024 Substitute 2048 for 1024 if you are using 2048 bit keys. +dh {{openvpn_keydir}}/dh{{openvpn_key_size}}.pem + +{% if openvpn_tls_auth -%} +# Use a static pre-shared key (PSK) +tls-auth {{ openvpn_keydir }}/{{ openvpn_tls_key }} 0 +tls-server +{% endif %} + +# Client configuration directory. +{% if openvpn_client_config_dir is defined -%} +client-config-dir {{ openvpn_client_config_dir }} +{% endif %} + +# Which VPN topology to use? (net30, subnet, p2p) +{% if openvpn_topology is defined -%} +topology {{ openvpn_topology }} +{% endif %} + +{% if openvpn_server and not openvpn_bridge %} +# Configure server mode and supply a VPN subnet for OpenVPN to draw client +# addresses from. The server will take 10.8.0.1 for itself, the rest will be +# made available to clients. Each client will be able to reach the server on +# 10.8.0.1. Comment this line out if you are ethernet bridging. See the man +# page for more info. +server {{ openvpn_server }} +{% endif %} +{% if openvpn_bridge %} +# Configure server mode for ethernet bridging. +# You must first use your OS's bridging capability +# to bridge the TAP interface with the ethernet +# NIC interface. Then you must manually set the +# IP/netmask on the bridge interface, here we +# assume 10.8.0.4/255.255.255.0. Finally we +# must set aside an IP range in this subnet +# (start=10.8.0.50 end=10.8.0.100) to allocate +# to connecting clients. Leave this line commented +# out unless you are ethernet bridging. +server-bridge {{ openvpn_bridge.address }} {{ openvpn_bridge.netmask }} {{ openvpn_bridge.dhcp_start }} {{ openvpn_bridge.dhcp_end }} + +{% if ansible_os_family == 'RedHat' %} +# Tap management through script +up "/etc/openvpn/up.sh br-{{ openvpn_dev }}" +down "/etc/openvpn/down.sh br-{{ openvpn_dev }}" +script-security 2 +{% endif %} +{% endif %} + +# Maintain a record of client <-> virtual IP address associations in this file. +# If OpenVPN goes down or is restarted, reconnecting clients can be assigned +# the same virtual IP address from the pool that was previously assigned. +ifconfig-pool-persist {{openvpn_ifconfig_pool_persist}} + +# The keepalive directive causes ping-like messages to be sent back and forth +# over the link so that each side knows when the other side has gone down. Ping +# every 10 seconds, assume that remote peer is down if no ping received during +# a 120 second time period. +keepalive {{ openvpn_keepalive }} + +# Enable compression on the VPN link. If you enable it here, you must also +# enable it in the client config file. +{% if openvpn_comp_lzo -%} +comp-lzo +{% else -%} +;comp-lzo +{% endif %} + +# The persist options will try to avoid accessing certain resources on restart +# that may no longer be accessible because of the privilege downgrade. +persist-key +persist-tun + +# Output a short status file showing current connections, truncated and +# rewritten every minute. +status {{openvpn_status}} + +# By default, log messages will go to the syslog (or on Windows, if running as +# a service, they will go to the "\Program Files\OpenVPN\log" directory). Use +# log or log-append to override this default. "log" will truncate the log file +# on OpenVPN startup, while "log-append" will append to it. Use one or the +# other (but not both). +;log openvpn.log +log-append {{openvpn_log}} + +# Set the appropriate level of log file verbosity. +# +# 0 is silent, except for fatal errors 4 is reasonable for general usage 5 and +# 6 can help to debug connection problems 9 is extremely verbose +verb {{openvpn_verb}} + +# The maximum number of concurrently connected clients we want to allow. +max-clients {{openvpn_max_clients}} + +# It's a good idea to reduce the OpenVPN daemon's privileges after +# initialization. +# +# You can uncomment this out on non-Windows systems. +{% if openvpn_user -%} +user {{openvpn_user}} +{% else -%} +;user nobody +{% endif %} +{% if openvpn_group -%} +group {{openvpn_group}} +{% else -%} +group nogroup +{% endif %} + +{% if openvpn_client_to_client %} +client-to-client +{% endif %} + +{% if openvpn_use_pam %} +client-cert-not-required +plugin {{openvpn_use_pam_plugin|default(openvpn_use_pam_plugin_distribution)}} openvpn +{% endif %} + +{% if openvpn_use_ldap %} +plugin {{ openvpn_use_ldap_plugin | default(openvpn_use_ldap_plugin_distribution) }} "/etc/openvpn/auth-ldap.conf" +{% endif %} + +{% if openvpn_simple_auth and openvpn_simple_auth_password %} +auth-user-pass-verify auth-client.sh via-env +script-security 3 execve +{% endif %} + +{% for option in openvpn_server_options %} +{{option}} +{% endfor %} + +{% if crl_pem_file.stat.exists %} +crl-verify {{openvpn_keydir}}/crl.pem +{% endif %} + +{% for dns in openvpn_dns_servers %} +push "dhcp-option DNS {{ dns }}" +{% endfor %} + +{% for push_route in openvpn_route_ranges %} +push "route {{ push_route }}" +{% endfor %} diff --git a/roles/stouts.openvpn/templates/up.sh.j2 b/roles/stouts.openvpn/templates/up.sh.j2 new file mode 100644 index 0000000..9cc1bd5 --- /dev/null +++ b/roles/stouts.openvpn/templates/up.sh.j2 @@ -0,0 +1,9 @@ +#!/bin/sh +PATH=/sbin:/usr/sbin:/bin:/usr/bin +BR=$1 +DEV=$2 +MTU=$3 +ip link set "$DEV" up promisc on mtu "$MTU" +if ! brctl show $BR | egrep -q "\W+$DEV$"; then + brctl addif $BR $DEV +fi \ No newline at end of file diff --git a/roles/stouts.openvpn/templates/vars.j2 b/roles/stouts.openvpn/templates/vars.j2 new file mode 100644 index 0000000..0200369 --- /dev/null +++ b/roles/stouts.openvpn/templates/vars.j2 @@ -0,0 +1,63 @@ +# {{ ansible_managed }} + +# easy-rsa parameter settings +{% if openvpn_use_system_easyrsa %} +export EASY_RSA=/usr/share/easy-rsa +{% else %} +export EASY_RSA="{{ openvpn_etcdir }}/easy-rsa" +{% endif %} + +# This variable should point to +# the requested executables +export OPENSSL="openssl" +export PKCS11TOOL="pkcs11-tool" +export GREP="grep" + + +# This variable should point to +# the openssl.cnf file included +# with easy-rsa. +export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA` + +# Edit this variable to point to +# your soon-to-be-created key +# directory. +# +# WARNING: clean-all will do +# a rm -rf on this directory +# so make sure you define +# it correctly! +export KEY_DIR="{{ openvpn_keydir }}" + +# Issue rm -rf warning +echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR + +# PKCS11 fixes +export PKCS11_MODULE_PATH="dummy" +export PKCS11_PIN="dummy" + +# Increase this to 2048 if you +# are paranoid. This will slow +# down TLS negotiation performance +# as well as the one-time DH parms +# generation process. +export KEY_SIZE={{ openvpn_key_size }} + +# In how many days should the root CA key expire? +export CA_EXPIRE=3650 + +# In how many days should certificates expire? +export KEY_EXPIRE=3650 + +# These are the default values for fields +# which will be placed in the certificate. +# Don't leave any of these fields blank. +export KEY_COUNTRY="{{ openvpn_key_country }}" +export KEY_PROVINCE="{{ openvpn_key_province }}" +export KEY_CITY="{{ openvpn_key_city }}" +export KEY_ORG="{{ openvpn_key_org }}" +export KEY_EMAIL="{{ openvpn_key_email }}" +export KEY_OU="{{ openvpn_key_ou|default('MyOrganizationalUnit') }}" + +# X509 Subject Field +export KEY_NAME="EasyRSA" diff --git a/roles/stouts.openvpn/vars/os/Common-default.yml b/roles/stouts.openvpn/vars/os/Common-default.yml new file mode 100644 index 0000000..c99be61 --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Common-default.yml @@ -0,0 +1,3 @@ +--- + +openvpn_service: openvpn diff --git a/roles/stouts.openvpn/vars/os/Debian.jessie.yml b/roles/stouts.openvpn/vars/os/Debian.jessie.yml new file mode 100644 index 0000000..267e8bc --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Debian.jessie.yml @@ -0,0 +1,10 @@ +--- + +openvpn_use_pam_plugin_distribution: + /usr/lib/openvpn/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: + /usr/lib/openvpn/openvpn-auth-ldap.so + +openvpn_service: + "{{ 'openvpn@server' if ansible_service_mgr == 'systemd' else 'openvpn' }}" diff --git a/roles/stouts.openvpn/vars/os/Debian.stretch.yml b/roles/stouts.openvpn/vars/os/Debian.stretch.yml new file mode 100644 index 0000000..267e8bc --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Debian.stretch.yml @@ -0,0 +1,10 @@ +--- + +openvpn_use_pam_plugin_distribution: + /usr/lib/openvpn/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: + /usr/lib/openvpn/openvpn-auth-ldap.so + +openvpn_service: + "{{ 'openvpn@server' if ansible_service_mgr == 'systemd' else 'openvpn' }}" diff --git a/roles/stouts.openvpn/vars/os/Debian.yml b/roles/stouts.openvpn/vars/os/Debian.yml new file mode 100644 index 0000000..0254469 --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Debian.yml @@ -0,0 +1,8 @@ +--- + +openvpn_service: openvpn + +openvpn_use_pam_plugin_distribution: /usr/lib/openvpn/openvpn-auth-pam.so + +openvpn_use_ldap_plugin_distribution: /usr/lib/openvpn/openvpn-auth-ldap.so + diff --git a/roles/stouts.openvpn/vars/os/RedHat.yml b/roles/stouts.openvpn/vars/os/RedHat.yml new file mode 100644 index 0000000..3359d3b --- /dev/null +++ b/roles/stouts.openvpn/vars/os/RedHat.yml @@ -0,0 +1,13 @@ +--- + +openvpn_service: openvpn@server + +openvpn_group: nobody + +openvpn_use_pam_plugin_distribution: + /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: + /usr/lib64/openvpn/plugin/lib/openvpn-auth-ldap.so + +openvpn_use_external_repo: true diff --git a/roles/stouts.openvpn/vars/os/Ubuntu.bionic.yml b/roles/stouts.openvpn/vars/os/Ubuntu.bionic.yml new file mode 100644 index 0000000..df10bcb --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Ubuntu.bionic.yml @@ -0,0 +1,9 @@ +--- + +openvpn_use_pam_plugin_distribution: + /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: + /usr/lib/x86_64-linux-gnu/openvpn/plugins/openvpn-auth-ldap.so + +openvpn_service: openvpn \ No newline at end of file diff --git a/roles/stouts.openvpn/vars/os/Ubuntu.trusty.yml b/roles/stouts.openvpn/vars/os/Ubuntu.trusty.yml new file mode 100644 index 0000000..2da5bf5 --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Ubuntu.trusty.yml @@ -0,0 +1,7 @@ +--- + +openvpn_use_pam_plugin_distribution: /usr/lib/openvpn/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: /usr/lib/openvpn/openvpn-auth-ldap.so + +openvpn_service: openvpn diff --git a/roles/stouts.openvpn/vars/os/Ubuntu.vivid.yml b/roles/stouts.openvpn/vars/os/Ubuntu.vivid.yml new file mode 100644 index 0000000..2da5bf5 --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Ubuntu.vivid.yml @@ -0,0 +1,7 @@ +--- + +openvpn_use_pam_plugin_distribution: /usr/lib/openvpn/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: /usr/lib/openvpn/openvpn-auth-ldap.so + +openvpn_service: openvpn diff --git a/roles/stouts.openvpn/vars/os/Ubuntu.xenial.yml b/roles/stouts.openvpn/vars/os/Ubuntu.xenial.yml new file mode 100644 index 0000000..751d5a5 --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Ubuntu.xenial.yml @@ -0,0 +1,10 @@ +--- + +openvpn_use_pam_plugin_distribution: + /usr/lib/openvpn/openvpn-plugin-auth-pam.so + +openvpn_use_ldap_plugin_distribution: + /usr/lib/openvpn/openvpn-auth-ldap.so + +openvpn_service: openvpn + diff --git a/roles/stouts.openvpn/vars/os/Ubuntu.yml b/roles/stouts.openvpn/vars/os/Ubuntu.yml new file mode 100644 index 0000000..b2052ff --- /dev/null +++ b/roles/stouts.openvpn/vars/os/Ubuntu.yml @@ -0,0 +1,7 @@ +--- + +openvpn_use_pam_plugin_distribution: /usr/lib/openvpn/openvpn-auth-pam.so + +openvpn_use_ldap_plugin_distribution: /usr/lib/openvpn/openvpn-auth-ldap.so + +openvpn_service: openvpn diff --git a/types/bastion/firewall.yml b/types/bastion/firewall.yml index 4de9df9..ae68ead 100644 --- a/types/bastion/firewall.yml +++ b/types/bastion/firewall.yml @@ -9,6 +9,8 @@ firewall_default_input: DROP firewall_default_output: DROP firewall_default_forward: DROP + firewall_additional_rules: + - "iptables -t filter -A INPUT -p tcp -s {{ network.private_range | ipaddr('address') | ipmath(2) }} --dport 22 -j ACCEPT" # Authorize input firewall_allowed_input_tcp_ports: - 22 diff --git a/types/gateway/firewall/management.yml b/types/gateway/firewall/management.yml index 41e3acb..def1db2 100644 --- a/types/gateway/firewall/management.yml +++ b/types/gateway/firewall/management.yml @@ -9,6 +9,7 @@ firewall_masquerade_authorized_ips: - "{{ network.private_range | ipaddr('address') | ipmath(1) }}" - "{{ network.private_range | ipaddr('address') | ipmath(2) }}" + - "{{ network.private_range | ipaddr('address') | ipmath(3) }}" firewall_allowed_input_tcp_ports: - 80 - 443 @@ -16,6 +17,9 @@ - src_port: 22 dest_ip: "{{ network.private_range | ipaddr('address') | ipmath(1) }}" dest_port: 22 + - src_port: 8443 + dest_ip: "{{ network.private_range | ipaddr('address') | ipmath(3) }}" + dest_port: 443 # Authorize SSH from bastion and awx firewall_additional_rules: - "iptables -t filter -A INPUT -p tcp -s {{ network.private_range | ipaddr('address') | ipmath(1) }} --dport 22 -j ACCEPT" diff --git a/types/gateway/webserver/management.yml b/types/gateway/webserver/management.yml index 5df6909..46be63a 100644 --- a/types/gateway/webserver/management.yml +++ b/types/gateway/webserver/management.yml @@ -35,6 +35,7 @@ - name: WebServer - Set LetsEncrypt Configuration set_fact: + certbot_install_from_source: true certbot_create_if_missing: true certbot_certs: - domains: diff --git a/types/gateway/webserver/management/awx.yml b/types/gateway/webserver/management/awx.yml index 5ba7534..0594cd6 100644 --- a/types/gateway/webserver/management/awx.yml +++ b/types/gateway/webserver/management/awx.yml @@ -1,18 +1,18 @@ -- name: Virtual Host - AWX redirect SSL - set_fact: - nginx_http_template: "{{ nginx_http_template|default({}) | combine({key: conf}) }}" - vars: - key: awx-redirect-ssl - conf: - template_file: http/default.conf.j2 - conf_file_name: "awx.{{ dns.domain }}.redirect.conf" - conf_file_location: /etc/nginx/conf.d/ - listen: - listen_http: - port: 80 - server_name: awx.{{ dns.domain }} - error_page: /usr/share/nginx/html - https_redirect: true +# - name: Virtual Host - AWX redirect SSL +# set_fact: +# nginx_http_template: "{{ nginx_http_template|default({}) | combine({key: conf}) }}" +# vars: +# key: awx-redirect-ssl +# conf: +# template_file: http/default.conf.j2 +# conf_file_name: "awx.{{ dns.domain }}.redirect.conf" +# conf_file_location: /etc/nginx/conf.d/ +# listen: +# listen_http: +# port: 80 +# server_name: awx.{{ dns.domain }} +# error_page: /usr/share/nginx/html +# https_redirect: true - name: Virtual Host - AWX with SSL set_fact: diff --git a/types/vpn/base.yml b/types/vpn/base.yml new file mode 100644 index 0000000..b64d0a1 --- /dev/null +++ b/types/vpn/base.yml @@ -0,0 +1,23 @@ +--- +- name: Apply Basic Roles on VPN + hosts: all + pre_tasks: + - name: Load Default Debian Config + include_tasks: ../../config/debian.yml + + - name: Launch Default Debian Pre Tasks + include_tasks: ../../base/pretask_debian.yml + + - name: Set Special Sysctl configuration for openvpn traversal + set_fact: + sysctl_overwrite: + net.ipv4.ip_forward: 1 + when: extras.vpn.route_traffic == True + + roles: + - dev-sec.os-hardening + - dev-sec.ssh-hardening + - mderasse.fail2ban + - mderasse.rkhunter + - mderasse.bastion_client + - geerlingguy.ntp \ No newline at end of file diff --git a/types/vpn/firewall.yml b/types/vpn/firewall.yml new file mode 100644 index 0000000..7645e32 --- /dev/null +++ b/types/vpn/firewall.yml @@ -0,0 +1,33 @@ +--- +- name: Apply Firewall on VPN + hosts: all + become: yes + pre_tasks: + - name: Set Iptables rules for VPN + set_fact: + # Default Policies + firewall_default_input: DROP + firewall_default_output: DROP + firewall_default_forward: DROP + firewall_allowed_input_tcp_ports: + - 443 + # Authorize SSH from bastion and awx + firewall_additional_rules: + - "iptables -t filter -A INPUT -p tcp -s {{ network.private_range | ipaddr('address') | ipmath(1) }} --dport 22 -j ACCEPT" + - "iptables -t filter -A INPUT -p tcp -s {{ network.private_range | ipaddr('address') | ipmath(2) }} --dport 22 -j ACCEPT" + + - name: Set Iptables rules if openVpn is in traversal mode + set_fact: + # Authorize internet access to bastion and awx + firewall_masquerade_enabled: true + firewall_masquerade_authorized_ips: + - "10.8.0.0/24" + firewall_additional_rules: "{{ firewall_additional_rules + vpn_nat_additional_rules }}" + vars: + vpn_nat_additional_rules: + - "iptables -t filter -A FORWARD -i tun+ -j ACCEPT" + - "iptables -t filter -A FORWARD -o tun+ -j ACCEPT" + when: extras.vpn.route_traffic == True + + roles: + - mderasse.firewall \ No newline at end of file diff --git a/types/vpn/openvpn.yml b/types/vpn/openvpn.yml new file mode 100644 index 0000000..8482c53 --- /dev/null +++ b/types/vpn/openvpn.yml @@ -0,0 +1,27 @@ +--- +- name: Install and configure OpenVpn on VPN + hosts: all + become: yes + pre_tasks: + - name: Set Configuration for OpenVpn + set_fact: + openvpn_route_traffic: false # Whatever happen, keep it false. Firewall is managed by vploy + openvpn_server_options: + - "push \"redirect-gateway def1 bypass-dhcp\"" + openvpn_port: "{{ extras.vpn.port }}" + openvpn_proto: "{{ extras.vpn.proto }}" + openvpn_client_to_client: "{{ extras.vpn.client_to_client }}" + openvpn_route_ranges: + - "{{ network.private_range | ipaddr('address') | ipmath(0) }} {{ network.private_range | ipaddr('netmask') }}" + openvpn_dns_servers: "{{ dns.nameservers }}" + openvpn_use_pam: true + openvpn_use_pam_users: + + - name: Configure routing traffic + set_fact: + openvpn_server_options: + - "push \"redirect-gateway def1 bypass-dhcp\"" + when: extras.vpn.route_traffic == True + + roles: + - stouts.openvpn diff --git a/types/vpn/roles b/types/vpn/roles new file mode 120000 index 0000000..148b132 --- /dev/null +++ b/types/vpn/roles @@ -0,0 +1 @@ +../../roles/ \ No newline at end of file