New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dockerd: make -H tcp://... even harder without --tlsverify #37299
Conversation
bedc4d9
to
7962115
Compare
Codecov Report
@@ Coverage Diff @@
## master #37299 +/- ##
=========================================
Coverage ? 35.03%
=========================================
Files ? 609
Lines ? 45005
Branches ? 0
=========================================
Hits ? 15769
Misses ? 27123
Partials ? 2113 |
WoW is broken because it uses
|
Historically we have been a little bit too forgiving to people who run Docker with an unauthenticated API server, and recent events[1] have shown that a worrying amount of people are running Docker daemons with the API accessible from the internet. For a long time Docker has not used a TCP port by default, and using it without --tlsverify has elicited an all-caps warning for a while as well. But obviously this is not sufficient (maybe people aren't noticing the warnings). We cannot just disallow this (because it is in theory possible to set this up safely, and we use it ourselves in testing), so instead add a new flag with a scary name to make sure that users are aware of the significant risk of binding the API server to a TCP port without any authentication: % sudo dockerd -H tcp://0.0.0.0:1337 --give-the-internet-root-access While it might sound like a bit of a joke, these sorts of "scary and very obvious" flags exist in other tools. For instance, flashrom requires you to use the flag "i_want_a_brick" if you are trying to flash a laptop's ROM in a way it doesn't think is safe (and anyone reading such an option should recognise why the flag has such a scary name). [1]: https://kromtech.com/blog/security-center/cryptojacking-invades-cloud-how-modern-containerization-trend-is-exploited-by-attackers Signed-off-by: Aleksa Sarai <asarai@suse.de>
7962115
to
658cc5c
Compare
What do you recommend for dind usecase? |
@@ -72,7 +73,8 @@ func (o *daemonOptions) InstallFlags(flags *pflag.FlagSet) { | |||
flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file") | |||
|
|||
hostOpt := opts.NewNamedListOptsRef("hosts", &o.Hosts, opts.ValidateHost) | |||
flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to") | |||
flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to bind to") | |||
flags.BoolVar(&o.InsecureHostBind, "give-the-internet-root-access", false, "Allow -H to bind to a TCP address without --tlsverify (this could allow anyone on your network to get root access to your *host*)") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think we will support rootless mode. Just --insecure-bind-tcp
seems fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think --insecure
is scary enough. Maybe --allow-internet-facing-remote-code-execution
or something? 😉
if proto == "tcp" && (serverConfig.TLSConfig == nil || serverConfig.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert) { | ||
logrus.Warn("[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]") | ||
if !cli.Config.InsecureHostBind { | ||
return nil, fmt.Errorf("refusing to bind to an IP address without --tlsverify configured: use --give-the-internet-root-access to ignore this error") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will break existing deployments.
Probably we should have some grace period? (cc @thaJeztah )
Or maybe we could inject time.Sleep(punishment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will break existing deployments.
Well, yes. But I'm not sure how to gracefully deny it -- adding a sleep would not be noticed (people would probably blame systemd) and nobody has noticed the warning message on startup that has been there for many years. We could make docker
(the client) output a warning on each operation but that will also break deployments. (Personally I think it should've been done this way in the first place.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would definitely require a deprecation period; one thing to make this situation more visible would be to include a warning to docker info
, if the CLI is using an insecure TCP connection.
Also; making it easier to do the right thing (which could be, e.g. automatically generating a certificate) would help moving things to a better place
Do you mean the "bind-mount-the-socket" usecase? I mean, I wouldn't recommend that either. If you want I could bind-mount |
I meant a case where DinD client connects to a DinD daemon using a TCP socket using a Docker network
|
The The only other option I can think of is that we auto-detect that we're in a container but I really don't like that idea. Not to mention that people should still be careful even when running Docker inside a (privileged) container. |
Is the flag really being called "--give-the-internet-root-access"? This is definitely not the case if you are running in a virtual machine or an otherwise segregated environment. The naming of the flag implies that it will not only open up the container to internet access (even if it's technically infeasible), but give it root access. If you want to name it something practical, going along with what other projects do, --ignore-insecure-tls-warnings would be more descriptive. --give-the-internet-root-access is not only confusing but comes off unprofessional. This is aside from the fact that you can't prevent stupid. If people don't care to learn the implications of running without TLS on an externally bound port, I don't think a flag is going to change their mind. If anything a more effective approach would be to require TLS by default and require a flag to disable TLS. |
Exposing the API to your network is giving the network root access to the machine that the Docker daemon is running on. If you're running the Docker daemon in a VM then I think it should be somewhat expected that It is definitely possible to configure this to be done safely, but it is non-trivial and it is pretty obvious that users aren't doing it properly. I would prefer that there be a very scary-looking option required in order to undermine the security of a machine, so that users don't do this by accident (or intentionally) without knowing that they're doing something bad. As for whether the option will be called
It wouldn't be possible to do that automatically because we don't know what the CA root (for authorising clients) should be. We could just accept no connections by default (with effectively a |
ping @justincormack @cyli @n4ss for thoughts / input |
Could just require either —tlsverify or whatever flag like —disable-tls-authentication then docker would not run insecure by default, and the flag would make it clear the user is not disabling encrypted transport but also cert based auth. That’s how SSH runs and this would be the container equivalent of requiring key based auth or explicitly allowing passwordless ssh. |
@Faheetah That is essentially what this patch is doing (the flag just has a different name). All jokes aside, I don't really mind if we change the flag name -- at the end of that day that's mostly just bikeshedding. I just want people to stop exposing their machines to unauthenticated root access. 😸 |
@justincormack I can change the flag to @thaJeztah I just looked up how |
Wondering if we should return, as part of the system info, the trusted client CA root certificate? |
Ah, yes; those warnings being generated client-side have been a thorn in my side for a long time (I think we should move them to the daemon, and return a list of warning that the client just prints). For this use-case, I assume the client has all information that's needed to decide if this warning should be printed or not, so we would be able to print the warning based on that information, correct? (So not using |
We can add a "Warnings" field to the Info struct, no? |
Yes that was my thinking; if present, the client would use that (backward compatibility with older API versions), and otherwise use the current behavior, and generate those warnings client-side. |
Yeah, that's what I would do (it's what I thought |
Discussing this in the maintainers meeting and we definitely agree that we should improve this; things being discussed;
@cyphar let us know if you want to help with those; perhaps we should create an epic for tracking those changes |
@justincormack @cyli @n4ss ^^ wdyt? |
+1 warning on daemon side, prometheus metrics, and deprecation warning Could you explain a bit about the delay? Is it per API request/per IP? Would the warning in the HTTP header be translated into a warning that the client would display per command? (This way, every command run against the daemon would display a warning, not just Also, this may not be desirable since mutual TLS works pretty well, but one possible usability improvement for security the daemon by default is generating server TLS certs and providing an auth token that can be used by clients instead. Jupyter notebook introduced this for example: https://jupyter-notebook.readthedocs.io/en/stable/security.html It might be easier/friendlier for people to get set up with a remote daemon by copying an API token, as opposed to setting up a client CA cert and a key, and possibly signing leaf client certs with the CA. |
There already has been a daemon warning for at least 4 years. I'm not sure people have seen it -- most people run inside systemd and given how chatty Docker (and containerd) are in logs, I'm not surprised that people miss a warning very early in the startup of dockerd. |
@cyphar Sorry, I was referring to your and @thaJeztah's suggestion for generating warnings for |
Ah okay. Yeah, I agree. |
What's current status? Wondering if this is closable in favor of #37684 |
@AkihiroSuda No, #37684 was a first step towards deprecation -- the long-term goal should be that we require a flag to allow this (rather than just have another warning). I don't think closing this makes sense because otherwise we'll just forget -- instead I think this should be put on a future milestone with a deprecation notice added to the warning in #37684. |
@cyphar plz check those failing tests |
I'm in favor of this PR but we should honor the future deprecation policy, although I feel waiting for 3 releases (19.09, 20.03, and 20.09) is too much in this case: Also, I'm wondering we can let every invocation of |
btw I opened a dind doc PR docker-library/docs#1525 and a dind proposal docker-library/docker#164 toward deprecating dind w/o TLS |
#41285 is merged now which:
The deprecation notice warns that in the next release we'll enable Closing this one. Thanks for all the discussion. We finally landed on a solution that seems like it will take care most of the cases of unexpectedly exposing the docker API to the world. People should also note, we do support tunneling API access over SSH, which is negotiated by the docker CLI... e.g. |
Historically we have been a little bit too forgiving to people who run
Docker with an unauthenticated API server, and recent events have
shown that a worrying amount of people are running Docker daemons with
the API accessible from the internet.
For a long time Docker has not used a TCP port by default, and using it
without --tlsverify has elicited an all-caps warning for a while as
well. But obviously this is not sufficient (maybe people aren't noticing
the warnings). We cannot just disallow this (because it is in theory
possible to set this up safely, and we use it ourselves in testing), so
instead add a new flag with a scary name to make sure that users are
aware of the significant risk of binding the API server to a TCP port
without any authentication:
% sudo dockerd -H tcp://0.0.0.0:1337 --give-the-internet-root-access
While it might sound like a bit of a joke, these sorts of "scary and
very obvious" flags exist in other tools. For instance, flashrom
requires you to use the flag "i_want_a_brick" if you are trying to flash
a laptop's ROM in a way it doesn't think is safe (and anyone reading
such an option should recognise why the flag has such a scary name).
Midge our cute new kitten cat by dougwoods
Ref: docker/hub-feedback#1121
Signed-off-by: Aleksa Sarai asarai@suse.de