Last week i was working with the docker daemon
to help prepare it for remote cli access (which to be clear, isn’t a good idea but i’ll detail this anyway)
By default, the docker daemon runs locally and you use the docker cli
to access the daemon which in turn does all the heavy lifting for you (as root usually, by the way!).
The docker daemon accepts requests from the cli as plain old REST api call which you can read about here under Develop with Docker Engine API. As it is just a rest api, you can invoke it directly if its interface is listening. As mentioned, the only interface docker daemon installs by default is on local domain named pipes so that only local docker cli can access it (see Docker daemon attack surface).
I had to look into ways where a remote VMs docker daemon was exposed to external clients with the requirements that:
This led me to look a bit more into the docker’s authorization plugin which is a pretty simple admission controller that provides the plugin metadata about the requestor and action attempted and returns back a allow/deny decision (similar kubernetes RBAC)
Thats great…but do i have to write the controller from scratch?
Nope, may others have done that to ofcourse…and i ended up using Open Policy Agents
This tutorial is a simple walkthrough on how i set it up locally…its nothing new; you can follow OPA’s github page if you want to or here
What we will do is configure your local docker daemon to use mTLS certificates and configure selective access for two different users:
This article will use certificates you can find here (feel free to clone+download or just copy the certs over)
We will define the OPA policy handler to use for this demo
First create the policy file here /etc/docker/policies/authz.rego
with content like this:
package docker.authz
default allow = false
users = {
"user1@domain.com": {"readOnly": true},
"user2@domain.com": {"readOnly": false},
}
allow {
users[input.User].readOnly
input.Method == "GET"
}
What does that mean? Well, its OPAs rego language which basically says:
user1
has a label “readOnly” which evalutes to true
and user2
which evaluates to false.readOnly
label.OPA automatically extracts the CN value for the user and places it into input.User
For example, you can decode user1 and user2
certificates like this
openssl x509 -in client_crt.pem -text -noout
Subject: C = US, ST = California, O = Google, OU = Enterprise, CN = user1@domain.com
openssl x509 -in client2_crt.pem -text -noout
Subject: C = US, ST = California, O = Google, OU = Enterprise, CN = user2.domain.com
The CN=
value for gets extracted and populated as the input.User
field.
For more information, see: OPA Request Attributes
Install the OPA plugin and point towards the policy file:
docker plugin install openpolicyagent/opa-docker-authz-v2:0.4 opa-args="-policy-file /opa/policies/authz.rego"
Create the docker daemon file at /etc/docker/daemon.json
like this:
Remember to download the TEST certificates provided in this repo and place them in the path appropriate for the file:
/etc/docker/daemon.json
{
"authorization-plugins": ["openpolicyagent/opa-docker-authz-v2:0.4"],
"tlsverify": true,
"tlscacert": "/path/to/CA_crt.pem",
"tlscert": "/path/to/server_crt.pem",
"tlskey": "/path/to/server_key.pem",
"hosts": ["127.0.0.1:2376"]
}
Edit /etc/hosts
file for Certificate CN value.
The server certificates provided in this repo have a hardcoded value for server.domain.com
which is intended to represent your remote docker daemons’s host.
In our case its just localhost so add this
127.0.0.1 server.domain.com
Restart docker
sudo service docker restart
Use user1
certificate to list operations on the daemon:
eg
$ docker --tlsverify --tlscacert=CA_crt.pem --tlscert=client_crt.pem --tlskey=client_key.pem -H=server.domain.com:2376 images
REPOSITORY TAG IMAGE ID CREATED SIZE
server.domain.com/alpine latest a187dde48cd2 2 weeks ago 5.6MB
Now as try to run an image:
$ docker --tlsverify --tlscacert=CA_crt.pem --tlscert=client_crt.pem --tlskey=client_key.pem -H=server.domain.com:2376 run -ti server.domain.com/alpine
docker: Error response from daemon: authorization denied by plugin openpolicyagent/opa-docker-authz-v2:0.4: request rejected by administrative policy.
This failed since we only allow GET
operations (docker run will create a VM)
Use user2
to perform even a simple operation
$ docker --tlsverify --tlscacert=CA_crt.pem --tlscert=client2_crt.pem --tlskey=client2_key.pem -H=server.domain.com:2376 images
Error response from daemon: authorization denied by plugin openpolicyagent/opa-docker-authz-v2:0.4: request rejected by administrative policy
which fails since user2 isn’t allowed to do anything
Thats it, really..you’ll notice i didn’t even allow admin access to do any thing in there…thats left upto the reader and rego-jockys!
This site supports webmentions. Send me a mention via this form.