From 9ed7ecea4c23b2cb2e563be75c5d0e1d4e5b6edf Mon Sep 17 00:00:00 2001 From: Magnus Jungsbluth Date: Mon, 14 Aug 2023 18:31:16 +0200 Subject: [PATCH] Add open policy agent filters (#2407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add Open Policy Agent based filters opaAuthorizeRequest() and opaServeResponse() see also https://opensource.zalando.com/skipper/reference/filters/#open-policy-agent Signed-off-by: Magnus Jungsbluth Signed-off-by: Sandor Szücs Co-authored-by: Sandor Szücs --- config/config.go | 15 + config/config_test.go | 1 + docs/operation/operation.md | 26 + docs/reference/filters.md | 122 ++++ docs/tutorials/auth.md | 86 +++ filters/filters.go | 2 + filters/openpolicyagent/evaluation.go | 73 +++ .../internal/envoy/envoyextauth.go | 7 + .../internal/envoy/envoyplugin.go | 121 ++++ .../internal/envoy/skipperadapter.go | 34 ++ .../opaauthorizerequest.go | 151 +++++ .../opaauthorizerequest_test.go | 282 +++++++++ .../opaserveresponse/opaserveresponse.go | 104 ++++ .../opaserveresponse/opaserveresponse_test.go | 228 ++++++++ filters/openpolicyagent/openpolicyagent.go | 545 ++++++++++++++++++ .../openpolicyagent/openpolicyagent_test.go | 336 +++++++++++ filters/openpolicyagent/response.go | 80 +++ filters/openpolicyagent/tracing.go | 46 ++ filters/openpolicyagent/tracing_test.go | 41 ++ go.mod | 62 +- go.sum | 440 +++++++++++++- skipper.go | 29 + 22 files changed, 2815 insertions(+), 16 deletions(-) create mode 100644 filters/openpolicyagent/evaluation.go create mode 100644 filters/openpolicyagent/internal/envoy/envoyextauth.go create mode 100644 filters/openpolicyagent/internal/envoy/envoyplugin.go create mode 100644 filters/openpolicyagent/internal/envoy/skipperadapter.go create mode 100644 filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go create mode 100644 filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go create mode 100644 filters/openpolicyagent/opaserveresponse/opaserveresponse.go create mode 100644 filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go create mode 100644 filters/openpolicyagent/openpolicyagent.go create mode 100644 filters/openpolicyagent/openpolicyagent_test.go create mode 100644 filters/openpolicyagent/response.go create mode 100644 filters/openpolicyagent/tracing.go create mode 100644 filters/openpolicyagent/tracing_test.go diff --git a/config/config.go b/config/config.go index 5f9df1d99e..53231eb4f0 100644 --- a/config/config.go +++ b/config/config.go @@ -19,6 +19,7 @@ import ( "github.com/zalando/skipper" "github.com/zalando/skipper/dataclients/kubernetes" "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters/openpolicyagent" "github.com/zalando/skipper/net" "github.com/zalando/skipper/proxy" "github.com/zalando/skipper/swarm" @@ -273,6 +274,11 @@ type Config struct { LuaModules *listFlag `yaml:"lua-modules"` LuaSources *listFlag `yaml:"lua-sources"` + + EnableOpenPolicyAgent bool `yaml:"enable-open-policy-agent"` + OpenPolicyAgentConfigTemplate string `yaml:"open-policy-agent-config-template"` + OpenPolicyAgentEnvoyMetadata string `yaml:"open-policy-agent-envoy-metadata"` + OpenPolicyAgentCleanerInterval time.Duration `yaml:"open-policy-agent-cleaner-interval"` } const ( @@ -483,6 +489,10 @@ func NewConfig() *Config { flag.DurationVar(&cfg.OidcDistributedClaimsTimeout, "oidc-distributed-claims-timeout", 2*time.Second, "sets the default OIDC distributed claims request timeout duration to 2000ms") flag.Var(cfg.CredentialPaths, "credentials-paths", "directories or files to watch for credentials to use by bearerinjector filter") flag.DurationVar(&cfg.CredentialsUpdateInterval, "credentials-update-interval", 10*time.Minute, "sets the interval to update secrets") + flag.BoolVar(&cfg.EnableOpenPolicyAgent, "enable-open-policy-agent", false, "enables Open Policy Agent filters") + flag.StringVar(&cfg.OpenPolicyAgentConfigTemplate, "open-policy-agent-config-template", "", "file containing a template for an Open Policy Agent configuration file that is interpolated for each OPA filter instance") + flag.StringVar(&cfg.OpenPolicyAgentEnvoyMetadata, "open-policy-agent-envoy-metadata", "", "JSON file containing meta-data passed as input for compatibility with Envoy policies in the format") + flag.DurationVar(&cfg.OpenPolicyAgentCleanerInterval, "open-policy-agent-cleaner-interval", openpolicyagent.DefaultCleanIdlePeriod, "JSON file containing meta-data passed as input for compatibility with Envoy policies in the format") // TLS client certs flag.StringVar(&cfg.ClientKeyFile, "client-tls-key", "", "TLS Key file for backend connections, multiple keys may be given comma separated - the order must match the certs") @@ -879,6 +889,11 @@ func (c *Config) ToOptions() skipper.Options { LuaModules: c.LuaModules.values, LuaSources: c.LuaSources.values, + + EnableOpenPolicyAgent: c.EnableOpenPolicyAgent, + OpenPolicyAgentConfigTemplate: c.OpenPolicyAgentConfigTemplate, + OpenPolicyAgentEnvoyMetadata: c.OpenPolicyAgentEnvoyMetadata, + OpenPolicyAgentCleanerInterval: c.OpenPolicyAgentCleanerInterval, } for _, rcci := range c.CloneRoute { eskipClone := eskip.NewClone(rcci.Reg, rcci.Repl) diff --git a/config/config_test.go b/config/config_test.go index f70172a3ab..c833717040 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -160,6 +160,7 @@ func defaultConfig() *Config { ValidateQueryLog: true, LuaModules: commaListFlag(), LuaSources: commaListFlag(), + OpenPolicyAgentCleanerInterval: 10 * time.Second, } } diff --git a/docs/operation/operation.md b/docs/operation/operation.md index b8936417c8..e40feee974 100644 --- a/docs/operation/operation.md +++ b/docs/operation/operation.md @@ -489,6 +489,22 @@ by the default, and exposed among the timers via the following keys: See more details about rate limiting at [Rate limiting](../reference/filters.md#clusterclientratelimit). +### Open Policy Agent metrics + +If Open Policy Agent filters are enabled, the following counters show up in the `/metrics` endpoint. The bundle-name is the first parameter of the filter so that for example increased error codes can be attributed to a specific source bundle / system. + +- `skipper.opaAuthorizeRequest.custom.decision.allow.` +- `skipper.opaAuthorizeRequest.custom.decision.deny.` +- `skipper.opaAuthorizeRequest.custom.decision.err.` +- `skipper.opaServeResponse.custom.decision.allow.` +- `skipper.opaServeResponse.custom.decision.deny.` +- `skipper.opaServeResponse.custom.decision.err.` + +The following timer metrics are exposed per used bundle-name: + +- `skipper.opaAuthorizeRequest.custom.eval_time.` +- `skipper.opaServeResponse.custom.eval_time.` + ## OpenTracing Skipper has support for different [OpenTracing API](http://opentracing.io/) vendors, including @@ -612,6 +628,16 @@ connect, TLS handshake and connection pool: ![tokeninfo auth filter span with logs](../img/skipper_opentracing_auth_filter_tokeninfo_span_with_logs.png) +### Open Policy Agent span + +When one of the Open Policy Agent filters is used, child spans with the operation name `open-policy-agent` are added to the Trace. + +The following tags are added to the Span, labels are taken from the OPA configuration YAML file as is and are not interpreted: +- `opa.decision_id=` +- `opa.labels.=` + +The labels can for example be used to link to a specific decision in the control plane if they contain URL fragments for the receiving entity. + ### Redis rate limiting spans #### Operation: redis_allow_check_card diff --git a/docs/reference/filters.md b/docs/reference/filters.md index f2478bb5b5..3ef8cfa4a8 100644 --- a/docs/reference/filters.md +++ b/docs/reference/filters.md @@ -1719,6 +1719,128 @@ oidcClaimsQuery("/:name%\"*One\"", "/path:groups.#[%\"*-Test-Users\"] groups.#[= As of now there is no negative/deny rule possible. The first matching path is evaluated against the defined query/queries and if positive, permitted. +### Open Policy Agent + +To get started with [Open Policy Agent](https://www.openpolicyagent.org/), also have a look at the [tutorial](../tutorials/auth.md#open-policy-agent). This section is only a reference for the implemented filters. + +#### opaAuthorizeRequest + +The canonical use case that is also implemented with [Envoy External Authorization](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter): Use the http request to evaluate if Skipper should deny the request (with customizable response) or let the request pass to the downstream service + +Example: + +``` +opaAuthorizeRequest("my-app-id") +``` + +Example (passing context): +``` +opaAuthorizeRequest("my-app-id", "com.mydomain.xxx.myprop: myvalue") +``` + +*Data Flows* + +The data flow in case the policy allows the request looks like this + +```ascii + ┌──────────────────┐ ┌────────────────────┐ + (1) Request │ Skipper │ (4) Request │ Target Aplication │ +─────────────┤ ├──────────────►│ │ + │ │ │ │ + (6) Response│ (2)│ ▲ (3) │ (5) Response │ │ +◄────────────┤Req ->│ │ allow │◄──────────────┤ │ + │Input │ │ │ │ │ + ├──────┴───┴───────┤ └────────────────────┘ + │Open Policy Agent │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────┴─────┐ │ + │ │ Policy │ │ + │ └──────────────┘ │ + │ │ + └──────────────────┘ + +``` + +In Step (2) the http request is transformed into an input object following the [Envoy structure](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto#service-auth-v3-checkrequest) that is also used by the OPA Envoy plugin. In (3) the decision of the policy is evaluated. If it is equivalent to an "allow", the remaining steps are executed as without the filter. + +The data flow in case the policy disallows the request looks like this + +```ascii + ┌──────────────────┐ ┌────────────────────┐ + (1) Request │ Skipper │ │ Target Aplication │ +─────────────┤ ├──────────────►│ │ + │ │ │ │ + (4) Response│ (2)│ ▲ (3) │ │ │ +◄────────────┤Req ->│ │ allow │◄──────────────┤ │ + │Input │ │ =false│ │ │ + ├──────┴───┴───────┤ └────────────────────┘ + │Open Policy Agent │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────┴─────┐ │ + │ │ Policy │ │ + │ └──────────────┘ │ + │ │ + └──────────────────┘ + +``` + +The difference is that if the decision in (3) is equivalent to false, the response is handled directly from the filter. If the decision contains response body, status or headers those are used to build the response in (6) otherwise a 403 Forbidden with a generic body is returned. + +*Manipulating Request Headers* + +Headers both to the upstream and the downstream service can be manipulated the same way this works for [Envoy external authorization](https://www.openpolicyagent.org/docs/latest/envoy-primer/#example-policy-with-additional-controls) + +This allows both to add and remove unwanted headers in allow/deny cases. + +#### opaServeResponse + +Always serves the response even if the policy allows the request and can customize the response completely. Can be used to re-implement legacy authorization services by already using data in Open Policy Agent but implementing an old REST API. This can also be useful to support Single Page Applications to return the calling users' permissions. + +*Hint*: As there is no real allow/deny in this case and the policy computes the http response, you typically will want to [drop all decision logs](https://www.openpolicyagent.org/docs/latest/management-decision-logs/#drop-decision-logs) + +Example: + +``` +opaServeResponse("my-app-id") +``` + +Example (passing context): +``` +opaServeResponse("my-app-id", "com.mydomain.xxx.myprop: myvalue") +``` + +*Data Flows* + +For this filter, the data flow looks like this independent of an allow/deny decision + +```ascii + ┌──────────────────┐ + (1) Request │ Skipper │ +─────────────┤ ├ + │ │ + (4) Response│ (2)│ ▲ (3) │ +◄────────────┤Req ->│ │ resp │ + │Input │ │ │ + ├──────┴───┴───────┤ + │Open Policy Agent │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ ▼ │ │ + │ ┌────────┴─────┐ │ + │ │ Policy │ │ + │ └──────────────┘ │ + │ │ + └──────────────────┘ + +``` + ## Cookie Handling ### dropRequestCookie diff --git a/docs/tutorials/auth.md b/docs/tutorials/auth.md index 91f7294579..35fd393dfa 100644 --- a/docs/tutorials/auth.md +++ b/docs/tutorials/auth.md @@ -415,3 +415,89 @@ scope by doing the following: > The subject is the field that identifies the user and is often called `sub`, > especially in the context of OpenID Connect. In the example above, it is `username`. + +## Open Policy Agent + +To enable [Open Policy Agent](https://www.openpolicyagent.org/) filter, use the `-enable-open-policy-agent` command line flag. + +Open Policy Agent is integrated as a Go library so no extra setup is needed to run. Every filter creates a virtual OPA instance in memory that is configured using a configuration file in the same [configuration format](https://www.openpolicyagent.org/docs/latest/configuration/) that a standalone OPA would use. To allow for configurability, the configuration file is interpolated using Go Templates to allow every virtual instance to pull different bundles. This template file is passed using the `-open-policy-agent-config-template` flag. + +### Configuration File + +As an example the following initial config can be used + +```yaml +services: + - name: bundle-service + url: https://my-example-opa-bucket.s3.eu-central-1.amazonaws.com + credentials: + s3_signing: + environment_credentials: {} +labels: + environment: production +discovery: + name: discovery + prefix: "/applications/{{ .bundlename }}" +``` + +The variable `.bundlename` is the first argument in the following filters and can be in any format that OPA can understand, so for example application IDs from a registry, uuids, ... + +### Input Structures + +Input structures to policies follow those that are used by the [opa-envoy-plugin](https://github.com/open-policy-agent/opa-envoy-plugin), the existing [examples and documentation](https://www.openpolicyagent.org/docs/latest/envoy-primer/#example-input) apply also to Skipper. Please note that the filters in Skipper always generate v3 input structures. + +### Passing context to the policy + +Generally there are two ways to pass context to a policy: + +1. as part of the labels in Open Policy Agent (configured in the configuration file, see below) that should be used for deployment level taxonomy, +2. as part of so called context extensions that are part of the Envoy external auth specification. + +This context can be passed as second argument to filters: + +`opaAuthorizeRequest("my-app-id", "com.mycompany.myprop: myvalue")` +or `opaAuthorizeRequest("my-app-id", "{'com.mycompany.myprop': 'my value'}")` + +The second argument is parsed as YAML, cannot be nested and values need to be strings. + +In Rego this can be used like this `input.attributes.contextExtensions["com.mycompany.myprop"] == "my value"` + +### Quick Start Rego Playground + +A quick way without setting up Backend APIs is to use the [Rego Playground](https://play.openpolicyagent.org/). + +To get started pick from examples Envoy > Hello World. Click on "Publish" and note the random ID in the section "Run OPA with playground policy". + +Place the following file in your local directory with the name `opaconfig.yaml` + +```yaml +bundles: + play: + resource: bundles/{{ .bundlename }} + polling: + long_polling_timeout_seconds: 45 +services: + - name: play + url: https://play.openpolicyagent.org +plugins: + envoy_ext_authz_grpc: + # This needs to match the package, defaulting to envoy/authz/allow + path: envoy/http/public/allow + dry-run: false +decision_logs: + console: true +``` + +Start Skipper with + +``` +skipper -enable-open-policy-agent -open-policy-agent-config-template opaconfig.yaml \ + -inline-routes 'notfound: * -> opaAuthorizeRequest("") -> inlineContent("

Authorized Hello

") -> ' +``` + +You can test the policy with + +- Authorized: `curl http://localhost:9090/ -i` +- Authorized: `curl http://localhost:9090/foobar -H "Authorization: Basic charlie" -i` +- Forbidden: `curl http://localhost:9090/foobar -i` + diff --git a/filters/filters.go b/filters/filters.go index 9ac59de3a6..33d89ad731 100644 --- a/filters/filters.go +++ b/filters/filters.go @@ -341,6 +341,8 @@ const ( EndpointCreatedName = "endpointCreated" ConsistentHashKeyName = "consistentHashKey" ConsistentHashBalanceFactorName = "consistentHashBalanceFactor" + OpaAuthorizeRequestName = "opaAuthorizeRequest" + OpaServeResponseName = "opaServeResponse" // Undocumented filters HealthCheckName = "healthcheck" diff --git a/filters/openpolicyagent/evaluation.go b/filters/openpolicyagent/evaluation.go new file mode 100644 index 0000000000..cc8ed0042c --- /dev/null +++ b/filters/openpolicyagent/evaluation.go @@ -0,0 +1,73 @@ +package openpolicyagent + +import ( + "context" + "fmt" + "time" + + ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" + "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" + "github.com/open-policy-agent/opa-envoy-plugin/opa/decisionlog" + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/rego" + "github.com/open-policy-agent/opa/server" + "github.com/open-policy-agent/opa/tracing" + "github.com/opentracing/opentracing-go" +) + +func (opa *OpenPolicyAgentInstance) Eval(ctx context.Context, req *ext_authz_v3.CheckRequest) (*envoyauth.EvalResult, error) { + result, stopeval, err := envoyauth.NewEvalResult() + span := opentracing.SpanFromContext(ctx) + if span != nil { + span.SetTag("opa.decision_id", result.DecisionID) + } + + if err != nil { + opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to generate decision ID.") + return nil, err + } + + var input map[string]interface{} + defer func() { + stopeval() + err := opa.logDecision(ctx, input, result, err) + if err != nil { + opa.Logger().WithFields(map[string]interface{}{"err": err}).Error("Unable to log decision to control plane.") + } + }() + + if ctx.Err() != nil { + return nil, fmt.Errorf("check request timed out before query execution: %w", ctx.Err()) + } + + logger := opa.manager.Logger().WithFields(map[string]interface{}{"decision-id": result.DecisionID}) + input, err = envoyauth.RequestToInput(req, logger, nil, true) + if err != nil { + return nil, fmt.Errorf("failed to convert request to input: %w", err) + } + + inputValue, err := ast.InterfaceToValue(input) + if err != nil { + return nil, err + } + + err = envoyauth.Eval(ctx, opa, inputValue, result, rego.DistributedTracingOpts(tracing.Options{opa})) + if err != nil { + return nil, err + } + + return result, nil +} + +func (opa *OpenPolicyAgentInstance) logDecision(ctx context.Context, input interface{}, result *envoyauth.EvalResult, err error) error { + info := &server.Info{ + Timestamp: time.Now(), + Input: &input, + } + + if opa.EnvoyPluginConfig().Path != "" { + info.Path = opa.EnvoyPluginConfig().Path + } + + return decisionlog.LogDecision(ctx, opa.manager, info, result, err) +} diff --git a/filters/openpolicyagent/internal/envoy/envoyextauth.go b/filters/openpolicyagent/internal/envoy/envoyextauth.go new file mode 100644 index 0000000000..2a808b846a --- /dev/null +++ b/filters/openpolicyagent/internal/envoy/envoyextauth.go @@ -0,0 +1,7 @@ +package envoy + +const defaultPath = "envoy/authz/allow" +const defaultDryRun = false + +// PluginName is the name to register with the OPA plugin manager +const PluginName = "envoy_ext_authz_grpc" diff --git a/filters/openpolicyagent/internal/envoy/envoyplugin.go b/filters/openpolicyagent/internal/envoy/envoyplugin.go new file mode 100644 index 0000000000..a0a88a940f --- /dev/null +++ b/filters/openpolicyagent/internal/envoy/envoyplugin.go @@ -0,0 +1,121 @@ +package envoy + +import ( + "context" + "strconv" + "strings" + + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/plugins" + "github.com/open-policy-agent/opa/util" +) + +// Factory defines the interface OPA uses to instantiate a plugin. +type Factory struct{} + +// New returns the object initialized with a valid plugin configuration. +func (Factory) New(m *plugins.Manager, cfg interface{}) plugins.Plugin { + p := &Plugin{ + manager: m, + cfg: *cfg.(*PluginConfig), + } + + m.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateNotReady}) + + return p +} + +// Validate returns a valid configuration to instantiate the plugin. +func (Factory) Validate(m *plugins.Manager, bs []byte) (interface{}, error) { + cfg := PluginConfig{ + DryRun: defaultDryRun, + } + + if err := util.Unmarshal(bs, &cfg); err != nil { + return nil, err + } + + if err := cfg.ParseQuery(); err != nil { + return nil, err + } + + return &cfg, nil +} + +func (p *Plugin) Reconfigure(ctx context.Context, config interface{}) { + p.cfg = *config.(*PluginConfig) +} + +// PluginConfig represents the plugin configuration. +type PluginConfig struct { + Path string `json:"path"` + DryRun bool `json:"dry-run"` + ParsedQuery ast.Body +} + +type Plugin struct { + cfg PluginConfig + manager *plugins.Manager +} + +func (p *Plugin) Start(ctx context.Context) error { + p.manager.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateOK}) + return nil +} + +func (cfg *PluginConfig) ParseQuery() error { + var parsedQuery ast.Body + var err error + + if cfg.Path == "" { + cfg.Path = defaultPath + } + path := stringPathToDataRef(cfg.Path) + parsedQuery, err = ast.ParseBody(path.String()) + + if err != nil { + return err + } + + cfg.ParsedQuery = parsedQuery + + return nil +} + +func (p *Plugin) Stop(ctx context.Context) { + p.manager.UpdatePluginStatus(PluginName, &plugins.Status{State: plugins.StateNotReady}) +} + +func (p *Plugin) GetConfig() PluginConfig { + return p.cfg +} + +func (p *Plugin) ParsedQuery() ast.Body { return p.cfg.ParsedQuery } +func (p *Plugin) Path() string { return p.cfg.Path } + +func stringPathToDataRef(s string) (r ast.Ref) { + result := ast.Ref{ast.DefaultRootDocument} + result = append(result, stringPathToRef(s)...) + return result +} + +func stringPathToRef(s string) (r ast.Ref) { + if len(s) == 0 { + return r + } + + p := strings.Split(s, "/") + for _, x := range p { + if x == "" { + continue + } + + i, err := strconv.Atoi(x) + if err != nil { + r = append(r, ast.StringTerm(x)) + } else { + r = append(r, ast.IntNumberTerm(i)) + } + } + return r +} diff --git a/filters/openpolicyagent/internal/envoy/skipperadapter.go b/filters/openpolicyagent/internal/envoy/skipperadapter.go new file mode 100644 index 0000000000..918d9099d7 --- /dev/null +++ b/filters/openpolicyagent/internal/envoy/skipperadapter.go @@ -0,0 +1,34 @@ +package envoy + +import ( + "net/http" + "strings" + + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3" +) + +func AdaptToExtAuthRequest(req *http.Request, metadata *ext_authz_v3_core.Metadata, contextExtensions map[string]string) *ext_authz_v3.CheckRequest { + + headers := make(map[string]string, len(req.Header)) + for h, vv := range req.Header { + // This makes headers in the input compatible with what Envoy does, i.e. allows to use policy fragments designed for envoy + // See: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/header_casing#http-1-1-header-casing + headers[strings.ToLower(h)] = strings.Join(vv, ", ") + } + + return &ext_authz_v3.CheckRequest{ + Attributes: &ext_authz_v3.AttributeContext{ + Request: &ext_authz_v3.AttributeContext_Request{ + Http: &ext_authz_v3.AttributeContext_HttpRequest{ + Host: req.Host, + Method: req.Method, + Path: req.URL.Path, + Headers: headers, + }, + }, + ContextExtensions: contextExtensions, + MetadataContext: metadata, + }, + } +} diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go new file mode 100644 index 0000000000..b3747d5d5b --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest.go @@ -0,0 +1,151 @@ +package opaauthorizerequest + +import ( + "net/http" + "time" + + "github.com/zalando/skipper/filters" + "gopkg.in/yaml.v2" + + "github.com/zalando/skipper/filters/openpolicyagent" + "github.com/zalando/skipper/filters/openpolicyagent/internal/envoy" +) + +type spec struct { + registry *openpolicyagent.OpenPolicyAgentRegistry + opts []func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error +} + +func NewOpaAuthorizeRequestSpec(registry *openpolicyagent.OpenPolicyAgentRegistry, opts ...func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error) filters.Spec { + return &spec{ + registry: registry, + opts: opts, + } +} + +func (s *spec) Name() string { + return filters.OpaAuthorizeRequestName +} + +func (s *spec) CreateFilter(args []interface{}) (filters.Filter, error) { + var err error + + if len(args) < 1 { + return nil, filters.ErrInvalidFilterParameters + } + + if len(args) > 2 { + return nil, filters.ErrInvalidFilterParameters + } + + bundleName, ok := args[0].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + envoyContextExtensions := map[string]string{} + if len(args) > 1 { + _, ok := args[1].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + err = yaml.Unmarshal([]byte(args[1].(string)), &envoyContextExtensions) + if err != nil { + return nil, err + } + } + + configOptions := s.opts + + opaConfig, err := openpolicyagent.NewOpenPolicyAgentConfig(configOptions...) + if err != nil { + return nil, err + } + + opa, err := s.registry.NewOpenPolicyAgentInstance(bundleName, *opaConfig, s.Name()) + + if err != nil { + return nil, err + } + + return &opaAuthorizeRequestFilter{ + opa: opa, + registry: s.registry, + envoyContextExtensions: envoyContextExtensions, + }, nil +} + +type opaAuthorizeRequestFilter struct { + opa *openpolicyagent.OpenPolicyAgentInstance + registry *openpolicyagent.OpenPolicyAgentRegistry + envoyContextExtensions map[string]string +} + +func (f *opaAuthorizeRequestFilter) Request(fc filters.FilterContext) { + req := fc.Request() + span, ctx := f.opa.StartSpanFromFilterContext(fc) + defer span.Finish() + + authzreq := envoy.AdaptToExtAuthRequest(req, f.opa.InstanceConfig().GetEnvoyMetadata(), f.envoyContextExtensions) + + start := time.Now() + result, err := f.opa.Eval(ctx, authzreq) + fc.Metrics().MeasureSince(f.opa.MetricsKey("eval_time"), start) + + if err != nil { + f.opa.HandleInvalidDecisionError(fc, span, result, err, !f.opa.EnvoyPluginConfig().DryRun) + return + } + + if f.opa.EnvoyPluginConfig().DryRun { + return + } + + allowed, err := result.IsAllowed() + if err != nil { + f.opa.HandleInvalidDecisionError(fc, span, result, err, !f.opa.EnvoyPluginConfig().DryRun) + return + } + if !allowed { + fc.Metrics().IncCounter(f.opa.MetricsKey("decision.deny")) + f.opa.ServeResponse(fc, span, result) + return + } + + fc.Metrics().IncCounter(f.opa.MetricsKey("decision.allow")) + + headersToRemove, err := result.GetRequestHTTPHeadersToRemove() + if err != nil { + f.opa.HandleInvalidDecisionError(fc, span, result, err, !f.opa.EnvoyPluginConfig().DryRun) + return + } + removeRequestHeaders(fc, headersToRemove) + + headers, err := result.GetResponseHTTPHeaders() + if err != nil { + f.opa.HandleInvalidDecisionError(fc, span, result, err, !f.opa.EnvoyPluginConfig().DryRun) + return + } + addRequestHeaders(fc, headers) +} + +func removeRequestHeaders(fc filters.FilterContext, headersToRemove []string) { + for _, header := range headersToRemove { + fc.Request().Header.Del(header) + } +} + +func addRequestHeaders(fc filters.FilterContext, headers http.Header) { + for key, values := range headers { + for _, value := range values { + // This is the default behavior from https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/base.proto#config-core-v3-headervalueoption + fc.Request().Header.Add(key, value) + } + } +} + +func (*opaAuthorizeRequestFilter) Response(filters.FilterContext) {} + +func (f *opaAuthorizeRequestFilter) OpenPolicyAgent() *openpolicyagent.OpenPolicyAgentInstance { + return f.opa +} diff --git a/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go new file mode 100644 index 0000000000..41c3169259 --- /dev/null +++ b/filters/openpolicyagent/opaauthorizerequest/opaauthorizerequest_test.go @@ -0,0 +1,282 @@ +package opaauthorizerequest + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + opasdktest "github.com/open-policy-agent/opa/sdk/test" + "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/proxy/proxytest" + + "github.com/zalando/skipper/filters/openpolicyagent" +) + +func TestAuthorizeRequestFilter(t *testing.T) { + for _, ti := range []struct { + msg string + bundleName string + regoQuery string + requestPath string + contextExtensions string + expectedBody string + expectedHeaders http.Header + expectedStatus int + backendHeaders http.Header + removeHeaders http.Header + }{ + { + msg: "Allow Requests", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/allow", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Allow Matching Context Extension", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_context_extensions", + requestPath: "/allow", + contextExtensions: "com.mycompany.myprop: myvalue", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Simple Forbidden", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "/forbidden", + contextExtensions: "", + expectedStatus: http.StatusForbidden, + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Allow With Structured Rules", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "/allow/structured", + contextExtensions: "", + expectedStatus: http.StatusOK, + expectedBody: "Welcome!", + expectedHeaders: make(http.Header), + backendHeaders: map[string][]string{"X-Consumer": {"x-consumer header value"}}, + removeHeaders: map[string][]string{"X-Remove-Me": {"Remove me"}}, + }, + { + msg: "Forbidden With Body", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "/forbidden", + contextExtensions: "", + expectedStatus: http.StatusUnauthorized, + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"no"}}, + expectedBody: "Unauthorized Request", + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Misconfigured Rego Query", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/invalid_path", + requestPath: "/allow", + contextExtensions: "", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Wrong Query Data Type", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_wrong_type", + requestPath: "/allow", + contextExtensions: "", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Wrong Query Data Type", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object_invalid_headers_to_remove", + requestPath: "/allow", + contextExtensions: "", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + { + msg: "Wrong Query Data Type", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object_invalid_headers", + requestPath: "/allow", + contextExtensions: "", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: make(http.Header), + backendHeaders: make(http.Header), + removeHeaders: make(http.Header), + }, + } { + t.Run(ti.msg, func(t *testing.T) { + t.Logf("Running test for %v", ti) + clientServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Welcome!")) + assert.True(t, isHeadersPresent(t, ti.backendHeaders, r.Header), "Enriched request header is absent.") + assert.True(t, isHeadersAbsent(t, ti.removeHeaders, r.Header), "Unwanted HTTP Headers present.") + })) + + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle("/bundles/"+ti.bundleName, map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + + allow { + input.parsed_path = [ "allow" ] + } + + allow_context_extensions { + input.attributes.contextExtensions["com.mycompany.myprop"] == "myvalue" + } + + default allow_object = { + "allowed": false, + "headers": {"x-ext-auth-allow": "no"}, + "body": "Unauthorized Request", + "http_status": 401 + } + + allow_object = response { + input.parsed_path = [ "allow", "structured" ] + response := { + "allowed": true, + "headers": { + "x-consumer": "x-consumer header value" + }, + "request_headers_to_remove" : [ + "x-remove-me", + "absent-header" + ] + } + } + + allow_wrong_type := "true" + + allow_object_invalid_headers_to_remove := { + "allowed": true, + "request_headers_to_remove": "bogus string instead of object" + } + + allow_object_invalid_headers := { + "allowed": true, + "headers": "bogus string instead of object" + } + `, + }), + ) + + fr := make(filters.Registry) + + config := []byte(fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/{{ .bundlename }}" + } + }, + "plugins": { + "envoy_ext_authz_grpc": { + "path": %q, + "dry-run": false + } + } + }`, opaControlPlane.URL(), ti.regoQuery)) + + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + ftSpec := NewOpaAuthorizeRequestSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + fr.Register(ftSpec) + + r := eskip.MustParse(fmt.Sprintf(`* -> opaAuthorizeRequest("%s", "%s") -> "%s"`, ti.bundleName, ti.contextExtensions, clientServer.URL)) + + proxy := proxytest.New(fr, r...) + + req, err := http.NewRequest("GET", proxy.URL+ti.requestPath, nil) + for name, values := range ti.removeHeaders { + for _, value := range values { + req.Header.Add(name, value) //adding the headers to validate removal. + } + } + + assert.NoError(t, err) + + rsp, err := proxy.Client().Do(req) + assert.NoError(t, err) + + assert.Equal(t, ti.expectedStatus, rsp.StatusCode, "HTTP status does not match") + + assert.True(t, isHeadersPresent(t, ti.expectedHeaders, rsp.Header), "HTTP Headers do not match") + + defer rsp.Body.Close() + body, err := io.ReadAll(rsp.Body) + assert.NoError(t, err) + assert.Equal(t, ti.expectedBody, string(body), "HTTP Body does not match") + }) + } +} + +func TestCreateFilterArguments(t *testing.T) { + opaRegistry := openpolicyagent.NewOpenPolicyAgentRegistry() + ftSpec := NewOpaAuthorizeRequestSpec(opaRegistry, openpolicyagent.WithConfigTemplate([]byte(""))) + + _, err := ftSpec.CreateFilter([]interface{}{}) + assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) + + _, err = ftSpec.CreateFilter([]interface{}{"a bundle", "extra: value", "superfluous argument"}) + assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) +} + +func isHeadersPresent(t *testing.T, expectedHeaders http.Header, headers http.Header) bool { + for headerName, expectedValues := range expectedHeaders { + actualValues, headerFound := headers[headerName] + if !headerFound { + return false + } + + assert.ElementsMatch(t, expectedValues, actualValues) + } + return true +} + +func isHeadersAbsent(t *testing.T, unwantedHeaders http.Header, headers http.Header) bool { + for headerName := range unwantedHeaders { + if _, ok := headers[headerName]; ok { + return false + } + } + return true +} diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse.go new file mode 100644 index 0000000000..6134fa0d74 --- /dev/null +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse.go @@ -0,0 +1,104 @@ +package opaserveresponse + +import ( + "time" + + "github.com/zalando/skipper/filters" + "gopkg.in/yaml.v2" + + "github.com/zalando/skipper/filters/openpolicyagent" + "github.com/zalando/skipper/filters/openpolicyagent/internal/envoy" +) + +type spec struct { + registry *openpolicyagent.OpenPolicyAgentRegistry + opts []func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error +} + +func NewOpaServeResponseSpec(registry *openpolicyagent.OpenPolicyAgentRegistry, opts ...func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error) filters.Spec { + return &spec{ + registry: registry, + opts: opts, + } +} + +func (s *spec) Name() string { + return filters.OpaServeResponseName +} + +func (s *spec) CreateFilter(args []interface{}) (filters.Filter, error) { + var err error + + if len(args) < 1 { + return nil, filters.ErrInvalidFilterParameters + } + + if len(args) > 2 { + return nil, filters.ErrInvalidFilterParameters + } + + bundleName, ok := args[0].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + + envoyContextExtensions := map[string]string{} + if len(args) > 1 { + _, ok := args[1].(string) + if !ok { + return nil, filters.ErrInvalidFilterParameters + } + err = yaml.Unmarshal([]byte(args[1].(string)), &envoyContextExtensions) + if err != nil { + return nil, err + } + } + + configOptions := s.opts + + opaConfig, err := openpolicyagent.NewOpenPolicyAgentConfig(configOptions...) + if err != nil { + return nil, err + } + + opa, err := s.registry.NewOpenPolicyAgentInstance(bundleName, *opaConfig, s.Name()) + if err != nil { + return nil, err + } + + return &opaServeResponseFilter{ + opa: opa, + registry: s.registry, + envoyContextExtensions: envoyContextExtensions, + }, nil +} + +type opaServeResponseFilter struct { + opa *openpolicyagent.OpenPolicyAgentInstance + registry *openpolicyagent.OpenPolicyAgentRegistry + envoyContextExtensions map[string]string +} + +func (f *opaServeResponseFilter) Request(fc filters.FilterContext) { + span, ctx := f.opa.StartSpanFromFilterContext(fc) + defer span.Finish() + + authzreq := envoy.AdaptToExtAuthRequest(fc.Request(), f.opa.InstanceConfig().GetEnvoyMetadata(), f.envoyContextExtensions) + + start := time.Now() + result, err := f.opa.Eval(ctx, authzreq) + fc.Metrics().MeasureSince(f.opa.MetricsKey("eval_time"), start) + if err != nil { + f.opa.ServeInvalidDecisionError(fc, span, result, err) + + return + } + + f.opa.ServeResponse(fc, span, result) +} + +func (f *opaServeResponseFilter) Response(fc filters.FilterContext) {} + +func (f *opaServeResponseFilter) OpenPolicyAgent() *openpolicyagent.OpenPolicyAgentInstance { + return f.opa +} diff --git a/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go new file mode 100644 index 0000000000..63494faf43 --- /dev/null +++ b/filters/openpolicyagent/opaserveresponse/opaserveresponse_test.go @@ -0,0 +1,228 @@ +package opaserveresponse + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "path" + "testing" + + opasdktest "github.com/open-policy-agent/opa/sdk/test" + "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/eskip" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/proxy/proxytest" + + "github.com/zalando/skipper/filters/openpolicyagent" +) + +func TestAuthorizeRequestFilter(t *testing.T) { + for _, ti := range []struct { + msg string + bundleName string + regoQuery string + requestPath string + expectedBody string + contextExtensions string + expectedHeaders http.Header + expectedStatus int + }{ + { + msg: "Allow Requests", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "allow", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: make(http.Header), + }, + { + msg: "Simple Forbidden", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow", + requestPath: "forbidden", + expectedStatus: http.StatusForbidden, + expectedHeaders: make(http.Header), + }, + { + msg: "Misconfigured Rego Query", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/invalid_path", + requestPath: "allow", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: make(http.Header), + }, + { + msg: "Allow With Structured Rules", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object", + requestPath: "allow/structured", + expectedStatus: http.StatusOK, + expectedBody: "Welcome from policy!", + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, + }, + { + msg: "Allow With Structured Body", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object_structured_body", + requestPath: "allow/structured", + expectedStatus: http.StatusInternalServerError, + expectedBody: "", + expectedHeaders: map[string][]string{}, + }, + { + msg: "Allow with context extensions", + bundleName: "somebundle.tar.gz", + regoQuery: "envoy/authz/allow_object_contextextensions", + requestPath: "allow/structured", + contextExtensions: "hello: world", + expectedStatus: http.StatusOK, + expectedHeaders: map[string][]string{"X-Ext-Auth-Allow": {"yes"}}, + expectedBody: `{"hello":"world"}`, + }, + } { + t.Run(ti.msg, func(t *testing.T) { + t.Logf("Running test for %v", ti) + clientServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("Welcome!")) + })) + + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle("/bundles/"+ti.bundleName, map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + + allow { + input.parsed_path = [ "allow" ] + } + + default allow_object = { + "allowed": false, + "headers": {"x-ext-auth-allow": "no"}, + "body": "Unauthorized Request", + "http_status": 401 + } + + allow_object = response { + input.parsed_path = [ "allow", "structured" ] + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": "Welcome from policy!", + "http_status": 200 + } + } + + allow_object_structured_body = response { + input.parsed_path = [ "allow", "structured" ] + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": {"hello": "world"}, + "http_status": 200 + } + } + + + allow_object_contextextensions = response { + input.parsed_path = [ "allow", "structured" ] + response := { + "allowed": true, + "headers": {"x-ext-auth-allow": "yes"}, + "body": json.marshal(input.attributes.contextExtensions), + "http_status": 200 + } + } + + `, + }), + ) + + fr := make(filters.Registry) + + config := []byte(fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/{{ .bundlename }}" + } + }, + "plugins": { + "envoy_ext_authz_grpc": { + "path": %q, + "dry-run": false + } + } + }`, opaControlPlane.URL(), ti.regoQuery)) + + opaFactory := openpolicyagent.NewOpenPolicyAgentRegistry() + ftSpec := NewOpaServeResponseSpec(opaFactory, openpolicyagent.WithConfigTemplate(config)) + + filterArgs := []interface{}{ti.bundleName} + if ti.contextExtensions != "" { + filterArgs = append(filterArgs, ti.contextExtensions) + } + + _, err := ftSpec.CreateFilter(filterArgs) + assert.NoErrorf(t, err, "error in creating filter: %v", err) + + fr.Register(ftSpec) + + r := eskip.MustParse(fmt.Sprintf(`* -> opaServeResponse("%s", "%s") -> "%s"`, ti.bundleName, ti.contextExtensions, clientServer.URL)) + + proxy := proxytest.New(fr, r...) + reqURL, err := url.Parse(proxy.URL) + assert.NoErrorf(t, err, "Failed to parse url %s: %v", proxy.URL, err) + + reqURL.Path = path.Join(reqURL.Path, ti.requestPath) + req, err := http.NewRequest("GET", reqURL.String(), nil) + assert.NoError(t, err) + + rsp, err := proxy.Client().Do(req) + assert.NoError(t, err) + + assert.Equal(t, ti.expectedStatus, rsp.StatusCode, "HTTP status does not match") + + sanitizedHeaders := rsp.Header + sanitizedHeaders.Del("Date") + sanitizedHeaders.Del("Server") + sanitizedHeaders.Del("Content-Length") + sanitizedHeaders.Del("Content-Type") + assert.Equal(t, ti.expectedHeaders, sanitizedHeaders, "HTTP Headers do not match") + + defer rsp.Body.Close() + body, err := io.ReadAll(rsp.Body) + assert.NoError(t, err) + assert.Equal(t, ti.expectedBody, string(body), "HTTP Headers do not match") + }) + } +} + +func TestCreateFilterArguments(t *testing.T) { + opaRegistry := openpolicyagent.NewOpenPolicyAgentRegistry() + ftSpec := NewOpaServeResponseSpec(opaRegistry, openpolicyagent.WithConfigTemplate([]byte(""))) + + _, err := ftSpec.CreateFilter([]interface{}{}) + assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) + + _, err = ftSpec.CreateFilter([]interface{}{42}) + assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) + + _, err = ftSpec.CreateFilter([]interface{}{"a bundle", 42}) + assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) + + _, err = ftSpec.CreateFilter([]interface{}{"a bundle", "invalid; context extensions"}) + assert.Error(t, err) + + _, err = ftSpec.CreateFilter([]interface{}{"a bundle", "extra: value", "superfluous argument"}) + assert.ErrorIs(t, err, filters.ErrInvalidFilterParameters) +} diff --git a/filters/openpolicyagent/openpolicyagent.go b/filters/openpolicyagent/openpolicyagent.go new file mode 100644 index 0000000000..56aa581131 --- /dev/null +++ b/filters/openpolicyagent/openpolicyagent.go @@ -0,0 +1,545 @@ +package openpolicyagent + +import ( + "bytes" + "context" + "fmt" + "os" + "strings" + "sync" + "text/template" + "time" + + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + "github.com/google/uuid" + "github.com/open-policy-agent/opa/ast" + "github.com/open-policy-agent/opa/config" + "github.com/open-policy-agent/opa/logging" + "github.com/open-policy-agent/opa/plugins" + "github.com/open-policy-agent/opa/plugins/discovery" + "github.com/open-policy-agent/opa/rego" + "github.com/open-policy-agent/opa/runtime" + "github.com/open-policy-agent/opa/storage" + "github.com/open-policy-agent/opa/storage/inmem" + iCache "github.com/open-policy-agent/opa/topdown/cache" + opatracing "github.com/open-policy-agent/opa/tracing" + opautil "github.com/open-policy-agent/opa/util" + "github.com/opentracing/opentracing-go" + "google.golang.org/protobuf/encoding/protojson" + + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/openpolicyagent/internal/envoy" + "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/tracing" +) + +const ( + defaultReuseDuration = 30 * time.Second + defaultShutdownGracePeriod = 30 * time.Second + DefaultCleanIdlePeriod = 10 * time.Second +) + +type OpenPolicyAgentRegistry struct { + // Ideally share one Bundle storage across many OPA "instances" using this registry. + // This allows to save memory on bundles that are shared + // between different policies (i.e. global team memberships) + // This not possible due to some limitations in OPA + // See https://github.com/open-policy-agent/opa/issues/5707 + + mu sync.Mutex + instances map[string]*OpenPolicyAgentInstance + lastused map[*OpenPolicyAgentInstance]time.Time + + once sync.Once + closed bool + quit chan struct{} + reuseDuration time.Duration + cleanInterval time.Duration +} + +type OpenPolicyAgentFilter interface { + OpenPolicyAgent() *OpenPolicyAgentInstance +} + +func WithReuseDuration(duration time.Duration) func(*OpenPolicyAgentRegistry) error { + return func(cfg *OpenPolicyAgentRegistry) error { + cfg.reuseDuration = duration + return nil + } +} + +func WithCleanInterval(interval time.Duration) func(*OpenPolicyAgentRegistry) error { + return func(cfg *OpenPolicyAgentRegistry) error { + cfg.cleanInterval = interval + return nil + } +} + +func NewOpenPolicyAgentRegistry(opts ...func(*OpenPolicyAgentRegistry) error) *OpenPolicyAgentRegistry { + registry := &OpenPolicyAgentRegistry{ + reuseDuration: defaultReuseDuration, + cleanInterval: DefaultCleanIdlePeriod, + instances: make(map[string]*OpenPolicyAgentInstance), + lastused: make(map[*OpenPolicyAgentInstance]time.Time), + quit: make(chan struct{}), + } + + for _, opt := range opts { + opt(registry) + } + + go registry.startCleanerDaemon() + + return registry +} + +type OpenPolicyAgentInstanceConfig struct { + envoyMetadata *ext_authz_v3_core.Metadata + configTemplate []byte +} + +func WithConfigTemplate(configTemplate []byte) func(*OpenPolicyAgentInstanceConfig) error { + return func(cfg *OpenPolicyAgentInstanceConfig) error { + cfg.configTemplate = configTemplate + return nil + } +} + +func WithConfigTemplateFile(configTemplateFile string) func(*OpenPolicyAgentInstanceConfig) error { + return func(cfg *OpenPolicyAgentInstanceConfig) error { + var err error + cfg.configTemplate, err = os.ReadFile(configTemplateFile) + return err + } +} + +func WithEnvoyMetadata(metadata *ext_authz_v3_core.Metadata) func(*OpenPolicyAgentInstanceConfig) error { + return func(cfg *OpenPolicyAgentInstanceConfig) error { + cfg.envoyMetadata = metadata + return nil + } +} + +func WithEnvoyMetadataBytes(content []byte) func(*OpenPolicyAgentInstanceConfig) error { + return func(cfg *OpenPolicyAgentInstanceConfig) error { + cfg.envoyMetadata = &ext_authz_v3_core.Metadata{} + + return protojson.Unmarshal(content, cfg.envoyMetadata) + } +} + +func WithEnvoyMetadataFile(file string) func(*OpenPolicyAgentInstanceConfig) error { + return func(cfg *OpenPolicyAgentInstanceConfig) error { + content, err := os.ReadFile(file) + if err != nil { + return err + } + + err = WithEnvoyMetadataBytes(content)(cfg) + if err != nil { + return fmt.Errorf("cannot parse '%q': %w", file, err) + } + + return nil + } +} + +func (cfg *OpenPolicyAgentInstanceConfig) GetEnvoyMetadata() *ext_authz_v3_core.Metadata { + return cfg.envoyMetadata +} + +func NewOpenPolicyAgentConfig(opts ...func(*OpenPolicyAgentInstanceConfig) error) (*OpenPolicyAgentInstanceConfig, error) { + cfg := OpenPolicyAgentInstanceConfig{} + + for _, opt := range opts { + if err := opt(&cfg); err != nil { + return nil, err + } + } + + if cfg.configTemplate == nil { + var err error + cfg.configTemplate, err = os.ReadFile("opaconfig.yaml") + if err != nil { + return nil, err + } + } + + return &cfg, nil +} + +func (registry *OpenPolicyAgentRegistry) Close() { + registry.once.Do(func() { + registry.mu.Lock() + defer registry.mu.Unlock() + + ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownGracePeriod) + defer cancel() + + for _, instance := range registry.instances { + instance.Close(ctx) + } + + registry.closed = true + close(registry.quit) + }) +} + +func (registry *OpenPolicyAgentRegistry) cleanUnusedInstances(t time.Time) { + registry.mu.Lock() + defer registry.mu.Unlock() + + if registry.closed { + return + } + + ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownGracePeriod) + defer cancel() + + for key, inst := range registry.instances { + lastused, ok := registry.lastused[inst] + + if ok && t.Sub(lastused) > registry.reuseDuration { + inst.Close(ctx) + + delete(registry.instances, key) + delete(registry.lastused, inst) + } + } +} + +func (registry *OpenPolicyAgentRegistry) startCleanerDaemon() { + ticker := time.NewTicker(registry.cleanInterval) + defer ticker.Stop() + + for { + select { + case <-registry.quit: + return + case t := <-ticker.C: + registry.cleanUnusedInstances(t) + } + } +} + +// Do implements routing.PostProcessor and cleans unused OPA instances +func (registry *OpenPolicyAgentRegistry) Do(routes []*routing.Route) []*routing.Route { + inUse := make(map[*OpenPolicyAgentInstance]struct{}) + + for _, ri := range routes { + for _, fi := range ri.Filters { + if ff, ok := fi.Filter.(OpenPolicyAgentFilter); ok { + inUse[ff.OpenPolicyAgent()] = struct{}{} + } + } + } + + registry.markUnused(inUse) + + return routes +} + +func (registry *OpenPolicyAgentRegistry) NewOpenPolicyAgentInstance(bundleName string, config OpenPolicyAgentInstanceConfig, filterName string) (*OpenPolicyAgentInstance, error) { + registry.mu.Lock() + defer registry.mu.Unlock() + + if registry.closed { + return nil, fmt.Errorf("open policy agent registry is already closed") + } + + if instance, ok := registry.instances[bundleName]; ok { + delete(registry.lastused, instance) + return instance, nil + } + + instance, err := registry.newOpenPolicyAgentInstance(bundleName, config, filterName) + if err != nil { + return nil, err + } + registry.instances[bundleName] = instance + + return instance, nil +} + +func (registry *OpenPolicyAgentRegistry) markUnused(inUse map[*OpenPolicyAgentInstance]struct{}) error { + registry.mu.Lock() + defer registry.mu.Unlock() + + for _, instance := range registry.instances { + if _, ok := inUse[instance]; !ok { + registry.lastused[instance] = time.Now() + } + } + + return nil +} + +func (registry *OpenPolicyAgentRegistry) newOpenPolicyAgentInstance(bundleName string, config OpenPolicyAgentInstanceConfig, filterName string) (*OpenPolicyAgentInstance, error) { + runtime.RegisterPlugin(envoy.PluginName, envoy.Factory{}) + + configBytes, err := interpolateConfigTemplate(config.configTemplate, bundleName) + if err != nil { + return nil, err + } + + engine, err := New(inmem.New(), configBytes, config, filterName, bundleName) + if err != nil { + return nil, err + } + + ctx := context.Background() + if err = engine.Start(ctx); err != nil { + return nil, err + } + + err = engine.waitPluginsReady(100*time.Millisecond, 30*time.Second) + if err != nil { + engine.Logger().WithFields(map[string]interface{}{"err": err}).Error("Failed to wait for plugins activation.") + return nil, err + } + + return engine, nil +} + +type OpenPolicyAgentInstance struct { + manager *plugins.Manager + instanceConfig OpenPolicyAgentInstanceConfig + opaConfig *config.Config + bundleName string + preparedQuery *rego.PreparedEvalQuery + preparedQueryDoOnce *sync.Once + interQueryBuiltinCache iCache.InterQueryCache + once sync.Once +} + +func envVariablesMap() map[string]string { + rawEnvVariables := os.Environ() + envVariables := make(map[string]string) + + for _, item := range rawEnvVariables { + tokens := strings.SplitN(item, "=", 2) + envVariables[tokens[0]] = tokens[1] + } + + return envVariables +} + +// Config sets the configuration file to use on the OPA instance. +func interpolateConfigTemplate(configTemplate []byte, bundleName string) ([]byte, error) { + var buf bytes.Buffer + + tpl := template.Must(template.New("opa-config").Parse(string(configTemplate))) + + binding := make(map[string]interface{}) + binding["bundlename"] = bundleName + binding["Env"] = envVariablesMap() + + err := tpl.ExecuteTemplate(&buf, "opa-config", binding) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// New returns a new OPA object. +func New(store storage.Store, configBytes []byte, instanceConfig OpenPolicyAgentInstanceConfig, filterName string, bundleName string) (*OpenPolicyAgentInstance, error) { + id := uuid.New().String() + + opaConfig, err := config.ParseConfig(configBytes, id) + if err != nil { + return nil, err + } + + runtime.RegisterPlugin(envoy.PluginName, envoy.Factory{}) + + 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)) + if err != nil { + return nil, err + } + + discovery, err := discovery.New(manager, discovery.Factories(map[string]plugins.Factory{envoy.PluginName: envoy.Factory{}})) + if err != nil { + return nil, err + } + + manager.Register("discovery", discovery) + + opa := &OpenPolicyAgentInstance{ + instanceConfig: instanceConfig, + manager: manager, + opaConfig: opaConfig, + bundleName: bundleName, + + preparedQueryDoOnce: new(sync.Once), + interQueryBuiltinCache: iCache.NewInterQueryCache(manager.InterQueryBuiltinCacheConfig()), + } + + manager.RegisterCompilerTrigger(opa.compilerUpdated) + + return opa, nil +} + +// Start asynchronously starts the policy engine's plugins that download +// policies, report status, etc. +func (opa *OpenPolicyAgentInstance) Start(ctx context.Context) error { + return opa.manager.Start(ctx) +} + +func (opa *OpenPolicyAgentInstance) Close(ctx context.Context) { + opa.once.Do(func() { + opa.manager.Stop(ctx) + }) +} + +func (opa *OpenPolicyAgentInstance) waitPluginsReady(checkInterval, timeout time.Duration) error { + if timeout <= 0 { + return nil + } + + // check readiness of all plugins + pluginsReady := func() bool { + for _, status := range opa.manager.PluginStatus() { + if status != nil && status.State != plugins.StateOK { + return false + } + } + return true + } + + opa.Logger().Debug("Waiting for plugins activation (%v).", timeout) + + return opautil.WaitFunc(pluginsReady, checkInterval, timeout) +} + +func (opa *OpenPolicyAgentInstance) InstanceConfig() *OpenPolicyAgentInstanceConfig { + return &opa.instanceConfig +} + +func (opa *OpenPolicyAgentInstance) compilerUpdated(txn storage.Transaction) { + opa.preparedQueryDoOnce = new(sync.Once) +} + +func (opa *OpenPolicyAgentInstance) EnvoyPluginConfig() envoy.PluginConfig { + if plugin, ok := opa.manager.Plugin(envoy.PluginName).(*envoy.Plugin); ok { + return plugin.GetConfig() + } + + defaultConfig := envoy.PluginConfig{ + Path: "envoy/authz/allow", + DryRun: false, + } + defaultConfig.ParseQuery() + return defaultConfig +} + +func (opa *OpenPolicyAgentInstance) startSpanFromContextWithTracer(tr opentracing.Tracer, parent opentracing.Span, ctx context.Context) (opentracing.Span, context.Context) { + + var span opentracing.Span + if parent != nil { + span = tr.StartSpan("open-policy-agent", opentracing.ChildOf(parent.Context())) + } else { + span = tracing.CreateSpan("open-policy-agent", ctx, tr) + } + + span.SetTag("opa.bundle_name", opa.bundleName) + + for label, value := range opa.manager.Labels() { + span.SetTag("opa.label."+label, value) + } + + return span, opentracing.ContextWithSpan(ctx, span) +} + +func (opa *OpenPolicyAgentInstance) StartSpanFromFilterContext(fc filters.FilterContext) (opentracing.Span, context.Context) { + return opa.startSpanFromContextWithTracer(fc.Tracer(), fc.ParentSpan(), fc.Request().Context()) +} + +func (opa *OpenPolicyAgentInstance) StartSpanFromContext(ctx context.Context) (opentracing.Span, context.Context) { + span := opentracing.SpanFromContext(ctx) + if span != nil { + return opa.startSpanFromContextWithTracer(span.Tracer(), span, ctx) + } + + return opa.startSpanFromContextWithTracer(opentracing.GlobalTracer(), nil, ctx) +} + +func (opa *OpenPolicyAgentInstance) MetricsKey(key string) string { + return key + "." + opa.bundleName +} + +// ParsedQuery is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) ParsedQuery() ast.Body { + return opa.EnvoyPluginConfig().ParsedQuery +} + +// Store is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) Store() storage.Store { return opa.manager.Store } + +// Compiler is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) Compiler() *ast.Compiler { return opa.manager.GetCompiler() } + +// Runtime is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) Runtime() *ast.Term { return opa.manager.Info } + +// Logger is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) Logger() logging.Logger { return opa.manager.Logger() } + +// PreparedQueryDoOnce is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) PreparedQueryDoOnce() *sync.Once { return opa.preparedQueryDoOnce } + +// InterQueryBuiltinCache is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) InterQueryBuiltinCache() iCache.InterQueryCache { + return opa.interQueryBuiltinCache +} + +// PreparedQuery is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) PreparedQuery() *rego.PreparedEvalQuery { return opa.preparedQuery } + +// SetPreparedQuery is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) SetPreparedQuery(q *rego.PreparedEvalQuery) { + opa.preparedQuery = q +} + +// Config is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) Config() *config.Config { return opa.opaConfig } + +// DistributedTracing is an implementation of the envoyauth.EvalContext interface +func (opa *OpenPolicyAgentInstance) DistributedTracing() opatracing.Options { + return opatracing.NewOptions(opa) +} + +// logging.Logger that does not pollute info with debug logs +type QuietLogger struct { + target logging.Logger +} + +func (l *QuietLogger) WithFields(fields map[string]interface{}) logging.Logger { + return &QuietLogger{target: l.target.WithFields(fields)} +} + +func (l *QuietLogger) SetLevel(level logging.Level) { + l.target.SetLevel(level) +} + +func (l *QuietLogger) GetLevel() logging.Level { + return l.target.GetLevel() +} + +func (l *QuietLogger) Debug(fmt string, a ...interface{}) { + l.target.Debug(fmt, a) +} + +func (l *QuietLogger) Info(fmt string, a ...interface{}) { + l.target.Debug(fmt, a) +} + +func (l *QuietLogger) Error(fmt string, a ...interface{}) { + l.target.Error(fmt, a) +} + +func (l *QuietLogger) Warn(fmt string, a ...interface{}) { + l.target.Warn(fmt, a) +} diff --git a/filters/openpolicyagent/openpolicyagent_test.go b/filters/openpolicyagent/openpolicyagent_test.go new file mode 100644 index 0000000000..4cbf9f5f2e --- /dev/null +++ b/filters/openpolicyagent/openpolicyagent_test.go @@ -0,0 +1,336 @@ +package openpolicyagent + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "strconv" + "testing" + "time" + + ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + 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" + opasdktest "github.com/open-policy-agent/opa/sdk/test" + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/filters" + "github.com/zalando/skipper/filters/filtertest" + "github.com/zalando/skipper/filters/openpolicyagent/internal/envoy" + "github.com/zalando/skipper/metrics/metricstest" + "github.com/zalando/skipper/routing" + "github.com/zalando/skipper/tracing/tracingtest" + "google.golang.org/protobuf/encoding/protojson" +) + +type MockOpenPolicyAgentFilter struct { + opa *OpenPolicyAgentInstance +} + +func (f *MockOpenPolicyAgentFilter) OpenPolicyAgent() *OpenPolicyAgentInstance { + return f.opa +} + +func (f *MockOpenPolicyAgentFilter) Request(filters.FilterContext) {} + +func (f *MockOpenPolicyAgentFilter) Response(filters.FilterContext) {} + +func TestInterpolateTemplate(t *testing.T) { + os.Setenv("CONTROL_PLANE_TOKEN", "testtoken") + interpolatedConfig, err := interpolateConfigTemplate([]byte(` + token: {{.Env.CONTROL_PLANE_TOKEN }} + bundle: {{ .bundlename }} + `), + "helloBundle") + + assert.NoError(t, err) + + assert.Equal(t, ` + token: testtoken + bundle: helloBundle + `, string(interpolatedConfig)) + +} + +func TestLoadEnvoyMetadata(t *testing.T) { + cfg := &OpenPolicyAgentInstanceConfig{} + WithEnvoyMetadataBytes([]byte(` + { + "filter_metadata": { + "envoy.filters.http.header_to_metadata": { + "policy_type": "ingress" + } + } + } + `))(cfg) + + expectedBytes, err := protojson.Marshal(&ext_authz_v3_core.Metadata{ + FilterMetadata: map[string]*_struct.Struct{ + "envoy.filters.http.header_to_metadata": { + Fields: map[string]*_struct.Value{ + "policy_type": { + Kind: &_struct.Value_StringValue{StringValue: "ingress"}, + }, + }, + }, + }, + }) + + assert.NoError(t, err) + + expected := &ext_authz_v3_core.Metadata{} + err = protojson.Unmarshal(expectedBytes, expected) + assert.NoError(t, err) + + assert.Equal(t, expected, cfg.envoyMetadata) +} + +func mockControlPlane() (*opasdktest.Server, []byte) { + opaControlPlane := opasdktest.MustNewServer( + opasdktest.MockBundle("/bundles/test", map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + `, + }), + opasdktest.MockBundle("/bundles/anotherbundlename", map[string]string{ + "main.rego": ` + package envoy.authz + + default allow = false + `, + }), + ) + + config := []byte(fmt.Sprintf(`{ + "services": { + "test": { + "url": %q + } + }, + "bundles": { + "test": { + "resource": "/bundles/{{ .bundlename }}" + } + }, + "plugins": { + "envoy_ext_authz_grpc": { + "path": "/envoy/authz/allow", + "dry-run": false + } + } + }`, opaControlPlane.URL())) + + return opaControlPlane, config +} + +func TestRegistry(t *testing.T) { + _, config := mockControlPlane() + + registry := NewOpenPolicyAgentRegistry(WithReuseDuration(1*time.Second), WithCleanInterval(1*time.Second)) + + cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config)) + assert.NoError(t, err) + + inst1, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + + registry.markUnused(map[*OpenPolicyAgentInstance]struct{}{}) + + inst2, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + assert.Equal(t, inst1, inst2, "same instance is reused after release") + + inst3, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + assert.Equal(t, inst2, inst3, "same instance is reused multiple times") + + registry.markUnused(map[*OpenPolicyAgentInstance]struct{}{}) + + //Allow clean up + time.Sleep(2 * time.Second) + + inst_different_bundle, err := registry.NewOpenPolicyAgentInstance("anotherbundlename", *cfg, "testfilter") + assert.NoError(t, err) + + inst4, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + assert.NotEqual(t, inst1, inst4, "after cleanup a new instance should be created") + + //Trigger cleanup via post processor + registry.Do([]*routing.Route{ + { + Filters: []*routing.RouteFilter{{Filter: &MockOpenPolicyAgentFilter{opa: inst_different_bundle}}}, + }, + }) + + // Allow clean up + time.Sleep(2 * time.Second) + + inst5, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + assert.NotEqual(t, inst4, inst5, "after cleanup a new instance should be created") + + registry.Close() + + _, err = registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.Error(t, err, "should not work after close") +} + +func TestStartup(t *testing.T) { + _, config := mockControlPlane() + + registry := NewOpenPolicyAgentRegistry(WithReuseDuration(1*time.Second), WithCleanInterval(1*time.Second)) + + cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config)) + assert.NoError(t, err) + + inst1, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + + target := envoy.PluginConfig{Path: "/envoy/authz/allow", DryRun: false} + target.ParseQuery() + assert.Equal(t, target, inst1.EnvoyPluginConfig()) +} + +func TestTracing(t *testing.T) { + _, config := mockControlPlane() + + registry := NewOpenPolicyAgentRegistry(WithReuseDuration(1*time.Second), WithCleanInterval(1*time.Second)) + + cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config)) + assert.NoError(t, err) + + inst, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + + tracer := &tracingtest.Tracer{} + parent := tracer.StartSpan("start_span") + ctx := opentracing.ContextWithSpan(context.Background(), parent) + span, _ := inst.StartSpanFromContext(ctx) + span.Finish() + parent.Finish() + + recspan, ok := tracer.FindSpan("open-policy-agent") + assert.True(t, ok, "No span was created for open policy agent") + assert.Equal(t, map[string]interface{}{"opa.bundle_name": "test", "opa.label.id": inst.manager.Labels()["id"], "opa.label.version": inst.manager.Labels()["version"]}, recspan.Tags) +} + +func TestEval(t *testing.T) { + _, config := mockControlPlane() + + registry := NewOpenPolicyAgentRegistry(WithReuseDuration(1*time.Second), WithCleanInterval(1*time.Second)) + + cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config)) + assert.NoError(t, err) + + inst, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + + tracer := &tracingtest.Tracer{} + span := tracer.StartSpan("open-policy-agent") + ctx := opentracing.ContextWithSpan(context.Background(), span) + + result, err := inst.Eval(ctx, &authv3.CheckRequest{}) + assert.NoError(t, err) + + allowed, err := result.IsAllowed() + assert.NoError(t, err) + assert.False(t, allowed) + + span.Finish() + testspan, ok := tracer.FindSpan("open-policy-agent") + assert.True(t, ok) + assert.Equal(t, result.DecisionID, testspan.Tags["opa.decision_id"]) +} + +func TestResponses(t *testing.T) { + _, config := mockControlPlane() + + registry := NewOpenPolicyAgentRegistry(WithReuseDuration(1*time.Second), WithCleanInterval(1*time.Second)) + + cfg, err := NewOpenPolicyAgentConfig(WithConfigTemplate(config)) + assert.NoError(t, err) + + inst, err := registry.NewOpenPolicyAgentInstance("test", *cfg, "testfilter") + assert.NoError(t, err) + + tracer := &tracingtest.Tracer{} + span := tracer.StartSpan("open-policy-agent") + metrics := &metricstest.MockMetrics{} + + fc := &filtertest.Context{FMetrics: metrics} + + inst.ServeInvalidDecisionError(fc, span, &envoyauth.EvalResult{}, fmt.Errorf("something happened")) + assert.True(t, fc.FServed) + assert.Equal(t, fc.FResponse.StatusCode, http.StatusInternalServerError) + metrics.WithCounters(func(counters map[string]int64) { + assert.Equal(t, int64(1), counters["decision.err.test"]) + }) + span.Finish() + testspan, ok := tracer.FindSpan("open-policy-agent") + assert.True(t, ok, "span not found") + assert.Contains(t, testspan.Tags, "error") + + fc = &filtertest.Context{FMetrics: metrics} + inst.ServeInvalidDecisionError(fc, span, nil, fmt.Errorf("something happened")) + assert.True(t, fc.FServed) + assert.Equal(t, fc.FResponse.StatusCode, http.StatusInternalServerError) + metrics.WithCounters(func(counters map[string]int64) { + assert.Equal(t, int64(2), counters["decision.err.test"]) + }) + + fc = &filtertest.Context{FMetrics: metrics} + inst.ServeResponse(fc, span, &envoyauth.EvalResult{ + Decision: map[string]interface{}{ + "http_status": json.Number(strconv.Itoa(http.StatusOK)), + "headers": map[string]interface{}{ + "someheader": "somevalue", + }, + "body": "Welcome!", + }, + }) + assert.True(t, fc.FServed) + assert.Equal(t, fc.FResponse.StatusCode, http.StatusOK) + assert.Equal(t, fc.FResponse.Header, http.Header{ + "Someheader": {"somevalue"}, + }) + body, err := io.ReadAll(fc.FResponse.Body) + assert.NoError(t, err) + assert.Equal(t, string(body), "Welcome!") + + fc = &filtertest.Context{FMetrics: metrics} + inst.ServeResponse(fc, span, &envoyauth.EvalResult{ + Decision: map[string]interface{}{ + "headers": "invalid header type", + "body": "Welcome!", + }, + }) + assert.True(t, fc.FServed) + assert.Equal(t, fc.FResponse.StatusCode, http.StatusInternalServerError) + + fc = &filtertest.Context{FMetrics: metrics} + inst.ServeResponse(fc, span, &envoyauth.EvalResult{ + Decision: map[string]interface{}{ + "body": map[string]interface{}{ + "invalid": "body type", + }, + }, + }) + assert.True(t, fc.FServed) + assert.Equal(t, fc.FResponse.StatusCode, http.StatusInternalServerError) + + fc = &filtertest.Context{FMetrics: metrics} + inst.ServeResponse(fc, span, &envoyauth.EvalResult{ + Decision: map[string]interface{}{ + "http_status": "invalid status code", + }, + }) + assert.True(t, fc.FServed) + assert.Equal(t, fc.FResponse.StatusCode, http.StatusInternalServerError) +} diff --git a/filters/openpolicyagent/response.go b/filters/openpolicyagent/response.go new file mode 100644 index 0000000000..df613b1716 --- /dev/null +++ b/filters/openpolicyagent/response.go @@ -0,0 +1,80 @@ +package openpolicyagent + +import ( + "bytes" + "io" + + "net/http" + + "github.com/open-policy-agent/opa-envoy-plugin/envoyauth" + "github.com/opentracing/opentracing-go" + "github.com/zalando/skipper/filters" +) + +func (opa *OpenPolicyAgentInstance) ServeInvalidDecisionError(fc filters.FilterContext, span opentracing.Span, result *envoyauth.EvalResult, err error) { + opa.HandleInvalidDecisionError(fc, span, result, err, true) +} + +func (opa *OpenPolicyAgentInstance) HandleInvalidDecisionError(fc filters.FilterContext, span opentracing.Span, result *envoyauth.EvalResult, err error, serve bool) { + fc.Metrics().IncCounter(opa.MetricsKey("decision.err")) + span.SetTag("error", true) + + if result != nil { + span.LogKV( + "event", "error", + "opa.decision_id", result.DecisionID, + "message", err.Error(), + ) + + opa.Logger().WithFields(map[string]interface{}{ + "decision": result.Decision, + "err": err, + "decision_id": result.DecisionID, + }).Info("Rejecting request because of an invalid decision") + } else { + span.LogKV( + "event", "error", + "message", err.Error(), + ) + + opa.Logger().WithFields(map[string]interface{}{ + "err": err, + }).Info("Rejecting request because of an invalid decision") + } + + if serve { + resp := http.Response{} + resp.StatusCode = http.StatusInternalServerError + + fc.Serve(&resp) + } +} + +func (opa *OpenPolicyAgentInstance) ServeResponse(fc filters.FilterContext, span opentracing.Span, result *envoyauth.EvalResult) { + resp := http.Response{} + + var err error + resp.StatusCode, err = result.GetResponseHTTPStatus() + if err != nil { + opa.ServeInvalidDecisionError(fc, span, result, err) + return + } + + resp.Header, err = result.GetResponseHTTPHeaders() + if err != nil { + opa.ServeInvalidDecisionError(fc, span, result, err) + return + } + + if result.HasResponseBody() { + body, err := result.GetResponseBody() + if err != nil { + opa.ServeInvalidDecisionError(fc, span, result, err) + return + } + + resp.Body = io.NopCloser(bytes.NewReader([]byte(body))) + } + + fc.Serve(&resp) +} diff --git a/filters/openpolicyagent/tracing.go b/filters/openpolicyagent/tracing.go new file mode 100644 index 0000000000..d0b6b60244 --- /dev/null +++ b/filters/openpolicyagent/tracing.go @@ -0,0 +1,46 @@ +package openpolicyagent + +import ( + "net/http" + + opatracing "github.com/open-policy-agent/opa/tracing" + "github.com/opentracing/opentracing-go" +) + +func init() { + opatracing.RegisterHTTPTracing(&tracingFactory{}) +} + +type tracingFactory struct{} + +type transport struct { + opa *OpenPolicyAgentInstance + wrapped http.RoundTripper +} + +func (*tracingFactory) NewTransport(tr http.RoundTripper, opts opatracing.Options) http.RoundTripper { + return &transport{ + opa: opts[0].(*OpenPolicyAgentInstance), + wrapped: tr, + } +} + +func (*tracingFactory) NewHandler(f http.Handler, label string, opts opatracing.Options) http.Handler { + return f +} + +func (tr *transport) RoundTrip(req *http.Request) (*http.Response, error) { + ctx := req.Context() + parentSpan := opentracing.SpanFromContext(ctx) + + if parentSpan != nil { + span := parentSpan.Tracer().StartSpan("http.send", opentracing.ChildOf(parentSpan.Context())) + defer span.Finish() + req = req.WithContext(opentracing.ContextWithSpan(ctx, span)) + + carrier := opentracing.HTTPHeadersCarrier(req.Header) + span.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, carrier) + } + + return tr.wrapped.RoundTrip(req) +} diff --git a/filters/openpolicyagent/tracing_test.go b/filters/openpolicyagent/tracing_test.go new file mode 100644 index 0000000000..aad4f77ad4 --- /dev/null +++ b/filters/openpolicyagent/tracing_test.go @@ -0,0 +1,41 @@ +package openpolicyagent + +import ( + "context" + "net/http" + "testing" + + opatracing "github.com/open-policy-agent/opa/tracing" + "github.com/opentracing/opentracing-go" + "github.com/stretchr/testify/assert" + "github.com/zalando/skipper/tracing/tracingtest" +) + +type MockTransport struct { +} + +func (t *MockTransport) RoundTrip(*http.Request) (*http.Response, error) { + return &http.Response{}, nil +} + +func TestTracingFactory(t *testing.T) { + f := &tracingFactory{} + + tr := f.NewTransport(&MockTransport{}, opatracing.Options{&OpenPolicyAgentInstance{}}) + + tracer := &tracingtest.Tracer{} + span := tracer.StartSpan("open-policy-agent") + ctx := opentracing.ContextWithSpan(context.Background(), span) + + req := &http.Request{ + Header: map[string][]string{}, + } + req = req.WithContext(ctx) + + _, err := tr.RoundTrip(req) + assert.NoError(t, err) + + span.Finish() + _, ok := tracer.FindSpan("http.send") + assert.True(t, ok, "No http.send span was created") +} diff --git a/go.mod b/go.mod index 8927bbc177..b60c9d3ce0 100644 --- a/go.mod +++ b/go.mod @@ -16,14 +16,19 @@ require ( github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 + github.com/envoyproxy/go-control-plane v0.11.1 github.com/ghodss/yaml v1.0.0 github.com/golang-jwt/jwt/v4 v4.5.0 + github.com/golang/protobuf v1.5.3 github.com/google/go-cmp v0.5.9 + github.com/google/uuid v1.3.0 github.com/hashicorp/memberlist v0.5.0 github.com/instana/go-sensor v1.55.2 github.com/lightstep/lightstep-tracer-go v0.26.0 github.com/miekg/dns v1.1.55 github.com/oklog/ulid v1.3.1 + github.com/open-policy-agent/opa v0.55.0 + github.com/open-policy-agent/opa-envoy-plugin v0.55.0-envoy github.com/opentracing/basictracer-go v1.1.0 github.com/opentracing/opentracing-go v1.2.0 github.com/prometheus/client_golang v1.16.0 @@ -48,34 +53,57 @@ require ( golang.org/x/sync v0.3.0 golang.org/x/term v0.11.0 golang.org/x/time v0.3.0 + google.golang.org/protobuf v1.31.0 gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/yaml.v2 v2.4.0 layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf + ) require ( cloud.google.com/go/compute v1.20.1 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect dario.cat/mergo v1.0.0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/agnivade/levenshtein v1.1.1 // indirect github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b // indirect - github.com/cenkalti/backoff/v4 v4.2.0 // indirect + github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 // indirect github.com/containerd/containerd v1.7.3 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgraph-io/badger/v3 v3.2103.5 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 // indirect github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker v24.0.5+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.1 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/glog v1.1.0 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-immutable-radix v1.0.0 // indirect github.com/hashicorp/go-msgpack v0.5.3 // indirect @@ -88,40 +116,60 @@ require ( github.com/looplab/fsm v1.0.1 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.0 // indirect + github.com/mattn/go-runewidth v0.0.13 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/moby/locker v1.0.1 // indirect github.com/moby/patternmatcher v0.5.0 // indirect github.com/moby/sys/sequential v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.19.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc4 // indirect github.com/opencontainers/runc v1.1.5 // indirect + github.com/peterh/liner v1.2.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect - github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d // indirect + github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/yashtewari/glob-intersection v0.2.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect + go.opentelemetry.io/otel v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 // indirect + go.opentelemetry.io/otel/metric v1.16.0 // indirect + go.opentelemetry.io/otel/sdk v1.16.0 // indirect + go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/atomic v1.9.0 // indirect + go.uber.org/automaxprocs v1.5.3 // indirect golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect - golang.org/x/mod v0.9.0 // indirect + golang.org/x/mod v0.12.0 // indirect golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.11.0 // indirect gonum.org/v1/gonum v0.8.2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect google.golang.org/grpc v1.57.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + oras.land/oras-go/v2 v2.2.1 // indirect ) go 1.19 diff --git a/go.sum b/go.sum index 049b91627f..0278b0b096 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,44 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= github.com/AlexanderYastrebov/noleak v0.0.0-20230711175737-345842f874fb h1:ZG/Y3/ecDXt8GFY6ZWRgP5COaph3dwoXRvTc7Mz0L7U= github.com/AlexanderYastrebov/noleak v0.0.0-20230711175737-345842f874fb/go.mod h1:Ac8KyJXsCfx2Gb9h/Eb6SUYk2tQ9At1ICaBm/1mipJQ= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= @@ -20,11 +52,20 @@ github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x9 github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM= +github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= +github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= +github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/aryszka/jobqueue v0.0.2 h1:LYPhzklo0XFpVF+QtzfP9XRQPEsbJ2EW5Pur6pxxaS4= @@ -35,27 +76,51 @@ github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDf github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HVHpXvjfy0Dy7g6fuA= +github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9 h1:rdWOzitWlNYeUsXmz+IQfa9NkGEq3gA/qQ3mOEqBU6o= github.com/cjoudrey/gluahttp v0.0.0-20201111170219-25003d9adfa9/go.mod h1:X97UjDTXp+7bayQSFZk2hPvCTmTZIicUjZQRtkwgAKY= github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199 h1:cJ1E8ZwZLfercTX3dywnCAQDilbbi+m2cw3+8tCFpRo= github.com/cjoudrey/gluaurl v0.0.0-20161028222611-31cbb9bef199/go.mod h1:jC+zrjHA5CaxJzn+tojIoIOzSp/6BlkRWXnMlxNkB+g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.7.3 h1:cKwYKkP1eTj54bP3wCdXXBymmKRQMrWjkLSWZZJDa8o= github.com/containerd/containerd v1.7.3/go.mod h1:32FOM4/O0RkNg7AjQj3hDzN9cUGtu+HMvaKUNiqCZB8= +github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -66,6 +131,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= +github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= +github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654 h1:XOPLOMn/zT4jIgxfxSsoXPxkrzz0FaCHwp33x5POJ+Q= github.com/dgryski/go-gk v0.0.0-20200319235926-a69029f61654/go.mod h1:qm+vckxRlDt0aOla0RYJJVeqHZlWfOm2UIxHaqPB46E= github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 h1:0wH6nO9QEa02Qx8sIQGw6ieKdz+BXjpccSOo9vXNl4U= @@ -74,6 +145,8 @@ github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1 h1:De28BM16VaAD github.com/dgryski/go-mpchash v0.0.0-20200819201138-7382f34c4cd1/go.mod h1:ut6ck43wTobSb/Jvxo/5uzEEtrfU4gj4m0vh02v7GuQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= +github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZsmLR/+RGffQSXwEkXgfLSA08qDn9AI= github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= @@ -85,16 +158,45 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.1 h1:kt9FtLiooDc0vbwTLhdg3dyNX1K9Qwa1EK9LcD4jVUQ= +github.com/envoyproxy/protoc-gen-validate v1.0.1/go.mod h1:0vj8bNkYbSTNS2PIyH87KZaeN4x9zpL9Qt8fQC7d+vs= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-restit/lzjson v0.0.0-20161206095556-efe3c53acc68/go.mod h1:7vXSKQt83WmbPeyVjCfNT9YDJ5BUFmcwFsEjI9SCvYM= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -105,35 +207,82 @@ github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOW github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -151,20 +300,27 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/tdigest v0.0.1 h1:XpFptwYmnEKUqmkcDjrzffswZ3nvNeevbUSLPP/ZzIY= github.com/influxdata/tdigest v0.0.1/go.mod h1:Z0kXnxzbTC2qrx4NaIzYkE1k66+6oEDQTvL95hQFh5Y= github.com/instana/go-sensor v1.55.2 h1:XdLN38mSFsHpaL+jIDkE/ZrW7pxgPeUC/bV9bSwVHyM= github.com/instana/go-sensor v1.55.2/go.mod h1:Ks06EG9Da5O3hbdJiHIePG/vNmToovkaJjMlUBd70Yc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -177,18 +333,28 @@ github.com/lightstep/lightstep-tracer-go v0.26.0 h1:ZOw8meo7+7SvvUWrL0c4IRr3bd4Y github.com/lightstep/lightstep-tracer-go v0.26.0/go.mod h1:+H6HJI7VlzXOAyxt5a/ZhsOUFbBU89BTMrBFEWSWGoY= github.com/looplab/fsm v1.0.1 h1:OEW0ORrIx095N/6lgoGkFkotqH6s7vaFPsgjLAaF5QU= github.com/looplab/fsm v1.0.1/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -202,6 +368,8 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -212,6 +380,10 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/open-policy-agent/opa v0.55.0 h1:s7Vm4ph6zDqqP/KzvUSw9fsKVsm9lhbTZhYGxxTK7mo= +github.com/open-policy-agent/opa v0.55.0/go.mod h1:2Vh8fj/bXCqSwGMbBiHGrw+O8yrho6T/fdaHt5ROmaQ= +github.com/open-policy-agent/opa-envoy-plugin v0.55.0-envoy h1:5hgZ+s8MFKA5DBNgsWs0nDJ0egE7p/aUSlGSfuokgQU= +github.com/open-policy-agent/opa-envoy-plugin v0.55.0-envoy/go.mod h1:KAM7MoXtuuu2/do4Gm4Ut3nCAT+O/hAyslI0pdOKc4Y= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= @@ -228,16 +400,21 @@ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+ github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= +github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= @@ -246,7 +423,12 @@ github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5X github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091 h1:L644WnBAUw4546Wrt52yzuSPoV24t0ArlMwc5iRr8U0= github.com/sarslanhan/cronmask v0.0.0-20230801193303-54e29300a091/go.mod h1:qZKxttzn8iyVLtc7edFrmQper3FUBJsc/rHCONN2wIQ= @@ -262,6 +444,15 @@ github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYl github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d h1:X4+kt6zM/OVO6gbJdAfJR60MGPsqCzbtXNnjoGqdfAs= github.com/streadway/quantile v0.0.0-20220407130108-4246515d968d/go.mod h1:lbP8tGiBjZ5YWIc2fzuRpTaz0b/53vT6PEs3QuAWzuU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -282,6 +473,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/szuecs/rate-limit-buffer v0.9.0 h1:65fBCVsaJFh0E1G5C6/sInEPlYR6dXtF9J9bAv48lLg= github.com/szuecs/rate-limit-buffer v0.9.0/go.mod h1:BxqrsmnHsCnWcvbtdcaDLEBmjNEvRFU5LQ8edoZ9B0M= +github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= +github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/testcontainers/testcontainers-go v0.22.1-0.20230806163346-d1c291e3c8e1 h1:iwspfBu8RJQeMGfkBvR+9sMoia/4CK0ndFNpuJgXEBQ= github.com/testcontainers/testcontainers-go v0.22.1-0.20230806163346-d1c291e3c8e1/go.mod h1:k0YiPa26xJCRUbUkYqy5rY6NGvSbVCeUBXCvucscBR4= github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= @@ -296,22 +489,63 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= +github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yookoala/gofast v0.7.0 h1:wVqXc+S0FDmlkieRNDxabGRX44znHT++Hb9lEfWi4iM= github.com/yookoala/gofast v0.7.0/go.mod h1:OJU201Q6HCaE1cASckaTbMm3KB6e0cZxK0mgqfwOKvQ= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= +go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= +go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 h1:t4ZwRPU+emrcvM2e9DHd0Fsf0JTPVcbfa/BhTDF03d0= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0/go.mod h1:vLarbg68dH2Wa77g71zmKQqlQ8+8Rq3GRG31uc0WcWI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0 h1:cbsD4cUcviQGXdw8+bo5x2wazq10SKz8hEbtCRPcU78= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.16.0/go.mod h1:JgXSGah17croqhJfhByOLVY719k1emAXC8MVhCIJlRs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0 h1:TVQp/bboR4mhZSav+MdgXB8FaRho1RC8UwVn3T0vjVc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.16.0/go.mod h1:I33vtIe0sR96wfrUcilIzLoA3mLHhRmz9S9Te0S3gDo= +go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= +go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= +go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= +go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= +go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= +go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab h1:+yW1yrZ09EYNu1spCUOHBBNRbrLnfmutwyhbhCv3b6Q= go4.org/netipx v0.0.0-20220925034521-797b0c90d8ab/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -322,7 +556,14 @@ golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= @@ -330,33 +571,66 @@ golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMx golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -365,46 +639,78 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -414,17 +720,50 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200908211811-12e1bf57a112/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -436,13 +775,62 @@ gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/netlib v0.0.0-20181029234149-ec6d1f5cefe6/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= @@ -452,6 +840,21 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go. google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -459,15 +862,22 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.38.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= @@ -476,6 +886,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -485,6 +896,17 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf h1:rRz0YsF7VXj9fXRF6yQgFI7DzST+hsI3TeFSGupntu0= layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf/go.mod h1:ivKkcY8Zxw5ba0jldhZCYYQfGdb2K6u9tbYK1AwMIBc= +oras.land/oras-go/v2 v2.2.1 h1:3VJTYqy5KfelEF9c2jo1MLSpr+TM3mX8K42wzZcd6qE= +oras.land/oras-go/v2 v2.2.1/go.mod h1:GeAwLuC4G/JpNwkd+bSZ6SkDMGaaYglt6YK2WvZP7uQ= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/skipper.go b/skipper.go index c331d999d6..1f4af8f91a 100644 --- a/skipper.go +++ b/skipper.go @@ -36,6 +36,9 @@ import ( "github.com/zalando/skipper/filters/builtin" "github.com/zalando/skipper/filters/fadein" logfilter "github.com/zalando/skipper/filters/log" + "github.com/zalando/skipper/filters/openpolicyagent" + "github.com/zalando/skipper/filters/openpolicyagent/opaauthorizerequest" + "github.com/zalando/skipper/filters/openpolicyagent/opaserveresponse" ratelimitfilters "github.com/zalando/skipper/filters/ratelimit" "github.com/zalando/skipper/filters/shedder" teefilters "github.com/zalando/skipper/filters/tee" @@ -892,6 +895,11 @@ type Options struct { // defaults to "file","inline" and "none" disables lua // filters. LuaSources []string + + EnableOpenPolicyAgent bool + OpenPolicyAgentConfigTemplate string + OpenPolicyAgentEnvoyMetadata string + OpenPolicyAgentCleanerInterval time.Duration } func (o *Options) KubernetesDataClientOptions() kubernetes.Options { @@ -1753,6 +1761,23 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ) } + var opaRegistry *openpolicyagent.OpenPolicyAgentRegistry + if o.EnableOpenPolicyAgent { + opaRegistry = openpolicyagent.NewOpenPolicyAgentRegistry(openpolicyagent.WithCleanInterval(o.OpenPolicyAgentCleanerInterval)) + defer opaRegistry.Close() + + opts := make([]func(*openpolicyagent.OpenPolicyAgentInstanceConfig) error, 0) + opts = append(opts, openpolicyagent.WithConfigTemplateFile(o.OpenPolicyAgentConfigTemplate)) + if o.OpenPolicyAgentEnvoyMetadata != "" { + opts = append(opts, openpolicyagent.WithEnvoyMetadataFile(o.OpenPolicyAgentEnvoyMetadata)) + } + + o.CustomFilters = append(o.CustomFilters, + opaauthorizerequest.NewOpaAuthorizeRequestSpec(opaRegistry, opts...), + opaserveresponse.NewOpaServeResponseSpec(opaRegistry, opts...), + ) + } + if len(o.CompressEncodings) > 0 { compress, err := builtin.NewCompressWithOptions(builtin.CompressOptions{Encodings: o.CompressEncodings}) if err != nil { @@ -1882,6 +1907,10 @@ func run(o Options, sig chan os.Signal, idleConnsCH chan struct{}) error { ro.PreProcessors = append(ro.PreProcessors, oauthConfig.NewGrantPreprocessor()) } + if o.EnableOpenPolicyAgent { + ro.PostProcessors = append(ro.PostProcessors, opaRegistry) + } + if o.CustomRoutingPreProcessors != nil { ro.PreProcessors = append(ro.PreProcessors, o.CustomRoutingPreProcessors...) }