Bind OPA server to localhost interface by default

Currently OPA binds to the 0.0.0.0 interface by default, which allows
the OPA server to be exposed to services running outside of the same 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.

This change also adds a new feature flag to `opa run` to allow users to enable
future OPA compatible behavior.

Fixes: #6286

Signed-off-by: Ashutosh Narkar <anarkar4387@gmail.com>
This commit is contained in:
Ashutosh Narkar
2023-10-16 16:19:19 -07:00
parent 94a0b946eb
commit f48a4563db
7 changed files with 76 additions and 9 deletions
+12 -2
View File
@@ -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
}
+42
View File
@@ -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"}
+2 -1
View File
@@ -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
+1
View File
@@ -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).
+4
View File
@@ -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
+6 -1
View File
@@ -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"
}
+9 -5
View File
@@ -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)