Curl is my tool of choice for exploring new HTTP APIs. It's simple, full-featured, and I'm very productive with it. Naturally, when I want to learn the docker remote API, I turn to curl.
I am running boot2docker (installed via docker machine) on OS X Yosemite.
boot2docker runs docker over https, which uses TLS client authentication and signs certificates using a custom certificate authority (CA).
In order to use the docker remote API with curl, I must supply the cert, key, and CA files.
These files are conveniently located in $DOCKER_CERT_PATH/ca.pem
, $DOCKER_CERT_PATH/cert.pem
, and $DOCKER_CERT_PATH/key.pem
, so here is my first attempt.
$ curl -i --cacert $DOCKER_CERT_PATH/ca.pem --cert $DOCKER_CERT_PATH/cert.pem --key $DOCKER_CERT_PATH/key.pem https://192.168.99.100:2376/images/json
curl: (58) SSL: Can't load the certificate "/Users/jordan/.docker/machine/machines/dev/cert.pem" and its private key: OSStatus -25299
That's weird. I supplied a private key, but curl can't load it. What's going on here?
After a quick search for the error message, I found this important note for curl users on OS X Yosemite.
2. You can now use the -E/--cert option, for the purpose of authenticating with a TLS host using a client certificate and private key. When using the option, you can either specify:
2a. The name of the certificate as it appears in your Keychain (the certificate's private key has to be present in the same Keychain in order for this to work),
-or-
2b. A path to a PKCS#12-encoded file on a disk, which contains both the certificate and the private key. (If it's in the present working directory, you need to add a ./ to the start of the path, or curl will assume you want to search the Keychain.)
Option 2b sounds like the choice in my case, so now I just need to figure out how to generate this PKCS#12-encoded file. Fortunately, another quick search reveals how to generate a PKCS12 via OpenSSL. So to combine my client cert and private key into a single file, I use the following command:
openssl pkcs12 -export -in $DOCKER_CERT_PATH/cert.pem -inkey $DOCKER_CERT_PATH/key.pem -out $DOCKER_CERT_PATH/cert.pfx -password pass:supersecret
Now I can successfully connect to the docker remote api with the following curl command.
curl --cacert $DOCKER_CERT_PATH/ca.pem --cert $DOCKER_CERT_PATH/cert.pfx --pass supersecret https://192.168.99.100:2376/images/json
That's a lot of arguments to remember and to type, so let's put them in a variable.
docker_api="--cacert $DOCKER_CERT_PATH/ca.pem --cert $DOCKER_CERT_PATH/cert.pfx --pass supersecret https://192.168.99.100:2376"
curl $docker_api/images/json
Now making requests to the API via curl is trvial, but we still have some gnarly JSON output to deal with.
A new tool I've been using to work with large blobs from the command-line is jq.
You can install it with homebrew brew install jq
.
And now you can actually read JSON responses in your terminal.
curl $docker_api/images/json | jq . | less
[
{
"Id": "8c185118488e194f5d42fb7aa5239212b2f35d60f322905100092d68ecc5b4f6",
"ParentId": "eb7e1e522da91d060b7ca6f137712c9c728e9ab9b98b5c6672cdd391a7b4960d",
"RepoTags": [
"mc_reconfigure_proxy:latest"
],
"RepoDigests": [],
"Created": 1436412277,
"Size": 0,
"VirtualSize": 531649317,
"Labels": {}
},
{
That's still a lot of data to process. I'm only interested in the human-readable names of the images for now, so I can use jq to find those for me.
curl $docker_api/images/json | jq '.[] | .RepoTags[0]' | grep -v none
"mc_reconfigure_proxy:latest"
"mc_proxy:latest"
"curl:latest"
"mc_configure_proxy:latest"
"ruby-app:latest"
"mc_agent:latest"
"ruby:2.2.2-onbuild"
"jbgo/shoutouts_web:latest"
"golang:1.4-onbuild"
"nginx:latest"
"haproxy:latest"
"ruby:2.1-onbuild"
"debian:wheezy"
"postgres:latest"
"built-by-ansible:ex2b"
"built-by-ansible:ex2"
"ex1:v05"
"ex1:v04"
"ex1:v03"
"ex1:v02"
"ex1:v01"
"python:latest"
"python:2.7.9-wheezy"
"python:2.7.9-slim"
"debian:latest"
"ubuntu:latest"
"ansible/ubuntu14.04-ansible:latest"
"google/python:latest"
Still with me? Good! Now it's time to go learn the docker remote API! See you later.