diff --git a/group_vars/all/helpers.yml b/group_vars/all/helpers.yml index 29d2c29d8a..dd5a9e0451 100644 --- a/group_vars/all/helpers.yml +++ b/group_vars/all/helpers.yml @@ -18,7 +18,9 @@ multisite_subdomains_wildcards: "{{ item.value.multisite.subdomains | default(fa ssl_enabled: "{{ item.value.ssl is defined and item.value.ssl.enabled | default(false) }}" ssl_stapling_enabled: "{{ item.value.ssl is defined and item.value.ssl.stapling_enabled | default(true) }}" cron_enabled: "{{ site_env.disable_wp_cron and (not item.value.multisite.enabled | default(false) or (item.value.multisite.enabled | default(false) and item.value.multisite.cron | default(true))) }}" -sites_use_ssl: "{{ wordpress_sites.values() | map(attribute='ssl') | selectattr('enabled') | list | count > 0 }}" +sites_using_ssl: "{{ wordpress_sites | dict2items | selectattr('value.ssl.enabled', 'equalto', true) | items2dict }}" +first_site_using_ssl: "{{ (sites_using_ssl | dict2items | first | default(None, True)) }}" +sites_use_ssl: "{{ sites_using_ssl | length > 0 }}" composer_authentications: "{{ vault_wordpress_sites[site].composer_authentications | default([]) }}" # Default `type` is `http-basic`. diff --git a/group_vars/all/security.yml b/group_vars/all/security.yml index 57c2bcdd7d..e9d7809b49 100644 --- a/group_vars/all/security.yml +++ b/group_vars/all/security.yml @@ -8,6 +8,11 @@ ferm_input_list: dport: [https] filename: nginx_accept_https delete: "{{ not (sites_use_ssl | bool) }}" + - type: dport_accept + dport: ['443'] + protocol: udp + filename: nginx_accept_http3 + delete: "{{ not (nginx_http3_enabled and (sites_use_ssl | bool)) }}" - type: dport_accept dport: [ssh] saddr: "{{ ip_whitelist }}" diff --git a/roles/wordpress-setup/tasks/nginx.yml b/roles/wordpress-setup/tasks/nginx.yml index 9f81d32c23..4188bf568a 100644 --- a/roles/wordpress-setup/tasks/nginx.yml +++ b/roles/wordpress-setup/tasks/nginx.yml @@ -23,6 +23,13 @@ - import_tasks: "{{ playbook_dir }}/roles/common/tasks/disable_challenge_sites.yml" +- name: Copy Nginx Wordpress site include folder + copy: + src: templates/includes + dest: "{{ nginx_path }}" + mode: '0755' + notify: reload nginx + - name: Create Nginx available sites template: src: "{{ item.src }}" @@ -68,6 +75,8 @@ loop: "{{ wordpress_sites | dict2items }}" loop_control: label: "{{ item.key }}" + vars: + is_first_site_use_ssl: "{{ first_site_using_ssl.key == item.key }}" notify: reload nginx tags: nginx-includes diff --git a/roles/wordpress-setup/templates/includes/directive-only/http3-negotiate-redirect.conf b/roles/wordpress-setup/templates/includes/directive-only/http3-negotiate-redirect.conf new file mode 100644 index 0000000000..7f06e32b83 --- /dev/null +++ b/roles/wordpress-setup/templates/includes/directive-only/http3-negotiate-redirect.conf @@ -0,0 +1,2 @@ +# Add Alt-Svc header to negotiate HTTP/3 (when redirecting from HTTP). +add_header alt-svc 'h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400, h3=":443"; ma=86400'; diff --git a/roles/wordpress-setup/templates/includes/directive-only/http3-negotiate.conf b/roles/wordpress-setup/templates/includes/directive-only/http3-negotiate.conf new file mode 100644 index 0000000000..4fb2ef3abb --- /dev/null +++ b/roles/wordpress-setup/templates/includes/directive-only/http3-negotiate.conf @@ -0,0 +1,2 @@ +# Add Alt-Svc header to negotiate HTTP/3. +add_header alt-svc 'h3=":443"; ma=86400'; diff --git a/roles/wordpress-setup/templates/includes/directive-only/http3-tune.conf b/roles/wordpress-setup/templates/includes/directive-only/http3-tune.conf new file mode 100644 index 0000000000..dae5850fae --- /dev/null +++ b/roles/wordpress-setup/templates/includes/directive-only/http3-tune.conf @@ -0,0 +1,7 @@ +http3_hq on; +quic_retry on; +quic_gso on; + +# enable 0-RTT +#ssl_early_data on; +#proxy_set_header Early-Data $ssl_early_data; diff --git a/roles/wordpress-setup/templates/wordpress-site.conf.j2 b/roles/wordpress-setup/templates/wordpress-site.conf.j2 index 95b406439a..a88b3cd17f 100644 --- a/roles/wordpress-setup/templates/wordpress-site.conf.j2 +++ b/roles/wordpress-setup/templates/wordpress-site.conf.j2 @@ -6,6 +6,15 @@ server { {% block server_id -%} listen {{ ssl_enabled | ternary('[::]:443 ssl', '[::]:80') }}; listen {{ ssl_enabled | ternary('443 ssl', '80') }}; + + {% if nginx_http3_enabled and ssl_enabled -%} + # Listen on UDP for QUIC+HTTP/3 + listen [::]:443 quic{% if is_first_site_use_ssl %} reuseport{% endif -%}; + listen 443 quic{% if is_first_site_use_ssl %} reuseport{% endif -%}; + {% if is_first_site_use_ssl -%}# there has to be one listen quic directive with reusport for working QUIC responses in current nginx version, using the first site. + {% endif %} + {% endif %} + http2 {{ nginx_http2_enabled | default(false) | ternary('on', 'off') }}; http3 {{ nginx_http3_enabled | default(false) | ternary('on', 'off') }}; server_name {{ site_hosts_canonical | union(multisite_subdomains_wildcards) | join(' ') }}; @@ -32,6 +41,11 @@ server { sendfile off; {% endif -%} + + {% if nginx_http3_enabled and ssl_enabled -%} + include includes/directive-only/http3-tune.conf; + include includes/directive-only/http3-negotiate.conf; + {% endif %} {% endblock -%} {% block cache_conditions -%} @@ -277,6 +291,10 @@ server { {{ self.includes_d() -}} + {% if nginx_http3_enabled and ssl_enabled -%} + include includes/directive-only/http3-negotiate-redirect.conf; + {% endif %} + location / { return 301 https://$host$request_uri; } @@ -295,12 +313,25 @@ server { listen [::]:443 ssl; listen 443 ssl; {% endif -%} + listen [::]:80; listen 80; + + {% if nginx_http3_enabled and ssl_enabled -%} + # Listen on UDP for QUIC+HTTP/3 + listen [::]:443 quic; + listen 443 quic; + {% endif %} + http2 {{ nginx_http2_enabled | default(false) | ternary('on', 'off') }}; http3 {{ nginx_http3_enabled | default(false) | ternary('on', 'off') }}; server_name {{ host.redirects | join(' ') }}; + {% if nginx_http3_enabled and ssl_enabled -%} + include includes/directive-only/http3-tune.conf; + include includes/directive-only/http3-negotiate-redirect.conf; + {% endif %} + {{ self.https() -}} {{ self.acme_challenge() -}}