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