mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
Replace Hello World with Docker AuthZ example
Also, make a few tweaks to headers and download steps based on feedback.
This commit is contained in:
+2
-1
@@ -19,4 +19,5 @@ github_repo: opa
|
||||
markdown: redcarpet
|
||||
redcarpet:
|
||||
extensions:
|
||||
- tables
|
||||
- tables
|
||||
- no_intra_emphasis
|
||||
@@ -5,7 +5,7 @@ section: references
|
||||
sort_order: 100
|
||||
---
|
||||
|
||||
## Development Environment
|
||||
## Development
|
||||
|
||||
OPA is written in the [Go](https://golang.org) programming language.
|
||||
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
---
|
||||
layout: docs
|
||||
title: Authorization
|
||||
section: examples
|
||||
sort_order: 1000
|
||||
---
|
||||
|
||||
Authorization
|
||||
-------------
|
||||
|
||||
This example helps you get started with OPA and shows how you can start policy enabling an existing
|
||||
app or service. This example introduces core concepts in OPA including the language used to define policies.
|
||||
|
||||
> 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.
|
||||
|
||||
In this example, we will write policies to authorize access to Docker. There are many examples of policies around access control. For demonstration purposes, we want to prevent the following:
|
||||
|
||||
- Containers with insecure configurations.
|
||||
- Users modifying the system without sufficient read+write access.
|
||||
|
||||
There are two key ideas that this example illustrates:
|
||||
|
||||
1. The policy definition is decoupled from the implementation of the app (in this case Docker). The administrator is empowered to define and manage policies without requiring changes to any of the apps.
|
||||
|
||||
1. Both the data relevant to policy and the policy definitions themselves can change rapidly.
|
||||
|
||||
Once you finish this example, you will be familiar with:
|
||||
|
||||
- Running OPA as a server/daemon.
|
||||
- Loading policy definitions and data via the REST APIs.
|
||||
- Querying data via the REST APIs.
|
||||
|
||||
You will also be introduced to the language used to write policies in OPA: [Rego](/docs/lang.html).
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
The example has been tested on the following platforms:
|
||||
|
||||
- Ubuntu 16.04 (64-bit)
|
||||
|
||||
If you are using a different distro, OS, or architecture the steps will be the same however there will be slight differences in the commands you need to run.
|
||||
|
||||
- This example requires Docker Engine 1.11 or newer to be installed on your machine.
|
||||
|
||||
- This examples requires that you have root or sudo access on your machine.
|
||||
|
||||
Steps
|
||||
=====
|
||||
|
||||
1. Create a directory that OPA can store policy definitions in:
|
||||
|
||||
mkdir -p policies
|
||||
|
||||
1. Download the latest version of OPA:
|
||||
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc2/opa_linux_amd64 > opa
|
||||
chmod u+x opa
|
||||
|
||||
1. Run OPA in server mode with logging enabled:
|
||||
|
||||
./opa run -s --alsologtostderr 1 --v 2 --policy-dir policies
|
||||
|
||||
OPA will run until it receives a signal to stop. Open another terminal to continue with the rest of the example.
|
||||
|
||||
1. Download the [open-policy-agent/docker-authz-plugin](https://github.com/open-policy-agent/docker-authz-plugin) executable from GitHub:
|
||||
|
||||
curl -L https://github.com/open-policy-agent/docker-authz-plugin/releases/download/v0.1.0-rc1/docker-authz-plugin_linux_amd64 > docker-authz-plugin
|
||||
chmod u+x docker-authz-plugin
|
||||
|
||||
The open-policy-agent/docker-authz-plugin repository hosts a small [Docker Authorization Plugin](https://docs.docker.com/engine/extend/plugins_authorization/).
|
||||
Docker's authorization plugin system allows an external process to receive all requests sent to the Docker daemon. The
|
||||
authorization plugin replies, instructing the Docker daemon to allow or reject the request.
|
||||
|
||||
1. Create an empty policy definition that will allow all requests:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
allow_request = true :- true
|
||||
EOF
|
||||
|
||||
This policy definition is about simple as it can be. The policy includes a single rule named allow_request that is defined
|
||||
to always be true. Once all of the components are running, we will come back and extend the policy.
|
||||
|
||||
1. Run the executable downloaded in the previous step and then open another terminal:
|
||||
|
||||
sudo ./docker-authz-plugin
|
||||
|
||||
> This step requires sudo access because the Docker plugin framework will attempt to update the Docker daemon configuration.
|
||||
If you run without sudo you may encounter a permission error.
|
||||
|
||||
1. <a name="step-6"></a>Update Docker's configuration to include the following command line argument:
|
||||
|
||||
--authorization-plugin=docker-authz-plugin
|
||||
|
||||
On Ubuntu 16.04 with systemd, this can be done as follows (requires root):
|
||||
|
||||
sudo mkdir -p /etc/systemd/system/docker.service.d
|
||||
sudo tee -a /etc/systemd/system/docker.service.d/override.conf > /dev/null <<EOF
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/docker daemon -H fd:// --authorization-plugin=docker-authz-plugin
|
||||
EOF
|
||||
sudo systemctl daemon-reload
|
||||
sudo service docker restart
|
||||
|
||||
If you are using a different Linux distribution or you are not running systemd the step will be slightly different.
|
||||
|
||||
1. Run a simple Docker command to make sure everything is still working:
|
||||
|
||||
docker ps
|
||||
|
||||
If everything is setup correctly, the command should exit successfully. You can expect to see log messages
|
||||
from OPA and the plugin.
|
||||
|
||||
1. Test the policy definition is working by modifying it to **deny** all requests:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
allow_request = true :- false
|
||||
EOF
|
||||
|
||||
In OPA, rules defines the content of documents, e.g., objects, arrays, strings, booleans, etc.
|
||||
|
||||
In steps above, we created a rule named allow_request that defines a document that is the boolean value true.
|
||||
When all of the expressions on the body of the rule evaluate to true, we say the document is defined.
|
||||
If any of the expressions evaluate to false, we say the document is *undefined*.
|
||||
In this case, the document will always be undefined because the body of the rule is false.
|
||||
|
||||
docker ps
|
||||
|
||||
The output from the last command will be:
|
||||
|
||||
Error response from daemon: authorization denied by plugin docker-authz-plugin: request rejected by administrative policy
|
||||
|
||||
To learn more about how rules define the content of documents, see the [Data Model](/docs/arch.html#data-model) section in the architectural overview.
|
||||
|
||||
With this policy in place, users will not be able to run any Docker commands. Go ahead and try other commands such as `docker run` or `docker pull`,
|
||||
they will all be rejected. Let's change the policy so that it's a bit more useful.
|
||||
|
||||
1. Update the policy to reject requests with the unconfined [seccomp](https://en.wikipedia.org/wiki/Seccomp) profile:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import request as req
|
||||
|
||||
seccomp_unconfined :-
|
||||
# This expression asserts that the string on the right hand side exists
|
||||
# within the array SecurityOpt referenced on the left hand side.
|
||||
req.Body.HostConfig.SecurityOpt[_] = "seccomp:unconfined"
|
||||
|
||||
allow_request = true :- not seccomp_unconfined
|
||||
EOF
|
||||
|
||||
The plugin is watching the policy definition file for changes. Each time we change the file, the plugin reads the file and sends it to OPA. To manually send the policy to OPA, you can use the following API:
|
||||
|
||||
curl -X PUT --data-binary @example.rego \
|
||||
http://localhost:8181/v1/policies/example_policy
|
||||
|
||||
This API is idempotent so sending the policy multiple times is fine. Go ahead and try it yourself.
|
||||
|
||||
1. Test the policy is working by running a simple container:
|
||||
|
||||
docker run hello-world
|
||||
|
||||
Now try running the same container but disable seccomp (which should be prevented by the policy):
|
||||
|
||||
docker run --security-opt seccomp=unconfined hello-world
|
||||
|
||||
When Docker processes the run command it contacts the plugin to see if the request(s) should be allowed. The plugin takes the request and executes a query against OPA using the request as input data to the query. The same API call that the plugin makes can be executed using curl:
|
||||
|
||||
curl -v -G http://localhost:8181/v1/data/opa/example/allow_request \
|
||||
--data-urlencode \
|
||||
'global=request:{"Body":{"HostConfig":{"SecurityOpt":["seccomp=unconfined"]}}}'
|
||||
|
||||
Because the document generated by the allow_request rule is undefined in this case, OPA responds with a 404.
|
||||
|
||||
You can re-run the same query with the default seccomp profile and see that it succeeds:
|
||||
|
||||
curl -G http://localhost:8181/v1/data/opa/example/allow_request \
|
||||
--data-urlencode \
|
||||
'global=request:{"Body":{"HostConfig":{"SecurityOpt":["seccomp=default"]}}}'
|
||||
|
||||
Congratulations! You have successfully prevented containers from running without seccomp!
|
||||
|
||||
So far, the policy has been defined in terms of input data from the plugin. In many cases, it's necessary to write policies against multiple data sources.
|
||||
|
||||
The rest of the example shows how you can grant fine grained access to specific clients. To do so, we will insert fake user data into OPA to simulate an authentication system.
|
||||
|
||||
1. Create a configuration file to include an HTTP header in all of the requests sent to the Docker daemon. This header will identify the user:
|
||||
|
||||
mkdir -p ~/.docker
|
||||
|
||||
# Backup your existing Docker configuration, just in case. You can replace the configuration
|
||||
# which we generate below after you are done the example.
|
||||
cp ~/.docker/config.json ~/.docker/config.json~
|
||||
|
||||
cat >~/.docker/config.json <<EOF
|
||||
{
|
||||
"HttpHeaders": {
|
||||
"Authz-User": "bob"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
Currently, Docker does not provide a way to authenticate clients. 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 example, we assume that an authentication system is place.
|
||||
|
||||
1. Add user data directly into OPA:
|
||||
|
||||
cat >users.json <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/",
|
||||
"value": {
|
||||
"alice": {
|
||||
"readOnly": false
|
||||
},
|
||||
"bob": {
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
|
||||
curl -X PATCH -d @users.json \
|
||||
http://localhost:8181/v1/data/users \
|
||||
-H "Content-Type: application/json"
|
||||
|
||||
This data represents information about users that could either come from an external system or be included in policy definitions.
|
||||
|
||||
To see that the user data has been added, we can query the Data API. This shows the properties associated with the user "alice":
|
||||
|
||||
curl http://localhost:8181/v1/data/users/alice
|
||||
|
||||
1. Update the policy definition to include basic user access controls:
|
||||
|
||||
cat >example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import request as req
|
||||
import data.users
|
||||
|
||||
allow_request = true :- valid_user_role
|
||||
|
||||
# valid_user_role defines a document that is the boolean value true if this is
|
||||
# a write request and the user is allowed to perform writes.
|
||||
valid_user_role :-
|
||||
user_id = req.Headers["Authz-User"],
|
||||
user = users[user_id],
|
||||
user.readOnly = false
|
||||
|
||||
# valid_user_role is defined again here to handle read requests. When a rule
|
||||
# like this is defined multiple times, the rule definition must ensure that
|
||||
# only one instance evaluates successfully in a given query. If multiple
|
||||
# instances evaluated successfully, it indicates a conflict.
|
||||
valid_user_role :-
|
||||
user_id = req.Headers["Authz-User"],
|
||||
user = users[user_id],
|
||||
req.Method = "GET",
|
||||
user.readOnly = true
|
||||
EOF
|
||||
|
||||
In the new policy, the valid_user_role rules reference the "users" document created in the previous step.
|
||||
|
||||
1. Attempt to run a container. Because the configured user is "bob", the request is rejected:
|
||||
|
||||
docker run hello-world
|
||||
|
||||
1. Change the user to "alice" and re-run the container:
|
||||
|
||||
cat > ~/.docker/config.json <<EOF
|
||||
{
|
||||
"HttpHeaders": {
|
||||
"Authz-User": "alice"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
docker run hello-world
|
||||
|
||||
That's it!
|
||||
|
||||
Remember to undo the configuration changes from [Step 6](#step-6) and [Step 12](#step-12) after you are
|
||||
finished with the example.
|
||||
@@ -1,172 +0,0 @@
|
||||
---
|
||||
layout: docs
|
||||
header_title: Examples
|
||||
title: Hello World
|
||||
section: examples
|
||||
show_in_header: true
|
||||
sort_order: 1000
|
||||
---
|
||||
|
||||
Hello World
|
||||
-----------
|
||||
|
||||
This example demonstrates how to run OPA and load policy definitions and data
|
||||
via the REST APIs.
|
||||
|
||||
Once you finish this example you will be familiar with:
|
||||
|
||||
- Running OPA in server mode locally on your machine.
|
||||
- Loading policy definitions and [Base Documents](/docs/arch.html#data-model) via the REST APIs.
|
||||
- Querying Base and [Virtual Documents](/docs/arch.html#data-model) via the REST APIs.
|
||||
|
||||
In this example, we will write rules to identify servers that are in violation of
|
||||
a security policy. The security policy states that servers connected to a public network
|
||||
must not expose unencrypted HTTP endpoints.
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
The steps below assume you have the latest version of OPA on your machine. You can download the
|
||||
latest executable by following the link for your platform at [openpolicyagent.org](http://openpolicyagent.org).
|
||||
|
||||
The steps below assume the OPA executable is in your PATH.
|
||||
|
||||
Steps
|
||||
=====
|
||||
|
||||
1. Create a directory that OPA can store policy definitions in:
|
||||
|
||||
mkdir -p policies
|
||||
|
||||
1. Run OPA in server mode and enable console logging:
|
||||
|
||||
opa run -s --alsologtostderr 1 --v 2 --policy-dir policies
|
||||
|
||||
1. Open another shell and create a policy definition that defines two rules ("violations" and "public_servers"):
|
||||
|
||||
cat > example.rego <<EOF
|
||||
package opa.example
|
||||
|
||||
import data.servers
|
||||
import data.networks
|
||||
import data.ports
|
||||
|
||||
violations[server] :-
|
||||
server = servers[_],
|
||||
server.protocols[_] = "http",
|
||||
public_servers[server]
|
||||
|
||||
public_servers[server] :-
|
||||
server = servers[_],
|
||||
server.ports[_] = ports[i].id,
|
||||
ports[i].networks[_] = networks[j].id,
|
||||
networks[j].public = true
|
||||
EOF
|
||||
|
||||
1. Load the policy definition into OPA via the REST API.
|
||||
|
||||
curl -X PUT --data-binary @example.rego \
|
||||
http://localhost:8181/v1/policies/example
|
||||
|
||||
1. Load "servers" data into OPA via the REST API:
|
||||
|
||||
cat > servers.json <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add", "path": "/",
|
||||
"value": []
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
|
||||
curl -X PATCH -d @servers.json \
|
||||
http://localhost:8181/v1/data/servers \
|
||||
-H "Content-Type: application/json-patch+json"
|
||||
|
||||
1. Load "networks" data into OPA via the REST API:
|
||||
|
||||
cat > networks.json <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add", "path": "/",
|
||||
"value": []
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "n1", "public": false}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "n2", "public": false}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "n3", "public": true}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
|
||||
curl -X PATCH -d @networks.json \
|
||||
http://localhost:8181/v1/data/networks \
|
||||
-H "Content-Type: application/json-patch+json"
|
||||
|
||||
1. Load the "ports" into OPA via the REST API:
|
||||
|
||||
cat > ports.json <<EOF
|
||||
[
|
||||
{
|
||||
"op": "add", "path": "/",
|
||||
"value": []
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "p1", "networks": ["n1"]}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "p2", "networks": ["n3"]}
|
||||
},
|
||||
{
|
||||
"op": "add", "path": "-",
|
||||
"value": {"id": "p3", "networks": ["n2"]}
|
||||
}
|
||||
]
|
||||
EOF
|
||||
|
||||
curl -X PATCH -d @ports.json \
|
||||
http://localhost:8181/v1/data/ports \
|
||||
-H "Content-Type: application/json-patch+json"
|
||||
|
||||
1. Query the "public_servers" document. Because "s1" and "s4" are connected to network "n3" they will be contained in the response:
|
||||
|
||||
curl http://localhost:8181/v1/data/opa/example/public_servers
|
||||
|
||||
1. Query the "violations" document. Because "s4" is the only *public server* exposing unencrypted HTTP, it will be the only server in the document:
|
||||
|
||||
curl http://localhost:8181/v1/data/opa/example/violations
|
||||
|
||||
1. Update "s4" to reflect that it no longer exposes unencrypted HTTP:
|
||||
|
||||
curl -X PATCH -d '[{"op": "remove", "path": "/protocols/0"}]' \
|
||||
http://localhost:8181/v1/data/servers/3 \
|
||||
-H "Content-Type: application/json-patch+json"
|
||||
|
||||
1. Query the "violations" document again. The document will be empty now.
|
||||
|
||||
curl http://localhost:8181/v1/data/opa/example/violations
|
||||
+2
-2
@@ -7,8 +7,8 @@ show_in_header: true
|
||||
sort_order: 1
|
||||
---
|
||||
|
||||
What is Policy?
|
||||
===============
|
||||
Introduction
|
||||
============
|
||||
|
||||
Policy defines expected behavior in response to specific events within an application.
|
||||
|
||||
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
---
|
||||
layout: docs
|
||||
title: Language
|
||||
title: Rego
|
||||
section: references
|
||||
sort_order: 3
|
||||
---
|
||||
|
||||
# Rego: OPA's Query Language
|
||||
# Rego
|
||||
|
||||
OPA includes a policy engine that is purpose built for reasoning about information represented in structured documents such as JSON. Data stored in in the policy engine can be queried using OPA's native query language: Rego.
|
||||
|
||||
|
||||
+9
-4
@@ -10,11 +10,15 @@ layout: default
|
||||
<h2>Get OPA</h2>
|
||||
Latest binary (Linux/64-bit):<br><br>
|
||||
<pre>
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc1/opa_linux_amd64 > opa
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc2/opa_linux_amd64 > opa
|
||||
chmod u+x opa
|
||||
./opa version
|
||||
</pre>
|
||||
Latest binary (Mac OSX/64-bit):<br><br>
|
||||
<pre>
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc1/opa_darwin_amd64 > opa
|
||||
curl -L https://github.com/open-policy-agent/opa/releases/download/v0.1.0-rc2/opa_darwin_amd64 > opa
|
||||
chmod u+x opa
|
||||
./opa version
|
||||
</pre>
|
||||
From source (requires Go):<br><br>
|
||||
<pre>
|
||||
@@ -22,6 +26,7 @@ git clone https://github.com/open-policy-agent/opa.git $GOPATH/src/github.com/op
|
||||
cd $GOPATH/src/github.com/open-policy-agent/opa
|
||||
make deps
|
||||
make
|
||||
./opa version
|
||||
</pre>
|
||||
<div class="features">
|
||||
<h2>Features</h2>
|
||||
@@ -74,8 +79,8 @@ make
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Usage">
|
||||
<h2>Usage</h2>
|
||||
<div class="License">
|
||||
<h2>License</h2>
|
||||
<p>
|
||||
OPA is licensed under the Apache 2.0 open source license. OPA is under
|
||||
active development.
|
||||
|
||||
Reference in New Issue
Block a user