# Docker Remote API with TLS client verification

Docker's Remote API can be secured via TLS and client certificate verification.  
First of all you need a few certificates and keys:

* CA certificate
    
* Server certificate
    
* Server key
    
* Client certificate
    
* Client key
    

## Create certificate files

You can create these files as described in the official docs in [Protect the Docker daemon socket](https://docs.docker.com/engine/security/https/).  
You can also use my create-certs.sh script to create them.  
Download the script and run like this:

1. Create a CA with the password `yourSecretPassword` and `900` days until it wil expire. The cert files will be in the directory `./certs`.
    

```plaintext
./create-certs.sh -m ca -pw yourSecretPassword -t certs -e 900
```

1. Create server certificate and key with the password of step 1 `yourSecretPassword`, with the servername [`myserver.example.com`](http://myserver.example.com) and `365` days until it wil expire. The cert files will be in the directory `./certs`.
    

```plaintext
./create-certs.sh -m server -h myserver.example.com -pw yourSecretPassword -t certs -e 365
```

1. Create client certificate and key with the password of step 1 `yourSecretPassword`, with the clientname `testClient` and `365` days until it wil expire. The cert files will be in the directory `./certs`.
    

```plaintext
./create-certs.sh -m client -h testClient -pw yourSecretPassword -t certs -e 365
```

Now you have a directory `./certs` with certificates and keys for CA, server and client.

# Enable Remote API with TLS

Make sure, you have a ca certificate and a server certificate with a server key.  
Open or create the file `/etc/docker/daemon.json`. This is the main configuration file for Docker.  
Take the content of the daemon.json file and write it to /etc/docker/daemon.json. Edit the paths to your ca and server certificate files. daemon.json:

```plaintext
{
    "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2376"],
    "tls": true,
    "tlscacert": "/data/certs/ca.pem",
    "tlscert": "/data/certs/server-cert.pem",
    "tlskey": "/data/certs/server-key.pem",
    "tlsverify": true
}
```

In some machines if the above changes wont work then try

```plaintext
mkdir -p  /etc/systemd/system/docker.service.d
cat /etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock  --graph=/data/docker-data --storage-driver=overlay2 --tls=true --tlsverify=true --tlscacert=/data/certs/ca.pem  --tlscert=/data/certs/server-cert.pem  --tlskey=/data/certs/server-key.pem
```

Restart your Docker engine with `sudo service docker restart`.  
The Docker Remote API is ready to use. You can run Docker commands from a remote device by using the ca.pem and the client certificate and key.

## [**create-certs.sh**](http://create-certs.sh)

```plaintext
#!/bin/bash
#see https://docs.docker.com/engine/security/https/

EXPIRATIONDAYS=700
CASUBJSTRING="/C=GB/ST=London/L=London/O=ExampleCompany/OU=IT/CN=example.com/emailAddress=test@example.com"

while [[ $# -gt 1 ]]
do
key="$1"

case $key in
    -m|--mode)
    MODE="$2"
    shift 
    ;;
    -h|--hostname)
    NAME="$2"
    shift 
    ;;
    -hip|--hostip)
    SERVERIP="$2"
    shift 
    ;;    
    -pw|--password)
    PASSWORD="$2"
    shift 
    ;;
    -t|--targetdir)
    TARGETDIR="$2"
    shift 
    ;;    
    -e|--expirationdays)
    EXPIRATIONDAYS="$2"
    shift 
    ;;    
    --ca-subj)
    CASUBJSTRING="$2"
    shift 
    ;; 
    *)
            # unknown option
    ;;
esac
shift 
done

echo "Mode $MODE"
echo "Host/Clientname $NAME"
echo "Host IP $SERVERIP"
echo "Targetdir $TARGETDIR"
echo "Expiration $EXPIRATIONDAYS"

programname=$0

function usage {
    echo "usage: $programname -m ca -h example.de [-hip 1.2.3.4] -pw my-secret -t /target/dir [-e 365]"
    echo "  -m|--mode                 'ca' to create CA, 'server' to create server cert, 'client' to create client cert"
    echo "  -h|--hostname|-n|--name   DNS hostname for the server or name of client"
    echo "  -hip|--hostip             host's IP - default: none"
    echo "  -pw|--password            Password for CA Key generation"
    echo "  -t|--targetdir            Targetdir for certfiles and keys"
    echo "  -e|--expirationdays       certificate expiration in day - default: 700 days"    
    echo "  --ca-subj                 subj string for ca cert - default: Example String..."
    exit 1
}

function createCA {
    openssl genrsa -aes256 -passout pass:$PASSWORD -out $TARGETDIR/ca-key.pem 4096
    openssl req -passin pass:$PASSWORD -new -x509 -days $EXPIRATIONDAYS -key $TARGETDIR/ca-key.pem -sha256 -out $TARGETDIR/ca.pem -subj $CASUBJSTRING
    
    chmod 0400 $TARGETDIR/ca-key.pem
    chmod 0444 $TARGETDIR/ca.pem
}

function checkCAFilesExist {
    if [[ ! -f "$TARGETDIR/ca.pem" || ! -f "$TARGETDIR/ca-key.pem" ]]; then
        echo "$TARGETDIR/ca.pem or $TARGETDIR/ca-key.pem not found. Create CA first with '-m ca'"
        exit 1
    fi
}

function createServerCert {
    checkCAFilesExist

    if [[ -z $SERVERIP ]]; then
        IPSTRING=""
    else
        IPSTRING=",IP:$SERVERIP"
    fi

    openssl genrsa -out $TARGETDIR/server-key.pem 4096
    openssl req -subj "/CN=$NAME" -new -key $TARGETDIR/server-key.pem -out $TARGETDIR/server.csr
    echo "subjectAltName = DNS:$NAME$IPSTRING" > $TARGETDIR/extfile.cnf
    openssl x509 -passin pass:$PASSWORD -req -days $EXPIRATIONDAYS -in $TARGETDIR/server.csr -CA $TARGETDIR/ca.pem -CAkey $TARGETDIR/ca-key.pem -CAcreateserial -out $TARGETDIR/server-cert.pem -extfile $TARGETDIR/extfile.cnf

    rm $TARGETDIR/server.csr $TARGETDIR/extfile.cnf $TARGETDIR/ca.srl
    chmod 0400 $TARGETDIR/server-key.pem
    chmod 0444 $TARGETDIR/server-cert.pem
}

function createClientCert {
    checkCAFilesExist

    openssl genrsa -out $TARGETDIR/client-key.pem 4096
    openssl req -subj "/CN=$NAME" -new -key $TARGETDIR/client-key.pem -out $TARGETDIR/client.csr
    echo "extendedKeyUsage = clientAuth" > $TARGETDIR/extfile.cnf
    openssl x509 -passin pass:$PASSWORD -req -days $EXPIRATIONDAYS -in $TARGETDIR/client.csr -CA $TARGETDIR/ca.pem -CAkey $TARGETDIR/ca-key.pem -CAcreateserial -out $TARGETDIR/client-cert.pem -extfile $TARGETDIR/extfile.cnf

    rm $TARGETDIR/client.csr $TARGETDIR/extfile.cnf $TARGETDIR/ca.srl
    chmod 0400 $TARGETDIR/client-key.pem
    chmod 0444 $TARGETDIR/client-cert.pem

    mv $TARGETDIR/client-key.pem $TARGETDIR/client-$NAME-key.pem
    mv $TARGETDIR/client-cert.pem $TARGETDIR/client-$NAME-cert.pem 
}


if [[ -z $MODE || ($MODE != "ca" && -z $NAME) || -z $PASSWORD || -z $TARGETDIR ]]; then
    usage   
fi

mkdir -p $TARGETDIR

if [[ $MODE = "ca" ]]; then 
    createCA
elif [[ $MODE = "server" ]]; then
    createServerCert
elif [[ $MODE = "client" ]]; then
    createClientCert
else
    usage
fi
```
