5

I got the following setup:

Internet => nginx[public:80 +443, SSL termination)
=> Varnish[localhost:81] => Apache[localhost:82]

Now some sites should only be reachable via HTTPS and a valid SSL certificate. For these few exceptions I'd like to activate HSTS, either on nginx (preferred, or on Apache).

The problem:

  • On nginx, I'd need some logic à la if Host = foo.tld then set Strict-Transport-Security xxx, but according to http://wiki.nginx.org/IfIsEvil one should not use if in a location
  • On Apache, I'd need something like if X-Forwarded-Proto 443 set Strict-Transport-Security xxx, but I don't seem to be able to construct this with SetEnvIf (Apache 2.2)

Is my logic flawed? Another idea for an approach?

This is the configuration currently active:

nginx

server {
        server_tokens off;

        listen xx.xx.xxx.xxx:80;
        server_name localhost;

        location / {
                proxy_pass http://127.0.0.1:81;
                proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-Port 80;
                proxy_set_header Host $host;
                add_header X-XSS-Protection "1; mode=block";
        }
}

server {
        server_tokens off;

        listen xx.xx.xxx.xxx:443 ssl;
        server_name localhost;

        ssl on;
        ssl_certificate /etc/ssl/foo.crt;
        ssl_certificate_key /etc/ssl/private/foo.key;

        ssl_session_timeout 10m;

        # http://blog.ivanristic.com/2013/08/configuring-apache-nginx-and-openssl-for-forward-secrecy.html
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;
        ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

        location / {
                proxy_pass http://127.0.0.1:81;
                proxy_set_header X-Real-IP  $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_set_header X-Forwarded-Port 443;
                proxy_set_header Host $host;
                add_header X-XSS-Protection "1; mode=block";
        }
}

Varnish

No special configuration.

Apache

<VirtualHost *:82>
[...] nothing special
</VirtualHost>
2
  • 2
    You could always add another server block for some servers with HSTS headers
    – Alexey Ten
    Sep 23, 2014 at 5:50
  • Thanks I couldn't somehow see that option ;-) If you add it as an answer, I'll gladly accept it.
    – weeheavy
    Sep 23, 2014 at 17:42

2 Answers 2

7

You could have multiple server blocks. So just add new server block for domains that need HSTS.

server {
    listen xx.xx.xxx.xxx:443 ssl default_server;

    # all ssl stuff
    # and other directives
}

server {
    listen xx.xx.xxx.xxx:443 ssl;
    server_name example.com other.example.com;

    # all ssl stuff
    # and other directives with HSTS enabled
}

Here first block will handle all https connections except example.com and other.example.com.

And you don't need ssl on directive if you have ssl flag in listen.

EDIT

There is another solution with only one server block:

map $scheme:$host $hsts_header {
    default "";
    https:example.com "max-age=31536000";
    https:other.example.com "max-age=31536000";
}

server {
    server_tokens off;

    listen xx.xx.xxx.xxx:80;
    listen xx.xx.xxx.xxx:443 ssl;

    ssl_certificate /etc/ssl/foo.crt;
    ssl_certificate_key /etc/ssl/private/foo.key;

    ssl_session_timeout 10m;

    # ... other ssl stuff

    location / {
        proxy_pass http://127.0.0.1:81;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header Host $host;
        add_header X-XSS-Protection "1; mode=block";
        add_header Strict-Transport-Security $hsts_header;
    }
}

We use map to define HSTS header value and use the fact, than nginx will not add header with empty value.

3
  • Nice solution. I bet you could figure this one out too: serverfault.com/questions/630416/…
    – Gaia
    Oct 6, 2014 at 1:09
  • The problem with HSTS is browser keeps remember if header for a domain has been sent previously. No matter if you fix that and take it out later, browser may redirect to HTTPS based on the fact HTST header was previously used.
    – Anatoly
    Sep 11, 2015 at 7:50
  • 2
    @Anatoly, that's not a problem but the main reason for HSTS to exist. If you want to remove HSTS, you have to set max-age=0 and wait for some period of time. Or clear your browser's cache
    – Alexey Ten
    Sep 11, 2015 at 8:43
-1

You can also add the HSTS header unconditionnaly.

Indeed, it only takes effect if the connection succeeded, over TLS and without cert warning. Cf paragraph 5.1 of RFC6797.

2
  • This is not true. You should read paragraph 7 of the same RFC. (More precisely, it's true that it will be ignored, but it's not true that you can add it.)
    – gparent
    Oct 15, 2015 at 15:18
  • 1
    RFC is clear that the header must only be added over secure transport. From section 7.2 in the RFC: "An HSTS Host MUST NOT include the STS header field in HTTP responses conveyed over non-secure transport." Dec 18, 2015 at 21:47

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .