diff --git a/defaults/main.yml b/defaults/main.yml
index 71590e4..f47c75d 100644
--- a/defaults/main.yml
+++ b/defaults/main.yml
@@ -172,6 +172,11 @@ openvpn_ccd_configs: []
# The above will create a file named `client` under the ccd folder containing
# the `ifconfig-push` directive. This will be applied to the `client` when it
# connects to the openvpn server.
+
+# Use a custom template for client configuration. In that case, you have to
+# take care of which of the above variables will actually have an effect on
+# the client config.
+openvpn_client_conf_template: client.conf.j2
# }}}
# Authentication {{{
# Use PAM authentication
@@ -199,13 +204,14 @@ openvpn_tls_key: "ta.key"
# Scripting {{{
# A list of directories that the role should create and that should be
# accessible by the OpenVPN server to write into after it has dropped
-# privileges. The OpenVPN server should run with limited privileges, eg with
+# privileges. The OpenVPN server should run with limited privileges, e.g. with
# `openvpn_user` set to `nobody`. Such a user will not be able to access many
# files and directories in the file system. This means that if you want one of
-# your scripts to write to some file, that file will need to be writable by the
-# OpenVPN server. The directories included in this variable will be created by
-# the role and your scripts will be able to create and write to files inside
-# them. Eg, `/var/log/openvpn-script-out/`
+# your scripts to write to some file (e.g. under `/var/log`), that file will
+# need to be writable by the OpenVPN server. The directories included in this
+# variable will be created by the role with permissions that will allow the
+# OpenVPN server to write into them, thus your scripts will be able to create
+# and write to files inside them. Example: [`/var/log/openvpn-script-out/`].
openvpn_script_output_directories: []
# A path on the OpenVPN server where OpenVPN scripts should be uploaded to.
diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml
index e8d8515..a7c008b 100644
--- a/molecule/default/converge.yml
+++ b/molecule/default/converge.yml
@@ -21,6 +21,19 @@
- name: client2
content: '# pass'
+ ## scripting
+ openvpn_script_output_directories:
+ - /var/log/openvpn-script-out/
+
+ openvpn_script_files:
+ - scripts/client-disconnect.sh.j2
+
+ openvpn_inline_scripts:
+ - name: my-up-script.sh
+ content: |
+ #!/usr/bin/env
+ echo 'Up!' >> "/var/up.log"
+
# Enabled them
openvpn_download_clients: false
openvpn_open_firewall: false
diff --git a/tasks/core/clients.yml b/tasks/core/clients.yml
index 49eb49c..9252178 100644
--- a/tasks/core/clients.yml
+++ b/tasks/core/clients.yml
@@ -1,13 +1,8 @@
---
-- name: Create client configuration directory
- file:
- path: "{{ openvpn_etcdir }}/ovpns"
- state: directory
-
- name: Generate client configurations
template:
- src: client.conf.j2
+ src: "{{ openvpn_client_conf_template }}"
dest: "{{ openvpn_etcdir }}/ovpns/{{ item }}.ovpn"
loop: "{{ openvpn_clients }}"
register: openvpn_clients_changed
diff --git a/tasks/core/configure.yml b/tasks/core/configure.yml
index 40a6f75..6916598 100644
--- a/tasks/core/configure.yml
+++ b/tasks/core/configure.yml
@@ -17,3 +17,11 @@
src: server.conf.j2
dest: "{{ openvpn_etcdir }}/server.conf"
notify: openvpn restart
+
+# Needed by both tls-authentication tasks and client-configuration tasks. Placed
+# here to avoid repeating it twice in both places where the tls and
+# client-config tasks are located.
+- name: Create client configuration directory
+ file:
+ path: "{{ openvpn_etcdir }}/ovpns"
+ state: directory
diff --git a/tasks/openvpn.yml b/tasks/openvpn.yml
index e008ef4..13df633 100644
--- a/tasks/openvpn.yml
+++ b/tasks/openvpn.yml
@@ -28,8 +28,6 @@
- include_tasks: core/read-client-files.yml
when: openvpn_unified_client_profiles
-- import_tasks: core/clients.yml
-
- include_tasks: authentication/ldap.yml
- include_tasks: authentication/pam.yml
@@ -38,6 +36,8 @@
- include_tasks: authentication/tls.yml
+- import_tasks: core/clients.yml
+
- include_tasks: scripts.yml
- include_tasks: "system/bridge/{{ ansible_os_family }}.yml"
diff --git a/tasks/scripts.yml b/tasks/scripts.yml
index bb71ed5..9244054 100644
--- a/tasks/scripts.yml
+++ b/tasks/scripts.yml
@@ -16,14 +16,14 @@
- name: Upload script files
template:
src: "{{ item }}"
- dest: "{{ openvpn_scripts_dir }}{{ item | basename | replace('.j2', '') }}"
+ dest: "{{ openvpn_scripts_dir }}/{{ item | basename | replace('.j2', '') }}"
owner: "{{ openvpn_user }}"
group: "{{ openvpn_group }}"
mode: 0o744
loop: "{{ openvpn_script_files }}"
- name: Upload inline scripts
- template:
+ copy:
content: "{{ item.content }}"
dest: "{{ openvpn_scripts_dir }}/{{ item.name }}"
owner: "{{ openvpn_user }}"
diff --git a/tasks/system/bridge/Debian.yml b/tasks/system/bridge/Debian.yml
index 8538bf5..cca7ee3 100644
--- a/tasks/system/bridge/Debian.yml
+++ b/tasks/system/bridge/Debian.yml
@@ -4,11 +4,13 @@
template:
src: bridge/bridge-interface.deb.j2
dest: "/etc/network/interfaces.d/{{ openvpn_dev }}"
- when: openvpn_bridge | bool
+ when:
+ - openvpn_bridge is defined
+ - openvpn_bridge | length > 0
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
+ when: openvpn_bridge is not defined or openvpn_bridge | length == 0
diff --git a/tasks/system/bridge/RedHat.yml b/tasks/system/bridge/RedHat.yml
index 05e540a..8444889 100644
--- a/tasks/system/bridge/RedHat.yml
+++ b/tasks/system/bridge/RedHat.yml
@@ -1,28 +1,29 @@
---
-- name: Setup up script
- when: openvpn_bridge | bool
- template:
- src: bridge/up.sh.j2
- dest: "{{ openvpn_scripts_dir }}/up.sh"
- mode: o+x
+- block:
+ - name: Setup up script
+ template:
+ src: bridge/up.sh.j2
+ dest: "{{ openvpn_scripts_dir }}/up.sh"
+ mode: o+x
-- name: Setup down script
- template:
- src: bridge/down.sh.j2
- dest: "{{ openvpn_scripts_dir }}/down.sh"
- mode: o+x
- when: openvpn_bridge | bool
+ - name: Setup down script
+ template:
+ src: bridge/down.sh.j2
+ dest: "{{ openvpn_scripts_dir }}/down.sh"
+ mode: o+x
-- name: Setup bridge
- template:
- src: bridge/bridge-interface.rh.j2
- dest: "/etc/sysconfig/network-scripts/ifcfg-br-{{ openvpn_dev }}"
- when: openvpn_bridge | bool
+ - name: Setup bridge
+ template:
+ src: bridge/bridge-interface.rh.j2
+ dest: "/etc/sysconfig/network-scripts/ifcfg-br-{{ openvpn_dev }}"
+ when:
+ - openvpn_bridge is defined
+ - openvpn_bridge | length > 0
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
+ when: openvpn_bridge is not defined or openvpn_bridge | length == 0
diff --git a/meta/beats/elasticsearch-ingest-pipelines.yml b/templates/beats/elasticsearch.openvpn.ingest.pipelines.yml
similarity index 81%
rename from meta/beats/elasticsearch-ingest-pipelines.yml
rename to templates/beats/elasticsearch.openvpn.ingest.pipelines.yml
index 2d12930..479ca04 100644
--- a/meta/beats/elasticsearch-ingest-pipelines.yml
+++ b/templates/beats/elasticsearch.openvpn.ingest.pipelines.yml
@@ -15,6 +15,8 @@ pipelines:
pattern_definitions:
TIMESTAMP: "%{DAY} %{MONTH} ?%{MONTHDAY} %{TIME} %{YEAR}"
- gsub:
+ # the month day is ' 3' or '24'. The space before '3' will break the
+ # date filter that follows, so removing it.
field: "openvpn.date"
pattern: " "
replacement: ' '
@@ -25,6 +27,7 @@ pipelines:
- set:
field: 'openvpn.event'
value: 'client-connected'
+
# Parses log lines created with the
# `templates/etc/openvpn/scripts/client-disconnect.sh` script.
- description: "openvpn-disconnection-log-line"
@@ -36,7 +39,9 @@ pipelines:
field: message
ignore_failure: true
patterns:
- - "%{DATESTAMP_OTHER:openvpn.date},%{DATA:openvpn.common_name},%{IP:openvpn.client_ip}"
+ - "%{TIMESTAMP:openvpn.date},%{DATA:openvpn.common_name},%{IP:openvpn.client_ip}"
+ pattern_definitions:
+ TIMESTAMP: "%{DAY} %{MONTH} ?%{MONTHDAY} %{TIME} %{TZ} %{YEAR}"
- gsub:
field: "openvpn.date"
pattern: " "
diff --git a/templates/beats/filebeat.openvpn.fields.yml b/templates/beats/filebeat.openvpn.fields.yml
new file mode 100644
index 0000000..2e4b334
--- /dev/null
+++ b/templates/beats/filebeat.openvpn.fields.yml
@@ -0,0 +1,12 @@
+---
+
+- name: openvpn.date
+ type: date
+- name: openvpn.client_ip
+ type: ip
+- name: openvpn.common_name
+ type: keyword
+- name: openvpn.event
+ type: keyword
+- name: openvpn.port
+ type: long
diff --git a/meta/beats/filebeat-inputs.yml b/templates/beats/filebeat.openvpn.inputs.yml
similarity index 100%
rename from meta/beats/filebeat-inputs.yml
rename to templates/beats/filebeat.openvpn.inputs.yml
diff --git a/meta/beats/heartbeat-monitors.yml b/templates/beats/heartbeat.openvpn.monitors.yml
similarity index 100%
rename from meta/beats/heartbeat-monitors.yml
rename to templates/beats/heartbeat.openvpn.monitors.yml
diff --git a/templates/client.conf.j2 b/templates/client.conf.j2
index 7286eb4..0637f88 100644
--- a/templates/client.conf.j2
+++ b/templates/client.conf.j2
@@ -20,7 +20,7 @@ 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}}
+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
@@ -42,10 +42,10 @@ persist-tun
{{ openvpn_ca_file_contents }}
-{{ openvpn_client_cert_output |default([{'item':client,'stdout':''}])|selectattr('item', 'match', client)|map(attribute='stdout')|list|first }}
+{{ 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 }}
+{{ openvpn_client_keys_output | default([{'item':client,'stdout':''}]) | selectattr('item', 'match', client) | map(attribute='stdout') | list | first }}
{% if openvpn_tls_auth %}
key-direction 1
@@ -56,23 +56,24 @@ key-direction 1
{% else %}
ca ca.crt
-cert {{client}}.crt
-key {{client}}.key
+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 avoid a possible Man-in-the-Middle attack where an authorized client tries
+# to connect to another client by impersonating the server, make sure to enforce
+# some kind of server certificate verification by clients.
+# 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 (Deprecated by 'remote-cert-tls' since OpenVPN 2.1)
+# the nsCertType field set to "server". The build-key-server script in the easy-rsa
+# folder will do this. See https://openvpn.net/community-resources/rsa-key-management/
remote-cert-tls server
{% if openvpn_tls_auth and not openvpn_unified_client_profiles -%}
# Use a static pre-shared key (PSK)
-tls-auth {{openvpn_tls_key}} 1
+tls-auth {{ openvpn_tls_key }} 1
{% endif %}
# Enable compression on the VPN link. Don't enable this unless it is also
@@ -84,12 +85,12 @@ comp-lzo
{% endif %}
# Set log file verbosity.
-verb {{openvpn_verb}}
+verb {{ openvpn_verb }}
{% if openvpn_use_pam or openvpn_use_ldap %}
auth-user-pass
{% endif %}
{% for option in openvpn_client_options %}
-{{option}}
+{{ option }}
{% endfor %}
diff --git a/templates/scripts/client-disconnect.sh.j2 b/templates/scripts/client-disconnect.sh.j2
index e7bc837..99199b1 100644
--- a/templates/scripts/client-disconnect.sh.j2
+++ b/templates/scripts/client-disconnect.sh.j2
@@ -3,6 +3,11 @@
{# openvpn_client_disconnect_log is a variable specific to this file and is not
mentioned in $(defaults/main.yml) #}
+{%- if openvpn_client_disconnect_log is not defined -%}
+ {% set openvpn_client_disconnect_log = "{{
+ openvpn_script_output_directories[0] }}/disconnect.log" %}
+{%- endif -%}
+
if [[ ! -e "{{ openvpn_client_disconnect_log }}" ]]; then
echo 'time,common_name,external_ip' >"{{ openvpn_client_disconnect_log }}"
fi