Alpine Linux Docker images have NULL for root password
cve.mitre.org
cve.mitre.org
$ docker run -it -u guest alpine
/ $ su
su: must be suid to work properly
/ $ login
login: must be suid to work properly
/ $ find / -perm /4000 -print
find: /root: Permission denied
find: /proc/tty/driver: Permission denied
/ $The link from the CVE says "Due to the nature of this issue, systems deployed using affected versions of the Alpine Linux container that utilize Linux PAM, or some other mechanism that uses the system shadow file as an authentication database, may accept a NULL password for the root user." - so the config would have to install PAM with suid binaries, and configure shadow passwords and not change the password. This is pretty unlikely overall.
On their website they claim docker is "quite secure" if you "run your processes as non-privileged users inside the container", but I do not see how this would be the case given that breakouts happen all the time, AND again, it requires root to run, which gives me the opposite vibe of secure.
Docker is for convenience and it is a security risk.
All a container is, is a linux process with added security constraints. So, if you're talking about an escalation from an unprivileged process inside a container, you need a standard Linux privesc attack (nothing to do with Docker) and you also need to bypass the isolation mechanisms on that process (namespaces, capabilities, seccomp filter, SELinux/AppArmor)
# docker run -it -p 2222:22/tcp alpine
/ # apk add openssh-server
/ # for t in ecdsa ed25519 rsa ; do ssh-keygen -t $t -C root -P '' -f /etc/ssh/ssh_host_${t}_key ; done
/ # echo PermitRootLogin yes >> /etc/ssh/sshd_config
/ # echo PermitEmptyPasswords yes >> /etc/ssh/sshd_config
/ # /usr/sbin/sshd
Then, on another machine: ssh -p 2222 root@host> 9.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
(Leaving my opinion out of this.)
If you can do what you said with a non-wheel/sudo account, that would be a serious vulnerability.
suid binaries are binaries with a special flag set that will make it run with root privileges regardless of who started it.
sudo is an example of something that would use suid. When a user runs sudo, the binary actually runs with root privileges from the get-go, checks if the user is OK, then executes the command you specified.
However, use of sudo or other suid binaries is entirely pointless in an alpine container. There being no password also does not matter, as you are by default already running everything as root. Who cares if root can become root?
Best practice would have you switch to a non root user before running whatever it is inside the container. Although if you haven’t added any suid binaries by accident then there’s no way to go back.
E.g. the node alpine image adds a “node:node” user and group for the process to run as instead of root. https://github.com/nodejs/docker-node/blob/master/10/alpine/...
"Xless" is just a different way of saying "without X". "fearless" = "without fear"; "serverless" = "without a server" (yes, I know, not really); so "distroless" = "(comes) without a distribution"
So in my opinon the name correctly suggests that it's "just the package" and comes "without a distro", and not "was built without the help of a distribution".
No, they are not.
Debian packages are deployable packages, which are built around packages and services and conventions adopted and provided by the target distribution.
Not to mention, you still need runtimes if you are programming in Java, Python or many other languages. https://github.com/GoogleContainerTools/distroless project gives a way to have these runtimes + their lib dependencies while still maintaining a minimal attack surface.
https://aws.amazon.com/blogs/aws/new-tls-termination-for-net...
>Today we are simplifying the process of building secure web applications by giving you the ability to make use of TLS (Transport Layer Security) connections that terminate at a Network Load Balancer (you can think of TLS as providing the “S” in HTTPS). This will free your backend servers from the compute-intensive work of encrypting and decrypting all of your traffic, while also giving you a host of other features and benefits:
I think may main point is that kneejerk reactions to satisfy a security list checkbox are just as useless as a default "encrypt everything everywhere" stance must be better.
IMHO this should be viewed as a big, glaring anti-pattern, as it fundamentally puts security team goals at odds with product team goals.
we once saw this from some other company where they noticed we where talking to the competitors and wanted to talk.
I think the start of this thread was a plea not to terminate HTTPS at the edge, but instead to plumb it all the way to the serving container. That's unlikely to be mTLS in any case.
Which is a truly rare case as many backend APIs these days are mandatory secured by HTTPS (or LDAPS, SMTPS, IMAPS to name a couple other openssl-based secure protocols).
The downside of plumbing directly to the container is that you lose many of the routing features of a service mesh. If it can't inspect the traffic, it can't do layer 7 routing. It can only route and shape at layer 4.
The `static` image is very bare minimal and only contains things like nsswitch.conf and ca certificates. This is the recommended base image for statically linked languages.
There's also a `base` image that I usually use as the base image of C programs, e.g. stunnel/unbound DNS. In those cases, I usually use Debian Stretch as build environment (distroless uses binary from Debian stable, so ABI is compatible), build a dynamically linked binary, then copy the result binary along with all dynamic object files to a distroless base image.
So when I talk about openssl in the image, I was referring to the `base` flavor. If you are happy with the provided openssl version, the openssl in the base image is indeed useful.
or you can just build gdb into the container and run the process under gdb, then attach to the tty.
or you can debug from the host system where the container's pid namespace is a descendant of the root namespace and the other namespaces can be accessed via /proc or unshare.
https://github.com/clearlinux/dockerfiles/tree/master/python
Perhaps the point is not to enable program execution, but to make use of the benefits that may come with container orchestration.
Depends on your workload, of course. Some people want to run a huge number of containers and each isn’t compute intensive.
Or maybe you don’t use libc at all in your fast path?
Lots of cases it makes sense.
They see Alpine at 5Mb and Ubuntu at 80Mb. They mentally multiply, without realising that each of these will be pulled once for each image built on top of them.
For a large cluster it's a wash. You might as well use Ubuntu, Centos -- anything where there are people working fulltime to fix CVEs quickly.
I do agree that people prematurely optimise and mainly incorrectly consider disk space but I think there's a decent use case for tiny images.
There's a case for tiny images, but it's in severely-constrained environments. Otherwise folks are fetishising the wrong thing based on a misunderstanding of how container images and runtimes work.
I just downloaded Ubuntu 18.04 and 19.04 and they are not 40MB:
$ docker image ls | grep ubuntu
ubuntu 19.04 f723e3b6f1bd 76.4MB
ubuntu 18.04 d131e0fa2585 102.0MB
ubuntu 16.04 a51debf7e1eb 116.0MB
How do you get a 40MB Ubuntu Docker image?---
I followed @sofaofthedamned — https://blog.ubuntu.com/2018/07/09/minimal-ubuntu-released
But I’m still confused, where are they getting 29MB from? Even the compressed files are big:
Ubuntu Bionic [1]
- ubuntu-18.04-minimal-cloudimg-amd64-root.tar.xz | 77M
- ubuntu-18.04-minimal-cloudimg-amd64.img | 163M
- ubuntu-18.04-minimal-cloudimg-amd64.squashfs | 96M
Ubuntu Cosmic [2]
- ubuntu-18.10-minimal-cloudimg-amd64-root.tar.xz | 210M
- ubuntu-18.10-minimal-cloudimg-amd64.img | 295M
- ubuntu-18.10-minimal-cloudimg-amd64.squashfs | 229M
Ubuntu Disco [3]
- ubuntu-19.04-minimal-cloudimg-amd64-root.tar.xz | 69M
- ubuntu-19.04-minimal-cloudimg-amd64.img | 155M
- ubuntu-19.04-minimal-cloudimg-amd64.squashfs | 89M
[1] http://cloud-images.ubuntu.com/minimal/releases/bionic/relea...[2] http://cloud-images.ubuntu.com/minimal/releases/cosmic/relea...
[3] http://cloud-images.ubuntu.com/minimal/releases/disco/releas...
Apologies, it's actually 29mb.
There are also slimmed down images based on Debian or Ubuntu. A number of packages is a bit older versions, though.
Do you get all developers to agree on which base image to build all their services from?
I heard about this "oh, it's shared, don't worry" thing before. It started with 40MB. Now that supposedly shared image is half a gig. "Don't worry, it's shared anyway". Expect when it isn't. And when it is, it still slow us down in bringing up new nodes. And guess what, turns out that not everyone is starting from the same point, so there is a multitude of 'shared' images now.
Storage is cheap, but bandwidth may not be. And it still takes time to download. Try to keep your containers as small as possible for as long as possible. Your tech debt may grow slower that way.
Images built from dockerfiles can do this too, but it requires some degree of centralisation and control. Recently folks have done this with One Multibuild To Rule Them All.
By the time you're going to the trouble of reinventing buildpacks ... why not just use buildpacks? Let someone else worry about watching all the upstream dependencies, let someone else find and fix all the weird things that build systems can barf up, let someone else do all the heavy testing so you don't have to.
Disclosure: I worked on Cloud Native Buildpacks for a little while.
In production, the smallest box has half a gig of RAM.
In development, it's indeed a single box, usually a laptop.
> all developers to agree on which base image to build all their services from
Yes. In a small org it's easy. In a large org devops people will quickly explain the benefits of standardizing on one, at most two, base images. Special services that are run from a third-party image is a different beast.
> Storage is cheap, but bandwidth may not be.
Verily! Bandwidth, build time, etc.
Just run the process. Let whatever is managing your container restart it if the process quits, be it docker or K8s.
There's plenty of other reasons.
[Edit - fix link]
In most cases with Alpine-based containers, the only process is the one that you actually want to run.
Add to that that modern Ubuntu uses systemd which greatly exhausts the system’s inotify limits, so running 3-4 Ubuntu-containers can easily kill a systems ability to use inotify at all, across containers and the host system. Causing all kind of fun issues, I assure you.
So the cost is not just about disk-space.
Disclaimer: more experience with LXC than Docker.
For Python in particular, significantly slower.
[0] https://github.com/docker-library/python/issues/160 [1] https://gist.github.com/blopker/9fff37e67f14143d2757f9f2172c...
If your software is I/O-bound, and is written in a language like Python or Ruby, and sits idle waiting for requests for significant time anyway, CPU performance is likely not key for you. This also represents the majority case, AFAICT.
then you look at: https://sourceware.org/git/?p=glibc.git;a=blob;f=string/strl...
and your mind will sort of explode for a bit. The difference is the GLIBC version is dramatically faster; take the comments away and most of us wouldn't even know that's strlen. It's more complex, no question, but it's much faster. GLIBC is full of stuff like that. qsort and memcpy are non-obvious to many folks. It's not complexity for no reason, you'd be challenged to build a better qsort than the one in glibc, it's not easy.
[1]: https://github.com/bminor/glibc/blob/master/sysdeps/x86_64/s...
http://ridiculousfish.com/blog/posts/old-age-and-treachery.html
All of those old Unix programs aren't fast by being simple and clean on the inside. Old age and treachery...For me it's mostly a non-issue. But things like redis or nginx work nicely with alpine as a base. And more than likely, whatever I want to containerize has already been containerized for Alpine. If not, getting something to work on Alpine may just not be worth it...
Might you have any citation(s) for this? I have not heard this before. Could you elaborate on what some the "wide variety of software" is? Thanks.
Worst case for slower processes, things take longer. Worst case for more disk use, things start crashing. For general cases, the former is preferable.
Following some links from the CVE, you can find the details (from https://talosintelligence.com/vulnerability_reports/TALOS-20...):
> In builds of the Alpine Docker Image (>=3.3) the /etc/shadow file contains a blank field in place of the encrypted password
> ...
> The net result of a blank sp_pwdp field is that the system will treat the root user as having no password, rather than a 'locked' account if a ! or is explicitly specified.*
For those not super familiar with how Unix/Linux password files work, if the field is non-empty, the system will collect a password, hash it, and compare for a match. If the field is empty, the system will just skip prompting for a password and log you in after entering a username.
Arguably, this is crappy design and a better design for /etc/shadow would be to require some kind of explicit, very obvious value like "NO-PASSWORD-REQUIRED".
I think pretty much all Linux distributions use PAM nowadays (although I'm sure there are exceptions).
From pam_unix.so(8):
> The default action of this module is to not permit the user access to a service if their official password is blank.
Unfortunately, many distributions pass the "nullok" option to the pam_unix.so module, overriding this default behavior. Removing any instances of "nullok" from any files in /etc/pam.d/ is highly recommended -- unless you explicitly want this behavior (which could be a reasonable choice, in some specific use cases).
Also the text of the CVE is more clear.
> contain a NULL password for the `root` user
It's no password.
"systems deployed using affected versions of the Alpine Linux container which utilize Linux PAM, or some other mechanism which uses the system shadow file as an authentication database, may accept a NULL password for the `root` user"
So if you're not using a container running ssh/etc, it doesn't affect you.
Serious question: what's the use of PAM in a docker image that's vulnerable here?
> docker run -it alpine:3.$i head -n 1 /etc/shadow
The container process itself is running as the locally namespaced root account anyway.
From my possibly incorrect interpretation... unless you are trying to build containers like VMs with multiple users and init etc, this null root password issue seems irrelevant.
It means the host is protected from privilege escalation in the container.
That... Did not take long. From fix, to regression on a fairly serious security issue (though perhaps not critical, depending how the container infrastructure is set up).
> Unfortunately, later that same year, a commit was pushed to simplify the regression tests. This lead to logic that may have caught this regression being simplified, causing these tests to be incorrectly 'satisfied' if the root password was once again removed.
Testing is hard, and ensuring you meet the requirements but keeping it readable can be harder.
Emphasis mine.
If this tells us something is that how inconsequential is that.
A lot of software already runs with uid 0 inside a container. So, if you got an RCE in that software, you've got a container root anyway. Containers normally do not expose anything else but the target daemon to the network, so cracking in through a non-privileged daemon is unlikely.
Container's root does not map to host's root, so any intrusion is most likely limited to the container if detected soon enough (though it's far from being bullet-proof).
However, root in a container does have many privileges dropped and seccomp policies applied (assuming you haven't turned seccomp off via k8s).
It depends how you have things set up, and what the container is actually doing, and what it's linked to. So it may not be as critical as root on the server, but it may also allow some damage that is more than irritating to happen.
I would call it serious, but not critical.
Or to put another way, under what condition is this actually helpful defense in depth?
Nothing should be able to login locally to an Alpine container. All authentication should be public key based to begin with.
What would the root password be if not null?
Deactivated, allowing no root login.
EDIT:
> Seriously, if an attacker is in a position to exploit this, hasn’t everything already failed?
Not necessarily I think. Something might support PAM auth (by accident or intentionally) and it wouldn't allow anything if no/only accounts with strong passwords can be logged into, but obviously breaks once root is available for login.
> 2019-03-01 - It was discovered that this issue was also reported and made public in their Github prior to our report, but was not flagged as a security issue and thus remained unresolved until it was rediscovered and reported by Cisco.
https://talosintelligence.com/vulnerability_reports/TALOS-20...
That issue is claimed to have been fixed, with a reference to a commit of the updated images, says issue 430 is a security issue and closed, but no link to the actual fix.
Word to the wise folks: If you are fixing bugs by posting binaries, it's a good idea to include a reference to the git hash of the actual fixes you've built those binaries with.
The official image was also affected, too, it appears.
$ docker run -it alpine head -1 /etc/shadow
root:!::0::::: » docker run --rm -ti alpine:3.8 sh -c 'cat /etc/shadow | grep root'
root:!::0:::::
The others you mention though, I agree, they look less than fine.Also, has anyone reported this to the official Alpine repository? (since the Talos disclosure seems to be confused; it says official but has URLs to the Glider Labs version?)
Edit: Ah, so here's the relevant GitHub issue for official Alpine Linux docker: https://github.com/docker-library/official-images/pull/5516
<=3.5 is no longer supported. Everything newer is patched.