[MicroOS] Nginx reverse proxy for Nextcloud not applying headers

Hi,

Have a MicroOS server on Linode running in podman an Nginx ssl reverse proxy to be able to access my Nextcloud installation remotely. This deployment is fairly fresh and yesterday when I finished things up ran a scan with Mozilla Observatory which gave some pretty poor results.
By the look of it when Observatory is scanning the site it is unable to pick up a whole bunch of headers like HSTS, X-XSS protection and so on. Weird part is that when I curl the headers everything is there, but anything I use to scan with fails to recognize the headers.
The container itself is running with the following command:

podman run -d --network=host --name nginx-rp --label io.containers.autoupdate=image -p 80:80 -p 443:443 --mount type=bind,source=/root/podman_vol/nginx,target=/etc/nginx --mount type=bind,source=/etc/letsencrypt,target=/etc/letsencrypt nginx:stable

My nginx config with all the included configs and the server configs is here: https://paste.opensuse.org/96420431

What can be the reason that the headers are not applied ?

I haven’t used Mozilla Observatory before, so I’m working off reading the documentation and a little guesswork…

You may need to actually post output,
You may want to remove the web address for your own protection and privacy as you’re analyzing your security.
Simply saying “unable to pick up… headers” is too unclear. I think you may be saying “didn’t detect” but need to be certain you didn’t actually see output that might provide some hints what is happening. And, it may be important to know if an actual test was performed for that header or if you simply assumed a test was run without knowing for sure (did you see an actual result for each header?)

I don’t know how relevant it might be to your situation, but the scoring system is weird to me… I’m used to scanners providing a binary pass/no pass result for a series of tests, I’m not sure where scoring comes into the picture. I guess I’ll have to actually run the tool a number of times against different websites to get a better feel for what this is about.

Lastly, I don’t know how much experience you have with Mozilla Observatory, it looks like an interesting tool but there are others out there, too.
One source I’d recommend are the web application vulnerability tests that are in the Kali pen testing distro.

TSU

Thank you for your reply!

I’m using Observatory as a general “guideline” to give me an idea what works and what doesn’t, not really into the scoring. Yesterday was able to fix the issue at least with the HSTS on the domain, but subdoamin is giving me some headache as it sends duplicate HSTS header. Got this pretty strong feeling that there is something wrong with my Nginx config… the add_header is inherited from global blocks, but can be overwritten…

There could be several add_header directives. These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level.

The output of the curl for the domain and the subdomain is here: header output - Pastebin.com (sorry, paste-o-o decided that I’m spamming and can’t post there…)

The output for the main domain from Observatory:


|
|
|[Content Security Policy](https://infosec.mozilla.org/guidelines/web_security#content-security-policy)||-20|Content Security Policy (CSP) implemented unsafely.

This includes 'unsafe-inline' or data: inside script-src, overly broad sources such as https: inside object-src or script-src, or not restricting the sources for object-src or script-src.||
|[Cookies](https://infosec.mozilla.org/guidelines/web_security#cookies)||0|All cookies use the Secure flag and all session cookies use the HttpOnly flag||
|[Cross-origin Resource Sharing](https://infosec.mozilla.org/guidelines/web_security#cross-origin-resource-sharing)||0|Content is not visible via cross-origin resource sharing (CORS) files or headers||
|[HTTP Public Key Pinning](https://infosec.mozilla.org/guidelines/web_security#http-public-key-pinning)||0|HTTP Public Key Pinning (HPKP) header not implemented (optional)||
|[HTTP Strict Transport Security](https://infosec.mozilla.org/guidelines/web_security#http-strict-transport-security)||0|HTTP Strict Transport Security (HSTS) header set to a minimum of six months (15768000)||
|[Redirection](https://infosec.mozilla.org/guidelines/web_security#http-redirections)||0|Initial redirection is to HTTPS on same host, final destination is HTTPS||
|[Referrer Policy](https://infosec.mozilla.org/guidelines/web_security#referrer-policy)||+5|Referrer-Policy header set to "no-referrer", "same-origin", "strict-origin" or "strict-origin-when-cross-origin"||
|[Subresource Integrity](https://infosec.mozilla.org/guidelines/web_security#subresource-integrity)||0|Subresource Integrity (SRI) is not needed since site contains no script tags||
|[X-Content-Type-Options](https://infosec.mozilla.org/guidelines/web_security#x-content-type-options)||-5|X-Content-Type-Options header cannot be recognized||
|[X-Frame-Options](https://infosec.mozilla.org/guidelines/web_security#x-frame-options)||0|X-Frame-Options (XFO) header set to SAMEORIGIN or DENY||
|[X-XSS-Protection](https://infosec.mozilla.org/guidelines/web_security#x-xss-protection)||-10|X-XSS-Protection header cannot be recognized|





The output for the NC subdomain from Observatory:

|
|
|[Content Security Policy](https://infosec.mozilla.org/guidelines/web_security#content-security-policy)||0|Content Security Policy (CSP) implemented with unsafe sources inside style-src.

This includes 'unsafe-inline', data: or overly broad sources such as https:.||
|[Cookies](https://infosec.mozilla.org/guidelines/web_security#cookies)||+5|All cookies use the Secure flag, session cookies use the HttpOnly flag, and cross-origin restrictions are in place via the SameSite flag||
|[Cross-origin Resource Sharing](https://infosec.mozilla.org/guidelines/web_security#cross-origin-resource-sharing)||0|Content is not visible via cross-origin resource sharing (CORS) files or headers||
|[HTTP Public Key Pinning](https://infosec.mozilla.org/guidelines/web_security#http-public-key-pinning)||0|HTTP Public Key Pinning (HPKP) header not implemented (optional)||
|[HTTP Strict Transport Security](https://infosec.mozilla.org/guidelines/web_security#http-strict-transport-security)||-20|HTTP Strict Transport Security (HSTS) header cannot be recognized||
|[Redirection](https://infosec.mozilla.org/guidelines/web_security#http-redirections)||0|Initial redirection is to HTTPS on same host, final destination is HTTPS||
|[Referrer Policy](https://infosec.mozilla.org/guidelines/web_security#referrer-policy)||+5|Referrer-Policy header set to "no-referrer", "same-origin", "strict-origin" or "strict-origin-when-cross-origin"||
|[Subresource Integrity](https://infosec.mozilla.org/guidelines/web_security#subresource-integrity)||0|Subresource Integrity (SRI) not implemented, but all scripts are loaded from a similar origin||
|[X-Content-Type-Options](https://infosec.mozilla.org/guidelines/web_security#x-content-type-options)||-5|X-Content-Type-Options header cannot be recognized||
|[X-Frame-Options](https://infosec.mozilla.org/guidelines/web_security#x-frame-options)||+5|X-Frame-Options (XFO) implemented via the CSP frame-ancestors directive||
|[X-XSS-Protection](https://infosec.mozilla.org/guidelines/web_security#x-xss-protection)||-10|X-XSS-Protection header cannot be recognized|



I tried also with ssllabs and Google CSP, gave similar results. hstspreload.org was helpful in fixing the HSTS issue at least on the domain, but the subdoamin is still showing as duplicate. My conclusion is kinda that add_header in Nginx is kinda painful when you have multiple site and based on my research the internet kinda agrees.
Anyhow I’m going to continue debugging the config, fairly positive that the issue is with the way the headers are added and inherited.

A.

Just one thing I would like to clarify, because reading back I sound a bit vague on my problem: my issue is not with the headers or applying them, but with the way Nginx is reading and implementing those which renders duplicate headers on every subdomain I add for some reason.

I guess my question is more like: how to add headers in a way Nginx is implementing it correctly instead of duplicating them?

Found the solution!
Turns out that the internet is full of bad advice - shocker - and pretty hard to find the truth without digging deep enough and testing options. Big thanks for Tsu for the idea to use Kali provided tools for analysis. Modified the original Toolbox scrip to be able to run Kali in podman and used ZAP which revealed a whole bunch of issues. All header related of course.

This blog post helped me: Nginx add_header configuration pitfall · blog.g3rt.nl

TL;DR: Nginx add_header inheritance sucks. Using 3rd party modules sucks even more. Need to add the required headers to a snippet and include that in the server block of the configuration. From there every location block can have it’s own add_header directive without overwriting the original headers or duplicating those. It can definitely become an overhead with bigger, more complex installations - like mine, which it is… -, but at least it works.

-SOLVED-