Score:1

NGINX enforces TLS 1.3 even though I set up TLS 1.2

ye flag

on my Server, Debian 11 bullseye 5.10.0-20-amd64

# nginx -V
nginx version: nginx/1.18.0
built with OpenSSL 1.1.1n  15 Mar 2022
TLS SNI support enabled

when using the SSL config from mozilla (https://ssl-config.mozilla.org/#server=nginx&version=1.18.0&config=intermediate&openssl=1.1.1n&guideline=5.6), nginx enforces TLS 1.3 only. I cannot connect with TLS 1.2 to the server.

* [CONN-0-0][CF-SSL] TLSv1.0 (OUT), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS handshake, Client hello (1):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Unknown (21):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS alert, protocol version (582):
* OpenSSL/3.0.7: error:0A00042E:SSL routines::tlsv1 alert protocol version
* Closing connection 0
curl: (35) OpenSSL/3.0.7: error:0A00042E:SSL routines::tlsv1 alert protocol version

There are some clients using services on my server that don't support TLS 1.3. All of them cannot connect as of right now.

Does someone know why the f* nginx enforces TLS 1.3 only?

There is no line in nginx config (looked through nginx -T) that says TLS 1.3 only. I'm totally at a loss how to fix this, so any help or directions where to look are much appreciated.

nginx.conf

user www-data;
worker_processes 4;
worker_rlimit_nofile 25000;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
load_module modules/ngx_http_modsecurity_module.so;

events {
        worker_connections 4096;
        # multi_accept on;
}

http {

        ##
        # Basic Settings
        ##

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;
        server_tokens off;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        ##
        # Logging Settings
        ##

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log error;

        ##
        # Gzip Settings
        ##

        gzip on;
        gzip_vary on;
        gzip_min_length 10240;
        gzip_proxied expired no-cache no-store private auth;
        gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;

        ##
        # Virtual Host Configs
        ##

        include /etc/nginx/sites-enabled/*.vhost;
}

default.vhost

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        server_name master.nerdvpn.de;

        location /.well-known/acme-challenge/ {
                allow all;
                default_type "text/plain";
                alias /var/www/acme/;
        }
}

server {
        listen 443 ssl http2 default_server;
        listen [::]:443 ssl http2 default_server;

        server_name master.nerdvpn.de;

        add_header X-Robots-Tag "none" always;

        ssl_certificate         /srv/letsencrypt/certs/master.nerdvpn.de/fullchain.pem;
        ssl_certificate_key     /srv/letsencrypt/certs/master.nerdvpn.de/privkey.pem;

        # https://ssl-config.mozilla.org/#server=nginx&version=1.18.0&config=intermediate&openssl=1.1.1n&guideline=5.6
        ssl_session_timeout 1d;
        ssl_session_cache shared:nginx_TLS_default:10m;  # about 40000 sessions
        ssl_session_tickets off;

        # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
        ssl_dhparam /srv/letsencrypt/dhparam.pem;

        # intermediate configuration
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;

        # HSTS (ngx_http_headers_module is required) (63072000 seconds)
        add_header Strict-Transport-Security "max-age=63072000" always;

        # OCSP stapling
        ssl_stapling on;
        ssl_stapling_verify on;

        # verify chain of trust of OCSP response using Root CA and Intermediate certs
        ssl_trusted_certificate /srv/letsencrypt/ocsp/BPClass2CA2Bundle.pem;

        access_log off;
        error_log /var/log/nginx/errors/server.log;

        location / {
                deny all;
        }
}

nextcloud.vhost

upstream php-handler {
        server unix:/var/run/php/php-fpm.sock;
}

limit_req_zone $binary_remote_addr zone=nextcloud_login:8m rate=60r/m;

server {
                listen 80;
                listen [::]:80;

                server_name nerdvpn.de www.nerdvpn.de;

                location ^~ /.well-known/acme-challenge/ {
                                allow all;
                                default_type "text/plain";
                                alias /var/www/acme/;
                }

                location / {
                                return 301 https://$server_name:443$request_uri;
                }
}


server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name nerdvpn.de www.nerdvpn.de;

        ssl_certificate /srv/letsencrypt/certs/nerdvpn.de/fullchain.pem;
        ssl_certificate_key /srv/letsencrypt/certs/nerdvpn.de/privkey.pem;
        
        # https://ssl-config.mozilla.org/#server=nginx&version=1.18.0&config=intermediate&openssl=1.1.1n&guideline=5.6
        ssl_session_timeout 1d;
        ssl_session_cache shared:nginx_TLS_nx:10m;  # about 40000 sessions
        ssl_session_tickets off;

        # curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam
        ssl_dhparam /srv/letsencrypt/dhparam.pem;

        # intermediate configuration
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
        ssl_prefer_server_ciphers off;

        # HSTS (ngx_http_headers_module is required) (63072000 seconds)
        add_header Strict-Transport-Security "max-age=63072000" always;

        # OCSP stapling
        ssl_stapling on;
        ssl_stapling_verify on;

        # verify chain of trust of OCSP response using Root CA and Intermediate certs
        ssl_trusted_certificate /srv/letsencrypt/ocsp/BPClass2CA2Bundle.pem;

        access_log off;
        error_log /var/log/nginx/errors/nextcloud.log error;

        client_max_body_size 512M;
        fastcgi_buffers 64 4K;

        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

        add_header Referrer-Policy "no-referrer" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header X-Download-Options "noopen" always;
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-Permitted-Cross-Domain-Policies "none" always;
        add_header X-Robots-Tag "none" always;
        add_header X-XSS-Protection "1; mode=block" always;

        fastcgi_hide_header X-Powered-By;

        root /srv/nextcloud;

        index index.php index.html /index.php$request_uri;

        # Microsoft DAV clients
        location = / {
                if ( $http_user_agent ~ ^DavClnt ) {
                        return 302 /remote.php/webdav/$is_args$args;
                }
        }

        location = /robots.txt {
                allow all;
                log_not_found off;
                access_log off;
        }

        location ^~ /.well-known {
                # The rules in this block are an adaptation of the rules
                # in `.htaccess` that concern `/.well-known`.

                location = /.well-known/carddav { return 301 /remote.php/dav/; }
                location = /.well-known/caldav  { return 301 /remote.php/dav/; }

                location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
                location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

                return 301 /index.php$request_uri;
        }

        # Hide certain paths from clients
        location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)  { return 404; }
        location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console)                                { return 404; }

        location ~ \.php(?:$|/) {
                rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;

                fastcgi_split_path_info ^(.+?\.php)(/.*)$;
                set $path_info $fastcgi_path_info;

                try_files $fastcgi_script_name =404;

                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $path_info;
                fastcgi_param HTTPS on;

                fastcgi_param modHeadersAvailable true;
                fastcgi_param front_controller_active true;
                fastcgi_pass php-handler;

                fastcgi_intercept_errors on;
                fastcgi_request_buffering off;

                fastcgi_max_temp_file_size 0;
        }

        location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
                try_files $uri /index.php$request_uri;
                expires 6M;              # Cache-Control policy borrowed from `.htaccess`
                access_log off;  # Optional: Don't log access to assets

                location ~ \.wasm$ {
                        default_type application/wasm;
                }
        }

        location ~ \.woff2?$ {
                try_files $uri /index.php$request_uri;
                expires 7d;              # Cache-Control policy borrowed from `.htaccess`
                access_log off;  # Optional: Don't log access to assets
        }

        location /remote {
                return 301 /remote.php$request_uri;
        }

        location /login {
                limit_req zone=nextcloud_login burst=12 delay=4;
                try_files $uri $uri/ /index.php$request_uri;
        }
        location / {
                try_files $uri $uri/ /index.php$request_uri;
        }
}

Cheers!

Steffen Ullrich avatar
se flag
nginx does not enforce TLS 1.3 if properly configured. So the error is somewhere in your configuration. Unfortunately you don't provide the actual config, but only make claims about it. Without further details one cannot help you.
Weidenwiesel avatar
ye flag
I literally copy pasted the moderate config from mozilla.org, so I don't think anyone REALLY needs my nginx config, but here you go, updated my post above.
Steffen Ullrich avatar
se flag
This is only the config for the specific virtual host. there should be also global settings and maybe other virtual hosts - settings on these place can affect what you see. Also, are you sure that you are actually connecting directly to your nginx and not to some load balancer or reverse proxy in front of it which might also change the results? Also check the nginx error log for any information which might help.
Weidenwiesel avatar
ye flag
You're too fast, I was still adding the nginx.conf ^^ Also, yes directly to nginx, no additional reverse proxy or load balancer.
Steffen Ullrich avatar
se flag
This isn't all. There is at least another virtual host on the system (master.nerdvpn.de). Again, the SSL protocol config there can have global effects.
Weidenwiesel avatar
ye flag
All other vhosts use the same TLS config - intermediate from mozilla.org. Still, only TLS 1.3 is accepted and ssllabs reports TLS 1.2 as not supported. If you want, I can post all of them, but like I said, its the same lines (except for the session part). It's all via one include snippets/tls.conf)
Steffen Ullrich avatar
se flag
Are there any files which match `/etc/nginx/modules-enabled/*.conf` (which you have also included)? Maybe there are some SSL related settings there?
Weidenwiesel avatar
ye flag
50-mod-http-auth-pam.conf, 50-mod-http-dav-ext.conf, 50-mod-http-echo.conf, 50-mod-http-geoip.conf, 50-mod-http-image-filter.conf, 50-mod-http-subs-filter.conf, 50-mod-http-upstream-fair.conf, 50-mod-http-xslt-filter.conf, 50-mod-mail.conf, 50-mod-stream.conf, 70-mod-stream-geoip.conf
Steffen Ullrich avatar
se flag
Could you simply do a `nginx -T | grep ssl_protocols` to check for all relevant settings no matter from which file they are included?
Weidenwiesel avatar
ye flag
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful ssl_protocols TLSv1.2 TLSv1.3;
Weidenwiesel avatar
ye flag
I've also tried several grep -R "keyword" for ssl tls etc and found nothing.
Steffen Ullrich avatar
se flag
That's strange. Then I have no clue either of what this can be.
Weidenwiesel avatar
ye flag
Yeah... now you know how I feel. This is not my first server BUT the first one where something like this happened. Oh and thank you so far for your quick replies.
Score:1
ye flag

I could fix the problem by building nginx from source. With no config changes, it now works. I don't know why but the default nginx from the Debian repos refuses to use TLS 1.2 on my machine.

* [CONN-0-0][CF-SSL] TLSv1.0 (OUT), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS handshake, Client hello (1):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS handshake, Server hello (2):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS handshake, Certificate (11):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS handshake, Server finished (14):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS header, Finished (20):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (OUT), TLS handshake, Finished (20):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Finished (20):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS header, Certificate Status (22):
* [CONN-0-0][CF-SSL] TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES256-GCM-SHA384
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.