Tempo
Tempo

Reputation: 3

HTTP Upgrade to TLS in Nginx Per RFC 2817

I have been looking into how I can upgrade an HTTP connection to TLS and achieve and end to end tunnel across proxy hops. I would like to use the client certificate in this tunnel to act upon it on the receiving end after a few hops. I read RFC 2817 (HTTP Upgrade to TLS) and it seems this is possible. I just cannot figure out how to do that using Nginx being new to Nginx.

I was wondering if I am making a complete noob mistake or if this is at all possible in Nginx.

I have Nginx instance 1 with the following config for the http block:

http {
  include       mime.types;
  default_type  application/octet-stream;
  log_format main    '$remote_addr - $remote_user [$time_local] '
                   '$ssl_protocol/$ssl_cipher '
                   '$ssl_client_cert '
                   '$ssl_client_raw_cert '
                   'HTTP UPGRADE: $http_upgrade '
                   '"$request" $status $body_bytes_sent '
                   '"$http_referer" "$http_user_agent"';

access_log  logs/access.log;

sendfile        on;
keepalive_timeout  65;

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

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

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;
}

# HTTPS server
server {
    listen       443 ssl;
    listen       [::]:443 ssl;
    server_name  localhost;

    access_log logs/access-ssl.log main;

    ssl_certificate      /root/certs/server.crt;
    ssl_certificate_key  /root/certs/server.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /root/certs/dhparams.pem;

    # modern configuration. tweak to your needs.
    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;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass https://10.0.3.4/;
    }

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

The second Nginx instance has the following config has pretty much the same config except for one difference proxy_pass https://10.0.3.5/index.html

The last Nginx instance has the following config:

http {
include       mime.types;
default_type  application/octet-stream;
log_format main    '$remote_addr - $remote_user [$time_local] '
                   '$ssl_protocol/$ssl_cipher '
                   '$ssl_client_cert '
                   '$ssl_client_raw_cert '
                   'HTTP UPGRADE: $http_upgrade '
                   '"$request" $status $body_bytes_sent '
                   '"$http_referer" "$http_user_agent"';

access_log  logs/access.log;

sendfile        on;
keepalive_timeout  65;

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

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

    # Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;
}

# HTTPS server
server {
    listen       443 ssl;
    listen       [::]:443 ssl;
    server_name  localhost;

    access_log logs/access-ssl.log main;

    ssl_certificate      /root/certs/server.crt;
    ssl_certificate_key  /root/certs/server.key;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /root/certs/dhparams.pem;

    # modern configuration. tweak to your needs.
    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;

    # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
    add_header Strict-Transport-Security max-age=15768000;

    location / {
        root   html;
        index  index.html index.htm;
    }

    # redirect server error pages to the static page /50x.html
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

When I hit the URL using HTTPS, I do get the response but nothing about the client cert is printed in the logs. When I use only HTTP, I get the 301 response. These are the two calls I am making: $ curl -k -i --cert /root/certs/client-cert.pem --key /root/certs/client-key.pem --header "Upgrade: TLS/1.2" --header "Connection: Upgrade" https://10.0.3.3/

and

$ curl -k -i --cert /root/certs/client-cert.pem --key /root/certs/client-key.pem --header "Upgrade: TLS/1.2" --header "Connection: Upgrade" http://10.0.3.3/

Upvotes: 0

Views: 734

Answers (1)

Steffen Ullrich
Steffen Ullrich

Reputation: 123531

RFC 2817 defines two methods for TLS upgrade: CONNECT request and Connection: Upgrade.

CONNECT is a HTTP request done by the browser when using an explicitly configured HTTP proxy. It gets not used with transparent proxies or reverse proxies. Contrary to for instance squid nginx is a web server and not a HTTP proxy (from the perspective of the browser) and thus does not implement the CONNECT request.

As for the Connection Upgrade to TLS specified in RFC 2817: this was just an idea and no browser supports this. This kind of Upgrade mechanisms is actually used in browsers today but not for upgrading to TLS but only for WebSockets.

Upvotes: 1

Related Questions