sudo apt-get remove docker docker-engine docker.io containerd runc
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
Fall back to stretch
if you get an error from apt when running sudo apt update
.
# /etc/apt/sources.list
deb [arch=amd64] https://download.docker.com/linux/debian stretch stable
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
Then check the status.
systemctl start docker
systemctl enable docker
systemctl status docker
DANGER This exposes your docker to the world unsafely DANGER
sudo vim /etc/systemd/system/multi-user.target.wants/docker.service
Change this...
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
To this...
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:
Then reload and restart.
sudo systemctl daemon-reload
sudo systemctl restart docker
Test the connection from the remote client that wants to access your newly exposed docker API with this command.
docker -H 1.2.3.4:2375 info
Lastly, make sure to expose this port on your firewall.
Not done yet, havent learnt how to do this. In the meantime (and for my reference later)...
https://docs.docker.com/engine/security/protect-access/
https://medium.com/trabe/using-docker-engine-api-securely-584e0882158e
https://lemariva.com/blog/2019/12/portainer-managing-docker-engine-remotely
Another idea i had was to put the communication in a wireguard tunnel somehow. But i havent read much about this yet, the todo for this task would be to maybe understand this https://youtu.be/88GyLoZbDNw?t=1142 (@19:05 onwards) about networking namespaces.
Based on trabes article here, TLS is a good approach to securing dockers API.
In this situation we have two machines:
- My local LAN network hosting a docker daemon on 10.0.0.50
- My remote docker network on my VPS (which also hosts portainer) on 1.2.3.4
We need to do the following to succeed at securing docker.
- Expose port 2376 for secure traffic (2375 is traditionally for unsecure traffic exposed by dockers api)
- Generate a TLS server certificate authority to sign a...
- Server certificate
- Client certificate
- Associate the client certificate with portainer to authenticate against my local LAN networks docker daemon
create a location to put these certs.
mkdir ~/portainer-certs
cd portainer-certs
Generate a CA certificate on the host machine you want to expose the api from (in my case 10.0.0.50).
You must fill the Common Name field with the FQDN of the docker host machine.
Here i am picking store.rolandw.lan for my FQDN because while my portainer instance is running elsewhere on portainer.rolandw.dev (.dev instead of .lan). The CA FQDN should be picked for for the host its generated on even if its not reachable via the internet. So you will most likely be picking some.example.lan or some.example.local.
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
Country Name (2 letter code) [AU]:AU
Locality Name (eg, city) []:Melbourne
Common Name (e.g. server FQDN or YOUR name) []:store.rolandw.lan
Create a certificate signing request (CSR). Make sure to replace the CN with your own.
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=store.rolandw.lan" -sha256 -new -key server-key.pem -out server.csr
Then sign the CSR with the CA.
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem
Before generating the server certificate, we need to create an options file that defined the IP and domain names for our server.
echo subjectAltName = \
DNS:store.rolandw.lan,IP:10.0.0.50,IP:127.0.0.1 >> extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf
Then generate the server cert.
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem \
-CAkey ca-key.pem -CAcreateserial -out server-cert.pem \
-extfile extfile.cnf
Now we need to generate a client certificate.
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
echo extendedKeyUsage = clientAuth > extfile-client.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem \
-CAkey ca-key.pem -CAcreateserial -out cert.pem \
-extfile extfile-client.cnf
Lastly, docker needs to be started with TLS. Change the exec start in your docker service file to this, you back up the original ExecStart by copying and commenting it out. Also change your paths for each file specified to the location you created your own certificates.
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2376 --tlsverify --tlscacert=/home/roland/portainer-certs/ca.pem --tlscert=/home/roland/portainer-certs/server-cert.pem --tlskey=/home/roland/portainer-certs/server-key.pem
Then all you need to do is from portainer, navigate to endpoints -> add endpoint
and add a docker endpoint
(connect directly to docker api).
- Name: whatever you want
- Endpoint URL: 1.2.3.4:2376
- Public IP: 1.2.3.4
- TLS: yes
- Select the 2nd TLS mode (TLS with client verification only)
- TLS certificate: server-cert.pem
- TLS key: server-key.pem