diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml index 18488bd..f221132 100644 --- a/roles/nginx/defaults/main.yml +++ b/roles/nginx/defaults/main.yml @@ -1,4 +1,24 @@ --- - is_proxy: false - php: false - config_names: [] +is_proxy: false +php: false +config_names: [] +letsencrypt: false + +nginx_max_clients: 512 + +nginx_http_params: + senfile: "on" + tcp_nopush: "on" + tcp_nodelay: "on" + keepalive_timeout: "65" + +nginx_log_dir: "/var/log/nginx" +nginx_access_log_name: "access.log" +nginx_error_log_name: "error.log" +nginx_separate_logs_per_site: False + +letsencrypt_account_key: "/etc/ssl/private/letsencrypt.key.pem" +letsencrypt_challenge_webroot: "/var/www/html" + +nginx_sites: + diff --git a/roles/nginx/tasks/letsencrypt.yaml b/roles/nginx/tasks/letsencrypt.yaml new file mode 100644 index 0000000..6f463e8 --- /dev/null +++ b/roles/nginx/tasks/letsencrypt.yaml @@ -0,0 +1,35 @@ +- name: provision ssl host private key + openssl_privatekey: + path: "{{ item.server.ssl_certificate_key }}" + +- name: generate certificate signing request + command: > + openssl req + -new + -sha256 + -nodes + -key {{ item.server.ssl_certificate_key }} + -out {{ item.letsencrypt.ssl_csr }} + -subj "/C={{ item.letsencrypt.ssl_country }} + /ST={{ item.letsencrypt.ssl_state }} + /L{{ item.letsencrypt.ssl_loc }} + /O={{ item.letsencrypt.ssl_org }} + /emailAddress={{ item.letsencrypt.ssl_email }}" + +- name: get challenge(s) from letsencrypt server + letsencrypt: + account_key: "{{ letsencrypt_account_key }}" + csr: "{{ item.letsencrypt.ssl_csr }}" + dest: "{{ item.server.ssl_certificate }}" + register: letsencrypt_challenge + +- name: store challenge(s) in local dir + include: store_challenge.yaml + when: letsencrypt_challenge|changed + +- name: get signed certificate(s) from letsencrypt server + letsencrypt: + account_key: "{{ letsencrypt_account_key }}" + csr: "{{ item.letsencrypt.ssl_csr }}" + dest: "{{ item.server.ssl_certificate }}" + data: "{{ letsencrypt_challenge }}" diff --git a/roles/nginx/tasks/main.yaml b/roles/nginx/tasks/main.yaml index 96c4b95..2b1c3f5 100644 --- a/roles/nginx/tasks/main.yaml +++ b/roles/nginx/tasks/main.yaml @@ -6,6 +6,35 @@ service_packages: - nginx +- name: install letsencrypt dependencies + apt: + name: "{{ item }}" + state: present + update_cache: yes + cache_valid_time: 3600 + with_items: "{{ letsencrypt_requirements }}" + when: letsencrypt|bool + +- name: provision directories for site specific configurations + file: + path: /etc/nginx/{{ item }} + state: directory + owner: root + group: root + mode: 0755 + with_items: + - "sites-available" + - "sites-enabled" + +- name: provision letsencrypt challenge folder + file: + path: "{{ letsencrypt.challenge_webroot }}/.well-known/acme-challenge" + state: directory + owner: root + group: root + mode: 0755 + when: letsencrypt|bool + - name: disable nginx default configuration file: path: /etc/nginx/sites-enabled/default @@ -33,16 +62,55 @@ - enable nginx configuration - restart nginx -- name: add nginx configurations +- name: add nginx configuration custom templates template: src: "roles/{{ parent_role_path }}/templates/{{ item }}.conf.nginx.j2" - dest: /etc/nginx/sites-available/{{ item }}.conf + dest: "/etc/nginx/sites-available/{{ item }}.conf" with_items: "{{ config_names }}" -- name: enable nginx configurations +- name: enable nginx configuration custom templates file: src: "/etc/nginx/sites-available/{{ item }}.conf" dest: "/etc/nginx/sites-enabled/{{ item }}.conf" state: link with_items: "{{ config_names }}" notify: restart nginx + +- name: generate nginx configurations from standard template + template: + src: site.j2 + dest: "/etc/nginx/sites-available/{{ item.server.file_name }}" + with_items: "{{ nginx_sites }}" + +- name: enable nginx configurations used for letsencrypt challenge + file: + path: "/etc/nginx/sites-enabled/{{ item.server.file_name }}" + state: link + src: "/etc/nginx/sites-enabled/{{ item.server.file_name }}" + with_items: "{{ nginx_sites }}" + when: letsencrypt|bool and item.use_for_challenge is defined and item.use_for_challenge|bool + +- name: restart nginx to start enabled configurations used for letsencrypt + service: + name: nginx + state: restarted + when: letsencrypt|bool + +- name: provision letsencrypt account private key + openssl_privatekey: + path: "{{ letsencrypt_account_key }}" + when: letsencrypt|bool + +- name: provision ssl cert/key(s) with letsencrypt + include: letsencrypt.yml + with_items: "{{ nginx_sites }}" + when: letsencrypt|bool and item.letsencrypt is defined + +- name: enable nginx configuration generated from standard template + file: + path: "/etc/nginx/sites-enabled/{{ item.server.file_name }}" + state: link + src: "/etc/nginx/sites-available/{{ item.server.file_name }}" + with_items: "{{ nginx_sites }}" + when: nginx_sites is defined and nginx_sites + notify: restart nginx diff --git a/roles/nginx/tasks/store_challenge.yaml b/roles/nginx/tasks/store_challenge.yaml new file mode 100644 index 0000000..7763237 --- /dev/null +++ b/roles/nginx/tasks/store_challenge.yaml @@ -0,0 +1,9 @@ +- name: copy challenge file inside webroot + copy: + dest: "{{ letsencrypt_challenge_webroot }}/{{ chall.value.http-01.resource }}" + content: "{{ chall.value.http-01.resource_value }}" + with_dict: "{{ letsencrypt_challenge.challenge_data }}" + loop_contro: + loop_var: chall + + diff --git a/roles/nginx/templates/nginx.conf.j2 b/roles/nginx/templates/nginx.conf.j2 new file mode 100644 index 0000000..3d0398d --- /dev/null +++ b/roles/nginx/templates/nginx.conf.j2 @@ -0,0 +1,29 @@ +# {{ ansible_managed }} +# +user www-data; + +worker_processes {{ ansible_processor_count }}; +pid /var/run/nginx.pid; + +events { + worker_connections {{ nginx_max_clients }}; +} + +http { + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + include /etc/nginx/mime.types; + default_type application/octet-stream; + + access_log {{ nginx_log_dir }}/{{ nginx_access_log_name }}; + error_log {{ nginx_log_dir }}/{{ nginx_error_log_name }}; + +{% for k,v in nginx_http_params.iteritems() %} + {{ k }} {{ v }}; +{% endfor %} + + gzip on; + gzip_disable "msie6"; + + include /etc/nginx/conf.d/*.conf; + include /etc/nginx/sites-enabled/*; +} diff --git a/roles/nginx/templates/site.j2 b/roles/nginx/templates/site.j2 new file mode 100644 index 0000000..14e1d94 --- /dev/null +++ b/roles/nginx/templates/site.j2 @@ -0,0 +1,28 @@ +# {{ ansible_managed }} + +server { + +{% for key,value in item.server|dictsort if key != 'file_name' %} + {{ key }} {{ value }}; +{% if nginx_separate_logs_per_site == True %} + access_log {{ nginx_log_dir }}/{{ item.server.server_name }}-{{ nginx_access_log_name }}; + error_log {{ nginx_log_dir }}/{{ item.server.server_name }}-{{ nginx_error_log_name }}; +{% endif %} + +{% endfor %} +{% if item.use_for_challenge is defined %} + location /.well-known/acme-challenge { + root {{ letsencrypt_challenge_webroot }}; + + } +{% endif %} +{% if 'location' in item %} +{% for location in item.location if 'location' in item %} + location {{ location.name }} { {% for key,value in location|dictsort if key != 'name' %} + {{ key }} {{ value }}; {% endfor %} + + } +{% endfor %} +{% endif %} +} + diff --git a/roles/nginx/vars/main.yaml b/roles/nginx/vars/main.yaml new file mode 100644 index 0000000..ee8b137 --- /dev/null +++ b/roles/nginx/vars/main.yaml @@ -0,0 +1,6 @@ +--- +letsencrypt_requirements: +# - python-selinux + - openssl + - python-openssl + - ca-certificates