- The Docker daemon runs as root: any user in the docker group effectively also has sudo (--privileged)
- Ports exposed by Docker punch through the firewall
- In general, you can break the security boundary towards root (not your user!) by mounting the wrong things, setting the wrong flags etc.
What Docker primarily gives you is a stupid (good!) solution for having a reproducible, re-settable environment. But containers (read: magic isolated box) are not really a good tool to reason about security in Linux imo.
If you are a beginner, instead make sure you don't run services as the sudo-capable/root user as a first step. Then, I would recommend you look into Systemd services: you can configure all the Linux sandboxing features Docker uses and more. This composes well with Podman, which gives you a reproducible environment (drop-in replacement for Docker) but contained to an unprivileged user.
It's unfortunately very common to install, for example, a project as the "ubuntu" user and also run it as the "ubuntu" user. But this arrangement effectively turns any kind of file-overwrite vulnerability into a remote-execution vulnerability.
Owning executables as root:root, perms 0755, and running as a separate unprivileged user, is a standard approach.
To take this further, it's also possible to enforce that executables are confined to a few well-understood directories with good permissions by using the noexec option when mounting. Directories with executables are mounted normally (with locked down write permissions) and everything else mounted with noexec.
I use this on Nix OS. There all executables live under the read-only directory /nix/store. So you can mount / with noexec, giving the nice property that all files that are writable are by definition not executable.
I've been using ufw-docker [1] to force ufw and docker to cooperate. Without it, Docker ports do actually get exposed to to the Internet. As far as I can tell, it does its job correctly. Is there another problem I am not aware of?
Docker is for organizing things for yourself, just like directories are. If you want actual isolation you have to take extra steps.
EDIT: and I feel like I should add those extra steps are exactly what most server software does automatically when it chroots itself. Again docker is really just for organizing things.
Services have the following dependencies: static data files; configuration files; executable code/binaries; library dependencies.
In days of yonder, you'd need to download/install all of that ^ on each machine where "service A" needs to run. Developers would run and test "service A" on ubuntu 18.04. But production servers had to run ubuntu 16.04 because "service X" that also runs on the same server needs a library that has not been ported to 18.04 yet.
But "service A" needs a library that was never available on 16.04. Welcome to dependency hell!
Containers bundle all of those dependencies into one object that can be downloaded directly onto the host server, ready for the "service A" process to execute. Now it doesn't matter if production servers are running 16.04. Everything "service A" needs is stored inside the container blob (including some minimal ubuntu 18.04 stuff).
the magic that lets this happen -- containers re-use the host server's OS kernel. Running a new ubuntu 18.04 container does not start a new OS kernel running. the process for your container is just 'firewalled' off from all other processes using cgroups [0]. containers re-use the host's kernel, start a cgroup'd process which starts your container's services and processes (the 18.04 'OS' services and your binary/code/executable).
short/simpler version: containers share the core of the underlying operating system on the host server.
> If you want actual isolation you have to take extra steps.
unfortunately, this means containers share the core of the underlying operating system on the host server.
containers not being isolated from the host server OS can present a security risk as you can escape from the container and "do bad things to host server". [1]
In cases where that is a problem you mostly have two choices:
* use VMs instead (a completely isolated OS instance is started for each service, cannot interact with the host OS at all -- this uses a lot more memory/cpu)
* use rootless containers [2] (container processes are launched under a specific user namespace rather than kernel namespace -- escaping the container means you only get access to the user namespace)
[0]: https://en.wikipedia.org/wiki/Cgroups
[1]: by default the docker daemon service and all the container processes it starts are running as root, which means escaping out of a container in a a default docker installation is as bad as giving someone root.
Docker containers don't have mount permissions by default.
The idea is to make your app immutable and store all state in the DB. Then, with every deployment, you throw away the VM running the old version of your app and replace it with a new VM running the new version. If the VM running the old app somehow got compromised, the new VM will (hopefully) not be compromised anymore. In this regard, this approach is less vulnerable than just reusing the old VM.
Containers have essentially 3 advantages:
- Restart the containers after they got pwned, takes less than a second to get your business up and running again.
- Separation of concerns: database, reverse proxy, and web service run in separate containers to spread the risk, meaning that an attacker now has to successfully exploit X of the containers to have the same kind of capabilities.
- Updates in containers are much easier to deploy than on host systems (or VPSes).
Sorta: yes the container is immutable and can be restarted, but when it does, it has the same privs and creds to phone up the same DB again or mount the same filesystem again. I'd argue touching the data is always the problem you're concerned about. If you can get an exec in that container you can own its data.
For VMs, they did take off and essentially the entire cloud ecosystem runs on mostly VMs behind the scenes for VPS and similar hosting.
It's true though at it seems more popular for developers to reach for containers when they need to think about deployments, particularly docker containers. But VMs are still widely in use and deployed today.
(I helped build digitalocean from zero the pre-IPO, so I'm verrry rusty, this all might be nonsense/wrong think, and happy to be told as much! :))
And, either way, do you have a thought on whether you'd still prefer a docker approach?
I have some on-prem "private cloud"-style severs with proxmox, and just curious about thinking through this advice.
Edit: Im aware its not truly immutable (read only) but you can reset your environment very easy and patching also becomes easier.
If ssh password out would be no problem, then why are so many APTs "wasting" their botnets with credentials stuffing?
Your assumption is wrong, and policies for key based auth eliminate the problem quite easily. Versus on the other hand: are you checking every colleague's password for length, charset, etc? All the time? On every server?
Probably not.
On SSH password auth: its secure if you use a long, random, not reused elsewhere password for every user. But it is also very easy to not do these things. SSH certs are just more convenient imo.
For ssh, the problem does not lie within password auth itself, but with weak passwords. A good password is more secure than a keypair on a machine whose files you can't keep private.