Reputation: 3
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
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