From b523cf86c8cbedb43cf625a1a847ca828afd5fba Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Sun, 13 Oct 2019 17:29:11 +0200 Subject: nextcloud basic installation is finally working properly --- roles/elevate/liquidtruth/tasks/main.yml | 16 ++--- roles/elevate/media/tasks/main.yml | 14 ++--- roles/nextcloud/defaults/main.yml | 1 + roles/nextcloud/tasks/main.yml | 73 ++++++++++++++++------ roles/nextcloud/templates/apache-site.conf.j2 | 18 ++++++ roles/nextcloud/templates/nginx-vhost.conf.j2 | 45 +++++++++++++ roles/nextcloud/templates/pod-with-mariadb.yml.j2 | 10 ++- roles/nginx/base/defaults/main.yml | 10 +++ .../base/files/conf.d/connection-upgrade.conf | 6 ++ roles/nginx/base/files/snippets/hsts.conf | 1 + roles/nginx/base/files/snippets/proxy-nobuff.conf | 4 ++ .../base/files/snippets/security-headers.conf | 4 ++ roles/nginx/base/files/snippets/ssl.conf | 10 +++ roles/nginx/base/handlers/main.yml | 5 ++ roles/nginx/base/tasks/main.yml | 31 +++++++++ roles/nginx/defaults/main.yml | 21 ------- roles/nginx/files/conf.d/connection-upgrade.conf | 6 -- roles/nginx/files/snippets/hsts.conf | 1 - roles/nginx/files/snippets/proxy-nobuff.conf | 4 -- roles/nginx/files/snippets/security-headers.conf | 4 -- roles/nginx/files/snippets/ssl.conf | 10 --- roles/nginx/handlers/main.yml | 5 -- roles/nginx/tasks/acme.yml | 44 ------------- roles/nginx/tasks/main.yml | 68 -------------------- .../generic-proxy-no-buffering-with-acme.conf.j2 | 43 ------------- roles/nginx/vhost/defaults/main.yml | 13 ++++ roles/nginx/vhost/handlers/main.yml | 5 ++ roles/nginx/vhost/tasks/acme.yml | 44 +++++++++++++ roles/nginx/vhost/tasks/main.yml | 25 ++++++++ .../generic-proxy-no-buffering-with-acme.conf.j2 | 43 +++++++++++++ 30 files changed, 342 insertions(+), 242 deletions(-) create mode 100644 roles/nextcloud/templates/apache-site.conf.j2 create mode 100644 roles/nextcloud/templates/nginx-vhost.conf.j2 create mode 100644 roles/nginx/base/defaults/main.yml create mode 100644 roles/nginx/base/files/conf.d/connection-upgrade.conf create mode 100644 roles/nginx/base/files/snippets/hsts.conf create mode 100644 roles/nginx/base/files/snippets/proxy-nobuff.conf create mode 100644 roles/nginx/base/files/snippets/security-headers.conf create mode 100644 roles/nginx/base/files/snippets/ssl.conf create mode 100644 roles/nginx/base/handlers/main.yml create mode 100644 roles/nginx/base/tasks/main.yml delete mode 100644 roles/nginx/defaults/main.yml delete mode 100644 roles/nginx/files/conf.d/connection-upgrade.conf delete mode 100644 roles/nginx/files/snippets/hsts.conf delete mode 100644 roles/nginx/files/snippets/proxy-nobuff.conf delete mode 100644 roles/nginx/files/snippets/security-headers.conf delete mode 100644 roles/nginx/files/snippets/ssl.conf delete mode 100644 roles/nginx/handlers/main.yml delete mode 100644 roles/nginx/tasks/acme.yml delete mode 100644 roles/nginx/tasks/main.yml delete mode 100644 roles/nginx/templates/generic-proxy-no-buffering-with-acme.conf.j2 create mode 100644 roles/nginx/vhost/defaults/main.yml create mode 100644 roles/nginx/vhost/handlers/main.yml create mode 100644 roles/nginx/vhost/tasks/acme.yml create mode 100644 roles/nginx/vhost/tasks/main.yml create mode 100644 roles/nginx/vhost/templates/generic-proxy-no-buffering-with-acme.conf.j2 (limited to 'roles') diff --git a/roles/elevate/liquidtruth/tasks/main.yml b/roles/elevate/liquidtruth/tasks/main.yml index d791c33f..4cb50dc9 100644 --- a/roles/elevate/liquidtruth/tasks/main.yml +++ b/roles/elevate/liquidtruth/tasks/main.yml @@ -23,16 +23,16 @@ - name: install and configure nodejs import_tasks: nodejs.yml -- name: install and configure nginx +- name: configure nginx vhost import_role: - name: nginx + name: nginx/vhost vars: - nginx_vhosts: - liquidtruth: - template: generic-proxy-no-buffering-with-acme - acme: true - hostnames: "{{ liquidtruth_hostnames }}" - proxy_pass: "http://127.0.0.1:8080" + nginx_vhost: + name: liquidtruth + template: generic-proxy-no-buffering-with-acme + acme: true + hostnames: "{{ liquidtruth_hostnames }}" + proxy_pass: "http://127.0.0.1:8080" - name: create app user user: diff --git a/roles/elevate/media/tasks/main.yml b/roles/elevate/media/tasks/main.yml index 77d53d23..f2d8b85d 100644 --- a/roles/elevate/media/tasks/main.yml +++ b/roles/elevate/media/tasks/main.yml @@ -20,15 +20,15 @@ - name: install and configure nextcloud import_tasks: nextcloud.yml -- name: install and configure nginx +- name: configure nginx vhost import_role: - name: nginx + name: nginx/vhost vars: - nginx_vhosts: - nextcloud: - content: "{{ lookup('template', 'nextcloud-nginx.conf.j2') }}" - acme: true - hostnames: "{{ nextcloud_hostnames }}" + nginx_vhost: + name: nextcloud + content: "{{ lookup('template', 'nextcloud-nginx.conf.j2') }}" + acme: true + hostnames: "{{ nextcloud_hostnames }}" - name: install dstat script template: diff --git a/roles/nextcloud/defaults/main.yml b/roles/nextcloud/defaults/main.yml index 0cd84485..16637f44 100644 --- a/roles/nextcloud/defaults/main.yml +++ b/roles/nextcloud/defaults/main.yml @@ -14,6 +14,7 @@ nextcloud_db_gid: "951" # nextcloud_instances: # example: +# new: yes # version: 17.0.0 # port: 8100 # hostnames: diff --git a/roles/nextcloud/tasks/main.yml b/roles/nextcloud/tasks/main.yml index 190afb47..0f9413b9 100644 --- a/roles/nextcloud/tasks/main.yml +++ b/roles/nextcloud/tasks/main.yml @@ -77,7 +77,7 @@ state: directory -- name: create image config dir +- name: create auxiliary config directory loop: "{{ nextcloud_instances | list }}" file: path: "{{ nextcloud_base_path }}/{{ item }}/config" @@ -85,33 +85,47 @@ - name: create apache vhost config loop: "{{ nextcloud_instances | list }}" - copy: - content: | - - ServerAdmin webmaster@localhost - DocumentRoot /var/www/html - - # SetEnv HTTPS on - # SetEnvIfNoCase X-Forwarded-Proto https HTTPS=on - - ErrorLog ${APACHE_LOG_DIR}/error.log - CustomLog ${APACHE_LOG_DIR}/access.log combined - + template: + src: apache-site.conf.j2 dest: "{{ nextcloud_base_path }}/{{ item }}/config/apache-site.conf" -- name: configure apache to run on port 8080 only +- name: configure apache to run on port 8443 only loop: "{{ nextcloud_instances | list }}" copy: content: | - Listen 8080 + Listen 8443 dest: "{{ nextcloud_base_path }}/{{ item }}/config/ports.conf" -- name: install cron trigger script + +- name: create tls cert/key directory loop: "{{ nextcloud_instances | list }}" - template: - src: run-cron.sh.j2 - dest: "{{ nextcloud_base_path }}/{{ item }}/config/run-cron.sh" - mode: 0755 + file: + path: "{{ nextcloud_base_path }}/{{ item }}/config/tls" + state: directory + +- name: generate tls private key for apache + loop: "{{ nextcloud_instances | list }}" + openssl_privatekey: + path: "{{ nextcloud_base_path }}/{{ item }}/config/tls/nextcloud.key" + mode: 0640 + owner: root + group: nc-app + +- name: generate csr for selfsigned certifacate + loop: "{{ nextcloud_instances | list }}" + openssl_csr: + path: "{{ nextcloud_base_path }}/{{ item }}/config/tls/nextcloud.csr" + privatekey_path: "{{ nextcloud_base_path }}/{{ item }}/config/tls/nextcloud.key" + common_name: "nextcloud-{{ item }}" + +## TODO: fix idempotence +- name: generate tls self-signed certificate for apache + loop: "{{ nextcloud_instances | list }}" + openssl_certificate: + path: "{{ nextcloud_base_path }}/{{ item }}/config/tls/nextcloud.crt" + privatekey_path: "{{ nextcloud_base_path }}/{{ item }}/config/tls/nextcloud.key" + csr_path: "{{ nextcloud_base_path }}/{{ item }}/config/tls/nextcloud.csr" + provider: selfsigned - name: generate pod manifests @@ -124,6 +138,13 @@ mode: 0600 +- name: install cron trigger script + loop: "{{ nextcloud_instances | list }}" + template: + src: run-cron.sh.j2 + dest: "{{ nextcloud_base_path }}/{{ item }}/config/run-cron.sh" + mode: 0755 + - name: install template systemd unit for cron trigger template: src: cron@.service.j2 @@ -142,3 +163,15 @@ name: "nextcloud-cron-{{ item }}.timer" state: started enabled: yes + + +- name: configure nginx vhost + loop: "{{ nextcloud_instances | dict2items }}" + include_role: + name: nginx/vhost + vars: + nginx_vhost: + name: "{{ item.key }}" + content: "{{ lookup('template', 'nginx-vhost.conf.j2') }}" + acme: true + hostnames: "{{ item.value.hostnames }}" diff --git a/roles/nextcloud/templates/apache-site.conf.j2 b/roles/nextcloud/templates/apache-site.conf.j2 new file mode 100644 index 00000000..457cdfd4 --- /dev/null +++ b/roles/nextcloud/templates/apache-site.conf.j2 @@ -0,0 +1,18 @@ +Include mods-available/socache_shmcb.load +Include mods-available/ssl.load +Include mods-available/ssl.conf + + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + UseCanonicalName Off + UseCanonicalPhysicalPort Off + + SSLEngine On + SSLCertificateFile /etc/apache2/tls/nextcloud.crt + SSLCertificateKeyFile /etc/apache2/tls/nextcloud.key + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/roles/nextcloud/templates/nginx-vhost.conf.j2 b/roles/nextcloud/templates/nginx-vhost.conf.j2 new file mode 100644 index 00000000..717c0dea --- /dev/null +++ b/roles/nextcloud/templates/nginx-vhost.conf.j2 @@ -0,0 +1,45 @@ +server { + listen 80; + listen [::]:80; + server_name {{ item.value.hostnames | join(' ') }}; + + include snippets/acmetool.conf; + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name {{ item.value.hostnames | join(' ') }}; + + include snippets/acmetool.conf; + include snippets/ssl.conf; + ssl_certificate /var/lib/acme/live/{{ item.value.hostnames[0] }}/fullchain; + ssl_certificate_key /var/lib/acme/live/{{ item.value.hostnames[0] }}/privkey; + include snippets/hsts.conf; + + location / { + include snippets/proxy-nobuff.conf; + client_max_body_size 512M; + + proxy_set_header Host $host:443; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-Port $server_port; + + # for websockets + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_ssl_trusted_certificate /srv/storage/nextcloud/{{ item.key }}/config/tls/nextcloud.crt; + proxy_ssl_verify on; + proxy_ssl_name nextcloud-{{ item.key }}; + + proxy_pass https://127.0.0.1:{{ item.value.port }}; + } +} diff --git a/roles/nextcloud/templates/pod-with-mariadb.yml.j2 b/roles/nextcloud/templates/pod-with-mariadb.yml.j2 index 7fa40cd4..92347a44 100644 --- a/roles/nextcloud/templates/pod-with-mariadb.yml.j2 +++ b/roles/nextcloud/templates/pod-with-mariadb.yml.j2 @@ -14,6 +14,7 @@ spec: resources: limits: memory: "4Gi" +{% if 'new' in item.value and item.value.new %} env: - name: NEXTCLOUD_TRUSTED_DOMAINS value: "{{ item.value.hostnames | join(' ') }}" @@ -25,6 +26,7 @@ spec: value: nextcloud - name: MYSQL_PASSWORD value: "{{ item.value.database.password }}" +{% endif %} volumeMounts: - name: nextcloud mountPath: /var/www/html @@ -36,8 +38,12 @@ spec: mountPath: /etc/apache2/ports.conf subPath: ports.conf readOnly: true + - name: config + mountPath: /etc/apache2/tls/ + subPath: tls + readOnly: true ports: - - containerPort: 8080 + - containerPort: 8443 hostPort: {{ item.value.port }} - name: database image: "mariadb:{{ item.value.database.version }}" @@ -50,6 +56,7 @@ spec: resources: limits: memory: "2Gi" +{% if 'new' in item.value and item.value.new %} env: - name: MYSQL_RANDOM_ROOT_PASSWORD value: "true" @@ -59,6 +66,7 @@ spec: value: nextcloud - name: MYSQL_PASSWORD value: "{{ item.value.database.password }}" +{% endif %} volumeMounts: - name: database mountPath: /var/lib/mysql diff --git a/roles/nginx/base/defaults/main.yml b/roles/nginx/base/defaults/main.yml new file mode 100644 index 00000000..50920f20 --- /dev/null +++ b/roles/nginx/base/defaults/main.yml @@ -0,0 +1,10 @@ +--- +nginx_pkg_variant: nginx-light + +nginx_conf_d_files: + - connection-upgrade + +nginx_snippets: + - ssl + - hsts + - proxy-nobuff diff --git a/roles/nginx/base/files/conf.d/connection-upgrade.conf b/roles/nginx/base/files/conf.d/connection-upgrade.conf new file mode 100644 index 00000000..4153effe --- /dev/null +++ b/roles/nginx/base/files/conf.d/connection-upgrade.conf @@ -0,0 +1,6 @@ +# used for websockets +# set http_connection to either upgrade or close (as normal) +map $http_upgrade $connection_upgrade { + default upgrade; + '' close; +} diff --git a/roles/nginx/base/files/snippets/hsts.conf b/roles/nginx/base/files/snippets/hsts.conf new file mode 100644 index 00000000..4ca8396e --- /dev/null +++ b/roles/nginx/base/files/snippets/hsts.conf @@ -0,0 +1 @@ +add_header Strict-Transport-Security max-age=15768000; diff --git a/roles/nginx/base/files/snippets/proxy-nobuff.conf b/roles/nginx/base/files/snippets/proxy-nobuff.conf new file mode 100644 index 00000000..b08de70c --- /dev/null +++ b/roles/nginx/base/files/snippets/proxy-nobuff.conf @@ -0,0 +1,4 @@ +proxy_buffering off; +proxy_ignore_headers "X-Accel-Buffering"; +proxy_request_buffering off; +proxy_http_version 1.1; diff --git a/roles/nginx/base/files/snippets/security-headers.conf b/roles/nginx/base/files/snippets/security-headers.conf new file mode 100644 index 00000000..b94d479d --- /dev/null +++ b/roles/nginx/base/files/snippets/security-headers.conf @@ -0,0 +1,4 @@ +add_header X-Frame-Options DENY; +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block"; +# add_header Content-Security-Policy "default-src 'none'; connect-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'"; diff --git a/roles/nginx/base/files/snippets/ssl.conf b/roles/nginx/base/files/snippets/ssl.conf new file mode 100644 index 00000000..d187a7c0 --- /dev/null +++ b/roles/nginx/base/files/snippets/ssl.conf @@ -0,0 +1,10 @@ +ssl_protocols TLSv1 TLSv1.1 TLSv1.2; +ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AES:!ADH:!AECDH:!MD5; +ssl_prefer_server_ciphers on; + +# openssl dhparam -out /etc/ssl/certs/dhparams.pem 2048 +ssl_dhparam /etc/ssl/dhparams.pem; + +ssl_session_cache shared:SSL:10m; +ssl_session_timeout 10m; +ssl_session_tickets off; diff --git a/roles/nginx/base/handlers/main.yml b/roles/nginx/base/handlers/main.yml new file mode 100644 index 00000000..6deed0cd --- /dev/null +++ b/roles/nginx/base/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: restart nginx + service: + name: nginx + state: restarted diff --git a/roles/nginx/base/tasks/main.yml b/roles/nginx/base/tasks/main.yml new file mode 100644 index 00000000..a975ce52 --- /dev/null +++ b/roles/nginx/base/tasks/main.yml @@ -0,0 +1,31 @@ +--- +- name: install nginx + apt: + name: "{{ nginx_pkg_variant }}" + state: present + +- name: remove nginx default config + file: + name: /etc/nginx/sites-enabled/default + state: absent + notify: restart nginx + +- name: install nginx config.d files + loop: "{{ nginx_conf_d_files }}" + copy: + src: "conf.d/{{ item }}.conf" + dest: /etc/nginx/conf.d/ + notify: restart nginx + +- name: install nginx config snippets + loop: "{{ nginx_snippets }}" + copy: + src: "snippets/{{ item }}.conf" + dest: /etc/nginx/snippets/ + notify: restart nginx + +- name: generate Diffie-Hellman parameters + openssl_dhparam: + path: /etc/ssl/dhparams.pem + size: 2048 + notify: restart nginx diff --git a/roles/nginx/defaults/main.yml b/roles/nginx/defaults/main.yml deleted file mode 100644 index a38a95a0..00000000 --- a/roles/nginx/defaults/main.yml +++ /dev/null @@ -1,21 +0,0 @@ ---- -nginx_pkg_variant: nginx-light - -nginx_conf_d_files: - - connection-upgrade - -nginx_snippets: - - ssl - - hsts - - proxy-nobuff - -# nginx_vhosts: -# example: -# template: generic-proxy-no-buffering-with-acme -# acme: yes -# hostnames: -# - example.com -# - www.example.com -# proxy_pass: http://127.0.0.1:8080 -# other.io: -# content: "<< nginx vhost config file contents >>" diff --git a/roles/nginx/files/conf.d/connection-upgrade.conf b/roles/nginx/files/conf.d/connection-upgrade.conf deleted file mode 100644 index 4153effe..00000000 --- a/roles/nginx/files/conf.d/connection-upgrade.conf +++ /dev/null @@ -1,6 +0,0 @@ -# used for websockets -# set http_connection to either upgrade or close (as normal) -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -} diff --git a/roles/nginx/files/snippets/hsts.conf b/roles/nginx/files/snippets/hsts.conf deleted file mode 100644 index 4ca8396e..00000000 --- a/roles/nginx/files/snippets/hsts.conf +++ /dev/null @@ -1 +0,0 @@ -add_header Strict-Transport-Security max-age=15768000; diff --git a/roles/nginx/files/snippets/proxy-nobuff.conf b/roles/nginx/files/snippets/proxy-nobuff.conf deleted file mode 100644 index b08de70c..00000000 --- a/roles/nginx/files/snippets/proxy-nobuff.conf +++ /dev/null @@ -1,4 +0,0 @@ -proxy_buffering off; -proxy_ignore_headers "X-Accel-Buffering"; -proxy_request_buffering off; -proxy_http_version 1.1; diff --git a/roles/nginx/files/snippets/security-headers.conf b/roles/nginx/files/snippets/security-headers.conf deleted file mode 100644 index b94d479d..00000000 --- a/roles/nginx/files/snippets/security-headers.conf +++ /dev/null @@ -1,4 +0,0 @@ -add_header X-Frame-Options DENY; -add_header X-Content-Type-Options nosniff; -add_header X-XSS-Protection "1; mode=block"; -# add_header Content-Security-Policy "default-src 'none'; connect-src 'self'; img-src 'self'; script-src 'self'; style-src 'self'"; diff --git a/roles/nginx/files/snippets/ssl.conf b/roles/nginx/files/snippets/ssl.conf deleted file mode 100644 index d187a7c0..00000000 --- a/roles/nginx/files/snippets/ssl.conf +++ /dev/null @@ -1,10 +0,0 @@ -ssl_protocols TLSv1 TLSv1.1 TLSv1.2; -ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AES:!ADH:!AECDH:!MD5; -ssl_prefer_server_ciphers on; - -# openssl dhparam -out /etc/ssl/certs/dhparams.pem 2048 -ssl_dhparam /etc/ssl/dhparams.pem; - -ssl_session_cache shared:SSL:10m; -ssl_session_timeout 10m; -ssl_session_tickets off; diff --git a/roles/nginx/handlers/main.yml b/roles/nginx/handlers/main.yml deleted file mode 100644 index 6deed0cd..00000000 --- a/roles/nginx/handlers/main.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- name: restart nginx - service: - name: nginx - state: restarted diff --git a/roles/nginx/tasks/acme.yml b/roles/nginx/tasks/acme.yml deleted file mode 100644 index b8ab7879..00000000 --- a/roles/nginx/tasks/acme.yml +++ /dev/null @@ -1,44 +0,0 @@ ---- -- name: check if acme certs already exist - loop: "{{ item.value.hostnames }}" - loop_control: - loop_var: acme_hostname - stat: - path: "/var/lib/acme/live/{{ acme_hostname }}" - register: acme_cert_stat - -- name: set acmecert_missing_hostnames variable - set_fact: - acmecert_missing_hostnames: "{{ acme_cert_stat.results | acme_cert_nonexistent(item.value.hostnames) }}" - -- name: link nonexistent hostnames to self-signed interim cert - when: acmecert_missing_hostnames | length > 0 - block: - - name: get id of existing selfsigned interim certificate - command: cat /var/lib/acme/.selfsigned-interim-cert - changed_when: false - check_mode: false - register: selfsigned_interim_cert_id - - - name: set selfsigned_interim_cert_id variable - set_fact: - selfsigned_interim_cert_id: "{{ selfsigned_interim_cert_id.stdout }}" - - - name: link to snakeoil cert for nonexistent hostnames - loop: "{{ acmecert_missing_hostnames }}" - loop_control: - loop_var: acme_missing_hostname - file: - src: "../certs/{{ selfsigned_interim_cert_id }}" - dest: "/var/lib/acme/live/{{ acme_missing_hostname }}" - state: link - -- name: make sure nginx config has been (re)loaded - meta: flush_handlers - -- name: get certificate using acmetool - import_role: - name: acmetool/cert - vars: - acmetool_cert_name: "{{ item.value.hostnames[0] }}" - acmetool_cert_hostnames: "{{ item.value.hostnames }}" diff --git a/roles/nginx/tasks/main.yml b/roles/nginx/tasks/main.yml deleted file mode 100644 index 57816cea..00000000 --- a/roles/nginx/tasks/main.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -- name: install nginx - apt: - name: "{{ nginx_pkg_variant }}" - state: present - -- name: remove nginx default config - file: - name: /etc/nginx/sites-enabled/default - state: absent - notify: restart nginx - -- name: install nginx config.d files - loop: "{{ nginx_conf_d_files }}" - copy: - src: "conf.d/{{ item }}.conf" - dest: /etc/nginx/conf.d/ - notify: restart nginx - -- name: install nginx config snippets - loop: "{{ nginx_snippets }}" - copy: - src: "snippets/{{ item }}.conf" - dest: /etc/nginx/snippets/ - notify: restart nginx - -- name: generate Diffie-Hellman parameters - openssl_dhparam: - path: /etc/ssl/dhparams.pem - size: 2048 - notify: restart nginx - -- name: install nginx configs from template - loop: "{{ nginx_vhosts | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: "'template' in item.value" - template: - src: "{{ item.value.template }}.conf.j2" - dest: "/etc/nginx/sites-available/{{ item.key }}" - notify: restart nginx - -- name: install nginx configs from config data - loop: "{{ nginx_vhosts | dict2items }}" - loop_control: - label: "{{ item.key }}" - when: "'content' in item.value" - copy: - content: "{{ item.value.content }}" - dest: "/etc/nginx/sites-available/{{ item.key }}" - notify: restart nginx - -- name: enable vhost config - loop: "{{ nginx_vhosts | dict2items }}" - loop_control: - label: "{{ item.key }}" - file: - src: "../sites-available/{{ item.key }}" - dest: "/etc/nginx/sites-enabled/{{ item.key }}" - state: link - notify: restart nginx - -- name: generate acme certificate - loop: "{{ nginx_vhosts | dict2items }}" - loop_control: - label: "{{ item.key }} ({{ item.value.hostnames | default([]) | join(', ') }})" - when: "'acme' in item.value and item.value.acme" - include_tasks: acme.yml diff --git a/roles/nginx/templates/generic-proxy-no-buffering-with-acme.conf.j2 b/roles/nginx/templates/generic-proxy-no-buffering-with-acme.conf.j2 deleted file mode 100644 index 9f165726..00000000 --- a/roles/nginx/templates/generic-proxy-no-buffering-with-acme.conf.j2 +++ /dev/null @@ -1,43 +0,0 @@ -server { - listen 80; - listen [::]:80; - server_name {{ item.value.hostnames | join(' ') }}; - - include snippets/acmetool.conf; - - location / { - return 301 https://$host$request_uri; - } -} - -server { - listen 443 ssl http2; - listen [::]:443 ssl http2; - server_name {{ item.value.hostnames | join(' ') }}; - - include snippets/acmetool.conf; - include snippets/ssl.conf; - ssl_certificate /var/lib/acme/live/{{ item.value.hostnames[0] }}/fullchain; - ssl_certificate_key /var/lib/acme/live/{{ item.value.hostnames[0] }}/privkey; - include snippets/hsts.conf; - - location / { - include snippets/proxy-nobuff.conf; -{% if 'client_max_body_size' in item.value %} - client_max_body_size {{ item.value.client_max_body_size }}; -{% endif %} - - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Ssl on; - proxy_set_header X-Forwarded-Port $server_port; - - # for websockets - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - - proxy_pass {{ item.value.proxy_pass }}; - } -} diff --git a/roles/nginx/vhost/defaults/main.yml b/roles/nginx/vhost/defaults/main.yml new file mode 100644 index 00000000..dfedb50b --- /dev/null +++ b/roles/nginx/vhost/defaults/main.yml @@ -0,0 +1,13 @@ +--- +# nginx_vhost: +# name: example +# template: generic-proxy-no-buffering-with-acme +# acme: yes +# hostnames: +# - example.com +# - www.example.com +# proxy_pass: http://127.0.0.1:8080 + +# nginx_vhost: +# name: other-example +# content: "<<< content of vhost >>>" diff --git a/roles/nginx/vhost/handlers/main.yml b/roles/nginx/vhost/handlers/main.yml new file mode 100644 index 00000000..d4e42ca0 --- /dev/null +++ b/roles/nginx/vhost/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: reload nginx + service: + name: nginx + state: reloaded diff --git a/roles/nginx/vhost/tasks/acme.yml b/roles/nginx/vhost/tasks/acme.yml new file mode 100644 index 00000000..99ad7856 --- /dev/null +++ b/roles/nginx/vhost/tasks/acme.yml @@ -0,0 +1,44 @@ +--- +- name: check if acme certs already exist + loop: "{{ nginx_vhost.hostnames }}" + loop_control: + loop_var: acme_hostname + stat: + path: "/var/lib/acme/live/{{ acme_hostname }}" + register: acme_cert_stat + +- name: set acmecert_missing_hostnames variable + set_fact: + acmecert_missing_hostnames: "{{ acme_cert_stat.results | acme_cert_nonexistent(nginx_vhost.hostnames) }}" + +- name: link nonexistent hostnames to self-signed interim cert + when: acmecert_missing_hostnames | length > 0 + block: + - name: get id of existing selfsigned interim certificate + command: cat /var/lib/acme/.selfsigned-interim-cert + changed_when: false + check_mode: false + register: selfsigned_interim_cert_id + + - name: set selfsigned_interim_cert_id variable + set_fact: + selfsigned_interim_cert_id: "{{ selfsigned_interim_cert_id.stdout }}" + + - name: link to snakeoil cert for nonexistent hostnames + loop: "{{ acmecert_missing_hostnames }}" + loop_control: + loop_var: acme_missing_hostname + file: + src: "../certs/{{ selfsigned_interim_cert_id }}" + dest: "/var/lib/acme/live/{{ acme_missing_hostname }}" + state: link + +- name: make sure nginx config has been (re)loaded + meta: flush_handlers + +- name: get certificate using acmetool + import_role: + name: acmetool/cert + vars: + acmetool_cert_name: "{{ nginx_vhost.hostnames[0] }}" + acmetool_cert_hostnames: "{{ nginx_vhost.hostnames }}" diff --git a/roles/nginx/vhost/tasks/main.yml b/roles/nginx/vhost/tasks/main.yml new file mode 100644 index 00000000..4de3393d --- /dev/null +++ b/roles/nginx/vhost/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- name: install nginx configs from template + when: "'template' in nginx_vhost" + template: + src: "{{ nginx_vhost.template }}.conf.j2" + dest: "/etc/nginx/sites-available/{{ nginx_vhost.name }}" + notify: reload nginx + +- name: install nginx configs from config data + when: "'content' in nginx_vhost" + copy: + content: "{{ nginx_vhost.content }}" + dest: "/etc/nginx/sites-available/{{ nginx_vhost.name }}" + notify: reload nginx + +- name: enable vhost config + file: + src: "../sites-available/{{ nginx_vhost.name }}" + dest: "/etc/nginx/sites-enabled/{{ nginx_vhost.name }}" + state: link + notify: reload nginx + +- name: generate acme certificate + when: "'acme' in nginx_vhost and nginx_vhost.acme" + include_tasks: acme.yml diff --git a/roles/nginx/vhost/templates/generic-proxy-no-buffering-with-acme.conf.j2 b/roles/nginx/vhost/templates/generic-proxy-no-buffering-with-acme.conf.j2 new file mode 100644 index 00000000..55bd5ac6 --- /dev/null +++ b/roles/nginx/vhost/templates/generic-proxy-no-buffering-with-acme.conf.j2 @@ -0,0 +1,43 @@ +server { + listen 80; + listen [::]:80; + server_name {{ nginx_vhost.hostnames | join(' ') }}; + + include snippets/acmetool.conf; + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name {{ nginx_vhost.hostnames | join(' ') }}; + + include snippets/acmetool.conf; + include snippets/ssl.conf; + ssl_certificate /var/lib/acme/live/{{ nginx_vhost.hostnames[0] }}/fullchain; + ssl_certificate_key /var/lib/acme/live/{{ nginx_vhost.hostnames[0] }}/privkey; + include snippets/hsts.conf; + + location / { + include snippets/proxy-nobuff.conf; +{% if 'client_max_body_size' in nginx_vhost %} + client_max_body_size {{ nginx_vhost.client_max_body_size }}; +{% endif %} + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Ssl on; + proxy_set_header X-Forwarded-Port $server_port; + + # for websockets + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_pass {{ nginx_vhost.proxy_pass }}; + } +} -- cgit v1.2.3