nginxinc / docker-nginx

Official NGINX Dockerfiles
BSD 2-Clause "Simplified" License
3.26k stars 1.73k forks source link

Image nginx: 1.25.4-alpine3.18 does not have support for HTTPS enabled, and after container startup, an error message is reported using HTTP 3 #867

Closed chenxudong2020 closed 8 months ago

chenxudong2020 commented 8 months ago

Describe the bug

Image nginx: 1.25.4-alpine3.18 does not have support for HTTPS enabled, and after container startup, an error message is reported using HTTP 3

My configuration file is as follows: server { listen $HTTP_PORT default_server; listen [::]:$HTTP_PORT default_server; server_name localhost; index index.php index.html index.htm index.nginx-debian.html; root /web; } server { listen $HTTPS_PORT ssl default_server; listen $HTTPS_PORT http3 reuseport default_server; listen [::]:$HTTPS_PORT ssl default_server; listen [::]:$HTTPS_PORT http3 reuseport default_server; http2 on; ssl_certificate /cert/$SSL_PUBLIC; ssl_certificate_key /cert/$SSL_KEY; ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; add_header Alt-Svc 'quic=":$HTTPS_PORT"; h3-27=":$HTTPS_PORT";h3-25=":$HTTPS_PORT"; h3-T050=":$HTTPS_PORT"; h3-Q050=":$HTTPS_PORT";h3-Q049=":$HTTPS_PORT";h3-Q048=":$HTTPS_PORT"; h3-Q046=":$HTTPS_PORT"; h3-Q043=":$HTTPS_PORT"'; server_name localhost; index index.php index.html index.htm index.nginx-debian.html; root /web; error_page 400 = /400.html;

    location ~ \\.php$ {

    }

    location $TUNNEL_PATH {
        proxy_redirect off;
        proxy_pass http://$OVERTLS_HOST:$OVERTLS_PORT;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host \$http_host;
    }
}

Error reported after running: 2024/02/21 09:06:23 [emerg] 12#12: invalid parameter "http3" in /etc/nginx/conf.d/overtls.conf:10

To reproduce

Steps to reproduce the behavior:

Expected behavior

A clear and concise description of what you expected to happen.

Your environment

Additional context

Add any other context about the problem here.

thresheek commented 8 months ago

Hi @chenxudong2020!

There is no http3 parameter for the listen directive. You probably want quic instead: http://nginx.org/en/docs/http/ngx_http_v3_module.html

DJviolin commented 8 months ago

For some reason, http3 also not working for me in my compose project. I'm assuming the error is in my settings, but even when I try a very minimal setup with defaults, always http2 version returning. This haunting me for 2 days already.

DJviolin commented 8 months ago

My Nginx settings:

nginx.conf:

user nginx;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 1024000;
error_log /dev/stderr crit;
pid /run/nginx.pid;
daemon off;
pcre_jit on;

events {
    worker_connections 4096;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    charset UTF-8;
    access_log /dev/stdout combined;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_timeout 75s;
    keepalive_disable none;
    keepalive_requests 4096;

    client_max_body_size 20M;

    http2 on;
    quic_retry on;
    quic_gso on;
    map $http3 $alt_svc_value {
        default '';
        'h3' 'h3=":$server_port"; ma=86400';
    }
    add_header Alt-Svc $alt_svc_value;

    ssl_session_timeout 1h;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.3;
    ssl_early_data on;
    proxy_set_header Early-Data $ssl_early_data;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    proxy_buffering on;
    proxy_cache_path /var/cache/nginx
        levels=1:2
        use_temp_path=off
        keys_zone=mycache:10m
        inactive=24h
        max_size=1g
    ;
    proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
    proxy_cache_valid 200 302 24h;
    proxy_cache_valid 404      1m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
    proxy_cache_background_update on;
    proxy_cache_lock on;

    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-available/*.conf;
}

Server file:

server {
    listen [::]:443 quic reuseport;
    listen 443 quic reuseport;
    listen [::]:443 ssl deferred reuseport;
    listen 443 ssl deferred reuseport;

    server_name docker.test;

    ssl_certificate /run/secrets/ssl_certificate;
    ssl_certificate_key /run/secrets/ssl_certificate_key;

    # ...

    location /benchmark_json {
        default_type application/json;
        return 200 '{"status": "ok", "message": "Service is up and running"}';
    }
}

In Docker Compose, I exposing Nginx ports like this:

ports:
  - 80:80
  - 443:443/udp
  - 443:443/tcp
thresheek commented 8 months ago

@DJviolin, browsers can be picky about enabling http3 - I'd suggest testing with cli tools like curl.

DJviolin commented 8 months ago

It works with curl, but not with latest Firefox and Edge. I'm absolutely exhausted, why it's not working... Can be an issue with the local certificate? I created it with this command:

$ docker run -it --rm -v "./secrets/:/secrets/" alpine/openssl:latest req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /secrets/docker.test.key -out /secrets/docker.test.crt -subj "/C=HU/ST=Pest/L=Budapest/O=Docker Compose Company/CN=docker.test"

Here is the curl output for HTTP3 request:

HTTP/3 200
date: Wed, 21 Feb 2024 18:20:06 GMT
content-type: application/json
content-length: 56
content-security-policy: upgrade-insecure-requests
permissions-policy: autoplay=(), camera=(), microphone=()
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
alt-svc: h3=":443"; ma=86400
x-protocol: HTTP/3.0
strict-transport-security: max-age=63072000; includeSubDomains; preload

I tried the minimal http block from the nginx docs with this certs and still the same. Curl returns http3, but the browsers only http2.

DJviolin commented 8 months ago

The problem was caused by the browser's strict policy about locally signed certificates and certificate authorities with HTTP3/QUIC. Longer explanation can be found here why the browsers doing this:

This is because non-official TLS certificates are often used by companies to decrypt their employees’ TLS traffic (so that they can, for example, have their firewalls scan inside encrypted traffic). However, if companies would start doing that with QUIC, we would again have custom middlebox implementations that make their own assumptions about the protocol. This could lead to them potentially breaking protocol support in the future, which is exactly what we tried to prevent by encrypting QUIC so extensively in the first place! As such, Chrome takes a very opinionated stance on this: If you’re not using an official TLS certificate (signed by a certificate authority or root certificate that is trusted by Chrome, such as Let’s Encrypt), then you cannot use QUIC. This, sadly, also includes self-signed certificates, which are often used for local test set-ups.

How to whitelist your localhost domains in the browsers can be found here.