diff --git a/.travis.yml b/.travis.yml index 904dbdbee..7b2a394ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 \ No newline at end of file + - ./tests/update-users.sh diff --git a/docs/FreeBSD.md b/docs/FreeBSD.md new file mode 100644 index 000000000..e082809f6 --- /dev/null +++ b/docs/FreeBSD.md @@ -0,0 +1,24 @@ +# FreeBSD + +It is only possible to install Algo on existing systems only in order to avoid recompiling the kernel while deploying + +## 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 + +## 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` diff --git a/playbooks/common.yml b/playbooks/common.yml index c195b13d3..3dce63848 100644 --- a/playbooks/common.yml +++ b/playbooks/common.yml @@ -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: diff --git a/playbooks/facts/FreeBSD.yml b/playbooks/facts/FreeBSD.yml new file mode 100644 index 000000000..0d025fc07 --- /dev/null +++ b/playbooks/facts/FreeBSD.yml @@ -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 diff --git a/playbooks/freebsd.yml b/playbooks/freebsd.yml new file mode 100644 index 000000000..00d77b1ae --- /dev/null +++ b/playbooks/freebsd.yml @@ -0,0 +1,9 @@ +--- + +- name: FreeBSD | Install prerequisites + raw: sleep 10 && sudo pkg install -y python27 + +- name: FreeBSD | Configure defaults + raw: sudo ln -sf /usr/local/bin/python2.7 /usr/bin/python2.7 + +- include: facts/FreeBSD.yml diff --git a/playbooks/ubuntu.yml b/playbooks/ubuntu.yml new file mode 100644 index 000000000..d67cbde4d --- /dev/null +++ b/playbooks/ubuntu.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 diff --git a/roles/common/handlers/main.yml b/roles/common/handlers/main.yml index c22968503..2272403cb 100644 --- a/roles/common/handlers/main.yml +++ b/roles/common/handlers/main.yml @@ -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 $? diff --git a/roles/common/tasks/freebsd.yml b/roles/common/tasks/freebsd.yml new file mode 100644 index 000000000..b82d19215 --- /dev/null +++ b/roles/common/tasks/freebsd.yml @@ -0,0 +1,50 @@ +--- + +- set_fact: + tools: + - git + - 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 diff --git a/roles/common/tasks/main.yml b/roles/common/tasks/main.yml index 1262d3fcd..d8f6ec3ea 100644 --- a/roles/common/tasks/main.yml +++ b/roles/common/tasks/main.yml @@ -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 diff --git a/roles/common/tasks/ubuntu.yml b/roles/common/tasks/ubuntu.yml new file mode 100644 index 000000000..e83099390 --- /dev/null +++ b/roles/common/tasks/ubuntu.yml @@ -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 diff --git a/roles/dns_adblocking/tasks/freebsd.yml b/roles/dns_adblocking/tasks/freebsd.yml new file mode 100644 index 000000000..1e32f2bc3 --- /dev/null +++ b/roles/dns_adblocking/tasks/freebsd.yml @@ -0,0 +1,4 @@ +--- + +- name: FreeBSD | Enable dnsmasq + lineinfile: dest=/etc/rc.conf regexp=^dnsmasq_enable= line='dnsmasq_enable="YES"' diff --git a/roles/dns_adblocking/tasks/main.yml b/roles/dns_adblocking/tasks/main.yml index bf5893194..90a86ee3a 100644 --- a/roles/dns_adblocking/tasks/main.yml +++ b/roles/dns_adblocking/tasks/main.yml @@ -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 - diff --git a/roles/dns_adblocking/tasks/ubuntu.yml b/roles/dns_adblocking/tasks/ubuntu.yml new file mode 100644 index 000000000..f0ffb9159 --- /dev/null +++ b/roles/dns_adblocking/tasks/ubuntu.yml @@ -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 diff --git a/roles/security/handlers/main.yml b/roles/security/handlers/main.yml index e6d614b7b..ab98db635 100644 --- a/roles/security/handlers/main.yml +++ b/roles/security/handlers/main.yml @@ -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 diff --git a/roles/ssh_tunneling/handlers/main.yml b/roles/ssh_tunneling/handlers/main.yml index 276ebfe6d..066d9600c 100644 --- a/roles/ssh_tunneling/handlers/main.yml +++ b/roles/ssh_tunneling/handlers/main.yml @@ -1,2 +1,2 @@ - name: restart ssh - service: name=ssh state=restarted + service: name="{{ ssh_service_name|default('ssh') }}" state=restarted diff --git a/roles/ssh_tunneling/tasks/main.yml b/roles/ssh_tunneling/tasks/main.yml index 9ade7e339..98f2872e9 100644 --- a/roles/ssh_tunneling/tasks/main.yml +++ b/roles/ssh_tunneling/tasks/main.yml @@ -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: diff --git a/roles/vpn/tasks/client_configs.yml b/roles/vpn/tasks/client_configs.yml new file mode 100644 index 000000000..76f5a05ae --- /dev/null +++ b/roles/vpn/tasks/client_configs.yml @@ -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 }} diff --git a/roles/vpn/tasks/distribute_keys.yml b/roles/vpn/tasks/distribute_keys.yml new file mode 100644 index 000000000..d50ecfa4b --- /dev/null +++ b/roles/vpn/tasks/distribute_keys.yml @@ -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 diff --git a/roles/vpn/tasks/freebsd.yml b/roles/vpn/tasks/freebsd.yml new file mode 100644 index 000000000..396194310 --- /dev/null +++ b/roles/vpn/tasks/freebsd.yml @@ -0,0 +1,4 @@ +--- + +- name: FreeBSD | Enable strongswan + lineinfile: dest=/etc/rc.conf regexp=^strongswan_enable= line='strongswan_enable="YES"' diff --git a/roles/vpn/tasks/ipec_configuration.yml b/roles/vpn/tasks/ipec_configuration.yml new file mode 100644 index 000000000..a6b1530ba --- /dev/null +++ b/roles/vpn/tasks/ipec_configuration.yml @@ -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 }}" diff --git a/roles/vpn/tasks/main.yml b/roles/vpn/tasks/main.yml index f98b4c6b4..5cbca5429 100644 --- a/roles/vpn/tasks/main.yml +++ b/roles/vpn/tasks/main.yml @@ -3,7 +3,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: @@ -16,304 +16,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 diff --git a/roles/vpn/tasks/openssl.yml b/roles/vpn/tasks/openssl.yml new file mode 100644 index 000000000..8f9d52aba --- /dev/null +++ b/roles/vpn/tasks/openssl.yml @@ -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 }}" diff --git a/roles/vpn/tasks/ubuntu.yml b/roles/vpn/tasks/ubuntu.yml new file mode 100644 index 000000000..d00896f35 --- /dev/null +++ b/roles/vpn/tasks/ubuntu.yml @@ -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 diff --git a/roles/vpn/templates/strongswan.conf.j2 b/roles/vpn/templates/strongswan.conf.j2 index 4eab82fdf..5e66cb2e5 100644 --- a/roles/vpn/templates/strongswan.conf.j2 +++ b/roles/vpn/templates/strongswan.conf.j2 @@ -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