diff options
26 files changed, 250 insertions, 165 deletions
diff --git a/dan/ele-lt.yml b/dan/ele-lt.yml index 876d0d16..5100cfb2 100644 --- a/dan/ele-lt.yml +++ b/dan/ele-lt.yml @@ -6,4 +6,5 @@ - role: sshd - role: zsh - role: acmetool/base + - role: nginx/base - role: elevate/liquidtruth diff --git a/dan/sk-cloudia.yml b/dan/sk-cloudia.yml index cd87c77e..c8281bb1 100644 --- a/dan/sk-cloudia.yml +++ b/dan/sk-cloudia.yml @@ -11,5 +11,5 @@ - role: kubernetes/base - role: kubernetes/standalone - role: acmetool/base - - role: nginx + - role: nginx/base - role: nextcloud diff --git a/inventory/host_vars/sk-cloudia/nextcloud.yml b/inventory/host_vars/sk-cloudia/nextcloud.yml index a39c21dd..dce6e4fe 100644 --- a/inventory/host_vars/sk-cloudia/nextcloud.yml +++ b/inventory/host_vars/sk-cloudia/nextcloud.yml @@ -6,22 +6,25 @@ nextcloud_zfs: nextcloud_instances: wolke.elevate.at: + new: yes version: 15.0.11 port: 8100 hostnames: - - wolke.elevate.at + - wolke.elev8.at +# - wolke.elevate.at quota: 300G database: type: mariadb version: 10.4.8 password: "{{ vault_nextcloud_database_passwords['wolke.elevate.at'] }}" - insomnia.skillz.biz: - version: 16.0.5 - port: 8101 - hostnames: - - insomnia.skillz.biz - quota: 200G - database: - type: mariadb - version: 10.4.8 - password: "{{ vault_nextcloud_database_passwords['insomnia.skillz.biz'] }}" + # insomnia.skillz.biz: + # new: yes + # version: 16.0.5 + # port: 8101 + # hostnames: + # - insomnia.skillz.biz + # quota: 200G + # database: + # type: mariadb + # version: 10.4.8 + # password: "{{ vault_nextcloud_database_passwords['insomnia.skillz.biz'] }}" diff --git a/inventory/host_vars/sk-cloudia/vars.yml b/inventory/host_vars/sk-cloudia/vars.yml index cb5b47a8..f239ad4a 100644 --- a/inventory/host_vars/sk-cloudia/vars.yml +++ b/inventory/host_vars/sk-cloudia/vars.yml @@ -36,22 +36,3 @@ kubernetes_standalone_cni_variant: with-localonly-portmap acmetool_directory_server: "{{ acmetool_directory_server_le_live }}" - - -### TODO: should this be done via the nextcloud role? -nginx_vhosts: - wolke.elevate.at: - template: generic-proxy-no-buffering-with-acme - client_max_body_size: 0 - acme: true - hostnames: - - wolke.elev8.at - # - wolke.elevate.at - proxy_pass: "http://127.0.0.1:8100" - # insomnia.skillz.biz: - # template: generic-proxy-no-buffering-with-acme - # client_max_body_size: 0 - # acme: true - # hostnames: - # - insomnia.skillz.biz - # proxy_pass: "http://127.0.0.1:8101" 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: | - <VirtualHost *:8080> - 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 - </VirtualHost> + 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 + +<VirtualHost *:8443> + 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 +</VirtualHost> diff --git a/roles/nginx/templates/generic-proxy-no-buffering-with-acme.conf.j2 b/roles/nextcloud/templates/nginx-vhost.conf.j2 index 9f165726..717c0dea 100644 --- a/roles/nginx/templates/generic-proxy-no-buffering-with-acme.conf.j2 +++ b/roles/nextcloud/templates/nginx-vhost.conf.j2 @@ -23,11 +23,9 @@ server { 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 %} + client_max_body_size 512M; - proxy_set_header Host $host; + 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; @@ -38,6 +36,10 @@ server { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - proxy_pass {{ item.value.proxy_pass }}; + 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/files/conf.d/connection-upgrade.conf b/roles/nginx/base/files/conf.d/connection-upgrade.conf index 4153effe..4153effe 100644 --- a/roles/nginx/files/conf.d/connection-upgrade.conf +++ b/roles/nginx/base/files/conf.d/connection-upgrade.conf diff --git a/roles/nginx/files/snippets/hsts.conf b/roles/nginx/base/files/snippets/hsts.conf index 4ca8396e..4ca8396e 100644 --- a/roles/nginx/files/snippets/hsts.conf +++ b/roles/nginx/base/files/snippets/hsts.conf diff --git a/roles/nginx/files/snippets/proxy-nobuff.conf b/roles/nginx/base/files/snippets/proxy-nobuff.conf index b08de70c..b08de70c 100644 --- a/roles/nginx/files/snippets/proxy-nobuff.conf +++ b/roles/nginx/base/files/snippets/proxy-nobuff.conf diff --git a/roles/nginx/files/snippets/security-headers.conf b/roles/nginx/base/files/snippets/security-headers.conf index b94d479d..b94d479d 100644 --- a/roles/nginx/files/snippets/security-headers.conf +++ b/roles/nginx/base/files/snippets/security-headers.conf diff --git a/roles/nginx/files/snippets/ssl.conf b/roles/nginx/base/files/snippets/ssl.conf index d187a7c0..d187a7c0 100644 --- a/roles/nginx/files/snippets/ssl.conf +++ b/roles/nginx/base/files/snippets/ssl.conf diff --git a/roles/nginx/handlers/main.yml b/roles/nginx/base/handlers/main.yml index 6deed0cd..6deed0cd 100644 --- a/roles/nginx/handlers/main.yml +++ b/roles/nginx/base/handlers/main.yml 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/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/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/tasks/acme.yml b/roles/nginx/vhost/tasks/acme.yml index b8ab7879..99ad7856 100644 --- a/roles/nginx/tasks/acme.yml +++ b/roles/nginx/vhost/tasks/acme.yml @@ -1,6 +1,6 @@ --- - name: check if acme certs already exist - loop: "{{ item.value.hostnames }}" + loop: "{{ nginx_vhost.hostnames }}" loop_control: loop_var: acme_hostname stat: @@ -9,7 +9,7 @@ - name: set acmecert_missing_hostnames variable set_fact: - acmecert_missing_hostnames: "{{ acme_cert_stat.results | acme_cert_nonexistent(item.value.hostnames) }}" + 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 @@ -40,5 +40,5 @@ import_role: name: acmetool/cert vars: - acmetool_cert_name: "{{ item.value.hostnames[0] }}" - acmetool_cert_hostnames: "{{ item.value.hostnames }}" + 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 }}; + } +} |