Securing local docker private registry - docker

I'm trying to create a secure private Docker registry that would work only in the local network and be accessed by IP address.
I have read a lot of articles regarding this issue but most of them talk about the need to have a registered domain name that points to a valid public IP address ( where the registry is ) , and then obtaining a certificate for such domain.
I'd like to know if there's a way of creating a docker registry with the following properties:
accessible only from the local network
secured with a valid certificate ( not the self signed certificate which is still considered "insecure" by docker )
How would I obtain a valid certificate for such a registry? I understand that certificates cannot be created for IP addresses alone, but can I generate a certificate for a domain that is registered but doesn't point to any public IP ( I've read something about dns-01 challenge, so I belive it's possible ) and then use that certificate, provided I map the said domain to the local IP of the server in my hosts file.
If this isn't possible, what is the best alternative for creative a secure, local, private docker registry?

Use Nginx to secure your Docker Registry.
The relevant documentation is here:
https://github.com/docker/distribution/tree/master/contrib/compose
You can use self-signed certificates if you add the root CA to /usr/local/share/ca-certificates and run update-ca-certificates command on the clients.

Related

How to pull images from a secure registry without copying CA to each host?

I am running a secure private docker registry on a machine (server) within my local network. My kubernetes and helm clients are running on another machine (host).
I am using a Certification Authority (CA) in the server machine in order to verify https pull requests from the host. That is why I am copying the registry's Certificate Authority (CA) to the host as mentioned here:
Instruct every Docker daemon to trust that certificate. For Linux:
Copy the domain.crt file to
/etc/docker/certs.d/myregistrydomain.com:5000/ca.crt on every Docker
host. You do not need to restart Docker
Even though this solution works as a charm, but only for few nodes.
From kubernetes or HELM perspective, is there a way to generate a secret or stating username/password inside the charts. Sot that, when 1000 hosts exist, there will be no need to copy the CA to every and each single host?
Server and hosts run Centos7

Using edge / proxy node certificate identity

I'm using haproxy as a bastion server / cluster gateway, as only some of the nodes in my network have direct access to the external network. My internal nodes are part of a kubernetes cluster, and need to be able to pull images from a private registry external to my cluster which requires certificate identities.
k8s cluster internal node -> haproxy on edge node -> docker registry
I'm trying to configure my back_end in my haproxy.cfg to route to the docker registry, and update the request with the certificate identity of the edge node. (Internal node does not have certs acceptable to the docker registry, and I've not been allowed to host the external node's certs on the internal node.) What I have right now looks like the below...
frontend ft_ssl
bind <boxIP>:443
mode http
default_backend bk_global_import_registry_certs
backend bk_global_import_registry_certs
mode http
balance roundrobin
server registry_alias-0 <registryIP>:443 ssl ca-file fullyqualified/ca.crt crt fullyqualified/file.pem check
server registry_alias-1 <registryIP2>:443 ssl ca-file fullyqualified/ca.crt crt fullyqualified/file.pem check
I currently have the HTTPS_PROXY setting in /etc/systemd/system/docker.service.d/http-proxy.conf and am getting a 400 Bad Request. Scrubbed log message below, with only changes as removal of IPs or typos.
InternalIP:randomPort [09/Jul/2019:13:28:08.659] ft_ssl bk_global_import_registry_certs 0/0/10/5/15 400 350 - - ---- 1/1/0/0/0 0/0 {} "CONNECT externalFQDN:443 HTTP/1.1"
For those looking at this via the kubernetes or docker tags, I also considered setting up a pull-through cache, but realized this only works with Docker's public registry - see open Docker GitHub issue 1431 for other folks trying to find ways to get past that, as well.
Posting the answer that resolved the situation for us, in case it helps others...
All traffic from the internal node now routes through to the HAProxy.
I'm no longer using the HTTPS_PROXY setting in /etc/systemd/system/docker.service.d/http-proxy.conf. Proxying through was not appropriate, since I could not use my internal node's certificate to authenticate against the docker registry.
From the internal node, I'm now treating the docker registry as an insecure registry by adding its address to /etc/docker/daemon.json in insecure-registries: [].
We can now access our internal private registry using the certificates passed along on the forward on the HAProxy backend. We do verify the certificate on the front-end, but because we have the registry listed in insecure-registries, Docker accepts the cert name mismatch.
One side effect noticed: pulling an image from "default" docker registry without specifying a prefix does not succeed using our solution. To pull from Docker, for example, you'd pull from registry-1.docker.io/imageName:imageVersion, rather than just imageName:imageVersion.
I might be mistaking but as far as I know Haproxy cannot be used as a direct HTTP proxy, it only acts as a reverse HTTP proxy. You should use Squid or something like that to be a direct HTTP proxy.
If you want Haproxy to terminate SSL for you like you do, then you will need to change hostname part of your Docker image to be the hostname of your Haproxy node. You should also make sure your Docker daemons trust the Haproxy certificate or add Haproxy to the list of INSECURE_REGISTRIES on all Kube nodes.

In Docker, what DOCKER_TLS_VERIFY=0 does?

I'm running Docker on Windows 10, and I noticed that by default, DOCKER_TLS_VERIFY=0.
I have a few questions about it:
Does it mean that I'm pulling images from Docker Hub insecurly?
Does Docker pull images through https when it is set to 0?
Is it recommended to set it to 1?
Communication with external registry servers like docker hub will default to TLS, this option is for something very different.
DOCKER_TLS_VERIFY tells the docker client (aka the docker command) whether to communicate with the docker daemon (dockerd) with any TLS verification. If set to 1, the server needs to have a private CA signed key pair, and the client also needs to have a key pair signed by the same CA. This setting tells the client to verify that server key it receives is signed by the private CA. The daemon/server will have a similar setting to verify client certificates.
If you are communicating with a remote docker engine over the network, this would be very bad since it implies that the remote docker engine allows anyone to hit the API (which gives root level access) without any client credentials. When communicating to a local socket that is protected with filesystem permissions, this feature is not needed.
This variable is documented here: https://docs.docker.com/engine/reference/commandline/cli/
Steps to configure the daemon and clients with TLS keys are documented here: https://docs.docker.com/engine/security/https/
For windows, many of the above steps would need to be translated (e.g. different locations for the daemon.json file). Have a look at the following:
https://docs.docker.com/engine/reference/commandline/dockerd/
https://docs.docker.com/docker-for-windows/

Configure docker repo with https without domain name

I have a website that I'm running on a digital ocean droplet, which I want to continuously deploy via docker and a Teamcity build server which I have running on my home server. I want to enable https on my docker repo, with a self signed certificate, and without a domain name.
Let's say my home's ip address is 10.10.10.10 and the docker repo is running on port 5000.
I followed the steps here, however docker on my website complained that it cannot connect to the docker repo on my homeserver because it doesn't specify an IP in the SAN extension.
Okay. So I created a new certificate without the CN field and only an IP in the SAN, and now my cert config on my website looks like...
/etc/docker/certs.d/10.10.10.10:5000/ca.crt
I also added the cert to my general certs (Ubuntu 16.04 btw)
Then I try to pull the image from my home server to my website...
docker pull 10.10.10.10:5000/personal_site:latest
However, I'm getting this error.
Error response from daemon: Get https://10.10.10.10:5000/v1/_ping: x509:
certificate signed by unknown authority (possibly because of "x509:
invalid signature: parent certificate cannot sign this kind of
certificate" while trying to verify candidate authority certificate "serial:xxx")
I thought by adding my cert to the /etc/docker/... it would accept a self-signed cert. Anyone have any advice here?
You can't used a self signed certificate for this, it needs to be a CA certificate. Follow the same steps required to create a certificate for a docker host and store your CA in /etc/docker/certs.d/.... Or you can also define 10.10.10.10 as an insecure registry as part of the docker daemon startup (dockerd --insecure-registry 10.10.10.10:5000 ...) and docker should ignore any certificate issues.
I just did the same thing with this instructions create private repo with password without domain and ssl. That will require you to add certificate on client and domain on host file (if you love to have an domain yourself without registering new domain)

docker is using the v1 registry api when it should use v2

I'm trying to use a self hosted docker registry v2. I should be able to push a docker image, which does work locally on the host server (coreos) running the registry v2 container. However, on a separate machine (also coreos, same version) when I try to push to the registry, it's try to push to v1, giving this error:
Error response from daemon: v1 ping attempt failed with error: Get
https://172.22.22.11:5000/v1/_ping: dial tcp 172.22.22.11:5000: i/o timeout.
If this private registry supports only HTTP or HTTPS with an unknown CA
certificate, please add `--insecure-registry 172.22.22.11:5000` to the
daemon's arguments. In the case of HTTPS, if you have access to the registry's
CA certificate, no need for the flag; simply place the CA certificate at
/etc/docker/certs.d/172.22.22.11:5000/ca.crt
both machine's docker executable is v1.6.2. Why is it that one works and is pushing to v2 but the other is v1?
Here's the repo for the registry: https://github.com/docker/distribution
You need to secure the registry before you can access it remotely, or explicitly allow all your Docker daemons to access insecure registries.
To secure the registry the easiest choice is to buy an SSL certificate for your server, but you can also self-sign the certificate and distribute to clients.
To allow insecure access add the argument --insecure-registry myregistrydomain.com:5000 to all the daemons who need to access the registry. (Obviously replace the domain name and port with yours).
The full instructions (including an example of your error message) are available at: https://github.com/docker/distribution/blob/master/docs/deploying.md
Regarding the error message, I guess Docker tries to use v2 first, fails because of the security issue then tries v1 and fails again.
This may be due to an env variable being set. I had a very similar issue when using a system with this env variable set.
export DOCKER_HOST="tcp://hostname:5000"
Running docker login http://hostname:5000 did not work and gave the same v1 behaviour. I did not expect the env variable to take precedence over an argument passed directly to the command.
Go to /etc/docker/daemon.json. If the file is not present, create a file and add the following
{
"insecure-registries": ["hosted-registry-IP:port"]
}
After that restart the docker service by
service docker restart

Resources