diff --git a/.travis.yml b/.travis.yml index 76d8bb2..904dbdb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,4 +45,7 @@ install: script: - ansible-playbook deploy.yml --syntax-check - - ansible-playbook deploy.yml -t local,vpn,dns,ssh_tunneling,security -e "server_ip=$LXC_IP server_user=root IP_subject_alt_name=$LXC_IP local_dns=Y" + - 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 diff --git a/algo b/algo index 26fbbd2..d001a6e 100755 --- a/algo +++ b/algo @@ -50,6 +50,12 @@ Do you want the VPN to support Windows 10 clients? (requires RSA certificates an Win10_Enabled=${Win10_Enabled:-n} if [[ "$Win10_Enabled" =~ ^(y|Y)$ ]]; then EXTRA_VARS+=" Win10_Enabled=Y"; fi +read -p " +Do you want to store the CA key? (required for update-users script, but less secure) +[y/N]: " -r Store_CAKEY +Store_CAKEY=${Store_CAKEY:-N} +if [[ "$Store_CAKEY" =~ ^(n|N)$ ]]; then EXTRA_VARS+=" Store_CAKEY=N"; fi + } deploy () { @@ -332,7 +338,31 @@ Enter the number of your desired provider } user_management () { - ansible-playbook users.yml + + read -p " +Enter IP address of your server: (use localhost for local installation) +: " -r server_ip + + read -p " +What user should we use to login on the server? (ignore if you're deploying to localhost) +[root]: " -r server_user + server_user=${server_user:-root} + +read -p " +Do you want each user to have their own account for SSH tunneling? +[y/N]: " -r ssh_tunneling_enabled +ssh_tunneling_enabled=${ssh_tunneling_enabled:-n} + + read -p " +Enter the public IP address of your server: (IMPORTANT! This IP is used to verify the certificate) +: " -r IP_subject + + read -p " +Enter the password for the private CA key: +[pasted values will not be displayed] +: " -rs easyrsa_CA_password + +ansible-playbook users.yml -e "server_ip=$server_ip server_user=$server_user ssh_tunneling_enabled=$ssh_tunneling_enabled IP_subject=$IP_subject easyrsa_CA_password=$easyrsa_CA_password" } case "$1" in diff --git a/deploy.yml b/deploy.yml index 7b7e9ef..94a6c3d 100644 --- a/deploy.yml +++ b/deploy.yml @@ -63,3 +63,18 @@ - debug: msg="{{ additional_information.split('\n') }}" tags: cloud + + - name: Save the CA key password + local_action: > + shell echo "{{ easyrsa_CA_password }}" > /tmp/ca_password + become: no + tags: tests + + - name: Delete the CA key + local_action: + module: file + path: "configs/{{ IP_subject_alt_name }}/pki/private/cakey.pem" + state: absent + become: no + tags: always + when: Store_CAKEY is defined and Store_CAKEY == "N" diff --git a/roles/ssh_tunneling/tasks/main.yml b/roles/ssh_tunneling/tasks/main.yml index b279b02..ba0baf2 100644 --- a/roles/ssh_tunneling/tasks/main.yml +++ b/roles/ssh_tunneling/tasks/main.yml @@ -57,13 +57,13 @@ template: src=known_hosts.j2 dest=/root/.ssh/{{ IP_subject_alt_name }}_known_hosts - name: Fetch users SSH private keys - fetch: src='/var/jail/{{ item }}/.ssh/id_rsa' dest=configs/{{ IP_subject_alt_name }}_{{ item }}.ssh.pem flat=yes + fetch: src='/var/jail/{{ item }}/.ssh/id_rsa' dest=configs/{{ IP_subject_alt_name }}/{{ IP_subject_alt_name }}_{{ item }}.ssh.pem flat=yes with_items: "{{ users }}" - name: Change mode for SSH private keys - local_action: file path=configs/{{ IP_subject_alt_name }}_{{ item }}.ssh.pem mode=0600 + local_action: file path=configs/{{ IP_subject_alt_name }}/{{ IP_subject_alt_name }}_{{ item }}.ssh.pem mode=0600 with_items: "{{ users }}" become: false - name: Fetch the known_hosts file - fetch: src='/root/.ssh/{{ IP_subject_alt_name }}_known_hosts' dest=configs/{{ IP_subject_alt_name }}_known_hosts flat=yes + fetch: src='/root/.ssh/{{ IP_subject_alt_name }}_known_hosts' dest=configs/{{ IP_subject_alt_name }}/{{ IP_subject_alt_name }}_known_hosts flat=yes diff --git a/roles/vpn/tasks/main.yml b/roles/vpn/tasks/main.yml index 1770ac5..16b0bf1 100644 --- a/roles/vpn/tasks/main.yml +++ b/roles/vpn/tasks/main.yml @@ -9,6 +9,12 @@ - set_fact: easyrsa_p12_export_password: "{{ (ansible_date_time.iso8601_basic|sha1|to_uuid).split('-')[0] }}" easyrsa_CA_password: "{{ CA_password.stdout }}" + IP_subject_alt_name: "{{ IP_subject_alt_name }}" + +- name: Change the algorithm to RSA + set_fact: + algo_params: "rsa:2048" + when: Win10_Enabled is defined and Win10_Enabled == "Y" - name: Install StrongSwan apt: name=strongswan state=latest update_cache=yes @@ -97,153 +103,206 @@ when: item in strongswan_enabled_plugins with_items: "{{ strongswan_plugins.stdout_lines }}" -- name: Fetch easy-rsa-ipsec from git - git: - repo: git://github.com/ValdikSS/easy-rsa-ipsec.git - version: ipsec-with-patches - dest: "{{ easyrsa_dir }}" - -- name: Setup the vars file from our template - template: src=easy-rsa.vars.j2 dest={{ easyrsa_dir }}/easyrsa3/vars - - name: Ensure the pki directory is not exist - file: dest={{ easyrsa_dir }}/easyrsa3/pki state=absent + local_action: + module: file + dest: configs/{{ IP_subject_alt_name }}/pki + state: absent + become: no when: easyrsa_reinit_existent == True -- name: Build the pki enviroments - shell: > - ./easyrsa init-pki && - touch '{{ easyrsa_dir }}/easyrsa3/pki/pki_initialized' - args: - chdir: '{{ easyrsa_dir }}/easyrsa3/' - creates: '{{ easyrsa_dir }}/easyrsa3/pki/pki_initialized' +- 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 - shell: > - ./easyrsa --batch build-ca -- -passout pass:"{{ easyrsa_CA_password }}" && - touch {{ easyrsa_dir }}/easyrsa3/pki/ca_initialized + 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: '{{ easyrsa_dir }}/easyrsa3/' - creates: '{{ easyrsa_dir }}/easyrsa3/pki/ca_initialized' - notify: - - restart strongswan + 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 - shell: > - ./easyrsa gen-req {{ IP_subject_alt_name }} batch nopass -- -subj "/CN={{ IP_subject_alt_name }}" && - ./easyrsa --subject-alt-name='DNS:{{ IP_subject_alt_name }},IP:{{ IP_subject_alt_name }}' sign-req server {{ IP_subject_alt_name }} -- -passin pass:"{{ easyrsa_CA_password }}" && - touch '{{ easyrsa_dir }}/easyrsa3/pki/server_initialized' + 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: '{{ easyrsa_dir }}/easyrsa3/' - creates: '{{ easyrsa_dir }}/easyrsa3/pki/server_initialized' - notify: - - restart strongswan + 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 - shell: > - ./easyrsa gen-req {{ item }} nopass -- -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ item }}" && - ./easyrsa --subject-alt-name='DNS:{{ item }}' sign-req client {{ item }} nopass -- -passin pass:"{{ easyrsa_CA_password }}" && - touch '{{ easyrsa_dir }}/easyrsa3/pki/{{ item }}_initialized' + 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: '{{ easyrsa_dir }}/easyrsa3/' - creates: '{{ easyrsa_dir }}/easyrsa3/pki/{{ item }}_initialized' + 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 - shell: > - openssl pkcs12 -in {{ easyrsa_dir }}/easyrsa3//pki/issued/{{ item }}.crt -inkey {{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.key -export -name {{ item }} -out /{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.p12 -certfile {{ easyrsa_dir }}/easyrsa3//pki/ca.crt -passout pass:"{{ easyrsa_p12_export_password }}" + 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: '{{ easyrsa_dir }}/easyrsa3/' + 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: remote_src=True src='{{ easyrsa_dir }}/easyrsa3/pki/ca.crt' dest=/etc/ipsec.d/cacerts/ca.crt owner=strongswan group=root mode=0600 + 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: remote_src=True src='{{ easyrsa_dir }}/easyrsa3/pki/issued/{{ IP_subject_alt_name }}.crt' dest=/etc/ipsec.d/certs/{{ IP_subject_alt_name }}.crt owner=strongswan group=root mode=0600 + 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: remote_src=True src='{{ easyrsa_dir }}/easyrsa3/pki/private/{{ IP_subject_alt_name }}.key' dest=/etc/ipsec.d/private/{{ IP_subject_alt_name }}.key owner=strongswan group=root mode=0600 + 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 - shell: > - cat /{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.p12 | base64 + local_action: > + shell cat private/{{ item }}.p12 | base64 register: PayloadContent + become: no + args: + chdir: "configs/{{ IP_subject_alt_name }}/pki/" with_items: "{{ users }}" -- name: Register CA PayloadContent - shell: > - cat /{{ easyrsa_dir }}/easyrsa3/pki/ca.crt | base64 - register: PayloadContentCA - - 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 - template: src=mobileconfig.j2 dest=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item.0 }}.mobileconfig mode=0600 + 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 client ipsec config file - template: src=client_ipsec.conf.j2 dest=/{{ easyrsa_dir }}/easyrsa3//pki/private/ipsec_{{ item }}.conf mode=0600 + 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 - template: src=client_ipsec.secrets.j2 dest=/{{ easyrsa_dir }}/easyrsa3//pki/private/ipsec_{{ item }}.secrets mode=0600 + 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: Fetch users P12 - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.p12 dest=configs/{{ IP_subject_alt_name }}_{{ item }}.p12 flat=yes - with_items: "{{ users }}" - -- name: Fetch users mobileconfig - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.mobileconfig dest=configs/{{ IP_subject_alt_name }}_{{ item }}.mobileconfig flat=yes - with_items: "{{ users }}" - -- name: Fetch users certificates - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/issued/{{ item }}.crt dest=configs/{{ IP_subject_alt_name }}_{{ item }}.crt flat=yes - with_items: "{{ users }}" - -- name: Fetch users keys - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.key dest=configs/{{ IP_subject_alt_name }}_{{ item }}.key flat=yes - with_items: "{{ users }}" - -- name: Fetch users ipsec configs - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/ipsec_{{ item }}.conf dest=configs/{{ IP_subject_alt_name }}_{{ item }}_ipsec.conf flat=yes - with_items: "{{ users }}" - -- name: Fetch users ipsec secrets - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/ipsec_{{ item }}.secrets dest=configs/{{ IP_subject_alt_name }}_{{ item }}_ipsec.secrets flat=yes - with_items: "{{ users }}" - - name: Build the windows client powershell script - template: src=client_windows.ps1.j2 dest=/{{ easyrsa_dir }}/easyrsa3//pki/private/windows_{{ item }}.ps1 mode=0600 + 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: Fetch users windows scripts - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/windows_{{ item }}.ps1 dest=configs/{{ IP_subject_alt_name }}_{{ item }}_windows.ps1 flat=yes - when: Win10_Enabled is defined and Win10_Enabled == "Y" - with_items: "{{ users }}" - -- name: Restrict permissions +- 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 -- name: Fetch server CA certificate - fetch: src=/{{ easyrsa_dir }}/easyrsa3/pki/ca.crt dest=configs/{{ IP_subject_alt_name }}_ca.crt flat=yes +- 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 }} - include: iptables.yml tags: iptables diff --git a/roles/vpn/templates/easy-rsa.vars.j2 b/roles/vpn/templates/easy-rsa.vars.j2 deleted file mode 100644 index 2805b3b..0000000 --- a/roles/vpn/templates/easy-rsa.vars.j2 +++ /dev/null @@ -1,202 +0,0 @@ -# Easy-RSA 3 parameter settings - -# NOTE: If you installed Easy-RSA from your distro's package manager, don't edit -# this file in place -- instead, you should copy the entire easy-rsa directory -# to another location so future upgrades don't wipe out your changes. - -# HOW TO USE THIS FILE -# -# vars.example contains built-in examples to Easy-RSA settings. You MUST name -# this file 'vars' if you want it to be used as a configuration file. If you do -# not, it WILL NOT be automatically read when you call easyrsa commands. -# -# It is not necessary to use this config file unless you wish to change -# operational defaults. These defaults should be fine for many uses without the -# need to copy and edit the 'vars' file. -# -# All of the editable settings are shown commented and start with the command -# 'set_var' -- this means any set_var command that is uncommented has been -# modified by the user. If you're happy with a default, there is no need to -# define the value to its default. - -# NOTES FOR WINDOWS USERS -# -# Paths for Windows *MUST* use forward slashes, or optionally double-esscaped -# backslashes (single forward slashes are recommended.) This means your path to -# the openssl binary might look like this: -# "C:/Program Files/OpenSSL-Win32/bin/openssl.exe" - -# A little housekeeping: DON'T EDIT THIS SECTION -# -# Easy-RSA 3.x doesn't source into the environment directly. -# Complain if a user tries to do this: -if [ -z "$EASYRSA_CALLER" ]; then - echo "You appear to be sourcing an Easy-RSA 'vars' file." >&2 - echo "This is no longer necessary and is disallowed. See the section called" >&2 - echo "'How to use this file' near the top comments for more details." >&2 - return 1 -fi - -# DO YOUR EDITS BELOW THIS POINT - -# This variable should point to the top level of the easy-rsa tree. By default, -# this is taken to be the directory you are currently in. - -set_var EASYRSA "{{ easyrsa_dir }}/easyrsa3/" - -# If your OpenSSL command is not in the system PATH, you will need to define the -# path to it here. Normally this means a full path to the executable, otherwise -# you could have left it undefined here and the shown default would be used. -# -# Windows users, remember to use paths with forward-slashes (or escaped -# back-slashes.) Windows users should declare the full path to the openssl -# binary here if it is not in their system PATH. - -#set_var EASYRSA_OPENSSL "openssl" -# -# This sample is in Windows syntax -- edit it for your path if not using PATH: -#set_var EASYRSA_OPENSSL "C:/Program Files/OpenSSL-Win32/bin/openssl.exe" - -# Edit this variable to point to your soon-to-be-created key directory. -# -# WARNING: init-pki will do a rm -rf on this directory so make sure you define -# it correctly! (Interactive mode will prompt before acting.) - -set_var EASYRSA_PKI "$EASYRSA/pki" - -# Define X509 DN mode. -# This is used to adjust what elements are included in the Subject field as the DN -# (this is the "Distinguished Name.") -# Note that in cn_only mode the Organizational fields further below aren't used. -# -# Choices are: -# cn_only - use just a CN value -# org - use the "traditional" Country/Province/City/Org/OU/email/CN format - -set_var EASYRSA_DN "cn_only" - -# Organizational fields (used with 'org' mode and ignored in 'cn_only' mode.) -# These are the default values for fields which will be placed in the -# certificate. Don't leave any of these fields blank, although interactively -# you may omit any specific field by typing the "." symbol (not valid for -# email.) - -#set_var EASYRSA_REQ_COUNTRY "US" -#set_var EASYRSA_REQ_PROVINCE "California" -#set_var EASYRSA_REQ_CITY "San Francisco" -#set_var EASYRSA_REQ_ORG "Copyleft Certificate Co" -#set_var EASYRSA_REQ_EMAIL "me@example.net" -#set_var EASYRSA_REQ_OU "My Organizational Unit" - -# Choose a size in bits for your keypairs. The recommended value is 2048. Using -# 2048-bit keys is considered more than sufficient for many years into the -# future. Larger keysizes will slow down TLS negotiation and make key/DH param -# generation take much longer. Values up to 4096 should be accepted by most -# software. Only used when the crypto alg is rsa (see below.) - -# set_var EASYRSA_KEY_SIZE 2048 - -# The default crypto mode is rsa; ec can enable elliptic curve support. -# Note that not all software supports ECC, so use care when enabling it. -# Choices for crypto alg are: (each in lower-case) -# * rsa -# * ec - -{% if Win10_Enabled is defined and Win10_Enabled == "Y" %} -set_var EASYRSA_ALGO rsa -{% else %} -set_var EASYRSA_ALGO ec -{% endif %} - -# Define the named curve, used in ec mode only: - -set_var EASYRSA_CURVE prime256v1 - -# In how many days should the root CA key expire? - -set_var EASYRSA_CA_EXPIRE {{ easyrsa_ca_expire }} - -# In how many days should certificates expire? - -set_var EASYRSA_CERT_EXPIRE {{ easyrsa_cert_expire }} - -# How many days until the next CRL publish date? Note that the CRL can still be -# parsed after this timeframe passes. It is only used for an expected next -# publication date. - -#set_var EASYRSA_CRL_DAYS 180 - -# Support deprecated "Netscape" extensions? (choices "yes" or "no".) The default -# is "no" to discourage use of deprecated extensions. If you require this -# feature to use with --ns-cert-type, set this to "yes" here. This support -# should be replaced with the more modern --remote-cert-tls feature. If you do -# not use --ns-cert-type in your configs, it is safe (and recommended) to leave -# this defined to "no". When set to "yes", server-signed certs get the -# nsCertType=server attribute, and also get any NS_COMMENT defined below in the -# nsComment field. - -#set_var EASYRSA_NS_SUPPORT "no" - -# When NS_SUPPORT is set to "yes", this field is added as the nsComment field. -# Set this blank to omit it. With NS_SUPPORT set to "no" this field is ignored. - -#set_var EASYRSA_NS_COMMENT "Easy-RSA Generated Certificate" - -# A temp file used to stage cert extensions during signing. The default should -# be fine for most users; however, some users might want an alternative under a -# RAM-based FS, such as /dev/shm or /tmp on some systems. - -#set_var EASYRSA_TEMP_FILE "$EASYRSA_PKI/extensions.temp" - -# !! -# NOTE: ADVANCED OPTIONS BELOW THIS POINT -# PLAY WITH THEM AT YOUR OWN RISK -# !! - -# Broken shell command aliases: If you have a largely broken shell that is -# missing any of these POSIX-required commands used by Easy-RSA, you will need -# to define an alias to the proper path for the command. The symptom will be -# some form of a 'command not found' error from your shell. This means your -# shell is BROKEN, but you can hack around it here if you really need. These -# shown values are not defaults: it is up to you to know what you're doing if -# you touch these. -# -#alias awk="/alt/bin/awk" -#alias cat="/alt/bin/cat" - -# X509 extensions directory: -# If you want to customize the X509 extensions used, set the directory to look -# for extensions here. Each cert type you sign must have a matching filename, -# and an optional file named 'COMMON' is included first when present. Note that -# when undefined here, default behaviour is to look in $EASYRSA_PKI first, then -# fallback to $EASYRSA for the 'x509-types' dir. You may override this -# detection with an explicit dir here. -# -#set_var EASYRSA_EXT_DIR "$EASYRSA/x509-types" - -# OpenSSL config file: -# If you need to use a specific openssl config file, you can reference it here. -# Normally this file is auto-detected from a file named openssl-1.0.cnf from the -# EASYRSA_PKI or EASYRSA dir (in that order.) NOTE that this file is Easy-RSA -# specific and you cannot just use a standard config file, so this is an -# advanced feature. - -set_var EASYRSA_SSL_CONF "$EASYRSA/openssl-1.0.cnf" - -# Default CN: -# This is best left alone. Interactively you will set this manually, and BATCH -# callers are expected to set this themselves. - -set_var EASYRSA_REQ_CN "{{ IP_subject_alt_name }}" - -# Cryptographic digest to use. -# Do not change this default unless you understand the security implications. -# Valid choices include: md5, sha1, sha256, sha224, sha384, sha512 - -#set_var EASYRSA_DIGEST "sha256" - -# Batch mode. Leave this disabled unless you intend to call Easy-RSA explicitly -# in batch mode without any user input, confirmation on dangerous operations, -# or most output. Setting this to any non-blank string enables batch mode. - -set_var EASYRSA_BATCH "{{ IP_subject_alt_name }}" diff --git a/roles/vpn/templates/mobileconfig.j2 b/roles/vpn/templates/mobileconfig.j2 index d287326..9ee20c4 100644 --- a/roles/vpn/templates/mobileconfig.j2 +++ b/roles/vpn/templates/mobileconfig.j2 @@ -172,7 +172,7 @@ ca.crt PayloadContent - {{ PayloadContentCA.stdout }} + {{ PayloadContentCA }} PayloadDescription Adds a CA root certificate diff --git a/roles/vpn/templates/openssl.cnf.j2 b/roles/vpn/templates/openssl.cnf.j2 new file mode 100644 index 0000000..415557f --- /dev/null +++ b/roles/vpn/templates/openssl.cnf.j2 @@ -0,0 +1,143 @@ +# For use with Easy-RSA 3.0 and OpenSSL 1.0.* + +RANDFILE = .rnd + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = . # Where everything is kept +certs = $dir # Where the issued certs are kept +crl_dir = $dir # Where the issued crl are kept +database = $dir/index.txt # database index file. +new_certs_dir = $dir/certs # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem # The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = basic_exts # The extentions to add to the cert + +# This allows a V2 CRL. Ancient browsers don't like it, but anything Easy-RSA +# is designed for will. In return, we get the Issuer attached to CRLs. +crl_extensions = crl_ext + +default_days = 3650 # how long to certify for +default_crl_days= 3650 # how long before next CRL +default_md = sha256 # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_anything + +# For the 'anything' policy, which defines allowed DN fields +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +name = optional +emailAddress = optional + +#################################################################### +# Easy-RSA request handling +# We key off $DN_MODE to determine how to format the DN +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +default_md = sha256 +distinguished_name = cn_only +x509_extensions = easyrsa_ca # The extentions to add to the self signed cert + +# A placeholder to handle the $EXTRA_EXTS feature: +#%EXTRA_EXTS% # Do NOT remove or change this line as $EXTRA_EXTS support requires it + +#################################################################### +# Easy-RSA DN (Subject) handling + +# Easy-RSA DN for cn_only support: +[ cn_only ] +commonName = Common Name (eg: your user, host, or server name) +commonName_max = 64 +commonName_default = {{ IP_subject_alt_name }} + +# Easy-RSA DN for org support: +[ org ] +countryName = Country Name (2 letter code) +countryName_default = US +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = California + +localityName = Locality Name (eg, city) +localityName_default = San Francisco + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Copyleft Certificate Co + +organizationalUnitName = Organizational Unit Name (eg, section) +organizationalUnitName_default = My Organizational Unit + +commonName = Common Name (eg: your user, host, or server name) +commonName_max = 64 +commonName_default = {{ IP_subject_alt_name }} + +emailAddress = Email Address +emailAddress_default = me@example.net +emailAddress_max = 64 + +#################################################################### +# Easy-RSA cert extension handling + +# This section is effectively unused as the main script sets extensions +# dynamically. This core section is left to support the odd usecase where +# a user calls openssl directly. +[ basic_exts ] +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always + +extendedKeyUsage = serverAuth,1.3.6.1.5.5.7.3.17 +keyUsage = digitalSignature, keyEncipherment +subjectAltName = ${ENV::subjectAltName} + +# The Easy-RSA CA extensions +[ easyrsa_ca ] + +# PKIX recommendations: + +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 + +# Limit key usage to CA tasks. If you really want to use the generated pair as +# a self-signed cert, comment this out. +keyUsage = cRLSign, keyCertSign + +# nsCertType omitted by default. Let's try to let the deprecated stuff die. +# nsCertType = sslCA + +# CRL extensions. +[ crl_ext ] + +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always + + + diff --git a/tests/update-users.sh b/tests/update-users.sh new file mode 100755 index 0000000..b0cbb19 --- /dev/null +++ b/tests/update-users.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -e + +CAPW=`cat /tmp/ca_password` + +sed -i 's/- jack$/- jack_test/' config.cfg + +ansible-playbook users.yml -e "server_ip=$LXC_IP server_user=root ssh_tunneling_enabled=y IP_subject=$LXC_IP easyrsa_CA_password=$CAPW" + +cd configs/$LXC_IP/pki/ + +if openssl crl -inform pem -noout -text -in crl/jack.crt | grep CRL + then + echo "The CRL check passed" + else + echo "The CRL check failed" + exit 1 +fi + +if openssl x509 -inform pem -noout -text -in certs/jack_test.crt | grep CN=jack_test + then + echo "The new user exist" + else + echo "The new user does not exist" + exit 1 +fi diff --git a/users.yml b/users.yml index b6f7130..c9837a2 100644 --- a/users.yml +++ b/users.yml @@ -4,30 +4,6 @@ gather_facts: False vars_files: - config.cfg - vars_prompt: - - - name: "server_ip" - prompt: "Enter IP address of your server: (use localhost for local installation)\n" - default: localhost - private: no - - - name: "server_user" - prompt: "What user should we use to login on the server? (ignore if you're deploying to localhost):\n" - default: "root" - private: no - - - name: "ssh_tunneling_enabled" - prompt: "Do you want each user to have their own account for SSH tunneling? (y/n):\n" - default: "n" - private: no - - - name: "IP_subject" - prompt: "Enter public IP address of your server: (IMPORTANT! This IP is used to verify the certificate)\n" - private: no - - - name: "easyrsa_CA_password" - prompt: "Enter the password for the private CA key:\n" - private: yes tasks: - name: Add the server to the vpn-host group @@ -39,6 +15,7 @@ ssh_tunneling_enabled: "{{ ssh_tunneling_enabled }}" easyrsa_CA_password: "{{ easyrsa_CA_password }}" IP_subject: "{{ IP_subject }}" + ansible_ssh_private_key_file: "{{ SSH_keys.private }}" - name: Wait until SSH becomes ready... local_action: @@ -53,7 +30,7 @@ - name: User management hosts: vpn-host - gather_facts: false + gather_facts: true become: true vars_files: - config.cfg @@ -61,6 +38,7 @@ pre_tasks: - set_fact: IP_subject_alt_name: "{{ IP_subject }}" + easyrsa_p12_export_password: "{{ (ansible_date_time.iso8601_basic|sha1|to_uuid).split('-')[0] }}" roles: - { role: ssh_tunneling, tags: [ 'ssh_tunneling' ], when: ssh_tunneling_enabled is defined and ssh_tunneling_enabled == "y" } @@ -70,73 +48,136 @@ - name: Gather Facts setup: - - set_fact: - easyrsa_p12_export_password: "{{ (ansible_date_time.iso8601_basic|sha1|to_uuid).split('-')[0] }}" + - name: Cheking the signature algorithm + local_action: > + shell openssl x509 -text -in certs/{{ IP_subject_alt_name }}.crt | grep 'Signature Algorithm' | head -n1 + become: no + register: sig_algo + args: + chdir: "configs/{{ IP_subject_alt_name }}/pki/" + + - name: Change the algorithm to RSA + set_fact: + algo_params: "rsa:2048" + when: '"ecdsa" not in sig_algo.stdout' - name: Build the client's pair - shell: > - ./easyrsa gen-req {{ item }} nopass -- -passin pass:"{{ easyrsa_CA_password }}" -subj "/CN={{ item }}" && - ./easyrsa --subject-alt-name='DNS:{{ item }}' sign-req client {{ item }} nopass -- -passin pass:"{{ easyrsa_CA_password }}" && - touch '{{ easyrsa_dir }}/easyrsa3/pki/{{ item }}_initialized' + 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: '{{ easyrsa_dir }}/easyrsa3/' - creates: '{{ easyrsa_dir }}/easyrsa3/pki/{{ item }}_initialized' + 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 - shell: > - openssl pkcs12 -in {{ easyrsa_dir }}/easyrsa3//pki/issued/{{ item }}.crt -inkey {{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.key -export -name {{ item }} -out /{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.p12 -certfile {{ easyrsa_dir }}/easyrsa3//pki/ca.crt -passout pass:{{ easyrsa_p12_export_password }} + 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: '{{ easyrsa_dir }}/easyrsa3/' + 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: Get active users - shell: > - grep ^V pki/index.txt | grep -v "{{ IP_subject_alt_name }}" | awk '{print $5}' | sed 's/\/CN=//g' + local_action: > + shell grep ^V index.txt | grep -v "{{ IP_subject_alt_name }}" | awk '{print $5}' | sed 's/\/CN=//g' + become: no args: - chdir: '{{ easyrsa_dir }}/easyrsa3/' + chdir: "configs/{{ IP_subject_alt_name }}/pki/" register: valid_certs - name: Revoke non-existing users - shell: > - openssl ec -in pki/private/ca.key -out pki/private/ca.key -passin pass:"{{ easyrsa_CA_password }}" -passout pass:"" && - ipsec pki --signcrl --cacert {{ easyrsa_dir }}/easyrsa3//pki/ca.crt --cakey {{ easyrsa_dir }}/easyrsa3/pki/private/ca.key --reason superseded --cert {{ easyrsa_dir }}/easyrsa3//pki/issued/{{ item }}.crt > /etc/ipsec.d/crls/{{ item }}.der && - ./easyrsa revoke {{ item }} && - openssl ec -aes256 -in pki/private/ca.key -out pki/private/ca.key -passin pass:"" -passout pass:"{{ easyrsa_CA_password }}" && - ipsec rereadcrls + local_action: > + shell openssl ca -config openssl.cnf -passin pass:"{{ easyrsa_CA_password }}" -revoke certs/{{ item }}.crt && + openssl ca -gencrl -config openssl.cnf -passin pass:"{{ easyrsa_CA_password }}" -revoke certs/{{ item }}.crt -out crl/{{ item }}.crt + touch crl/{{ item }}_revoked + become: no args: - chdir: '{{ easyrsa_dir }}/easyrsa3/' + chdir: "configs/{{ IP_subject_alt_name }}/pki/" + creates: crl/{{ item }}_revoked + environment: + subjectAltName: "DNS:{{ item }}" when: item not in users with_items: "{{ valid_certs.stdout_lines }}" + - 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 + when: item not in users + with_items: "{{ valid_certs.stdout_lines }}" + notify: + - rereadcrls + - name: Register p12 PayloadContent - shell: > - cat /{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.p12 | base64 + local_action: > + shell cat private/{{ item }}.p12 | base64 register: PayloadContent + become: no + args: + chdir: "configs/{{ IP_subject_alt_name }}/pki/" with_items: "{{ users }}" - - name: Register CA PayloadContent - shell: > - cat /{{ easyrsa_dir }}/easyrsa3/pki/ca.crt | base64 - register: PayloadContentCA + - 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 - template: src=roles/vpn/templates/mobileconfig.j2 dest=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item.0 }}.mobileconfig mode=0600 + local_action: + module: template + src: roles/vpn/templates/mobileconfig.j2 + dest: configs/{{ IP_subject_alt_name }}/{{ item.0 }}.mobileconfig + mode: 0600 + become: no with_together: - "{{ users }}" - "{{ PayloadContent.results }}" no_log: True - - name: Fetch users P12 - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.p12 dest=configs/{{ IP_subject_alt_name }}_{{ item }}.p12 flat=yes - with_items: "{{ users }}" + - name: Build the client ipsec config file + local_action: + module: template + src: roles/vpn/templates/client_ipsec.conf.j2 + dest: configs/{{ IP_subject_alt_name }}/ipsec_{{ item }}.conf + mode: 0600 + become: no + with_items: + - "{{ users }}" - - name: Fetch users mobileconfig - fetch: src=/{{ easyrsa_dir }}/easyrsa3//pki/private/{{ item }}.mobileconfig dest=configs/{{ IP_subject_alt_name }}_{{ item }}.mobileconfig flat=yes - with_items: "{{ users }}" + - name: Build the client ipsec secret file + local_action: + module: template + src: roles/vpn/templates/client_ipsec.secrets.j2 + dest: configs/{{ IP_subject_alt_name }}/ipsec_{{ item }}.secrets + mode: 0600 + become: no + with_items: + - "{{ users }}" - - name: Fetch server CA certificate - fetch: src=/{{ easyrsa_dir }}/easyrsa3/pki/ca.crt dest=configs/{{ IP_subject_alt_name }}_ca.crt flat=yes + - name: Build the windows client powershell script + local_action: + module: template + src: roles/vpn/templates/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 }}" # SSH @@ -163,3 +204,7 @@ post_tasks: - debug: msg="{{ congrats.split('\n') }}" tags: always + + handlers: + - name: rereadcrls + shell: ipsec rereadcrls