Show HN: Bunkerized-Nginx – Nginx Docker image secure by default
github.com
github.com
Why would someone like to block crawlers and also TOR-clients? The malicious ones will be anyway disguised as normal browsers.
Seems some very critical security mechanisms (such as CSP) still require additional/careful user config, and don't get highlighted by the guides. Might help to add a walk-through or, e.g. link to a good CSP generator tool.
Also I wonder if compiling from source then using `FROM scratch` would improve security even further, similar to this nginx container image: https://github.com/ricardbejarano/nginx
https://chrome.google.com/webstore/search/csp?hl=en-US
Helpful to gather all the assets being loaded and generating the rule sets, it's probably better to work on dynamic websites which may load additional data live.
I had a lot of Google Tag Manager and 3rd party libraries.
According to its readme, it does not check IP addresses, which means an attacker can just complete the CAPTCHA once, and copy their session to as many computers as they like. So you probably want to set session_check_addr.
But I've never used resty or lua, so I may be misreading how your code works.
Are you sure that this is actually more secure?
Really isn’t of concern at my current scale, but Nginx offers some margin of speed over Caddy.
(secure) {
jwt {
path /
redirect https://auth.mydomain.com/login?backTo=https://{host}{uri}
except /api
except /rest
except /zm/cgi-bin/nph-zms
except /zm/api
}
}
The "except"'s are for some services I use that have some kind of api-based auth for reaching certain endpoints that need to be whitelisted. This approach (of importing this "secure" block in each caddy block, that's just what I called it, "secure" isn't a special word) has the downside of requiring a global list but makes it easier as you will see in just a minute. Also there wasn't any overlap of "services that have api endpoints using api keys" and "services that have the same endpoint but it needs to be under SSO auth". Moving on, after that block I have my "auth" url, again you can name this whatever: auth.mydomain.com {
login {
simple username=password
jwt_expiry 24h
redirect_check_referer false
redirect_host_file /root/.caddy/hosts
cookie_expiry 2400h
cookie_domain mydomain.com
}
}
The "simple username=password" is the line doing a lot of the heavy lifting but you can replace that with something that uses a different SSO provider (like Google, LDAP, etc). I have a random U/P that I store in 1Password so "simple" is fine for me but I've wanted to setup Google's OAUTH to at least test it out. The other big thing here is "/root/.caddy/hosts", this is a list of all the domains/hosts (one per line) that you want to be able to redirect to. This is so that if you go to "gitlab.mydomain.com" and you aren't logged in it will bounce to auth.mydomain.com and then, because you put "gitlab.mydomain.com" in that "/root/.caddy/hosts" file, it will redirect back to "gitlab.mydomain.com" once you login. Gitlab may be a bad example since you will absolutely be using GL's auth as well but substitute Gitlab with other services that either don't provide auth or provide some basic auth you can turn on/off, that's where this really shines. Once you are logged into 1 of them you are logged into all of them.Lastly we have our actual, regular, caddy entires: (and looking at mine I see I should have been using Syncthing as the example all along haha)
syncthing.mydomain.com {
import secure
proxy / 10.0.1.123:8384 {
transparent
websocket
}
gzip
tls myname@mydomain.com
}
I will note that you might not need the "websocket" line anymore but it works and I'm not touching it. That "import secure" line is what "protects" the Syncthing service.It has been a breeze to add/remove services that I want to stick behind auth and I'm very happy with it. A couple caveats: I still need to update to Caddy 2, it came out shortly after I did all this work I decided to sit back and wait for the dust to settle. Also I wasn't able to use the default caddy docker image, it needs some extra plugins. This may no longer be the case for Caddy 2, I don't know. I ended up just making my own image [0] (/Do not use this/, really, don't. I'm not going to keep it updated, I make no promises, and it's a huge security risk IMHO) by forking the repo and adding the extra plugins I needed [1]. You can do the same and build locally or maybe you don't even use docker and so this is a non-issue. The two plugins I needed were "jwt" and "login".
I hope this helps and I can answer any other questions you might have about it.
[0] https://hub.docker.com/r/joshstrange/caddy
[1] https://github.com/joshstrange/caddy-docker/blob/master/Dock...
On a slight tangent, this is a good thing to look out for when writing docs or other explanatory notes that include code/config samples - if your sample includes names that could be confused for some semantically meaningful thing, change the name to disambiguate or mention it in the sample explanation, like above.
I wrote a whole blog post [0] about this that just completely slipped my mind. I think my comment does a good job of getting the same points across but I can’t believe I spent 20-some minutes regurgitating this information haha.
[0] https://joshstrange.com/securing-your-self-hosted-apps-with-...Does this really help? Especially blocking TOR and whole countries seems bad. As for blocking user-agents: why? Sorta seems like when cisco "fixed" a vuln by blocking the curl user agent.
For example, this image might be good for someone who wants to host an internet facing service but only for a few friends, for example a some sort of git forge.
How does blocking user-agents, countries, TOR or proxies help with hosting a git forge for a few friends?
Every morning I get an email from logwatch showing me activity on the server, particularly failed http requests and "attempts to use known hacks" (which are just pattern matches on URL requests).
I used to get hundreds of lines of logs PER DAY of this stuff..
Then about a year ago I found a list of IP subnets for China and Russia, and blocked all of it at the OS level with the firewall, and now I get almost nothing in my logs anymore (every so often I add a new subnet to the blocklist, usually from eastern Europe).
I can't see any reason why my server that hosts personal stuff would need to see traffic from those countries (and I'm fine with blocking false positives - YMMV).
I know it's not a foolproof security measure and if anyone was specifically targeting me this would not stop them, but it helps with all the drive-by scanning.
There is a TON of automated scan traffic coming out of China and Russia specifically that is constantly scanning IP blocks known to belong to hosting providers.
These are web scanners trying to find common vulnerabilities in typical web software (phpMyAdmin, Wordpress, Rails, etc). But also potentially exploits in nginx or other parts of the stack for all I know.
And they send requests by the hundreds, which pollutes my logs and my logwatch emails.
I have automatic security updates turned on for my server, but it's not my full-time job to manage it, so I want to maximize the return on the time I do spend managing it.
And since there is zero value/reason in allowing access to my server by anyone in those countries, why not just block it all and have one less thing to worry about, since 99% of that automated traffic comes from there?
Sure, if this worked. But it's not 2010 any more. Simple stuff like this only stops the most technologically illiterate attacks.
If your site is being attacked by more than bubba the local "computer guy" - they will be using proxies indistinguishable from your normal traffic, assuming you do any volume at all.
It's just a silly exercise in futility. I put it up there with moving sshd from port 22 to a random port. Yeah, you get less spam I suppose, but it sure as hell didn't make you any more secure. Probably the opposite.
Outright blocks of "bad IPs" are not that useful in my opinion. However, developing reputation systems around IPs and using "is tor" as one metric certainly has shown it's uses in my field of work.
If 90% of spam that requires human moderation comes from those, this seems like an excellent starting point though, as indeed is moving your ssh port. Security is in layers.
Just blocking parts of the internet does nothing to help the problem and simply introduces other problems.
Time is money, and for most businesses or people, this is the most important metric. If I can spend 5 minutes on an 80% solution, or 5 days on a 90% solution, I will absolutely start with the 80% solution and see how things develop.
A config generator seems obviously better since anyone who knows what they're doing can use it with either nginx installed from their distro's repos, or with an nginx docker container.
[0]: https://ssl-config.mozilla.org/#server=nginx&version=1.17.7
EDIT: or running as separate containers on the host - the point is that these are security provisions you want to apply across all containers, not just for Nginx.
Surely all the complexity should be inside the container and the surrounding host should be as vanilla as possible.
Isn't the idea to push the complexity into the layer where builds are reproducible and state is controllable?
I am probably misunderstanding something here. I'm fairly new to this.
In regards to docker worldview, this project currently doesn't follow best practices.
And while I agree mostly with this statement:
> Surely all the complexity should be inside the container
The caveat being that complexity should be split up into separate concerns. Otherwise there's little difference between the host and container aside from an extra layer of abstraction.
For example, this repo should probably be split into several containers: cert management should probably be its own container, which a shared volume for certs); php should be rolled into its own container, and php files should reside there; logging shouldn't be handled at the container level; firewall concerns (namely fail2ban) probably should be handled at by the host, or in a container with appropriate permissions; etc
And yes, you want the host as vanilla as possible. Generally just running the orchestration layer (Kubernetes, Swarm, Nomad) and maybe some logging / metrics stuff (and, depending on who you ask, sshd).
To distribute security to the single images/VMs increases complexity and the likeliness that some image/VM will miss some security filter, and leaves the host itself unprotected (e.g. network time sync & ssh & other stuff will probably be running, any update to the host's SW might result in unexpected services running, etc...).
An additional (dedicated) layer of security in the images/VMs would of course still be ok.
For example a reverse proxy container which redirects to a gitea container or a wordpress container depending on the request. The reverse proxy container can also centralize the security with certificate handling or fail2ban.
Did you know the linux kernel requires you to set an init? If you check what you were booted with (/proc/cmdline), it probably includes something like `init=/usr/lib/systemd/systemd`.
Docker requires you to set a single entrypoint not because it's "baked in", but because that's the way linux works: when you create a new namespace, you run a single program as its pid1, just like when you boot linux, a single program is pid1.
It's very easy to set pid1 of a docker container to runit or various other process runners to run multiple processes.
Arguing otherwise is no different than arguing that linux only supports running one process because if you set "init=/bin/bash" at the cmdline, it gets ugly real fast to run a multi-process system off it.
These are problems that don't actually exist by default for most situations. They imply a need for, well, something like Bunkerized-Nginx. But the need necessarily implies something less secure than a simple repo nginx install. In most cases this is the tail wagging the dog.
> Block TOR
Why the hell should I block TOR? I better block users from mass testing passwords on my login masks, but do not distinguish them of their origin.
Back in the day, they claimed (stupidly) that tor users where rife with DDoS attacks. This, of course, was a naked money grab by CF to scare non-technical site owners into use CF to block nasty tor users.
Also, during the (still ongoing) data collection hype train, site-owners like the idea of being able to individually identify every user, and fuck everyone else ... as is their right by Tech Decree.
So now we are at a place where just caring about your privacy, despite all of the overwhelming evidence that this is exactly what we should be doing, is prima facie proof that you are a HaX0r or, even worse, you are not exploitable for databux!
I've opened them an issue just in case https://github.com/bunkerity/bunkerized-nginx/issues/11
A web server need root privileges in order to bind ports 80/443.
But in this case it’s only the primary Nginx process that will run as root. The subprocesses will run as a non-privileged user, as specified with the “user nginx” directive.
https://github.com/bunkerity/bunkerized-nginx/blob/master/co...
https://unix.stackexchange.com/questions/134301/why-does-ngi...
Starting as root for the sole purpose of binding to a low-numbered port and then dropping privileges is an outdated practice that is both difficult to program correctly and arguably unnecessary today.
[1] https://www.archlinux.org/packages/extra/x86_64/nginx/
[2] https://github.com/archlinux/svntogit-packages/blob/packages...
A quick googling tells me that FreeBSD has something similar: https://gist.github.com/TomHetmer/b0a048d688af78e78f45609880...
PS. If I would have bothered to read the whole Stack Exchange article I linked to, that capability is even mentioned there (^_^) https://unix.stackexchange.com/a/134324/68449
sadface
At least have a good reason for doing this instead of outright doing it for 'Security'.
As far as I'm concerned you've made the site less privacy friendly. At least redirect them to an onion of indded are shaving serious problem at some point in the Future.
Identity is the bread and butter or mass Surveillance. The two can exist with spam protection.
This problem might need a good solution to be thought of that aren't bans and extemely hard capthaa in every single page (cloudfares half baked 'solution')
It's kind of sad that they've taken over the "security" moniker, and the general compute industry is totally fine with them doing so.
- everything is easily configurable by env vars so the features that some are hating are on demand
- it is open source, so you can fork it and make your on version. You can even contribute back to improve it instead of posting hate comments on HN
There's big discussion of it here: https://github.com/kubernetes/kubernetes/issues/33554
Nginx is highly optimized zero-copy sockets-reuse code, while docker's network code is crappy, does NAT and other stuff.
I would classify it as unnecessary, redundant set of abstractions. Definitely for production.