1# Get Started
2
3The Open Policy Agent (OPA) is an open source, general-purpose policy engine that enables unified, context-aware policy enforcement across the entire stack.
4
5OPA provides a high-level **declarative language** for authoring policies and
6simple APIs to answer policy queries. Using OPA, you can offload policy
7decisions from your service such as:
8
9* Should this API call be allowed? E.g., `true` or `false`.
10* How much quota remains for this user? E.g., `1048`.
11* Which hosts can this container be deployed on? E.g., `["host1", "host40", ..., "host329"]`.
12* What updates must be applied to this resource? E.g., `{"labels": {"team": "products}}`.
13
14This tutorial shows how to get started with OPA using an interactive shell or [REPL (read-eval-print loop)](https://en.wikipedia.org/wiki/Read–eval–print_loop).
15
16## Goals
17
18REPLs are great for learning new languages and running quick experiments. You
19can use OPA's REPL to experiment with policies and prototype new ones.
20
21To introduce the REPL, you will use dummy data and an example policy. In English, the policy can be stated as follows:
22
23- Servers that open an unencrypted HTTP port must not be connected to a public network.
24
25Inside the REPL, you will define rules that codify the policy stated above.
26
27Once you finish this tutorial, you will be familiar with:
28
29  * Running OPA as an interactive shell/REPL.
30  * Writing ad-hoc queries in [Rego](/how-do-i-write-policies.md).
31
32## Prerequisites
33
34If this is your first time using OPA, download the latest executable for your system.
35
36On macOS (64-bit):
37
38```shell
39curl -L -o opa https://github.com/open-policy-agent/opa/releases/download/v0.8.2/opa_darwin_amd64
40```
41
42On Linux (64-bit):
43
44```shell
45curl -L -o opa https://github.com/open-policy-agent/opa/releases/download/v0.8.2/opa_linux_amd64
46```
47
48> Windows users can obtain the OPA executable from [GitHub Releases](https://github.com/open-policy-agent/opa/releases). The steps below are the same for Windows users except the executable name will be different.
49
50Set permissions on the OPA executable:
51
52```shell
53chmod 755 ./opa
54```
55
56## Steps
57
58### 1. Make sure you can run the REPL on your machine.
59
60```shell
61./opa run
62```
63
64Without any data, you can experiment with simple expressions to get the hang of it:
65
66```ruby
67> true
68true
69> 3.14
703.14
71> ["hello", "world"]
72[
73  "hello",
74  "world"
75]
76```
77
78You can also test simple boolean expressions:
79
80```ruby
81> true == false
82false
83> 3.14 > 3
84true
85> "hello" != "goodbye"
86true
87```
88
89Most REPLs let you define variables that you can reference later on. OPA allows you to do something similar. For example, you can define a `pi` constant as follows:
90
91```ruby
92> pi = 3.14
93```
94
95Once "pi" is defined, you query for the value and write expressions in terms of it:
96
97```ruby
98> pi
993.14
100> pi > 3
101true
102```
103
104One thing to watch out for in the REPL is that `=` is used both for assigning variables values and for testing the value of variables. For example `p = q` sometimes assigns `p` the value of `q` and sometimes checks if the values of `p` and `q` are the same. The REPL decides between assignment and test based on whether `p` already has a value or not. If `p` has a value, `p = q` is a test (returning `true` or `false`), and if `p` has no value `p = q` is an assignment. To unset a value for a variable, use the `unset` command. (This ambiguity is only really an issue in the REPL. When writing policy, the duality of `=` is actually beneficial.)
105
106```ruby
107> pi = 3
108undefined
109> unset pi
110> pi = 3
111> pi
1123
113```
114
115In addition to running queries, the REPL also lets you define rules:
116
117```ruby
118> items = ["pizza", "apples", "bread", "coffee"]
119> users = {"bob": {"likes": [0, 2]}, "alice": {"likes": [1, 2, 3]}}
120> likes[[name, item]] { index = users[name].likes[_]; item = items[index] }
121```
122
123The likes rule above defines a set of tuples specifying what each user likes.
124
125```
126> likes[["alice", item]] # what does alice like?
127+----------+
128|   item   |
129+----------+
130| "apples" |
131| "bread"  |
132| "coffee" |
133+----------+
134> likes[[name, "bread"]] # who likes bread?
135+---------+
136|  name   |
137+---------+
138| "bob"   |
139| "alice" |
140+---------+
141```
142
143When you enter expressions into the OPA REPL, you are effectively running *queries*. The REPL output shows the values of variables in the expression that make the query `true`. If there is no set of variables that would make the query `true`, the REPL prints `false`. If there are no variables in the query and the query evaluates successfully, then the REPL just prints `true`.
144
145Quit out of the REPL by pressing Control-D or typing `exit`:
146
147```ruby
148> exit
149Exiting
150```
151
152### 2. Create a data file and a policy module.
153
154Let's define a bit of JSON data that will be used in the tutorial:
155
156```shell
157cat >data.json <<EOF
158{
159    "servers": [
160        {"id": "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]},
161        {"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]},
162        {"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]},
163        {"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]}
164    ],
165    "networks": [
166        {"id": "n1", "public": false},
167        {"id": "n2", "public": false},
168        {"id": "n3", "public": true}
169    ],
170    "ports": [
171        {"id": "p1", "networks": ["n1"]},
172        {"id": "p2", "networks": ["n3"]},
173        {"id": "p3", "networks": ["n2"]}
174    ]
175}
176EOF
177```
178
179Also, let's include a rule that defines a set of servers that are attached to public networks:
180
181```shell
182cat >example.rego <<EOF
183package opa.example
184
185import data.servers
186import data.networks
187import data.ports
188
189public_servers[s] {
190    s = servers[_]
191    s.ports[_] = ports[i].id
192    ports[i].networks[_] = networks[j].id
193    networks[j].public = true
194}
195EOF
196```
197
198### 3. Run the REPL with the data file and policy module as input.
199
200```shell
201./opa run data.json example.rego
202```
203
204You can now run queries against the various documents:
205
206```ruby
207> data.servers[_].id
208+--------------------+
209| data.servers[_].id |
210+--------------------+
211| "s1"               |
212| "s2"               |
213| "s3"               |
214| "s4"               |
215+--------------------+
216> data.opa.example.public_servers[x]
217+-------------------------------------------------------------------------------+
218|                                       x                                       |
219+-------------------------------------------------------------------------------+
220| {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} |
221| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]}             |
222+-------------------------------------------------------------------------------+
223```
224
225One powerful thing about Rego and the REPL is that you can run queries using the same syntax that you would use to lookup values.
226
227For example if `i` has value `0` then `data.servers[i]` returns the first value in the `data.servers` array:
228
229```ruby
230> i = 0
231> data.servers[i]
232{
233  "id": "s1",
234  "name": "app",
235  "ports": [
236      "p1",
237      "p2",
238      "p3"
239  ],
240  "protocols": [
241      "https",
242      "ssh"
243  ]
244}
245```
246
247That same expression `data.servers[i]` when `i` has no value defines a query that returns all the values of `i` and `data.servers[i]`:
248
249```ruby
250> unset i
251> data.servers[i]
252+---+-------------------------------------------------------------------------------+
253| i |                                data.servers[i]                                |
254+---+-------------------------------------------------------------------------------+
255| 0 | {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} |
256| 1 | {"id":"s2","name":"db","ports":["p3"],"protocols":["mysql"]}                  |
257| 2 | {"id":"s3","name":"cache","ports":["p3"],"protocols":["memcache"]}            |
258| 3 | {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]}             |
259+---+-------------------------------------------------------------------------------+
260```
261
262### 4. Import and export documents.
263
264The REPL also understands the [Import and Package](/how-do-i-write-policies.md#modules) directives.
265
266```ruby
267> import data.servers
268> servers[i].ports[_] = "p2"; servers[i].id = id
269+---+------+
270| i |  id  |
271+---+------+
272| 0 | "s1" |
273| 3 | "s4" |
274+---+------+
275```
276
277```ruby
278> package opa.example
279> public_servers[x].protocols[_] = "http"
280+-------------------------------------------------------------------+
281|                                 x                                 |
282+-------------------------------------------------------------------+
283| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
284+-------------------------------------------------------------------+
285```
286
287### 5. Define a rule to identify servers in violation of our security policy.
288
289```ruby
290> import data.servers
291> violations[s] {
292  s = servers[_]
293  s.protocols[_] = "http"
294  public_servers[s]
295}
296
297> violations[server]
298+-------------------------------------------------------------------+
299|                              server                               |
300+-------------------------------------------------------------------+
301| {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} |
302+-------------------------------------------------------------------+
303```
304
305> The REPL accepts multi-line input and will change appearance when it detects multi-line input.
306