diff --git a/openvpn/defaults/main.yml b/openvpn/defaults/main.yml
index 5feb942..bf13e47 100644
--- a/openvpn/defaults/main.yml
+++ b/openvpn/defaults/main.yml
@@ -5,14 +5,24 @@ openvpn_pkg_state: latest
openvpn_pkgs:
- openvpn
+# Authentication choices
+openvpn_cert_auth_enabled: True
+openvpn_username_pam_auth: False
+
openvpn_radius_auth: False
openvpn_radius_pkg:
- openvpn-auth-radius
+# With openvpn-auth-ldap. Broken on Ubuntu trusty
openvpn_ldap_auth: False
openvpn_ldap_pkg:
- openvpn-auth-ldap
+openvpn_ldap_perl_auth: False
+openvpn_perl_pkg:
+ - libnet-ldap-perl
+
+# Server con parameters
openvpn_conf_dir: /etc/openvpn
openvpn_conf_name: openvpn.conf
@@ -26,12 +36,13 @@ openvpn_push_routes:
#openvpn_push_settings:
# - "dhcp-option DNS 10.66.0.4"
-
+
openvpn_tls_server: True
openvpn_dh: /etc/openvpn/dh2048.pem
openvpn_tls_auth: '/etc/openvpn/ta.key 0'
openvpn_install_alternative_ca: False
openvpn_alternative_ca_name: ca.pem
+openvpn_ca_dir: False
openvpn_ca: '/var/lib/acme/live/{{ ansible_fqdn }}/chain'
openvpn_cert: '/var/lib/acme/live/{{ ansible_fqdn }}/cert'
openvpn_key: '/var/lib/acme/live/{{ ansible_fqdn }}/privkey'
@@ -39,9 +50,6 @@ openvpn_key: '/var/lib/acme/live/{{ ansible_fqdn }}/privkey'
openvpn_compression_enabled: False
openvpn_keepalive: '10 120'
-openvpn_cert_auth_enabled: True
-openvpn_username_pam_auth: False
-
openvpn_max_clients: 50
openvpn_run_unprivileged: True
openvpn_unprivileged_user: nobody
@@ -50,3 +58,33 @@ openvpn_letsencrypt_managed: True
openvpn_verbosity_log: 3
openvpn_mute_after: 20
+
+# LDAP conf
+openvpn_ldap_uri: 'ldap:'
+openvpn_ldap_host: ldap.example.org
+openvpn_ldap_url: '{{ openvpn_ldap_uri }}//{{ openvpn_ldap_host }}'
+openvpn_ldap_nonanon_bind: False
+openvpn_ldap_binddn: uid=admin
+openvpn_ldap_bindpwd: test
+openvpn_ldap_ca: '{{ openvpn_ca }}'
+openvpn_ldap_use_ca_dir: False
+openvpn_ldap_ca_dir: /etc/ssl/certs
+openvpn_ldap_starttls: False
+openvpn_ldap_tls_auth: False
+openvpn_ldap_tls_cert: '{{ openvpn_cert }}'
+openvpn_ldap_tls_key: '{{ openvpn_key }}'
+openvpn_ldap_tls_ciphersuite: 'ALL:!ADH:@STRENGTH'
+# LDAP auth
+openvpn_ldap_base_dn: 'ou=People,dc=example,dc=org'
+openvpn_ldap_user_search: '(&(uid=%u))'
+openvpn_ldap_require_group: False
+# See https://github.com/threerings/openvpn-auth-ldap/issues/7
+openvpn_ldap_without_posix_groups: True
+openvpn_ldap_group_base: 'ou=Groups,dc=example,dc=org'
+openvpn_ldap_group_filter: '(|(cn=developers)(cn=artists))'
+openvpn_ldap_group_member_attr: uniqueMember
+
+# Perl LDAP conf
+openvpn_ldap_perl_auth_ssl: True
+openvpn_ldap_perl_auth_sslport: 636
+openvpn_ldap_perl_auth_group: vpn_ldap_posix_group
diff --git a/openvpn/tasks/openvpn.yml b/openvpn/tasks/openvpn.yml
index a3935d0..b3146aa 100644
--- a/openvpn/tasks/openvpn.yml
+++ b/openvpn/tasks/openvpn.yml
@@ -1,79 +1,113 @@
---
-- name: Install the OpenVPN main packages
- apt: pkg={{ item }} state={{ openvpn_pkg_state }} update_cache=yes
- with_items: '{{ openvpn_pkgs }}'
- tags: openvpn
+- block:
+ - name: Install the OpenVPN main packages
+ apt: pkg={{ item }} state={{ openvpn_pkg_state }} update_cache=yes cache_valid_time=1800
+ with_items: '{{ openvpn_pkgs }}'
-- name: Install the OpenVPN radius auth plugin package
- apt: pkg={{ item }} state={{ openvpn_pkg_state }}
- with_items: '{{ openvpn_radius_pkg }}'
- when: openvpn_radius_auth
- tags: openvpn
+ - name: Create the auth, ipp and status subdirs
+ file: dest={{ openvpn_conf_dir }}/{{ item }} state=directory owner={{ openvpn_unprivileged_user }} group=root mode=0770
+ with_items:
+ - ipp
+ - status
+ - auth
-- name: Install the OpenVPN ldap auth plugin package
- apt: pkg={{ item }} state={{ openvpn_pkg_state }}
- with_items: '{{ openvpn_ldap_pkg }}'
- when: openvpn_ldap_auth
- tags: openvpn
-
-- name: Install the OpenVPN PAM auth plugin
- shell: cp /usr/lib/openvpn/openvpn-plugin-auth-pam.so {{ openvpn_conf_dir }}/openvpn-plugin-auth-pam.so
- args:
- creates: '{{ openvpn_conf_dir }}/openvpn-plugin-auth-pam.so'
- when: openvpn_username_pam_auth
- tags: openvpn
-
-- name: Remove the OpenVPN PSM auth plugin
- file: dest={{ openvpn_conf_dir }}/openvpn-plugin-auth-pam.so state=absent
- when: not openvpn_username_pam_auth
- tags: openvpn
-
-- name: Create the ipp and status subdirs
- file: dest={{ openvpn_conf_dir }}/{{ item }} state=directory
- with_items:
- - ipp
- - status
- tags: openvpn
-
-- name: Install the main OpenVPN configuration file
- template: src=openvpn.conf.j2 dest={{ openvpn_conf_dir }}/{{ openvpn_conf_name }} owner=root group=root mode=0444
- notify: Reload OpenVPN
- tags: openvpn
-
-- name: Create the dh file
- shell: openssl dhparam -out {{ openvpn_conf_dir }}/dh2048.pem 2048 ; chmod 444 {{ openvpn_conf_dir }}/dh2048.pem
- args:
- creates: '{{ openvpn_conf_dir }}/dh2048.pem'
- tags: openvpn
-
-- name: Create the ta key
- shell: cd {{ openvpn_conf_dir }} ; openvpn --genkey --secret ta.key ; chmod 400 {{ openvpn_conf_dir }}/ta.key
- args:
- creates: '{{ openvpn_conf_dir }}/ta.key'
- tags: openvpn
-
-- name: Ensure that the OpenVPN service is enabled and running
- service: name=openvpn state=started enabled=yes
when: openvpn_enabled
tags: openvpn
-- name: Ensure that the OpenVPN service is stopped and disabled
- service: name=openvpn state=stopped enabled=no
- when: not openvpn_enabled
+- block:
+ - name: Install the OpenVPN radius auth plugin package
+ apt: pkg={{ item }} state={{ openvpn_pkg_state }} update_cache=yes cache_valid_time=1800
+ with_items: '{{ openvpn_radius_pkg }}'
+
+ when: openvpn_radius_auth
+ tags: [ 'openvpn', 'openvpn_radius' ]
+
+
+- block:
+ - name: apt key for the internal ppa repository. Needed by the openvpn ldap auth with posix groups
+ apt_key: url=http://ppa.research-infrastructures.eu/system/keys/system-archive.asc state=present
+ when:
+ - openvpn_ldap_auth
+ - not openvpn_ldap_without_posix_groups
+
+ - name: Setup the internal apt repository
+ apt_repository: repo='deb http://ppa.research-infrastructures.eu/system trusty main'
+ when:
+ - openvpn_ldap_auth
+ - not openvpn_ldap_without_posix_groups
+
+ - name: Install the OpenVPN ldap auth plugin package
+ apt: pkg={{ item }} state={{ openvpn_pkg_state }} update_cache=yes cache_valid_time=1800
+ with_items: '{{ openvpn_ldap_pkg }}'
+
+ - name: Install the LDAP auth configuration file
+ template: src=auth-ldap.conf.j2 dest={{ openvpn_conf_dir }}/auth/auth-ldap.conf owner=root group={{ openvpn_unprivileged_group }} mode=0440
+ notify: Reload OpenVPN
+
+ - name: Remove the LDAP auth configuration file if LDAP is not used
+ file: dest={{ openvpn_conf_dir }}/auth/auth-ldap.conf state=absent
+ notify: Reload OpenVPN
+ when: not openvpn_ldap_auth
+
+
+ when: openvpn_ldap_auth
+ tags: [ 'openvpn', 'openvpn_ldap' ]
+
+
+- block:
+ - name: Install the perl libraries needed by the LDAP client authentication script
+ apt: pkg={{ item }} state={{ openvpn_pkg_state }} update_cache=yes cache_valid_time=1800
+ with_items: '{{ openvpn_perl_pkg }}'
+
+ - name: Install the perl LDAP auth script
+ template: src=auth-ldap.pl.j2 dest={{ openvpn_conf_dir }}/auth/auth-ldap owner=root group={{ openvpn_unprivileged_group }} mode=0550
+
+ when: openvpn_ldap_perl_auth
+ tags: [ 'openvpn', 'openvpn_ldap' ]
+
+
+- block:
+ - name: Install the main OpenVPN configuration file
+ template: src=openvpn.conf.j2 dest={{ openvpn_conf_dir }}/{{ openvpn_conf_name }} owner=root group={{ openvpn_unprivileged_group }} mode=0440
+ notify: Reload OpenVPN
+
+ - name: Create the dh file
+ shell: openssl dhparam -out {{ openvpn_conf_dir }}/dh2048.pem 2048 ; chmod 444 {{ openvpn_conf_dir }}/dh2048.pem
+ args:
+ creates: '{{ openvpn_conf_dir }}/dh2048.pem'
+
+ - name: Create the ta key
+ shell: cd {{ openvpn_conf_dir }} ; openvpn --genkey --secret ta.key ; chmod 400 {{ openvpn_conf_dir }}/ta.key
+ args:
+ creates: '{{ openvpn_conf_dir }}/ta.key'
+
+ tags: [ 'openvpn', 'openvpn_conf' ]
+
+
+- block:
+ - name: Enable kernel forwarding
+ sysctl: name={{ item }} value=1 reload=yes state=present
+ with_items:
+ - net.ipv4.ip_forward
+ # - net.ipv6.conf.all.forwarding
+ when:
+ - openvpn_enable_system_forward
+ - openvpn_enabled
+
+ - name: Disable kernel forwarding
+ sysctl: name={{ item }} value=0 reload=yes state=present
+ with_items:
+ - net.ipv4.ip_forward
+ # - net.ipv6.conf.all.forwarding
+ when: not openvpn_enable_system_forward
+
+ - name: Ensure that the OpenVPN service is enabled and running
+ service: name=openvpn state=started enabled=yes
+ when: openvpn_enabled
+
+ - name: Ensure that the OpenVPN service is stopped and disabled
+ service: name=openvpn state=stopped enabled=no
+ when: not openvpn_enabled
+
tags: openvpn
-- name: Enable kernel forwarding
- sysctl: name={{ item }} value=1 reload=yes state=present
- with_items:
- - net.ipv4.ip_forward
-# - net.ipv6.conf.all.forwarding
- when: openvpn_enable_system_forward
- tags: openvpn
-
-- name: Disable kernel forwarding
- sysctl: name={{ item }} value=0 reload=yes state=present
- with_items:
- - net.ipv4.ip_forward
-# - net.ipv6.conf.all.forwarding
- when: not openvpn_enable_system_forward
- tags: openvpn
diff --git a/openvpn/templates/auth-ldap.conf.j2 b/openvpn/templates/auth-ldap.conf.j2
new file mode 100644
index 0000000..6403aa1
--- /dev/null
+++ b/openvpn/templates/auth-ldap.conf.j2
@@ -0,0 +1,67 @@
+
+ # LDAP server URL
+ URL {{ openvpn_ldap_url }}
+
+{% if openvpn_ldap_nonanon_bind %}
+ # Bind DN (If your LDAP server doesn't support anonymous binds)
+ BindDN {{ openvpn_ldap_binddn }}
+ # Bind Password
+ Password {{ openvpn_ldap_bindpwd }}
+{% endif %}
+
+ # Network timeout (in seconds)
+ Timeout 15
+
+{% if openvpn_ldap_starttls %}
+ # Enable Start TLS
+ TLSEnable yes
+{% endif %}
+
+ # Follow LDAP Referrals (anonymously)
+ FollowReferrals yes
+
+ # TLS CA Certificate File
+ TLSCACertFile {{ openvpn_ldap_ca }}
+
+{% if openvpn_ldap_use_ca_dir %}
+ # TLS CA Certificate Directory
+ # TLSCACertDir {{ openvpn_ldap_ca_dir }}
+{% endif %}
+
+{% if openvpn_ldap_tls_auth %}
+ # Client Certificate and key
+ # If TLS client authentication is required
+ TLSCertFile {{ openvpn_ldap_tls_cert }}
+ TLSKeyFile {{ openvpn_ldap_tls_key }}
+{% endif %}
+
+ # Cipher Suite
+ # The defaults are usually fine here
+ TLSCipherSuite {{ openvpn_ldap_tls_ciphersuite }}
+
+
+
+ # Base DN
+ BaseDN "{{ openvpn_ldap_base_dn }}"
+
+ # User Search Filter
+ # SearchFilter "(&(uid=%u)(accountStatus=active))"
+ SearchFilter "{{ openvpn_ldap_user_search }}"
+
+ # Require Group Membership
+ RequireGroup {{ openvpn_ldap_require_group }}
+
+{% if openvpn_ldap_require_group %}
+ # Add non-group members to a PF table (disabled)
+ #PFTable ips_vpn_users
+
+
+ BaseDN "{{ openvpn_ldap_group_base }}"
+ SearchFilter "{{ openvpn_ldap_group_filter }}"
+ RFC2307bis {{ openvpn_ldap_without_posix_groups }}
+ MemberAttribute {{ openvpn_ldap_group_member_attr }}
+ # Add group members to a PF table (disabled)
+ # #PFTable ips_vpn_eng
+
+{% endif %}
+
diff --git a/openvpn/templates/auth-ldap.pl.j2 b/openvpn/templates/auth-ldap.pl.j2
new file mode 100644
index 0000000..8bd14ae
--- /dev/null
+++ b/openvpn/templates/auth-ldap.pl.j2
@@ -0,0 +1,42 @@
+#!/usr/bin/perl -w
+
+{% if openvpn_ldap_perl_auth_ssl %}
+use Net::LDAPS;
+{% else %}
+use Net::LDAP;
+{% endif %}
+use strict;
+
+my $ldap;
+my $result;
+
+my $opt_uri = "{{ openvpn_ldap_host }}";
+my $opt_user = $ENV{'username'};
+my $opt_passwd = $ENV{'password'};
+my $opt_group = "cn={{ openvpn_ldap_perl_auth_group }},{{ openvpn_ldap_group_base }}";
+my $opt_binddn = "uid=".$opt_user.",{{ openvpn_ldap_base_dn }}";
+
+{% if openvpn_ldap_perl_auth_ssl %}
+$ldap = Net::LDAPS->new($opt_uri, version => 3,
+ port => '{{ openvpn_ldap_perl_auth_sslport }}',
+ verify => 'require',
+ {% if openvpn_ca_dir %}
+ capath => '{{ openvpn_ldap_ca }}'
+ {% else %}
+ cafile => '{{ openvpn_ldap_ca }}'
+ {% endif %}
+ ) or die("LDAPS connect to $opt_uri failed!");
+{% else %}
+$ldap = Net::LDAP->new($opt_uri) or die("LDAP connect to $opt_uri failed!");
+{% endif %}
+
+{% if openvpn_ldap_nonanon_bind %}
+$result = $ldap->bind('{{ openvpn_ldap_binddn }}', password => '{{ openvpn_ldap_bindpwd }}');
+{% else %}
+$result = $ldap->bind($opt_binddn, password => $opt_passwd);
+{% endif %}
+$result->code and die($result->error);
+$result = $ldap->search(base=>$opt_group, filter => "(&({{ openvpn_ldap_group_member_attr }}=$opt_user))");
+$result->code();
+if ($result->count == 1) { exit 0; }
+unless($result->count){ exit 1; }
diff --git a/openvpn/templates/openvpn.conf.j2 b/openvpn/templates/openvpn.conf.j2
index cc08d93..7da699f 100644
--- a/openvpn/templates/openvpn.conf.j2
+++ b/openvpn/templates/openvpn.conf.j2
@@ -44,7 +44,16 @@ client-cert-not-required
{% if openvpn_username_pam_auth %}
username-as-common-name
# PAM login
-plugin /etc/openvpn/openvpn-plugin-auth-pam.so login
+plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so login
+{% endif %}
+
+{% if openvpn_ldap_auth %}
+plugin /usr/lib/openvpn/openvpn-auth-ldap.so /etc/openvpn/auth/auth-ldap.conf
+{% endif %}
+
+{% if openvpn_ldap_perl_auth %}
+auth-user-pass-verify /etc/openvpn/auth/auth-ldap via-env
+script-security 3 execve
{% endif %}
max-clients {{ openvpn_max_clients }}
diff --git a/ubuntu-deb-general/tasks/packages.yml b/ubuntu-deb-general/tasks/packages.yml
index dc7ca2c..c34810b 100644
--- a/ubuntu-deb-general/tasks/packages.yml
+++ b/ubuntu-deb-general/tasks/packages.yml
@@ -53,6 +53,12 @@
ignore_errors: True
tags: packages
+- name: setup system apt repository for specific distributions
+ apt_repository: repo='deb http://ppa.research-infrastructures.eu/system {{ ansible_distribution_release }} main' update_cache=yes
+ when: is_trusty
+ ignore_errors: True
+ tags: packages
+
- name: install common packages
apt: pkg={{ item }} state={{ pkg_state }}
with_items: '{{ common_packages }}'