FreeBSD / HardenedBSD (#262)

* FreeBSD draft

ifconfig fix

Pre-tasks fixes

fix hardcoded IP

some refactoring

disable system-based tags

disable freebsd tags

FreeBSD vpn role

add defaults

ssh role freebsd

default fix

dns_adblocking freebsd

ubuntu dict fix

* HardenedBSD

update-users BSD

* Rebuild the kernel

docs changing
pull/282/head
Jack Ivanov 7 years ago committed by GitHub
parent 49ba1f76b4
commit 6facb6cb4f

@ -48,4 +48,4 @@ script:
- ansible-playbook deploy.yml -t local,vpn,dns,ssh_tunneling,security,tests -e "server_ip=$LXC_IP server_user=root IP_subject_alt_name=$LXC_IP local_dns=Y"
after_script:
- ./tests/update-users.sh
- ./tests/update-users.sh

@ -0,0 +1,28 @@
# FreeBSD / HardenedBSD
It is only possible to install Algo on existing systems only. We support only 11 version for now.
## Pre-paring the system
Ensure that the following kernel options are enabled:
```
# sysctl kern.conftxt | grep -iE "IPSEC|crypto"
options IPSEC
options IPSEC_NAT_T
device crypto
```
## Available roles
* vpn
* ssh_tunneling
* dns_adblocking
## Additional variables
* rebuild_kernel - set to `true` if you want to let Algo to rebuild your kernel if needed (Takes a lot of time)
## Installation
`ansible-playbook deploy.yml -t local,vpn -e "server_ip=$server_ip server_user=$server_user IP_subject_alt_name=$server_ip Store_CAKEY=N" --skip-tags cloud`

@ -1,10 +1,16 @@
- name: Install prerequisites
raw: sleep 10 && sudo apt-get update -qq && sudo apt-get install -qq -y python2.7
---
- name: Configure defaults
raw: sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1
tags:
- update-alternatives
- name: Check the system
raw: uname -a
register: OS
- name: Ubuntu pre-tasks
include: ubuntu.yml
when: '"Ubuntu" in OS.stdout'
- name: FreeBSD pre-tasks
include: freebsd.yml
when: '"FreeBSD" in OS.stdout'
- name: Ensure the algo ssh key exist on the server
authorized_key:

@ -0,0 +1,10 @@
---
- set_fact:
config_prefix: "/usr/local/"
root_group: wheel
ssh_service_name: sshd
apparmor_enabled: false
strongswan_additional_plugins:
- kernel-pfroute
- kernel-pfkey

@ -0,0 +1,9 @@
---
- name: FreeBSD / HardenedBSD | Install prerequisites
raw: sleep 10 && env ASSUME_ALWAYS_YES=YES sudo pkg install -y python27
- name: FreeBSD / HardenedBSD | Configure defaults
raw: sudo ln -sf /usr/local/bin/python2.7 /usr/bin/python2.7
- include: facts/FreeBSD.yml

@ -0,0 +1,9 @@
---
- name: Ubuntu | Install prerequisites
raw: sleep 10 && sudo apt-get update -qq && sudo apt-get install -qq -y python2.7
- name: Ubuntu | Configure defaults
raw: sudo update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1
tags:
- update-alternatives

@ -1,8 +1,18 @@
- name: restart rsyslog
service: name=rsyslog state=restarted
- name: restart ipfw
service: name=ipfw state=restarted
- name: flush routing cache
shell: echo 1 > /proc/sys/net/ipv4/route/flush
- name: restart loopback
shell: ifdown lo:100 && ifup lo:100
- name: restart loopback bsd
shell: >
ifconfig lo100 destroy || true &&
ifconfig lo100 create &&
ifconfig lo100 inet {{ local_service_ip }} netmask 255.255.255.255 &&
ifconfig lo100 inet6 FCAA::1/64; echo $?

@ -0,0 +1,51 @@
---
- set_fact:
tools:
- git
- subversion
- screen
- coreutils
- openssl
- bash
- wget
sysctl:
forwarding:
- net.inet.ip.forwarding
- net.inet6.ip6.forwarding
tags:
- always
- name: Loopback included into the rc config
blockinfile:
dest: /etc/rc.conf
create: yes
block: |
cloned_interfaces="lo100"
ifconfig_lo100="inet {{ local_service_ip }} netmask 255.255.255.255"
ifconfig_lo100="inet6 FCAA::1/64"
notify:
- restart loopback bsd
tags:
- always
- name: Enable the gateway features
lineinfile: dest=/etc/rc.conf regexp='^{{ item.param }}.*' line='{{ item.param }}={{ item.value }}'
with_items:
- { param: firewall_enable, value: '"YES"' }
- { param: firewall_type, value: '"open"' }
- { param: gateway_enable, value: '"YES"' }
- { param: natd_enable, value: '"YES"' }
- { param: natd_interface, value: '"{{ ansible_default_ipv4.device|default() }}"' }
- { param: natd_flags, value: '"-dynamic -m"' }
notify:
- restart ipfw
tags:
- always
- name: FreeBSD | Activate IPFW
shell: >
kldstat -n ipfw.ko || kldload ipfw ; sysctl net.inet.ip.fw.enable=0 &&
bash /etc/rc.firewall && sysctl net.inet.ip.fw.enable=1
- meta: flush_handlers

@ -5,101 +5,24 @@
tags:
- always
- name: Install software updates
apt: update_cache=yes upgrade=dist
tags:
- cloud
- name: Check if reboot is required
shell: >
if [[ -e /var/run/reboot-required ]]; then echo "required"; else echo "no"; fi
args:
executable: /bin/bash
register: reboot_required
tags:
- cloud
- name: Reboot
shell: sleep 2 && shutdown -r now "Ansible updates triggered"
async: 1
poll: 0
when: reboot_required is defined and reboot_required.stdout == 'required'
ignore_errors: true
tags:
- cloud
- name: Wait until SSH becomes ready...
local_action:
module: wait_for
port: 22
host: "{{ inventory_hostname }}"
search_regex: OpenSSH
delay: 10
timeout: 320
when: reboot_required is defined and reboot_required.stdout == 'required'
become: false
tags:
- cloud
- include: ubuntu.yml
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
- name: Disable MOTD on login and SSHD
replace: dest="{{ item.file }}" regexp="{{ item.regexp }}" replace="{{ item.line }}"
with_items:
- { regexp: '^session.*optional.*pam_motd.so.*', line: '# MOTD DISABLED', file: '/etc/pam.d/login' }
- { regexp: '^session.*optional.*pam_motd.so.*', line: '# MOTD DISABLED', file: '/etc/pam.d/sshd' }
tags:
- cloud
- include: freebsd.yml
when: ansible_distribution == 'FreeBSD'
- name: Install tools
apt: name="{{ item }}" state=latest
package: name="{{ item }}" state=present
with_items:
- git
- screen
- apparmor-utils
- uuid-runtime
- coreutils
- sendmail
- iptables-persistent
- cgroup-tools
- openssl
tags:
- always
- name: Loopback for services configured
template: src=10-loopback-services.cfg.j2 dest=/etc/network/interfaces.d/10-loopback-services.cfg
notify:
- restart loopback
tags:
- always
- name: Loopback included into the network config
lineinfile: dest=/etc/network/interfaces line='source /etc/network/interfaces.d/10-loopback-services.cfg' state=present
notify:
- restart loopback
tags:
- always
- meta: flush_handlers
- "{{ tools }}"
tags:
- always
- name: Enable packet forwarding for IPv4
sysctl: name="{{ item }}" value=1
with_items:
- net.ipv4.ip_forward
- net.ipv4.conf.all.forwarding
tags:
- always
- name: Enable packet forwarding for IPv6
sysctl: name=net.ipv6.conf.all.forwarding value=1
- "{{ sysctl.forwarding }}"
tags:
- always
- name: Check apparmor support
shell: apparmor_status
ignore_errors: yes
register: apparmor_status
- set_fact:
apparmor_enabled: true
when: '"profiles are in enforce mode" in apparmor_status.stdout'
- meta: flush_handlers

@ -0,0 +1,91 @@
---
- name: Install software updates
apt: update_cache=yes upgrade=dist
tags:
- cloud
- name: Check if reboot is required
shell: >
if [[ -e /var/run/reboot-required ]]; then echo "required"; else echo "no"; fi
args:
executable: /bin/bash
register: reboot_required
tags:
- cloud
- name: Reboot
shell: sleep 2 && shutdown -r now "Ansible updates triggered"
async: 1
poll: 0
when: reboot_required is defined and reboot_required.stdout == 'required'
ignore_errors: true
tags:
- cloud
- name: Wait until SSH becomes ready...
local_action:
module: wait_for
port: 22
host: "{{ inventory_hostname }}"
search_regex: OpenSSH
delay: 10
timeout: 320
when: reboot_required is defined and reboot_required.stdout == 'required'
become: false
tags:
- cloud
- name: Disable MOTD on login and SSHD
replace: dest="{{ item.file }}" regexp="{{ item.regexp }}" replace="{{ item.line }}"
with_items:
- { regexp: '^session.*optional.*pam_motd.so.*', line: '# MOTD DISABLED', file: '/etc/pam.d/login' }
- { regexp: '^session.*optional.*pam_motd.so.*', line: '# MOTD DISABLED', file: '/etc/pam.d/sshd' }
tags:
- cloud
- name: Loopback for services configured
template: src=10-loopback-services.cfg.j2 dest=/etc/network/interfaces.d/10-loopback-services.cfg
notify:
- restart loopback
tags:
- always
- name: Loopback included into the network config
lineinfile: dest=/etc/network/interfaces line='source /etc/network/interfaces.d/10-loopback-services.cfg' state=present
notify:
- restart loopback
tags:
- always
- meta: flush_handlers
tags:
- always
- name: Check apparmor support
shell: apparmor_status
ignore_errors: yes
register: apparmor_status
- set_fact:
apparmor_enabled: true
when: '"profiles are in enforce mode" in apparmor_status.stdout'
- set_fact:
tools:
- git
- screen
- apparmor-utils
- uuid-runtime
- coreutils
- sendmail
- iptables-persistent
- cgroup-tools
- openssl
sysctl:
forwarding:
- net.ipv4.ip_forward
- net.ipv4.conf.all.forwarding
- net.ipv6.conf.all.forwarding
tags:
- always

@ -0,0 +1,4 @@
---
- name: FreeBSD / HardenedBSD | Enable dnsmasq
lineinfile: dest=/etc/rc.conf regexp=^dnsmasq_enable= line='dnsmasq_enable="YES"'

@ -2,55 +2,41 @@
setup:
- name: Dnsmasq installed
apt: name=dnsmasq state=latest
package: name=dnsmasq
- name: Dnsmasq profile for apparmor configured
template: src=usr.sbin.dnsmasq.j2 dest=/etc/apparmor.d/usr.sbin.dnsmasq owner=root group=root mode=0600
when: apparmor_enabled is defined and apparmor_enabled == true
notify:
- restart dnsmasq
- name: Ensure that the dnsmasq user exist
user: name=dnsmasq groups=nogroup append=yes state=present
- name: The dnsmasq directory created
file: dest=/var/lib/dnsmasq state=directory mode=0755 owner=dnsmasq group=nogroup
- name: Enforce the dnsmasq AppArmor policy
shell: aa-enforce usr.sbin.dnsmasq
when: apparmor_enabled is defined and apparmor_enabled == true
tags: ['apparmor']
- name: Ensure that the dnsmasq service directory exist
file: path=/etc/systemd/system/dnsmasq.service.d/ state=directory mode=0755 owner=root group=root
- include: ubuntu.yml
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
- name: Setup the cgroup limitations for the ipsec daemon
template: src=100-CustomLimitations.conf.j2 dest=/etc/systemd/system/dnsmasq.service.d/100-CustomLimitations.conf
notify:
- daemon-reload
- restart dnsmasq
- include: freebsd.yml
when: ansible_distribution == 'FreeBSD'
- meta: flush_handlers
- name: Dnsmasq configured
template: src=dnsmasq.conf.j2 dest=/etc/dnsmasq.conf
template: src=dnsmasq.conf.j2 dest="{{ config_prefix|default('/') }}etc/dnsmasq.conf"
notify:
- restart dnsmasq
- name: Adblock script created
template: src=adblock.sh dest=/opt/adblock.sh owner=root group=root mode=0755
template: src=adblock.sh dest=/usr/local/sbin/adblock.sh owner=root group="{{ root_group|default('root') }}" mode=0755
- name: Adblock script added to cron
cron:
name: Adblock hosts update
minute: 10
hour: 2
job: /opt/adblock.sh
job: /usr/local/sbin/adblock.sh
user: dnsmasq
- name: Update adblock hosts
shell: >
/opt/adblock.sh
become: true
become_user: dnsmasq
sudo -u dnsmasq "/usr/local/sbin/adblock.sh"
- name: Dnsmasq enabled and started
service: name=dnsmasq state=started enabled=yes

@ -0,0 +1,21 @@
---
- name: Ubuntu | Dnsmasq profile for apparmor configured
template: src=usr.sbin.dnsmasq.j2 dest=/etc/apparmor.d/usr.sbin.dnsmasq owner=root group=root mode=0600
when: apparmor_enabled is defined and apparmor_enabled == true
notify:
- restart dnsmasq
- name: Ubuntu | Enforce the dnsmasq AppArmor policy
shell: aa-enforce usr.sbin.dnsmasq
when: apparmor_enabled is defined and apparmor_enabled == true
tags: ['apparmor']
- name: Ubuntu | Ensure that the dnsmasq service directory exist
file: path=/etc/systemd/system/dnsmasq.service.d/ state=directory mode=0755 owner=root group=root
- name: Ubuntu | Setup the cgroup limitations for the ipsec daemon
template: src=100-CustomLimitations.conf.j2 dest=/etc/systemd/system/dnsmasq.service.d/100-CustomLimitations.conf
notify:
- daemon-reload
- restart dnsmasq

@ -1,5 +1,5 @@
- name: restart ssh
service: name=ssh state=restarted
service: name="{{ ssh_service_name|default('ssh') }}" state=restarted
- name: flush routing cache
shell: echo 1 > /proc/sys/net/ipv4/route/flush

@ -1,2 +1,2 @@
- name: restart ssh
service: name=ssh state=restarted
service: name="{{ ssh_service_name|default('ssh') }}" state=restarted

@ -6,7 +6,7 @@
- name: Ensure that the sshd_config file has desired options
blockinfile:
dest: /etc/ssh/sshd_config
marker: '# ANSIBLE_MANAGED_ssh_tunneling_role'
marker: '# {mark} ANSIBLE MANAGED BLOCK ssh_tunneling_role'
block: |
Match Group algo
AllowTcpForwarding local
@ -21,7 +21,7 @@
group: name=algo state=present
- name: Ensure that the jail directory exist
file: path=/var/jail/ state=directory mode=0755 owner=root group=root
file: path=/var/jail/ state=directory mode=0755 owner=root group="{{ root_group|default('root') }}"
- name: Ensure that the SSH users exist
user:

@ -0,0 +1,79 @@
---
- name: Register p12 PayloadContent
local_action: >
shell cat private/{{ item }}.p12 | base64
register: PayloadContent
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
with_items: "{{ users }}"
- name: Set facts for mobileconfigs
set_fact:
proxy_enabled: false
PayloadContentCA: "{{ lookup('file' , 'configs/{{ IP_subject_alt_name }}/pki/cacert.pem')|b64encode }}"
- name: Build the mobileconfigs
local_action:
module: template
src: mobileconfig.j2
dest: configs/{{ IP_subject_alt_name }}/{{ item.0 }}.mobileconfig
mode: 0600
become: no
with_together:
- "{{ users }}"
- "{{ PayloadContent.results }}"
no_log: True
- name: Build the strongswan app android config
local_action:
module: template
src: sswan.j2
dest: configs/{{ IP_subject_alt_name }}/{{ item.0 }}.sswan
mode: 0600
become: no
with_together:
- "{{ users }}"
- "{{ PayloadContent.results }}"
no_log: True
- name: Build the client ipsec config file
local_action:
module: template
src: client_ipsec.conf.j2
dest: configs/{{ IP_subject_alt_name }}/ipsec_{{ item }}.conf
mode: 0600
become: no
with_items:
- "{{ users }}"
- name: Build the client ipsec secret file
local_action:
module: template
src: client_ipsec.secrets.j2
dest: configs/{{ IP_subject_alt_name }}/ipsec_{{ item }}.secrets
mode: 0600
become: no
with_items:
- "{{ users }}"
- name: Build the windows client powershell script
local_action:
module: template
src: client_windows.ps1.j2
dest: configs/{{ IP_subject_alt_name }}/windows_{{ item }}.ps1
mode: 0600
become: no
when: Win10_Enabled is defined and Win10_Enabled == "Y"
with_items: "{{ users }}"
- name: Restrict permissions for the local private directories
local_action:
module: file
path: "{{ item }}"
state: directory
mode: 0700
become: no
with_items:
- configs/{{ IP_subject_alt_name }}

@ -0,0 +1,27 @@
---
- name: Copy the keys to the strongswan directory
copy:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: "{{ item.owner }}"
group: "{{ item.group }}"
mode: "{{ item.mode }}"
with_items:
- src: "configs/{{ IP_subject_alt_name }}/pki/cacert.pem"
dest: "{{ config_prefix|default('/') }}etc/ipsec.d/cacerts/ca.crt"
owner: strongswan
group: "{{ root_group|default('root') }}"
mode: "0600"
- src: "configs/{{ IP_subject_alt_name }}/pki/certs/{{ IP_subject_alt_name }}.crt"
dest: "{{ config_prefix|default('/') }}etc/ipsec.d/certs/{{ IP_subject_alt_name }}.crt"
owner: strongswan
group: "{{ root_group|default('root') }}"
mode: "0600"
- src: "configs/{{ IP_subject_alt_name }}/pki/private/{{ IP_subject_alt_name }}.key"
dest: "{{ config_prefix|default('/') }}etc/ipsec.d/private/{{ IP_subject_alt_name }}.key"
owner: strongswan
group: "{{ root_group|default('root') }}"
mode: "0600"
notify:
- restart strongswan

@ -0,0 +1,113 @@
---
- name: FreeBSD / HardenedBSD | Get the existing kernel parameters
command: sysctl -b kern.conftxt
register: kern_conftxt
when: rebuild_kernel is defined and rebuild_kernel == "true"
- name: FreeBSD / HardenedBSD | Set the rebuild_needed fact
set_fact:
rebuild_needed: true
when: item not in kern_conftxt.stdout and rebuild_kernel is defined and rebuild_kernel == "true"
with_items:
- "IPSEC"
- "IPSEC_NAT_T"
- "crypto"
- name: FreeBSD / HardenedBSD | Make the kernel config
shell: >
sysctl -b kern.conftxt > /tmp/IPSEC
when: rebuild_needed is defined and rebuild_needed == true
- name: FreeBSD / HardenedBSD | Ensure the all options are enabled
lineinfile:
dest: /tmp/IPSEC
line: "{{ item }}"
insertbefore: BOF
with_items:
- "options IPSEC"
- "options IPSEC_NAT_T"
- "device crypto"
when: rebuild_needed is defined and rebuild_needed == true
- name: HardenedBSD | Determine the sources
set_fact:
sources_repo: https://github.com/HardenedBSD/hardenedBSD.git
sources_version: "hardened/{{ ansible_distribution_release.split('.')[0] }}-stable/master"
when: "'Hardened' in ansible_distribution_version"
- name: FreeBSD | Determine the sources
set_fact:
sources_repo: https://github.com/freebsd/freebsd.git
sources_version: "stable/{{ ansible_distribution_major_version }}"
when: "'Hardened' not in ansible_distribution_version"
- name: FreeBSD / HardenedBSD | Increase the git postBuffer size
git_config:
name: http.postBuffer
scope: global
value: 1048576000
- block:
- name: FreeBSD / HardenedBSD | Fetching the sources...
git:
repo: "{{ sources_repo }}"
dest: /usr/krnl_src
version: "{{ sources_version }}"
accept_hostkey: true
async: 1000
poll: 0
register: fetching_sources
- name: FreeBSD / HardenedBSD | Fetching the sources...
async_status: jid={{ fetching_sources.ansible_job_id }}
when: rebuild_needed is defined and rebuild_needed == true
register: result
until: result.finished
retries: 600
delay: 30
rescue:
- debug: var=fetching_sources
- fail:
msg: "Something went wrong. Check the debug output above."
- block:
- name: FreeBSD / HardenedBSD | The kernel is being built...
shell: >
mv /tmp/IPSEC /usr/krnl_src/sys/{{ ansible_architecture }}/conf &&
make buildkernel KERNCONF=IPSEC &&
make installkernel KERNCONF=IPSEC
args:
chdir: /usr/krnl_src
executable: /usr/local/bin/bash
when: rebuild_needed is defined and rebuild_needed == true
async: 1000
poll: 0
register: building_kernel
- name: FreeBSD / HardenedBSD | The kernel is being built...
async_status: jid={{ building_kernel.ansible_job_id }}
when: rebuild_needed is defined and rebuild_needed == true
register: result
until: result.finished
retries: 600
delay: 30
rescue:
- debug: var=building_kernel
- fail:
msg: "Something went wrong. Check the debug output above."
- name: FreeBSD / HardenedBSD | Reboot
shell: >
sleep 2 && shutdown -r now
args:
executable: /usr/local/bin/bash
when: rebuild_needed is defined and rebuild_needed == true
async: 1
poll: 0
ignore_errors: true
- name: FreeBSD / HardenedBSD | Enable strongswan
lineinfile: dest=/etc/rc.conf regexp=^strongswan_enable= line='strongswan_enable="YES"'

@ -0,0 +1,46 @@
---
- name: Setup the config files from our templates
template:
src: "{{ item.src }}"
dest: "{{ item.dest }}"
owner: "{{ item.owner }}"
group: "{{ item.group }}"
mode: "{{ item.mode }}"
with_items:
- src: strongswan.conf.j2
dest: "{{ config_prefix|default('/') }}etc/strongswan.conf"
owner: root
group: "{{ root_group|default('root') }}"
mode: "0644"
- src: ipsec.conf.j2
dest: "{{ config_prefix|default('/') }}etc/ipsec.conf"
owner: root
group: "{{ root_group|default('root') }}"
mode: "0644"
- src: ipsec.secrets.j2
dest: "{{ config_prefix|default('/') }}etc/ipsec.secrets"
owner: strongswan
group: "{{ root_group|default('root') }}"
mode: "0600"
notify:
- restart strongswan
- name: Get loaded plugins
shell: >
find {{ config_prefix|default('/') }}etc/strongswan.d/charon/ -type f -name '*.conf' -exec basename {} \; | cut -f1 -d.
register: strongswan_plugins
- name: Disable unneeded plugins
lineinfile: dest="{{ config_prefix|default('/') }}etc/strongswan.d/charon/{{ item }}.conf" regexp='.*load.*' line='load = no' state=present
notify:
- restart strongswan
when: item not in strongswan_enabled_plugins and item not in strongswan_additional_plugins
with_items: "{{ strongswan_plugins.stdout_lines }}"
- name: Ensure that required plugins are enabled
lineinfile: dest="{{ config_prefix|default('/') }}etc/strongswan.d/charon/{{ item }}.conf" regexp='.*load.*' line='load = yes' state=present
notify:
- restart strongswan
when: item in strongswan_enabled_plugins or item in strongswan_additional_plugins
with_items: "{{ strongswan_plugins.stdout_lines }}"

@ -8,7 +8,7 @@
- name: Generate password for the CA key
shell: >
< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c${1:-12};echo;
openssl rand -hex 6
register: CA_password
- set_fact:
@ -21,304 +21,27 @@
algo_params: "rsa:2048"
when: Win10_Enabled is defined and Win10_Enabled == "Y"
- name: Install StrongSwan
apt: name=strongswan state=latest update_cache=yes install_recommends=yes
- name: Enforcing ipsec with apparmor
shell: aa-enforce "{{ item }}"
when: apparmor_enabled is defined and apparmor_enabled == true
with_items:
- /usr/lib/ipsec/charon
- /usr/lib/ipsec/lookip
- /usr/lib/ipsec/stroke
notify:
- restart apparmor
tags: ['apparmor']
- name: Enable services
service: name={{ item }} enabled=yes
with_items:
- apparmor
- strongswan
- netfilter-persistent
- name: Configure iptables so IPSec traffic can traverse the tunnel
iptables: table=nat chain=POSTROUTING source="{{ vpn_network }}" jump=MASQUERADE
when: (security_enabled is not defined) or
(security_enabled is defined and security_enabled != "y")
notify:
- save iptables
- name: Configure ip6tables so IPSec traffic can traverse the tunnel
iptables: ip_version=ipv6 table=nat chain=POSTROUTING source="{{ vpn_network_ipv6 }}" jump=MASQUERADE
when: ((security_enabled is not defined) or (security_enabled is defined and security_enabled != "y")) and
(ipv6_support is defined and ipv6_support == true)
notify:
- save iptables
- name: Ensure that the strongswan group exist
group: name=strongswan state=present
- name: Ensure that the strongswan user exist
user: name=strongswan group=strongswan state=present
- name: Ensure that the strongswan service directory exist
file: path=/etc/systemd/system/strongswan.service.d/ state=directory mode=0755 owner=root group=root
- name: Setup the cgroup limitations for the ipsec daemon
template: src=100-CustomLimitations.conf.j2 dest=/etc/systemd/system/strongswan.service.d/100-CustomLimitations.conf
notify:
- daemon-reload
- restart strongswan
- meta: flush_handlers
- name: Setup the strongswan.conf file from our template
template: src=strongswan.conf.j2 dest=/etc/strongswan.conf owner=root group=root mode=0644
notify:
- restart strongswan
- name: Setup the ipsec.conf file from our template
template: src=ipsec.conf.j2 dest=/etc/ipsec.conf owner=root group=root mode=0644
notify:
- restart strongswan
- name: Setup the ipsec.secrets file
template: src=ipsec.secrets.j2 dest=/etc/ipsec.secrets owner=strongswan group=root mode=0600
notify:
- restart strongswan
- name: Get loaded plugins
shell: >
find /etc/strongswan.d/charon/ -type f -name '*.conf' -printf '%f\n' | cut -f1 -d.
register: strongswan_plugins
- name: Disable unneeded plugins
lineinfile: dest="/etc/strongswan.d/charon/{{ item }}.conf" regexp='.*load.*' line='load = no' state=present
notify:
- restart strongswan
when: item not in strongswan_enabled_plugins
with_items: "{{ strongswan_plugins.stdout_lines }}"
- name: Ensure that required plugins are enabled
lineinfile: dest="/etc/strongswan.d/charon/{{ item }}.conf" regexp='.*load.*' line='load = yes' state=present
notify:
- restart strongswan
when: item in strongswan_enabled_plugins
with_items: "{{ strongswan_plugins.stdout_lines }}"
- name: Ensure the pki directory is not exist
local_action:
module: file
dest: configs/{{ IP_subject_alt_name }}/pki
state: absent
become: no
when: easyrsa_reinit_existent == True
- name: Ensure the pki directories are exist
local_action:
module: file
dest: "configs/{{ IP_subject_alt_name }}/pki/{{ item }}"
state: directory
recurse: yes
become: no
with_items:
- ecparams
- certs
- crl
- newcerts
- private
- reqs
- name: Ensure the files are exist
local_action:
module: file
dest: "configs/{{ IP_subject_alt_name }}/pki/{{ item }}"
state: touch
become: no
with_items:
- ".rnd"
- "private/.rnd"
- "index.txt"
- "index.txt.attr"
- "serial"
- name: Generate the openssl server configs
local_action:
module: template
src: openssl.cnf.j2
dest: "configs/{{ IP_subject_alt_name }}/pki/openssl.cnf"
become: no
- include: ubuntu.yml
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
- name: Build the CA pair
local_action: >
shell openssl ecparam -name prime256v1 -out ecparams/prime256v1.pem &&
openssl req -utf8 -new -newkey {{ algo_params | default('ec:ecparams/prime256v1.pem') }} -config openssl.cnf -keyout private/cakey.pem -out cacert.pem -x509 -days 3650 -batch -passout pass:"{{ easyrsa_CA_password }}" &&
touch {{ IP_subject_alt_name }}_ca_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: "{{ IP_subject_alt_name }}_ca_generated"
environment:
subjectAltName: "DNS:{{ IP_subject_alt_name }},IP:{{ IP_subject_alt_name }}"
- include: freebsd.yml
when: ansible_distribution == 'FreeBSD'
- name: Copy the CA certificate
local_action:
module: copy
src: "configs/{{ IP_subject_alt_name }}/pki/cacert.pem"
dest: "configs/{{ IP_subject_alt_name }}/cacert.pem"
mode: 0600
become: no
- name: Generate the serial number
local_action: >
shell echo 01 > serial &&
touch serial_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: serial_generated
- name: Build the server pair
local_action: >
shell openssl req -utf8 -new -newkey {{ algo_params | default('ec:ecparams/prime256v1.pem') }} -config openssl.cnf -keyout private/{{ IP_subject_alt_name }}.key -out reqs/{{ IP_subject_alt_name }}.req -nodes -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ IP_subject_alt_name }}" -batch &&
openssl ca -utf8 -in reqs/{{ IP_subject_alt_name }}.req -out certs/{{ IP_subject_alt_name }}.crt -config openssl.cnf -days 3650 -batch -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ IP_subject_alt_name }}" &&
touch certs/{{ IP_subject_alt_name }}_crt_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: certs/{{ IP_subject_alt_name }}_crt_generated
environment:
subjectAltName: "DNS:{{ IP_subject_alt_name }},IP:{{ IP_subject_alt_name }}"
- name: Build the client's pair
local_action: >
shell openssl req -utf8 -new -newkey {{ algo_params | default('ec:ecparams/prime256v1.pem') }} -config openssl.cnf -keyout private/{{ item }}.key -out reqs/{{ item }}.req -nodes -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ item }}" -batch &&
openssl ca -utf8 -in reqs/{{ item }}.req -out certs/{{ item }}.crt -config openssl.cnf -days 3650 -batch -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ item }}" &&
touch certs/{{ item }}_crt_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: certs/{{ item }}_crt_generated
environment:
subjectAltName: "DNS:{{ item }}"
with_items: "{{ users }}"
- name: Build the client's p12
local_action: >
shell openssl pkcs12 -in certs/{{ item }}.crt -inkey private/{{ item }}.key -export -name {{ item }} -out private/{{ item }}.p12 -certfile cacert.pem -passout pass:"{{ easyrsa_p12_export_password }}"
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
with_items: "{{ users }}"
- name: Copy the p12 certificates
local_action:
module: copy
src: "configs/{{ IP_subject_alt_name }}/pki/private/{{ item }}.p12"
dest: "configs/{{ IP_subject_alt_name }}/{{ item }}.p12"
mode: 0600
become: no
with_items:
- "{{ users }}"
- name: Copy the CA cert to the strongswan directory
copy: src='configs/{{ IP_subject_alt_name }}/pki/cacert.pem' dest=/etc/ipsec.d/cacerts/ca.crt owner=strongswan group=root mode=0600
notify:
- restart strongswan
- name: Copy the server cert to the strongswan directory
copy: src='configs/{{ IP_subject_alt_name }}/pki/certs/{{ IP_subject_alt_name }}.crt' dest=/etc/ipsec.d/certs/{{ IP_subject_alt_name }}.crt owner=strongswan group=root mode=0600
notify:
- restart strongswan
- name: Copy the server key to the strongswan directory
copy: src='configs/{{ IP_subject_alt_name }}/pki/private/{{ IP_subject_alt_name }}.key' dest=/etc/ipsec.d/private/{{ IP_subject_alt_name }}.key owner=strongswan group=root mode=0600
notify:
- restart strongswan
- name: Register p12 PayloadContent
local_action: >
shell cat private/{{ item }}.p12 | base64
register: PayloadContent
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
with_items: "{{ users }}"
- name: Set facts for mobileconfigs
set_fact:
proxy_enabled: false
PayloadContentCA: "{{ lookup('file' , 'configs/{{ IP_subject_alt_name }}/pki/cacert.pem')|b64encode }}"
- name: Build the mobileconfigs
local_action:
module: template
src: mobileconfig.j2
dest: configs/{{ IP_subject_alt_name }}/{{ item.0 }}.mobileconfig
mode: 0600
become: no
with_together:
- "{{ users }}"
- "{{ PayloadContent.results }}"
no_log: True
- name: Build the strongswan app android config
local_action:
module: template
src: sswan.j2
dest: configs/{{ IP_subject_alt_name }}/{{ item.0 }}.sswan
mode: 0600
become: no
with_together:
- "{{ users }}"
- "{{ PayloadContent.results }}"
no_log: True
- name: Build the client ipsec config file
local_action:
module: template
src: client_ipsec.conf.j2
dest: configs/{{ IP_subject_alt_name }}/ipsec_{{ item }}.conf
mode: 0600
become: no
with_items:
- "{{ users }}"
- name: Build the client ipsec secret file
local_action:
module: template
src: client_ipsec.secrets.j2
dest: configs/{{ IP_subject_alt_name }}/ipsec_{{ item }}.secrets
mode: 0600
become: no
with_items:
- "{{ users }}"
- name: Build the windows client powershell script
local_action:
module: template
src: client_windows.ps1.j2
dest: configs/{{ IP_subject_alt_name }}/windows_{{ item }}.ps1
mode: 0600
become: no
when: Win10_Enabled is defined and Win10_Enabled == "Y"
with_items: "{{ users }}"
- name: Install StrongSwan
package: name=strongswan state=present
- name: Restrict permissions for the remote private directories
file: path="{{ item }}" state=directory mode=0700 owner=strongswan group=root
with_items:
- /etc/ipsec.d/private
- include: ipec_configuration.yml
- include: openssl.yml
- include: distribute_keys.yml
- include: client_configs.yml
- name: Restrict permissions for the local private directories
local_action:
module: file
path: "{{ item }}"
state: directory
mode: 0700
become: no
with_items:
- configs/{{ IP_subject_alt_name }}
- meta: flush_handlers
- include: iptables.yml
tags: iptables
- name: StrongSwan started
service: name=strongswan state=started

@ -0,0 +1,117 @@
---
- name: Ensure the pki directory is not exist
local_action:
module: file
dest: configs/{{ IP_subject_alt_name }}/pki
state: absent
become: no
when: easyrsa_reinit_existent == True
- name: Ensure the pki directories are exist
local_action:
module: file
dest: "configs/{{ IP_subject_alt_name }}/pki/{{ item }}"
state: directory
recurse: yes
become: no
with_items:
- ecparams
- certs
- crl
- newcerts
- private
- reqs
- name: Ensure the files are exist
local_action:
module: file
dest: "configs/{{ IP_subject_alt_name }}/pki/{{ item }}"
state: touch
become: no
with_items:
- ".rnd"
- "private/.rnd"
- "index.txt"
- "index.txt.attr"
- "serial"
- name: Generate the openssl server configs
local_action:
module: template
src: openssl.cnf.j2
dest: "configs/{{ IP_subject_alt_name }}/pki/openssl.cnf"
become: no
- name: Build the CA pair
local_action: >
shell openssl ecparam -name prime256v1 -out ecparams/prime256v1.pem &&
openssl req -utf8 -new -newkey {{ algo_params | default('ec:ecparams/prime256v1.pem') }} -config openssl.cnf -keyout private/cakey.pem -out cacert.pem -x509 -days 3650 -batch -passout pass:"{{ easyrsa_CA_password }}" &&
touch {{ IP_subject_alt_name }}_ca_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: "{{ IP_subject_alt_name }}_ca_generated"
environment:
subjectAltName: "DNS:{{ IP_subject_alt_name }},IP:{{ IP_subject_alt_name }}"
- name: Copy the CA certificate
local_action:
module: copy
src: "configs/{{ IP_subject_alt_name }}/pki/cacert.pem"
dest: "configs/{{ IP_subject_alt_name }}/cacert.pem"
mode: 0600
become: no
- name: Generate the serial number
local_action: >
shell echo 01 > serial &&
touch serial_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: serial_generated
- name: Build the server pair
local_action: >
shell openssl req -utf8 -new -newkey {{ algo_params | default('ec:ecparams/prime256v1.pem') }} -config openssl.cnf -keyout private/{{ IP_subject_alt_name }}.key -out reqs/{{ IP_subject_alt_name }}.req -nodes -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ IP_subject_alt_name }}" -batch &&
openssl ca -utf8 -in reqs/{{ IP_subject_alt_name }}.req -out certs/{{ IP_subject_alt_name }}.crt -config openssl.cnf -days 3650 -batch -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ IP_subject_alt_name }}" &&
touch certs/{{ IP_subject_alt_name }}_crt_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: certs/{{ IP_subject_alt_name }}_crt_generated
environment:
subjectAltName: "DNS:{{ IP_subject_alt_name }},IP:{{ IP_subject_alt_name }}"
- name: Build the client's pair
local_action: >
shell openssl req -utf8 -new -newkey {{ algo_params | default('ec:ecparams/prime256v1.pem') }} -config openssl.cnf -keyout private/{{ item }}.key -out reqs/{{ item }}.req -nodes -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ item }}" -batch &&
openssl ca -utf8 -in reqs/{{ item }}.req -out certs/{{ item }}.crt -config openssl.cnf -days 3650 -batch -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ item }}" &&
touch certs/{{ item }}_crt_generated
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
creates: certs/{{ item }}_crt_generated
environment:
subjectAltName: "DNS:{{ item }}"
with_items: "{{ users }}"
- name: Build the client's p12
local_action: >
shell openssl pkcs12 -in certs/{{ item }}.crt -inkey private/{{ item }}.key -export -name {{ item }} -out private/{{ item }}.p12 -certfile cacert.pem -passout pass:"{{ easyrsa_p12_export_password }}"
become: no
args:
chdir: "configs/{{ IP_subject_alt_name }}/pki/"
with_items: "{{ users }}"
- name: Copy the p12 certificates
local_action:
module: copy
src: "configs/{{ IP_subject_alt_name }}/pki/private/{{ item }}.p12"
dest: "configs/{{ IP_subject_alt_name }}/{{ item }}.p12"
mode: 0600
become: no
with_items:
- "{{ users }}"

@ -0,0 +1,52 @@
---
- set_fact:
strongswan_additional_plugins: []
- name: Ubuntu | Install StrongSwan
apt: name=strongswan state=latest update_cache=yes install_recommends=yes
- name: Ubuntu | Enforcing ipsec with apparmor
shell: aa-enforce "{{ item }}"
when: apparmor_enabled is defined and apparmor_enabled == true
with_items:
- /usr/lib/ipsec/charon
- /usr/lib/ipsec/lookip
- /usr/lib/ipsec/stroke
notify:
- restart apparmor
tags: ['apparmor']
- name: Ubuntu | Enable services
service: name={{ item }} enabled=yes
with_items:
- apparmor
- strongswan
- netfilter-persistent
- name: Ubuntu | Configure iptables so IPSec traffic can traverse the tunnel
iptables: table=nat chain=POSTROUTING source="{{ vpn_network }}" jump=MASQUERADE
when: (security_enabled is not defined) or
(security_enabled is defined and security_enabled != "y")
notify:
- save iptables
- name: Ubuntu | Configure ip6tables so IPSec traffic can traverse the tunnel
iptables: ip_version=ipv6 table=nat chain=POSTROUTING source="{{ vpn_network_ipv6 }}" jump=MASQUERADE
when: ((security_enabled is not defined) or
(security_enabled is defined and security_enabled != "y")) and
ipv6_support is defined and ipv6_support == "yes"
notify:
- save iptables
- name: Ubuntu | Ensure that the strongswan service directory exist
file: path=/etc/systemd/system/strongswan.service.d/ state=directory mode=0755 owner=root group=root
- name: Ubuntu | Setup the cgroup limitations for the ipsec daemon
template: src=100-CustomLimitations.conf.j2 dest=/etc/systemd/system/strongswan.service.d/100-CustomLimitations.conf
notify:
- daemon-reload
- restart strongswan
- include: iptables.yml
tags: iptables

@ -11,6 +11,16 @@ charon {
}
user = strongswan
group = strongswan
filelog {
/var/log/charon.log {
time_format = %b %e %T
ike_name = yes
append = no
default = 1
flush_line = yes
}
}
}
include strongswan.d/*.conf

@ -36,6 +36,9 @@
- config.cfg
pre_tasks:
- name: Common pre-tasks
include: playbooks/common.yml
- set_fact:
IP_subject_alt_name: "{{ IP_subject }}"
easyrsa_p12_export_password: "{{ p12_export_password|default((ansible_date_time.iso8601_basic|sha1|to_uuid).split('-')[0]) }}"
@ -117,7 +120,7 @@
- name: Copy the revoked certificates to the vpn server
copy:
src: configs/{{ IP_subject_alt_name }}/pki/crl/{{ item }}.crt
dest: /etc/ipsec.d/crls/{{ item }}.crt
dest: "{{ config_prefix|default('/') }}etc/ipsec.d/crls/{{ item }}.crt"
when: item not in users
with_items: "{{ valid_certs.stdout_lines }}"
notify:

Loading…
Cancel
Save