commercialhaskell / stack

The Haskell Tool Stack
http://haskellstack.org
BSD 3-Clause "New" or "Revised" License
3.98k stars 845 forks source link

Improve security of docker options in stack.yaml #260

Open borsboom opened 9 years ago

borsboom commented 9 years ago

Currently, if a user has docker installed and set up so their user can run it without sudo, it's possible to craft a stack.yaml that runs arbitrary commands as root. This is due to well-known security weaknesses with Docker and basically any arbitrary code you run on your system (like, say, downloading an executable or shell script, or building a Haskell package that uses TemplateHaskell) could also take advantage of it), so this is far from unique to stack.

That said, I'd like to at least close the hole with stack.yaml, so that if you're doing your builds in a Docker container security is improved. I propose to disallow the following settings in the project's stack.yaml (but still allow them in global/user stack.yamls and from the command-line).

Since a user may want to change these settings for a project, adding the option of a "local" project config inside .stack-work may be desirable (kind of like you can add extra git excludes in .git/extra/excludes).

In addition, any time a new image is encountered in a stack.yaml, an explicit action should be required to approve it with a big warning to not approve untrusted images. Anyone could craft a malicious image and point the repo setting to it, but I don't think we want to be in the business of whitelisting images.

I also considered using the docker run -u option to always start containers as a non-root user, but anyone could still craft an image that has an entrypoint which sudos to root, so that doesn't end up helping very much.

gregwebs commented 9 years ago

More concretely, the attack scenario is: I clone a project because I want to patch a library, so I start by running stack build. Generally build systems have escape hatches to execute arbitrary code, but it is limited to the user's permission. If I have docker installed, the build process could escalate its privileges to root!

I think this is getting at a larger problem for library authors; there is a stack.yaml that you would check in and distribute, and there are stack.yaml settings that you only want to develop with. Probably STACK_ROOT is meant to solve this, but in this case it cannot solve a security issue.

I think it is better to not allow enabling docker from stack.yaml because it does not make sense to distribute that setting to other people. Enabling docker should be done some other way. Obviously env variables and cli options can be used, but eventually I may want to write that setting down and have it be automatically used. The approach we could take is to have a list of docker enabled projects in ~/.stack

borsboom commented 9 years ago

Yes, that is exactly the scenario I was thinking of. Of course, stack itself doesn't really create this problem -- any library could have some TemplateHaskell that uses the same loophole to escalate its privileges even if you're using cabal-install.

We actually really do want to allow you to enable docker and set the image in the project's stack.yaml, because that way you can ensure that everyone on a team is using a precisely consistent build environment. This is, in fact, the primary purpose of the Docker support. However, any new image will require the user to perform an explicit "stack docker pull" so you can't accidentally end up running an unknown image.

gregwebs commented 9 years ago

We actually really do want to allow you to enable docker and set the image in the project's stack.yaml, because that way you can ensure that everyone on a team is using a precisely consistent build environment. This is, in fact, the primary purpose of the Docker support. However, any new image will require the user to perform an explicit "stack docker pull" so you can't accidentally end up running an unknown image.

Restricting to not allow pulling an unknown image helps, but does not solve the problem unless the docker build commands that are automatically executed are restricted to just stack. Maybe this is already the case? I have no idea how the docker integration actually works at this point (I am hoping for some documentation).

gregwebs commented 9 years ago

This may be a little off topic, but I created a shell script in my PATH called docker to override the docker command

#!/bin/bash -e
# The goal of this script is to maintain the security privileges of sudo
# Without having to constantly type "sudo"
exec sudo /usr/bin/docker "$@"

This seems like a much better solution that the docker group that gets widely used today. Maybe I am missing something.

borsboom commented 9 years ago

unless the docker build commands that are automatically executed are restricted to just stack

That is correct, it the only thing it'll run automatically is stack.

And using that wrapper is an excellent idea. I'll mention that in the docs I'm working on.

jwaldmann commented 7 years ago

@gregwebs Does your method https://github.com/commercialhaskell/stack/issues/260#issuecomment-110858705 really work?

I put such a script in my $PATH (at $HOME/.local/bin/docker) and then the initial stack docker pull works, but for stack build I get

...
@(System/Process/Read.hs:306:3)
2017-01-03 13:47:47.461984: [debug] Creating process: /home/me/.local/bin/docker start -a -i d63ea057b976fb01939e1de2e05e6f04207113685593b02a27d9fa4a070f1558
@(System/Process/Run.hs:102:5)
pid1: /opt/host/bin/stack: createProcess: runInteractiveProcess: exec: permission denied (Permission denied)
...

I have stack --version : Version 1.3.2 x86_64 hpack-0.15.0, on Fedora 25, with docker from their distro. I have no experience with docker, but at least sudo docker run ubuntu /bin/echo 'Hello world' does work for me.

gregwebs commented 7 years ago

Works for me