Javier Ramírez

  • Technology Advocate
  • Docker Captain
  • Docker Certificate Associate
  • Docker Certified Instructor
  • Microsoft Certified Systems Engineer
  • HP Openview Solutions Certified

  • Docker 1.13 Features 2/3

    15 Jan 2017 » docker, swarm

    As we have seen in previous post 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