From c6e8bb5d26bb9bf85ded4902f409204c9d3825bc Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 30 Oct 2019 09:07:58 +0100 Subject: [PATCH 01/13] Always expect subdomain xmpp-upload.domain.net. This subdomain will be part of Letsencrypt certificate so it MUST be defined in DNS zone otherwise certificate renewal will fail. --- data/templates/ssl/openssl.cnf | 2 +- src/yunohost/certificate.py | 3 +++ src/yunohost/domain.py | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/data/templates/ssl/openssl.cnf b/data/templates/ssl/openssl.cnf index fa5d19fa3..3ef7d80c3 100644 --- a/data/templates/ssl/openssl.cnf +++ b/data/templates/ssl/openssl.cnf @@ -192,7 +192,7 @@ authorityKeyIdentifier=keyid,issuer basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment -subjectAltName=DNS:yunohost.org,DNS:www.yunohost.org,DNS:ns.yunohost.org +subjectAltName=DNS:yunohost.org,DNS:www.yunohost.org,DNS:ns.yunohost.org,DNS:xmpp-upload.yunohost.org [ v3_ca ] diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index d141ac8e5..9b50749ea 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -639,6 +639,9 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): # Set the domain csr.get_subject().CN = domain + # Include xmpp-upload subdomain as subject alternate names + csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:xmpp-upload." + domain)]) + # Set the key with open(key_file, 'rt') as f: key = crypto.load_privatekey(crypto.FILETYPE_PEM, f.read()) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 8f8a68812..5037e9334 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -412,6 +412,7 @@ def _build_dns_conf(domain, ttl=3600): {"type": "CNAME", "name": "muc", "value": "@", "ttl": 3600}, {"type": "CNAME", "name": "pubsub", "value": "@", "ttl": 3600}, {"type": "CNAME", "name": "vjud", "value": "@", "ttl": 3600} + {"type": "CNAME", "name": "xmpp-upload", "value": "@", "ttl": 3600} ], "mail": [ {"type": "MX", "name": "@", "value": "10 domain.tld.", "ttl": 3600}, @@ -453,6 +454,7 @@ def _build_dns_conf(domain, ttl=3600): ["muc", ttl, "CNAME", "@"], ["pubsub", ttl, "CNAME", "@"], ["vjud", ttl, "CNAME", "@"], + ["xmpp-upload", ttl, "CNAME", "@"], ] # SPF record From 994f0ca1efa6439a5338f9fc35b9db31c179b04d Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 30 Oct 2019 18:14:25 +0100 Subject: [PATCH 02/13] nginx + metronome config for http_upload --- data/hooks/conf_regen/12-metronome | 4 ++ data/templates/metronome/metronome.cfg.lua | 9 ++- data/templates/nginx/server.tpl.conf | 83 +++++++++++++++++++++- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index fbd956e7c..db5910620 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -47,6 +47,10 @@ do_post_regen() { # create metronome directories for domains for domain in $domain_list; do mkdir -p "/var/lib/metronome/${domain//./%2e}/pep" + # http_upload directory must be writable by metronome and readable by nginx + mkdir -p "/var/www/xmpp-upload.${domain}/upload" + chmod g+s "/var/www/xmpp-upload.${domain}/upload" + chown -R metronome:www-data "/var/www/xmpp-upload.${domain}" done # fix some permissions diff --git a/data/templates/metronome/metronome.cfg.lua b/data/templates/metronome/metronome.cfg.lua index 0640ef9d5..c1420c7fb 100644 --- a/data/templates/metronome/metronome.cfg.lua +++ b/data/templates/metronome/metronome.cfg.lua @@ -85,7 +85,7 @@ use_ipv6 = true disco_items = { { "muc.{{ main_domain }}" }, { "pubsub.{{ main_domain }}" }, - { "upload.{{ main_domain }}" }, + { "xmpp-upload.{{ main_domain }}" }, { "vjud.{{ main_domain }}" } }; @@ -141,11 +141,16 @@ Component "pubsub.{{ main_domain }}" "pubsub" unrestricted_node_creation = true -- Anyone can create a PubSub node (from any server) ---Set up a HTTP Upload service -Component "upload.{{ main_domain }}" "http_upload" +Component "xmpp-upload.{{ main_domain }}" "http_upload" name = "{{ main_domain }} Sharing Service" + http_file_path = "/var/www/xmpp-upload.{{ main_domain }}/upload" + http_external_url = "https://xmpp-upload.{{ main_domain }}:443" + http_file_base_path = "/upload" http_file_size_limit = 6*1024*1024 http_file_quota = 60*1024*1024 + http_upload_file_size_limit = 100 * 1024 * 1024 -- bytes + http_upload_quota = 10 * 1024 * 1024 * 1024 -- bytes ---Set up a VJUD service diff --git a/data/templates/nginx/server.tpl.conf b/data/templates/nginx/server.tpl.conf index 9acc6c0fd..dac188ea3 100644 --- a/data/templates/nginx/server.tpl.conf +++ b/data/templates/nginx/server.tpl.conf @@ -6,7 +6,7 @@ map $http_upgrade $connection_upgrade { server { listen 80; listen [::]:80; - server_name {{ domain }}; + server_name {{ domain }} xmpp-upload.{{ domain }}; access_by_lua_file /usr/share/ssowat/access.lua; @@ -97,3 +97,84 @@ server { access_log /var/log/nginx/{{ domain }}-access.log; error_log /var/log/nginx/{{ domain }}-error.log; } + +# vhost dedicated to XMPP http_upload +server { + listen 443 ssl http2; + listen [::]:443 ssl http2; + server_name xmpp-upload.{{ domain }}; + + location /upload { + alias /var/www/xmpp-upload.{{ domain }}/upload; + # Pass all requests to metronome, except for GET and HEAD requests. + limit_except GET HEAD { + proxy_pass http://localhost:5290; + } + + include proxy_params; + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'HEAD, GET, PUT, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Authorization'; + add_header 'Access-Control-Allow-Credentials' 'true'; + client_max_body_size 105M; # Choose a value a bit higher than the max upload configured in XMPP server + } + + ssl_certificate /etc/yunohost/certs/{{ domain }}/crt.pem; + ssl_certificate_key /etc/yunohost/certs/{{ domain }}/key.pem; + ssl_session_timeout 5m; + ssl_session_cache shared:SSL:50m; + + {% if compatibility == "modern" %} + # Ciphers with modern compatibility + # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern + # The following configuration use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...) + ssl_protocols TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + {% else %} + # As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519 + ssl_ecdh_curve secp521r1:secp384r1:prime256v1; + ssl_prefer_server_ciphers on; + + # Ciphers with intermediate compatibility + # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=intermediate + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; + + # Uncomment the following directive after DH generation + # > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048 + #ssl_dhparam /etc/ssl/private/dh2048.pem; + {% endif %} + + # Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners + # https://wiki.mozilla.org/Security/Guidelines/Web_Security + # https://observatory.mozilla.org/ + {% if domain_cert_ca != "Self-signed" %} + more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload"; + {% endif %} + more_set_headers "Content-Security-Policy : upgrade-insecure-requests"; + more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval'"; + more_set_headers "X-Content-Type-Options : nosniff"; + more_set_headers "X-XSS-Protection : 1; mode=block"; + more_set_headers "X-Download-Options : noopen"; + more_set_headers "X-Permitted-Cross-Domain-Policies : none"; + more_set_headers "X-Frame-Options : SAMEORIGIN"; + + {% if domain_cert_ca == "Let's Encrypt" %} + # OCSP settings + ssl_stapling on; + ssl_stapling_verify on; + ssl_trusted_certificate /etc/yunohost/certs/{{ domain }}/crt.pem; + resolver 127.0.0.1 127.0.1.1 valid=300s; + resolver_timeout 5s; + {% endif %} + + # Disable gzip to protect against BREACH + # Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!) + gzip off; + +# access_by_lua_file /usr/share/ssowat/access.lua; + + access_log /var/log/nginx/xmpp-upload.{{ domain }}-access.log; + error_log /var/log/nginx/xmpp-upload.{{ domain }}-error.log; +} From 0bd717a21e5567269abb7c98a003fa8fd019f020 Mon Sep 17 00:00:00 2001 From: pitchum Date: Sun, 22 Mar 2020 12:17:08 +0100 Subject: [PATCH 03/13] Include XMPP subdomain in certificate when possible. --- locales/en.json | 1 + src/yunohost/certificate.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index d6784a78d..d2117b7d0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -133,6 +133,7 @@ "certmanager_domain_http_not_working": "It seems the domain {domain:s} cannot be accessed through HTTP. Check that your DNS and NGINX configuration is correct", "certmanager_domain_unknown": "Unknown domain '{domain:s}'", "certmanager_error_no_A_record": "No DNS 'A' record found for '{domain:s}'. You need to make your domain name point to your machine to be able to install a Let's Encrypt certificate. (If you know what you are doing, use '--no-checks' to turn off those checks.)", + "certmanager_warning_subdomain_dns_record": "Subdomain '{subdomain:s}' does not resolve to the same IP address as '{domain:s}'. Some features will not be available until you fix this and regenerate the certificate.", "certmanager_hit_rate_limit": "Too many certificates already issued for this exact set of domains {domain:s} recently. Please try again later. See https://letsencrypt.org/docs/rate-limits/ for more details", "certmanager_http_check_timeout": "Timed out when server tried to contact itself through HTTP using a public IP address (domain '{domain:s}' with IP '{ip:s}'). You may be experiencing a hairpinning issue, or the firewall/router ahead of your server is misconfigured.", "certmanager_no_cert_file": "Could not read the certificate file for the domain {domain:s} (file: {file:s})", diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 9b50749ea..e49db9733 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -639,8 +639,14 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): # Set the domain csr.get_subject().CN = domain - # Include xmpp-upload subdomain as subject alternate names - csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:xmpp-upload." + domain)]) + # Include xmpp-upload subdomain in subject alternate names + subdomain="xmpp-upload." + domain + try: + _check_domain_is_ready_for_ACME(subdomain) + logger.info("Subdmain {} is ready for ACME and will be included in the certificate.".format(subdomain)) + csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + subdomain)]) + except YunohostError: + logger.warning(m18n.n('certmanager_warning_subdomain_dns_record', subdomain=subdomain, domain=domain)) # Set the key with open(key_file, 'rt') as f: From 27f6899b65dc63bcfbf77755af6b0ee064af6821 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 23 Mar 2020 22:15:03 +0100 Subject: [PATCH 04/13] /var/www/xmpp-upload.{domain} -> /var/xmpp-upload/{domain} --- data/hooks/conf_regen/12-metronome | 6 +++--- data/templates/metronome/metronome.cfg.lua | 2 +- data/templates/nginx/server.tpl.conf | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index db5910620..0cfb42fd4 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -48,9 +48,9 @@ do_post_regen() { for domain in $domain_list; do mkdir -p "/var/lib/metronome/${domain//./%2e}/pep" # http_upload directory must be writable by metronome and readable by nginx - mkdir -p "/var/www/xmpp-upload.${domain}/upload" - chmod g+s "/var/www/xmpp-upload.${domain}/upload" - chown -R metronome:www-data "/var/www/xmpp-upload.${domain}" + mkdir -p "/var/xmpp-upload/${domain}/upload" + chmod g+s "/var/xmpp-upload/${domain}/upload" + chown -R metronome:www-data "/var/xmpp-upload/${domain}" done # fix some permissions diff --git a/data/templates/metronome/metronome.cfg.lua b/data/templates/metronome/metronome.cfg.lua index c1420c7fb..b35684add 100644 --- a/data/templates/metronome/metronome.cfg.lua +++ b/data/templates/metronome/metronome.cfg.lua @@ -144,7 +144,7 @@ Component "pubsub.{{ main_domain }}" "pubsub" Component "xmpp-upload.{{ main_domain }}" "http_upload" name = "{{ main_domain }} Sharing Service" - http_file_path = "/var/www/xmpp-upload.{{ main_domain }}/upload" + http_file_path = "/var/xmpp-upload/{{ main_domain }}/upload" http_external_url = "https://xmpp-upload.{{ main_domain }}:443" http_file_base_path = "/upload" http_file_size_limit = 6*1024*1024 diff --git a/data/templates/nginx/server.tpl.conf b/data/templates/nginx/server.tpl.conf index dac188ea3..823e3ce39 100644 --- a/data/templates/nginx/server.tpl.conf +++ b/data/templates/nginx/server.tpl.conf @@ -105,7 +105,7 @@ server { server_name xmpp-upload.{{ domain }}; location /upload { - alias /var/www/xmpp-upload.{{ domain }}/upload; + alias /var/xmpp-upload/{{ domain }}/upload; # Pass all requests to metronome, except for GET and HEAD requests. limit_except GET HEAD { proxy_pass http://localhost:5290; From af415e38e6a57236ac2cac13b70e20e96414a6ab Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 23 Mar 2020 22:43:29 +0100 Subject: [PATCH 05/13] Factorize ciphers and headers configuration into a common file for all vhosts --- data/hooks/conf_regen/15-nginx | 1 + data/templates/nginx/server.tpl.conf | 76 +--------------------------- 2 files changed, 3 insertions(+), 74 deletions(-) diff --git a/data/hooks/conf_regen/15-nginx b/data/hooks/conf_regen/15-nginx index 55a5494b2..11e5f596c 100755 --- a/data/hooks/conf_regen/15-nginx +++ b/data/hooks/conf_regen/15-nginx @@ -49,6 +49,7 @@ do_pre_regen() { # Support different strategy for security configurations export compatibility="$(yunohost settings get 'security.nginx.compatibility')" + ynh_render_template "security.conf.inc" "${nginx_conf_dir}/security.conf.inc" # add domain conf files for domain in $domain_list; do diff --git a/data/templates/nginx/server.tpl.conf b/data/templates/nginx/server.tpl.conf index 823e3ce39..0eb64dd8d 100644 --- a/data/templates/nginx/server.tpl.conf +++ b/data/templates/nginx/server.tpl.conf @@ -38,42 +38,11 @@ server { ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; - {% if compatibility == "modern" %} - # Ciphers with modern compatibility - # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern - # The following configuration use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...) - ssl_protocols TLSv1.2; - ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; - ssl_prefer_server_ciphers on; - {% else %} - # As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519 - ssl_ecdh_curve secp521r1:secp384r1:prime256v1; - ssl_prefer_server_ciphers on; + include /etc/nginx/conf.d/security.conf.inc; - # Ciphers with intermediate compatibility - # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=intermediate - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; - - # Uncomment the following directive after DH generation - # > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048 - #ssl_dhparam /etc/ssl/private/dh2048.pem; - {% endif %} - - # Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners - # https://wiki.mozilla.org/Security/Guidelines/Web_Security - # https://observatory.mozilla.org/ {% if domain_cert_ca != "Self-signed" %} more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload"; {% endif %} - more_set_headers "Content-Security-Policy : upgrade-insecure-requests"; - more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval'"; - more_set_headers "X-Content-Type-Options : nosniff"; - more_set_headers "X-XSS-Protection : 1; mode=block"; - more_set_headers "X-Download-Options : noopen"; - more_set_headers "X-Permitted-Cross-Domain-Policies : none"; - more_set_headers "X-Frame-Options : SAMEORIGIN"; - {% if domain_cert_ca == "Let's Encrypt" %} # OCSP settings ssl_stapling on; @@ -83,10 +52,6 @@ server { resolver_timeout 5s; {% endif %} - # Disable gzip to protect against BREACH - # Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!) - gzip off; - access_by_lua_file /usr/share/ssowat/access.lua; include /etc/nginx/conf.d/{{ domain }}.d/*.conf; @@ -124,42 +89,11 @@ server { ssl_session_timeout 5m; ssl_session_cache shared:SSL:50m; - {% if compatibility == "modern" %} - # Ciphers with modern compatibility - # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern - # The following configuration use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...) - ssl_protocols TLSv1.2; - ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; - ssl_prefer_server_ciphers on; - {% else %} - # As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519 - ssl_ecdh_curve secp521r1:secp384r1:prime256v1; - ssl_prefer_server_ciphers on; + include /etc/nginx/conf.d/security.conf.inc; - # Ciphers with intermediate compatibility - # https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=intermediate - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; - - # Uncomment the following directive after DH generation - # > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048 - #ssl_dhparam /etc/ssl/private/dh2048.pem; - {% endif %} - - # Follows the Web Security Directives from the Mozilla Dev Lab and the Mozilla Obervatory + Partners - # https://wiki.mozilla.org/Security/Guidelines/Web_Security - # https://observatory.mozilla.org/ {% if domain_cert_ca != "Self-signed" %} more_set_headers "Strict-Transport-Security : max-age=63072000; includeSubDomains; preload"; {% endif %} - more_set_headers "Content-Security-Policy : upgrade-insecure-requests"; - more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval'"; - more_set_headers "X-Content-Type-Options : nosniff"; - more_set_headers "X-XSS-Protection : 1; mode=block"; - more_set_headers "X-Download-Options : noopen"; - more_set_headers "X-Permitted-Cross-Domain-Policies : none"; - more_set_headers "X-Frame-Options : SAMEORIGIN"; - {% if domain_cert_ca == "Let's Encrypt" %} # OCSP settings ssl_stapling on; @@ -169,12 +103,6 @@ server { resolver_timeout 5s; {% endif %} - # Disable gzip to protect against BREACH - # Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!) - gzip off; - -# access_by_lua_file /usr/share/ssowat/access.lua; - access_log /var/log/nginx/xmpp-upload.{{ domain }}-access.log; error_log /var/log/nginx/xmpp-upload.{{ domain }}-error.log; } From 1f09abfa5108f30f17bbb30341495f6fb197c6ae Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 25 Mar 2020 11:51:57 +0100 Subject: [PATCH 06/13] Rationalize some nginx config into security.conf.inc. --- data/templates/nginx/security.conf.inc | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 data/templates/nginx/security.conf.inc diff --git a/data/templates/nginx/security.conf.inc b/data/templates/nginx/security.conf.inc new file mode 100644 index 000000000..272a29e26 --- /dev/null +++ b/data/templates/nginx/security.conf.inc @@ -0,0 +1,33 @@ +{% if compatibility == "modern" %} +# Ciphers with modern compatibility +# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=modern +# The following configuration use modern ciphers, but remove compatibility with some old clients (android < 5.0, Internet Explorer < 10, ...) +ssl_protocols TLSv1.2; +ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; +ssl_prefer_server_ciphers on; +{% else %} +# As suggested by Mozilla : https://wiki.mozilla.org/Security/Server_Side_TLS and https://en.wikipedia.org/wiki/Curve25519 +ssl_ecdh_curve secp521r1:secp384r1:prime256v1; +ssl_prefer_server_ciphers on; + +# Ciphers with intermediate compatibility +# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.6.2&openssl=1.0.1t&hsts=yes&profile=intermediate +ssl_protocols TLSv1 TLSv1.1 TLSv1.2; +ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; + +# Uncomment the following directive after DH generation +# > openssl dhparam -out /etc/ssl/private/dh2048.pem -outform PEM -2 2048 +#ssl_dhparam /etc/ssl/private/dh2048.pem; +{% endif %} + +more_set_headers "Content-Security-Policy : upgrade-insecure-requests"; +more_set_headers "Content-Security-Policy-Report-Only : default-src https: data: 'unsafe-inline' 'unsafe-eval'"; +more_set_headers "X-Content-Type-Options : nosniff"; +more_set_headers "X-XSS-Protection : 1; mode=block"; +more_set_headers "X-Download-Options : noopen"; +more_set_headers "X-Permitted-Cross-Domain-Policies : none"; +more_set_headers "X-Frame-Options : SAMEORIGIN"; + +# Disable gzip to protect against BREACH +# Read https://trac.nginx.org/nginx/ticket/1720 (text/html cannot be disabled!) +gzip off; From ada95f8fca57db7cab97b14c4ce03b799632a87a Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 25 Mar 2020 12:09:24 +0100 Subject: [PATCH 07/13] http-upload only available on maindomain (for the moment). --- data/hooks/conf_regen/12-metronome | 10 ++++++---- src/yunohost/certificate.py | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/data/hooks/conf_regen/12-metronome b/data/hooks/conf_regen/12-metronome index 0cfb42fd4..5c9c67f11 100755 --- a/data/hooks/conf_regen/12-metronome +++ b/data/hooks/conf_regen/12-metronome @@ -42,16 +42,18 @@ do_post_regen() { regen_conf_files=$1 # retrieve variables + main_domain=$(cat /etc/yunohost/current_host) domain_list=$(yunohost domain list --output-as plain --quiet) # create metronome directories for domains for domain in $domain_list; do mkdir -p "/var/lib/metronome/${domain//./%2e}/pep" - # http_upload directory must be writable by metronome and readable by nginx - mkdir -p "/var/xmpp-upload/${domain}/upload" - chmod g+s "/var/xmpp-upload/${domain}/upload" - chown -R metronome:www-data "/var/xmpp-upload/${domain}" done + # http_upload directory must be writable by metronome and readable by nginx + mkdir -p "/var/xmpp-upload/${main_domain}/upload" + chmod g+s "/var/xmpp-upload/${main_domain}/upload" + chown -R metronome:www-data "/var/xmpp-upload/${main_domain}" + # fix some permissions chown -R metronome: /var/lib/metronome/ diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index e49db9733..31a4c1200 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -43,6 +43,7 @@ from yunohost.utils.network import get_public_ip from moulinette import m18n from yunohost.app import app_ssowatconf +from yunohost.domain import _get_maindomain from yunohost.service import _run_service_command from yunohost.regenconf import regen_conf from yunohost.log import OperationLogger @@ -639,14 +640,15 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): # Set the domain csr.get_subject().CN = domain - # Include xmpp-upload subdomain in subject alternate names - subdomain="xmpp-upload." + domain - try: - _check_domain_is_ready_for_ACME(subdomain) - logger.info("Subdmain {} is ready for ACME and will be included in the certificate.".format(subdomain)) - csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + subdomain)]) - except YunohostError: - logger.warning(m18n.n('certmanager_warning_subdomain_dns_record', subdomain=subdomain, domain=domain)) + if domain == _get_maindomain(): + # Include xmpp-upload subdomain in subject alternate names + subdomain="xmpp-upload." + domain + try: + _check_domain_is_ready_for_ACME(subdomain) + logger.info("Subdmain {} is ready for ACME and will be included in the certificate.".format(subdomain)) + csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + subdomain)]) + except YunohostError: + logger.warning(m18n.n('certmanager_warning_subdomain_dns_record', subdomain=subdomain, domain=domain)) # Set the key with open(key_file, 'rt') as f: From 5e6e53142bb621e0552136dfa16a8c4bf6a1c562 Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 25 Mar 2020 12:09:53 +0100 Subject: [PATCH 08/13] Improve nginx config for xmpp-upload subdomain. --- data/templates/nginx/server.tpl.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/templates/nginx/server.tpl.conf b/data/templates/nginx/server.tpl.conf index 0eb64dd8d..6316960c4 100644 --- a/data/templates/nginx/server.tpl.conf +++ b/data/templates/nginx/server.tpl.conf @@ -68,8 +68,9 @@ server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name xmpp-upload.{{ domain }}; + root /dev/null; - location /upload { + location /upload/ { alias /var/xmpp-upload/{{ domain }}/upload; # Pass all requests to metronome, except for GET and HEAD requests. limit_except GET HEAD { From ceaacfbd975a3fab117e8940c95d3e86be4a186e Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 25 Mar 2020 12:20:23 +0100 Subject: [PATCH 09/13] Simplified check for subdomain inclusion in certificate. --- src/yunohost/certificate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 31a4c1200..e4d4874e3 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -643,11 +643,10 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): if domain == _get_maindomain(): # Include xmpp-upload subdomain in subject alternate names subdomain="xmpp-upload." + domain - try: - _check_domain_is_ready_for_ACME(subdomain) + if _dns_ip_match_public_ip(get_public_ip(), subdomain): logger.info("Subdmain {} is ready for ACME and will be included in the certificate.".format(subdomain)) csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + subdomain)]) - except YunohostError: + else: logger.warning(m18n.n('certmanager_warning_subdomain_dns_record', subdomain=subdomain, domain=domain)) # Set the key From 094cb15b0aaafa8b96d7c3e86f9490352e0d8a1a Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 25 Mar 2020 19:53:36 +0100 Subject: [PATCH 10/13] Workaround some python loading issue. --- src/yunohost/certificate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index e4d4874e3..5ff88ca4e 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -43,7 +43,6 @@ from yunohost.utils.network import get_public_ip from moulinette import m18n from yunohost.app import app_ssowatconf -from yunohost.domain import _get_maindomain from yunohost.service import _run_service_command from yunohost.regenconf import regen_conf from yunohost.log import OperationLogger @@ -640,6 +639,7 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): # Set the domain csr.get_subject().CN = domain + from yunohost.domain import _get_maindomain if domain == _get_maindomain(): # Include xmpp-upload subdomain in subject alternate names subdomain="xmpp-upload." + domain From d85bd1f25ab1a17ddb72b73a010d481f31e05d44 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 25 Mar 2020 20:15:34 +0100 Subject: [PATCH 11/13] Forbid users from trying to add a domain starting by xmpp-upload. --- locales/en.json | 1 + src/yunohost/domain.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/locales/en.json b/locales/en.json index d2117b7d0..8fe555744 100644 --- a/locales/en.json +++ b/locales/en.json @@ -223,6 +223,7 @@ "diagnosis_unknown_categories": "The following categories are unknown: {categories}", "diagnosis_never_ran_yet": "It looks like this server was setup recently and there's no diagnosis report to show yet. You should start by running a full diagnosis, either from the webadmin or using 'yunohost diagnosis run' from the command line.", "domain_cannot_remove_main": "You cannot remove '{domain:s}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n '; here is the list of candidate domains: {other_domains:s}", + "domain_cannot_add_xmpp_upload": "You cannot add domains starting with 'xmpp-upload.'. This kind of name is reserved for the XMPP upload feature integrated in YunoHost.", "domain_cannot_remove_main_add_new_one": "You cannot remove '{domain:s}' since it's the main domain and your only domain, you need to first add another domain using 'yunohost domain add ', then set is as the main domain using 'yunohost domain main-domain -n ' and then you can remove the domain '{domain:s}' using 'yunohost domain remove {domain:s}'.'", "domain_cert_gen_failed": "Could not generate certificate", "domain_created": "Domain created", diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 5037e9334..eb84f27d0 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -79,6 +79,9 @@ def domain_add(operation_logger, domain, dyndns=False): from yunohost.app import app_ssowatconf from yunohost.utils.ldap import _get_ldap_interface + if domain.startswith("xmpp-upload."): + raise YunohostError("domain_cannot_add_xmpp_upload") + ldap = _get_ldap_interface() try: From e59a38a88a5d117a443be97ceebcf1d6b8315ef1 Mon Sep 17 00:00:00 2001 From: pitchum Date: Wed, 25 Mar 2020 20:31:08 +0100 Subject: [PATCH 12/13] Remove useless debug message. --- src/yunohost/certificate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 5ff88ca4e..6e9c97bca 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -644,7 +644,6 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): # Include xmpp-upload subdomain in subject alternate names subdomain="xmpp-upload." + domain if _dns_ip_match_public_ip(get_public_ip(), subdomain): - logger.info("Subdmain {} is ready for ACME and will be included in the certificate.".format(subdomain)) csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + subdomain)]) else: logger.warning(m18n.n('certmanager_warning_subdomain_dns_record', subdomain=subdomain, domain=domain)) From f52eef4bc2db4398841e0475a6ce9e13d24e917b Mon Sep 17 00:00:00 2001 From: pitchum Date: Sun, 29 Mar 2020 11:51:12 +0200 Subject: [PATCH 13/13] [fix] Don't break the cert renew process, just warn. --- src/yunohost/certificate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/certificate.py b/src/yunohost/certificate.py index 6e9c97bca..5fae59060 100644 --- a/src/yunohost/certificate.py +++ b/src/yunohost/certificate.py @@ -643,9 +643,10 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder): if domain == _get_maindomain(): # Include xmpp-upload subdomain in subject alternate names subdomain="xmpp-upload." + domain - if _dns_ip_match_public_ip(get_public_ip(), subdomain): + try: + _dns_ip_match_public_ip(get_public_ip(), subdomain) csr.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + subdomain)]) - else: + except YunohostError: logger.warning(m18n.n('certmanager_warning_subdomain_dns_record', subdomain=subdomain, domain=domain)) # Set the key