-
Notifications
You must be signed in to change notification settings - Fork 18.6k
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
Add support for certificates for repositories #3070
Conversation
FYI see also #3068 |
@@ -25,6 +28,82 @@ var ( | |||
ErrLoginRequired = errors.New("Authentication is required.") | |||
) | |||
|
|||
type HostConfig struct { |
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.
Config and HostConfig might be confusing as we already have those types in docker.
This is server side only, It might be interesting to let the client change the certificate? |
What do you mean by server side? Its not on the server in terms of the https connection to the registry, but it is with respect to the docker client. It might be nice to be able to specify a custom certificate or root ca on the commandline, but I'm not sure how much that would be used. You generally want to set up the certs for a particular repository on the system once, and then use docker like normal without having to always specify it on the command line. |
@alexlarsson I mean daemon side. We have this arch: client ---> daemon (server) ---> registry. When you have the client and daemon on different machine, you might still want to be able to specify certificates. |
@creack Eh? Thats exactly what this does? You can specify the root certificates to use when the daemon does a https call to the registry. The whole point here is that I want to be able to set up an ISV registry (say e.g. registry.company.com) that requires a client certificate. So, when i do "docker run registry.company.com/product" i will get a HTTP/403 unless I have on my machine a valid certificate for that product. |
Oh, i see what you mean, you want the client to forward the cert data so that the deamon can use it when talking to the registry? |
(Its actually a bit more complex, because the client also calls registry.ExpandAndVerifyRegistryUrl() which needs the root CAs set, or it will fall back to the http (non-https) registry.) |
@alexlarsson can you write up some documentation for this please (including how to setup the server side, and make the client side certs)? I'm guessing a howto in docs/sources/use would be great - perhaps with a reference to it in some of the other relevant docs. I like the idea that if this is in core already, we could then use the same mechanism to (optionally or default) secure the |
@SvenDowideit Well, currently it is kinda tricky to set up the server side for client side certs, I just had a custom apache config hack to test it. I could put it up somewhere, but its not really a production thing. It would probably be better to add support for it in the docker registry code instead. For the root CAs its pretty simple though, you just specify the CAs. |
I can describe my test setup if someone else wants to test it. It is based on http://atodorov.org/blog/2011/09/15/protected-rpm-repositories-with-yum-and-ssl/ First of all, i have on my machine apache2 running with modssl. It has a non-root signed certificate, so I have Secondly, i have a client key generated via: Then i expanded http://people.gnome.org/~alexl/v1.tar.gz in the /var/www/html/ directory, which contains all the files needed for a registry to serve out the "busybox" image. Then i needed to configure apache to 1) work as a registry and 2) verify client certs
and /var/www/cgi-bin/cert.cgi:
This causes apache to accept (any) valid client certificate (or give 403), and emit the headers that docker relies on:
I then add the configuration for docker in cat /etc/docker-certs
And can then use it:
|
New version just renamed to RegistryConfig and RegistryHostConfig |
@alexlarsson +1 for the support in this PR. I will try to find time to try out your changes in the next few days. In particular I'm trying to use docker-registry fronted by nginx with SSL enabled as outlined here: https://github.com/dotcloud/docker-registry#nginx However in my env I created the SSL certs with SAN IP/DNS as outlined here: http://grevi.ch/blog/ssl-certificate-request-with-subject-alternative-names-san |
@creack Asked me whether it's difficult to unify tls support from #3068 and this PR: Well, we could configure the keys via the command line flags as in my PR - or configure the keys in my PR via a similar/same configuration file as here. This is more a question of taste. Anyways, since the daemon is a client to the registry, it needs:
Which means hell a lot of keys and certificates - but sure, it's possible and should be that hard. We could just pass the tls config from somewhere we have access to the flags to registry.NewRegistry. |
I don't see the point of passing keys via the commandline (or rather, it may be useful in corner cases, but for day to day use it would be totally painful). But, there "config file" case is slightly complex in the certificate-for-registry case in that you may have a config file either on the machine the client runs on or the one that the daemon runs on, and if it is the earlier you have to actually forward the cert data rather than just the filenames. Although there are some risks forwarding private keys like that, so maybe that is not a great setup in practice. |
@discordianfish @alexlarsson Tentatively scheduling for 0.7.4 next week. Let's make this happen! |
@alexlarsson Just a small nitpick: Do we really need to introduce another file (ie. /etc/docker-certs if i'm not mistaken) ? Can't we just use /etc/default/docker that we already have by default. If not then we can at least just use /etc/docker.conf or something like that, not cert specific. |
@denibertovic Furthermore, that file will only be read by the server. This means it cannot be used to configure certificates for the client. It also causes trouble for auto-guessing repository addresses, as if the client sees "my.local.server/my-image" and fails to connect to it because it is not using the same certs as the daemon would then it will not properly expand it to the right url and the docker commands will fail. |
Neither this commit or any of the other various related cert PRs are very hard. All they do is juggle around some config options and enable them at the right place. The problem imho is to have a decent unified "UI" design for all the separate configurations that are possible: SSL protected client<->daemon connection
Authenticate client<->daemon connection
SSL protected daemon<->repository connection
Authenticated daemon<->repository connection
All of these could be specified in multiple ways, depending on what we want. Like e.g. a config file, command line args, or env vars. However, to be practical I don't think we should only support commandline, as that would make each docker command very long when you're working with authenticated servers. There is also the added complexity that specifying e.g. a cert file on the command line of the client means we have to actually upload it to the daemon rather than pass the filename as the files may not be on the same machine. Personally I think it would be easiest if all these were specified in the same (optional) config file in /etc. That way one could easily put it there to have both the daemon and the client read it. We should probably also have some of the options available as command line options on the client and on the daemon for easy testing. The question to me is, do we make this a cert only config file or do we have other potential daemon configuration we want to put in there? |
I'm not sure how you and @discordianfish were going to merge you're 2 approaches. I was just nitpicking about the file name. |
@denibertovic Well, the docker developers in general seem to dislike configuration files, which is why I made it a cert file configuration only. If the maintainers agree on using a generic config file we should probably call it /etc/docker.conf. |
This is great, and is a business-case requirement for us. One small tweak that include several edge cases, would be to make the cert/key for a registry to be a list, rather than a single pair. Like
In the certificate interaction on Red Hat, you may be provided multiple cert/key pair depending on permutations of products and registration. There is encoded data in these certs that behave differently on the server-side. While this describes a specific process, the use case also applies to cert life cycling, where you may be provided future certs, and still use current valid cert. As to how this ought to behave, I imagine it would try the first pair, and if it's a 403 or 5xx then proceed to the next pair. If a 200 or 404, then return. |
I rebased the branch to current master. Will now try to look at the other ssl stuff that landed and integrate with that. |
Ok. i pushed a new version of this which doesn't use the json config file, but rather just a simple filesystem layout. I'll duplicate the (long) commit text here for easy reading: Add support for client certificates for registriesThis lets you specify custom client TLS certificates and CA root for a specific registry hostname. Docker will then verify the registry against the CA and present the client cert when talking to that registry. This allows the registry to verify that the client has a proper key, indicating that the client is allowed to access the images. A custom cert is configured by creating a directory in /etc/docker/certs with the same name as the registry hostname. Inside this directory all *.crt files are added as CA Roots (if none exists, the system default is used) and pair of files $filename.key and $filename.cert indicate a custom certificate to present to the registry. If there are multiple certificates each one will be tried in alphabetical order, proceeding to the next if we get a 403 of 5xx response. So, an example setup would be:
A simple way to test this setup is to use an apache server to host a registry. Just copy a registry tree into the apache root, here is an example one containing the busybox image: Then add this conf file as /etc/httpd/conf.d/registry.conf:
And this as /var/www/cgi-bin/cert.cgi
This will return 403 for all accessed to /v1 unless any client cert is presented. Obviously a real implementation would verify more details about the certificate. Example client certs can be generated with:
|
awesome - Can you add the above text as a new doc in |
@SvenDowideit Added |
Docs LGTM merci - hopefully I'll get to test it soon too |
Using certificates for repository client verification | ||
===================================================== | ||
|
||
This lets you specify custom client TLS certificates and CA root for a specific registry hostname. Docker will then verify the registry against the CA and present the client cert when talking to that registry. This allows the registry to verify that the client has a proper key, indicating that the client is allowed to access the images. |
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.
Hi @alexlarsson,
Can you cap the line-length at 80 chrs. max please?
Updated reflowed docs. |
The CAs used here are very special. I don't think you want to add those to the system default CAs. Also, with the per-user configs you can't modify the system CAs. Wrt distributing the client certs. This may have uses for others, but for the usecase we're looking at for this the client certificates will be the entitlement of the machine running the code, and which is something owned by each node, not something that is per-user. |
For Red Hat based distros we want to support using the host subscription certificates. With this model we'll need to add links like this:
I would prefer a configfile or NSS DB implementation a la |
Any more feedback on this? Its pretty important to us. |
@shykes ping? |
#assignee=shykes |
Updated code: rebase on master, converted docs to md, changed /etc/docker/certs to /etc/docker/certs.d |
Docs LGTM |
Rebased on master |
This lets you specify custom client TLS certificates and CA root for a specific registry hostname. Docker will then verify the registry against the CA and present the client cert when talking to that registry. This allows the registry to verify that the client has a proper key, indicating that the client is allowed to access the images. A custom cert is configured by creating a directory in /etc/docker/certs.d with the same name as the registry hostname. Inside this directory all *.crt files are added as CA Roots (if none exists, the system default is used) and pair of files <filename>.key and <filename>.cert indicate a custom certificate to present to the registry. If there are multiple certificates each one will be tried in alphabetical order, proceeding to the next if we get a 403 of 5xx response. So, an example setup would be: /etc/docker/certs.d/ └── localhost ├── client.cert ├── client.key └── localhost.crt A simple way to test this setup is to use an apache server to host a registry. Just copy a registry tree into the apache root, here is an example one containing the busybox image: http://people.gnome.org/~alexl/v1.tar.gz Then add this conf file as /etc/httpd/conf.d/registry.conf: # This must be in the root context, otherwise it causes a re-negotiation # which is not supported by the tls implementation in go SSLVerifyClient optional_no_ca <Location /v1> Action cert-protected /cgi-bin/cert.cgi SetHandler cert-protected Header set x-docker-registry-version "0.6.2" SetEnvIf Host (.*) custom_host=$1 Header set X-Docker-Endpoints "%{custom_host}e" </Location> And this as /var/www/cgi-bin/cert.cgi #!/bin/bash if [ "$HTTPS" != "on" ]; then echo "Status: 403 Not using SSL" echo "x-docker-registry-version: 0.6.2" echo exit 0 fi if [ "$SSL_CLIENT_VERIFY" == "NONE" ]; then echo "Status: 403 Client certificate invalid" echo "x-docker-registry-version: 0.6.2" echo exit 0 fi echo "Content-length: $(stat --printf='%s' $PATH_TRANSLATED)" echo "x-docker-registry-version: 0.6.2" echo "X-Docker-Endpoints: $SERVER_NAME" echo "X-Docker-Size: 0" echo cat $PATH_TRANSLATED This will return 403 for all accessed to /v1 unless *any* client cert is presented. Obviously a real implementation would verify more details about the certificate. Example client certs can be generated with: openssl genrsa -out client.key 1024 openssl req -new -x509 -text -key client.key -out client.cert Docker-DCO-1.1-Signed-off-by: Alexander Larsson <alexl@redhat.com> (github: alexlarsson)
Rebased again. @shykes Any chance you can have a look at this next week? |
├── client.key | ||
└── localhost.crt | ||
|
||
A simple way to test this setup is to use an apache server to host a |
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.
Apache.
Code looks fine, no regression in the automated tests. Doesn't break on existing registry endpoints. We've been trying to test this against a registry setup which requires client certs. Right now 'docker pull' fails with an error 400, probably a SSL configuration error. If anybody has a Docker image of this test environment, that would be great. There's a starting point on https://github.com/shykes/test-registry Once we overcome this issue, I see no obstacle to merging. |
LGTM |
2 similar comments
LGTM |
LGTM |
Introduced in moby#3070 Docker-DCO-1.1-Signed-off-by: Ben Firshman <ben@firshman.co.uk> (github: bfirsh)
Introduced in moby/moby#3070 Docker-DCO-1.1-Signed-off-by: Ben Firshman <ben@firshman.co.uk> (github: bfirsh)
This adds support for a /etc/docker-certs configuration file that allows you to specify tls certificate config.
Currently it has two settings:
You can specify a list of root CAs in case you're using an https repository that is not signed by a default CA
You can specify a per-repository client side certificate and private key to send to the server. This allows the server to e.g. verify that the client is allowed to access a particular image.
I've set up a local apache that serves as a registry with "SSLVerifyClient optional_no_ca" and a cgi script that verifies that you have a valid client side certificate. It works:
With the following /etc/docker-certs