From 107921431100fda56a725cc1286d1df9bbd3ef22 Mon Sep 17 00:00:00 2001 From: Anders Eknert Date: Wed, 22 Dec 2021 14:36:47 +0100 Subject: [PATCH] Apply credentials masking on opa.runtime().config In order to prevent sensitive data to accidentally leak out into policies, reuse masking logic previously serving the /v1/config endpoint. Fixes #4159 Signed-off-by: Anders Eknert --- topdown/runtime.go | 109 +++++++++++++++++++++++++++++++++++++++- topdown/runtime_test.go | 39 ++++++++++++++ 2 files changed, 147 insertions(+), 1 deletion(-) diff --git a/topdown/runtime.go b/topdown/runtime.go index 67e183d01d..7d512f7c00 100644 --- a/topdown/runtime.go +++ b/topdown/runtime.go @@ -4,7 +4,11 @@ package topdown -import "github.com/open-policy-agent/opa/ast" +import ( + "fmt" + + "github.com/open-policy-agent/opa/ast" +) func builtinOPARuntime(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) error) error { @@ -12,9 +16,112 @@ func builtinOPARuntime(bctx BuiltinContext, _ []*ast.Term, iter func(*ast.Term) return iter(ast.ObjectTerm()) } + if bctx.Runtime.Get(ast.StringTerm("config")) != nil { + iface, err := ast.ValueToInterface(bctx.Runtime.Value, illegalResolver{}) + if err != nil { + return err + } + if object, ok := iface.(map[string]interface{}); ok { + if cfgRaw, ok := object["config"]; ok { + if config, ok := cfgRaw.(map[string]interface{}); ok { + configPurged, err := activeConfig(config) + if err != nil { + return err + } + object["config"] = configPurged + value, err := ast.InterfaceToValue(object) + if err != nil { + return err + } + return iter(ast.NewTerm(value)) + } + } + } + } + return iter(bctx.Runtime) } func init() { RegisterBuiltinFunc(ast.OPARuntime.Name, builtinOPARuntime) } + +func activeConfig(config map[string]interface{}) (interface{}, error) { + + if config["services"] != nil { + err := removeServiceCredentials(config["services"]) + if err != nil { + return nil, err + } + } + + if config["keys"] != nil { + err := removeCryptoKeys(config["keys"]) + if err != nil { + return nil, err + } + } + + return config, nil +} + +func removeServiceCredentials(x interface{}) error { + + switch x := x.(type) { + case []interface{}: + for _, v := range x { + err := removeKey(v, "credentials") + if err != nil { + return err + } + } + + case map[string]interface{}: + for _, v := range x { + err := removeKey(v, "credentials") + if err != nil { + return err + } + } + default: + return fmt.Errorf("illegal service config type: %T", x) + } + + return nil +} + +func removeCryptoKeys(x interface{}) error { + + switch x := x.(type) { + case map[string]interface{}: + for _, v := range x { + err := removeKey(v, "key", "private_key") + if err != nil { + return err + } + } + default: + return fmt.Errorf("illegal keys config type: %T", x) + } + + return nil +} + +func removeKey(x interface{}, keys ...string) error { + val, ok := x.(map[string]interface{}) + if !ok { + return fmt.Errorf("type assertion error") + } + + for _, key := range keys { + delete(val, key) + } + + return nil +} + +type illegalResolver struct{} + +func (illegalResolver) Resolve(ref ast.Ref) (interface{}, error) { + return nil, fmt.Errorf("illegal value: %v", ref) +} diff --git a/topdown/runtime_test.go b/topdown/runtime_test.go index 13f32da351..bd6f671d53 100644 --- a/topdown/runtime_test.go +++ b/topdown/runtime_test.go @@ -44,3 +44,42 @@ func TestOPARuntime(t *testing.T) { } } + +func TestOPARuntimeConfigMasking(t *testing.T) { + + ctx := context.Background() + q := NewQuery(ast.MustParseBody("opa.runtime(x)")).WithRuntime(ast.MustParseTerm(`{"config": { + "labels": {"foo": "bar"}, + "services": { + "foo": { + "url": "https://remote.example.com", + "credentials": { + "oauth2": { + "client_id": "opa_client", + "client_secret": "sup3rs3cr3t" + } + } + } + } + }}`)) + rs, err := q.Run(ctx) + if err != nil { + t.Fatal(err) + } else if len(rs) != 1 { + t.Fatal("Expected result set to contain exactly one result") + } + + term := rs[0][ast.Var("x")] + exp := ast.MustParseTerm(`{"config": { + "labels": {"foo": "bar"}, + "services": { + "foo": { + "url": "https://remote.example.com" + } + } + }}`) + + if ast.Compare(term, exp) != 0 { + t.Fatalf("Expected %v but got %v", exp, term) + } +}