diff --git a/cmd/deps.go b/cmd/deps.go index 1ab654fc1e..dba62cdeec 100644 --- a/cmd/deps.go +++ b/cmd/deps.go @@ -74,8 +74,6 @@ Given a policy like this: package policy - import rego.v1 - allow if is_admin is_admin if "admin" in input.user.roles diff --git a/cmd/flags.go b/cmd/flags.go index 032833aa60..20a7ec742f 100644 --- a/cmd/flags.go +++ b/cmd/flags.go @@ -162,7 +162,7 @@ func addRegoV0V1FlagWithDescription(fs *pflag.FlagSet, regoV1 *bool, value bool, } func addV0CompatibleFlag(fs *pflag.FlagSet, v1Compatible *bool, value bool) { - fs.BoolVar(v1Compatible, "v0-compatible", value, "opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible") + fs.BoolVar(v1Compatible, "v0-compatible", value, "opt-in to OPA features and behaviors prior to the OPA v1.0 release") } func addV1CompatibleFlag(fs *pflag.FlagSet, v1Compatible *bool, value bool) { diff --git a/cmd/run.go b/cmd/run.go index 761de2adeb..68703655f3 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -215,7 +215,7 @@ See https://godoc.org/crypto/tls#pkg-constants for more information. runCommand.Flags().BoolVarP(&cmdParams.serverMode, "server", "s", false, "start the runtime in server mode") runCommand.Flags().IntVar(&cmdParams.rt.ReadyTimeout, "ready-timeout", 0, "wait (in seconds) for configured plugins before starting server (value <= 0 disables ready check)") runCommand.Flags().StringVarP(&cmdParams.rt.HistoryPath, "history", "H", historyPath(), "set path of history file") - cmdParams.rt.Addrs = runCommand.Flags().StringSliceP("addr", "a", []string{defaultAddr}, "set listening address of the server (e.g., [ip]: for TCP, unix:// for UNIX domain socket)") + cmdParams.rt.Addrs = runCommand.Flags().StringSliceP("addr", "a", []string{defaultLocalAddr}, "set listening address of the server (e.g., [ip]: for TCP, unix:// for UNIX domain socket)") cmdParams.rt.DiagnosticAddrs = runCommand.Flags().StringSlice("diagnostic-addr", []string{}, "set read-only diagnostic listening address of the server for /health and /metric APIs (e.g., [ip]: for TCP, unix:// for UNIX domain socket)") cmdParams.rt.UnixSocketPerm = runCommand.Flags().String("unix-socket-perm", "755", "specify the permissions for the Unix domain socket if used to listen for incoming connections") runCommand.Flags().BoolVar(&cmdParams.rt.H2CEnabled, "h2c", false, "enable H2C for HTTP listeners") @@ -381,9 +381,8 @@ func initRuntime(ctx context.Context, params runCmdParams, args []string, addrSe rt.SetDistributedTracingLogging() rt.Params.AddrSetByUser = addrSetByUser - // v0 negates v1 - if !addrSetByUser && !rt.Params.V0Compatible && rt.Params.V1Compatible { - rt.Params.Addrs = &[]string{defaultLocalAddr} + if !addrSetByUser && rt.Params.V0Compatible { + rt.Params.Addrs = &[]string{defaultAddr} } return rt, nil diff --git a/cmd/run_test.go b/cmd/run_test.go index 9caf11b261..676256740d 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -59,7 +59,7 @@ func TestRunServerBaseListenOnLocalhost(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - rt, err := initRuntime(ctx, params, nil, false) + rt, err := initRuntime(ctx, params, nil, true) if err != nil { t.Fatalf("Unexpected error: %v", err) } @@ -436,7 +436,7 @@ func TestInitRuntimeAddrSetByUser(t *testing.T) { func newTestRunParams() runCmdParams { params := newRunParams() params.rt.GracefulShutdownPeriod = 1 - params.rt.Addrs = &[]string{"localhost:0"} + params.rt.Addrs = &[]string{"localhost:8181"} params.rt.DiagnosticAddrs = &[]string{} params.serverMode = true return params diff --git a/cmd/test.go b/cmd/test.go index f271395c6c..94cdd173e9 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -467,8 +467,6 @@ Example policy (example/authz.rego): package authz - import rego.v1 - allow if { input.path == ["users"] input.method == "POST" @@ -483,8 +481,6 @@ Example test (example/authz_test.rego): package authz_test - import rego.v1 - import data.authz.allow test_post_allowed if { diff --git a/docs/README.md b/docs/README.md index 7267f952eb..373ad68ed2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -173,8 +173,6 @@ In this module: ```live:rule_body:module package example -import rego.v1 - u if { "foo" == "foo" } @@ -202,8 +200,6 @@ Here's what a more complex set of blocks could look like: ``````markdown ```live:eg:module:hidden package example - -import rego.v1 ``` We can define a scalar rule: @@ -253,8 +249,6 @@ Whereas the `eg/string/rule` output is evaluated with the module: ``` package example -import rego.v1 - string := "hello" r if input.value == string diff --git a/docs/content/_index.md b/docs/content/_index.md index 153ace10fe..5f42076c75 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -127,7 +127,6 @@ at some point in time, but have been introduced gradually. ```live:example/refs:module:hidden package example -import rego.v1 ``` When OPA evaluates policies it binds data provided in the query to a global @@ -164,7 +163,6 @@ input.deadbeef ```live:example/exprs:module:hidden package example -import rego.v1 ``` To produce policy decisions in Rego you write expressions against input and @@ -223,7 +221,6 @@ input.servers[0].protocols[0] == "telnet" ```live:example/vars:module:hidden package example -import rego.v1 ``` You can store values in intermediate variables using the `:=` (assignment) @@ -275,7 +272,6 @@ x != y # y has not been assigned a value ```live:example/iter:module:hidden package example -import rego.v1 ``` Like other declarative languages (e.g., SQL), iteration in Rego happens @@ -375,19 +371,6 @@ some i; input.servers[i].protocols[i] == "ssh" # there is no assignment of i th While plain iteration serves as a powerful building block, Rego also features ways to express _FOR SOME_ and _FOR ALL_ more explicitly. -{{< info >}} -To ensure backwards-compatibility, the keywords discussed below introduced slowly. -In the first stage, users can opt-in to using the new keywords via a special import: -`import rego.v1` or, alternatively, `import future.keywords.every` introduces the `every` keyword described here. -(Importing `every` means also importing `in` without an extra `import` statement.) - -At some point in the future, the keyword will become _standard_, and the import will -become a no-op that can safely be removed. This should give all users ample time to -update their policies, so that the new keyword will not cause clashes with existing -variable names. -[See the docs on _future keywords_](./policy-language/#future-keywords) for more information. -{{< /info >}} - ##### FOR SOME (`some`) `some ... in ...` is used to iterate over the collection (its last argument), @@ -482,7 +465,6 @@ logic statements. Rules can either be "complete" or "partial". ```live:example/complete:module:hidden package example.rules -import rego.v1 ``` #### Complete Rules @@ -573,7 +555,6 @@ any_public_networks ```live:example/partial_set:module:hidden package example -import rego.v1 ``` Partial rules are if-then statements that generate a set of values and @@ -649,8 +630,6 @@ protocols: ```live:example/logical_or/complete:module:openable,merge_down package example.logical_or -import rego.v1 - default shell_accessible := false shell_accessible if { @@ -692,8 +671,6 @@ could be modified to generate a set of servers that expose `"telnet"` or ```live:example/logical_or/partial_set:module:openable,merge_down package example.logical_or -import rego.v1 - shell_accessible contains server.id if { server := input.servers[_] server.protocols[_] == "telnet" @@ -753,8 +730,6 @@ For example: ```live:example/final:module:openable,merge_down package example -import rego.v1 - allow if { # allow is true if... count(violation) == 0 # there are zero violations. } @@ -892,8 +867,6 @@ For example: ```live:example/using_opa:module:openable,read_only package example -import rego.v1 - default allow := false # unless otherwise defined, allow is false allow if { # allow is true if... @@ -1034,10 +1007,9 @@ You can start OPA as a server with `-s` or `--server`: ./opa run --server ./example.rego ``` -By default OPA listens for HTTP connections on `0.0.0.0:8181`. See `opa run +By default OPA listens for HTTP connections on `localhost:8181`. See `opa run --help` for a list of options to change the listening address, enable TLS, and -more. For example, if the `--v1-compatible` flag is set, OPA will listen -for HTTP connections on `localhost:8181` by default. +more. Inside of another terminal use `curl` (or a similar tool) to access OPA's HTTP API. When you query the `/v1/data` HTTP API you must wrap input data inside of a diff --git a/docs/content/aws-cloudformation-hooks.md b/docs/content/aws-cloudformation-hooks.md index 03c4389de8..712b6abe5f 100644 --- a/docs/content/aws-cloudformation-hooks.md +++ b/docs/content/aws-cloudformation-hooks.md @@ -174,8 +174,6 @@ simple policy to block an S3 Bucket unless it has an `AccessControl` attribute s ```live:example/system:module package system -import rego.v1 - main := { "allow": count(deny) == 0, "violations": deny, @@ -361,8 +359,6 @@ take a look at what such a main policy might look like: # package system -import rego.v1 - main := { "allow": count(violations) == 0, "violations": violations, @@ -437,8 +433,6 @@ We can now modify our original policy to verify S3 bucket resources only: ```live:example/bucket:module package aws.s3.bucket -import rego.v1 - deny contains sprintf("S3 Bucket %s 'AccessControl' attribute value must be 'Private'", [input.resource.id]) if { not bucket_is_private } @@ -467,8 +461,6 @@ A simple authz policy for checking the bearer token might look something like th ```live:example/authz:module package system.authz -import rego.v1 - default allow := false allow if { diff --git a/docs/content/cli.md b/docs/content/cli.md index 6b070b2ae8..d12b1b1026 100755 --- a/docs/content/cli.md +++ b/docs/content/cli.md @@ -63,7 +63,7 @@ opa bench [flags] -I, --stdin-input read input document from stdin -t, --target {rego,wasm} set the runtime to exercise (default rego) -u, --unknowns stringArray set paths to treat as unknown during partial evaluation (default [input]) - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release ``` ____ @@ -256,7 +256,7 @@ opa build [ [...]] [flags] --signing-key string set the secret (HMAC) or path of the PEM file containing the private key (RSA and ECDSA) --signing-plugin string name of the plugin to use for signing/verification (see https://www.openpolicyagent.org/docs/latest/management-bundles/#signature-plugin -t, --target {rego,wasm,plan} set the output bundle target type (default rego) - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release --verification-key string set the secret (HMAC) or path of the PEM file containing the public key (RSA and ECDSA) --verification-key-id string name assigned to the verification key used for bundle verification (default "default") --wasm-include-print enable print statements inside of WebAssembly modules compiled by the compiler @@ -324,7 +324,7 @@ opa capabilities [flags] --current print current capabilities --file string print capabilities defined by a file -h, --help help for capabilities - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release --version string print capabilities of a specific version ``` @@ -355,10 +355,10 @@ opa check [path [...]] [flags] -h, --help help for check --ignore strings set file and directory names to ignore during loading (e.g., '.*' excludes hidden files) -m, --max-errors int set the number of errors to allow before compilation fails early (default 10) - --rego-v1 check for Rego v1 compatibility (policies must also be compatible with current OPA version) -s, --schema string set schema file path or directory path -S, --strict enable compiler strict mode - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release + --v0-v1 check for Rego v0 and v1 compatibility (policies must be compatible with both Rego versions) ``` ____ @@ -380,8 +380,6 @@ Given a policy like this: package policy - import rego.v1 - allow if is_admin is_admin if "admin" in input.user.roles @@ -572,7 +570,7 @@ opa eval [flags] -t, --target {rego,wasm} set the runtime to exercise (default rego) --timeout duration set eval timeout (default unlimited) -u, --unknowns stringArray set paths to treat as unknown during partial evaluation (default [input]) - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release --var-values show local variable values in pretty trace output ``` @@ -634,7 +632,7 @@ opa exec [ [...]] [flags] --set-file stringArray override config values with files on the command line (use commas to specify multiple values) -I, --stdin-input read input document from stdin rather than a static file --timeout duration set exec timeout with a Go-style duration, such as '5m 30s'. (default unlimited) - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release ``` ____ @@ -652,7 +650,7 @@ is provided - this tool will use stdin. The format of the output is not defined specifically; whatever this tool outputs is considered correct format (with the exception of bugs). -If the '-w' option is supplied, the 'fmt' command with overwrite the source file +If the '-w' option is supplied, the 'fmt' command will overwrite the source file instead of printing to stdout. If the '-d' option is supplied, the 'fmt' command will output a diff between the @@ -665,6 +663,25 @@ to stdout from the 'fmt' command. If the '--fail' option is supplied, the 'fmt' command will return a non zero exit code if a file would be reformatted. +The 'fmt' command can be run in several compatibility modes for consuming and outputting +different Rego versions: + +* 'opa fmt': + * v1 Rego is formatted to v1 + * 'rego.v1'/'future.keywords' imports are NOT removed + * 'rego.v1'/'future.keywords' imports are NOT added if missing + * v0 rego is rejected +* 'opa fmt --v0-compatible': + * v0 Rego is formatted to v0 + * v1 Rego is rejected +* 'opa fmt --v0-v1': + * v0 Rego is formatted to be compatible with v0 AND v1 + * v1 Rego is rejected +* 'opa fmt --v0-v1 --v1-compatible': + * v1 Rego is formatted to be compatible with v0 AND v1 + * v0 Rego is rejected + + ``` opa fmt [path [...]] [flags] ``` @@ -678,8 +695,8 @@ opa fmt [path [...]] [flags] --fail non zero exit code on reformat -h, --help help for fmt -l, --list list all files who would change when formatted - --rego-v1 format module(s) to be compatible with both Rego v1 and current OPA version) - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release + --v0-v1 format module(s) to be compatible with both Rego v0 and v1 -w, --write overwrite the original source file ``` @@ -726,7 +743,7 @@ opa inspect [ [...]] [flags] -a, --annotations list annotations -f, --format {json,pretty} set output format (default pretty) -h, --help help for inspect - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release ``` ____ @@ -892,7 +909,7 @@ opa run [flags] ### Options ``` - -a, --addr strings set listening address of the server (e.g., [ip]: for TCP, unix:// for UNIX domain socket) (default [:8181]) + -a, --addr strings set listening address of the server (e.g., [ip]: for TCP, unix:// for UNIX domain socket) (default [localhost:8181]) --authentication {token,tls,off} set authentication scheme (default off) --authorization {basic,off} set authorization scheme (default off) -b, --bundle load paths as bundle files or root directories @@ -928,7 +945,7 @@ opa run [flags] --tls-cipher-suites strings set list of enabled TLS 1.0–1.2 cipher suites (IANA) --tls-private-key-file string set path of TLS private key file --unix-socket-perm string specify the permissions for the Unix domain socket if used to listen for incoming connections (default "755") - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release --verification-key string set the secret (HMAC) or path of the PEM file containing the public key (RSA and ECDSA) --verification-key-id string name assigned to the verification key used for bundle verification (default "default") -w, --watch watch command line files for changes @@ -1066,8 +1083,6 @@ Example policy (example/authz.rego): package authz - import rego.v1 - allow if { input.path == ["users"] input.method == "POST" @@ -1082,8 +1097,6 @@ Example test (example/authz_test.rego): package authz_test - import rego.v1 - import data.authz.allow test_post_allowed if { @@ -1147,7 +1160,7 @@ opa test [path [...]] [flags] -t, --target {rego,wasm} set the runtime to exercise (default rego) --threshold float set coverage threshold and exit with non-zero status if coverage is less than threshold % --timeout duration set test timeout (default 5s, 30s when benchmarking) - --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release. Takes precedence over --v1-compatible + --v0-compatible opt-in to OPA features and behaviors prior to the OPA v1.0 release --var-values show local variable values in test output -v, --verbose set verbose reporting mode -w, --watch watch command line files for changes diff --git a/docs/content/comparison-to-other-systems.md b/docs/content/comparison-to-other-systems.md index 679c22f5f0..cbb82a6093 100644 --- a/docs/content/comparison-to-other-systems.md +++ b/docs/content/comparison-to-other-systems.md @@ -54,8 +54,6 @@ example RBAC policy shown above. ```live:rbac:module:openable package rbac.authz -import rego.v1 - # user-role assignments user_roles := { "alice": ["engineering", "webdev"], @@ -185,8 +183,6 @@ OPA supports ABAC policies as shown below. ```live:abac:module:openable package abac -import rego.v1 - # User attributes user_attributes := { "alice": {"tenure": 15, "title": "trader"}, @@ -300,8 +296,6 @@ expect the input to have `principal`, `action`, and `resource` fields. ```live:iam:module:openable package aws -import rego.v1 - default allow := false # FirstStatement @@ -461,8 +455,6 @@ roughly the same as for XACML: attributes of users, actions, and resources. ```live:xacml:module:openable package xacml -import rego.v1 - # METADATA # title: urn:curtiss:ba:taa:taa-1.1 # description: Policy for Business Authorization category TAA-1.1 diff --git a/docs/content/deployments.md b/docs/content/deployments.md index 908a3b280e..a60e70138f 100644 --- a/docs/content/deployments.md +++ b/docs/content/deployments.md @@ -30,12 +30,11 @@ The `run` command accepts a `--server` (or `-s`) flag that starts OPA as a server. See `--help` for more information on other arguments. The most important command line arguments for OPA's server mode are: -* `--addr` to set the listening address (default: `0.0.0.0:8181`). +* `--addr` to set the listening address (default: `localhost:8181`). * `--log-level` (or `-l`) to set the log level (default: `"info"`). * `--log-format` to set the log format (default: `"json"`). -* `--v1-compatible` to opt-in to OPA features and behaviors that will be enabled by default in a future OPA v1.0 release. For example, setting the listening address to `localhost:8181` by default. -By default, OPA listens for normal HTTP connections on `0.0.0.0:8181`. To make +By default, OPA listens for normal HTTP connections on `localhost:8181`. To make OPA listen for HTTPS connections, see [Security](../security). We can run OPA as a server using Docker: diff --git a/docs/content/docker-authorization.md b/docs/content/docker-authorization.md index 0728497e5c..5fa471c134 100644 --- a/docs/content/docker-authorization.md +++ b/docs/content/docker-authorization.md @@ -189,8 +189,6 @@ Now let's change the policy so that it's a bit more useful. ```live:docker_authz_deny_unconfined:module:openable package docker.authz -import rego.v1 - default allow := false allow if { @@ -391,8 +389,6 @@ EOF ```live:docker_authz_users:module:read_only,openable package docker.authz -import rego.v1 - default allow := false # allow if the user is granted read/write access. diff --git a/docs/content/envoy-primer.md b/docs/content/envoy-primer.md index a2419ff904..7734fbeb54 100644 --- a/docs/content/envoy-primer.md +++ b/docs/content/envoy-primer.md @@ -15,8 +15,6 @@ Let's start with an example policy that restricts access to an endpoint based on ```live:bool_example:module:openable package envoy.authz -import rego.v1 - import input.attributes.request.http default allow := false @@ -113,8 +111,6 @@ If you want, you can also control the HTTP status sent to the upstream or downst ```live:obj_example:module:openable package envoy.authz -import rego.v1 - import input.attributes.request.http default allow := false @@ -440,8 +436,6 @@ access the path `/people`. ```live:parsed_path_example:module:read_only package envoy.authz -import rego.v1 - default allow := false allow if input.parsed_path == ["people"] @@ -454,8 +448,6 @@ the HTTP URL query as a map of string array. The below sample policy allows anyo ```live:parsed_query_example:module:read_only package envoy.authz -import rego.v1 - default allow := false allow if { @@ -472,8 +464,6 @@ can then be used in a policy as shown below. ```live:parsed_body_example:module:read_only package envoy.authz -import rego.v1 - default allow := false allow if { diff --git a/docs/content/envoy-tutorial-gloo-edge.md b/docs/content/envoy-tutorial-gloo-edge.md index 22122164b9..519207f16c 100644 --- a/docs/content/envoy-tutorial-gloo-edge.md +++ b/docs/content/envoy-tutorial-gloo-edge.md @@ -109,8 +109,6 @@ The following OPA policy will work as follows: ```live:example:module:openable package envoy.authz -import rego.v1 - import input.attributes.request.http as http_request default allow := false diff --git a/docs/content/envoy-tutorial-istio.md b/docs/content/envoy-tutorial-istio.md index d98a8f9a68..e6f9b5227f 100644 --- a/docs/content/envoy-tutorial-istio.md +++ b/docs/content/envoy-tutorial-istio.md @@ -48,8 +48,6 @@ The `quick_start.yaml` manifest defines the following resources: ```live:example:module:openable package istio.authz - - import rego.v1 default allow := false diff --git a/docs/content/envoy-tutorial-standalone-envoy.md b/docs/content/envoy-tutorial-standalone-envoy.md index 958e6580bc..23cde81cc1 100644 --- a/docs/content/envoy-tutorial-standalone-envoy.md +++ b/docs/content/envoy-tutorial-standalone-envoy.md @@ -96,8 +96,6 @@ This tutorial assumes you have some Rego knowledge, in summary the policy below # policy.rego package envoy.authz -import rego.v1 - import input.attributes.request.http as http_request default allow := false diff --git a/docs/content/faq.md b/docs/content/faq.md index 88543dc846..34b7457c7f 100644 --- a/docs/content/faq.md +++ b/docs/content/faq.md @@ -52,8 +52,8 @@ mean the same thing whichever order you write them in. ```live:unordered:module:openable package unordered -ratelimit := 4 { input.name == "alice" } -ratelimit := 5 { input.name == "bob" } +ratelimit := 4 if input.name == "alice" +ratelimit := 5 if input.name == "bob" ``` ```live:unordered:input @@ -74,9 +74,9 @@ Sometimes, though, you want the statement order to matter. For example, you mig ```live:ordered:module:openable package ordered -ratelimit := 4 { +ratelimit := 4 if { input.owner == "bob" -} else := 5 { +} else := 5 if { input.name == "alice" } ``` @@ -165,7 +165,7 @@ One is the *function*, which is conceptually identical to functions from most pr ```live:functions:module:openable package functions -trim_and_split(s) := result { +trim_and_split(s) := result if { t := trim(s, " ") result := split(t, ".") } @@ -183,7 +183,7 @@ The other way to factor out common logic is with a *rule*. Rules differ in that ```live:rules:module:openable package rules -app_to_hostnames[app_name] := hostnames { +app_to_hostnames[app_name] := hostnames if { app := apps[_] app_name := app.name hostnames := [hostname | name := app.servers[_] @@ -408,7 +408,6 @@ Depending on the use case and the integration with OPA that you are using, the s ```rego package example -import rego.v1 # entry point is 'deny' default deny := false @@ -422,7 +421,6 @@ If you assume all of the rules you write are correct, then you know that every r ```rego package example -import rego.v1 # entry point is 'allow' default allow := false @@ -436,7 +434,6 @@ If you assume your rules are correct, the only requests that are accepted are kn ```rego package example -import rego.v1 # entry point is 'authz' default authz := false diff --git a/docs/content/graphql-api-authorization.md b/docs/content/graphql-api-authorization.md index 9009601a80..737fc4efb8 100644 --- a/docs/content/graphql-api-authorization.md +++ b/docs/content/graphql-api-authorization.md @@ -75,8 +75,6 @@ The policy below does all of the above in parts: ```live:example:module:openable package graphqlapi.authz -import rego.v1 - subordinates := {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]} query_ast := graphql.parse(input.query, input.schema)[0] # If validation fails, the rules depending on this will be undefined. @@ -308,8 +306,6 @@ Let's extend the policy to handle this. ```live:hr_example:module:read_only,openable package graphqlapi.authz -import rego.v1 - # Allow HR members to get anyone's salary. allowed_query(q) if { selected_salary(q) @@ -352,8 +348,6 @@ To get a sense of one way the subordinate and HR data might be communicated in t ```live:jwt_example:module:hidden package graphqlapi.authz -import rego.v1 - query_ast := graphql.parse(input.query, input.schema)[0] # If validation fails, the rules depending on this will be undefined. # Helper rules. diff --git a/docs/content/http-api-authorization.md b/docs/content/http-api-authorization.md index 680301c195..807c38477f 100644 --- a/docs/content/http-api-authorization.md +++ b/docs/content/http-api-authorization.md @@ -39,8 +39,6 @@ cd bundles ```live:example:module:openable package httpapi.authz -import rego.v1 - # bob is alice's manager, and betty is charlie's. subordinates := {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]} @@ -217,8 +215,6 @@ this. ```live:hr_example:module:read_only,openable package httpapi.authz -import rego.v1 - # Allow HR members to get anyone's salary. allow if { input.method == "GET" @@ -264,8 +260,6 @@ real world, let's try a similar exercise utilizing the JWT utilities of OPA. ```live:jwt_example:module:openable package httpapi.authz -import rego.v1 - default allow := false # Allow users to get their own salaries. diff --git a/docs/content/images/opa-v0-upgrade.png b/docs/content/images/opa-v0-upgrade.png new file mode 100644 index 0000000000..b1789c4f7a Binary files /dev/null and b/docs/content/images/opa-v0-upgrade.png differ diff --git a/docs/content/integration.md b/docs/content/integration.md index 813b28cdfb..e2c72f8b0d 100644 --- a/docs/content/integration.md +++ b/docs/content/integration.md @@ -22,20 +22,19 @@ This page focuses predominantly on different ways to integrate with OPA's policy - See the [Health API](../rest-api#health-api) for checking agent deployment readiness and health. - See the [Prometheus API endpoint](../monitoring/#prometheus) to obtain insight into performance and errors. - ## Evaluating Policies OPA supports different ways to evaluate policies. * The [REST API](../rest-api) returns decisions as JSON over HTTP. * Also see the [Language SDKs](/ecosystem/#languages) for working with the REST API in different languages. -* The [Go API (GoDoc)](https://pkg.go.dev/github.com/open-policy-agent/opa/rego) returns +* The [Go API (GoDoc)](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego) returns decisions as simple Go types (`bool`, `string`, `map[string]interface{}`, etc.) * [WebAssembly](../wasm) compiles Rego policies into Wasm instructions so they can be embedded and evaluated by any WebAssembly runtime * Custom compilers and evaluators may be written to parse evaluation plans in the low-level [Intermediate Representation](../ir) format, which can be emitted by the `opa build` command -* The [SDK](https://pkg.go.dev/github.com/open-policy-agent/opa/sdk) provides high-level APIs for obtaining the output +* The [SDK](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/sdk) provides high-level APIs for obtaining the output of query evaluation as simple Go types (`bool`, `string`, `map[string]interface{}`, etc.) ### Integrating with the REST API @@ -71,8 +70,6 @@ decisions: `example/authz/allow` and `example/authz/is_admin`. ```live:authz:module:openable,read_only package example.authz -import rego.v1 - default allow := false allow if { @@ -207,8 +204,8 @@ import ( "context" "fmt" - "github.com/open-policy-agent/opa/sdk" - sdktest "github.com/open-policy-agent/opa/sdk/test" + "github.com/open-policy-agent/opa/v1/sdk" + sdktest "github.com/open-policy-agent/opa/v1/sdk/test" ) func main() { @@ -219,8 +216,6 @@ func main() { "example.rego": ` package authz - import rego.v1 - default allow := false allow if input.open == "sesame" @@ -278,10 +273,16 @@ Setting an `ID` in `sdk.Options` is optional, but recommended. If you do not set for the system. While this is fine for testing, it makes it difficult to monitor the system over time, as a new ID will be created each time the SDK is initialized, such as when the process is restarted. +{{< info >}} +This section documents the v1 SDK package. +Please see [v0 Backwards Compatibility](../v0-compatibility) for notes on using +the v0 SDK package. +{{< /info >}} + ### Integrating with the Go API Use the low-level -[github.com/open-policy-agent/opa/rego](https://pkg.go.dev/github.com/open-policy-agent/opa/rego) +[github.com/open-policy-agent/opa/v1/rego](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego) package to embed OPA as a library inside services written in Go, when only policy **evaluation** — and no other capabilities of OPA, like the management features — are desired. If you're unsure which one to use, the SDK is probably the better option. @@ -289,7 +290,7 @@ use, the SDK is probably the better option. To get started import the `rego` package: ```go -import "github.com/open-policy-agent/opa/rego" +import "github.com/open-policy-agent/opa/v1/rego" ``` The `rego` package exposes different options for customizing how policies are @@ -314,8 +315,6 @@ store, etc. module := ` package example.authz -import rego.v1 - default allow := false allow if { @@ -391,9 +390,15 @@ if !results.Allowed() { ``` For more examples of embedding OPA as a library see the -[`rego`](https://pkg.go.dev/github.com/open-policy-agent/opa/rego#pkg-examples) +[`rego`](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego#pkg-examples) package in the Go documentation. +{{< info >}} +This section documents the v1 Rego package. +Please see [v0 Backwards Compatibility](../v0-compatibility) for notes on using +the v0 Rego package. +{{< /info >}} + #### Ecosystem Projects The Go API is made available to allow other projects to build policy functionality into their diff --git a/docs/content/kafka-authorization.md b/docs/content/kafka-authorization.md index 2fb3a746ef..654be636c2 100644 --- a/docs/content/kafka-authorization.md +++ b/docs/content/kafka-authorization.md @@ -244,8 +244,6 @@ Update the `policies/tutorial.rego` with the following content. #----------------------------------------------------------------------------- package kafka.authz -import rego.v1 - default allow := false allow if { diff --git a/docs/content/kubernetes-introduction.md b/docs/content/kubernetes-introduction.md index 96e45d1afe..74771e011f 100644 --- a/docs/content/kubernetes-introduction.md +++ b/docs/content/kubernetes-introduction.md @@ -75,8 +75,6 @@ referring to illegal registries: ```live:container_image:module:openable package kubernetes.admission -import rego.v1 - deny contains reason if { some container input_containers[container] diff --git a/docs/content/kubernetes-primer.md b/docs/content/kubernetes-primer.md index 36e9e1249b..3804149270 100644 --- a/docs/content/kubernetes-primer.md +++ b/docs/content/kubernetes-primer.md @@ -17,8 +17,6 @@ trusted registry. ```live:container_images:module:openable package kubernetes.admission # line 1 -import rego.v1 - deny contains msg if { # line 2 input.request.kind.kind == "Pod" # line 3 image := input.request.object.spec.containers[_].image # line 4 @@ -205,8 +203,6 @@ When you write policies, you should use the OPA unit-test framework *before* sen ```live:container_images/test:module:read_only,openable package kubernetes.test_admission # line 1 -import rego.v1 - import data.kubernetes.admission # line 2 test_image_safety if { # line 3 @@ -285,8 +281,6 @@ To avoid conflicting ingresses, you write a policy like the one that follows. ```live:ingress_conflicts:module:read_only package kubernetes.admission -import rego.v1 - deny contains msg if { some namespace, name input.request.kind.kind == "Ingress" # line 1 @@ -492,8 +486,6 @@ have been loaded into OPA and unions the results: ```live:admission_main:module:read_only package system -import rego.v1 - import data.kubernetes.admission main := { diff --git a/docs/content/kubernetes-tutorial.md b/docs/content/kubernetes-tutorial.md index cfb3017690..e720e1320a 100644 --- a/docs/content/kubernetes-tutorial.md +++ b/docs/content/kubernetes-tutorial.md @@ -125,8 +125,6 @@ expressions will be allowed. ```live:ingress_allowlist:module:read_only package kubernetes.admission -import rego.v1 - import data.kubernetes.namespaces operations := {"CREATE", "UPDATE"} @@ -172,8 +170,6 @@ namespaces from sharing the same hostname. ```live:ingress_conflicts:module:read_only package kubernetes.admission -import rego.v1 - import data.kubernetes.ingresses deny contains msg if { @@ -198,8 +194,6 @@ Let's define a main policy that imports the [Restrict Hostnames](#policy-1-restr ```live:main:module:read_only package system -import rego.v1 - import data.kubernetes.admission main := { diff --git a/docs/content/management-decision-logs.md b/docs/content/management-decision-logs.md index 3deda27c5f..6dee5a948b 100644 --- a/docs/content/management-decision-logs.md +++ b/docs/content/management-decision-logs.md @@ -149,8 +149,6 @@ resources, supply the following policy to OPA: ```ruby package system.log -import rego.v1 - mask contains "/input/password" if { # OPA provides the entire decision log event as input to the masking policy. # Refer to the original input document under input.input. @@ -214,8 +212,6 @@ operations ```ruby package system.log -import rego.v1 - mask contains {"op": "upsert", "path": "/input/password", "value": "**REDACTED**"} if { # conditionally upsert password if it existed in the original event input.input.password @@ -228,8 +224,6 @@ the following rule format can be used. ```ruby package system.log -import rego.v1 - # always upsert, no conditions in rule body mask contains {"op": "upsert", "path": "/input/password", "value": "**REDACTED**"} ``` @@ -268,8 +262,6 @@ This rule will drop all requests to the _allow_ rule in the _kafka_ package, tha ```live:drop_rule_example/kafka_allow_rule:module:read_only package system.log -import rego.v1 - drop if { input.path == "kafka/allow" input.result == true @@ -282,8 +274,6 @@ Log only requests for _delete_ and _alter_ operations ```live:drop_rule_example/log_only_delete_alter_operations:module:read_only package system.log -import rego.v1 - drop if { input.path == "kafka/allow" not input.input.action.operation in {"DELETE", "ALTER"} diff --git a/docs/content/opa-1.md b/docs/content/opa-1.md deleted file mode 100644 index 7296f33663..0000000000 --- a/docs/content/opa-1.md +++ /dev/null @@ -1,260 +0,0 @@ ---- -title: OPA 1.0 -kind: documentation -weight: 120 ---- - -OPA v1.0 will introduce breaking changes to the Rego language and OPA runtime. -Here, these changes are described, along with the presentation of tools to make new and existing policies compatible with OPA 1.0. -The changes to the Rego language are backwards compatible in the sense that it is possible to author policies that are compatible with both OPA v1.0 and older versions of OPA. - -{{< info >}} -The full set of breaking changes in OPA v1.0 is not yet finalized. -The tools mentioned in the below sections - such as the `rego.v1` import and the `--rego-v1` command flag - may change over time, so it's good practice to regularly employ them to ensure your policies are compatible with the future release of OPA v1.0. -{{< /info >}} - -## What's changing in OPA 1.0 - -### The `future.keywords` imports - -The `in`, `every`, `if` and `contains` keywords have been introduced over time, and currently require opt-in to prevent them from breaking policies that existed before their introduction. -The `future.keywords` imports facilitate this opt-in mechanism. -These keywords help to increase the readability of policies and provide syntactic sugar for commonly used operations such as iteration, membership checks, defining multi-value rules, and so on. -There is growing adoption of these keywords and their usage is prevalent in the OPA documentation, Rego Playground, etc. - -In OPA v1.0 the `in`, `every`, `if` and `contains` keywords will be part of the language by default and the `future.keywords` imports will become a no-op. -A policy that makes use of these keywords, but doesn't import `future.keywords` is valid in OPA v1.0 but not in older versions of OPA. - -#### Making new and existing policies compatible with OPA v1.0 - -1. A new [rego.v1](../policy-language/#the-regov1-import) import has been introduced that, when used, makes OPA apply all restrictions that will eventually be enforced by default in OPA v1.0. - If a Rego module imports `rego.v1`, it means applicable `future.keywords` imports are implied. It is illegal to import both `rego.v1` and `future.keywords` in the same module. -2. The `--rego-v1` flag on the `opa fmt` command will rewrite existing modules to use the `rego.v1` import instead of `future.keywords` imports. -3. The `--rego-v1` flag on the `opa check` command will check that either the `rego.v1` import or applicable `future.keywords` imports are present if any of the `in`, `every`, `if` and `contains` keywords are used in a module. - -#### Backwards compatibility in OPA v1.0 - -To avoid breaking existing policies and give users a chance to update them such that these future keywords don’t clash with, for example, existing variable names, a new flag called `--v0-compatible` will be added to OPA commands such as `opa run` to maintain backward compatibility. -OPA’s Go SDK and Go API will be equipped with similar functionality, as well. - -Starting from OPA `v0.64.0`, the bundle manifest includes rego-version information that indicates whether contained modules are compatible with OPA v0.x or OPA v1.0. -This is useful for cases where users have limited control over the OPA deployment and hence cannot set the CLI `--v1-compatible`/`--v0-compatible` flags on the OPA runtime consuming the bundle. - -### Enforce use of `if` and `contains` keywords in rule head declarations - -Currently, there is semantic ambiguity between rules like `a.b {true}` and `a.b.c {true}`. Although syntactically similar, the former generates a set with the entry `b` at path `data.a`, while the latter generates an object with the attribute `"c": true` at path `data.a.b`. -This inconsistency makes it difficult for new users to understand how Rego works. -The `if` keyword is more than just syntactic sugar. When used in a rule head, that rule doesn't contribute to a partial set unless the `contains` keyword is also used. E.g. `a.b if {true}` will generate an object with the attribute `"b": true` at path `data.a`. -To make things simpler in the future, OPA v1.0 will require the usage of `if` and `contains` keywords when declaring rules. This would mean: - -* All rules are single-value by default. When the value is omitted from the head, it defaults to `true`. -* To make rules multi-value (i.e. partial set rules), use the `contains` keyword to convert the value into a set. - -The `contains` keyword is required to disambiguate rules that generate a single value from rules that generate multiple values. -The `if` keyword ensures that the semantics of rules does not change between today and v1.0. The table below illustrates why `if` is required. - -| rule | output today | output in v1.0 | -|-------------------|--------------------------|--------------------------| -| p { true } | {"p": true} | compile error | -| p.a { true } | {"p": {"a"}} | compile error | -| p.a.b { true } | {"p": {"a": {"b": true}} | compile error | -| p if { true } | {"p": true} | {"p": true} | -| p.a if { true } | {"p":{"a": true}} | {"p":{"a": true}} | -| p.a.b if { true } | {"p": {"a": {"b": true}} | {"p": {"a": {"b": true}} | -| p contains “a” | {"p": {"a"}} | {"p": {"a"}} | - -If the Rego language was changed so that all rules were single-value by default, unless the `contains` keyword was used to make them multi-value, then the outcome of a rule like `p.a { true }` would change between today and v1.0 without generating an error. -Generating errors in this case is preferable to changing the semantics of existing rules. Whereby, use of the `if` keyword will be a requirement in OPA v1.0, as this is also backwards compatible with older versions of OPA. - -In OPA v1.0, the `if` keyword is only required for rules with a declared body. Constants, rules that only consist of a value assignment, do not require `if`. -The following forms therefore remain valid in OPA v1.0: - -| rule | output today | output in v1.0 | -|------------|------------------------|------------------------| -| p := 1 | {“p”: 1} | {“p”: 1} | -| p.a := 1 | {“p”: {“a”: 1}} | {“p”: {“a”: 1}} | -| p.a.b := 1 | {“p”: {“a”: {“b”: 1}}} | {“p”: {“a”: {“b”: 1}}} | - -Because the `if` keyword can only be used in front of a rule body, rules with no body and no value assignment, i.e. a solitary reference, will not be allowed in the v1.0 Rego syntax: - -| rule | output today | output in v1.0 | -|-------|---------------------------|----------------| -| p | compile error | compile error | -| p.a | {“p”: {“a”}} | compile error | -| p.a.b | {“p”: {“a”: {“b”: true}}} | compile error | - -The below table gives examples of currently valid Rego syntax that will be invalid in OPA v1.0, along with the equivalent valid syntax in OPA v1.0: - -| invalid in v1.0 | v1.0 equivalent | Note | -|-----------------|----------------------------|-------------------------| -| p { true } | p if { true } | Single-value rule | -| p.a | p contains "a" | Multi-value insertion | -| p.a { true } | p contains "a" if { true } | Multi-value rule | -| p.a.b | p.a.b := true | Single-value assignment | -| p.a.b { true } | p.a.b if { true } | Single-value rule | - -Following is an example of how to define a rule that generates a set: - -```rego -package play - -import rego.v1 # Implies future.keywords.if and future.keywords.contains - -a contains b if { b := 1 } -``` - -When the above rule is evaluated the output is (sets are serialized into arrays in JSON): - -```json -{ - "a": [1] -} -``` - -Following is an example of how to define a rule that generates an object: - -```rego -package play - -import rego.v1 # Implies future.keywords.if and future.keywords.contains - -a[b] if { b := 1} -``` - -When the above rule is evaluated the output is: - -```json -{ - "a": { - "1": true - } -} -``` - -The requirement of `if` and `contains` keywords will remove the ambiguity between single-value and multi-value rule declaration. -This will make Rego code easier to author and read; thereby making it simpler for users to author their policies. - -#### Making new and existing policies compatible with OPA v1.0 - -1. A new [rego.v1](../policy-language/#the-regov1-import) import has been introduced that, when used, makes OPA apply all restrictions that will eventually be enforced by default in OPA v1.0. - If a Rego module imports `rego.v1`, it means the `if` and `contains` keywords are required when declaring rules. Constants, rules that only consist of a value assignment, are exempted. -2. The `--rego-v1` flag on the `opa fmt` command will rewrite existing modules to use the `if` and `contains` keywords where applicable. -3. The `--rego-v1` flag on the `opa check` command will check that the `if` and `contains` keywords are used where applicable in a module. - -#### Backwards compatibility in OPA v1.0 - -By default, OPA v1.0 will require the usage of `if` and `contains` as described above, but to maintain backward compatibility for policies written pre-OPA v1.0, a new flag called `--v0-compatible` will be added to OPA commands such as `opa run`, to avoid breaking existing policies and give users time to update them. -Similar functionality will be added to OPA’s Go SDK and Go API to assist users that embed OPA as a library in their Go services and also to OPA’s build command. - -Starting from OPA `v0.64.0`, the bundle manifest includes rego-version information that indicates whether contained modules are compatible with OPA v0.x or OPA v1.0. -This is useful for cases where users have limited control over the OPA deployment and hence cannot set the CLI `--v1-compatible`/`--v0-compatible` flags on the OPA runtime consuming the bundle. - -### Prohibit duplicate imports - -The Rego compiler supports [strict mode](../policy-language/#strict-mode) which enforces additional constraints and safety checks during compilation. -One of these checks is to prohibit duplicate imports where one import shadows another. OPA v1.0 will enforce this check by default. - -An import shadowing another is most likely an authoring error and probably unintentional. OPA checking this by default will help to avoid policy evaluations resulting in error-prone decisions. - -#### Making new and existing policies compatible with OPA v1.0 - -1. A new [rego.v1](../policy-language/#the-regov1-import) import has been introduced that, when used, makes OPA apply all restrictions that will eventually be enforced by default in OPA v1.0. - If a Rego module imports `rego.v1`, duplicate imports are prohibited. -2. The `--rego-v1` flag on the `opa fmt` command will reject modules with duplicate imports. -3. The `--rego-v1` flag on the `opa check` command will check that duplicate imports are not present in a module. - -#### Backwards compatibility in OPA v1.0 - -If pre-OPA v1.0 behavior is desired where this check is only enforced when `strict mode` is enabled, a new `--v0-compatible` flag will be added to the OPA CLI to achieve that. -Similar functionality will be added to OPA’s Go SDK, Go API and build command. - -Starting from OPA `v0.64.0`, the bundle manifest includes rego-version information that indicates whether contained modules are compatible with OPA v0.x or OPA v1.0. -This is useful for cases where users have limited control over the OPA deployment and hence cannot set the CLI `--v1-compatible`/`--v0-compatible` flags on the OPA runtime consuming the bundle. - -### `input` and `data` keywords are reserved - -The Rego compiler supports [strict mode](../policy-language/#strict-mode), which enforces additional constraints and safety checks during compilation. -One of these checks is to ensure that `input` and `data` are reserved keywords and may not be used as names for rules and variable assignments. - -The `input` document holds the user-provided input, while the data pushed into OPA and rule evaluation results are nested under the `data` document. -Hence, if a rule or variable shadows `input` or `data` you have the unintended consequence of erasing information under these, resulting in incorrect policy decisions. In OPA v1.0 such a scenario will be avoided by default. - -Note, using the [with](../policy-language/#with-keyword) keyword to insert values into - or to fully replace - the `input` or `data` documents, as in `my_func(x) with input as {...}` does not constitute shadowing and is therefore allowed in OPA v1.0. - -#### Making new and existing policies compatible with OPA v1.0 - -1. A new [rego.v1](../policy-language/#the-regov1-import) import has been introduced that, when used, makes OPA apply all restrictions that will eventually be enforced by default in OPA v1.0. - If a Rego module imports `rego.v1`, it means `input` and `data` are reserved keywords and may not be used as names for rules and variable assignments. -2. The `--rego-v1` flag on the `opa fmt` command will reject modules where `input` and `data` are used as names for rules and local variable assignments. - In a future release, a `--refactor-local-variables` flag will be added to `opa fmt` to refactor local variable assignments. -3. The `--rego-v1` flag on the `opa check` command will check that `input` and `data` are not used as names for rules and local variable assignments in a module. - -#### Backwards compatibility in OPA v1.0 - -OPA v1.0 will enforce this check by default. If pre-OPA v1.0 behavior is desired where this check is only enforced when `strict mode` is enabled, a new flag `--v0-compatible` will be added to the OPA CLI to achieve that. -Similar functionality will be added to OPA’s Go SDK, Go API and build command. - -Starting from OPA `v0.64.0`, the bundle manifest includes rego-version information that indicates whether contained modules are compatible with OPA v0.x or OPA v1.0. -This is useful for cases where users have limited control over the OPA deployment and hence cannot set the CLI `--v1-compatible`/`--v0-compatible` flags on the OPA runtime consuming the bundle. - -### Prohibit use of deprecated builtins - -The Rego compiler supports [strict mode](../policy-language/#strict-mode), which enforces additional constraints and safety checks during compilation. -One of these checks is to prohibit use of deprecated built-in functions. In OPA v1.0, these built-ins will be removed. - -The following built-in functions are deprecated: `any`, `all`, `re_match`, `net.cidr_overlap`, `set_diff`, `cast_array`, `cast_set`, `cast_string`, `cast_boolean`, `cast_null`, `cast_object`. -In some cases, new built-in functions have been added that provide functionality at least similar to a deprecated built-in. - -#### Making new and existing policies compatible with OPA v1.0 - -1. A new [rego.v1](../policy-language/#the-regov1-import) import has been introduced that, when used, makes OPA apply all restrictions that will eventually be enforced by default in OPA v1.0. - If a Rego module imports `rego.v1`, it means deprecated built-in functions are prohibited. -2. The `--rego-v1` flag on the `opa fmt` command will reject modules with calls to deprecated built-in functions. - In a future release, `opa fmt --rego-v1` will also rewrite modules to use an alternative, existing built-in function when possible and/or provide suggestions on how to author policies that avoid usage of deprecated built-ins. - An FAQ section will be published that explains why each built-in function is deprecated and why it should be removed or replaced in the policy. -3. The `--rego-v1` flag on the `opa check` command will check that deprecated built-in functions are not used in a module. - -#### Backwards compatibility in OPA v1.0 - -If pre-OPA v1.0 behavior is desired, where this check is only enforced when `strict` mode is enabled, a new flag `--v0-compatible` will be added to the OPA CLI to achieve that. -Similar functionality will be added to OPA’s Go SDK, Go API and build command. - -Starting from OPA `v0.64.0`, the bundle manifest includes rego-version information that indicates whether contained modules are compatible with OPA v0.x or OPA v1.0. -This is useful for cases where users have limited control over the OPA deployment and hence cannot set the CLI `--v1-compatible`/`--v0-compatible` flags on the OPA runtime consuming the bundle. - -### Binding the OPA server to the `localhost` interface by default - -By default, OPA binds to the `0.0.0.0` interface, which allows the OPA server to be exposed to services running outside of the local machine. -Though not inherently insecure in a trusted environment, it's good practice to bind OPA to the `localhost` interface by default if OPA is not intended to be exposed to remote services. - -In OPA v1.0, the OPA server will bind to the `localhost` interface by default. - -The `--v1-compatible` flag on the `opa run` command makes OPA employ configuration defaults that will eventually be used in OPA v1.0. -Binding to the `localhost` interface is one such default. - -## Running OPA in 1.0 compatibility mode - -OPA can be run in 1.0 compatibility mode by using the `--v1-compatible` flag. When this mode is enabled, the current release of OPA will behave as OPA v1.0 will eventually behave by default when it comes to the changes described [here](#whats-changing-in-opa-10). - -The `--v1-compatible` flag is currently supported on the following commands: - -* `bench`: requires modules to be compatible with OPA v1.0 syntax. -* `build`: requires modules to be compatible with OPA v1.0 syntax. -* `deps`: requires modules to be compatible with OPA v1.0 syntax. -* `check`*: requires modules to be compatible with OPA v1.0 syntax. -* `eval`: requires modules to be compatible with OPA v1.0 syntax. -* `exec`: requires modules to be compatible with OPA v1.0 syntax. -* `fmt`*: formats modules to be compatible with OPA v1.0 syntax, but not the current 0.x syntax. -* `inspect`: requires modules to be compatible with OPA v1.0 syntax. -* `parse`: requires modules to be compatible with OPA v1.0 syntax. -* `run`: requires modules (including discovery bundle) to be compatible with OPA v1.0 syntax. Binds server listeners to the `localhost` interface by default. -* `test`: requires modules to be compatible with OPA v1.0 syntax. - -Note (*): the `check` and `fmt` commands also support the `--rego-v1` flag, which will check/format Rego modules as if compatible with the Rego syntax of _both_ the current 0.x OPA version and OPA v1.0. -If both flags are used at the same time, `--rego-v1` takes precedence over `--v1-compatible`. - -## Rego-versioned bundles - -A bundle built with OPA `v0.64.0` or later, contains rego-version information in its [manifest](../management-bundles/#bundle-file-format), which the OPA consuming that bundle will use when processing the contained modules. -A bundle's internal rego-version takes precedence over the presence of the `--v1-compatible` flag; therefore, prerequisite knowledge about what Rego syntax any consumed bundle contains is not needed. -The `--v1-compatible` flag (and `--v0-compatible` in v1.0) on the `opa build` command allows the user to control the rego-version of the built bundle. diff --git a/docs/content/policy-language.md b/docs/content/policy-language.md index d1fb9a1f68..c08c31ff99 100644 --- a/docs/content/policy-language.md +++ b/docs/content/policy-language.md @@ -7,7 +7,6 @@ toc: true ```live:eg:module:hidden package example -import rego.v1 ``` OPA is purpose built for reasoning about information represented in structured @@ -772,19 +771,6 @@ document that is defined by the rule. The sample code in this section make use of the data defined in [Examples](#example-data). -{{< info >}} -Rule definitions can be more expressive when using the _future keywords_ `contains` and -`if` which will become standard in [OPA v1.0](../opa-1). - -To follow along as-is, please import the keywords, or preferably, import `rego.v1`: - -```live:eg/data/info:module:read_only -import rego.v1 -``` - -[See the docs on _future keywords_](#future-keywords) for more information. -{{< /info >}} - ### Generating Sets The following rule defines a set containing the hostnames of all servers: @@ -1018,8 +1004,6 @@ Module: ```live:general_ref_head:module package example -import rego.v1 - # A partial object rule that converts a list of users to a mapping by "role" and then "id". users_by_role[role][id] := user if { some user in input.users @@ -1052,8 +1036,6 @@ The first variable declared in a rule head's reference divides the reference in ```live:general_ref_head_conflict:module package example -import rego.v1 - # R1 p[x].r := y if { x := "q" @@ -1077,8 +1059,6 @@ Conflicts are detected at compile-time, where possible, between rules even if th ```live:general_ref_head_conflict2:module package example -import rego.v1 - # R1 p[x].r := y if { x := "foo" @@ -1104,8 +1084,6 @@ Rules are not allowed to overlap with object values of other rules. ```live:general_ref_head_conflict3:module package example -import rego.v1 - # R1 p.q.r := {"s": 1} @@ -1126,8 +1104,6 @@ We won't get a conflict if we update the policy to the following: ```live:general_ref_head_conflict4:module package example -import rego.v1 - # R1 p.q.r.s := 1 @@ -1583,7 +1559,8 @@ For more details see the language [Grammar](../policy-reference/#grammar). ### Imports -Import statements declare dependencies that modules have on documents defined outside the package. By importing a document, the identifiers exported by that document can be referenced within the current module. +Import statements declare dependencies that modules have on documents defined outside the package. By importing a +document, the identifiers exported by that document can be referenced within the current module. All modules contain implicit statements which import the `data` and `input` documents. @@ -1591,7 +1568,6 @@ Modules use the same syntax to declare dependencies on [Base and Virtual Documen ```live:import_data:module:read_only package opa.examples -import rego.v1 # uses 'in' and 'contains' and 'if' import data.servers @@ -1605,7 +1581,6 @@ Similarly, modules can declare dependencies on query arguments by specifying an ```live:import_input:module:read_only package opa.examples -import rego.v1 import input.user import input.method @@ -1637,7 +1612,6 @@ Imports can include an optional `as` keyword to handle namespacing issues: ```live:import_namespacing:module:read_only package opa.examples -import rego.v1 import data.servers as my_servers @@ -1647,36 +1621,7 @@ http_servers contains server if { } ``` -## Future Keywords - -To ensure backwards-compatibility, new keywords (like `every`) were introduced slowly. -In the first stage, users could opt-in to using the new keywords via a special import: - -* `import future.keywords` introduces _all_ future keywords, and -* `import future.keywords.x` _only_ introduces the `x` keyword -- see below for all known future keywords. -* **Recommended** `import rego.v1` introduces all future keywords, and enforces the use of `if` and `contains` in rule heads where applicable. - -{{< info >}} -It is recommended to use the `rego.v1` import instead of `future.keywords` imports, -as this will ensure that your policy is compatible with the future release of [OPA v1.0](../opa-1). -If the `rego.v1` import is present in a module, then `future.keywords` and -`future.keywords.*` import is implied, and not allowed. -{{< /info >}} - -In [OPA v1.0](../opa-1), the new keywords will become _standard_, and -the import will become a no-op that can safely be removed. This should give all -users ample time to update their policies, so that the new keyword will not cause -clashes with existing variable names. - -{{< info >}} -Note that some future keyword imports have consequences on pretty-printing: -If `contains` or `if` are imported, the pretty-printer will use them as applicable -when formatting the modules. -{{< /info >}} - -This is the list of all future keywords that will become standard in OPA v1.0: - -### `future.keywords.in` +## In Keyword More expressive membership and existential quantification keyword: @@ -1690,29 +1635,9 @@ deny { } ``` -`in` was introduced in [v0.34.0](https://github.com/open-policy-agent/opa/releases/tag/v0.34.0). See [the keywords docs](#membership-and-iteration-in) for details. -### `future.keywords.every` - -Expressive _universal quantification_ keyword: - -```live:eg/kws/every:module:read_only -allowed := {"customer", "admin"} - -allow { - every role in input.roles { - role.name in allowed - } -} -``` - -There is no need to also import `future.keywords.in`, that is **implied** by importing `future.keywords.every`. - -`every` was introduced in [v0.38.0](https://github.com/open-policy-agent/opa/releases/tag/v0.38.0). -See [Every Keyword](#every-keyword) for details. - -### `future.keywords.if` +## If Keyword This keyword allows more expressive rule heads: @@ -1720,9 +1645,7 @@ This keyword allows more expressive rule heads: deny if input.token != "secret" ``` -`if` was introduced in [v0.42.0](https://github.com/open-policy-agent/opa/releases/tag/v0.42.0). - -### `future.keywords.contains` +## Contains Keyword This keyword allows more expressive rule heads for partial set rules: @@ -1730,8 +1653,6 @@ This keyword allows more expressive rule heads for partial set rules: deny contains msg { msg := "forbidden" } ``` -`contains` was introduced in [v0.42.0](https://github.com/open-policy-agent/opa/releases/tag/v0.42.0). - ## Some Keyword The `some` keyword allows queries to explicitly declare local variables. Use the @@ -1787,14 +1708,6 @@ For using the `some` keyword with iteration, see ## Every Keyword -{{< info >}} -`every` is a future keyword and needs to be imported. - -`import rego.v1` or, alternatively, `import future.keywords.every` introduces the `every` keyword described here. - -[See the docs on _future keywords_](#future-keywords) for more information. -{{< /info >}} - ```live:eg/data/every0:module:merge_down names_with_dev if { some site in sites @@ -2184,14 +2097,6 @@ limit imposed on the number of `else` clauses on a rule. ### Membership and iteration: `in` -{{< info >}} -To ensure backwards-compatibility, new keywords (like `in`) are introduced slowly. -In the first stage, users can opt-in to using the new keywords via a special import: -`import rego.v1` or, alternatively, `import future.keywords.in` introduces the `in` keyword described here. - -[See the docs on _future keywords_](#future-keywords) for more information. -{{< /info >}} - The membership operator `in` lets you check if an element is part of a collection (array, set, or object). It always evaluates to `true` or `false`: ```live:eg/member1:module:merge_down @@ -2958,8 +2863,6 @@ The following policy ```live:example/metadata/1:module package example -import rego.v1 - # METADATA # title: Deny invalid numbers # description: Numbers may not be higher than 5 @@ -3139,8 +3042,6 @@ starts with a specific prefix. ``` package kubernetes.admission -import rego.v1 - deny contains msg if { input.request.kind.kinds == "Pod" image := input.request.object.spec.containers[_].image @@ -3251,8 +3152,6 @@ Consider the following Rego code which checks if an operation is allowed by a us ``` package policy -import rego.v1 - import data.acl default allow := false @@ -3371,8 +3270,6 @@ within the package: # - data.acl: schema["acl-schema"] package example -import rego.v1 - allow if { access := data.acl["alice"] access[_] == input.operation @@ -3411,8 +3308,6 @@ Consider the following example: ``` package kubernetes.admission -import rego.v1 - # METADATA # scope: rule # schemas: @@ -3473,8 +3368,6 @@ It is sometimes useful to have different input schemas for different rules in th ``` package policy -import rego.v1 - import data.acl default allow := false @@ -3533,8 +3426,6 @@ Specifically, `anyOf` acts as an Rego Or type where at least one (can be more th ``` package kubernetes.admission -import rego.v1 - # METADATA # scope: rule # schemas: @@ -3616,8 +3507,6 @@ Specifically, `allOf` keyword implies that all conditions under `allOf` within a ``` package kubernetes.admission -import rego.v1 - # METADATA # scope: rule # schemas: @@ -3775,9 +3664,6 @@ For a tool that generates JSON Schema from JSON samples, please see: }} -If the `rego.v1` import is present in a module, all strict mode checks documented above except the unused local assignment and unused imports checks are enforced on the module. - -Additionally the `rego.v1` import also requires the usage of `if` and `contains` keywords when declaring certain rules. The `if` keyword is required before a rule body and the `contains` keyword is required for partial set rules. -{{< /info >}} - -## The `rego.v1` Import - -In the future, when [OPA v1.0](../opa-1) is released, breaking changes will be introduced to the Rego language. -The `rego.v1` import is a way to opt-in to these breaking changes early, and ensure that your policies are compatible with OPA v1.0. -If a module containing this import is not compatible with OPA v1.0, it will cause a compilation error. - -When a module imports `rego.v1`, the following features and constraints are implied: - -* all [Future keywords](#future-keywords) are implied and can be used without import. - These imports are mutually exclusive, and it will cause a compilation error to import both `rego.v1` and `future.keywords` in the same module. -* the [if](#futurekeywordsif) keyword is required before a rule body declaration. -* the [contains](#futurekeywordscontains) keyword is required for partial set (multi-value) rules. -* most [Strict mode](#strict-mode) constraints and checks are implied and enforced. See the strict mode [constraints and checks table](#strict-mode-constraints-and-checks) for details. - -The `rego.v1` import only affects the module where it's declared. It does not affect any other modules, even if they are importing, or is imported by, a module where `rego.v1` is declared. - -In OPA v1.0, the `rego.v1` import will have no semantic impact on the policy, as all its implied features and constraints will be enforced by default. It will however still be a valid statement, and won't cause any compilation errors. - -Example policy that imports `rego.v1` to be compatible with the future syntax in OPA v1.0: - -```live:rego_v1:module:read_only -package example - -import rego.v1 - -l := [1, 2, 3] - -default allow := false - -# 'if' is part of default v1.0 syntax, and doesn't need import. -# 'if' is required before rule body. -allow if { - count(violations) == 0 -} - -# 'contains' is part of default v1.0 syntax, and doesn't need import. -# 'contains' is required to declare multi-value, partial set rule. -violations contains msg if { - # 'every' and 'in' are part of default v1.0 syntax, and doesn't need imports. - every x in l { - x > 0 - } - msg := "no negative entries" -} -``` +Name | Description +--- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Unused local assignments | Unused arguments or [assignments](../policy-reference/#assignment-and-equality) local to a rule, function or comprehension are prohibited +Unused imports | Unused [imports](../policy-language/#imports) are prohibited. ## Ecosystem Projects diff --git a/docs/content/policy-performance.md b/docs/content/policy-performance.md index 4b170ef24f..52c3a2fa16 100644 --- a/docs/content/policy-performance.md +++ b/docs/content/policy-performance.md @@ -20,8 +20,6 @@ For example, the following rule has one local variable `user`, and that variable ```live:linear:module:read_only,openable package linear -import rego.v1 - allow if { some user input.method == "GET" @@ -73,8 +71,6 @@ Here is an example policy from the [rule-indexing blog](https://blog.openpolicya ```live:indexed:module:openable package indexed -import rego.v1 - default allow := false allow if { @@ -153,8 +149,6 @@ The most common case for this are a set of `allow` rules: ```live:ee:module:read_only package earlyexit -import rego.v1 - allow if { input.user == "alice" } @@ -175,8 +169,6 @@ Intuitively, the value can be anything that does not contain a variable: ```live:eeexamples:module:read_only package earlyexit.examples -import rego.v1 - # p, q, r and s could be evaluated with early-exit semantics: p if { @@ -220,8 +212,6 @@ When "early exit" is possible for a (set of) rules, iterations inside that rule ```live:eeiteration:module:read_only package earlyexit.iteration -import rego.v1 - p if { some p data.projects[p] == "project-a" @@ -239,8 +229,6 @@ early; an evaluation with `{"user": "bob", "group": "admins"}` *would not*: ```live:eeindex:module:read_only package earlyexit -import rego.v1 - allow if { input.user == "alice" } @@ -325,7 +313,6 @@ To implement the policy above we could write: ```rego package example -import rego.v1 deny contains msg if { some i @@ -363,7 +350,6 @@ The following examples shows rules that are **not** indexed: ```rego package example -import rego.v1 not_indexed_because_missing_assignment if { x := input[_] @@ -438,7 +424,6 @@ let's take the following policy: ```rego package test -import rego.v1 p if { a := 1 @@ -488,7 +473,6 @@ sample policy. ```live:profile:module:read_only,openable package rbac -import rego.v1 # Example input request @@ -776,8 +760,6 @@ Adding a unit test file for the [policy source as shown above](#example-policy): ```rego package rbac -import rego.v1 - test_user_has_role_dev if { user_has_role.dev with input as {"subject": "alice"} } diff --git a/docs/content/policy-reference.md b/docs/content/policy-reference.md index c34238f7e0..4619c97e81 100644 --- a/docs/content/policy-reference.md +++ b/docs/content/policy-reference.md @@ -39,7 +39,7 @@ val := arr[0] # lookup last value val := arr[count(arr)-1] -# with `import rego.v1` or `import future.keywords.in` +# with keywords some 0, val in arr # lookup value at index 0 0, "foo" in arr # check if value at index 0 is "foo" some i, "foo" in arr # find all indices i that have value "foo" @@ -71,7 +71,7 @@ obj.foo.bar.baz # check if path foo.bar.baz, foo.bar, or foo does not exist or is false not obj.foo.bar.baz -# with `import rego.v1` or `import future.keywords.in` +# with keywords o := {"foo": false} # check if value exists: the expression will be true false in o @@ -94,7 +94,7 @@ a_set[["a", "b", "c"]] # find all arrays of the form [x, "b", z] in the set a_set[[x, "b", z]] -# with `import rego.v1` or `import future.keywords.in` +# with keywords "foo" in a_set not "foo" in a_set some ["a", "b", "c"] in a_set @@ -120,7 +120,7 @@ val := arr[_] # iterate over index/value pairs val := arr[i] -# with `import rego.v1` or `import future.keywords.in` +# with keywords some val in arr # iterate over values some i, _ in arr # iterate over indices some i, val in arr # iterate over index/value pairs @@ -138,7 +138,7 @@ val := obj[_] # iterate over key/value pairs val := obj[key] -# with `import rego.v1` or `import future.keywords.in` +# with keywords some val in obj # iterate over values some key, _ in obj # iterate over keys some key, val in obj # key/value pairs @@ -150,7 +150,7 @@ some key, val in obj # key/value pairs # iterate over values set[val] -# with `import rego.v1` or `import future.keywords.in` +# with keywords some val in set ``` @@ -187,7 +187,7 @@ not any_not_match ``` ```live:iteration/forall:module:read_only -# with `import rego.v1`, or `import future.keywords.in` and `import future.keywords.if` +# with keywords any_match if { some x in set f(x) @@ -223,7 +223,7 @@ c := a | b p := true { ... } # OR -# with `import rego.v1` or `import future.keywords.if` +# with keywords p if { ... } # OR @@ -233,7 +233,7 @@ p { ... } ### Conditionals ```live:rules/cond:module:read_only -# with `import rego.v1` or `import future.keywords.if` +# with keywords default a := 1 a := 5 if { ... } a := 100 if { ... } @@ -246,7 +246,7 @@ a := 100 if { ... } a_set[x] { ... } a_set[y] { ... } -# alternatively, with `import rego.v1`, or `import future.keywords.contains` and `import future.keywords.if` +# alternatively, with keywords a_set contains x if { ... } a_set contains y if { ... } @@ -258,7 +258,7 @@ a_map[w] := z if { ... } ### Ordered (Else) ```live:rules/ordered:module:read_only -# with `import rego.v1` or `import future.keywords.if` +# with keywords default a := 1 a := 5 if { ... } else := 10 if { ... } @@ -267,7 +267,7 @@ else := 10 if { ... } ### Functions (Boolean) ```live:rules/funcs:module:read_only -# with `import rego.v1` or `import future.keywords.if` +# with keywords f(x, y) if { ... } @@ -282,7 +282,7 @@ f(x, y) := true if { ### Functions (Conditionals) ```live:rules/condfuncs:module:read_only -# with `import rego.v1` or `import future.keywords.if` +# with keywords f(x) := "A" if { x >= 90 } f(x) := "B" if { x >= 80; x < 90 } f(x) := "C" if { x >= 70; x < 80 } @@ -291,7 +291,7 @@ f(x) := "C" if { x >= 70; x < 80 } ### Reference Heads ```live:rules/ref_heads:module:read_only -# with `import rego.v1`, or `import future.keywords.contains` and `import future.keywords.if` +# with keywords fruit.apple.seeds = 12 if input == "apple" # complete document (single value rule) fruit.pineapple.colors contains x if x := "yellow" # multi-value rule @@ -780,21 +780,21 @@ shows you how to "flatten" a hierarchy of access permissions. package graph_reachable_example org_chart_data := { - "ceo": {}, - "human_resources": {"owner": "ceo", "access": ["salaries", "complaints"]}, - "staffing": {"owner": "human_resources", "access": ["interviews"]}, - "internships": {"owner": "staffing", "access": ["blog"]} + "ceo": {}, + "human_resources": {"owner": "ceo", "access": ["salaries", "complaints"]}, + "staffing": {"owner": "human_resources", "access": ["interviews"]}, + "internships": {"owner": "staffing", "access": ["blog"]}, } -org_chart_graph[entity_name] := edges { - org_chart_data[entity_name] - edges := {neighbor | org_chart_data[neighbor].owner == entity_name} +org_chart_graph[entity_name] := edges if { + org_chart_data[entity_name] + edges := {neighbor | org_chart_data[neighbor].owner == entity_name} } -org_chart_permissions[entity_name] := access { - org_chart_data[entity_name] - reachable := graph.reachable(org_chart_graph, {entity_name}) - access := {item | reachable[k]; item := org_chart_data[k].access[_]} +org_chart_permissions[entity_name] := access if { + org_chart_data[entity_name] + reachable := graph.reachable(org_chart_graph, {entity_name}) + access := {item | reachable[k]; item := org_chart_data[k].access[_]} } ``` ```live:graph/reachable/example:query @@ -809,15 +809,15 @@ It may be useful to find all reachable paths from a root element. `graph.reachab package graph_reachable_paths_example path_data := { - "aTop": [], - "cMiddle": ["aTop"], - "bBottom": ["cMiddle"], - "dIgnored": [] + "aTop": [], + "cMiddle": ["aTop"], + "bBottom": ["cMiddle"], + "dIgnored": [], } -all_paths[root] := paths { - path_data[root] - paths := graph.reachable_paths(path_data, {root}) +all_paths[root] := paths if { + path_data[root] + paths := graph.reachable_paths(path_data, {root}) } ``` ```live:graph/reachable_paths/example:query @@ -1194,8 +1194,8 @@ package example # description: Numbers may not be higher than 5 # custom: # severity: MEDIUM -deny[format(rego.metadata.rule())] { - input.number > 5 +deny contains format(rego.metadata.rule()) if { + input.number > 5 } # METADATA @@ -1203,8 +1203,8 @@ deny[format(rego.metadata.rule())] { # description: Subject must have the 'admin' role # custom: # severity: HIGH -deny[format(rego.metadata.rule())] { - input.subject.role != "admin" +deny contains format(rego.metadata.rule()) if { + input.subject.role != "admin" } format(meta) := {"severity": meta.custom.severity, "reason": meta.description} @@ -1233,8 +1233,6 @@ OPA doesn't presume what merge strategy is appropriate; instead, this lies in th # - Acme Corp. package example -import rego.v1 - # METADATA # scope: document # description: A rule that merges metadata annotations in various ways. @@ -1400,8 +1398,6 @@ non-empty-set = "{" term { "," term } "}" empty-set = "set(" ")" ``` -Note that the grammar corresponds to Rego with `rego.v1` enabled (See ([OPA v1.0](../opa-1) for more info). - The grammar defined above makes use of the following syntax. See [the Wikipedia page on EBNF](https://en.wikipedia.org/wiki/Extended_Backus–Naur_Form) for more details: ``` diff --git a/docs/content/policy-testing.md b/docs/content/policy-testing.md index 8f57b3dcbd..9ff1ff61e6 100644 --- a/docs/content/policy-testing.md +++ b/docs/content/policy-testing.md @@ -33,8 +33,6 @@ profile. ```live:example:module:read_only,openable package authz -import rego.v1 - allow if { input.path == ["users"] input.method == "POST" @@ -53,8 +51,6 @@ To test this policy, we will create a separate Rego file that contains test case ```live:example/test:module:read_only package authz_test -import rego.v1 - import data.authz test_post_allowed if { @@ -129,8 +125,6 @@ Consider the following utility module: ```live:example_vars:module:read_only,openable package authz -import rego.v1 - allowed_actions(user) := [action | user in data.actions[action] ] @@ -142,7 +136,6 @@ with accompanying tests: package authz_test import data.authz -import rego.v1 test_allowed_actions_all_can_read if { users := ["alice", "bob", "jane"] @@ -190,8 +183,6 @@ name is prefixed with `test_`. It's a good practice for tests to be placed in a ```live:example_format:module:read_only package mypackage_test -import rego.v1 - import data.mypackage test_some_descriptive_name if { @@ -225,8 +216,6 @@ by zero condition) the test result is marked as an `ERROR`. Tests prefixed with ```live:example_results:module:read_only package example_test -import rego.v1 - import data.example # This test will pass. @@ -329,8 +318,6 @@ Below is a simple policy that depends on the data document. ```live:with_keyword:module:read_only,openable package authz -import rego.v1 - allow if { some x in data.policies x.name == "test_policy" @@ -347,8 +334,6 @@ Below is the Rego file to test the above policy. ```live:with_keyword/tests:module:read_only package authz_test -import rego.v1 - import data.authz policies := [{"name": "test_policy"}] @@ -377,8 +362,6 @@ Below is an example to replace a **rule without arguments**. ```live:with_keyword_rules:module:read_only package authz -import rego.v1 - allow1 if allow2 allow2 if 2 == 1 @@ -389,8 +372,6 @@ allow2 if 2 == 1 ```live:with_keyword_rules/tests:module:read_only package authz_test -import rego.v1 - import data.authz test_replace_rule if { @@ -412,8 +393,6 @@ Here is an example to replace a rule's **built-in function** with a user-defined ```live:with_keyword_builtins:module:read_only package authz -import rego.v1 - import data.jwks.cert allow if { @@ -426,8 +405,6 @@ allow if { ```live:with_keyword_builtins/tests:module:read_only package authz_test -import rego.v1 - import data.authz mock_decode_verify("my-jwt", _) := [true, {}, {}] @@ -469,8 +446,6 @@ function by a built-in function. ```live:with_keyword_funcs:module:read_only package authz -import rego.v1 - replace_rule if { replace(input.label) } @@ -485,8 +460,6 @@ replace(label) if { ```live:with_keyword_funcs/tests:module:read_only package authz_test -import rego.v1 - import data.authz test_replace_rule if { diff --git a/docs/content/rest-api.md b/docs/content/rest-api.md index a2767af467..ff45bce524 100644 --- a/docs/content/rest-api.md +++ b/docs/content/rest-api.md @@ -672,8 +672,6 @@ Content-Type: text/plain ```live:put_example:module:read_only package opa.examples -import rego.v1 - import data.networks import data.ports import data.servers @@ -894,8 +892,6 @@ The examples below assume the following policy: ```live:input_example:module:read_only package opa.examples -import rego.v1 - import input.example.flag allow_request if flag == true @@ -999,8 +995,6 @@ The examples below assume the following policy: ```live:webhook_example:module:read_only package opa.examples -import rego.v1 - import input.example.flag allow_request if flag == true @@ -1211,8 +1205,6 @@ Content-Type: text/plain ```live:system_example:module:read_only package system -import rego.v1 - main := msg if { msg := sprintf("hello, %v", [input.user]) } @@ -1362,8 +1354,6 @@ The example below assumes that OPA has been given the following policy: ```live:compile_example:module:read_only package example -import rego.v1 - allow if { input.subject.clearance_level >= data.reports[_].clearance_level } @@ -1459,8 +1449,6 @@ For example, if you extend to policy above to include a "break glass" condition, ```live:compile_unconditional_example:module:read_only package example -import rego.v1 - allow if { input.subject.clearance_level >= data.reports[_].clearance_level } @@ -1540,8 +1528,6 @@ exception: ```live:compile_unconditional_false_example:module:read_only package example -import rego.v1 - allow if { input.subject.clearance_level >= data.reports[_].clearance_level exceptions[input.subject.name] @@ -1676,8 +1662,6 @@ able to process the `live` rule. OPA is ready once all plugins have entered the ```live:health_policy_example:module:read_only package system.health -import rego.v1 - # opa is live if it can process this rule default live := true @@ -1696,8 +1680,6 @@ specific a plugin leaves the OK state, try this: ```live:health_policy_example_2:module:read_only package system.health -import rego.v1 - default live := true default ready := false diff --git a/docs/content/security.md b/docs/content/security.md index 3f9485a17d..9e32d92ac4 100644 --- a/docs/content/security.md +++ b/docs/content/security.md @@ -95,18 +95,13 @@ OPA can be configured to listen on specific interfaces using the `--addr` flag. ```bash opa run --server \ --log-level debug \ - --addr localhost:8181 \ + --addr 0.0.0.0:8181 \ ``` -By default, OPA binds to the 0.0.0.0 interface, which allows the OPA server to be exposed to services running outside of the same machine. It's important to note that binding OPA to the 0.0.0.0 interface by itself is not inherently insecure in a trusted environment, exposing OPA to the outside world would also require opening ports and likely a similar procedure on a gateway layer above. +By default, OPA binds to `localhost`, which prevents the OPA server from being exposed to services running outside of the same machine. In situations where OPA is not intended to be exposed to remote services, it is recommended to bind OPA to the localhost interface, which only allows connections from the same machine. If it is necessary to expose OPA to remote services, ensure to follow the security recommendations on this page, such as requiring authentication. -{{< info >}} -The `--v1-compatible` flag can be set on `opa run` to opt-in to OPA features and behaviors that will be enabled by default in a future OPA v1.0 releases. For example, if the `--v1-compatible` flag is set, OPA will listen -for HTTP connections on `localhost:8181` by default. -{{< /info >}} - ## Authentication and Authorization This section shows how to configure OPA to authenticate and authorize client @@ -159,8 +154,6 @@ must be provided on startup. The authorization policy must be structured as foll # system.authz as follows: package system.authz -import rego.v1 - default allow := false # Reject requests by default. allow if { @@ -267,8 +260,6 @@ identity: ```live:system_authz_secret:module:read_only package system.authz -import rego.v1 - default allow := false # Reject requests by default. allow if { # Allow request if... @@ -319,8 +310,6 @@ follows: ```live:system_authz_object_resp:module:read_only package system.authz -import rego.v1 - default allow := { "allowed": false, "reason": "unauthorized resource access", @@ -344,8 +333,6 @@ validate the identity: ```live:system_authz_bearer:module:read_only package system.authz -import rego.v1 - # Tokens may defined in policy or pushed into OPA as data. tokens := { "my-secret-token-foo": { @@ -376,8 +363,6 @@ documents: ```live:system_authz_bearer_complete:module:read_only package system.authz -import rego.v1 - # Rights may be defined in policy or pushed into OPA as data. rights := { "admin": { @@ -516,8 +501,6 @@ information such as which paths are allowed. ```live:system_authz_x509:module:read_only package system.authz -import rego.v1 - id_uri := input.client_certificates[0].URIs[0] id_string := sprintf("%s://%s%s", [id_uri.Scheme, id_uri.Host, id_uri.Path]) @@ -636,8 +619,6 @@ clients access to the default policy decision, i.e., `POST /`: ```live:hardened_example:module:read_only package system.authz -import rego.v1 - # Deny access by default. default allow := false diff --git a/docs/content/ssh-and-sudo-authorization.md b/docs/content/ssh-and-sudo-authorization.md index cabbb572e8..dcf030e0af 100644 --- a/docs/content/ssh-and-sudo-authorization.md +++ b/docs/content/ssh-and-sudo-authorization.md @@ -160,8 +160,6 @@ and non-admins to only SSH into hosts that they contributed code to. ```live:sshd_authz:module:read_only package sshd.authz -import rego.v1 - import input.pull_responses import input.sysinfo @@ -202,8 +200,6 @@ Create the `sudo` authorization policy. It should allow only admins to use `sudo ```live:sudo_authz:module:read_only package sudo.authz -import rego.v1 - # By default, users are not authorized. default allow := false @@ -375,8 +371,6 @@ Then we need to make sure that the authorization takes this input into account. # A package can be defined across multiple files. package sudo.authz -import rego.v1 - import data.elevate import input.display_responses import input.sysinfo diff --git a/docs/content/terraform.md b/docs/content/terraform.md index faa14c9916..036d9e00e6 100644 --- a/docs/content/terraform.md +++ b/docs/content/terraform.md @@ -470,8 +470,6 @@ practice you would vary the threshold depending on the user.) ```live:terraform:module:openable package terraform.analysis -import rego.v1 - import input as tfplan ######################## @@ -794,8 +792,6 @@ The policy uses the walk keyword to explore the json structure, and uses conditi ```rego package terraform.module -import rego.v1 - deny contains msg if { desc := resources[r].values.description contains(desc, "HTTP") diff --git a/docs/content/v0-compatibility.md b/docs/content/v0-compatibility.md new file mode 100644 index 0000000000..2f5770ca7e --- /dev/null +++ b/docs/content/v0-compatibility.md @@ -0,0 +1,189 @@ +--- +title: v0 Backwards Compatibility +kind: documentation +weight: 121 +--- + +## Running OPA in v0.x compatibility mode + +The v1.0 release of OPA comes with functionality to run in a backwards +compatible, v0.x mode. This is used by running OPA with the `--v0-compatible` +flag or using the v0.x compatible options in Go integrations. When enabled, OPA +instances and Go integrations will behave as they do in pre v1.0 releases. + +### When to use v0.x compatibility mode + +**Use of v0.x compatibility mode is not recommended for most users**. This +mode is intended to help users with large volumes of third party Rego stay up to +date while performing a longer term migration to a OPA v1.0 compatible OPA +feature set. Examples of when this applies: + +- You run a service for customers who supply their own Rego. +- You use OPA as part of a managed platform and need to run a mix of v0.x and + v1.0 OPAs based on customer demands. + +Users with control over their Rego and OPA deployments are instead encouraged +to migrate their Rego to be compatible with OPA v1.0 using the below tooling options: + +1. The `rego.v1` import makes OPA apply all restrictions that are enforced by default in OPA v1.0. + If a Rego module imports `rego.v1`, it means applicable `future.keywords` imports are implied. It is illegal to import both `rego.v1` and `future.keywords` in the same module. +2. The `--v0-v1` flag on the `opa fmt` command will rewrite existing modules to use the `rego.v1` import instead of `future.keywords` imports. +3. The `--v0-v1` flag on the `opa check` command will check that either the `rego.v1` import or applicable `future.keywords` imports are present if any of the `in`, `every`, `if` and `contains` keywords are used in a module. + +### v0.x compatibility mode in the OPA binary + +The `--v0-compatible` flag is supported on the following commands in OPA v1.0.x +releases: + +- `bench`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `build`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `deps`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `check`*: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `eval`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `exec`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `fmt`*: formats modules to be compatible with OPA v0.x syntax. See note about + `--v0-v1` flag below. +- `inspect`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `parse`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. +- `run`: supports modules (including discovery bundle) using Rego v0.x syntax, use of `import rego.v1` is optional. Binds server listeners to all interfaces by default, rather than localhost. +- `test`: supports Rego v0.x syntax modules, use of `import rego.v1` is optional. + +Note (*): the `check` and `fmt` commands also support the `--v0-v1` flag, +which will check/format Rego modules as if compatible with the Rego syntax of +_both_ the old 0.x OPA version and current OPA v1.0. + +Note (*): Pre v1.0 versions of OPA also support a comparable `--v1-compatible` +flag which can be used to produce and consume Rego v1 bundles. See +[Upgrading to v1.0](./v0-upgrade) for more information on how to use this flag +as part of an upgrade to OPA v1.0. + +### v0.x compatibility mode in Rego package + +There are three ways to enable v0.x compatibility mode in the [Rego package](https://pkg.go.dev/github.com/open-policy-agent/opa/rego): + +1. Set the Rego version on modules +2. Set the Rego version on bundle manifests +3. Use the SetRegoVersion Rego argument + +1 & 2 are preferred as they are more granular and make it easier to run a +mix of v0.x and v1.0 compatible Rego in the same OPA instance and thus better +support a gradual upgrade path. + +The `SetRegoVersion` method on [Module](https://pkg.go.dev/github.com/open-policy-agent/opa/ast#Module.SetRegoVersion?) +can be used like this: + +```go +m := ast.Module{ + Package: regoCode, +} + +m.SetRegoVersion(ast.RegoV0) +``` + +Similarly, the [Bundle Manifest](https://pkg.go.dev/github.com/open-policy-agent/opa/bundle#Manifest.SetRegoVersion) Rego version +can be set like this: + +```go +b := Bundle{ + // ... +} +b.SetRegoVersion(ast.RegoV0) +``` + +If you cannot set the Rego version on modules or bundle manifests, you +can use the [`SetRegoVersion`](https://pkg.go.dev/github.com/open-policy-agent/opa/rego#SetRegoVersion) Rego argument to control the Rego version used when +evaluating policies. + +Users are encouraged to use the more granular options where possible to better +allow them to upgrade Rego used in their system to Rego v1 gradually. + +In the example below, `SetRegoVersion` is used as a Rego argument instructing +the supplied Rego to be handled as v0.x syntax: + +```go +// Only to be used if the above are not suitable. +r := rego.New( + rego.Query("data.foo.bar"), + rego.Module("policy.rego", regoCode), + rego.SetRegoVersion(ast.RegoV1), // <--- +) +``` + +Finally, another option is to import the `v0` package instead. The program + +below imports the v0 package instead: + +```go +package main + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/open-policy-agent/opa/rego" + // rather than the v1 import, which is: + // "github.com/open-policy-agent/opa/v1/rego" +) + +func main() { + module := `package example +messages[msg] { + msg := "foo" +} +` + + r := v0rego.New( + rego.Query("data.example.messages"), + rego.Module("example.rego", module), + ) + + rs, _ = rv0.Eval(context.TODO()) + bs, _ = json.Marshal(rs) + + fmt.Println(string(bs)) +} +``` + +{{< danger >}} +**Note**: Using v0 packages and v1 packages in the same program is considered an +anti-pattern and is not recommended or supported. Any interoperability between +the two packages is not guaranteed and should be considered unsupported. +{{< /danger >}} + +### v0.x compatibility mode in the OPA Go SDK + +In OPA 1.0, the recommended + +[SDK package](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/sdk) +import for most users is `github.com/open-policy-agent/opa/v1/sdk`. + +Those who need to support v0 bundles should set the Rego version on bundle +manifests as outlined above wherever possible. For users unable to do this, use +of a v0 import of the SDK package is required. For example: + +```go +package main + +import ( + "bytes" + "context" + "fmt" + + "github.com/open-policy-agent/opa/sdk" // <-- import v0 sdk package +) + +func main() { + opa, _ := sdk.New(ctx, sdk.Options{ + ID: "opa-1", + Config: bytes.NewReader(config), + }) + + defer opa.Stop(ctx) + + // ... +} +``` + +Users in this scenario should look to version bundles as soon as possible to +allow them to use a v1 SDK instead. diff --git a/docs/content/v0-upgrade.md b/docs/content/v0-upgrade.md new file mode 100644 index 0000000000..44f98a2074 --- /dev/null +++ b/docs/content/v0-upgrade.md @@ -0,0 +1,511 @@ +--- +title: Upgrading to v1.0 +kind: documentation +weight: 122 +--- + +All users should plan to upgrade to OPA v1.0 eventually. Some users, with more +control over the Rego loaded into the OPA instances they run will be able to do +so more quickly. Other users with less control or running third party Rego may +wish to upgrade to OPA v1.0 and use the [v0 compatibility](./v0-compatibility) functionality to +upgrade gradually. + +This documentation covers the different upgrade scenarios and the best course of +action for each. The documentation makes use of the following concepts: + +- **Bundle Producer**: A system based on `opa build` that produces a bundle that + is loaded by consumers. +- **Bundle Consumer**: An OPA instance that loads and evaluates policy + from a bundle in the system. +- **Authoring**: The process of writing Rego policies before bundles are + produced and consumed. In managed systems, this might be a user's only contact + point with OPA. + +In some systems, where OPA is used without a bundle, there is no producer. This +simplifies the upgrade process. + +Users are encouraged to upgrade to OPA v1.0 as soon as possible. Using the v0 +compatible functionality until updating Rego is preferred to delaying the +upgrade. The first part of this guide refers to upgrading OPA instances used for +producing and consuming bundles. This is the first step users should take unless +their Rego is already v1.0 compatible. See [Upgrading Rego](#upgrading-rego) +below for information on how to upgrade Rego policies to be v1.0 compatible. + +## General Upgrade Approach: Upgrade Producers, then Consumers + +Users will need to upgrade to OPA v1.0 in their own way, depending on their +release and change management processes, use cases and risk tolerance. The +general advice is to upgrade producers first, then consumers. This is because +the updated producers would be able to set the Rego version on bundle manifests +and as a result it wouldn't be necessary to run consumers with the `--v0-compatible` flag. +Also since it's likely there are much more consumers than producers, upgrading producers +first would lead to a smoother upgrade process. + +Some users may wish to migrate to OPA v1.0 all at once, with adequate testing and validation +this is possible. Not all steps are necessary for all users so a hybrid approach +is also an option depending on your context. + +The rest of this documentation is designed to meet users where they find +themselves and direct them down the smoothest path to upgrade to OPA +v1.0. + +## Detailed Producer & Consumer Version Scenarios + +Tabulated in this section are the different versions of OPA users might be +working with in different parts of their systems. Select the scenario that best +matches your setup to find the recommended upgrade path. + +If you are in doubt, [Scenario 1](#scenario-1) is the most common starting +point and we recommended you start there. + +| | v0.x Consumer | Mix Consumer | v1.0 Consumer | +|-------------------|--------------------------------------|---------------------------|--------------------------------------| +| **v0.x Producer** | [Scenario 1](#scenario-1) (All v0.x) | [Scenario 4](#scenario-4) | [Scenario 7](#scenario-7) | +| **Mix Producer** | [Scenario 2](#scenario-2) | [Scenario 5](#scenario-5) | [Scenario 8](#scenario-8) | +| **v1.0 Producer** | [Scenario 3](#scenario-3) | [Scenario 6](#scenario-6) | [Scenario 9](#scenario-9) (All v1.0) | + + + +{{< figure src="opa-v0-upgrade.png" width="65" caption="Upgrade flows to OPA v1.0" >}} + +## Upgrade Scenarios + +Listed below are the different upgrade scenarios and the recommended migration +plans for each case. + +### Scenario 1: v0.x Producer, v0.x Consumer + +All OPA runtimes - both bundle consumers and producers - are v0.x. This is the +most common starting point for users upgrading from a v0.x version of OPA. + +#### How to Run + +- Policies are authored to be v0.x compatible. + +#### Next + +Start upgrading producers to v1.0 ([Scenario 2](#scenario-2)) until all producers +are v1.0 ([Scenario 3](#scenario-3)). + +### Scenario 2: Mix Producer, v0.x Consumer + +Some bundle producers are v1.0, while some remain on v0.x. All bundle consumers +are v0.x. This might be the case if you have bundles from different tenants or +users using different OPA versions and you cannot control the versions they use. + +#### Pre-requisites + +Users cannot proceed with upgrade until they have either a single version of bundle +producers or have a means to control the use of `--v0-compatible` on newer +producers. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x producers are run as-is. +- v1.0 producer is run with `--v0-compatible`, or modules have `rego.v1` import. +- v0.x consumers are run as-is. + +#### Next + +Continue migrating producers to v1.0 until all producers have been upgraded +([Scenario 3](#scenario-3)). + +### Scenario 3: v1.0 Producer, v0.x Consumer + +OPA bundles are produced by OPA v1.0 instances, consumers are still on v0.x. This +scenario is common as users upgrade to OPA v1.0 by upgrading their producers +first. + +#### Pre-requisites + +Control of producers to set `--v0-compatible` or use `rego.v1` imports is +required. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x consumers are run as-is. +- v1.0 producer is run with `--v0-compatible`, or modules have `rego.v1` import. +- Since policies will always be consumed by a v0.x OPA, all policies _must_ be v0.x compliant. + +#### Next + +Now that all producers are v1.0, and consumers are still not all v1.0, it's time +to get all the consumers to v1.0, ([Scenario 6](#scenario-6)). + +### Scenario 4: v0.x Producer, Mix Consumer + +Producers are v0.x, consumers are a mix of v0.x and v1.0. This scenario might occur +when users have partially upgraded OPA instances to v1.0, but have not yet +upgraded their consumers. This is not a recommended step if it can be avoided as +it's recommended to upgrade producers first. + +#### Pre-requisites + +OPA v1.0 consumers must be able to run with `--v0-compatible` to accept v0.x +bundles. This upgrade path cannot continue until this is possible. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x producers and v0.x consumers are run as is. + +### Next + +Upgrade producers to v1.0 and continue the upgrade from that point. Generally, it's recommended to +upgrade producers first, however depending on your existing OPAs v1.0 consumers deployments, you may prefer to +upgrade all your producers to v1.0 rather than to downgrade consumers. + +### Scenario 5: Mix Producer, Mix Consumer + +Mixed versions of OPA are being used for both bundle production and consumption. + +#### Pre-requisites + +As users have a mix of bundle producers, they must have control over the runtime +options for the producers to set `--v0-compatible`. Users must also have control +over their v1.0 consumers to set the `--v0-compatible` flag. Both these conditions +must be met for the upgrade to proceed. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v1.0 consumers are run with `--v0-compatible`. +- v1.0 producers are run with `--v0-compatible`. + +### Next + +Please gradually upgrade producers to v1.0 until all producers are v1.0 ([Scenario 6](#scenario-6)). + +### Scenario 6: v1.0 Producer, Mix Consumer + +All consumers can be run without flags, as the bundle will contain +attributes to inform v1.0 OPAs to accept v0.x modules. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x consumers are run as-is. Bundles will contain v0.x policies +- v1.0 consumers are run as-is. Bundles will contain `rego_version` attribute, so v0.x modules are accepted. + +#### Pre-requisites + +If users cannot set their OPA v1.0 producers to use `--v0-compatible` to be +compatible with their v0.x consumers, then this upgrade path is blocked. + +#### Next + +Running exclusively v1.0 producers and consumers, ([Scenario 9](#scenario-9)), is +the next and final step. + +### Scenario 7: v0.x Producer, v1.0 Consumer + +All consumers are v1.0, but producers are v0.x. This scenario might occur when +OPAs used for evaluation are upgraded before the policy bundling system. + +#### Pre-requisites + +If v1.0 consumers cannot be run with `--v0-compatible`, when loading v0.x consumer +generated bundle, the bundles cannot include `rego_version` attribute. This means +the upgrade path is blocked until either the consumers can create bundles with a +Rego version or the `--v0-compatible` flag is available for producers. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v1.0 consumers are run with `--v0-compatible` + +#### Next + +Upgrade producers to v1.0 ([Scenario 8](#scenario-8)) until all producers are v1.0 +([Scenario 9](#scenario-9)). + +### Scenario 8: Mix Producer, v1.0 Consumer + +All consumers are v1.0, but producers are a mix of v0.x and v1.0. + +#### How to Run + +- Policies are authored to be v0.x compatible. +- v0.x producers are run as is. +- v1.0 consumers are run with `--v0-compatible` +- v1.0 producers are run with `--v0-compatible` + +#### Pre-requisites + +If using v0.x bundles, it must be possible to use `--v0-compatible` on the bundle +producers in order for them to work in the v1.0 consumers. + +v1.0 consumers will accept v1.0 producer bundles, as these will have the Rego version specified in the manifest; +they won't however accept bundles from v0.x producers unless they have `--v0-compatible` set. + +#### Next + +Upgrade producers to v1.0 ([Scenario 9](#scenario-9)), completing the upgrade. + +### Scenario 9: v1.0 Producer, v1.0 Consumer + +Once you have all consumers and producers running at v1.0 then you have +completed the upgrade to OPA v1.0. If you are using `--v0-compatible` +functionality, the next task is to upgrade the Rego loaded into OPAs to Rego v1. + +Regardless of whether you are now upgrading your Rego, we encourage users to +use `opa check`, `opa check --strict` and to lint their Rego projects if you +have not already done so to identify issues. + +## Changes to Rego in OPA v1.0 + +Once you have upgraded OPA instance to v1.0, or if you are upgrading all at +once, you will need to upgrade your Rego policies to Rego v1.0. This section +outlines the changes in Rego v1.0. + +### The `future.keywords` imports + +The `in`, `every`, `if` and `contains` keywords have been introduced over time, +and Rego v0.x required an opt-in to prevent them from breaking policies that +existed before their introduction. The `future.keywords` imports facilitate this +opt-in mechanism. These keywords help to increase the readability of policies +and provide syntactic sugar for commonly used operations such as iteration, +membership checks, defining multi-value rules, and so on. There is growing +adoption of these keywords and their usage is prevalent in the OPA +documentation, Rego Playground, etc. + +In OPA v1.0 the `in`, `every`, `if` and `contains` keywords are part of the +language by default and the `future.keywords` imports will become a no-op. A +policy that makes use of these keywords, but doesn't import `future.keywords` is +valid in OPA v1.0 but not in older versions of OPA. + +### Enforce use of `if` and `contains` keywords in rule head declarations + +In Rego v0.x, there is semantic ambiguity between rules like `a.b {true}` and +`a.b.c {true}`. Although syntactically similar, the former generates a set with +the entry `b` at path `data.a`, while the latter generates an object with the +attribute `"c": true` at path `data.a.b`. This inconsistency makes it difficult +for new users to understand how Rego works. The `if` keyword is more than just +syntactic sugar. When used in a rule head, that rule doesn't contribute to a +partial set unless the `contains` keyword is also used. E.g. `a.b if {true}` +will generate an object with the attribute `"b": true` at path `data.a`. To make +things simpler, OPA v1.0 requires the usage of `if` and `contains` keywords when +declaring rules. This would mean: + +- All rules are single-value by default. When the value is omitted from the + head, it defaults to `true`. +- To make rules multi-value (i.e. partial set rules), use the `contains` keyword + to convert the value into a set. + +The `contains` keyword is required to disambiguate rules that generate a single +value from rules that generate multiple values. The `if` keyword ensures that +the semantics of rules do not change between v0.x and v1.0 Rego. The table below +illustrates why `if` is required. + +| rule | output in v0.x | output in v1.0 | +| ----------------- | ---------------------- | ------------------------ | +| p { true } | {"p": true} | compile error | +| p.a { true } | {"p": {"a"}} | compile error | +| p.a.b { true } | {"p": {"a": {"b": true}} | compile error | +| p if { true } | {"p": true} | {"p": true} | +| p.a if { true } | {"p":{"a": true}} | {"p":{"a": true}} | +| p.a.b if { true } | {"p": {"a": {"b": true}} | {"p": {"a": {"b": true}} | +| p contains “a” | {"p": {"a"}} | {"p": {"a"}} | + +If the Rego language was changed so that all rules were single-value by default, +unless the `contains` keyword was used to make them multi-value, then the +outcome of a rule like `p.a { true }` would change between v0.x and v1.0 +without generating an error. Generating errors in this case is preferable to +changing the semantics of existing rules. Therefore, use of the `if` keyword is +a requirement in OPA v1.0. + +In OPA v1.0, the `if` keyword is only required for rules with a declared body. +Constants, rules that only consist of a value assignment, do not require `if`. +The following forms therefore remain valid in OPA v1.0: + +| rule | output in v0.x | output in v1.0 | +| ---------- | -------------------- | ---------------------- | +| p := 1 | {“p”: 1} | {“p”: 1} | +| p.a := 1 | {“p”: {“a”: 1}} | {“p”: {“a”: 1}} | +| p.a.b := 1 | {“p”: {“a”: {“b”: 1}}} | {“p”: {“a”: {“b”: 1}}} | + +Since the `if` keyword can only be used in front of a rule body, rules with no +body and no value assignment, i.e. a solitary reference, are not allowed in +the v1.0 Rego syntax: + +| rule | output in v0.x | output in v1.0 | +| ----- | ----------------------- | -------------- | +| p | compile error | compile error | +| p.a | {“p”: {“a”}} | compile error | +| p.a.b | {“p”: {“a”: {“b”: true}}} | compile error | + +The below table gives examples of v0.x valid Rego syntax which are +invalid in OPA v1.0, along with the equivalent valid syntax in OPA v1.0: + +| invalid in v1.0 | v1.0 equivalent | Note | +| --------------- | -------------------------- | ----------------------- | +| p { true } | p if { true } | Single-value rule | +| p.a | p contains "a" | Multi-value insertion | +| p.a { true } | p contains "a" if { true } | Multi-value rule | +| p.a.b | p.a.b := true | Single-value assignment | +| p.a.b { true } | p.a.b if { true } | Single-value rule | + +Following is an example of how to define a rule that generates a set: + +```rego +package play + +a contains b if { b := 1 } +``` + +When the above rule is evaluated the output is (sets are serialized into arrays +in JSON): + +```json +{ + "a": [1] +} +``` + +Following is an example of how to define a rule that generates an object: + +```rego +package play + +a[b] if { b := 1} +``` + +When the above rule is evaluated the output is: + +```json +{ + "a": { + "1": true + } +} +``` + +The requirement of `if` and `contains` keywords remove the ambiguity +between single-value and multi-value rule declaration. This makes Rego code +easier to author and read; thereby making it simpler for users to author their +policies. + +### Prohibit duplicate imports + +As part of `strict` mode in OPA 0.x, the Rego compiler prohibits duplicate imports where one import shadows another. +OPA v1.0 enforces this check by default. + +An import shadowing another is most likely an authoring error and probably +unintentional. OPA checking this by default will help to avoid policy +evaluations resulting in error-prone decisions. + +### `input` and `data` keywords are reserved + +The Rego compiler ensures that `input` and `data` are reserved keywords and may +not be used as names for rules and variable assignments. This is part of`strict` mode in OPA 0.x + +The `input` document holds the user-provided input, while the data pushed into +OPA and rule evaluation results are nested under the `data` document. Hence, if +a rule or variable shadows `input` or `data` you have the unintended consequence +of erasing information under these inside the local scope, resulting in incorrect policy decisions. In +OPA v1.0 such scenarios are avoided by default. + +Note, using the [with](../policy-language/#with-keyword) keyword to insert +values into - or to fully replace - the `input` or `data` documents, as in +`my_func(x) with input as {...}` does not constitute shadowing and is therefore +allowed in OPA v1.0. + +### Prohibit use of deprecated builtins + +As part of `strict` mode in OPA 0.x, the Rego compiler prohibits use of deprecated built-in functions. In OPA v1.0, +these built-ins have been removed. + +The following built-in functions are deprecated: `any`, `all`, `re_match`, +`net.cidr_overlap`, `set_diff`, `cast_array`, `cast_set`, `cast_string`, +`cast_boolean`, `cast_null`, `cast_object`. In some cases, new built-in +functions have been added that provide functionality at least similar to a +deprecated built-in. + +### Rego-versioned bundles + +A bundle built with OPA `v0.64.0` or later, contain a `rego_version` attribute +in their [manifest](../management-bundles/#bundle-file-format), which the OPA +consuming that bundle will use when processing the contained modules. A bundle's +internal rego-version takes precedence over the presence of the +`--v1-compatible` flag; therefore, prerequisite knowledge about what Rego syntax +any consumed bundle contains is not needed. The `--v1-compatible` flag (and +`--v0-compatible` in v1.0) on the `opa build` command allows the user to control +the `rego-version` of the built bundle. + +See [Upgrading to v1.0](./v0-upgrade) for more information on how to use +versioned bundles as part of an upgrade to OPA v1.0. + +## Compilation Constraints and Checks + +Below constraints and safety checks are enforced by default in v1.0 during compilation. These checks along with the ones in [v1.0 strict mode](../policy-language/#strict-mode) +were part of the compiler `strict` mode in OPA 0.x. + +Name | Description +--- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +Duplicate imports | Duplicate [imports](../policy-language/#imports), where one import shadows another, are prohibited. +`input` and `data` reserved keywords | `input` and `data` are reserved keywords, and may not be used as names for rules and variable assignment. +Use of deprecated built-ins | Use of deprecated functions is prohibited, and these will be removed in OPA 1.0. Deprecated built-in functions: `any`, `all`, `re_match`, `net.cidr_overlap`, `set_diff`, `cast_array`, `cast_set`, `cast_string`, `cast_boolean`, `cast_null`, `cast_object` + +## Upgrading Rego + +Users with v0.x Rego projects are encouraged to follow the below process to +upgrade their Rego code to conform to best practices, and to be compatible with +OPA v1.0. These steps are largely based on the process outlined in this +[detailed blog post](https://www.styra.com/blog/renovating-rego/). + +Before starting the upgrade, users are recommended to ensure they have a local +OPA binary of version 1.0 or later. + +1. `opa check --v0-v1`, this will catch any parse or compilation errors. +2. `opa check --v0-v1 --strict`, this will raise a number of other issues found in code + that might make it incompatible with OPA v1.0 such as the use of deprecated + built-ins or duplicate imports. +3. Automatically reformat your code for OPA v1.0 with `opa fmt --write --v0-v1`. +4. `regal lint`, the [Regal linter](/integrations/regal/) has many more rules to + test for issues in Rego code that can lead to errors, poor performance or + unexpected behaviour. + +If you run into any issues while upgrading a Rego project, please drop a message +in the #help channel on the [OPA Slack](https://slack.openpolicyagent.org/). + +## Upgrading for Go Integrations + +Both users of the +[v0 SDK](https://pkg.go.dev/github.com/open-policy-agent/opa/sdk) +and +[v0 Rego](https://pkg.go.dev/github.com/open-policy-agent/opa/rego) packages are +encoraged to upgrade to the new v1 packages instead. These can be found here: + +- [SDK v1](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/sdk) +- [Rego v1](https://pkg.go.dev/github.com/open-policy-agent/opa/v1/rego) + +In order to upgrade to a v1 package, you need to make the following change: + +Before: + +``` +import ( + "github.com/open-policy-agent/opa/rego" +) +``` + +After: + +``` +import ( + "github.com/open-policy-agent/opa/v1/rego" +) +``` + +This will be needed for all OPA packages your application depends on, not just +`rego` and `sdk`, other commonly used packages are: `ast`, `bundle`, `compile`, +`types` & `topdown`. + +As of OPA 1.0, all v0 packages have been deprecated. While they will remain for +the lifetime of OPA 1.0, you are encouraged to upgrade as soon as possible. + +If you need to use v0 functionality, you can still use v1 packages. Please see +the [Backwards Compatibility](./v0-compatibility/) documentation for more +details. diff --git a/docs/website/scripts/live-blocks/src/preprocess/localEval.js b/docs/website/scripts/live-blocks/src/preprocess/localEval.js index 1f5fed04fb..0a5609de67 100644 --- a/docs/website/scripts/live-blocks/src/preprocess/localEval.js +++ b/docs/website/scripts/live-blocks/src/preprocess/localEval.js @@ -69,7 +69,7 @@ export default async function localEval(groups, groupName, opaVersion) { // Returns 1st a function that consumes a string for the output format you want and produces an array of arguments and 2nd a map of module file names to strings that should be replaced in error messages. May throw a user-friendly error. async function prepEval(groups, groupName, opaVersion) { const {module, package: pkg, query, input, included} = getGroupData(groups, groupName) - const base = ['eval', '--fail', '--v0-compatible'] // Fail on undefined + const base = ['eval', '--fail', '--v1-compatible'] // Fail on undefined const rest = [] const moduleFilenameMap = {} diff --git a/docs/website/scripts/live-blocks/src/web/playground.js b/docs/website/scripts/live-blocks/src/web/playground.js index 5a8801c870..b86855d0dc 100644 --- a/docs/website/scripts/live-blocks/src/web/playground.js +++ b/docs/website/scripts/live-blocks/src/web/playground.js @@ -66,6 +66,7 @@ function createDataRequestOpts(groups, groupName) { try { const body = {} // In the format accepted by playground backend body.rego_modules = {[EVAL_MODULE_NAME]: module, ...included} + body.rego_version = 1 body.query_package = pkg if (query) { body.rego = query @@ -73,6 +74,7 @@ function createDataRequestOpts(groups, groupName) { if (input) { body.input = input } + body.query_imports = ["rego.v1"] return { body: JSON.stringify(body), diff --git a/v1/runtime/runtime.go b/v1/runtime/runtime.go index eb234f7e76..1d54f0b6d9 100644 --- a/v1/runtime/runtime.go +++ b/v1/runtime/runtime.go @@ -525,7 +525,7 @@ func (rt *Runtime) Serve(ctx context.Context) error { } serverInitializingMessage := "Initializing server." - if !rt.Params.AddrSetByUser && (rt.Params.V0Compatible || !rt.Params.V1Compatible) { + if !rt.Params.AddrSetByUser && rt.Params.V0Compatible { serverInitializingMessage += " OPA is running on a public (0.0.0.0) network interface. Unless you intend to expose OPA outside of the host, binding to the localhost interface (--addr localhost:8181) is recommended. See https://www.openpolicyagent.org/docs/latest/security/#interface-binding" } diff --git a/v1/runtime/runtime_test.go b/v1/runtime/runtime_test.go index 1823bff461..2f0a408444 100644 --- a/v1/runtime/runtime_test.go +++ b/v1/runtime/runtime_test.go @@ -1547,12 +1547,12 @@ func TestAddrWarningMessage(t *testing.T) { name string addrSetByUser bool containsMsg bool - v1Compatible bool + v0Compatible bool }{ - {"WarningMessage", false, true, false}, {"NoWarningMessage", true, false, false}, - {"V1Compatible", false, false, true}, - {"V1InCompatible", false, true, false}, + {"WarningMessage", false, true, true}, + {"V0Compatible", false, true, true}, + {"V0InCompatible", false, false, false}, } for _, tc := range testCases { @@ -1569,7 +1569,7 @@ func TestAddrWarningMessage(t *testing.T) { params.Addrs = &[]string{"localhost:8181"} params.AddrSetByUser = tc.addrSetByUser params.GracefulShutdownPeriod = 1 - params.V1Compatible = tc.v1Compatible + params.V0Compatible = tc.v0Compatible rt, err := NewRuntime(ctx, params) if err != nil { t.Fatalf("Unexpected error %v", err)