diff --git a/cmd/run.go b/cmd/run.go index e1833a9668..b89da21f21 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -21,8 +21,9 @@ import ( ) const ( - defaultAddr = ":8181" // default listening address for server - defaultHistoryFile = ".opa_history" // default filename for shell history + defaultAddr = ":8181" // default listening address for server + defaultLocalAddr = "localhost:8181" // default listening address for server bound to localhost + defaultHistoryFile = ".opa_history" // default filename for shell history ) type runCmdParams struct { @@ -176,6 +177,10 @@ updates might cause them to be dropped by OPA. OPA will automatically perform type checking based on a schema inferred from known input documents and report any errors resulting from the schema check. Currently this check is performed on OPA's Authorization Policy Input document and will be expanded in the future. To disable this, use the --skip-known-schema-check flag. + +The --future-compat flag can be used to opt-in to OPA features and behaviors that will be enabled by default in future OPA releases. +Current behaviors enabled by this flag include: +- setting OPA's listening address to "localhost:8181" by default. `, Run: func(cmd *cobra.Command, args []string) { @@ -200,6 +205,7 @@ be expanded in the future. To disable this, use the --skip-known-schema-check fl runCommand.Flags().BoolVar(&cmdParams.rt.H2CEnabled, "h2c", false, "enable H2C for HTTP listeners") runCommand.Flags().StringVarP(&cmdParams.rt.OutputFormat, "format", "f", "pretty", "set shell output format, i.e, pretty, json") runCommand.Flags().BoolVarP(&cmdParams.rt.Watch, "watch", "w", false, "watch command line files for changes") + runCommand.Flags().BoolVar(&cmdParams.rt.FutureCompatibility, "future-compat", false, "opt-in to OPA features and behaviors that will be enabled by default in future OPA releases") addMaxErrorsFlag(runCommand.Flags(), &cmdParams.rt.ErrorLimit) runCommand.Flags().BoolVar(&cmdParams.rt.PprofEnabled, "pprof", false, "enables pprof endpoints") runCommand.Flags().StringVar(&cmdParams.tlsCertFile, "tls-cert-file", "", "set path of TLS certificate file") @@ -333,6 +339,10 @@ func initRuntime(ctx context.Context, params runCmdParams, args []string, addrSe rt.SetDistributedTracingLogging() rt.Params.AddrSetByUser = addrSetByUser + if !addrSetByUser && rt.Params.FutureCompatibility { + rt.Params.Addrs = &[]string{defaultLocalAddr} + } + return rt, nil } diff --git a/cmd/run_test.go b/cmd/run_test.go index 37461fb706..cb89ef9a40 100644 --- a/cmd/run_test.go +++ b/cmd/run_test.go @@ -50,6 +50,48 @@ func TestRunServerBase(t *testing.T) { <-done } +func TestRunServerBaseListenOnLocalhost(t *testing.T) { + params := newTestRunParams() + params.rt.FutureCompatibility = true + + ctx, cancel := context.WithCancel(context.Background()) + + rt, err := initRuntime(ctx, params, nil, false) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + testRuntime := e2e.WrapRuntime(ctx, cancel, rt) + + done := make(chan bool) + go func() { + err := rt.Serve(ctx) + if err != nil { + t.Errorf("Unexpected error: %s", err) + } + done <- true + }() + + err = testRuntime.WaitForServer() + if err != nil { + t.Fatalf("Unexpected error: %s", err) + } + + validateBasicServe(t, testRuntime) + + if len(rt.Addrs()) != 1 { + t.Fatalf("Expected 1 listening address but got %v", len(rt.Addrs())) + } + + expected := "127.0.0.1:8181" + if rt.Addrs()[0] != expected { + t.Fatalf("Expected listening address %v but got %v", expected, rt.Addrs()[0]) + } + + cancel() + <-done +} + func TestRunServerWithDiagnosticAddr(t *testing.T) { params := newTestRunParams() params.rt.DiagnosticAddrs = &[]string{"localhost:0"} diff --git a/docs/content/_index.md b/docs/content/_index.md index 362d7c5bef..4893491188 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -1022,7 +1022,8 @@ You can start OPA as a server with `-s` or `--server`: By default OPA listens for HTTP connections on `0.0.0.0:8181`. See `opa run --help` for a list of options to change the listening address, enable TLS, and -more. +more. For example, if the `--future-compat` flag is set, OPA will listen +for HTTP connections on `localhost:8181` by default. 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/deployments.md b/docs/content/deployments.md index 347e456b9c..19fb1b745b 100644 --- a/docs/content/deployments.md +++ b/docs/content/deployments.md @@ -33,6 +33,7 @@ command line arguments for OPA's server mode are: * `--addr` to set the listening address (default: `0.0.0.0:8181`). * `--log-level` (or `-l`) to set the log level (default: `"info"`). * `--log-format` to set the log format (default: `"json"`). +* `--future-compat` to opt-in to OPA features and behaviors that will be enabled by default in future OPA releases. 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 OPA listen for HTTPS connections, see [Security](../security). diff --git a/docs/content/security.md b/docs/content/security.md index b5759bfc6b..20805da297 100644 --- a/docs/content/security.md +++ b/docs/content/security.md @@ -102,6 +102,10 @@ By default, OPA binds to the 0.0.0.0 interface, which allows the OPA server to b 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 `--future-compat` flag can be set on `opa run` to opt-in to OPA features and behaviors that will be enabled by default in future OPA releases. For example, if the `--future-compat` flag is set, OPA will listen +for HTTP connections on `localhost:8181` by default. +{{< /info >}} ## Authentication and Authorization diff --git a/runtime/runtime.go b/runtime/runtime.go index 1935834e1e..f4e3afc49c 100644 --- a/runtime/runtime.go +++ b/runtime/runtime.go @@ -224,6 +224,11 @@ type Params struct { // UnixSocketPerm specifies the permission for the Unix domain socket if used to listen for connections UnixSocketPerm *string + + // FutureCompatibility will enable OPA features and behaviors that will be enabled by default in future OPA releases. + // This flag allows users to opt-in to the new behavior and helps transition to a future release upon which + // the new behavior will be enabled by default. + FutureCompatibility bool } // LoggingConfig stores the configuration for OPA's logging behaviour. @@ -472,7 +477,7 @@ func (rt *Runtime) Serve(ctx context.Context) error { } serverInitializingMessage := "Initializing server." - if !rt.Params.AddrSetByUser { + if !rt.Params.AddrSetByUser && !rt.Params.FutureCompatibility { 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/runtime/runtime_test.go b/runtime/runtime_test.go index 41f8e60f35..df3f98ba6d 100644 --- a/runtime/runtime_test.go +++ b/runtime/runtime_test.go @@ -556,12 +556,15 @@ func getTestRuntime(ctx context.Context, t *testing.T, logger logging.Logger) *R func TestAddrWarningMessage(t *testing.T) { testCases := []struct { - name string - addrSetByUser bool - containsMsg bool + name string + addrSetByUser bool + containsMsg bool + futureCompatibility bool }{ - {"WarningMessage", false, true}, - {"NoWarningMessage", true, false}, + {"WarningMessage", false, true, false}, + {"NoWarningMessage", true, false, false}, + {"FutureCompatibilityEnabled", false, false, true}, + {"FutureCompatibilityDisabled", false, true, false}, } for _, tc := range testCases { @@ -578,6 +581,7 @@ func TestAddrWarningMessage(t *testing.T) { params.Addrs = &[]string{"localhost:8181"} params.AddrSetByUser = tc.addrSetByUser params.GracefulShutdownPeriod = 1 + params.FutureCompatibility = tc.futureCompatibility rt, err := NewRuntime(ctx, params) if err != nil { t.Fatalf("Unexpected error %v", err)