From 49f11751e647094ddfc8e26f894fd9547d914e9d Mon Sep 17 00:00:00 2001 From: Emilien Mantel Date: Mon, 11 Jan 2016 18:20:42 +0100 Subject: [PATCH] Refactoring + SSL support --- defaults/main.yml | 19 ++++-- tasks/config.yml | 21 +++++++ tasks/install.yml | 13 ++++ tasks/legacy.yml | 4 -- tasks/main.yml | 53 +++------------- tasks/prepare.yml | 11 ++++ tasks/ssl.yml | 33 ++++++++++ tasks/vhost.yml | 16 ----- templates/etc/nginx/helper/ssl-legacy.j2 | 4 +- templates/etc/nginx/helper/ssl-strong.j2 | 5 +- templates/etc/nginx/sites-available/_base.j2 | 30 ++++++---- tests/test.yml | 63 +++++++++++++++++++- vars/main.yml | 3 - 13 files changed, 186 insertions(+), 89 deletions(-) create mode 100644 tasks/config.yml create mode 100644 tasks/install.yml delete mode 100644 tasks/legacy.yml create mode 100644 tasks/prepare.yml create mode 100644 tasks/ssl.yml diff --git a/defaults/main.yml b/defaults/main.yml index dc98a29..e16bb53 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -8,15 +8,19 @@ nginx_backports: false # nginx_root: "/srv/www" nginx_log_dir: '/var/log/nginx' -nginx_ssl_dir: '/etc/nginx/ssl' nginx_pid: '/run/nginx.pid' -nginx_resolver: - hosts: ['8.8.8.8', '8.8.4.4'] - valid: '300' - timeout: '5' +nginx_resolver_hosts: ['8.8.8.8', '8.8.4.4'] +nginx_resolver_valid: '300s' +nginx_resolver_timeout: '5s' nginx_error_log_level: 'warn' # http://nginx.org/en/docs/ngx_core_module.html#error_log nginx_dh_length: 2048 +# +# Nginx directories +# +nginx_htpasswd_dir: '/etc/nginx/htpasswd' +nginx_ssl_dir: '/etc/nginx/ssl' +nginx_helper_dir: '/etc/nginx/helper' # # Load upstream @@ -88,3 +92,8 @@ nginx_vhosts: [] # htpasswd # nginx_htpasswd: [] + +# +# SSL pairs +# +nginx_ssl_pairs: [] diff --git a/tasks/config.yml b/tasks/config.yml new file mode 100644 index 0000000..838ac0f --- /dev/null +++ b/tasks/config.yml @@ -0,0 +1,21 @@ +--- + +- name: TEMPLATE | Deploy nginx.conf + template: > + src=etc/nginx/nginx.conf.j2 + dest=/etc/nginx/nginx.conf + notify: reload nginx + +- name: TEMPLATE | Deploy all helpers + template: > + src={{ item }} + dest={{ nginx_helper_dir }}/{{ item | basename | regex_replace('\.j2$','') }} + with_fileglob: '../templates/etc/nginx/helper/*.j2' + notify: reload nginx + +- name: TEMPLATE | Deploy custom http configuration + template: > + src=etc/nginx/conf.d/custom.conf.j2 + dest=/etc/nginx/conf.d/custom.conf + notify: reload nginx + diff --git a/tasks/install.yml b/tasks/install.yml new file mode 100644 index 0000000..d956de2 --- /dev/null +++ b/tasks/install.yml @@ -0,0 +1,13 @@ +--- + +- name: APT | Install nginx and dependencies + apt: > + pkg={{ nginx_apt_package }} + state=present + update_cache=yes + cache_valid_time=3600 + default_release={{ ansible_distribution_release + '-backports' if nginx_backports else ansible_distribution_release }} + +- name: APT | Install python-passlib + apt: pkg=python-passlib state=present + diff --git a/tasks/legacy.yml b/tasks/legacy.yml deleted file mode 100644 index f0c298a..0000000 --- a/tasks/legacy.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- - -- name: FILE | Remove old directories - file: path=/etc/nginx/helpers state=absent diff --git a/tasks/main.yml b/tasks/main.yml index 8634412..e93c6a7 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,51 +1,13 @@ --- -- name: APT | Install nginx and dependencies - apt: > - pkg={{ nginx_apt_package }} - state=present - update_cache=yes - cache_valid_time=3600 - default_release={{ ansible_distribution_release + '-backports' if nginx_backports else ansible_distribution_release }} +- name: INCLUDE | Install + include: install.yml -- name: APT | Install python-passlib - apt: pkg=python-passlib state=present +- name: INCLUDE | Prepare + include: prepare.yml -- name: SHELL | Get Nginx version - shell: nginx -v 2>&1 | sed -r 's#.*/##;' | cut -d ' ' -f 1 - register: nginx_version - changed_when: false - -- name: TEMPLATE | Deploy nginx.conf - template: > - src=etc/nginx/nginx.conf.j2 - dest=/etc/nginx/nginx.conf - notify: reload nginx - -- name: INCLUDE | Fix legacy - include: legacy.yml - -- name: FILE | Create folders - file: dest={{ item }} owner=root mode=0755 state=directory - with_items: "{{ nginx_dirs }}" - -#- name: COMMAND | Creates DH file -# command: openssl dhparam -out {{ nginx_dh_path }} {{ nginx_dh_length }} -# args: -# creates: "{{ nginx_dh_path }}" - -- name: TEMPLATE | Deploy all helpers - template: > - src={{ item }} - dest={{ nginx_helper_dir }}/{{ item | basename | regex_replace('\.j2$','') }} - with_fileglob: '../templates/etc/nginx/helper/*.j2' - notify: reload nginx - -- name: TEMPLATE | Deploy custom http configuration - template: > - src=etc/nginx/conf.d/custom.conf.j2 - dest=/etc/nginx/conf.d/custom.conf - notify: reload nginx +- name: INCLUDE | Install + include: config.yml - name: INCLUDE | Upstream configuration include: upstream.yml @@ -54,6 +16,9 @@ - name: INCLUDE | htpasswd configuration include: htpasswd.yml +- name: INCLUDE | Vhosts configuration + include: ssl.yml + - name: INCLUDE | Vhosts configuration include: vhost.yml diff --git a/tasks/prepare.yml b/tasks/prepare.yml new file mode 100644 index 0000000..bea8b4e --- /dev/null +++ b/tasks/prepare.yml @@ -0,0 +1,11 @@ +--- + +- name: SHELL | Get Nginx version + shell: nginx -v 2>&1 | sed -r 's#.*/##;' | cut -d ' ' -f 1 + register: nginx_version + changed_when: false + +- name: FILE | Create folders + file: dest={{ item }} owner=root mode=0755 state=directory + with_items: "{{ nginx_dirs }}" + diff --git a/tasks/ssl.yml b/tasks/ssl.yml new file mode 100644 index 0000000..63da276 --- /dev/null +++ b/tasks/ssl.yml @@ -0,0 +1,33 @@ +--- + +- name: COMMAND | Creates DH file + command: openssl dhparam -out {{ nginx_dh_path }} {{ nginx_dh_length }} + args: + creates: "{{ nginx_dh_path }}" + +- name: FILE | Create SSL directories + file: > + path="{{ nginx_ssl_dir + '/' + item.name }}" + state=directory + with_items: nginx_ssl_pairs + +- name: COPY | Deploy SSL keys + copy: > + content="{{ item.key }}" + dest="{{ nginx_ssl_dir + '/' + item.name + '/' + item.name + '.key' }}" + with_items: nginx_ssl_pairs + notify: reload nginx + +- name: COPY | Deploy SSL certs + copy: > + content="{{ item.cert }}" + dest="{{ nginx_ssl_dir + '/' + item.name + '/' + item.name + '.crt' }}" + with_items: nginx_ssl_pairs + notify: reload nginx + +#- name: FAIL | Missmatch vhost SSL configuration +# fail: msg="FUCK {{ item.name }}" +# +# +# +# nginx_ssl_dir + '/' + ssl_name + '/' + ssl_name + '.key' diff --git a/tasks/vhost.yml b/tasks/vhost.yml index 89864b0..58d8577 100644 --- a/tasks/vhost.yml +++ b/tasks/vhost.yml @@ -69,19 +69,3 @@ notify: reload nginx when: (item.enable is defined and not item.enable) or (item.delete is defined and item.delete) -#- name: FILE | Create ssl dir per vhost (if needed) -# file: dest=/etc/nginx/ssl/{{ item.name }} owner=root mode=0750 state=directory -# with_items: nginx_vhosts -# when: item.ssl.use is defined and item.ssl.use - -# TODO... -#- name: COPY | Deploy SSL keys if needed -# copy: src=keys/{{ item.name }}/{{ item.name }}.crt dest=/etc/nginx/ssl/{{ item.name }} mode=660 -# copy: src=keys/{{ item.name }}/{{ item.name }}.key dest=/etc/nginx/ssl/{{ item.name }} mode=660 -# with_items: nginx_vhosts -# when: item.ssl.use and not generatekey - -# TODO: -# - deploy defaults files (index.html/index.php) allready in files/ -# - work with role "ssl_autosign" - diff --git a/templates/etc/nginx/helper/ssl-legacy.j2 b/templates/etc/nginx/helper/ssl-legacy.j2 index 7464285..fc36bf8 100644 --- a/templates/etc/nginx/helper/ssl-legacy.j2 +++ b/templates/etc/nginx/helper/ssl-legacy.j2 @@ -11,8 +11,8 @@ add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; ssl_stapling on; ssl_stapling_verify on; -resolver {{ nginx_resolver.hosts | default(['208.67.222.222', '208.67.220.220']) | join(' ') }} valid={{ nginx_resolver.valid}}s; -resolver_timeout {{ nginx_resolver.timeout }}s; +resolver {{ nginx_resolver_hosts | join(' ') }} valid={{ nginx_resolver_valid }}; +resolver_timeout {{ nginx_resolver_timeout }}; # vim:filetype=nginx diff --git a/templates/etc/nginx/helper/ssl-strong.j2 b/templates/etc/nginx/helper/ssl-strong.j2 index 1e5a5c4..f0ac0fc 100644 --- a/templates/etc/nginx/helper/ssl-strong.j2 +++ b/templates/etc/nginx/helper/ssl-strong.j2 @@ -11,8 +11,7 @@ add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; ssl_stapling on; ssl_stapling_verify on; -resolver {{ nginx_resolver.hosts | default(['208.67.222.222', '208.67.220.220']) | join(' ') }} valid={{ nginx_resolver.valid}}s; -resolver_timeout {{ nginx_resolver.timeout }}s; - +resolver {{ nginx_resolver_hosts | join(' ') }} valid={{ nginx_resolver_valid }}; +resolver_timeout {{ nginx_resolver_timeout }}; # vim:filetype=nginx diff --git a/templates/etc/nginx/sites-available/_base.j2 b/templates/etc/nginx/sites-available/_base.j2 index 472f2fa..ee5fec6 100644 --- a/templates/etc/nginx/sites-available/_base.j2 +++ b/templates/etc/nginx/sites-available/_base.j2 @@ -1,11 +1,19 @@ +{% set __proto = item.proto | default(['http']) %} +{% set __main_name = item.name if item.name is string else item.name[0] %} {% set __listen = item.listen | default(['80']) %} {% set __listen_ssl = item.listen_ssl | default(['443']) %} {% set __location = item.location | default({}) %} {% macro htpasswd(htpasswd_name, indent=1) -%} -{% for ht in nginx_htpasswd %}{% if ht.name == htpasswd_name %} +{% for ht in nginx_htpasswd if ht.name == htpasswd_name %} {{ "\t" * indent }}auth_basic "{{ ht.description }}"; {{ "\t" * indent }}auth_basic_user_file {{ nginx_htpasswd_dir }}/{{ ht.name }}; -{% endif %}{% endfor%} +{% endfor%} +{%- endmacro %} +{% macro ssl(ssl_name) %} +{% for sn in nginx_ssl_pairs if sn.name == ssl_name %} + ssl_certificate {{ nginx_ssl_dir + '/' + ssl_name + '/' + ssl_name + '.crt' }}; + ssl_certificate_key {{ nginx_ssl_dir + '/' + ssl_name + '/' + ssl_name + '.key' }}; +{% endfor %} {%- endmacro %} # # {{ ansible_managed }} @@ -15,9 +23,18 @@ # HTTP # server { +{% if 'http' in __proto %} {% for port in __listen %} listen {{ port }}; {% endfor %} +{% endif %} +{% if 'https' in __proto %} +{% for port in __listen_ssl %} + listen {{ port }} ssl; +{% endfor %} +{{ ssl(item.ssl_name) }} + include {{ nginx_helper_dir + '/ssl-' + item.ssl_template | default('strong') }}; +{% endif %} server_name {% if item.name is string %}{{ item.name }}{% else %}{{ item.name | join(' ') }}{% endif %}; {% block root %} {% if item.root is defined %} @@ -97,15 +114,6 @@ server { {% endif %} } -{# -# HTTPS -#server { -ssl on; -ssl_certificate {{ nginx_ssl_dir }}/{{ item.name }}/{{ item.name }}.crt; -ssl_certificate_key {{ nginx_ssl_dir }}/{{ item.name }}/{{ item.name }}.key; -include {{ nginx_helper_dir }}/ssl-{{ item.ssl.template | default('strong') }}; -#} - {% if item.redirect_from is defined and item.redirect_from is iterable %} # # Redirect from diff --git a/tests/test.yml b/tests/test.yml index d9f5a3b..114c862 100644 --- a/tests/test.yml +++ b/tests/test.yml @@ -9,7 +9,9 @@ - curl - fcgiwrap - service: name=fcgiwrap state=started + register: sf - pause: seconds=5 + when: sf.changed vars: nginx_backports: true nginx_php: true @@ -33,6 +35,56 @@ description: 'Please login!' users: [] state: 'absent' + nginx_ssl_pairs: + - name: 'test-ssl.local' + key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEAvavrJWFp3Al2VwRgKx+4Y2mbRRvoxvyd2pyN0xMJ/tCJscaG + 8s60v6WZ9FcCOeMkSI2DXsk4z7pbQdQn0h2GDr/5MOJkPAVWSWEN46tpaLZ3v0zp + 88ZIbnEk1G0PsdFuW/pnLsakPlAMrl1VArFsV6YsatLt30UIYYcRO97StkoOehCx + A5w+XqtfHZeQZ0/DS81633gwYUcMuSTUFZ60r7ge1/m77DTSKg3rTVk5sebP8cjS + +aWHvxP/GyvvDsT+3gjRJx2/5O3JkfH0zaOsaU2Avj0PR0c5rhynrNO/l1k+GJJB + cbBrM+yA8Ofzp4oXUrCfaIq3RuL3Pd+khcKsiwIDAQABAoIBAQCPpAMQ7BUfbosQ + m1+5SOx7XR8Z12kSSX3CcY12rJSFRakB2TeZ6rE38lIFmV82N67iw0kaH4nGx3sU + /3aoyXMc+IXfX5RJYEFYkQfTw5ywkH9fgQAsfZ2dBlK+DVo1cEYDoj9CTW1VQ4pX + Ape+0l8agd5hiBxdWgpe0ctbbARnx584viLiA/iPBDNxKi9zEYw+WP7hSj5QWahr + a09tubcC4L6tjvv8CoZTRSKfCW64vWRDvE6vmA+zJN9Arc1WTYzF1KO1Gybwf8h7 + stJb191smAgGDFhKo0j58ncyAnrS1k4mapm86QQhlfIA6DKvvC0qm3KdQns5b7HM + PyzW0hwBAoGBAO2mTVTOsziom9vtBwM0nRMMEgynR2X3EKMJz2mjcCf66f1F+aQ5 + DvQFM2V8S2s1nGnPh8NKKZ8DxW1NKuR4qx82zeAXpUs9ibHxOnw4YRC485zqc2Wt + fSO1OEDYeKyzWP1nGGtCntYUXzJnWn/wz0mBGKzLKTuLwyFIKx1b7bybAoGBAMxR + N+lT57rX6d4GUqcgNOuWMZ/D8egnE5+hsoiFnHOisRLOgUgBBSy4rwAZx+rdHYT+ + RO11L1PLYEzyvnO0f13R+N7aqKwNXDSzZGA+jb4pjkVidIC2smG/JYKJH5Z+kakw + mwMKP0wdRZJsCaMgScHmWJS8d6Ox/XJJoWrTWTbRAoGAWJlEgVaiaIArwz1F/QLz + gHNik0cWDkSi9jWlFxwwpycbbypUXM5M7dq2g6JoN6sACk6trbgLdlYgl5RKZm06 + VuPGs0H9hOSHXkix5jfasDJT2G9r4D9ixRo9w6cwriobBjYWW3612tgzeYYgrkwn + 655uhZUkZSfA8rqGIGbyZfsCgYAf5WH8G+wmIATTc1s92epJCOZwUY+XNVp75itP + 4sPczX4lOHW4PuiG5cH0GxI5mRE9rNAn3c5on2xGNvMCbyAfDmNyruH8Eg3d8E9w + MvO/xw79x/P2EA9i8QszCKMUxGeK6RqZ6+SbxkoRJKqQe77n9UTI228179hoGhSH + 77ySsQKBgQC8SSZn6a8PpSIIFXB9WCFMwfGFYbUz0wvpaeZP8GKx3BEzMeJqSUaJ + hrQgpwQXkueeamlCQcvV3AUCoBRWTYRLDrWiUIXuIgikDWBFp6TBvTnVRI7iktly + fNED7jXOSjJqnFmdkZlAI5V8dM++mVYVykJD6jcaVRQvxqFLrhSaRg== + -----END RSA PRIVATE KEY----- + cert: | + -----BEGIN CERTIFICATE----- + MIIDBTCCAe2gAwIBAgIJALKJfbk5vuieMA0GCSqGSIb3DQEBBQUAMBkxFzAVBgNV + BAMMDnRlc3Qtc3NsLmxvY2FsMB4XDTE2MDExMTE2NDI0NFoXDTI2MDEwODE2NDI0 + NFowGTEXMBUGA1UEAwwOdGVzdC1zc2wubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUA + A4IBDwAwggEKAoIBAQC9q+slYWncCXZXBGArH7hjaZtFG+jG/J3anI3TEwn+0Imx + xobyzrS/pZn0VwI54yRIjYNeyTjPultB1CfSHYYOv/kw4mQ8BVZJYQ3jq2lotne/ + TOnzxkhucSTUbQ+x0W5b+mcuxqQ+UAyuXVUCsWxXpixq0u3fRQhhhxE73tK2Sg56 + ELEDnD5eq18dl5BnT8NLzXrfeDBhRwy5JNQVnrSvuB7X+bvsNNIqDetNWTmx5s/x + yNL5pYe/E/8bK+8OxP7eCNEnHb/k7cmR8fTNo6xpTYC+PQ9HRzmuHKes07+XWT4Y + kkFxsGsz7IDw5/OnihdSsJ9oirdG4vc936SFwqyLAgMBAAGjUDBOMB0GA1UdDgQW + BBRaSF1L+ivPhmIVGQjtviBqZWDS9DAfBgNVHSMEGDAWgBRaSF1L+ivPhmIVGQjt + viBqZWDS9DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCjrgB9+Zuq + Rx7T2mRUl4jf75dLabuBQD0ePALTtvNyBSghhzSr90mE7GlFOYAv0JsmEa3R1LVF + wLPIdrIhNHpt7hN0PkhUlfgmxBnRSCfhpiq4xxsDVFM7ehtDz4+dv1LUDMXo07+E + f24g9aqmypiFzHisUQrYIhtQmHxRpKyGp6kDAW9qNxg6k/Um00aHdYfuD9ER4ksR + f8Hto7f+vssKxCRY2OZXqq13PxEwC5+hgAUkTdrycA/moXFuHJi3lCnCND7sSzvG + tXBggOusyFZFC4bs2m+V+Z+RN+tK2c/c0nq5HR8MV5HwIm4Z8GoT2/0BfJ00cgWL + lVz0gDBfdH8f + -----END CERTIFICATE----- nginx_custom_http: - 'add_header X-ansible 1;' nginx_vhosts: @@ -85,6 +137,10 @@ - name: 'backuppc.local' template: '_backuppc' htpasswd: 'hello' + - name: 'test-ssl.local' + proto: ['http', 'https'] + template: '_base' + ssl_name: 'test-ssl.local' roles: - ../../ post_tasks: @@ -95,7 +151,7 @@ with_items: ['test-php.local', 'test-php-index.local'] - name: -- Add HTML file -- copy: dest="{{ item }}/index.html" content="Index HTML test OK\n" - with_items: ['{{ nginx_root }}/test.local/public', '/var/tmp', '{{ nginx_root }}/test-htpasswd-all.local/public'] + with_items: ['{{ nginx_root }}/test.local/public', '/var/tmp', '{{ nginx_root }}/test-htpasswd-all.local/public', '{{ nginx_root }}/test-ssl.local/public'] - name: -- VERIFY VHOSTS -- command: "curl -H 'Host: {{ item.name if item.name is string else item.name[0] }}' http://127.0.0.1{% if item.listen is defined %}:{{ item.listen[0] }}{% endif %}/" with_items: nginx_vhosts @@ -143,3 +199,8 @@ changed_when: false register: authbpc failed_when: authbpc.stdout.find('BackupPC Server Status') == -1 + - name: -- VERIFY SSL -- + command: "curl --insecure -H 'Host: test-ssl.local' https://127.0.0.1/" + changed_when: false + failed_when: authok.stdout.find('Index HTML test OK') != -1 + diff --git a/vars/main.yml b/vars/main.yml index 40e0fdc..662bafe 100644 --- a/vars/main.yml +++ b/vars/main.yml @@ -29,7 +29,4 @@ nginx_dirs: - "{{ nginx_ssl_dir }}" - "{{ nginx_helper_dir }}" -nginx_htpasswd_dir: '/etc/nginx/htpasswd' -nginx_ssl_dir: '/etc/nginx/ssl' -nginx_helper_dir: '/etc/nginx/helper'