Review: Docker Engine Version 1.13 New Features (Part.2 – docker secret)

As we have seen in previous post (Part.1 – docker system) there are new features on new docker engine version that will make our life easier. I think that one of the most requested features for some time had been use of secrets. When deploying containers, we need to use information to configure our applications, and we need to encrypt some of this values.

It is never recommended to use passwords or other kind of sensible information in images itself as anyone who uses that image will have access to this data. We can change information on runtime, while creating containers passing key/value pairs as environments variables but someone could access that info published on container (you can even read them using docker inspect).

There is another option, using config files between volumes or introduce these files on runtime, but we need to share these files across all cluster nodes because when we create a service swarm will start it in the best host (knowing its affinities, constraints or even hosts loads).

Docker secret come to simplify this and help us to manage this kind of information.

We will use experimental mode and test version (at the time of writing this post, it is 1.13-rc7). Please read the references to know how to enable experimental mode and you can use a quick vagrant environment for these examples.

Docker secret will let us to manage all secrets in the swarm cluster. We could create, delete, inspect and list all secrets.

ubuntu@swarmnode1:/run$ docker secret --help

Usage: docker secret COMMAND

Manage Docker secrets

Options:
 --help Print usage

Commands:
 create Create a secret from a file or STDIN as content
 inspect Display detailed information on one or more secrets
 ls List secrets
 rm Remove one or more secrets

Run 'docker secret COMMAND --help' for more information on a command.

 

Let’s start creating a new secret:

ubuntu@swarmnode1:~$ echo "mysecretpasswordformyapp" |docker secret create MYSECRETS -
2rhdvw2o2g53ferldl47544io

This command returns the id of the newly created secret object. There is an option when creating secrets that allow us to add a list of labels for better management.

We can list all secrets created in our cluster:

ubuntu@swarmnode1:~$ docker secret ls
ID NAME CREATED UPDATED
2rhdvw2o2g53ferldl47544io MYSECRETS 20 seconds ago 20 seconds ago

And as with other objects, secrets can be reviewed with inspect.

ubuntu@swarmnode1:~$ docker secret inspect MYSECRETS
[
 {
 "ID": "2rhdvw2o2g53ferldl47544io",
 "Version": {
 "Index": 66
 },
 "CreatedAt": "2017-01-15T15:22:19.164299433Z",
 "UpdatedAt": "2017-01-15T15:22:19.164299433Z",
 "Spec": {
 "Name": "MYSECRETS"
 }
 }
]

Now we are going to create a simple service that pings wwwo.google.com based on busybox that will use the newly created secret object.

ubuntu@swarmnode1:~$ docker service create --secret MYSECRETS busybox ping www.google.es
qtken9b03a2j76lq4nvaxlxdr

We need to know where are running the tasks created to maintain the desired state of this service.

ubuntu@swarmnode1:~$ docker service ls
ID NAME MODE REPLICAS IMAGE
qtken9b03a2j determined_knuth replicated 1/1 busybox:latest

ubuntu@swarmnode1:~$ docker service ps determined_knuth
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
m4kslfp5h2hb determined_knuth.1 busybox:latest swarmnode3 Running Running 28 seconds ago
We connect to swarmnode3 and show containers running on this host.

ubuntu@swarmnode3:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2474ef1e7dfc busybox@sha256:817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e "ping www.google.es" 7 minutes ago Up 7 minutes determined_knuth.1.m4kslfp5h2hbxqxmm75tv90qq

We see that container 2474ef1e7dfc is associated with the task m4kslfp5h2hbxqxmm75tv90qq, as we can review in its name determined_knuth.1.m4kslfp5h2hbxqxmm75tv90qq.

When we use –secrets <SECRET_OBJECT_NAME> while creating a services, docker engine will create a file under /run/secrets with the name of that object on each task related to that service. In this example we will have a file named /run/secrets/MYSECRETS on all tasks related to this service.

Task 2474ef1e7dfc is running on swarmnode3 and we use a simple cat to see the content of /run/secrets/MYSECRETS.

ubuntu@swarmnode3:/run$ docker exec -ti 2474ef1e7dfc ls -l /run/secrets/MYSECRETS
-r--r--r-- 1 root root 25 Jan 15 15:26 /run/secrets/MYSECRETS
ubuntu@swarmnode3:/run$ docker exec -ti 2474ef1e7dfc cat /run/secrets/MYSECRETS
mysecretpasswordformyapp

The content of the secret created is available and we can use it to configure our application. It is read-only and accessible to any application that needs to use its information (remember to start container applications with its users, not always as root).

Information is secured when we review service or containers/tasks related.

Inspecting the service we can see now a new Secrets section:

ubuntu@swarmnode1:~/src$ docker service inspect determined_knuth
[
 {
 "ID": "qtken9b03a2j76lq4nvaxlxdr",
 "Version": {
 "Index": 67
 },
 "CreatedAt": "2017-01-15T15:26:14.53919447Z",
 "UpdatedAt": "2017-01-15T15:26:14.53919447Z",
 "Spec": {
 "Name": "determined_knuth",
 "TaskTemplate": {
 "ContainerSpec": {
 "Image": "busybox:latest@sha256:817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e",
 "Args": [
 "ping",
 "www.google.es"
 ],
 "DNSConfig": {},
 "Secrets": [
 {
 "File": {
 "Name": "MYSECRETS",
 "UID": "0",
 "GID": "0",
 "Mode": 292
 },
 "SecretID": "2rhdvw2o2g53ferldl47544io",
 "SecretName": "MYSECRETS"
 }
 ]
 },
 "Resources": {
 "Limits": {},
 "Reservations": {}
 },
 "RestartPolicy": {
 "Condition": "any",
 "MaxAttempts": 0
 },
 "Placement": {},
 "ForceUpdate": 0
 },
 "Mode": {
 "Replicated": {
 "Replicas": 1
 }
 },
 "UpdateConfig": {
 "Parallelism": 1,
 "FailureAction": "pause",
 "MaxFailureRatio": 0
 },
 "EndpointSpec": {
 "Mode": "vip"
 }
 },
 "Endpoint": {
 "Spec": {}
 },
 "UpdateStatus": {
 "StartedAt": "0001-01-01T00:00:00Z",
 "CompletedAt": "0001-01-01T00:00:00Z"
 }
 }
]

Inspecting the container we can’t see anything related to the secret object used.

ubuntu@swarmnode3:~$ docker inspect 2474ef1e7dfc
[
 {
 "Id": "2474ef1e7dfccd12fb4ac04cf8391b22edeea35c833d6a1800b8087502d34507",
 "Created": "2017-01-15T15:26:18.04161973Z",
 "Path": "ping",
 "Args": [
 "www.google.es"
 ],
 "State": {
 "Status": "running",
 "Running": true,
 "Paused": false,
 "Restarting": false,
 "OOMKilled": false,
 "Dead": false,
 "Pid": 22719,
 "ExitCode": 0,
 "Error": "",
 "StartedAt": "2017-01-15T15:26:18.13827557Z",
 "FinishedAt": "0001-01-01T00:00:00Z"
 },
 "Image": "sha256:7968321274dc6b6171697c33df7815310468e694ac5be0ec03ff053bb135e768",
 "ResolvConfPath": "/var/lib/docker/containers/2474ef1e7dfccd12fb4ac04cf8391b22edeea35c833d6a1800b8087502d34507/resolv.conf",
 "HostnamePath": "/var/lib/docker/containers/2474ef1e7dfccd12fb4ac04cf8391b22edeea35c833d6a1800b8087502d34507/hostname",
 "HostsPath": "/var/lib/docker/containers/2474ef1e7dfccd12fb4ac04cf8391b22edeea35c833d6a1800b8087502d34507/hosts",
 "LogPath": "/var/lib/docker/containers/2474ef1e7dfccd12fb4ac04cf8391b22edeea35c833d6a1800b8087502d34507/2474ef1e7dfccd12fb4ac04cf8391b22edeea35c833d6a1800b8087502d34507-json.log",
 "Name": "/determined_knuth.1.m4kslfp5h2hbxqxmm75tv90qq",
 "RestartCount": 0,
 "Driver": "aufs",
 "MountLabel": "",
 "ProcessLabel": "",
 "AppArmorProfile": "",
 "ExecIDs": null,
 "HostConfig": {
 "Binds": null,
 "ContainerIDFile": "",
 "LogConfig": {
 "Type": "json-file",
 "Config": {}
 },
 "NetworkMode": "default",
 "PortBindings": {},
 "RestartPolicy": {
 "Name": "",
 "MaximumRetryCount": 0
 },
 "AutoRemove": false,
 "VolumeDriver": "",
 "VolumesFrom": null,
 "CapAdd": null,
 "CapDrop": null,
 "Dns": null,
 "DnsOptions": null,
 "DnsSearch": null,
 "ExtraHosts": null,
 "GroupAdd": null,
 "IpcMode": "",
 "Cgroup": "",
 "Links": null,
 "OomScoreAdj": 0,
 "PidMode": "",
 "Privileged": false,
 "PublishAllPorts": false,
 "ReadonlyRootfs": false,
 "SecurityOpt": null,
 "UTSMode": "",
 "UsernsMode": "",
 "ShmSize": 67108864,
 "Runtime": "runc",
 "ConsoleSize": [
 0,
 0
 ],
 "Isolation": "",
 "CpuShares": 0,
 "Memory": 0,
 "NanoCpus": 0,
 "CgroupParent": "",
 "BlkioWeight": 0,
 "BlkioWeightDevice": null,
 "BlkioDeviceReadBps": null,
 "BlkioDeviceWriteBps": null,
 "BlkioDeviceReadIOps": null,
 "BlkioDeviceWriteIOps": null,
 "CpuPeriod": 0,
 "CpuQuota": 0,
 "CpuRealtimePeriod": 0,
 "CpuRealtimeRuntime": 0,
 "CpusetCpus": "",
 "CpusetMems": "",
 "Devices": null,
 "DiskQuota": 0,
 "KernelMemory": 0,
 "MemoryReservation": 0,
 "MemorySwap": 0,
 "MemorySwappiness": -1,
 "OomKillDisable": false,
 "PidsLimit": 0,
 "Ulimits": null,
 "CpuCount": 0,
 "CpuPercent": 0,
 "IOMaximumIOps": 0,
 "IOMaximumBandwidth": 0
 },
 "GraphDriver": {
 "Name": "aufs",
 "Data": null
 },
 "Mounts": [],
 "Config": {
 "Hostname": "2474ef1e7dfc",
 "Domainname": "",
 "User": "",
 "AttachStdin": false,
 "AttachStdout": false,
 "AttachStderr": false,
 "Tty": false,
 "OpenStdin": false,
 "StdinOnce": false,
 "Env": [
 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
 ],
 "Cmd": [
 "ping",
 "www.google.es"
 ],
 "Image": "busybox@sha256:817a12c32a39bbe394944ba49de563e085f1d3c5266eb8e9723256bc4448680e",
 "Volumes": null,
 "WorkingDir": "",
 "Entrypoint": null,
 "OnBuild": null,
 "Labels": {
 "com.docker.swarm.node.id": "4w4k52i4x5wdgqc171f3v0w61",
 "com.docker.swarm.service.id": "qtken9b03a2j76lq4nvaxlxdr",
 "com.docker.swarm.service.name": "determined_knuth",
 "com.docker.swarm.task": "",
 "com.docker.swarm.task.id": "m4kslfp5h2hbxqxmm75tv90qq",
 "com.docker.swarm.task.name": "determined_knuth.1.m4kslfp5h2hbxqxmm75tv90qq"
 }
 },
 "NetworkSettings": {
 "Bridge": "",
 "SandboxID": "661c75e5f313b2d8eaf5c4622661ab055bba0e18108f186ddb6cfc9ee2d99f88",
 "HairpinMode": false,
 "LinkLocalIPv6Address": "",
 "LinkLocalIPv6PrefixLen": 0,
 "Ports": {},
 "SandboxKey": "/var/run/docker/netns/661c75e5f313",
 "SecondaryIPAddresses": null,
 "SecondaryIPv6Addresses": null,
 "EndpointID": "3f608078cda4de700bdffffdba06e19705d2123aba6d4d912e1ae3867e07ef5f",
 "Gateway": "172.17.0.1",
 "GlobalIPv6Address": "",
 "GlobalIPv6PrefixLen": 0,
 "IPAddress": "172.17.0.4",
 "IPPrefixLen": 16,
 "IPv6Gateway": "",
 "MacAddress": "02:42:ac:11:00:04",
 "Networks": {
 "bridge": {
 "IPAMConfig": null,
 "Links": null,
 "Aliases": null,
 "NetworkID": "ec8d0516e31ed9d14c21a2c6768407def6dcbcc1a1139a093f5cb9505924328f",
 "EndpointID": "3f608078cda4de700bdffffdba06e19705d2123aba6d4d912e1ae3867e07ef5f",
 "Gateway": "172.17.0.1",
 "IPAddress": "172.17.0.4",
 "IPPrefixLen": 16,
 "IPv6Gateway": "",
 "GlobalIPv6Address": "",
 "GlobalIPv6PrefixLen": 0,
 "MacAddress": "02:42:ac:11:00:04"
 }
 }
 }
 }
]

But, working as expected, there is a new secret object created on that node too.

ubuntu@swarmnode3:/run$ docker secret ls
ID NAME CREATED UPDATED
2rhdvw2o2g53ferldl47544io MYSECRETS About an hour ago About an hour ago

 

Next steps will lead to change all our entrypoint files to use this new secrets feature 🙂

 

REFERENCES:

Docker Releases
https://github.com/docker/docker/releases

Enabling Experimental Mode
https://github.com/docker/docker/tree/master/experimental#use-docker-experimental

Vagrant Docker Swarm Mode Testing Environment 
https://github.com/frjaraur/docker-swarmmode

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s