- Create certificates
- Edit Docker options
- Restart Docker
- Copy client certificates from host
- (optional) Add remote endpoint in Portainer
Tested on a standard $5/mo DigitalOcean VPS running Ubuntu 16.04.
Log into Dokku host as root and create server/client certificates:
export HOST=my.domain
mkdir .docker && cd .docker
openssl genrsa -aes256 -out ca-key.pem 4096
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
openssl genrsa -out server-key.pem 4096
openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr
echo subjectAltName = DNS:$HOST,IP:127.0.0.1 >> extfile.cnf
echo extendedKeyUsage = serverAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
openssl genrsa -out key.pem 4096
openssl req -subj '/CN=client' -new -key key.pem -out client.csr
echo extendedKeyUsage = clientAuth >> extfile.cnf
openssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile extfile.cnf
rm client.csr server.csr
chmod 0400 ca-key.pem key.pem server-key.pem
chmod 0444 ca.pem server-cert.pem cert.pem
(please comment if you know how this can be done easier 😭)
For more details refer to the documentation
systemctl edit docker
Add:
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 --tlsverify --tlscacert /root/.docker/ca.pem --tlscert /root/.docker/server-cert.pem --tlskey /root/.docker/server-key.pem -H unix:///var/run/docker.sock
(note: -H unix:///var/run/docker.sock
as last argument!)
Creates /etc/systemd/system/docker.service.d/.#override.conf3aa8ba90533acd64
For more details refer to the documentation
systemctl restart docker
Exit host: exit
SFTP into host: sftp root@host
cd .docker
get ca.pem
get cert.pem
get key.pem
Guard these keys like root passwords, malicious users can severely damage your server with access to the Docker daemon.
Provide my.domain:2376
, enable TLS and add ca.pem
, cert.pem
and key.pem
If Portainer can't connect to the endpoint, most likely something went wrong while creating the certificates. Check out the documentation and re-generate the certificates.
# Check if the Docker daemon is running
docker -H my.domain:2376 info
# Check if the DNS settings are correct
dig +short my.domain
Run Portainer on Dokku host
Running Portainer as a container management tool for other Dokku hosts.
Host:
dokku apps:create portainer
dokku domains:add portainer portainer.my.domain
Create persistent storage folder: mkdir -p /var/lib/dokku/data/storage/portainer
Edit app docker-options:
dokku docker-options:add portainer deploy,run "-v /var/lib/dokku/data/storage/portainer:/data -v /var/run/docker.sock:/var/run/docker.sock"
Local
mkdir portainer && cd portainer
git init
git remote add dokku dokku@<host>:portainer
curl https://www.gitignore.io/api/macos%2Cwindows > .gitignore
touch Dockerfile
Edit Dockerfile:
FROM portainer/portainer
EXPOSE 9000
Push to Dokku host:
git add .
git commit -m 'initial'
git push dokku master
Host
Map ports and add SSL using dokku-letsencrypt:
# Map ports
dokku ps:stop portainer
dokku config:set portainer DOKKU_PROXY_PORT_MAP="http:80:9000"
# Add SSL
dokku config:set --no-restart portainer [email protected]
dokku letsencrypt portainer
# Check if ports are mapped correctly
dokku config:get portainer DOKKU_PROXY_PORT_MAP
# Should output: "http:80:9000 https:443:9000"
To re-deploy Portainer, first stop the Portainer container (it's using the Docker socket) dokku ps:stop portainer
I use this script to automate it, part of a cloud-init script I made for deploying new VPS.
#!/bin/bash
apt-update
apt-get upgrade -yq > /dev/null 2>&1
apt-get install -yq apt-transport-https ca-certificates curl gnupg-agent software-properties-common > /dev/null 2>&1
apt-get install -yq apache2-utils wget libnss3-tools ccze jq > /dev/null 2>&1
mkdir -p /etc/systemd/system/docker.service.d/
mkdir /root/.docker
mkdir /etc/docker/certs.d/
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssl_1.4.1_linux_amd64 -o cfssl
chmod +x cfssl
mv cfssl /usr/local/bin/cfssl
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssljson_1.4.1_linux_amd64 -o cfssljson
chmod +x cfssljson
mv cfssljson /usr/local/bin/cfssljson
curl -L https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssl-certinfo_1.4.1_linux_amd64 -o cfssl-certinfo
chmod +x cfssl-certinfo
mv cfssl-certinfo /usr/local/bin/cfssl-certinfo
cat > /root/.docker/ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"server": {
"expiry": "8760h",
"usages": [
"signing",
"key encipherment",
"server auth"
]
},
"client": {
"expiry": "8760h",
"usages": [
"signing",
"key encipherment",
"client auth"
]
}
}
}
}
EOF
cat > /root/.docker/ca-csr.json <<EOF
{
"CN": "localdomain",
"hosts": [
"localhost",
"localhost.localdomain"
],
"key": {
"algo": "ecdsa",
"size": 521
},
"names": [
{
"C": "US",
"ST": "CA",
"L": "San Francisco",
"OU": "CA",
"O": ""
}
]
}
EOF
cd /root/.docker
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
cat > /root/.docker/server-csr.json <<EOF
{
"CN": "server",
"hosts": [
"localhost.localdomain",
"localhost",
"docker.internal",
"127.0.0.1",
"127.0.1.1",
"172.17.0.1",
"172.20.20.1"
],
"key": {
"algo": "ecdsa",
"size": 521
},
"names": [
{
"C": "US",
"ST": "CA",
"L": "San Francisco",
"OU": "Server",
"O": "<organization"
}
]
}
EOF
cat > /root/.docker/client-csr.json <<EOF
{
"CN": "client",
"hosts": [""],
"key": {
"algo": "ecdsa",
"size": 521
},
"names": [
{
"C": "US",
"ST": "CA",
"L": "San Francisco",
"OU": "Client",
"O": ""
}
]
}
EOF
cfssl gencert
-ca=ca.pem
-ca-key=ca-key.pem
--config=ca-config.json
-profile=server
server-csr.json | cfssljson -bare server
cfssl gencert
-ca=ca.pem
-ca-key=ca-key.pem
-config=ca-config.json
-profile=client
client-csr.json | cfssljson -bare client
cp ca.pem /etc/docker/certs.d/ca.crt
cp client.pem /etc/docker/certs.d/client.pem
cp client-key.pem /etc/docker/certs.d/client.key
chmod -R 600 /etc/docker/certs.d/
chmod 444 /etc/docker/certs.d/ca.crt
chmod 444 /etc/docker/certs.d/client.pem
chmod 400 /etc/docker/certs.d/client.key
chown -R root:docker /etc/docker/certs.d/
chmod 400 ca-key.pem
chmod 444 ca.pem
chmod 400 server-key.pem
chmod 444 server.pem
chmod 400 client-key.pem
chmod 444 client.pem
cp ca.pem /usr/local/share/ca-certificates/localhost.crt
update-ca-certificates
cat > /etc/systemd/system/docker.service.d/override.conf <<EOF
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2376 --tlsverify --tlscacert /root/.docker/ca.pem --tlscert /root/.docker/server.pem --tlskey /root/.docker/server-key.pem -H unix:///var/run/docker.sock
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
echo 'export DOCKER_CERT_PATH=/etc/docker/certs.d/' >> /etc/profile
echo 'export DOCKER_HOST=tcp://127.0.0.1:2376'>> /etc/profile
echo 'export DOCKER_TLS_VERIFY=1' >> /etc/profile
systemctl daemon-reload
systemctl restart docker