diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go index 98b6648d2c..84b940249c 100644 --- a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -53,6 +53,17 @@ func TestAuthorizeRequestFilter(t *testing.T) { backendHeaders: make(http.Header), removeHeaders: make(http.Header), }, + { + msg: "Allow Matching Environment", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_runtime_environment", + requestPath: "/allow", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, { msg: "Simple Forbidden", bundleName: "somebundle.tar.gz", @@ -159,6 +170,10 @@ func TestAuthorizeRequestFilter(t *testing.T) { allow_context_extensions { input.attributes.contextExtensions["com.mycompany.myprop"] == "myvalue" } + + allow_runtime_environment { + opa.runtime().config.labels.environment == "test" + } default allow_object = { "allowed": false, @@ -213,6 +228,9 @@ func TestAuthorizeRequestFilter(t *testing.T) { "resource": "/bundles/{{ .bundlename }}" } }, + "labels": { + "environment": "test" + }, "plugins": { "envoy_ext_authz_grpc": { "path": %q, diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go index 63494faf43..7fec7665c8 100644 --- a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -64,6 +64,24 @@ func TestAuthorizeRequestFilter(t *testing.T) { expectedBody: "Welcome from policy!", expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, }, + { + msg: "Allow With opa.runtime execution", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "allow/production", + expectedStatus: http.StatusOK, + expectedBody: "Welcome to production evaluation!", + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, + }, + { + msg: "Deny With opa.runtime execution", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "allow/test", + expectedStatus: http.StatusForbidden, + expectedBody: "Unauthorized Request", + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"no"}}, + }, { msg: "Allow With Structured Body", bundleName: "somebundle.tar.gz", @@ -105,7 +123,7 @@ func TestAuthorizeRequestFilter(t *testing.T) { "allowed": false, "headers": {"x-ext-auth-allow": "no"}, "body": "Unauthorized Request", - "http_status": 401 + "http_status": 403 } allow_object = response { @@ -117,6 +135,28 @@ func TestAuthorizeRequestFilter(t *testing.T) { "http_status": 200 } } + + allow_object = response { + input.parsed_path = [ "allow", "production" ] + opa.runtime().config.labels.environment == "production" + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": "Welcome to production evaluation!", + "http_status": 200 + } + } + + allow_object = response { + input.parsed_path = [ "allow", "test" ] + opa.runtime().config.labels.environment == "test" + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": "Welcome to test evaluation!", + "http_status": 200 + } + } allow_object_structured_body = response { input.parsed_path = [ "allow", "structured" ] @@ -151,6 +191,9 @@ func TestAuthorizeRequestFilter(t *testing.T) { "url": %q } }, + "labels": { + "environment" : "production" + }, "bundles": { "test": { "resource": "/bundles/{{ .bundlename }}" diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go index 3963a00269..6e4b802982 100644 --- a/filters/openpolicyagent/openpolicyagent.go +++ b/filters/openpolicyagent/openpolicyagent.go @@ -359,7 +359,7 @@ func New(store storage.Store, configBytes []byte, instanceConfig OpenPolicyAgent var logger logging.Logger = &QuietLogger{target: logging.Get()} logger = logger.WithFields(map[string]interface{}{"skipper-filter": filterName}) - manager, err := plugins.New(configBytes, id, store, plugins.Logger(logger)) + manager, err := plugins.New(configBytes, id, store, configLabelsInfo(*opaConfig), plugins.Logger(logger)) if err != nil { return nil, err } @@ -449,6 +449,21 @@ func waitFunc(ctx context.Context, fun func() bool, interval time.Duration) erro } } +func configLabelsInfo(opaConfig config.Config) func(*plugins.Manager) { + info := ast.NewObject() + labels := ast.NewObject() + labelsWrapper := ast.NewObject() + + for key, value := range opaConfig.Labels { + labels.Insert(ast.StringTerm(key), ast.StringTerm(value)) + } + + labelsWrapper.Insert(ast.StringTerm("labels"), ast.NewTerm(labels)) + info.Insert(ast.StringTerm("config"), ast.NewTerm(labelsWrapper)) + + return plugins.Info(ast.NewTerm(info)) +} + func (opa *OpenPolicyAgentInstance) InstanceConfig() *OpenPolicyAgentInstanceConfig { return &opa.instanceConfig } diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go index 70c3621944..7a9f53e74e 100644 --- a/filters/openpolicyagent/openpolicyagent_test.go +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/open-policy-agent/opa/ast" "github.com/open-policy-agent/opa/storage/inmem" "io" "net/http" @@ -16,6 +17,7 @@ import ( authv3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" _struct "github.com/golang/protobuf/ptypes/struct" "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" + opaconf "github.com/open-policy-agent/opa/config" opasdktest "github.com/open-policy-agent/opa/sdk/test" "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" @@ -122,6 +124,9 @@ func mockControlPlaneWithDiscoveryBundle(discoveryBundle string) (*opasdktest.Se "url": %q } }, + "labels": { + "environment": "envValue" + }, "discovery": { "name": "discovery", "resource": %q, @@ -256,6 +261,41 @@ func TestOpaActivationSuccessWithDiscovery(t *testing.T) { assert.Equal(t, 1, len(registry.instances)) } +func TestOpaLabelsSetInRuntimeWithDiscovery(t *testing.T) { + _, config := mockControlPlaneWithDiscoveryBundle("bundles/discovery") + + registry := NewOpenPolicyAgentRegistry(WithReuseDuration(1*time.Second), WithCleanInterval(1*time.Second)) + + cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config)) + assert.NoError(t, err) + + instance, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + assert.NotNil(t, instance) + assert.NotNil(t, instance.Runtime()) + + value := instance.Runtime().Value + + j, err := ast.JSON(value) + assert.NoError(t, err) + + if m, ok := j.(map[string]interface{}); ok { + configObject := m["config"] + assert.NotNil(t, configObject) + + jsonData, err := json.Marshal(configObject) + assert.NoError(t, err) + + var parsed *opaconf.Config + json.Unmarshal(jsonData, &parsed) + + labels := parsed.Labels + assert.Equal(t, labels["environment"], "envValue") + } else { + t.Fatalf("Failed to process runtime value %v", j) + } +} + func TestOpaActivationFailureWithWrongServiceConfig(t *testing.T) { configWithUnknownService := []byte(`{ "discovery": {