From 2a3b821f018e8182f283bd39bbaae4a06395ea5d Mon Sep 17 00:00:00 2001 From: Jack Ivanov Date: Fri, 3 Jan 2020 15:20:17 +0100 Subject: [PATCH] X.509 Name Constraints --- config.cfg | 1 + roles/strongswan/defaults/main.yml | 3 ++- roles/strongswan/tasks/openssl.yml | 26 +++++++++++++++++++++- roles/strongswan/templates/charon.conf.j2 | 2 +- roles/strongswan/templates/mobileconfig.j2 | 2 +- roles/strongswan/templates/openssl.cnf.j2 | 5 ++--- tests/ipsec-client.sh | 10 +++++++++ 7 files changed, 42 insertions(+), 7 deletions(-) diff --git a/config.cfg b/config.cfg index 547b364..b935ef1 100644 --- a/config.cfg +++ b/config.cfg @@ -4,6 +4,7 @@ # Every device must have a unique username. # You can generate up to 250 users at one time. # Usernames with leading 0's or containing only numbers should be escaped in double quotes, e.g. "000dan" or "123". +# Emails are not allowed users: - phone - laptop diff --git a/roles/strongswan/defaults/main.yml b/roles/strongswan/defaults/main.yml index a9d71a2..bb1a1c6 100644 --- a/roles/strongswan/defaults/main.yml +++ b/roles/strongswan/defaults/main.yml @@ -11,8 +11,9 @@ algo_dns_adblocking: false ipv6_support: false dns_encryption: true domain: false +openssl_user_domain: algo.vpn subjectAltName_IP: "{{ 'DNS:' if IP_subject_alt_name|regex_search('[a-z]') else 'IP:' }}{{ IP_subject_alt_name }}" -subjectAltName_USER: "{% if '@' in item %}email:{{ item }}{% else %}DNS:{{ item }}{% endif %}" +subjectAltName_USER: "email:{{ item }}@{{ openssl_user_domain }}" openssl_bin: openssl strongswan_enabled_plugins: - aes diff --git a/roles/strongswan/tasks/openssl.yml b/roles/strongswan/tasks/openssl.yml index feb627f..f12c4a0 100644 --- a/roles/strongswan/tasks/openssl.yml +++ b/roles/strongswan/tasks/openssl.yml @@ -132,6 +132,30 @@ executable: bash with_items: "{{ users }}" + - name: Build the tests pair + shell: > + umask 077; + {{ openssl_bin }} req -utf8 -new + -newkey ec:ecparams/secp384r1.pem + -config <(cat openssl.cnf <(printf "[basic_exts]\nsubjectAltName=DNS:google-algo-test-pair.com")) + -keyout private/google-algo-test-pair.com.key + -out reqs/google-algo-test-pair.com.req -nodes + -passin pass:"{{ CA_password }}" + -subj "/CN=google-algo-test-pair.com" -batch && + {{ openssl_bin }} ca -utf8 + -in reqs/google-algo-test-pair.com.req + -out certs/google-algo-test-pair.com.crt + -config <(cat openssl.cnf <(printf "[basic_exts]\nsubjectAltName=DNS:google-algo-test-pair.com")) + -days 3650 -batch + -passin pass:"{{ CA_password }}" + -subj "/CN=google-algo-test-pair.com" && + touch certs/google-algo-test-pair.com_crt_generated + args: + chdir: "{{ ipsec_pki_path }}" + creates: certs/google-algo-test-pair.com_crt_generated + executable: bash + when: tests|default(false)|bool + - name: Build openssh public keys openssl_publickey: path: "{{ ipsec_pki_path }}/public/{{ item }}.pub" @@ -201,7 +225,7 @@ chdir: "{{ ipsec_pki_path }}" creates: crl/{{ item }}.crt executable: bash - when: item not in users + when: item.split('@')[0] not in users with_items: "{{ valid_certs.stdout_lines }}" - name: Genereate new CRL file diff --git a/roles/strongswan/templates/charon.conf.j2 b/roles/strongswan/templates/charon.conf.j2 index 303c92f..e8aab6a 100644 --- a/roles/strongswan/templates/charon.conf.j2 +++ b/roles/strongswan/templates/charon.conf.j2 @@ -358,7 +358,7 @@ charon { x509 { # Discard certificates with unsupported or unknown critical extensions. - # enforce_critical = yes + enforce_critical = no } diff --git a/roles/strongswan/templates/mobileconfig.j2 b/roles/strongswan/templates/mobileconfig.j2 index 807683f..98e55bf 100644 --- a/roles/strongswan/templates/mobileconfig.j2 +++ b/roles/strongswan/templates/mobileconfig.j2 @@ -93,7 +93,7 @@ 1440 LocalIdentifier - {{ item.0 }} + {{ item.0 }}@{{ openssl_user_domain }} PayloadCertificateUUID {{ pkcs12_PayloadCertificateUUID }} CertificateType diff --git a/roles/strongswan/templates/openssl.cnf.j2 b/roles/strongswan/templates/openssl.cnf.j2 index d4cff0c..1fe4322 100644 --- a/roles/strongswan/templates/openssl.cnf.j2 +++ b/roles/strongswan/templates/openssl.cnf.j2 @@ -119,9 +119,8 @@ keyUsage = digitalSignature, keyEncipherment subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer:always -# This could be marked critical, but it's nice to support reading by any -# broken clients who attempt to do so. -basicConstraints = CA:true +basicConstraints = critical,CA:true,pathlen:0 +nameConstraints = critical,permitted;{{ subjectAltName_IP }}/255.255.255.255{{ ',permitted;IP:' + ansible_default_ipv6['address'] + '/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' if ipv6_support else '' }}{{ ',permitted;DNS:' + subjectAltName_DNS if domain and subjectAltName_DNS else '' }},permitted;DNS:algo.local,permitted;email:{{ openssl_user_domain }} # Limit key usage to CA tasks. If you really want to use the generated pair as # a self-signed cert, comment this out. diff --git a/tests/ipsec-client.sh b/tests/ipsec-client.sh index c64ca53..b5d4958 100755 --- a/tests/ipsec-client.sh +++ b/tests/ipsec-client.sh @@ -4,6 +4,16 @@ set -euxo pipefail xmllint --noout ./configs/10.0.8.100/ipsec/apple/user1.mobileconfig +CA_CONSTRAINTS="$(openssl verify -verbose \ + -CAfile ./configs/10.0.8.100/ipsec/.pki/cacert.pem \ + ./configs/10.0.8.100/ipsec/.pki/certs/google-algo-test-pair.com.crt 2>&1)" || true + +echo "$CA_CONSTRAINTS" | grep "permitted subtree violation" >/dev/null && \ + echo "Name Constraints test passed" || \ + (echo "Name Constraints test failed" && exit 1) + +echo "$CA_CONSTRAINTS" + ansible-playbook deploy_client.yml \ -e client_ip=localhost \ -e vpn_user=desktop \