1

I have been working on my nginx config for a while now but somehow I seem to have a mixup with my headers. I got http/2 running with nginx 1.9.12 on ubuntu 14.04 and am getting a solid A rating. However even though I got most of the header modifications in I get an F for headers security.

I think it might have to do with the CORS config I copy pasted. But I just can't see how.

See below my config file with some retracted paths and some deleted error pages directives to make it simpler.

Any help is appreciated.

# settings
#
server_tokens  off;


# http to https redirect
#
server {
    listen       80;
    server_name  gel.westpacgroup.com.au;
    return 301   https://$host$request_uri;
}


# ssl and http2 config
#
server {
    listen       443 ssl http2;
    listen       [::]:443 ssl http2;
    server_name  gel.westpacgroup.com.au;

    ssl on;
    ssl_certificate      /path/to/fullchain.pem;
    ssl_certificate_key  /path/to/privkey.pem;

    ssl_session_timeout  1d;
    ssl_session_cache    shared:SSL:50m;
    ssl_session_tickets  off;

    ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers  on;
    ssl_dhparam                /path/to/dhparam.pem;
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

    # OCSP Stapling ---
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling         on;
    ssl_stapling_verify  on;

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

    # prevent clickjacking attacks
    add_header  X-Frame-Options 'SAMEORIGIN';

    # disallow circumventing declared MIME types
    add_header X-Content-Type-Options nosniff;

    # X-XSS-Protection
    add_header X-XSS-Protection '1; mode=block';

    # root server
    #
    location / {
        root   /path/to/;
        index  index.html index.htm;

        # Wide-open CORS config for nginx
        #
        if ($request_method = 'OPTIONS') {
            add_header  'Access-Control-Allow-Origin' '*';

            # Om nom nom cookies
            add_header  'Access-Control-Allow-Credentials' 'true';
            add_header  'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';

            # Custom headers and headers various browsers *should* be OK with but aren't
            add_header  'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';

            # Tell client that this pre-flight info is valid for 20 days
            add_header  'Access-Control-Max-Age' 1728000;
            add_header  'Content-Type' 'text/plain charset=UTF-8';
            add_header  'Content-Length' 0;
            return      204;
        }

        if ($request_method = 'POST') {
            add_header  'Access-Control-Allow-Origin' '*';
            add_header  'Access-Control-Allow-Credentials' 'true';
            add_header  'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header  'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }
        if ($request_method = 'GET') {
            add_header  'Access-Control-Allow-Origin' '*';
            add_header  'Access-Control-Allow-Credentials' 'true';
            add_header  'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header  'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }
        if ($request_method = 'GET') {
            add_header  'Access-Control-Allow-Origin' '*';
            add_header  'Access-Control-Allow-Credentials' 'true';
            add_header  'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header  'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        }
    }

    # redirect server error pages to the static error pages
    #

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    location ~ /\.ht {
        deny  all;
    }
}
Dominik
  • 295
  • 2
  • 4
  • 13

1 Answers1

1

It looks to me like those headers haven't been added - strict-transport-security etc. To use the add_header you need to build nginx from source with the headers_more module included. Have you done that? If not there's a tutorial here.

(Update - this was required, not sure why) You may like to try moving the add_headers to within a location block to see if that works better.

Do a simple test - in your main location add a simple, unconditional header, and look at it using curl or Firefox and "Live HTTP Headers" extension.

add_header Z_TESTHEADER "value"
Tim
  • 30,383
  • 6
  • 47
  • 77
  • Thanks for the post. I'm trying to do just that. Is it required to compile it from source because I'm using mainline? Would you have to do that with stable versions as well? – Dominik Mar 19 '16 at 21:15
  • 1
    I believe whatever version you're using you have to compile that module in, but you should check the precompiled binaries first just to be sure. Building Nginx is surprisingly easy, I'd never done it before I wrote it up for that tutorial. – Tim Mar 19 '16 at 21:38
  • I'm not sure where/how to find out if the binaries have `header_more` included. The changelog says it fixed a bug to do with `add_header` so why would I assume it isn't even included in the binaries? – Dominik Mar 19 '16 at 23:02
  • 1
    I can't test as I don't have stock Nginx. Just do what I suggested above, create a basic location with add_header inside it, curl it with the right option to show headers. Also "nginx -V" will show all the included modules. – Tim Mar 19 '16 at 23:22
  • `nginx -V` says nothing about `header_more` so I assume you're right. Will recompile and report back. Thanks for your help mate! – Dominik Mar 19 '16 at 23:29
  • I compiled from source and now get `--add-module=/tmp/headers-more-nginx-module-0.29` on `nginx -V`. Unfortunately even after restarting and even rebooting no headers... – Dominik Mar 21 '16 at 00:20
  • You went through the whole process, configure, build, deploy, verified the module is included, and set up a server with a single location to check it? This process will work, so there will be one tiny thing you haven't done right. If you can't work it out you may need a consultant. Based on what you've said it will be with your nginx configuration or the way you're looking for headers. Create a new location and put an add_header inside it, show us the config and the URL. – Tim Mar 21 '16 at 00:39
  • You're right. I just stripped down my config and added your `Z_TESTHEADER` to get the header added after restarting the service. So must be the config. That goes back to my original question: whats wrong with the config? :) Thanks for the help man. Really did help! – Dominik Mar 21 '16 at 00:43
  • Move one of your existing add_header statements to the new location block. If it works there add_header isn't working within the server context. It should work in the server context, according to the docs, but maybe you need it in each location block for some reason. – Tim Mar 21 '16 at 01:09
  • Ok I have moved the `add_header` outside the server scope and it all works now. Unfortunately the `if` statements are not allowed in that scope so I reduced my CORS to just the four headers and ignore the `Options` clause. If you add an answer I can mark it as correct to close this off. – Dominik Mar 21 '16 at 01:39
  • This is an answer, just mark it off. – Tim Mar 21 '16 at 01:59