--- title: Docker kind: tutorial weight: 1 --- Docker’s out-of-the-box authorization model is all or nothing. But many users require finer-grained access control and Docker’s plugin infrastructure allows us to do so. This is an excellent opportunity to see how to policy enable an existing service. ## Goals This tutorial helps you get started with OPA and introduces you to core concepts in OPA. > Policy enabling an application decouples the policy implementation from the > business logic so that administrators can define policy without changing the > application while still keeping up with the size, complexity, and dynamic > nature of modern applications. For the purpose of this tutorial, we want to use OPA to enforce a policy that prevents users from running insecure containers. This tutorial illustrates two key concepts: 1. OPA policy definition is decoupled from the implementation of the service (in this case Docker). The administrator is empowered to define and manage policies without requiring changes to any of the apps. 2. Both the data relevant to policy and the policy definitions themselves can change rapidly. ## Prerequisites This tutorial requires: * Docker Engine 18.06.0-ce or newer * Docker API version 1.38 or newer * `root` or `sudo` access The tutorial has been tested on the following platforms: * Ubuntu 18.04 (64-bit) If you are using a different distro, OS, or architecture, the steps will be the same. However, there may be slight differences in the commands you need to run. ## Steps Several of the steps below require `root` or `sudo` access. When you are modifying files under `/etc/docker` or signalling the Docker daemon to restart, you will need root access. ### 1. Create an empty policy definition that will allow all requests. ```shell mkdir -p /etc/docker/policies ``` **/etc/docker/policies/authz.rego**: ```live:docker_authz:module:read_only package docker.authz allow = true ``` This policy defines a single rule named `allow` that always produces the decision `true`. Once all of the components are running, we will come back to the policy. ### 2. Install the opa-docker-authz plugin. ```shell docker plugin install openpolicyagent/opa-docker-authz-v2:0.4 opa-args="-policy-file /opa/policies/authz.rego" ``` You need to configure the Docker daemon to use the plugin for authorization. ```shell cat > /etc/docker/daemon.json < Many of the examples in the documentation are interactive. Try editing the > input above by setting `Body.HostConfig.SecurityOpt` to > `["seccomp:unconfined"]`. ### 7. Test the policy is working by running a simple container: ```shell docker run hello-world ``` Now try running the same container but disable seccomp (which should be prevented by the policy): ```shell docker run --security-opt seccomp:unconfined hello-world ``` Congratulations! You have successfully prevented containers from running without seccomp! The rest of the tutorial shows how you can grant fine grained access to specific clients. ### 8. Identify the user in Docker requests. > Back up your existing Docker configuration, just in case. You can replace your > original configuration after you are done with the tutorial. ```shell mkdir -p ~/.docker cp ~/.docker/config.json ~/.docker/config.json~ ``` To identify the user, include an HTTP header in all of the requests sent to the Docker daemon: ```shell cat >~/.docker/config.json < Docker does not currently provide a way to authenticate clients. But in Docker > 1.12, clients can be authenticated using TLS and there are plans to include > other means of authentication. For the purpose of this tutorial, we assume that > an authentication system is place. ### 9. Update the policy to include basic user access controls. ```live:docker_authz_users:module:read_only,openable package docker.authz default allow = false # allow if the user is granted read/write access. allow { user_id := input.Headers["Authz-User"] user := users[user_id] not user.readOnly } # allow if the user is granted read-only access and the request is a GET. allow { user_id := input.Headers["Authz-User"] users[user_id].readOnly input.Method == "GET" } # users defines permissions for the user. In this case, we define a single # attribute 'readOnly' that controls the kinds of commands the user can run. users = { "bob": {"readOnly": true}, "alice": {"readOnly": false}, } ``` ### 10. Attempt to run a container. Because the configured user is `"bob"`, the request is rejected: ```shell docker run hello-world ``` ### 11. Change the user to "alice" and re-run the container. ```shell cat > ~/.docker/config.json <