From a16bdd9cb01e25d7ae219780f326736e2912f169 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:39:37 -0700 Subject: [PATCH 01/12] config: support arbitrary nested config structs (#4440) Update bindEnvs() to add support for binding nested fields of the Options struct to environment variables. The variable names are formed by joining the nested fields' mapstructure tags with underscores (after first converting to uppercase). This is in preparation for adding a new nested struct for downstream mTLS settings that will look something like this: downstream_mtls: ca_file: /path/to/CA/cert.pem enforcement: reject_connection With this change, these fields would be bound to the variables DOWNSTREAM_MTLS_CA_FILE and DOWNSTREAM_MTLS_ENFORCEMENT. --- config/options.go | 77 ++++++++++++++++++++++++++------------- config/options_test.go | 81 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 132 insertions(+), 26 deletions(-) diff --git a/config/options.go b/config/options.go index 48d23d76a..6e425e44b 100644 --- a/config/options.go +++ b/config/options.go @@ -371,7 +371,7 @@ func optionsFromViper(configFile string) (*Options, error) { o := NewDefaultOptions() v := o.viper // Load up config - err := bindEnvs(o, v) + err := bindEnvs(v) if err != nil { return nil, fmt.Errorf("failed to bind options to env vars: %w", err) } @@ -506,20 +506,11 @@ func (o *Options) parseHeaders(_ context.Context) error { return nil } -// bindEnvs binds a viper instance to each env var of an Options struct based -// on the mapstructure tag -func bindEnvs(o *Options, v *viper.Viper) error { - tagName := `mapstructure` - t := reflect.TypeOf(*o) - - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - envName := field.Tag.Get(tagName) - err := v.BindEnv(envName) - if err != nil { - return fmt.Errorf("failed to bind field '%s' to env var '%s': %w", field.Name, envName, err) - } - +// bindEnvs adds a Viper environment variable binding for each field in the +// Options struct (including nested structs), based on the mapstructure tag. +func bindEnvs(v *viper.Viper) error { + if _, err := bindEnvsRecursive(reflect.TypeOf(Options{}), v, "", ""); err != nil { + return err } // Statically bind fields @@ -531,20 +522,56 @@ func bindEnvs(o *Options, v *viper.Viper) error { if err != nil { return fmt.Errorf("failed to bind field 'HeadersEnv' to env var 'HEADERS': %w", err) } - // autocert options - ao := reflect.TypeOf(o.AutocertOptions) - for i := 0; i < ao.NumField(); i++ { - field := ao.Field(i) - envName := field.Tag.Get(tagName) - err := v.BindEnv(envName) - if err != nil { - return fmt.Errorf("failed to bind field '%s' to env var '%s': %w", field.Name, envName, err) - } - } return nil } +// bindEnvsRecursive binds all fields of the provided struct type that have a +// "mapstructure" tag to corresponding environment variables, recursively. If a +// nested struct contains no fields with a "mapstructure" tag, a binding will +// be added for the struct itself (e.g. null.Bool). +func bindEnvsRecursive(t reflect.Type, v *viper.Viper, keyPrefix, envPrefix string) (bool, error) { + anyFieldHasMapstructureTag := false + for i := 0; i < t.NumField(); i++ { + field := t.Field(i) + tag, hasTag := field.Tag.Lookup("mapstructure") + if !hasTag || tag == "-" { + continue + } + + anyFieldHasMapstructureTag = true + + key, _, _ := strings.Cut(tag, ",") + keyPath := keyPrefix + key + envName := envPrefix + strings.ToUpper(key) + + if field.Type.Kind() == reflect.Struct { + newKeyPrefix := keyPath + newEnvPrefix := envName + if key != "" { + newKeyPrefix += "." + newEnvPrefix += "_" + } + nestedMapstructure, err := bindEnvsRecursive(field.Type, v, newKeyPrefix, newEnvPrefix) + if err != nil { + return false, err + } else if nestedMapstructure { + // If we've bound any nested fields from this struct, do not + // also bind this struct itself. + continue + } + } + + if key != "" { + if err := v.BindEnv(keyPath, envName); err != nil { + return false, fmt.Errorf("failed to bind field '%s' to env var '%s': %w", + field.Name, envName, err) + } + } + } + return anyFieldHasMapstructureTag, nil +} + // Validate ensures the Options fields are valid, and hydrated. func (o *Options) Validate() error { ctx := context.TODO() diff --git a/config/options_test.go b/config/options_test.go index 4748ab046..b0380b613 100644 --- a/config/options_test.go +++ b/config/options_test.go @@ -11,6 +11,8 @@ import ( "net/url" "os" "path/filepath" + "reflect" + "strings" "sync" "testing" "time" @@ -94,7 +96,7 @@ func Test_bindEnvs(t *testing.T) { t.Setenv("POMERIUM_DEBUG", "true") t.Setenv("POLICY", "LSBmcm9tOiBodHRwczovL2h0dHBiaW4ubG9jYWxob3N0LnBvbWVyaXVtLmlvCiAgdG86IAogICAgLSBodHRwOi8vbG9jYWxob3N0OjgwODEsMQo=") t.Setenv("HEADERS", `{"X-Custom-1":"foo", "X-Custom-2":"bar"}`) - err := bindEnvs(o, v) + err := bindEnvs(v) if err != nil { t.Fatalf("failed to bind options to env vars: %s", err) } @@ -117,6 +119,83 @@ func Test_bindEnvs(t *testing.T) { } } +type Foo struct { + FieldOne Bar `mapstructure:"field_one"` + FieldTwo string `mapstructure:"field_two"` +} +type Bar struct { + Baz int `mapstructure:"baz"` + Quux string `mapstructure:"quux"` +} + +func Test_bindEnvsRecursive(t *testing.T) { + v := viper.New() + _, err := bindEnvsRecursive(reflect.TypeOf(Foo{}), v, "", "") + require.NoError(t, err) + + t.Setenv("FIELD_ONE_BAZ", "123") + t.Setenv("FIELD_ONE_QUUX", "hello") + t.Setenv("FIELD_TWO", "world") + + var foo Foo + v.Unmarshal(&foo) + assert.Equal(t, Foo{ + FieldOne: Bar{ + Baz: 123, + Quux: "hello", + }, + FieldTwo: "world", + }, foo) +} + +func Test_bindEnvsRecursive_Override(t *testing.T) { + v := viper.New() + v.SetConfigType("yaml") + v.ReadConfig(strings.NewReader(` +field_one: + baz: 10 + quux: abc +field_two: hello +`)) + + // Baseline: values populated from config file. + var foo1 Foo + v.Unmarshal(&foo1) + assert.Equal(t, Foo{ + FieldOne: Bar{ + Baz: 10, + Quux: "abc", + }, + FieldTwo: "hello", + }, foo1) + + _, err := bindEnvsRecursive(reflect.TypeOf(Foo{}), v, "", "") + require.NoError(t, err) + + // Environment variables should selectively override config file keys. + t.Setenv("FIELD_ONE_QUUX", "def") + var foo2 Foo + v.Unmarshal(&foo2) + assert.Equal(t, Foo{ + FieldOne: Bar{ + Baz: 10, + Quux: "def", + }, + FieldTwo: "hello", + }, foo2) + + t.Setenv("FIELD_TWO", "world") + var foo3 Foo + v.Unmarshal(&foo3) + assert.Equal(t, Foo{ + FieldOne: Bar{ + Baz: 10, + Quux: "def", + }, + FieldTwo: "world", + }, foo3) +} + func Test_parseHeaders(t *testing.T) { // t.Parallel() tests := []struct { From 24b09186a4d9cc5573770e17995af4b6e193878b Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Tue, 8 Aug 2023 10:22:48 -0700 Subject: [PATCH 02/12] config: move mTLS settings to new struct (#4442) Move downstream mTLS settings to a nested config file object, under the key 'downstream_mtls', and add a new DownstreamMTLSSettings struct for these settings. Deprecate the existing ClientCA and ClientCAFile fields in the Options struct, but continue to honor them for now (log a warning if either is populated). Delete the ClientCRL and ClientCRLFile fields entirely (in current releases these cannot be set without causing an Envoy error, so this should not be a breaking change). Update the Settings proto to mirror this nested structure. --- authorize/authorize.go | 4 +- config/config_source.go | 3 +- config/envoyconfig/listeners.go | 10 +- config/envoyconfig/listeners_test.go | 8 +- config/mtls.go | 98 +++ config/mtls_test.go | 100 +++ config/options.go | 66 +- config/options_test.go | 26 + integration/clusters/kubernetes/compose.yml | 8 +- integration/clusters/multi/compose.yml | 8 +- integration/clusters/single/compose.yml | 2 +- integration/tpl/backends/pomerium.libsonnet | 2 +- pkg/grpc/config/config.pb.go | 720 +++++++++++--------- pkg/grpc/config/config.proto | 11 +- 14 files changed, 671 insertions(+), 395 deletions(-) create mode 100644 config/mtls.go create mode 100644 config/mtls_test.go diff --git a/authorize/authorize.go b/authorize/authorize.go index e2956021c..d20a6360f 100644 --- a/authorize/authorize.go +++ b/authorize/authorize.go @@ -94,12 +94,12 @@ func newPolicyEvaluator(opts *config.Options, store *store.Store) (*evaluator.Ev ctx, span := trace.StartSpan(ctx, "authorize.newPolicyEvaluator") defer span.End() - clientCA, err := opts.GetClientCA() + clientCA, err := opts.DownstreamMTLS.GetCA() if err != nil { return nil, fmt.Errorf("authorize: invalid client CA: %w", err) } - clientCRL, err := opts.GetClientCRL() + clientCRL, err := opts.DownstreamMTLS.GetCRL() if err != nil { return nil, fmt.Errorf("authorize: invalid client CRL: %w", err) } diff --git a/config/config_source.go b/config/config_source.go index 6b0f46ddd..d4180e073 100644 --- a/config/config_source.go +++ b/config/config_source.go @@ -225,12 +225,13 @@ func (src *FileWatcherSource) check(ctx context.Context, cfg *Config) { cfg.Options.CAFile, cfg.Options.CertFile, cfg.Options.ClientCAFile, - cfg.Options.ClientCRLFile, cfg.Options.ClientSecretFile, cfg.Options.CookieSecretFile, cfg.Options.DataBrokerStorageCAFile, cfg.Options.DataBrokerStorageCertFile, cfg.Options.DataBrokerStorageCertKeyFile, + cfg.Options.DownstreamMTLS.CAFile, + cfg.Options.DownstreamMTLS.CRLFile, cfg.Options.KeyFile, cfg.Options.MetricsCertificateFile, cfg.Options.MetricsCertificateKeyFile, diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go index 33262d59e..71617294c 100644 --- a/config/envoyconfig/listeners.go +++ b/config/envoyconfig/listeners.go @@ -554,15 +554,15 @@ func (b *Builder) buildDownstreamValidationContext( }, } - if cfg.Options.ClientCRL != "" { - bs, err := base64.StdEncoding.DecodeString(cfg.Options.ClientCRL) + if crl := cfg.Options.DownstreamMTLS.CRL; crl != "" { + bs, err := base64.StdEncoding.DecodeString(crl) if err != nil { log.Error(ctx).Err(err).Msg("invalid client CRL") } else { vc.ValidationContext.Crl = b.filemgr.BytesDataSource("client-crl.pem", bs) } - } else if cfg.Options.ClientCRLFile != "" { - vc.ValidationContext.Crl = b.filemgr.FileDataSource(cfg.Options.ClientCRLFile) + } else if crlf := cfg.Options.DownstreamMTLS.CRLFile; crlf != "" { + vc.ValidationContext.Crl = b.filemgr.FileDataSource(crlf) } return vc @@ -572,7 +572,7 @@ func (b *Builder) buildDownstreamValidationContext( // per-route client CAs. func clientCABundle(ctx context.Context, cfg *config.Config) []byte { var bundle bytes.Buffer - ca, _ := cfg.Options.GetClientCA() + ca, _ := cfg.Options.DownstreamMTLS.GetCA() addCAToBundle(&bundle, ca) allPolicies := cfg.Options.GetAllPolicies() for i := range allPolicies { diff --git a/config/envoyconfig/listeners_test.go b/config/envoyconfig/listeners_test.go index 346079979..4957c99ef 100644 --- a/config/envoyconfig/listeners_test.go +++ b/config/envoyconfig/listeners_test.go @@ -98,7 +98,9 @@ func Test_buildDownstreamTLSContext(t *testing.T) { }) t.Run("client-ca", func(t *testing.T) { downstreamTLSContext, err := b.buildDownstreamTLSContextMulti(context.Background(), &config.Config{Options: &config.Options{ - ClientCA: "VEVTVAo=", // "TEST\n" (with a trailing newline) + DownstreamMTLS: config.DownstreamMTLSSettings{ + CA: "VEVTVAo=", // "TEST\n" (with a trailing newline) + }, }}, nil) require.NoError(t, err) testutil.AssertProtoJSONEqual(t, `{ @@ -218,7 +220,9 @@ func Test_clientCABundle(t *testing.T) { b64 := base64.StdEncoding.EncodeToString cfg := &config.Config{Options: &config.Options{ - ClientCA: b64(clientCA3), + DownstreamMTLS: config.DownstreamMTLSSettings{ + CA: b64(clientCA3), + }, Policies: []config.Policy{ { From: "https://foo.example.com", diff --git a/config/mtls.go b/config/mtls.go new file mode 100644 index 000000000..e2539dc48 --- /dev/null +++ b/config/mtls.go @@ -0,0 +1,98 @@ +package config + +import ( + "context" + "encoding/base64" + "fmt" + "os" + + "github.com/pomerium/pomerium/pkg/cryptutil" + "github.com/pomerium/pomerium/pkg/grpc/config" +) + +// DownstreamMTLSSettings specify the downstream client certificate requirements. +type DownstreamMTLSSettings struct { + // CA is the base64-encoded certificate authority (or bundle of certificate + // authorities) that should serve as the trust root(s). These will be + // advertised in the initial TLS handshake. + CA string `mapstructure:"ca" yaml:"ca"` + + // CAFile is the path to a file containing the certificate authority (or + // bundle of certificate authorities) that should serve as the trust + // root(s). These will be advertised in the initial TLS handshake. + CAFile string `mapstructure:"ca_file" yaml:"ca_file"` + + // CRL is the base64-encoded certificate revocation list (or bundle of + // CRLs) to use when validating client certificates. + CRL string `mapstructure:"crl" yaml:"crl,omitempty"` + + // CRLFile is the path to a file containing the certificate revocation + // list (or bundle of CRLs) to use when validating client certificates. + CRLFile string `mapstructure:"crl_file" yaml:"crl_file,omitempty"` +} + +// GetCA returns the certificate authority (or nil if unset). +func (s *DownstreamMTLSSettings) GetCA() ([]byte, error) { + if s.CA != "" { + ca, err := base64.StdEncoding.DecodeString(s.CA) + if err != nil { + return nil, fmt.Errorf("CA: %w", err) + } + return ca, nil + } + if s.CAFile != "" { + ca, err := os.ReadFile(s.CAFile) + if err != nil { + return nil, fmt.Errorf("CA file: %w", err) + } + return ca, nil + } + return nil, nil +} + +// GetCRL returns the certificate revocation list bundle (or nil if unset). +func (s *DownstreamMTLSSettings) GetCRL() ([]byte, error) { + if s.CRL != "" { + crl, err := base64.StdEncoding.DecodeString(s.CRL) + if err != nil { + return nil, fmt.Errorf("CRL: %w", err) + } + return crl, nil + } + if s.CRLFile != "" { + crl, err := os.ReadFile(s.CRLFile) + if err != nil { + return nil, fmt.Errorf("CRL file: %w", err) + } + return crl, nil + } + return nil, nil +} + +func (s *DownstreamMTLSSettings) validate() error { + if _, err := s.GetCA(); err != nil { + return err + } + + crl, err := s.GetCRL() + if err != nil { + return err + } + if len(crl) > 0 { + if _, err := cryptutil.DecodeCRL(crl); err != nil { + return fmt.Errorf("CRL: %w", err) + } + } + + return nil +} + +func (s *DownstreamMTLSSettings) applySettingsProto( + _ context.Context, p *config.DownstreamMtlsSettings, +) { + if p == nil { + return + } + set(&s.CA, p.Ca) + set(&s.CRL, p.Crl) +} diff --git a/config/mtls_test.go b/config/mtls_test.go new file mode 100644 index 000000000..52a8aceae --- /dev/null +++ b/config/mtls_test.go @@ -0,0 +1,100 @@ +package config + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDownstreamMTLSSettingsGetCA(t *testing.T) { + t.Parallel() + + fakeCACert := []byte("--- FAKE CA CERT ---") + caFile := filepath.Join(t.TempDir(), "CA.pem") + os.WriteFile(caFile, fakeCACert, 0644) + + cases := []struct { + label string + settings DownstreamMTLSSettings + expected []byte + }{ + {"not set", DownstreamMTLSSettings{}, nil}, + {"CA", DownstreamMTLSSettings{CA: "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0="}, fakeCACert}, + {"CA file", DownstreamMTLSSettings{CAFile: caFile}, fakeCACert}, + } + + for i := range cases { + c := &cases[i] + t.Run(c.label, func(t *testing.T) { + ca, err := c.settings.GetCA() + require.NoError(t, err) + assert.Equal(t, c.expected, ca) + }) + } +} + +func TestDownstreamMTLSSettingsGetCRL(t *testing.T) { + t.Parallel() + + fakeCRL := []byte("--- FAKE CRL ---") + crlFile := filepath.Join(t.TempDir(), "CRL.pem") + os.WriteFile(crlFile, fakeCRL, 0644) + + cases := []struct { + label string + settings DownstreamMTLSSettings + expected []byte + }{ + {"not set", DownstreamMTLSSettings{}, nil}, + {"CRL", DownstreamMTLSSettings{CRL: "LS0tIEZBS0UgQ1JMIC0tLQ=="}, fakeCRL}, + {"CRL file", DownstreamMTLSSettings{CRLFile: crlFile}, fakeCRL}, + } + + for i := range cases { + c := &cases[i] + t.Run(c.label, func(t *testing.T) { + crl, err := c.settings.GetCRL() + require.NoError(t, err) + assert.Equal(t, c.expected, crl) + }) + } +} + +func TestDownstreamMTLSSettingsValidate(t *testing.T) { + t.Parallel() + + cases := []struct { + label string + settings DownstreamMTLSSettings + errorMsg string + }{ + {"not set", DownstreamMTLSSettings{}, ""}, + {"bad CA", DownstreamMTLSSettings{CA: "not%valid%base64%data"}, + "CA: illegal base64 data at input byte 3"}, + {"bad CA file", DownstreamMTLSSettings{CAFile: "-"}, + "CA file: open -: no such file or directory"}, + {"bad CRL", DownstreamMTLSSettings{CRL: "dGhpc2lzZmluZQo="}, + "CRL: cryptutil: invalid crl, no X509 CRL block found"}, + {"bad CRL file", DownstreamMTLSSettings{CRLFile: "-"}, + "CRL file: open -: no such file or directory"}, + {"OK", DownstreamMTLSSettings{ + CA: "dGhpc2lzZmluZQo=", + CRL: "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNOVENCbmdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTWhjTk1qTXdOekU1TWpFMQpNREUxV2hjTk16TXdOekUyTWpFMU1ERTFXcUF3TUM0d0h3WURWUjBqQkJnd0ZvQVVDeFEyY0JhNVl6cVZ6YW1wCmlOQ3g4S3dGRnlRd0N3WURWUjBVQkFRQ0FoQUFNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJnUUNZYW14OHBNK1IKQ2x5c2tjdTdvdWh1L1IxSnkxbldHeVd0S3BoWXEwWEZiT0xsbmsyWjdlRGZBWDhFZWoyRmF2cXh6YXBSMngyTwo0aUpORENtaXdZWVlVUzJYMkxKM3JSUkpYeVh2V2h0ZkhyeFVSZDZCaXRDMklYcHlrQnRWbGYzekFuWjhHWkZRClMxamRmeUxNdUVBaUR3SWFpM1l0OEhzRHAvcUcwODlvWGNvU3R5UWcvdVJwbVd5MDVBOXVDVk9mTkhTTFNadTgKbHI0cWF0bGV1MHdXYlYxYW1MOHRPOXg0Q1JrTzBvMVlhUXE0RG9PcnVQciszTmtUbVB2R2lkaDNGNzFWNklFQQpoK0t6ZGJSWHhGbUNDV0xXbXBKRGNyZ1I3S1VxWk9oVVV0K0RVcWFxaFY0NHFJMG5ycFIrUVpMb2hvRG9yOUx3CksrdWZqM24yOWVTUlgrM1B4K29WV1BUOFlaUDJ1S1BkaXppOTZtZTJqV1RyNTF4OUFqRW9KRHNUbllSbDkrdVkKU2hpVXhXblRkUXNvb2tuSWZjUy8wemZnWjg3R3ZVVnppbkNRekpwd1Z4ZDRBbHQ4QWxSK2ZYQXFOSW9PZ3V5dgpwL0N0UlZualZFN2w3SFcvaFFScTFKMGlqQ0NLd215Zi9LVGQ2RUs0VGRydmJYL1U5bXNWTThZPQotLS0tLUVORCBYNTA5IENSTC0tLS0tCg==", + }, ""}, + } + + for i := range cases { + c := &cases[i] + t.Run(c.label, func(t *testing.T) { + err := c.settings.validate() + if c.errorMsg == "" { + assert.NoError(t, err) + } else { + assert.Equal(t, c.errorMsg, err.Error()) + } + }) + } +} diff --git a/config/options.go b/config/options.go index 6e425e44b..17249dacc 100644 --- a/config/options.go +++ b/config/options.go @@ -255,13 +255,16 @@ type Options struct { DataBrokerStorageCertSkipVerify bool `mapstructure:"databroker_storage_tls_skip_verify" yaml:"databroker_storage_tls_skip_verify,omitempty"` // ClientCA is the base64-encoded certificate authority to validate client mTLS certificates against. + // + // Deprecated: Use DownstreamMTLS.CA instead. ClientCA string `mapstructure:"client_ca" yaml:"client_ca,omitempty"` // ClientCAFile points to a file that contains the certificate authority to validate client mTLS certificates against. + // + // Deprecated: Use DownstreamMTLS.CAFile instead. ClientCAFile string `mapstructure:"client_ca_file" yaml:"client_ca_file,omitempty"` - // ClientCRL is the base64-encoded certificate revocation list for client mTLS certificates. - ClientCRL string `mapstructure:"client_crl" yaml:"client_crl,omitempty"` - // ClientCRLFile points to a file that contains the certificate revocation list for client mTLS certificates. - ClientCRLFile string `mapstructure:"client_crl_file" yaml:"client_crl_file,omitempty"` + + // DownstreamMTLS holds all downstream mTLS settings. + DownstreamMTLS DownstreamMTLSSettings `mapstructure:"downstream_mtls" yaml:"downstream_mtls,omitempty"` // GoogleCloudServerlessAuthenticationServiceAccount is the service account to use for GCP serverless authentication. // If unset, the GCP metadata server will be used to query for identity tokens. @@ -694,30 +697,22 @@ func (o *Options) Validate() error { } if o.ClientCA != "" { - if _, err := base64.StdEncoding.DecodeString(o.ClientCA); err != nil { - return fmt.Errorf("config: bad client ca base64: %w", err) + log.Warn(context.Background()).Msg("config: client_ca is deprecated, set " + + "downstream_mtls.ca instead") + if o.DownstreamMTLS.CA == "" { + o.DownstreamMTLS.CA = o.ClientCA } } - if o.ClientCAFile != "" { - _, err := os.ReadFile(o.ClientCAFile) - if err != nil { - return fmt.Errorf("config: bad client ca file: %w", err) + log.Warn(context.Background()).Msg("config: client_ca_file is deprecated, set " + + "downstream_mtls.ca_file instead") + if o.DownstreamMTLS.CAFile == "" { + o.DownstreamMTLS.CAFile = o.ClientCAFile } } - if o.ClientCRL != "" { - _, err = cryptutil.CRLFromBase64(o.ClientCRL) - if err != nil { - return fmt.Errorf("config: bad client crl base64: %w", err) - } - } - - if o.ClientCRLFile != "" { - _, err = cryptutil.CRLFromFile(o.ClientCRLFile) - if err != nil { - return fmt.Errorf("config: bad client crl file: %w", err) - } + if err := o.DownstreamMTLS.validate(); err != nil { + return fmt.Errorf("config: bad downstream mTLS settings: %w", err) } // strip quotes from redirect address (#811) @@ -981,30 +976,6 @@ func (o *Options) GetMetricsBasicAuth() (username, password string, ok bool) { return string(bs[:idx]), string(bs[idx+1:]), true } -// GetClientCA returns the client certificate authority. If neither client_ca nor client_ca_file is specified nil will -// be returned. -func (o *Options) GetClientCA() ([]byte, error) { - if o.ClientCA != "" { - return base64.StdEncoding.DecodeString(o.ClientCA) - } - if o.ClientCAFile != "" { - return os.ReadFile(o.ClientCAFile) - } - return nil, nil -} - -// GetClientCRL returns the client certificate revocation list bundle. If -// neither client_crl nor client_crl_file is specified nil will be returned. -func (o *Options) GetClientCRL() ([]byte, error) { - if o.ClientCRL != "" { - return base64.StdEncoding.DecodeString(o.ClientCRL) - } - if o.ClientCRLFile != "" { - return os.ReadFile(o.ClientCRLFile) - } - return nil, nil -} - // GetDataBrokerCertificate gets the optional databroker certificate. This method will return nil if no certificate is // specified. func (o *Options) GetDataBrokerCertificate() (*tls.Certificate, error) { @@ -1460,7 +1431,7 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi set(&o.DataBrokerStorageType, settings.DatabrokerStorageType) set(&o.DataBrokerStorageConnectionString, settings.DatabrokerStorageConnectionString) set(&o.DataBrokerStorageCertSkipVerify, settings.DatabrokerStorageTlsSkipVerify) - set(&o.ClientCA, settings.ClientCa) + o.DownstreamMTLS.applySettingsProto(ctx, settings.DownstreamMtls) set(&o.GoogleCloudServerlessAuthenticationServiceAccount, settings.GoogleCloudServerlessAuthenticationServiceAccount) set(&o.UseProxyProtocol, settings.UseProxyProtocol) set(&o.AutocertOptions.Enable, settings.Autocert) @@ -1477,7 +1448,6 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi setSlice(&o.ProgrammaticRedirectDomainWhitelist, settings.ProgrammaticRedirectDomainWhitelist) setAuditKey(&o.AuditKey, settings.AuditKey) setCodecType(&o.CodecType, settings.CodecType) - set(&o.ClientCRL, settings.ClientCrl) o.BrandingOptions = settings } diff --git a/config/options_test.go b/config/options_test.go index b0380b613..23168c9e0 100644 --- a/config/options_test.go +++ b/config/options_test.go @@ -1,6 +1,7 @@ package config import ( + "bytes" "context" "crypto/tls" "crypto/x509" @@ -19,12 +20,14 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/rs/zerolog" "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/pomerium/csrf" "github.com/pomerium/pomerium/internal/identity/oauth/apple" + "github.com/pomerium/pomerium/internal/testutil" "github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/grpc/config" ) @@ -693,6 +696,29 @@ func TestCompareByteSliceSlice(t *testing.T) { } } +func TestDeprecatedClientCAOptions(t *testing.T) { + fakeCACert := []byte("--- FAKE CA CERT ---") + caFile := filepath.Join(t.TempDir(), "CA.pem") + os.WriteFile(caFile, fakeCACert, 0644) + + var logOutput bytes.Buffer + zl := zerolog.New(&logOutput) + testutil.SetLogger(t, &zl) + + o := NewDefaultOptions() + o.ClientCA = "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0=" + o.ClientCAFile = caFile + o.AutocertOptions.Enable = true // suppress an unrelated warning + + err := o.Validate() + require.NoError(t, err) + assert.Equal(t, "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0=", o.DownstreamMTLS.CA) + assert.Equal(t, caFile, o.DownstreamMTLS.CAFile) + assert.Equal(t, `{"level":"warn","message":"config: client_ca is deprecated, set downstream_mtls.ca instead"} +{"level":"warn","message":"config: client_ca_file is deprecated, set downstream_mtls.ca_file instead"} +`, logOutput.String()) +} + func TestOptions_DefaultURL(t *testing.T) { t.Parallel() diff --git a/integration/clusters/kubernetes/compose.yml b/integration/clusters/kubernetes/compose.yml index 25afb9d79..437978071 100644 --- a/integration/clusters/kubernetes/compose.yml +++ b/integration/clusters/kubernetes/compose.yml @@ -139,10 +139,6 @@ services: "name": "CERTIFICATE_KEY", "value": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K" }, - { - "name": "CLIENT_CRL", - "value": "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K" - }, { "name": "COOKIE_SECRET", "value": "UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=" @@ -155,6 +151,10 @@ services: "name": "DATABROKER_STORAGE_TYPE", "value": "postgres" }, + { + "name": "DOWNSTREAM_MTLS_CRL", + "value": "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K" + }, { "name": "ENVOY_ADMIN_ADDRESS", "value": "0.0.0.0:9901" diff --git a/integration/clusters/multi/compose.yml b/integration/clusters/multi/compose.yml index 04cd5659f..863593c3e 100644 --- a/integration/clusters/multi/compose.yml +++ b/integration/clusters/multi/compose.yml @@ -159,11 +159,11 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TVRBNE1UQXgKTnpNeU1UQmFGdzB5TXpFeE1UQXhPRE15TVRCYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBdWZRQUY3OXM3YzFnbVo5Q0lLQlNHa0hoK1NIMDFDdUtZbm5IaU1vd0hzVGlvRmFVQVFzZC9QNFgKYzJYQnFjMzRlVDNtQ3ZwZ1pqSGJqejZKbG5UWUp4dUx2VnFuVkIzZW10V3JiMWNRdmg4QnBoeHNwVGxTOHVpRQpBRWYvbmd0cHpmQS9mNGxwR2t6clEwY3lQa0VKR3o1MTFxOTdpdHpuOVJaWnpWVFp4TlZGU1AydlZoTk5RVnNXCk94YWtjdllSZ256OEFPUVMzT1BIajJGUWMzaWlic2hjdDVsZUl3WVpGY3hJTkdIUjZLTDYrL0xTZVBOQ0VNbUsKcXltVlBrUUdzSWNVNkdROWZ4YVN1NG1wK0lVQUxQcm9pekVWSThTVms1bk9tM0hJZXorWmZYaHpmbkd4MDZTSQo2TnVvUVFQcVVCZVplWG4yWUZZaGlwZVJkclF4dkEzNi9ZWGEvQWtYQ2VVMHBYeGJ0WEtjdmF0ZnJpNUtuWUpECmtINTlhK2FGa1RzbDQxdGZJMmNuUllWZGRxWFZsM096TGJjZ0FGTG4xV2VDMXh4M3hSWGk3S2xkb2tPbHZndisKQjZuYVdmQ3hSbFdaL2xzbUhhZTRrYzFXSDRLYzduSytJVGI0MEVralY2OC9BN2tyWnNOMVZjcU50cG9tWWtnRQp4alVFOFhVdQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K - CLIENT_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres + DOWNSTREAM_MTLS_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K ENVOY_ADMIN_ADDRESS: 0.0.0.0:9901 GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: ewoiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImF1dGhfdXJpIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImNsaWVudF9lbWFpbCI6ICJyZWRhY3RlZEBwb21lcml1bS1yZWRhY3RlZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiJjbGllbnRfaWQiOiAiMTAxMjE1OTkwNDU4MDAwMzM0Mzg3IiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFDOEhMQkFJelhrUGVlZ1xubGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PL1xuRUtnQ09GRnhVRHFvUjgyaVkwNlNhY0FqSG5pNitQTzl0VlJiRlYwdzE0QkRBSlNwQitWdld5bCtGb1BEVi92c1xuWjMxRnRZdytFd3FrYkR4L2thVDl1emYrTEpkbGtmMTRuUVFqOEVreS84ZDNtV0piYi85dGpPYnNhUWdKNUxMeFxuQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUFxubXFta2F3VVd3M2VraGo4MFNKZy9USzlQUmFOL1Z2Y0kxUGdBZDdMWnp0VVJlU21UeTVoZDlyNnJPQnhweHduVFxuRHZIa0JuNnZBZ01CQUFFQ2dnRUFCMjhpMEFZVU5TYjFKbldGYkt6cnVVY3R1M3RDTlhvdkpnNkszQmlQVk1rcVxuRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBV1xuOGVKZXFSTFpFZnNTU0pPWFRHN1JkR3NuNHFIRkowMHMyWlRsY0lIU1B3bkZtK1hqSmk5OVU4RzRYc1VvWG8wclxuR3krMFZDdVU3TThnSUNFSEhzclFPOVhERDNuVDJqaXU1VGpyS3dqdXQzRW1vSnNzSTVicXgzMytPQnU1QnBDUFxuQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVFxuYkxrTEZ5V0JOVFdVWjJSLzJ3eG11b0M2bUxadzg3OU1MQ0tNdmsxZG9RS0JnUURobXdHYWZKTnltVGlFUVpSSVxuU3NReDRzZXFmT0tmZ0ZDN29ocUg5Y1JPT3U4SUoxbzdxMnBNMlc0WGlWK1Mzd1RkUEdtY2E2SU9qWDIzaXNWQlxuMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzN1xuYXh6bGFNcnhFdTNMSTlVRTdOdHJkUWlCeVFLQmdRRFZkSTZjZUlWQlQ2Umd2Vkd0OHprTGpQSUZqaFFFSEFJcFxudWhpcmdxcFM2Q1g5Qmx5ZjIrbzQwem1majNoZTVyQ2NFb0I1TXNlTStEZ0ZiY1ZoMmUvTVZuWWlOTnc2SkNEQlxuQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleVxudUJIVkJ2LzR0d0tCZ0h3SHVlUHk1U1UxczJxU216RDdXYzJMUGZZdTNuQ09ITlJyRkdiMjZNdVJmdVJlcmk3clxuMkc4VGdvRVNGeWNwMFFUSU44KzFKTTBYWUt4TmNKRDZCOFYxd0tiYnBRc3ltbmVJMWdqdXRpQi9JZ3cvUGtES1xuQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWlxuZk4yWWVZYnlZY2FNMTFwMVZpbHVsVlRWalkzaS9GWmlEUjRTTC9JR0pXak4vU3pnNGlYWXNLRm11K2R1bE9abFxuY0JBTHBFS3JxcG16WFl0ck42YnN2MTgrNWVPM3FHYksyRHJFcTNlV1ZldjJLb1RNb2J4ejdnKytYQklXSm1MQVxuSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkU1xuS3JsdHdtb2RIaXFYTmJWa3diVzFBRlBKYmlZYWk0WUZmSzRJQWJpZi9ZbXhmOUc3OGFPa3I5WnBDSXpPa0RQWlxuWXBFd1FHV3NBaEVsQ0Z2YzhFLzVkSEVTU3ArdFd0UCtObHVpbXBGcWlEZzMvU1VuTXdPMnhIMG5oTGEwemVqaFxuZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAoicHJpdmF0ZV9rZXlfaWQiOiAiZTA3ZjdjOTM4NzBjN2UwM2Y4ODM1NjBlY2Q4ZmQwZjRkMjdiMDA4MSIsCiJwcm9qZWN0X2lkIjogInBvbWVyaXVtLXJlZGFjdGVkIiwKInRva2VuX3VyaSI6ICJodHRwOi8vbW9jay1pZHA6ODAyNC90b2tlbiIsCiJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIKfQ== GRPC_ADDRESS: :5443 @@ -219,11 +219,11 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYekNDQXNlZ0F3SUJBZ0lSQU5NalQzSmp2cEdtbjRUTzVPWUFyazh3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB4T1RBMk1ERXcKTURBd01EQmFGdzB6TVRFeE1UVXhPVEV4TlRWYU1Gc3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXdNQzRHQTFVRUN3d25ZMkZzWldKQVkyRnNaV0l0YkdGd2RHOXdMV3hwCmJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0MKQVFFQTlqUkR2Mkl5V1FaV3V1STkxdWdlbWF3R0RxK1BERUpUaVNDZ3ZhWWkwSEc0SEowNndwVjk4cDNTYWZVbgpwWU12ajlmY3lqbkJaOHlvcGU5VUQrakFSMVhEYmI1YlUvVU1iV3JkU29LWVM4a0dzYllDdDAzMHk4QWRaejY5CmI3Q2t2Y25IQUpPcDV4VXRYU1ExaEo3ZjQzZ29aaEdSbHJKU2cxSkpWKzJTTlUxMHNnM1V3YjNieGdBL1hrRk0KaUIzWnF2STRJNTh3RzBqakxVSEFmQ043eTFEQWFiVlNJQll6b1B5cjJxUUEvR0lHMWJsbzljWWdrRFlkb1d3UgpwZ21maHBLQ2NrNUxWZWdZejNBcSs4NkpPVGxHbWc5WlIxaUdNSEpNYklsUGJNdEU2MXovQjhtWUc5aUptUHJTCmYzNHVZVlJ4VDZTMkZWTkU1cDZVbHZXNHd3SURBUUFCbzNVd2N6QU9CZ05WSFE4QkFmOEVCQU1DQmFBd0V3WUQKVlIwbEJBd3dDZ1lJS3dZQkJRVUhBd0V3REFZRFZSMFRBUUgvQkFJd0FEQWZCZ05WSFNNRUdEQVdnQlNGaGxoWQpnRWZLVHBsVk9lbmVWRzMrM0lFL1RUQWRCZ05WSFJFRUZqQVVnaEp3YjIxbGNtbDFiUzFoZFhSb2IzSnBlbVV3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFESHNQT1p6WHh5eWMrZ1EwYkxSbG5WejNZMDV6cTNmV2gxeWp0U04KMmtQTFJ1aU02ZkZWUGlDV1VuUzk0cnNDeDV6Sk0xQnRNR3VNcUNMWGxmZERWYlZNcDM2d0NHMms5TWt3aE01dgpuTFRXTkkzclFZSUJnU0xvdFNTeTZYdUV5U1pJQlhUZ2RHODFYcG5pVjBUc1dKMjJEdGxlMlhOZUo1cHNnMS93CjdMbTZLdElkL3FEYUF5T1pLR1Y2Z21GL0YyNTB4RDkxYys3Mk9DUkFrK0x2WXhydFBVY3lwV0lNV2lMUEtlRkMKQnB6Q3BsNDB3c0o5YzVFMTdJcWNjMVVzanRwUnVXSlFRb0FBb3NhQ2RVUThXN04yMXdyNTcyTjFKcmNVOElBeAprN3U3T0Rrc24vK0NaODQ2N3dYclB5OXZGL3F2QkxtYWVFMWswd3VnTldlSnZNT0JLcGRKNG5iS0VCbU5xcmpMCmRRM21aTW90ZWNlelRDY29WYmRhdC9NVHNKR3hPSG42ZExoeHlKQzQ5K0FxbjJ2OGJ1ajZEbk56M3d3VHZxdXUKOGJEOW83SzlHdmRaaEtBNUxISmpFOXNkdmxzZVpNUnZqUGVnUDRWeWpaWmlBejBFUW04d292MHYvK2J5d2RUZgp5QjQ4eVJjSVdzdzZyU3BNdHR5ZU1neGJiQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRDJORU8vWWpKWkJsYTYKNGozVzZCNlpyQVlPcjQ4TVFsT0pJS0M5cGlMUWNiZ2NuVHJDbFgzeW5kSnA5U2VsZ3krUDE5ektPY0ZuektpbAo3MVFQNk1CSFZjTnR2bHRUOVF4dGF0MUtncGhMeVFheHRnSzNUZlRMd0IxblByMXZzS1M5eWNjQWs2bm5GUzFkCkpEV0VudC9qZUNobUVaR1dzbEtEVWtsWDdaSTFUWFN5RGRUQnZkdkdBRDllUVV5SUhkbXE4amdqbnpBYlNPTXQKUWNCOEkzdkxVTUJwdFZJZ0ZqT2cvS3ZhcEFEOFlnYlZ1V2oxeGlDUU5oMmhiQkdtQ1orR2tvSnlUa3RWNkJqUApjQ3I3em9rNU9VYWFEMWxIV0lZd2NreHNpVTlzeTBUclhQOEh5WmdiMkltWSt0Si9maTVoVkhGUHBMWVZVMFRtCm5wU1c5YmpEQWdNQkFBRUNnZ0VBWk9ML3krSEtMYzY2SlhSbDBMVm5MS05nREE2R2VJaUpRSXk5TVZsdHRLcVUKdml6emtoNjk4SGdaYTgvSWdqc2lkemdHUWx1dVZBRjZMaHpHL29ucW1KQ2ROMUFramwvQXl1bGFhcGpmSFZidgp4a3lHQ21VSVphR0Fxdmo3bVZQeC9jODhHNUZtc3NscndGb0JFQ29tSmtqcmpQK3YvK2RzaG1GbXFvQXdSR1IrCkp0ZW1xTlo3QWNwajhzV09ZeWQxZDlLbWtxS1RlaFZTSmpUaExlNjhvWXpxV3JxZWtVY2psbkI0a2ppbEU5VzAKYm15N0JqQlJqTlRGTWtSRUQ0eWtWUXlscGVZd1JXQkhYdzUxMlVVQVYxQnEyb0k0TGJoc0xYR1U2YXZSKy9XagpoemhKQ3ZpUTlxSUJ1OVhsRWtkcjdrQjVOQVVOd25pZUJhRE1VZmpxd1FLQmdRRDVrV3RtWmk0T3U4T1Z3QjF5CkZraE93T3RyM1oyTjBTOVZJbjAwc3ROTVlUVGNxT0J1Ynl2Y3RPdzA4UDJDVTVRcUtxRzY5QUtPRlBubm0vOEkKR1Vvc2k1eEh3S2Q3MTdoN1VhK0VTdG9GNkRDemZhR1BadXkrN0JnVFloeS96cjA5L0VJYkRPZHBOZEpmbis2ago1K0lDSjVqdEJRMUVUZXRJRTVQbWZVMXRvUUtCZ1FEOGpLYXYrZGRIUmwvMGZGN1plWit5TnZheGlCWGhXZkh2ClVXZVVaOXlKK04vbXhOckpqYTkzam5aZzFCTnIrdlYxUzNXTEwwN01wZm9HZ0IzWW82K3hXSlVhamRreTJMdE0KeGwxU1VmSERBRmQ1bWpGWVNTM3IvUGJxNDdJQ2hFK0ZmYndWNk9PNlpkYW1US3Y0TjFncUpvZWl0d2hvcEVGeApPYjBybm9DajR3S0JnQVB6NTYyUmhMMDdBQmdKR081THFBMkhSTE5LcVVvcnZBSXZMNmg4QkppN0dXazlTNEMwClJnK1MxZ0xvcGp0QU02S1BWa3h0dlBTZkpHNGtyRjltZHlSSmVjb3hKUFh6THJyVlNtQmdCeC84MDNpa1RzREUKc284N2tnNE1pY1FIOUR0ZEdYYndOMklWTmlYSmxMQUg1aTUramcrQyt2alAreUFESDE2Uk1YN0JBb0dCQU80WQpSRVgveTMvOHp4WVY4dTRoQmZma2JhdHQyd0w3NDJWMlg2ckFTR0VqYXlPTFQ2RlpuS1dnZitaRnlwR3dwWjBSCnFSMnhXaDhDRTdmeExUSkRENjZwRHRsY0Jmc3o3VkV5YW1UanNJUGhYSmJlVWNza0l0RXJTamFrdC93N2RTVXIKSnhtWTJLbXd2UzlIZFpHcnk4amhUbHFQS0xST3R3dmlIWUIwREY5MUFvR0FkeDJ3TGllTzVUcThBdkZhOXVydApCaVhwVFlMQ2JaWVBGZkVYTmZCYkt2NmFiWExBd2ZyRkQ1dkdrM0pFY2JqUTlnbWN6YW53QTFkUVhmN1NJLzRwCkZrUWhaSjhhdGJEaWl3SEVaeHpUeSt3cGx6OVlSN2tNQ0hkMndacXBsRCt1eXVxRWtZVVE5R09YZ2IzSUthVk4KVmd4QnJWSzlCNjdXM3FQZVRYMWxJdFU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K - CLIENT_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres + DOWNSTREAM_MTLS_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K ENVOY_ADMIN_ADDRESS: 0.0.0.0:9901 GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: ewoiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImF1dGhfdXJpIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImNsaWVudF9lbWFpbCI6ICJyZWRhY3RlZEBwb21lcml1bS1yZWRhY3RlZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiJjbGllbnRfaWQiOiAiMTAxMjE1OTkwNDU4MDAwMzM0Mzg3IiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFDOEhMQkFJelhrUGVlZ1xubGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PL1xuRUtnQ09GRnhVRHFvUjgyaVkwNlNhY0FqSG5pNitQTzl0VlJiRlYwdzE0QkRBSlNwQitWdld5bCtGb1BEVi92c1xuWjMxRnRZdytFd3FrYkR4L2thVDl1emYrTEpkbGtmMTRuUVFqOEVreS84ZDNtV0piYi85dGpPYnNhUWdKNUxMeFxuQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUFxubXFta2F3VVd3M2VraGo4MFNKZy9USzlQUmFOL1Z2Y0kxUGdBZDdMWnp0VVJlU21UeTVoZDlyNnJPQnhweHduVFxuRHZIa0JuNnZBZ01CQUFFQ2dnRUFCMjhpMEFZVU5TYjFKbldGYkt6cnVVY3R1M3RDTlhvdkpnNkszQmlQVk1rcVxuRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBV1xuOGVKZXFSTFpFZnNTU0pPWFRHN1JkR3NuNHFIRkowMHMyWlRsY0lIU1B3bkZtK1hqSmk5OVU4RzRYc1VvWG8wclxuR3krMFZDdVU3TThnSUNFSEhzclFPOVhERDNuVDJqaXU1VGpyS3dqdXQzRW1vSnNzSTVicXgzMytPQnU1QnBDUFxuQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVFxuYkxrTEZ5V0JOVFdVWjJSLzJ3eG11b0M2bUxadzg3OU1MQ0tNdmsxZG9RS0JnUURobXdHYWZKTnltVGlFUVpSSVxuU3NReDRzZXFmT0tmZ0ZDN29ocUg5Y1JPT3U4SUoxbzdxMnBNMlc0WGlWK1Mzd1RkUEdtY2E2SU9qWDIzaXNWQlxuMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzN1xuYXh6bGFNcnhFdTNMSTlVRTdOdHJkUWlCeVFLQmdRRFZkSTZjZUlWQlQ2Umd2Vkd0OHprTGpQSUZqaFFFSEFJcFxudWhpcmdxcFM2Q1g5Qmx5ZjIrbzQwem1majNoZTVyQ2NFb0I1TXNlTStEZ0ZiY1ZoMmUvTVZuWWlOTnc2SkNEQlxuQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleVxudUJIVkJ2LzR0d0tCZ0h3SHVlUHk1U1UxczJxU216RDdXYzJMUGZZdTNuQ09ITlJyRkdiMjZNdVJmdVJlcmk3clxuMkc4VGdvRVNGeWNwMFFUSU44KzFKTTBYWUt4TmNKRDZCOFYxd0tiYnBRc3ltbmVJMWdqdXRpQi9JZ3cvUGtES1xuQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWlxuZk4yWWVZYnlZY2FNMTFwMVZpbHVsVlRWalkzaS9GWmlEUjRTTC9JR0pXak4vU3pnNGlYWXNLRm11K2R1bE9abFxuY0JBTHBFS3JxcG16WFl0ck42YnN2MTgrNWVPM3FHYksyRHJFcTNlV1ZldjJLb1RNb2J4ejdnKytYQklXSm1MQVxuSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkU1xuS3JsdHdtb2RIaXFYTmJWa3diVzFBRlBKYmlZYWk0WUZmSzRJQWJpZi9ZbXhmOUc3OGFPa3I5WnBDSXpPa0RQWlxuWXBFd1FHV3NBaEVsQ0Z2YzhFLzVkSEVTU3ArdFd0UCtObHVpbXBGcWlEZzMvU1VuTXdPMnhIMG5oTGEwemVqaFxuZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAoicHJpdmF0ZV9rZXlfaWQiOiAiZTA3ZjdjOTM4NzBjN2UwM2Y4ODM1NjBlY2Q4ZmQwZjRkMjdiMDA4MSIsCiJwcm9qZWN0X2lkIjogInBvbWVyaXVtLXJlZGFjdGVkIiwKInRva2VuX3VyaSI6ICJodHRwOi8vbW9jay1pZHA6ODAyNC90b2tlbiIsCiJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIKfQ== GRPC_ADDRESS: :5443 @@ -278,11 +278,11 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYekNDQXNlZ0F3SUJBZ0lRWDBCTFVEUmE0cHI3TFlueGRkYzRKREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEU1TURZd01UQXcKTURBd01Gb1hEVE14TVRFeE5URTVNVEl5TjFvd1d6RW5NQ1VHQTFVRUNoTWViV3RqWlhKMElHUmxkbVZzYjNCdApaVzUwSUdObGNuUnBabWxqWVhSbE1UQXdMZ1lEVlFRTERDZGpZV3hsWWtCallXeGxZaTFzWVhCMGIzQXRiR2x1CmRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNrd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUIKQVFEYVhWcVp4MXNXLzREdTlBdTJnSjNsT2JubVlzQzhpV2JLSTJ4UWVrSWljc0NHMm5qYkVPejMza2FuL3VsaQpNbXh2TEF2UUcwTjhDU3djSzlhakFydHJvbE96S2NZajFmUzNmK25yRWZpa3NiemNHNFFiMDc0MUtlcTJLZFJkCllwR0E0TSs2Y0c0QTQvVnhDOWxnNU5UMDJXd2tYUlRUVWVjUWJIdTV5blEzcjR1MWk3R3RCb3RDODRQVGlFdXUKUVpEbFcxMUx2UjlTUW5ZenVZUmM0aG9QR1pKN1dBNE1jdlIxY3MyUHZWU3FOWjc4aGZDRGgyQ21mYUxGQVhxVgpSUk5sbUVlclMvWjQ1N3JnbHhrOTlKSlBrd1FTWm43KzBrcHJKMm9ubmRtUFpxN2JnT1FOdG13WFQ0cTRjSEFCCjFyTWRGTEdXODJqMGFnbmhMdXl4MEtuTkFnTUJBQUdqZGpCME1BNEdBMVVkRHdFQi93UUVBd0lGb0RBVEJnTlYKSFNVRUREQUtCZ2dyQmdFRkJRY0RBVEFNQmdOVkhSTUJBZjhFQWpBQU1COEdBMVVkSXdRWU1CYUFGSVdHV0ZpQQpSOHBPbVZVNTZkNVViZjdjZ1Q5Tk1CNEdBMVVkRVFRWE1CV0NFM0J2YldWeWFYVnRMV1JoZEdGaWNtOXJaWEl3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFGSkovQzVRT3hBRnFxMXRjZDN5REdPbzB6V29NejBzZDZEZnQ1cVAKTXRaOWx5WTU4dnpZN1pIWEVwVzU2Mm9hYmpuRmRTMVYyZTFtTFM5ZGRFQkZIdzRxQm1hZmZHcFVQdzZkTndXawpCSmJVRUhXVFhvVmRaejhYMUExei9mUkNEY0NxRW50amZkUXo3NFkrZFZZcDhzQ215UmVIdy8rQ2tCMmtNblRZCmticzJBMUxpYnZ4ZUlrOVRmN0k0THZFbjNSRXZ1bjZjcSs4R1gvN1QxTm1KUS9sSkZjS0hrSXpvZFVBUTR4TUcKL29xUXFFalhHalM1cTRKMGNjVExDMlQ1Y0VsSDlHdnFwV3Q1L3RJSi96aTlFajl2UVROZVJ4Nllsc2RGNXA4OQpmbm9EWkZUb3BteXF3MGdySk5EN25mNU93R3BTNzlwN1NFcmUvWi9QTmlTSmdWNlU0L21IY25PVXk2UVk0SjBjClhqd2laK1J6cjlRam5DS2xPMEprNDI1U0J2Q0M2L0ZDcHViQ1JpSjViR0Rkd1k3bE9ydmNRV3R2NDJ1WUp4UEEKeG0yM0Q2WWowdjQrVWhNMWwvVURGdjlFcGgwSXdGM1I5Sk5WOGYvZUszM0VDc2QyQW5PWDkrTVljMG0weHRJMwpVMUYwM3czTlR5c2hKYXQ0ZHp4QytBbTBaZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRGFYVnFaeDFzVy80RHUKOUF1MmdKM2xPYm5tWXNDOGlXYktJMnhRZWtJaWNzQ0cybmpiRU96MzNrYW4vdWxpTW14dkxBdlFHME44Q1N3YwpLOWFqQXJ0cm9sT3pLY1lqMWZTM2YrbnJFZmlrc2J6Y0c0UWIwNzQxS2VxMktkUmRZcEdBNE0rNmNHNEE0L1Z4CkM5bGc1TlQwMld3a1hSVFRVZWNRYkh1NXluUTNyNHUxaTdHdEJvdEM4NFBUaUV1dVFaRGxXMTFMdlI5U1FuWXoKdVlSYzRob1BHWko3V0E0TWN2UjFjczJQdlZTcU5aNzhoZkNEaDJDbWZhTEZBWHFWUlJObG1FZXJTL1o0NTdyZwpseGs5OUpKUGt3UVNabjcrMGtwckoyb25uZG1QWnE3YmdPUU50bXdYVDRxNGNIQUIxck1kRkxHVzgyajBhZ25oCkx1eXgwS25OQWdNQkFBRUNnZ0VCQUxmYjlLWVllalVDQ3dDbmtaa244RXFLY1dGdmN0TU1hZlQzSUlNZWZjTGQKbGdlMXVoN1J3SVR2cmRSVHlSUTZXcHk4bkhHc0V3VkQvOVQwZ0hPZW9Fbi9VclEvRHlzclFqS2pURVQyUzhINApPWW1telhSVXRBbTFjbi9ROXBOdXBBNjh1NHRDa1F6RTQrczhTOXJPc2hRN1lWZTQzQWtXSkJUckVyNGJuZ0VuCmlyU1ZOdDhPQ0E0emxtR0VEYzBEOE1mMTNCbUpOditmNmtpa1p4d1pTUzNKSUwwcWUvcENvYWE5KzMyaVJGRngKVGo2cmlrTHkyYktxMUgwYW02dXViR2UxUEx0ZWpXS3AvcmRQejIza0RVTEJUSS9IZXU5dVlxY1pGZUNlQ2dIeApnWkxvd0d1emJVd0xUQUZqZ3FqSStMdlBuazZyR0dxaURpTkV5SEdqUWFFQ2dZRUE2dnNwQ212SE9iT3JtaU1MCkhpNWFNSTlDQW80c2RXWU5sd2tZdVQyK2tyT0VpM3d6RHNLaVpuUmxTWE1SV2YzVHR1OW9ETkVuNVk2dE9NdHUKVzg3OTUwNEtONmU3Ulk3aXZCQThPSlQxUFd3M3BySEJ4QkNmeEQxd2J3V2MyUGIyQlZnQzd1U01KWHR4UVNaOApLRStmaEwyMHVmL1NMZ0Z3ZTEwVURMbUVqVWtDZ1lFQTdlV3hnbnBpbUZ2UFRZYTBvNHFGQ2RueThPSmQ5V2VMCitZQkkxOWw0Q29yU2pqNkJ1ZmdzcWRDYUZKbDEwTnMwSzFTRldIMEM4Q3JkZkpvV3I4RllsVUphbXpWQUkvTGMKTnQrZG8xTmRCV2Q5ZnBTWk94S0dxOFMxNTZCRVV4cm9lbmdzNFU2bXRlWDMvMzhKbU9va3pYUlVMLzVOQS9BVgpGbFdEV1I0NWpHVUNnWUVBdVFaVFpwVS9KanhIWHZOQkNkN052WGJRTDFycnZ2VkF0akMvZXYwWFZrUnNsYmlTCks0a3VmeVlmcXhva0M0eEdiTUZPV09sSVZRVm5lRlVXN011c09ibkZhZFYxTEd3Nk5JTkVKL21Dc1g3SXcwVXMKb2NuRlE2eHRINzUzcG8rdllMM1FjWE1jdDFiZjhzRHUrbjdYeUpubitKMXJUalpyNzNkM0ZoM3VOYUVDZ1lBQwpCM2FISldJWDZKaVJFYmdTdVFpL2Q4Tnh1SXlTbmtwOFcxTER2SkFnanVTUzRBZWRQWGVzRHlKeFVJbkh1VWJ0Cm82L3JmRTE5SFBCOHNwZkU4Z0krYTYxNEszRWJuV094ZUNEUkZ0SHhUTnV1SzdoTzM2NlVZbjRtdHFBK2tUa3cKYWp2L3cvcjZERGNjVDhvV0t0RWJpNFY0VDFQNjE1NW1iVjVNY1V5T1hRS0JnUURJRFRnYnJGRE9sZ0ozOTVRNApwRWdsdjNUNDA0Wll0N05oUVFNd3FMaC9aYmZMVVhqUk8wZmI2VDZmOEV4L3Q4V1NaQnlVQ1NKcGloRzRXWTFPCnJlYWFhbzIwanVSMVdudU5mWDNiTVhNSEZ0ZEpDOFd1V3ZCK2MvcGttSDZRVFRjMXIySlJ3cEYzWXF0Mi9vVzcKRmFRbUdNNG8vVVBlSWhxaUhqMzZleDBwOGc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg== - CLIENT_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres + DOWNSTREAM_MTLS_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K ENVOY_ADMIN_ADDRESS: 0.0.0.0:9901 GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: ewoiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImF1dGhfdXJpIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImNsaWVudF9lbWFpbCI6ICJyZWRhY3RlZEBwb21lcml1bS1yZWRhY3RlZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiJjbGllbnRfaWQiOiAiMTAxMjE1OTkwNDU4MDAwMzM0Mzg3IiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFDOEhMQkFJelhrUGVlZ1xubGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PL1xuRUtnQ09GRnhVRHFvUjgyaVkwNlNhY0FqSG5pNitQTzl0VlJiRlYwdzE0QkRBSlNwQitWdld5bCtGb1BEVi92c1xuWjMxRnRZdytFd3FrYkR4L2thVDl1emYrTEpkbGtmMTRuUVFqOEVreS84ZDNtV0piYi85dGpPYnNhUWdKNUxMeFxuQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUFxubXFta2F3VVd3M2VraGo4MFNKZy9USzlQUmFOL1Z2Y0kxUGdBZDdMWnp0VVJlU21UeTVoZDlyNnJPQnhweHduVFxuRHZIa0JuNnZBZ01CQUFFQ2dnRUFCMjhpMEFZVU5TYjFKbldGYkt6cnVVY3R1M3RDTlhvdkpnNkszQmlQVk1rcVxuRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBV1xuOGVKZXFSTFpFZnNTU0pPWFRHN1JkR3NuNHFIRkowMHMyWlRsY0lIU1B3bkZtK1hqSmk5OVU4RzRYc1VvWG8wclxuR3krMFZDdVU3TThnSUNFSEhzclFPOVhERDNuVDJqaXU1VGpyS3dqdXQzRW1vSnNzSTVicXgzMytPQnU1QnBDUFxuQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVFxuYkxrTEZ5V0JOVFdVWjJSLzJ3eG11b0M2bUxadzg3OU1MQ0tNdmsxZG9RS0JnUURobXdHYWZKTnltVGlFUVpSSVxuU3NReDRzZXFmT0tmZ0ZDN29ocUg5Y1JPT3U4SUoxbzdxMnBNMlc0WGlWK1Mzd1RkUEdtY2E2SU9qWDIzaXNWQlxuMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzN1xuYXh6bGFNcnhFdTNMSTlVRTdOdHJkUWlCeVFLQmdRRFZkSTZjZUlWQlQ2Umd2Vkd0OHprTGpQSUZqaFFFSEFJcFxudWhpcmdxcFM2Q1g5Qmx5ZjIrbzQwem1majNoZTVyQ2NFb0I1TXNlTStEZ0ZiY1ZoMmUvTVZuWWlOTnc2SkNEQlxuQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleVxudUJIVkJ2LzR0d0tCZ0h3SHVlUHk1U1UxczJxU216RDdXYzJMUGZZdTNuQ09ITlJyRkdiMjZNdVJmdVJlcmk3clxuMkc4VGdvRVNGeWNwMFFUSU44KzFKTTBYWUt4TmNKRDZCOFYxd0tiYnBRc3ltbmVJMWdqdXRpQi9JZ3cvUGtES1xuQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWlxuZk4yWWVZYnlZY2FNMTFwMVZpbHVsVlRWalkzaS9GWmlEUjRTTC9JR0pXak4vU3pnNGlYWXNLRm11K2R1bE9abFxuY0JBTHBFS3JxcG16WFl0ck42YnN2MTgrNWVPM3FHYksyRHJFcTNlV1ZldjJLb1RNb2J4ejdnKytYQklXSm1MQVxuSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkU1xuS3JsdHdtb2RIaXFYTmJWa3diVzFBRlBKYmlZYWk0WUZmSzRJQWJpZi9ZbXhmOUc3OGFPa3I5WnBDSXpPa0RQWlxuWXBFd1FHV3NBaEVsQ0Z2YzhFLzVkSEVTU3ArdFd0UCtObHVpbXBGcWlEZzMvU1VuTXdPMnhIMG5oTGEwemVqaFxuZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAoicHJpdmF0ZV9rZXlfaWQiOiAiZTA3ZjdjOTM4NzBjN2UwM2Y4ODM1NjBlY2Q4ZmQwZjRkMjdiMDA4MSIsCiJwcm9qZWN0X2lkIjogInBvbWVyaXVtLXJlZGFjdGVkIiwKInRva2VuX3VyaSI6ICJodHRwOi8vbW9jay1pZHA6ODAyNC90b2tlbiIsCiJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIKfQ== GRPC_ADDRESS: :5443 @@ -337,11 +337,11 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TVRBNE1UQXgKTnpNeU1UQmFGdzB5TXpFeE1UQXhPRE15TVRCYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBdWZRQUY3OXM3YzFnbVo5Q0lLQlNHa0hoK1NIMDFDdUtZbm5IaU1vd0hzVGlvRmFVQVFzZC9QNFgKYzJYQnFjMzRlVDNtQ3ZwZ1pqSGJqejZKbG5UWUp4dUx2VnFuVkIzZW10V3JiMWNRdmg4QnBoeHNwVGxTOHVpRQpBRWYvbmd0cHpmQS9mNGxwR2t6clEwY3lQa0VKR3o1MTFxOTdpdHpuOVJaWnpWVFp4TlZGU1AydlZoTk5RVnNXCk94YWtjdllSZ256OEFPUVMzT1BIajJGUWMzaWlic2hjdDVsZUl3WVpGY3hJTkdIUjZLTDYrL0xTZVBOQ0VNbUsKcXltVlBrUUdzSWNVNkdROWZ4YVN1NG1wK0lVQUxQcm9pekVWSThTVms1bk9tM0hJZXorWmZYaHpmbkd4MDZTSQo2TnVvUVFQcVVCZVplWG4yWUZZaGlwZVJkclF4dkEzNi9ZWGEvQWtYQ2VVMHBYeGJ0WEtjdmF0ZnJpNUtuWUpECmtINTlhK2FGa1RzbDQxdGZJMmNuUllWZGRxWFZsM096TGJjZ0FGTG4xV2VDMXh4M3hSWGk3S2xkb2tPbHZndisKQjZuYVdmQ3hSbFdaL2xzbUhhZTRrYzFXSDRLYzduSytJVGI0MEVralY2OC9BN2tyWnNOMVZjcU50cG9tWWtnRQp4alVFOFhVdQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K - CLIENT_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres + DOWNSTREAM_MTLS_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K ENVOY_ADMIN_ADDRESS: 0.0.0.0:9901 GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: ewoiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImF1dGhfdXJpIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImNsaWVudF9lbWFpbCI6ICJyZWRhY3RlZEBwb21lcml1bS1yZWRhY3RlZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiJjbGllbnRfaWQiOiAiMTAxMjE1OTkwNDU4MDAwMzM0Mzg3IiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFDOEhMQkFJelhrUGVlZ1xubGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PL1xuRUtnQ09GRnhVRHFvUjgyaVkwNlNhY0FqSG5pNitQTzl0VlJiRlYwdzE0QkRBSlNwQitWdld5bCtGb1BEVi92c1xuWjMxRnRZdytFd3FrYkR4L2thVDl1emYrTEpkbGtmMTRuUVFqOEVreS84ZDNtV0piYi85dGpPYnNhUWdKNUxMeFxuQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUFxubXFta2F3VVd3M2VraGo4MFNKZy9USzlQUmFOL1Z2Y0kxUGdBZDdMWnp0VVJlU21UeTVoZDlyNnJPQnhweHduVFxuRHZIa0JuNnZBZ01CQUFFQ2dnRUFCMjhpMEFZVU5TYjFKbldGYkt6cnVVY3R1M3RDTlhvdkpnNkszQmlQVk1rcVxuRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBV1xuOGVKZXFSTFpFZnNTU0pPWFRHN1JkR3NuNHFIRkowMHMyWlRsY0lIU1B3bkZtK1hqSmk5OVU4RzRYc1VvWG8wclxuR3krMFZDdVU3TThnSUNFSEhzclFPOVhERDNuVDJqaXU1VGpyS3dqdXQzRW1vSnNzSTVicXgzMytPQnU1QnBDUFxuQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVFxuYkxrTEZ5V0JOVFdVWjJSLzJ3eG11b0M2bUxadzg3OU1MQ0tNdmsxZG9RS0JnUURobXdHYWZKTnltVGlFUVpSSVxuU3NReDRzZXFmT0tmZ0ZDN29ocUg5Y1JPT3U4SUoxbzdxMnBNMlc0WGlWK1Mzd1RkUEdtY2E2SU9qWDIzaXNWQlxuMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzN1xuYXh6bGFNcnhFdTNMSTlVRTdOdHJkUWlCeVFLQmdRRFZkSTZjZUlWQlQ2Umd2Vkd0OHprTGpQSUZqaFFFSEFJcFxudWhpcmdxcFM2Q1g5Qmx5ZjIrbzQwem1majNoZTVyQ2NFb0I1TXNlTStEZ0ZiY1ZoMmUvTVZuWWlOTnc2SkNEQlxuQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleVxudUJIVkJ2LzR0d0tCZ0h3SHVlUHk1U1UxczJxU216RDdXYzJMUGZZdTNuQ09ITlJyRkdiMjZNdVJmdVJlcmk3clxuMkc4VGdvRVNGeWNwMFFUSU44KzFKTTBYWUt4TmNKRDZCOFYxd0tiYnBRc3ltbmVJMWdqdXRpQi9JZ3cvUGtES1xuQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWlxuZk4yWWVZYnlZY2FNMTFwMVZpbHVsVlRWalkzaS9GWmlEUjRTTC9JR0pXak4vU3pnNGlYWXNLRm11K2R1bE9abFxuY0JBTHBFS3JxcG16WFl0ck42YnN2MTgrNWVPM3FHYksyRHJFcTNlV1ZldjJLb1RNb2J4ejdnKytYQklXSm1MQVxuSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkU1xuS3JsdHdtb2RIaXFYTmJWa3diVzFBRlBKYmlZYWk0WUZmSzRJQWJpZi9ZbXhmOUc3OGFPa3I5WnBDSXpPa0RQWlxuWXBFd1FHV3NBaEVsQ0Z2YzhFLzVkSEVTU3ArdFd0UCtObHVpbXBGcWlEZzMvU1VuTXdPMnhIMG5oTGEwemVqaFxuZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAoicHJpdmF0ZV9rZXlfaWQiOiAiZTA3ZjdjOTM4NzBjN2UwM2Y4ODM1NjBlY2Q4ZmQwZjRkMjdiMDA4MSIsCiJwcm9qZWN0X2lkIjogInBvbWVyaXVtLXJlZGFjdGVkIiwKInRva2VuX3VyaSI6ICJodHRwOi8vbW9jay1pZHA6ODAyNC90b2tlbiIsCiJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIKfQ== GRPC_ADDRESS: :5443 diff --git a/integration/clusters/single/compose.yml b/integration/clusters/single/compose.yml index 7724e0df7..43d4ae3b3 100644 --- a/integration/clusters/single/compose.yml +++ b/integration/clusters/single/compose.yml @@ -157,10 +157,10 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TVRBNE1UQXgKTnpNeU1UQmFGdzB5TXpFeE1UQXhPRE15TVRCYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBdWZRQUY3OXM3YzFnbVo5Q0lLQlNHa0hoK1NIMDFDdUtZbm5IaU1vd0hzVGlvRmFVQVFzZC9QNFgKYzJYQnFjMzRlVDNtQ3ZwZ1pqSGJqejZKbG5UWUp4dUx2VnFuVkIzZW10V3JiMWNRdmg4QnBoeHNwVGxTOHVpRQpBRWYvbmd0cHpmQS9mNGxwR2t6clEwY3lQa0VKR3o1MTFxOTdpdHpuOVJaWnpWVFp4TlZGU1AydlZoTk5RVnNXCk94YWtjdllSZ256OEFPUVMzT1BIajJGUWMzaWlic2hjdDVsZUl3WVpGY3hJTkdIUjZLTDYrL0xTZVBOQ0VNbUsKcXltVlBrUUdzSWNVNkdROWZ4YVN1NG1wK0lVQUxQcm9pekVWSThTVms1bk9tM0hJZXorWmZYaHpmbkd4MDZTSQo2TnVvUVFQcVVCZVplWG4yWUZZaGlwZVJkclF4dkEzNi9ZWGEvQWtYQ2VVMHBYeGJ0WEtjdmF0ZnJpNUtuWUpECmtINTlhK2FGa1RzbDQxdGZJMmNuUllWZGRxWFZsM096TGJjZ0FGTG4xV2VDMXh4M3hSWGk3S2xkb2tPbHZndisKQjZuYVdmQ3hSbFdaL2xzbUhhZTRrYzFXSDRLYzduSytJVGI0MEVralY2OC9BN2tyWnNOMVZjcU50cG9tWWtnRQp4alVFOFhVdQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K - CLIENT_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres + DOWNSTREAM_MTLS_CRL: LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNXakNCd3dJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTVJjTk1qTXdOekU1TWpFeApPRFExV2hjTk16TXdOekUyTWpFeE9EUTFXakFqTUNFQ0VFWTlqblU4Vmt0Mk1ZdWVza1JkN2J3WERUSXpNRGN4Ck9USXhNVGMwTjFxZ01EQXVNQjhHQTFVZEl3UVlNQmFBRk5IMU5BejhVajI0UGhDR2RCa0dpMENNUUdNTE1Bc0cKQTFVZEZBUUVBZ0lRQURBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVlFQTR3M293NGoxRGF1ZmlCQlhoZEMwRUN5WQp6RHhPdUFDZFI0enlvWWJqTjFnMmtjMGJ1Y2hKNytWMGVUWS9SblNOYyt1cU5ZK0xZcHJYUXF1WktscjlkRlVyCnZKL3BYSit1eUxSL016ZWhpVHIzSG9UTENQbGlLWkREYXlQbW9admFxSEQ4SW9HRW5RWDZrQ0Vob3BiN2d0cUoKVTdUZkhhZXhpMHA0M0ZIMDBnblpmYURNa2NBZDh6Q2xzRVhVckFGQ1FSRDFNNVB1Q09UTzdDZVFjSTUzdUJ2ZAo4YUd2eUhsS0EvMk8xN2duaU1uZ2NvQ083Mk5BVWx0SnpNYnVncWVYT29pR0hZb1NzS1RiWTdNZExoWTNNRUJhCjNaa0NGZ3QzSExIVHo1UzBQZUJWclQ3L3k3U3o1Y2owUUEwSktMM0ozcHNuZ1ZicFM0b0h1NmN5dmcvLzdOZEcKS05CcWRhcytLUEFzbVYrM3k2NENyMmhuditXc1dqaXV4RGdJRUZ6cFFPY3lOT1p6bUlTQUN3N1lYandGdUluZQpPaWlNdVlzLzJOdndRMU9QZnEzamczSWY4a0JVY1NWaCtUZTRGSTMrMDd0V1V2TjZuVllDNFZtWEFjRzFIdXhRCkdubmU5ZjVoZ0VKUFZmTFQrdUozMVZWMTYrdkJuWkQ4NURaSlRyRE0KLS0tLS1FTkQgWDUwOSBDUkwtLS0tLQotLS0tLUJFR0lOIFg1MDkgQ1JMLS0tLS0KTUlJQ05UQ0JuZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREE2TVI0d0hBWURWUVFLRXhWdGEyTmxjblFnWkdWMgpaV3h2Y0cxbGJuUWdRMEV4R0RBV0JnTlZCQU1URDJSdmQyNXpkSEpsWVcwZ1EwRWdNaGNOTWpNd056RTVNakUxCk1ERTFXaGNOTXpNd056RTJNakUxTURFMVdxQXdNQzR3SHdZRFZSMGpCQmd3Rm9BVUN4UTJjQmE1WXpxVnphbXAKaU5DeDhLd0ZGeVF3Q3dZRFZSMFVCQVFDQWhBQU1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQmdRQ1lhbXg4cE0rUgpDbHlza2N1N291aHUvUjFKeTFuV0d5V3RLcGhZcTBYRmJPTGxuazJaN2VEZkFYOEVlajJGYXZxeHphcFIyeDJPCjRpSk5EQ21pd1lZWVVTMlgyTEozclJSSlh5WHZXaHRmSHJ4VVJkNkJpdEMySVhweWtCdFZsZjN6QW5aOEdaRlEKUzFqZGZ5TE11RUFpRHdJYWkzWXQ4SHNEcC9xRzA4OW9YY29TdHlRZy91UnBtV3kwNUE5dUNWT2ZOSFNMU1p1OApscjRxYXRsZXUwd1diVjFhbUw4dE85eDRDUmtPMG8xWWFRcTREb09ydVByKzNOa1RtUHZHaWRoM0Y3MVY2SUVBCmgrS3pkYlJYeEZtQ0NXTFdtcEpEY3JnUjdLVXFaT2hVVXQrRFVxYXFoVjQ0cUkwbnJwUitRWkxvaG9Eb3I5THcKSyt1ZmozbjI5ZVNSWCszUHgrb1ZXUFQ4WVpQMnVLUGRpemk5Nm1lMmpXVHI1MXg5QWpFb0pEc1RuWVJsOSt1WQpTaGlVeFduVGRRc29va25JZmNTLzB6ZmdaODdHdlVWemluQ1F6SnB3VnhkNEFsdDhBbFIrZlhBcU5Jb09ndXl2CnAvQ3RSVm5qVkU3bDdIVy9oUVJxMUowaWpDQ0t3bXlmL0tUZDZFSzRUZHJ2YlgvVTltc1ZNOFk9Ci0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0K ENVOY_ADMIN_ADDRESS: 0.0.0.0:9901 GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: ewoiYXV0aF9wcm92aWRlcl94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImF1dGhfdXJpIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKImNsaWVudF9lbWFpbCI6ICJyZWRhY3RlZEBwb21lcml1bS1yZWRhY3RlZC5pYW0uZ3NlcnZpY2VhY2NvdW50LmNvbSIsCiJjbGllbnRfaWQiOiAiMTAxMjE1OTkwNDU4MDAwMzM0Mzg3IiwKImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHA6Ly9tb2NrLWlkcDo4MDI0IiwKInByaXZhdGVfa2V5IjogIi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLVxuTUlJRXZRSUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NCS2N3Z2dTakFnRUFBb0lCQVFDOEhMQkFJelhrUGVlZ1xubGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PL1xuRUtnQ09GRnhVRHFvUjgyaVkwNlNhY0FqSG5pNitQTzl0VlJiRlYwdzE0QkRBSlNwQitWdld5bCtGb1BEVi92c1xuWjMxRnRZdytFd3FrYkR4L2thVDl1emYrTEpkbGtmMTRuUVFqOEVreS84ZDNtV0piYi85dGpPYnNhUWdKNUxMeFxuQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUFxubXFta2F3VVd3M2VraGo4MFNKZy9USzlQUmFOL1Z2Y0kxUGdBZDdMWnp0VVJlU21UeTVoZDlyNnJPQnhweHduVFxuRHZIa0JuNnZBZ01CQUFFQ2dnRUFCMjhpMEFZVU5TYjFKbldGYkt6cnVVY3R1M3RDTlhvdkpnNkszQmlQVk1rcVxuRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBV1xuOGVKZXFSTFpFZnNTU0pPWFRHN1JkR3NuNHFIRkowMHMyWlRsY0lIU1B3bkZtK1hqSmk5OVU4RzRYc1VvWG8wclxuR3krMFZDdVU3TThnSUNFSEhzclFPOVhERDNuVDJqaXU1VGpyS3dqdXQzRW1vSnNzSTVicXgzMytPQnU1QnBDUFxuQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVFxuYkxrTEZ5V0JOVFdVWjJSLzJ3eG11b0M2bUxadzg3OU1MQ0tNdmsxZG9RS0JnUURobXdHYWZKTnltVGlFUVpSSVxuU3NReDRzZXFmT0tmZ0ZDN29ocUg5Y1JPT3U4SUoxbzdxMnBNMlc0WGlWK1Mzd1RkUEdtY2E2SU9qWDIzaXNWQlxuMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzN1xuYXh6bGFNcnhFdTNMSTlVRTdOdHJkUWlCeVFLQmdRRFZkSTZjZUlWQlQ2Umd2Vkd0OHprTGpQSUZqaFFFSEFJcFxudWhpcmdxcFM2Q1g5Qmx5ZjIrbzQwem1majNoZTVyQ2NFb0I1TXNlTStEZ0ZiY1ZoMmUvTVZuWWlOTnc2SkNEQlxuQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleVxudUJIVkJ2LzR0d0tCZ0h3SHVlUHk1U1UxczJxU216RDdXYzJMUGZZdTNuQ09ITlJyRkdiMjZNdVJmdVJlcmk3clxuMkc4VGdvRVNGeWNwMFFUSU44KzFKTTBYWUt4TmNKRDZCOFYxd0tiYnBRc3ltbmVJMWdqdXRpQi9JZ3cvUGtES1xuQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWlxuZk4yWWVZYnlZY2FNMTFwMVZpbHVsVlRWalkzaS9GWmlEUjRTTC9JR0pXak4vU3pnNGlYWXNLRm11K2R1bE9abFxuY0JBTHBFS3JxcG16WFl0ck42YnN2MTgrNWVPM3FHYksyRHJFcTNlV1ZldjJLb1RNb2J4ejdnKytYQklXSm1MQVxuSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkU1xuS3JsdHdtb2RIaXFYTmJWa3diVzFBRlBKYmlZYWk0WUZmSzRJQWJpZi9ZbXhmOUc3OGFPa3I5WnBDSXpPa0RQWlxuWXBFd1FHV3NBaEVsQ0Z2YzhFLzVkSEVTU3ArdFd0UCtObHVpbXBGcWlEZzMvU1VuTXdPMnhIMG5oTGEwemVqaFxuZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9XG4tLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tXG4iLAoicHJpdmF0ZV9rZXlfaWQiOiAiZTA3ZjdjOTM4NzBjN2UwM2Y4ODM1NjBlY2Q4ZmQwZjRkMjdiMDA4MSIsCiJwcm9qZWN0X2lkIjogInBvbWVyaXVtLXJlZGFjdGVkIiwKInRva2VuX3VyaSI6ICJodHRwOi8vbW9jay1pZHA6ODAyNC90b2tlbiIsCiJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIKfQ== IDP_CLIENT_ID: CLIENT_ID diff --git a/integration/tpl/backends/pomerium.libsonnet b/integration/tpl/backends/pomerium.libsonnet index c4f46df31..6351227e3 100644 --- a/integration/tpl/backends/pomerium.libsonnet +++ b/integration/tpl/backends/pomerium.libsonnet @@ -80,10 +80,10 @@ local Environment(mode, idp, dns_suffix) = CERTIFICATE: std.base64(importstr '../files/trusted.pem'), CERTIFICATE_KEY: std.base64(importstr '../files/trusted-key.pem'), CERTIFICATE_AUTHORITY: std.base64(importstr '../files/ca.pem'), - CLIENT_CRL: std.base64(importstr '../files/downstream-crl.pem'), COOKIE_SECRET: 'UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=', DATABROKER_STORAGE_TYPE: 'postgres', DATABROKER_STORAGE_CONNECTION_STRING: 'postgres://pomerium:password@postgres:5432/test', + DOWNSTREAM_MTLS_CRL: std.base64(importstr '../files/downstream-crl.pem'), ENVOY_ADMIN_ADDRESS: '0.0.0.0:9901', GOOGLE_CLOUD_SERVERLESS_AUTHENTICATION_SERVICE_ACCOUNT: std.base64(std.manifestJsonEx( GoogleCloudServerlessAuthenticationServiceAccount(dns_suffix), '' diff --git a/pkg/grpc/config/config.pb.go b/pkg/grpc/config/config.pb.go index f96363e95..684072b13 100644 --- a/pkg/grpc/config/config.pb.go +++ b/pkg/grpc/config/config.pb.go @@ -903,6 +903,7 @@ func (x *Policy) GetRemediation() string { return "" } +// Next ID: 117. type Settings struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -969,13 +970,14 @@ type Settings struct { GrpcClientTimeout *durationpb.Duration `protobuf:"bytes,99,opt,name=grpc_client_timeout,json=grpcClientTimeout,proto3,oneof" json:"grpc_client_timeout,omitempty"` GrpcClientDnsRoundrobin *bool `protobuf:"varint,100,opt,name=grpc_client_dns_roundrobin,json=grpcClientDnsRoundrobin,proto3,oneof" json:"grpc_client_dns_roundrobin,omitempty"` // optional string forward_auth_url = 50; - DatabrokerServiceUrls []string `protobuf:"bytes,52,rep,name=databroker_service_urls,json=databrokerServiceUrls,proto3" json:"databroker_service_urls,omitempty"` - DatabrokerInternalServiceUrl *string `protobuf:"bytes,84,opt,name=databroker_internal_service_url,json=databrokerInternalServiceUrl,proto3,oneof" json:"databroker_internal_service_url,omitempty"` - DatabrokerStorageType *string `protobuf:"bytes,101,opt,name=databroker_storage_type,json=databrokerStorageType,proto3,oneof" json:"databroker_storage_type,omitempty"` - DatabrokerStorageConnectionString *string `protobuf:"bytes,102,opt,name=databroker_storage_connection_string,json=databrokerStorageConnectionString,proto3,oneof" json:"databroker_storage_connection_string,omitempty"` - DatabrokerStorageTlsSkipVerify *bool `protobuf:"varint,106,opt,name=databroker_storage_tls_skip_verify,json=databrokerStorageTlsSkipVerify,proto3,oneof" json:"databroker_storage_tls_skip_verify,omitempty"` - ClientCa *string `protobuf:"bytes,53,opt,name=client_ca,json=clientCa,proto3,oneof" json:"client_ca,omitempty"` - ClientCrl *string `protobuf:"bytes,74,opt,name=client_crl,json=clientCrl,proto3,oneof" json:"client_crl,omitempty"` + DatabrokerServiceUrls []string `protobuf:"bytes,52,rep,name=databroker_service_urls,json=databrokerServiceUrls,proto3" json:"databroker_service_urls,omitempty"` + DatabrokerInternalServiceUrl *string `protobuf:"bytes,84,opt,name=databroker_internal_service_url,json=databrokerInternalServiceUrl,proto3,oneof" json:"databroker_internal_service_url,omitempty"` + DatabrokerStorageType *string `protobuf:"bytes,101,opt,name=databroker_storage_type,json=databrokerStorageType,proto3,oneof" json:"databroker_storage_type,omitempty"` + DatabrokerStorageConnectionString *string `protobuf:"bytes,102,opt,name=databroker_storage_connection_string,json=databrokerStorageConnectionString,proto3,oneof" json:"databroker_storage_connection_string,omitempty"` + DatabrokerStorageTlsSkipVerify *bool `protobuf:"varint,106,opt,name=databroker_storage_tls_skip_verify,json=databrokerStorageTlsSkipVerify,proto3,oneof" json:"databroker_storage_tls_skip_verify,omitempty"` + DownstreamMtls *DownstreamMtlsSettings `protobuf:"bytes,116,opt,name=downstream_mtls,json=downstreamMtls,proto3,oneof" json:"downstream_mtls,omitempty"` + // optional string client_ca = 53; + // optional string client_crl = 74; GoogleCloudServerlessAuthenticationServiceAccount *string `protobuf:"bytes,55,opt,name=google_cloud_serverless_authentication_service_account,json=googleCloudServerlessAuthenticationServiceAccount,proto3,oneof" json:"google_cloud_serverless_authentication_service_account,omitempty"` UseProxyProtocol *bool `protobuf:"varint,107,opt,name=use_proxy_protocol,json=useProxyProtocol,proto3,oneof" json:"use_proxy_protocol,omitempty"` Autocert *bool `protobuf:"varint,56,opt,name=autocert,proto3,oneof" json:"autocert,omitempty"` @@ -1465,18 +1467,11 @@ func (x *Settings) GetDatabrokerStorageTlsSkipVerify() bool { return false } -func (x *Settings) GetClientCa() string { - if x != nil && x.ClientCa != nil { - return *x.ClientCa +func (x *Settings) GetDownstreamMtls() *DownstreamMtlsSettings { + if x != nil { + return x.DownstreamMtls } - return "" -} - -func (x *Settings) GetClientCrl() string { - if x != nil && x.ClientCrl != nil { - return *x.ClientCrl - } - return "" + return nil } func (x *Settings) GetGoogleCloudServerlessAuthenticationServiceAccount() string { @@ -1675,6 +1670,61 @@ func (x *Settings) GetErrorMessageFirstParagraph() string { return "" } +type DownstreamMtlsSettings struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ca *string `protobuf:"bytes,1,opt,name=ca,proto3,oneof" json:"ca,omitempty"` + Crl *string `protobuf:"bytes,2,opt,name=crl,proto3,oneof" json:"crl,omitempty"` +} + +func (x *DownstreamMtlsSettings) Reset() { + *x = DownstreamMtlsSettings{} + if protoimpl.UnsafeEnabled { + mi := &file_config_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DownstreamMtlsSettings) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DownstreamMtlsSettings) ProtoMessage() {} + +func (x *DownstreamMtlsSettings) ProtoReflect() protoreflect.Message { + mi := &file_config_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DownstreamMtlsSettings.ProtoReflect.Descriptor instead. +func (*DownstreamMtlsSettings) Descriptor() ([]byte, []int) { + return file_config_proto_rawDescGZIP(), []int{6} +} + +func (x *DownstreamMtlsSettings) GetCa() string { + if x != nil && x.Ca != nil { + return *x.Ca + } + return "" +} + +func (x *DownstreamMtlsSettings) GetCrl() string { + if x != nil && x.Crl != nil { + return *x.Crl + } + return "" +} + type Settings_Certificate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1687,7 +1737,7 @@ type Settings_Certificate struct { func (x *Settings_Certificate) Reset() { *x = Settings_Certificate{} if protoimpl.UnsafeEnabled { - mi := &file_config_proto_msgTypes[10] + mi := &file_config_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1700,7 +1750,7 @@ func (x *Settings_Certificate) String() string { func (*Settings_Certificate) ProtoMessage() {} func (x *Settings_Certificate) ProtoReflect() protoreflect.Message { - mi := &file_config_proto_msgTypes[10] + mi := &file_config_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1741,7 +1791,7 @@ type Settings_StringList struct { func (x *Settings_StringList) Reset() { *x = Settings_StringList{} if protoimpl.UnsafeEnabled { - mi := &file_config_proto_msgTypes[11] + mi := &file_config_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1754,7 +1804,7 @@ func (x *Settings_StringList) String() string { func (*Settings_StringList) ProtoMessage() {} func (x *Settings_StringList) ProtoReflect() protoreflect.Message { - mi := &file_config_proto_msgTypes[11] + mi := &file_config_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2074,8 +2124,8 @@ var file_config_proto_rawDesc = []byte{ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf9, - 0x3a, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x69, + 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x81, + 0x3b, 0x0a, 0x08, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2c, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x47, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x64, 0x65, 0x62, @@ -2297,260 +2347,265 @@ var file_config_proto_rawDesc = []byte{ 0x65, 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x6a, 0x20, 0x01, 0x28, 0x08, 0x48, 0x35, 0x52, 0x1e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x6c, 0x73, 0x53, - 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x18, 0x35, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x36, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x61, 0x88, 0x01, 0x01, 0x12, 0x22, - 0x0a, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x6c, 0x18, 0x4a, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x37, 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x72, 0x6c, 0x88, - 0x01, 0x01, 0x12, 0x76, 0x0a, 0x36, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x37, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x38, 0x52, 0x31, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x43, 0x6c, 0x6f, 0x75, - 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, - 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x75, 0x73, - 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x18, 0x6b, 0x20, 0x01, 0x28, 0x08, 0x48, 0x39, 0x52, 0x10, 0x75, 0x73, 0x65, 0x50, 0x72, 0x6f, - 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, - 0x08, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x38, 0x20, 0x01, 0x28, 0x08, 0x48, - 0x3a, 0x52, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, 0x24, - 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x61, 0x18, 0x4c, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x3b, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x43, - 0x61, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, - 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x3c, 0x52, 0x0d, - 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x88, 0x01, 0x01, - 0x12, 0x35, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x75, 0x73, 0x65, - 0x5f, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x48, 0x3d, - 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x53, 0x74, 0x61, - 0x67, 0x69, 0x6e, 0x67, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x63, - 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x4e, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x3e, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, - 0x45, 0x61, 0x62, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x14, 0x61, - 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, 0x5f, 0x6d, 0x61, 0x63, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x48, 0x3f, 0x52, 0x11, 0x61, 0x75, 0x74, - 0x6f, 0x63, 0x65, 0x72, 0x74, 0x45, 0x61, 0x62, 0x4d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x88, 0x01, - 0x01, 0x12, 0x35, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x75, - 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x65, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, 0x48, - 0x40, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x4d, 0x75, 0x73, 0x74, 0x53, - 0x74, 0x61, 0x70, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x6f, - 0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x41, - 0x52, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x44, 0x69, 0x72, 0x88, 0x01, 0x01, - 0x12, 0x33, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x74, 0x72, 0x75, - 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x48, 0x42, 0x52, - 0x11, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, - 0x43, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x78, 0x66, - 0x66, 0x5f, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x08, 0x48, 0x43, - 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x58, 0x66, 0x66, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x88, - 0x01, 0x01, 0x12, 0x34, 0x0a, 0x14, 0x78, 0x66, 0x66, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x74, 0x72, - 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x46, 0x20, 0x01, 0x28, 0x0d, - 0x48, 0x44, 0x52, 0x11, 0x78, 0x66, 0x66, 0x4e, 0x75, 0x6d, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, - 0x64, 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1b, 0x65, 0x6e, 0x76, 0x6f, - 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, - 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x45, 0x52, - 0x17, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x18, 0x65, - 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x6d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x46, 0x52, - 0x15, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x65, 0x6e, 0x76, - 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x47, 0x52, 0x11, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x41, - 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x4b, - 0x0a, 0x20, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x48, 0x48, 0x52, 0x1c, 0x65, 0x6e, 0x76, 0x6f, - 0x79, 0x42, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x1a, 0x65, - 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x5f, 0x66, 0x72, 0x65, 0x65, 0x62, 0x69, 0x6e, 0x64, 0x18, 0x70, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x49, 0x52, 0x17, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x46, 0x72, 0x65, 0x65, 0x62, 0x69, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x53, 0x0a, - 0x26, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x77, 0x68, - 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x44, 0x20, 0x03, 0x28, 0x09, 0x52, 0x23, 0x70, - 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, - 0x73, 0x74, 0x12, 0x80, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x49, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x5c, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, - 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x5f, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, 0x67, - 0x65, 0x72, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x64, 0x65, - 0x63, 0x54, 0x79, 0x70, 0x65, 0x48, 0x4a, 0x52, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x54, 0x79, - 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x09, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x48, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, - 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, - 0x48, 0x4b, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, - 0x28, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, - 0x18, 0x55, 0x20, 0x01, 0x28, 0x09, 0x48, 0x4c, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x73, 0x65, 0x63, - 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x56, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x4d, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, - 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x16, 0x64, 0x61, 0x72, 0x6b, 0x6d, - 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, - 0x72, 0x18, 0x57, 0x20, 0x01, 0x28, 0x09, 0x48, 0x4e, 0x52, 0x14, 0x64, 0x61, 0x72, 0x6b, 0x6d, - 0x6f, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, - 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x18, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x73, - 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x58, - 0x20, 0x01, 0x28, 0x09, 0x48, 0x4f, 0x52, 0x16, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, - 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, - 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x59, 0x20, - 0x01, 0x28, 0x09, 0x48, 0x50, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x88, 0x01, - 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x51, 0x52, 0x0a, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, - 0x6e, 0x55, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, 0x1d, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x70, - 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x09, 0x48, 0x52, - 0x52, 0x1a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, 0x69, - 0x72, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x88, 0x01, 0x01, 0x1a, - 0x49, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x09, 0x63, 0x65, 0x72, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x0a, - 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x08, 0x6b, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x1a, 0x24, 0x0a, 0x0a, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, - 0x1a, 0x40, 0x0a, 0x12, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x4a, 0x77, 0x74, - 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x12, - 0x0a, 0x10, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x42, 0x0c, 0x0a, 0x0a, - 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, - 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x5f, 0x6c, - 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x10, 0x0a, - 0x0e, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x42, - 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, - 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x69, 0x6e, 0x73, - 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x14, 0x0a, 0x12, - 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x5f, 0x66, 0x61, 0x6d, 0x69, - 0x6c, 0x79, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x64, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x42, 0x0f, 0x0a, 0x0d, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x42, 0x1b, 0x0a, - 0x19, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x24, 0x0a, 0x22, 0x5f, 0x61, - 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, - 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x6f, 0x75, 0x74, 0x5f, 0x72, 0x65, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x61, 0x75, - 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, 0x62, - 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6f, 0x6f, - 0x6b, 0x69, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6f, - 0x6b, 0x69, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, - 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x10, 0x0a, 0x0e, - 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, 0x13, - 0x0a, 0x11, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6f, - 0x6e, 0x6c, 0x79, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x65, - 0x78, 0x70, 0x69, 0x72, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, - 0x5f, 0x73, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x74, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x69, - 0x64, 0x70, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x14, 0x0a, 0x12, - 0x5f, 0x69, 0x64, 0x70, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x69, 0x64, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x69, 0x64, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x21, 0x0a, 0x1f, 0x5f, 0x61, 0x75, 0x74, - 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x1c, 0x0a, 0x1a, 0x5f, - 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x63, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x5f, 0x74, - 0x6c, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6b, - 0x65, 0x79, 0x42, 0x1b, 0x0a, 0x19, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, - 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, - 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, - 0x62, 0x61, 0x73, 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x6d, - 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x74, 0x72, 0x61, - 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x42, 0x16, 0x0a, - 0x14, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, - 0x5f, 0x72, 0x61, 0x74, 0x65, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, - 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x42, 0x24, 0x0a, 0x22, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x61, - 0x65, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x65, - 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x20, 0x0a, 0x1e, 0x5f, 0x74, 0x72, 0x61, 0x63, - 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x74, 0x72, - 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x7a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x5f, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, - 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x67, 0x72, 0x70, - 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x72, 0x6f, 0x62, 0x69, 0x6e, 0x42, - 0x22, 0x0a, 0x20, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x5f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x75, 0x72, 0x6c, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, - 0x65, 0x72, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, - 0x27, 0x0a, 0x25, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x5f, 0x73, - 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x25, 0x0a, 0x23, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, - 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, - 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x42, 0x0d, 0x0a, - 0x0b, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x72, 0x6c, 0x42, 0x39, 0x0a, 0x37, - 0x5f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x75, 0x73, 0x65, 0x5f, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, 0x0b, - 0x0a, 0x09, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, - 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x61, 0x42, 0x11, 0x0a, 0x0f, 0x5f, - 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x17, - 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x5f, - 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x75, 0x74, 0x6f, - 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x42, - 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, - 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, - 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x75, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, - 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, - 0x69, 0x72, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, - 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, - 0x6b, 0x69, 0x70, 0x5f, 0x78, 0x66, 0x66, 0x5f, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x42, 0x17, - 0x0a, 0x15, 0x5f, 0x78, 0x66, 0x66, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, - 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, 0x1e, 0x0a, 0x1c, 0x5f, 0x65, 0x6e, 0x76, 0x6f, - 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, - 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x1b, 0x0a, 0x19, 0x5f, 0x65, 0x6e, 0x76, 0x6f, - 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x70, 0x61, 0x74, 0x68, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x61, - 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x23, 0x0a, 0x21, - 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x62, 0x69, 0x6e, 0x64, - 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, - 0x0c, 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x10, 0x0a, - 0x0e, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, - 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, - 0x6c, 0x6f, 0x72, 0x42, 0x19, 0x0a, 0x17, 0x5f, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, - 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x1b, - 0x0a, 0x19, 0x5f, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, - 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, - 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x61, 0x76, - 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x20, 0x0a, 0x1e, 0x5f, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x5f, 0x70, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, - 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, - 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x88, 0x01, 0x01, 0x12, 0x55, 0x0a, 0x0f, + 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x6d, 0x74, 0x6c, 0x73, 0x18, + 0x74, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, + 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x4d, 0x74, 0x6c, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x48, 0x36, + 0x52, 0x0e, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x74, 0x6c, 0x73, + 0x88, 0x01, 0x01, 0x12, 0x76, 0x0a, 0x36, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x37, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x37, 0x52, 0x31, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x43, 0x6c, 0x6f, + 0x75, 0x64, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, + 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x12, 0x75, + 0x73, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x08, 0x48, 0x38, 0x52, 0x10, 0x75, 0x73, 0x65, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x1f, + 0x0a, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x38, 0x20, 0x01, 0x28, 0x08, + 0x48, 0x39, 0x52, 0x08, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12, + 0x24, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x63, 0x61, 0x18, 0x4c, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x3a, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, + 0x43, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, + 0x74, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x4d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x3b, 0x52, + 0x0d, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x88, 0x01, + 0x01, 0x12, 0x35, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x75, 0x73, + 0x65, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x18, 0x39, 0x20, 0x01, 0x28, 0x08, 0x48, + 0x3c, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x55, 0x73, 0x65, 0x53, 0x74, + 0x61, 0x67, 0x69, 0x6e, 0x67, 0x88, 0x01, 0x01, 0x12, 0x32, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, + 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, + 0x4e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x3d, 0x52, 0x10, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, + 0x74, 0x45, 0x61, 0x62, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x14, + 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, 0x5f, 0x6d, 0x61, 0x63, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x4f, 0x20, 0x01, 0x28, 0x09, 0x48, 0x3e, 0x52, 0x11, 0x61, 0x75, + 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x45, 0x61, 0x62, 0x4d, 0x61, 0x63, 0x4b, 0x65, 0x79, 0x88, + 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6d, + 0x75, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x65, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x08, + 0x48, 0x3f, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x4d, 0x75, 0x73, 0x74, + 0x53, 0x74, 0x61, 0x70, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x26, 0x0a, 0x0c, 0x61, 0x75, 0x74, + 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x3b, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x40, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x44, 0x69, 0x72, 0x88, 0x01, + 0x01, 0x12, 0x33, 0x0a, 0x13, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x74, 0x72, + 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x18, 0x50, 0x20, 0x01, 0x28, 0x09, 0x48, 0x41, + 0x52, 0x11, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x54, 0x72, 0x75, 0x73, 0x74, 0x65, + 0x64, 0x43, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x78, + 0x66, 0x66, 0x5f, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, 0x3d, 0x20, 0x01, 0x28, 0x08, 0x48, + 0x42, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x58, 0x66, 0x66, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, + 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x14, 0x78, 0x66, 0x66, 0x5f, 0x6e, 0x75, 0x6d, 0x5f, 0x74, + 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x18, 0x46, 0x20, 0x01, 0x28, + 0x0d, 0x48, 0x43, 0x52, 0x11, 0x78, 0x66, 0x66, 0x4e, 0x75, 0x6d, 0x54, 0x72, 0x75, 0x73, 0x74, + 0x65, 0x64, 0x48, 0x6f, 0x70, 0x73, 0x88, 0x01, 0x01, 0x12, 0x41, 0x0a, 0x1b, 0x65, 0x6e, 0x76, + 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, + 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x09, 0x48, 0x44, + 0x52, 0x17, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x3c, 0x0a, 0x18, + 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x6d, 0x20, 0x01, 0x28, 0x09, 0x48, 0x45, + 0x52, 0x15, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x13, 0x65, 0x6e, + 0x76, 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x6e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x46, 0x52, 0x11, 0x65, 0x6e, 0x76, 0x6f, 0x79, + 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, + 0x4b, 0x0a, 0x20, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x6f, 0x20, 0x01, 0x28, 0x09, 0x48, 0x47, 0x52, 0x1c, 0x65, 0x6e, 0x76, + 0x6f, 0x79, 0x42, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x88, 0x01, 0x01, 0x12, 0x40, 0x0a, 0x1a, + 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x62, 0x69, 0x6e, 0x64, 0x18, 0x70, 0x20, 0x01, 0x28, 0x09, + 0x48, 0x48, 0x52, 0x17, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x42, 0x69, 0x6e, 0x64, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x46, 0x72, 0x65, 0x65, 0x62, 0x69, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x53, + 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, + 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x77, + 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x44, 0x20, 0x03, 0x28, 0x09, 0x52, 0x23, + 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x64, 0x69, + 0x72, 0x65, 0x63, 0x74, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, + 0x69, 0x73, 0x74, 0x12, 0x80, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x49, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x5c, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, + 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x68, 0x74, 0x74, 0x70, + 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x61, + 0x67, 0x65, 0x72, 0x2e, 0x76, 0x33, 0x2e, 0x48, 0x74, 0x74, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x64, + 0x65, 0x63, 0x54, 0x79, 0x70, 0x65, 0x48, 0x49, 0x52, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x63, 0x54, + 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x09, 0x61, 0x75, 0x64, 0x69, 0x74, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x48, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, 0x6d, 0x65, + 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, + 0x79, 0x48, 0x4a, 0x52, 0x08, 0x61, 0x75, 0x64, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, + 0x12, 0x28, 0x0a, 0x0d, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, + 0x72, 0x18, 0x55, 0x20, 0x01, 0x28, 0x09, 0x48, 0x4b, 0x52, 0x0c, 0x70, 0x72, 0x69, 0x6d, 0x61, + 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x73, 0x65, + 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, 0x56, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x4c, 0x52, 0x0e, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, + 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x16, 0x64, 0x61, 0x72, 0x6b, + 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, + 0x6f, 0x72, 0x18, 0x57, 0x20, 0x01, 0x28, 0x09, 0x48, 0x4d, 0x52, 0x14, 0x64, 0x61, 0x72, 0x6b, + 0x6d, 0x6f, 0x64, 0x65, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, + 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x18, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x5f, + 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x18, + 0x58, 0x20, 0x01, 0x28, 0x09, 0x48, 0x4e, 0x52, 0x16, 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, + 0x65, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x88, + 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x59, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x4f, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x88, + 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x75, 0x72, + 0x6c, 0x18, 0x5a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x50, 0x52, 0x0a, 0x66, 0x61, 0x76, 0x69, 0x63, + 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x46, 0x0a, 0x1d, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, + 0x70, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x18, 0x5b, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x51, 0x52, 0x1a, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x46, + 0x69, 0x72, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, 0x70, 0x68, 0x88, 0x01, 0x01, + 0x1a, 0x49, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x65, 0x72, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1b, + 0x0a, 0x09, 0x6b, 0x65, 0x79, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x42, 0x79, 0x74, 0x65, 0x73, 0x1a, 0x24, 0x0a, 0x0a, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x73, 0x1a, 0x40, 0x0a, 0x12, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x45, 0x0a, 0x17, 0x53, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x43, 0x0a, 0x15, 0x4a, 0x77, + 0x74, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, + 0x12, 0x0a, 0x10, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x69, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x42, 0x0c, 0x0a, + 0x0a, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x14, 0x0a, 0x12, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x73, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x5f, + 0x6c, 0x6f, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x10, + 0x0a, 0x0e, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, + 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x69, 0x6e, + 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x42, 0x14, 0x0a, + 0x12, 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x5f, 0x66, 0x61, 0x6d, + 0x69, 0x6c, 0x79, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x72, 0x65, 0x64, + 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x65, 0x42, 0x0f, 0x0a, + 0x0d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x69, 0x64, 0x6c, 0x65, 0x42, 0x1b, + 0x0a, 0x19, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x24, 0x0a, 0x22, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, + 0x6c, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x6f, 0x75, 0x74, 0x5f, 0x72, 0x65, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x61, + 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, + 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x42, 0x10, 0x0a, + 0x0e, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, + 0x13, 0x0a, 0x11, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x68, 0x74, 0x74, 0x70, 0x5f, + 0x6f, 0x6e, 0x6c, 0x79, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, + 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x63, 0x6f, 0x6f, 0x6b, 0x69, + 0x65, 0x5f, 0x73, 0x61, 0x6d, 0x65, 0x5f, 0x73, 0x69, 0x74, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, + 0x69, 0x64, 0x70, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x14, 0x0a, + 0x12, 0x5f, 0x69, 0x64, 0x70, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x69, 0x64, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x69, 0x64, 0x70, 0x5f, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x21, 0x0a, 0x1f, 0x5f, 0x61, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x1c, 0x0a, 0x1a, + 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x72, 0x69, 0x76, 0x65, 0x5f, + 0x74, 0x6c, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, + 0x6b, 0x65, 0x79, 0x42, 0x1b, 0x0a, 0x19, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, + 0x75, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, + 0x5f, 0x62, 0x61, 0x73, 0x69, 0x63, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x42, 0x16, 0x0a, 0x14, 0x5f, + 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x63, 0x61, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x74, 0x72, + 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x42, 0x16, + 0x0a, 0x14, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, + 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x42, 0x24, 0x0a, 0x22, 0x5f, 0x74, 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x6a, + 0x61, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, + 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x20, 0x0a, 0x1e, 0x5f, 0x74, 0x72, 0x61, + 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x61, 0x65, 0x67, 0x65, 0x72, 0x5f, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x74, + 0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x5f, 0x7a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x5f, 0x65, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x67, 0x72, 0x70, 0x63, + 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x67, 0x72, + 0x70, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x5f, 0x64, 0x6e, 0x73, 0x5f, 0x72, 0x6f, 0x75, 0x6e, 0x64, 0x72, 0x6f, 0x62, 0x69, 0x6e, + 0x42, 0x22, 0x0a, 0x20, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x5f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, + 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x42, 0x27, 0x0a, 0x25, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x5f, + 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x25, 0x0a, 0x23, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, + 0x5f, 0x74, 0x6c, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, + 0x6d, 0x74, 0x6c, 0x73, 0x42, 0x39, 0x0a, 0x37, 0x5f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x5f, + 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x6c, 0x65, 0x73, 0x73, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, + 0x15, 0x0a, 0x13, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, + 0x65, 0x72, 0x74, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, + 0x5f, 0x63, 0x61, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, + 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, + 0x65, 0x72, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x42, + 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, + 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x6f, + 0x63, 0x65, 0x72, 0x74, 0x5f, 0x65, 0x61, 0x62, 0x5f, 0x6d, 0x61, 0x63, 0x5f, 0x6b, 0x65, 0x79, + 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x6d, 0x75, + 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x65, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x61, 0x75, + 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, + 0x75, 0x74, 0x6f, 0x63, 0x65, 0x72, 0x74, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, + 0x63, 0x61, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x78, 0x66, 0x66, 0x5f, + 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x78, 0x66, 0x66, 0x5f, 0x6e, + 0x75, 0x6d, 0x5f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x68, 0x6f, 0x70, 0x73, 0x42, + 0x1e, 0x0a, 0x1c, 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, + 0x1b, 0x0a, 0x19, 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x16, 0x0a, 0x14, + 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x5f, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x42, 0x23, 0x0a, 0x21, 0x5f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x5f, 0x62, + 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x1d, 0x0a, 0x1b, 0x5f, 0x65, 0x6e, + 0x76, 0x6f, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, + 0x66, 0x72, 0x65, 0x65, 0x62, 0x69, 0x6e, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, 0x64, + 0x65, 0x63, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x64, 0x69, + 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, + 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x73, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x19, 0x0a, 0x17, 0x5f, + 0x64, 0x61, 0x72, 0x6b, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x42, 0x1b, 0x0a, 0x19, 0x5f, 0x64, 0x61, 0x72, 0x6b, 0x6d, + 0x6f, 0x64, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, + 0x6c, 0x6f, 0x72, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, + 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, + 0x42, 0x20, 0x0a, 0x1e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, + 0x70, 0x68, 0x22, 0x53, 0x0a, 0x16, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x4d, 0x74, 0x6c, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x13, 0x0a, 0x02, + 0x63, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x02, 0x63, 0x61, 0x88, 0x01, + 0x01, 0x12, 0x15, 0x0a, 0x03, 0x63, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, + 0x52, 0x03, 0x63, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x63, 0x61, 0x42, + 0x06, 0x0a, 0x04, 0x5f, 0x63, 0x72, 0x6c, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, + 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, + 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2566,7 +2621,7 @@ func file_config_proto_rawDescGZIP() []byte { } var file_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_config_proto_goTypes = []interface{}{ (Route_AuthorizationHeaderMode)(0), // 0: pomerium.config.Route.AuthorizationHeaderMode (*Config)(nil), // 1: pomerium.config.Config @@ -2575,57 +2630,59 @@ var file_config_proto_goTypes = []interface{}{ (*Route)(nil), // 4: pomerium.config.Route (*Policy)(nil), // 5: pomerium.config.Policy (*Settings)(nil), // 6: pomerium.config.Settings - nil, // 7: pomerium.config.Route.AllowedIdpClaimsEntry - nil, // 8: pomerium.config.Route.SetRequestHeadersEntry - nil, // 9: pomerium.config.Route.SetResponseHeadersEntry - nil, // 10: pomerium.config.Policy.AllowedIdpClaimsEntry - (*Settings_Certificate)(nil), // 11: pomerium.config.Settings.Certificate - (*Settings_StringList)(nil), // 12: pomerium.config.Settings.StringList - nil, // 13: pomerium.config.Settings.RequestParamsEntry - nil, // 14: pomerium.config.Settings.SetResponseHeadersEntry - nil, // 15: pomerium.config.Settings.JwtClaimsHeadersEntry - (*durationpb.Duration)(nil), // 16: google.protobuf.Duration - (*v3.Cluster)(nil), // 17: envoy.config.cluster.v3.Cluster - (v31.HttpConnectionManager_CodecType)(0), // 18: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType - (*crypt.PublicKeyEncryptionKey)(nil), // 19: pomerium.crypt.PublicKeyEncryptionKey - (*structpb.ListValue)(nil), // 20: google.protobuf.ListValue + (*DownstreamMtlsSettings)(nil), // 7: pomerium.config.DownstreamMtlsSettings + nil, // 8: pomerium.config.Route.AllowedIdpClaimsEntry + nil, // 9: pomerium.config.Route.SetRequestHeadersEntry + nil, // 10: pomerium.config.Route.SetResponseHeadersEntry + nil, // 11: pomerium.config.Policy.AllowedIdpClaimsEntry + (*Settings_Certificate)(nil), // 12: pomerium.config.Settings.Certificate + (*Settings_StringList)(nil), // 13: pomerium.config.Settings.StringList + nil, // 14: pomerium.config.Settings.RequestParamsEntry + nil, // 15: pomerium.config.Settings.SetResponseHeadersEntry + nil, // 16: pomerium.config.Settings.JwtClaimsHeadersEntry + (*durationpb.Duration)(nil), // 17: google.protobuf.Duration + (*v3.Cluster)(nil), // 18: envoy.config.cluster.v3.Cluster + (v31.HttpConnectionManager_CodecType)(0), // 19: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType + (*crypt.PublicKeyEncryptionKey)(nil), // 20: pomerium.crypt.PublicKeyEncryptionKey + (*structpb.ListValue)(nil), // 21: google.protobuf.ListValue } var file_config_proto_depIdxs = []int32{ 4, // 0: pomerium.config.Config.routes:type_name -> pomerium.config.Route 6, // 1: pomerium.config.Config.settings:type_name -> pomerium.config.Settings 3, // 2: pomerium.config.Route.redirect:type_name -> pomerium.config.RouteRedirect - 7, // 3: pomerium.config.Route.allowed_idp_claims:type_name -> pomerium.config.Route.AllowedIdpClaimsEntry - 16, // 4: pomerium.config.Route.timeout:type_name -> google.protobuf.Duration - 16, // 5: pomerium.config.Route.idle_timeout:type_name -> google.protobuf.Duration - 8, // 6: pomerium.config.Route.set_request_headers:type_name -> pomerium.config.Route.SetRequestHeadersEntry - 9, // 7: pomerium.config.Route.set_response_headers:type_name -> pomerium.config.Route.SetResponseHeadersEntry + 8, // 3: pomerium.config.Route.allowed_idp_claims:type_name -> pomerium.config.Route.AllowedIdpClaimsEntry + 17, // 4: pomerium.config.Route.timeout:type_name -> google.protobuf.Duration + 17, // 5: pomerium.config.Route.idle_timeout:type_name -> google.protobuf.Duration + 9, // 6: pomerium.config.Route.set_request_headers:type_name -> pomerium.config.Route.SetRequestHeadersEntry + 10, // 7: pomerium.config.Route.set_response_headers:type_name -> pomerium.config.Route.SetResponseHeadersEntry 2, // 8: pomerium.config.Route.rewrite_response_headers:type_name -> pomerium.config.RouteRewriteHeader 0, // 9: pomerium.config.Route.set_authorization_header:type_name -> pomerium.config.Route.AuthorizationHeaderMode - 17, // 10: pomerium.config.Route.envoy_opts:type_name -> envoy.config.cluster.v3.Cluster + 18, // 10: pomerium.config.Route.envoy_opts:type_name -> envoy.config.cluster.v3.Cluster 5, // 11: pomerium.config.Route.policies:type_name -> pomerium.config.Policy - 10, // 12: pomerium.config.Policy.allowed_idp_claims:type_name -> pomerium.config.Policy.AllowedIdpClaimsEntry - 12, // 13: pomerium.config.Settings.access_log_fields:type_name -> pomerium.config.Settings.StringList - 12, // 14: pomerium.config.Settings.authorize_log_fields:type_name -> pomerium.config.Settings.StringList - 11, // 15: pomerium.config.Settings.certificates:type_name -> pomerium.config.Settings.Certificate - 16, // 16: pomerium.config.Settings.timeout_read:type_name -> google.protobuf.Duration - 16, // 17: pomerium.config.Settings.timeout_write:type_name -> google.protobuf.Duration - 16, // 18: pomerium.config.Settings.timeout_idle:type_name -> google.protobuf.Duration - 16, // 19: pomerium.config.Settings.cookie_expire:type_name -> google.protobuf.Duration - 13, // 20: pomerium.config.Settings.request_params:type_name -> pomerium.config.Settings.RequestParamsEntry - 14, // 21: pomerium.config.Settings.set_response_headers:type_name -> pomerium.config.Settings.SetResponseHeadersEntry - 15, // 22: pomerium.config.Settings.jwt_claims_headers:type_name -> pomerium.config.Settings.JwtClaimsHeadersEntry - 16, // 23: pomerium.config.Settings.default_upstream_timeout:type_name -> google.protobuf.Duration - 11, // 24: pomerium.config.Settings.metrics_certificate:type_name -> pomerium.config.Settings.Certificate - 16, // 25: pomerium.config.Settings.grpc_client_timeout:type_name -> google.protobuf.Duration - 18, // 26: pomerium.config.Settings.codec_type:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType - 19, // 27: pomerium.config.Settings.audit_key:type_name -> pomerium.crypt.PublicKeyEncryptionKey - 20, // 28: pomerium.config.Route.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue - 20, // 29: pomerium.config.Policy.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue - 30, // [30:30] is the sub-list for method output_type - 30, // [30:30] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 11, // 12: pomerium.config.Policy.allowed_idp_claims:type_name -> pomerium.config.Policy.AllowedIdpClaimsEntry + 13, // 13: pomerium.config.Settings.access_log_fields:type_name -> pomerium.config.Settings.StringList + 13, // 14: pomerium.config.Settings.authorize_log_fields:type_name -> pomerium.config.Settings.StringList + 12, // 15: pomerium.config.Settings.certificates:type_name -> pomerium.config.Settings.Certificate + 17, // 16: pomerium.config.Settings.timeout_read:type_name -> google.protobuf.Duration + 17, // 17: pomerium.config.Settings.timeout_write:type_name -> google.protobuf.Duration + 17, // 18: pomerium.config.Settings.timeout_idle:type_name -> google.protobuf.Duration + 17, // 19: pomerium.config.Settings.cookie_expire:type_name -> google.protobuf.Duration + 14, // 20: pomerium.config.Settings.request_params:type_name -> pomerium.config.Settings.RequestParamsEntry + 15, // 21: pomerium.config.Settings.set_response_headers:type_name -> pomerium.config.Settings.SetResponseHeadersEntry + 16, // 22: pomerium.config.Settings.jwt_claims_headers:type_name -> pomerium.config.Settings.JwtClaimsHeadersEntry + 17, // 23: pomerium.config.Settings.default_upstream_timeout:type_name -> google.protobuf.Duration + 12, // 24: pomerium.config.Settings.metrics_certificate:type_name -> pomerium.config.Settings.Certificate + 17, // 25: pomerium.config.Settings.grpc_client_timeout:type_name -> google.protobuf.Duration + 7, // 26: pomerium.config.Settings.downstream_mtls:type_name -> pomerium.config.DownstreamMtlsSettings + 19, // 27: pomerium.config.Settings.codec_type:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType + 20, // 28: pomerium.config.Settings.audit_key:type_name -> pomerium.crypt.PublicKeyEncryptionKey + 21, // 29: pomerium.config.Route.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue + 21, // 30: pomerium.config.Policy.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue + 31, // [31:31] is the sub-list for method output_type + 31, // [31:31] is the sub-list for method input_type + 31, // [31:31] is the sub-list for extension type_name + 31, // [31:31] is the sub-list for extension extendee + 0, // [0:31] is the sub-list for field type_name } func init() { file_config_proto_init() } @@ -2706,8 +2763,8 @@ func file_config_proto_init() { return nil } } - file_config_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Settings_Certificate); i { + file_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DownstreamMtlsSettings); i { case 0: return &v.state case 1: @@ -2719,6 +2776,18 @@ func file_config_proto_init() { } } file_config_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Settings_Certificate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_config_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Settings_StringList); i { case 0: return &v.state @@ -2737,13 +2806,14 @@ func file_config_proto_init() { file_config_proto_msgTypes[2].OneofWrappers = []interface{}{} file_config_proto_msgTypes[3].OneofWrappers = []interface{}{} file_config_proto_msgTypes[5].OneofWrappers = []interface{}{} + file_config_proto_msgTypes[6].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_config_proto_rawDesc, NumEnums: 1, - NumMessages: 15, + NumMessages: 16, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/grpc/config/config.proto b/pkg/grpc/config/config.proto index 5d8251969..d2db14056 100644 --- a/pkg/grpc/config/config.proto +++ b/pkg/grpc/config/config.proto @@ -132,6 +132,7 @@ message Policy { string remediation = 9; } +// Next ID: 117. message Settings { message Certificate { bytes cert_bytes = 3; @@ -207,8 +208,9 @@ message Settings { optional string databroker_storage_type = 101; optional string databroker_storage_connection_string = 102; optional bool databroker_storage_tls_skip_verify = 106; - optional string client_ca = 53; - optional string client_crl = 74; + optional DownstreamMtlsSettings downstream_mtls = 116; + // optional string client_ca = 53; + // optional string client_crl = 74; optional string google_cloud_serverless_authentication_service_account = 55; optional bool use_proxy_protocol = 107; optional bool autocert = 56; @@ -239,3 +241,8 @@ message Settings { optional string favicon_url = 90; optional string error_message_first_paragraph = 91; } + +message DownstreamMtlsSettings { + optional string ca = 1; + optional string crl = 2; +} From 2220d1edf0807d1a2c87004035627691623008d1 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Wed, 9 Aug 2023 07:09:00 -0600 Subject: [PATCH 03/12] logs: add ip address to access logs (#4391) --- internal/controlplane/grpc_accesslog.go | 2 ++ internal/controlplane/grpc_accesslog_test.go | 8 ++++++++ internal/log/access.go | 2 ++ 3 files changed, 12 insertions(+) diff --git a/internal/controlplane/grpc_accesslog.go b/internal/controlplane/grpc_accesslog.go index 1481a381a..714f0eacb 100644 --- a/internal/controlplane/grpc_accesslog.go +++ b/internal/controlplane/grpc_accesslog.go @@ -62,6 +62,8 @@ func populateLogEvent( return evt.Dur(string(field), dur) case log.AccessLogFieldForwardedFor: return evt.Str(string(field), entry.GetRequest().GetForwardedFor()) + case log.AccessLogFieldIP: + return evt.Str(string(field), entry.GetCommonProperties().GetDownstreamRemoteAddress().GetSocketAddress().GetAddress()) case log.AccessLogFieldMethod: return evt.Str(string(field), entry.GetRequest().GetRequestMethod().String()) case log.AccessLogFieldPath: diff --git a/internal/controlplane/grpc_accesslog_test.go b/internal/controlplane/grpc_accesslog_test.go index aeab3e647..6f3a161dc 100644 --- a/internal/controlplane/grpc_accesslog_test.go +++ b/internal/controlplane/grpc_accesslog_test.go @@ -21,6 +21,13 @@ func Test_populateLogEvent(t *testing.T) { entry := &envoy_data_accesslog_v3.HTTPAccessLogEntry{ CommonProperties: &envoy_data_accesslog_v3.AccessLogCommon{ + DownstreamRemoteAddress: &envoy_config_core_v3.Address{ + Address: &envoy_config_core_v3.Address_SocketAddress{ + SocketAddress: &envoy_config_core_v3.SocketAddress{ + Address: "127.0.0.1", + }, + }, + }, TimeToLastDownstreamTxByte: durationpb.New(time.Second * 3), UpstreamCluster: "UPSTREAM-CLUSTER", }, @@ -47,6 +54,7 @@ func Test_populateLogEvent(t *testing.T) { {log.AccessLogFieldAuthority, `{"authority":"AUTHORITY"}`}, {log.AccessLogFieldDuration, `{"duration":3000}`}, {log.AccessLogFieldForwardedFor, `{"forwarded-for":"FORWARDED-FOR"}`}, + {log.AccessLogFieldIP, `{"ip":"127.0.0.1"}`}, {log.AccessLogFieldMethod, `{"method":"GET"}`}, {log.AccessLogFieldPath, `{"path":"https://www.example.com/some/path"}`}, {log.AccessLogFieldQuery, `{"query":"a=b"}`}, diff --git a/internal/log/access.go b/internal/log/access.go index 0fbc9a9f1..12a7acdea 100644 --- a/internal/log/access.go +++ b/internal/log/access.go @@ -13,6 +13,7 @@ const ( AccessLogFieldAuthority AccessLogField = "authority" AccessLogFieldDuration AccessLogField = "duration" AccessLogFieldForwardedFor AccessLogField = "forwarded-for" + AccessLogFieldIP AccessLogField = "ip" AccessLogFieldMethod AccessLogField = "method" AccessLogFieldPath AccessLogField = "path" AccessLogFieldQuery AccessLogField = "query" @@ -52,6 +53,7 @@ var accessLogFieldLookup = map[AccessLogField]struct{}{ AccessLogFieldAuthority: {}, AccessLogFieldDuration: {}, AccessLogFieldForwardedFor: {}, + AccessLogFieldIP: {}, AccessLogFieldMethod: {}, AccessLogFieldPath: {}, AccessLogFieldQuery: {}, From de68e37bc3900759456e4bdcfa689eb4d79c9844 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Wed, 9 Aug 2023 07:53:11 -0700 Subject: [PATCH 04/12] config: add new mTLS enforcement setting (#4443) Add an "enforcement" option to the new downstream mTLS configuration settings group. When not set, or when set to "policy_default_deny", keep the current behavior of adding an invalid_client_certificate rule to all policies. When the enforcement mode is set to just "policy", remove the default invalid_client_certificate rule that would be normally added. When the enforcement mode is set to "reject_connection", configure the Envoy listener with the require_client_certificate setting and remove the ACCEPT_UNTRUSTED option. Add a corresponding field to the Settings proto. --- authorize/authorize.go | 7 + authorize/evaluator/config.go | 9 + authorize/evaluator/evaluator.go | 4 +- authorize/evaluator/evaluator_test.go | 50 ++++- authorize/evaluator/policy_evaluator.go | 5 +- authorize/evaluator/policy_evaluator_test.go | 6 +- config/envoyconfig/listeners.go | 41 ++-- config/envoyconfig/listeners_test.go | 31 +++ config/mtls.go | 64 +++++- config/mtls_test.go | 39 +++- pkg/grpc/config/config.pb.go | 221 +++++++++++++------ pkg/grpc/config/config.proto | 8 + 12 files changed, 385 insertions(+), 100 deletions(-) diff --git a/authorize/authorize.go b/authorize/authorize.go index d20a6360f..7e932ee9f 100644 --- a/authorize/authorize.go +++ b/authorize/authorize.go @@ -114,9 +114,16 @@ func newPolicyEvaluator(opts *config.Options, store *store.Store) (*evaluator.Ev return nil, fmt.Errorf("authorize: invalid signing key: %w", err) } + // It is important to add an invalid_client_certificate rule even when the + // mTLS enforcement behavior is set to reject connections at the listener + // level, because of the per-route TLSDownstreamClientCA setting. + addDefaultClientCertificateRule := + opts.DownstreamMTLS.GetEnforcement() != config.MTLSEnforcementPolicy + return evaluator.New(ctx, store, evaluator.WithPolicies(opts.GetAllPolicies()), evaluator.WithClientCA(clientCA), + evaluator.WithAddDefaultClientCertificateRule(addDefaultClientCertificateRule), evaluator.WithClientCRL(clientCRL), evaluator.WithSigningKey(signingKey), evaluator.WithAuthenticateURL(authenticateURL.String()), diff --git a/authorize/evaluator/config.go b/authorize/evaluator/config.go index 2d96b3e49..8f3733b3b 100644 --- a/authorize/evaluator/config.go +++ b/authorize/evaluator/config.go @@ -8,6 +8,7 @@ type evaluatorConfig struct { policies []config.Policy clientCA []byte clientCRL []byte + addDefaultClientCertificateRule bool signingKey []byte authenticateURL string googleCloudServerlessAuthenticationServiceAccount string @@ -46,6 +47,14 @@ func WithClientCRL(clientCRL []byte) Option { } } +// WithAddDefaultClientCertificateRule sets whether to add a default +// invalid_client_certificate deny rule to all policies. +func WithAddDefaultClientCertificateRule(addDefaultClientCertificateRule bool) Option { + return func(cfg *evaluatorConfig) { + cfg.addDefaultClientCertificateRule = addDefaultClientCertificateRule + } +} + // WithSigningKey sets the signing key and algorithm in the config. func WithSigningKey(signingKey []byte) Option { return func(cfg *evaluatorConfig) { diff --git a/authorize/evaluator/evaluator.go b/authorize/evaluator/evaluator.go index ab5798719..926e6491f 100644 --- a/authorize/evaluator/evaluator.go +++ b/authorize/evaluator/evaluator.go @@ -122,8 +122,8 @@ func New(ctx context.Context, store *store.Store, options ...Option) (*Evaluator if err != nil { return nil, fmt.Errorf("authorize: error computing policy route id: %w", err) } - clientCA, _ := e.getClientCA(&configPolicy) - policyEvaluator, err := NewPolicyEvaluator(ctx, store, &configPolicy, clientCA) + policyEvaluator, err := + NewPolicyEvaluator(ctx, store, &configPolicy, cfg.addDefaultClientCertificateRule) if err != nil { return nil, err } diff --git a/authorize/evaluator/evaluator_test.go b/authorize/evaluator/evaluator_test.go index afaf940a5..de043bc46 100644 --- a/authorize/evaluator/evaluator_test.go +++ b/authorize/evaluator/evaluator_test.go @@ -115,6 +115,18 @@ func TestEvaluator(t *testing.T) { AllowedUsers: []string{"a@example.com"}, TLSDownstreamClientCA: base64.StdEncoding.EncodeToString([]byte(testCA)), }, + { + To: config.WeightedURLs{{URL: *mustParseURL("https://to12.example.com")}}, + AllowedUsers: []string{"a@example.com"}, + Policy: &config.PPLPolicy{ + Policy: &parser.Policy{ + Rules: []parser.Rule{{ + Action: parser.ActionDeny, + Or: []parser.Criterion{{Name: "invalid_client_certificate"}}, + }}, + }, + }, + }, } options := []Option{ WithAuthenticateURL("https://authn.example.com"), @@ -129,7 +141,8 @@ func TestEvaluator(t *testing.T) { t.Run("client certificate (default CA)", func(t *testing.T) { // Clone the existing options and add a default client CA. options := append([]Option(nil), options...) - options = append(options, WithClientCA([]byte(testCA))) + options = append(options, WithClientCA([]byte(testCA)), + WithAddDefaultClientCertificateRule(true)) t.Run("missing", func(t *testing.T) { res, err := eval(t, options, nil, &Request{ Policy: &policies[0], @@ -159,6 +172,9 @@ func TestEvaluator(t *testing.T) { }) }) t.Run("client certificate (per-policy CA)", func(t *testing.T) { + // Clone existing options and add the default client certificate rule. + options := append([]Option(nil), options...) + options = append(options, WithAddDefaultClientCertificateRule(true)) t.Run("missing", func(t *testing.T) { res, err := eval(t, options, nil, &Request{ Policy: &policies[10], @@ -190,6 +206,38 @@ func TestEvaluator(t *testing.T) { assert.False(t, res.Deny.Value) }) }) + t.Run("explicit client certificate rule", func(t *testing.T) { + // Clone the existing options and add a default client CA (but no + // default deny rule). + options := append([]Option(nil), options...) + options = append(options, WithClientCA([]byte(testCA))) + t.Run("invalid but allowed", func(t *testing.T) { + res, err := eval(t, options, nil, &Request{ + Policy: &policies[0], // no explicit deny rule + HTTP: RequestHTTP{ + ClientCertificate: ClientCertificateInfo{ + Presented: true, + Leaf: testUntrustedCert, + }, + }, + }) + require.NoError(t, err) + assert.False(t, res.Deny.Value) + }) + t.Run("invalid", func(t *testing.T) { + res, err := eval(t, options, nil, &Request{ + Policy: &policies[11], // policy has explicit deny rule + HTTP: RequestHTTP{ + ClientCertificate: ClientCertificateInfo{ + Presented: true, + Leaf: testUntrustedCert, + }, + }, + }) + require.NoError(t, err) + assert.Equal(t, NewRuleResult(true, criteria.ReasonInvalidClientCertificate), res.Deny) + }) + }) t.Run("identity_headers", func(t *testing.T) { t.Run("kubernetes", func(t *testing.T) { res, err := eval(t, options, []proto.Message{ diff --git a/authorize/evaluator/policy_evaluator.go b/authorize/evaluator/policy_evaluator.go index b50977c30..cdb19fab9 100644 --- a/authorize/evaluator/policy_evaluator.go +++ b/authorize/evaluator/policy_evaluator.go @@ -108,13 +108,14 @@ type PolicyEvaluator struct { // NewPolicyEvaluator creates a new PolicyEvaluator. func NewPolicyEvaluator( - ctx context.Context, store *store.Store, configPolicy *config.Policy, clientCA string, + ctx context.Context, store *store.Store, configPolicy *config.Policy, + addDefaultClientCertificateRule bool, ) (*PolicyEvaluator, error) { e := new(PolicyEvaluator) // generate the base rego script for the policy ppl := configPolicy.ToPPL() - if clientCA != "" { + if addDefaultClientCertificateRule { ppl.AddDefaultClientCertificateRule() } base, err := policy.GenerateRegoFromPolicy(ppl) diff --git a/authorize/evaluator/policy_evaluator_test.go b/authorize/evaluator/policy_evaluator_test.go index efa1a60ea..d4595a85c 100644 --- a/authorize/evaluator/policy_evaluator_test.go +++ b/authorize/evaluator/policy_evaluator_test.go @@ -32,7 +32,7 @@ func TestPolicyEvaluator(t *testing.T) { privateJWK, err := cryptutil.PrivateJWKFromBytes(encodedSigningKey) require.NoError(t, err) - var clientCA string + var addDefaultClientCertificateRule bool eval := func(t *testing.T, policy *config.Policy, data []proto.Message, input *PolicyRequest) (*PolicyResponse, error) { ctx := context.Background() @@ -40,7 +40,7 @@ func TestPolicyEvaluator(t *testing.T) { store := store.New() store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY")) store.UpdateSigningKey(privateJWK) - e, err := NewPolicyEvaluator(ctx, store, policy, clientCA) + e, err := NewPolicyEvaluator(ctx, store, policy, addDefaultClientCertificateRule) require.NoError(t, err) return e.Evaluate(ctx, input) } @@ -99,7 +99,7 @@ func TestPolicyEvaluator(t *testing.T) { }) // Enable client certificate validation. - clientCA = "---FAKE CA CERTIFICATE---" + addDefaultClientCertificateRule = true t.Run("allowed with cert", func(t *testing.T) { output, err := eval(t, diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go index 71617294c..75c0ca428 100644 --- a/config/envoyconfig/listeners.go +++ b/config/envoyconfig/listeners.go @@ -517,14 +517,15 @@ func (b *Builder) buildDownstreamTLSContextMulti( if err != nil { return nil, err } - return &envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext{ + dtc := &envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext{ CommonTlsContext: &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext{ - TlsParams: tlsParams, - TlsCertificates: envoyCerts, - AlpnProtocols: getALPNProtos(cfg.Options), - ValidationContextType: b.buildDownstreamValidationContext(ctx, cfg), + TlsParams: tlsParams, + TlsCertificates: envoyCerts, + AlpnProtocols: getALPNProtos(cfg.Options), }, - }, nil + } + b.buildDownstreamValidationContext(ctx, dtc, cfg) + return dtc, nil } func getALPNProtos(opts *config.Options) []string { @@ -540,18 +541,23 @@ func getALPNProtos(opts *config.Options) []string { func (b *Builder) buildDownstreamValidationContext( ctx context.Context, + dtc *envoy_extensions_transport_sockets_tls_v3.DownstreamTlsContext, cfg *config.Config, -) *envoy_extensions_transport_sockets_tls_v3.CommonTlsContext_ValidationContext { +) { clientCA := clientCABundle(ctx, cfg) if len(clientCA) == 0 { - return nil + return } - vc := &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext_ValidationContext{ - ValidationContext: &envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext{ - TrustChainVerification: envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext_ACCEPT_UNTRUSTED, - TrustedCa: b.filemgr.BytesDataSource("client-ca.pem", clientCA), - }, + vc := &envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext{ + TrustedCa: b.filemgr.BytesDataSource("client-ca.pem", clientCA), + } + + if cfg.Options.DownstreamMTLS.GetEnforcement() == config.MTLSEnforcementRejectConnection { + dtc.RequireClientCertificate = wrapperspb.Bool(true) + } else { + vc.TrustChainVerification = + envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext_ACCEPT_UNTRUSTED } if crl := cfg.Options.DownstreamMTLS.CRL; crl != "" { @@ -559,13 +565,16 @@ func (b *Builder) buildDownstreamValidationContext( if err != nil { log.Error(ctx).Err(err).Msg("invalid client CRL") } else { - vc.ValidationContext.Crl = b.filemgr.BytesDataSource("client-crl.pem", bs) + vc.Crl = b.filemgr.BytesDataSource("client-crl.pem", bs) } } else if crlf := cfg.Options.DownstreamMTLS.CRLFile; crlf != "" { - vc.ValidationContext.Crl = b.filemgr.FileDataSource(crlf) + vc.Crl = b.filemgr.FileDataSource(crlf) } - return vc + dtc.CommonTlsContext.ValidationContextType = + &envoy_extensions_transport_sockets_tls_v3.CommonTlsContext_ValidationContext{ + ValidationContext: vc, + } } // clientCABundle returns a bundle of the globally configured client CA and any diff --git a/config/envoyconfig/listeners_test.go b/config/envoyconfig/listeners_test.go index 4957c99ef..09540dbfa 100644 --- a/config/envoyconfig/listeners_test.go +++ b/config/envoyconfig/listeners_test.go @@ -126,6 +126,37 @@ func Test_buildDownstreamTLSContext(t *testing.T) { } }`, downstreamTLSContext) }) + t.Run("client-ca-strict", func(t *testing.T) { + downstreamTLSContext, err := b.buildDownstreamTLSContextMulti(context.Background(), &config.Config{Options: &config.Options{ + DownstreamMTLS: config.DownstreamMTLSSettings{ + CA: "VEVTVAo=", // "TEST\n" (with a trailing newline) + Enforcement: config.MTLSEnforcementRejectConnection, + }, + }}, nil) + require.NoError(t, err) + testutil.AssertProtoJSONEqual(t, `{ + "commonTlsContext": { + "tlsParams": { + "cipherSuites": [ + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-CHACHA20-POLY1305", + "ECDHE-RSA-CHACHA20-POLY1305" + ], + "tlsMinimumProtocolVersion": "TLSv1_2" + }, + "alpnProtocols": ["h2", "http/1.1"], + "validationContext": { + "trustedCa": { + "filename": "`+clientCAFileName+`" + } + } + }, + "requireClientCertificate": true + }`, downstreamTLSContext) + }) t.Run("policy-client-ca", func(t *testing.T) { downstreamTLSContext, err := b.buildDownstreamTLSContextMulti(context.Background(), &config.Config{Options: &config.Options{ Policies: []config.Policy{ diff --git a/config/mtls.go b/config/mtls.go index e2539dc48..20619362c 100644 --- a/config/mtls.go +++ b/config/mtls.go @@ -3,13 +3,34 @@ package config import ( "context" "encoding/base64" + "errors" "fmt" "os" + "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/grpc/config" ) +// MTLSEnforcement represents a client certificate enforcement behavior. +type MTLSEnforcement string + +const ( + // MTLSEnforcementPolicy specifies no default client certificate + // enforcement: any requirements must be explicitly specified in a policy. + MTLSEnforcementPolicy MTLSEnforcement = "policy" + + // MTLSEnforcementPolicyWithDefaultDeny specifies that client certificate + // requirements will be enforced by route policy, with a default + // invalid_client_certificate deny rule added to each policy. + MTLSEnforcementPolicyWithDefaultDeny MTLSEnforcement = "policy_with_default_deny" + + // MTLSEnforcementRejectConnection specifies that client certificate + // requirements will be enforced by rejecting any connection attempts + // without a trusted certificate. + MTLSEnforcementRejectConnection MTLSEnforcement = "reject_connection" +) + // DownstreamMTLSSettings specify the downstream client certificate requirements. type DownstreamMTLSSettings struct { // CA is the base64-encoded certificate authority (or bundle of certificate @@ -29,6 +50,10 @@ type DownstreamMTLSSettings struct { // CRLFile is the path to a file containing the certificate revocation // list (or bundle of CRLs) to use when validating client certificates. CRLFile string `mapstructure:"crl_file" yaml:"crl_file,omitempty"` + + // Enforcement indicates the behavior applied to requests without a valid + // client certificate. + Enforcement MTLSEnforcement `mapstructure:"enforcement" yaml:"enforcement,omitempty"` } // GetCA returns the certificate authority (or nil if unset). @@ -69,6 +94,14 @@ func (s *DownstreamMTLSSettings) GetCRL() ([]byte, error) { return nil, nil } +// GetEnforcement returns the enforcement behavior to apply. +func (s *DownstreamMTLSSettings) GetEnforcement() MTLSEnforcement { + if s.Enforcement == "" { + return MTLSEnforcementPolicyWithDefaultDeny + } + return s.Enforcement +} + func (s *DownstreamMTLSSettings) validate() error { if _, err := s.GetCA(); err != nil { return err @@ -84,15 +117,44 @@ func (s *DownstreamMTLSSettings) validate() error { } } + switch s.Enforcement { + case "", + MTLSEnforcementPolicy, + MTLSEnforcementPolicyWithDefaultDeny, + MTLSEnforcementRejectConnection: // OK + default: + return errors.New("unknown enforcement option") + } + return nil } func (s *DownstreamMTLSSettings) applySettingsProto( - _ context.Context, p *config.DownstreamMtlsSettings, + ctx context.Context, p *config.DownstreamMtlsSettings, ) { if p == nil { return } set(&s.CA, p.Ca) set(&s.CRL, p.Crl) + s.Enforcement = mtlsEnforcementFromProtoEnum(ctx, p.Enforcement) +} + +func mtlsEnforcementFromProtoEnum( + ctx context.Context, mode *config.MtlsEnforcementMode, +) MTLSEnforcement { + if mode == nil { + return "" + } + switch *mode { + case config.MtlsEnforcementMode_POLICY: + return MTLSEnforcementPolicy + case config.MtlsEnforcementMode_POLICY_WITH_DEFAULT_DENY: + return MTLSEnforcementPolicyWithDefaultDeny + case config.MtlsEnforcementMode_REJECT_CONNECTION: + return MTLSEnforcementRejectConnection + default: + log.Error(ctx).Msgf("unknown mTLS enforcement mode %s", mode) + return "" + } } diff --git a/config/mtls_test.go b/config/mtls_test.go index 52a8aceae..da2eb5f5f 100644 --- a/config/mtls_test.go +++ b/config/mtls_test.go @@ -63,6 +63,38 @@ func TestDownstreamMTLSSettingsGetCRL(t *testing.T) { } } +func TestDownstreamMTLSSettingsGetEnforcement(t *testing.T) { + t.Parallel() + + cases := []struct { + label string + settings DownstreamMTLSSettings + expected MTLSEnforcement + }{ + {"default", + DownstreamMTLSSettings{}, MTLSEnforcementPolicyWithDefaultDeny, + }, + {"policy", + DownstreamMTLSSettings{Enforcement: "policy"}, MTLSEnforcementPolicy, + }, + {"policy_with_default_deny", + DownstreamMTLSSettings{Enforcement: "policy_with_default_deny"}, + MTLSEnforcementPolicyWithDefaultDeny, + }, + {"reject_connection", + DownstreamMTLSSettings{Enforcement: "reject_connection"}, + MTLSEnforcementRejectConnection, + }, + } + + for i := range cases { + c := &cases[i] + t.Run(c.label, func(t *testing.T) { + assert.Equal(t, c.expected, c.settings.GetEnforcement()) + }) + } +} + func TestDownstreamMTLSSettingsValidate(t *testing.T) { t.Parallel() @@ -80,9 +112,12 @@ func TestDownstreamMTLSSettingsValidate(t *testing.T) { "CRL: cryptutil: invalid crl, no X509 CRL block found"}, {"bad CRL file", DownstreamMTLSSettings{CRLFile: "-"}, "CRL file: open -: no such file or directory"}, + {"bad enforcement mode", DownstreamMTLSSettings{Enforcement: "whatever"}, + "unknown enforcement option"}, {"OK", DownstreamMTLSSettings{ - CA: "dGhpc2lzZmluZQo=", - CRL: "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNOVENCbmdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTWhjTk1qTXdOekU1TWpFMQpNREUxV2hjTk16TXdOekUyTWpFMU1ERTFXcUF3TUM0d0h3WURWUjBqQkJnd0ZvQVVDeFEyY0JhNVl6cVZ6YW1wCmlOQ3g4S3dGRnlRd0N3WURWUjBVQkFRQ0FoQUFNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJnUUNZYW14OHBNK1IKQ2x5c2tjdTdvdWh1L1IxSnkxbldHeVd0S3BoWXEwWEZiT0xsbmsyWjdlRGZBWDhFZWoyRmF2cXh6YXBSMngyTwo0aUpORENtaXdZWVlVUzJYMkxKM3JSUkpYeVh2V2h0ZkhyeFVSZDZCaXRDMklYcHlrQnRWbGYzekFuWjhHWkZRClMxamRmeUxNdUVBaUR3SWFpM1l0OEhzRHAvcUcwODlvWGNvU3R5UWcvdVJwbVd5MDVBOXVDVk9mTkhTTFNadTgKbHI0cWF0bGV1MHdXYlYxYW1MOHRPOXg0Q1JrTzBvMVlhUXE0RG9PcnVQciszTmtUbVB2R2lkaDNGNzFWNklFQQpoK0t6ZGJSWHhGbUNDV0xXbXBKRGNyZ1I3S1VxWk9oVVV0K0RVcWFxaFY0NHFJMG5ycFIrUVpMb2hvRG9yOUx3CksrdWZqM24yOWVTUlgrM1B4K29WV1BUOFlaUDJ1S1BkaXppOTZtZTJqV1RyNTF4OUFqRW9KRHNUbllSbDkrdVkKU2hpVXhXblRkUXNvb2tuSWZjUy8wemZnWjg3R3ZVVnppbkNRekpwd1Z4ZDRBbHQ4QWxSK2ZYQXFOSW9PZ3V5dgpwL0N0UlZualZFN2w3SFcvaFFScTFKMGlqQ0NLd215Zi9LVGQ2RUs0VGRydmJYL1U5bXNWTThZPQotLS0tLUVORCBYNTA5IENSTC0tLS0tCg==", + CA: "dGhpc2lzZmluZQo=", + CRL: "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNOVENCbmdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTWhjTk1qTXdOekU1TWpFMQpNREUxV2hjTk16TXdOekUyTWpFMU1ERTFXcUF3TUM0d0h3WURWUjBqQkJnd0ZvQVVDeFEyY0JhNVl6cVZ6YW1wCmlOQ3g4S3dGRnlRd0N3WURWUjBVQkFRQ0FoQUFNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJnUUNZYW14OHBNK1IKQ2x5c2tjdTdvdWh1L1IxSnkxbldHeVd0S3BoWXEwWEZiT0xsbmsyWjdlRGZBWDhFZWoyRmF2cXh6YXBSMngyTwo0aUpORENtaXdZWVlVUzJYMkxKM3JSUkpYeVh2V2h0ZkhyeFVSZDZCaXRDMklYcHlrQnRWbGYzekFuWjhHWkZRClMxamRmeUxNdUVBaUR3SWFpM1l0OEhzRHAvcUcwODlvWGNvU3R5UWcvdVJwbVd5MDVBOXVDVk9mTkhTTFNadTgKbHI0cWF0bGV1MHdXYlYxYW1MOHRPOXg0Q1JrTzBvMVlhUXE0RG9PcnVQciszTmtUbVB2R2lkaDNGNzFWNklFQQpoK0t6ZGJSWHhGbUNDV0xXbXBKRGNyZ1I3S1VxWk9oVVV0K0RVcWFxaFY0NHFJMG5ycFIrUVpMb2hvRG9yOUx3CksrdWZqM24yOWVTUlgrM1B4K29WV1BUOFlaUDJ1S1BkaXppOTZtZTJqV1RyNTF4OUFqRW9KRHNUbllSbDkrdVkKU2hpVXhXblRkUXNvb2tuSWZjUy8wemZnWjg3R3ZVVnppbkNRekpwd1Z4ZDRBbHQ4QWxSK2ZYQXFOSW9PZ3V5dgpwL0N0UlZualZFN2w3SFcvaFFScTFKMGlqQ0NLd215Zi9LVGQ2RUs0VGRydmJYL1U5bXNWTThZPQotLS0tLUVORCBYNTA5IENSTC0tLS0tCg==", + Enforcement: "reject_connection", }, ""}, } diff --git a/pkg/grpc/config/config.pb.go b/pkg/grpc/config/config.pb.go index 684072b13..c026bb5fd 100644 --- a/pkg/grpc/config/config.pb.go +++ b/pkg/grpc/config/config.pb.go @@ -25,6 +25,58 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type MtlsEnforcementMode int32 + +const ( + MtlsEnforcementMode_UNKNOWN MtlsEnforcementMode = 0 + MtlsEnforcementMode_POLICY MtlsEnforcementMode = 1 + MtlsEnforcementMode_POLICY_WITH_DEFAULT_DENY MtlsEnforcementMode = 2 + MtlsEnforcementMode_REJECT_CONNECTION MtlsEnforcementMode = 3 +) + +// Enum value maps for MtlsEnforcementMode. +var ( + MtlsEnforcementMode_name = map[int32]string{ + 0: "UNKNOWN", + 1: "POLICY", + 2: "POLICY_WITH_DEFAULT_DENY", + 3: "REJECT_CONNECTION", + } + MtlsEnforcementMode_value = map[string]int32{ + "UNKNOWN": 0, + "POLICY": 1, + "POLICY_WITH_DEFAULT_DENY": 2, + "REJECT_CONNECTION": 3, + } +) + +func (x MtlsEnforcementMode) Enum() *MtlsEnforcementMode { + p := new(MtlsEnforcementMode) + *p = x + return p +} + +func (x MtlsEnforcementMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MtlsEnforcementMode) Descriptor() protoreflect.EnumDescriptor { + return file_config_proto_enumTypes[0].Descriptor() +} + +func (MtlsEnforcementMode) Type() protoreflect.EnumType { + return &file_config_proto_enumTypes[0] +} + +func (x MtlsEnforcementMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MtlsEnforcementMode.Descriptor instead. +func (MtlsEnforcementMode) EnumDescriptor() ([]byte, []int) { + return file_config_proto_rawDescGZIP(), []int{0} +} + type Route_AuthorizationHeaderMode int32 const ( @@ -58,11 +110,11 @@ func (x Route_AuthorizationHeaderMode) String() string { } func (Route_AuthorizationHeaderMode) Descriptor() protoreflect.EnumDescriptor { - return file_config_proto_enumTypes[0].Descriptor() + return file_config_proto_enumTypes[1].Descriptor() } func (Route_AuthorizationHeaderMode) Type() protoreflect.EnumType { - return &file_config_proto_enumTypes[0] + return &file_config_proto_enumTypes[1] } func (x Route_AuthorizationHeaderMode) Number() protoreflect.EnumNumber { @@ -1675,8 +1727,9 @@ type DownstreamMtlsSettings struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Ca *string `protobuf:"bytes,1,opt,name=ca,proto3,oneof" json:"ca,omitempty"` - Crl *string `protobuf:"bytes,2,opt,name=crl,proto3,oneof" json:"crl,omitempty"` + Ca *string `protobuf:"bytes,1,opt,name=ca,proto3,oneof" json:"ca,omitempty"` + Crl *string `protobuf:"bytes,2,opt,name=crl,proto3,oneof" json:"crl,omitempty"` + Enforcement *MtlsEnforcementMode `protobuf:"varint,3,opt,name=enforcement,proto3,enum=pomerium.config.MtlsEnforcementMode,oneof" json:"enforcement,omitempty"` } func (x *DownstreamMtlsSettings) Reset() { @@ -1725,6 +1778,13 @@ func (x *DownstreamMtlsSettings) GetCrl() string { return "" } +func (x *DownstreamMtlsSettings) GetEnforcement() MtlsEnforcementMode { + if x != nil && x.Enforcement != nil { + return *x.Enforcement + } + return MtlsEnforcementMode_UNKNOWN +} + type Settings_Certificate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2597,15 +2657,28 @@ var file_config_proto_rawDesc = []byte{ 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x42, 0x20, 0x0a, 0x1e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x67, 0x72, 0x61, - 0x70, 0x68, 0x22, 0x53, 0x0a, 0x16, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x4d, 0x74, 0x6c, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x13, 0x0a, 0x02, - 0x63, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x02, 0x63, 0x61, 0x88, 0x01, - 0x01, 0x12, 0x15, 0x0a, 0x03, 0x63, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, - 0x52, 0x03, 0x63, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x63, 0x61, 0x42, - 0x06, 0x0a, 0x04, 0x5f, 0x63, 0x72, 0x6c, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, - 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, - 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x68, 0x22, 0xb0, 0x01, 0x0a, 0x16, 0x44, 0x6f, 0x77, 0x6e, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x4d, 0x74, 0x6c, 0x73, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x13, 0x0a, + 0x02, 0x63, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x02, 0x63, 0x61, 0x88, + 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x63, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x03, 0x63, 0x72, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x0b, 0x65, 0x6e, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, + 0x2e, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x4d, 0x74, 0x6c, 0x73, 0x45, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, + 0x4d, 0x6f, 0x64, 0x65, 0x48, 0x02, 0x52, 0x0b, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, + 0x65, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f, 0x63, 0x61, 0x42, 0x06, 0x0a, + 0x04, 0x5f, 0x63, 0x72, 0x6c, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, + 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2a, 0x63, 0x0a, 0x13, 0x4d, 0x74, 0x6c, 0x73, 0x45, 0x6e, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0b, 0x0a, 0x07, + 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x4f, 0x4c, + 0x49, 0x43, 0x59, 0x10, 0x01, 0x12, 0x1c, 0x0a, 0x18, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x5f, + 0x57, 0x49, 0x54, 0x48, 0x5f, 0x44, 0x45, 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, 0x44, 0x45, 0x4e, + 0x59, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x5f, 0x43, 0x4f, + 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, + 0x6d, 0x2f, 0x70, 0x6f, 0x6d, 0x65, 0x72, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2620,69 +2693,71 @@ func file_config_proto_rawDescGZIP() []byte { return file_config_proto_rawDescData } -var file_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_config_proto_goTypes = []interface{}{ - (Route_AuthorizationHeaderMode)(0), // 0: pomerium.config.Route.AuthorizationHeaderMode - (*Config)(nil), // 1: pomerium.config.Config - (*RouteRewriteHeader)(nil), // 2: pomerium.config.RouteRewriteHeader - (*RouteRedirect)(nil), // 3: pomerium.config.RouteRedirect - (*Route)(nil), // 4: pomerium.config.Route - (*Policy)(nil), // 5: pomerium.config.Policy - (*Settings)(nil), // 6: pomerium.config.Settings - (*DownstreamMtlsSettings)(nil), // 7: pomerium.config.DownstreamMtlsSettings - nil, // 8: pomerium.config.Route.AllowedIdpClaimsEntry - nil, // 9: pomerium.config.Route.SetRequestHeadersEntry - nil, // 10: pomerium.config.Route.SetResponseHeadersEntry - nil, // 11: pomerium.config.Policy.AllowedIdpClaimsEntry - (*Settings_Certificate)(nil), // 12: pomerium.config.Settings.Certificate - (*Settings_StringList)(nil), // 13: pomerium.config.Settings.StringList - nil, // 14: pomerium.config.Settings.RequestParamsEntry - nil, // 15: pomerium.config.Settings.SetResponseHeadersEntry - nil, // 16: pomerium.config.Settings.JwtClaimsHeadersEntry - (*durationpb.Duration)(nil), // 17: google.protobuf.Duration - (*v3.Cluster)(nil), // 18: envoy.config.cluster.v3.Cluster - (v31.HttpConnectionManager_CodecType)(0), // 19: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType - (*crypt.PublicKeyEncryptionKey)(nil), // 20: pomerium.crypt.PublicKeyEncryptionKey - (*structpb.ListValue)(nil), // 21: google.protobuf.ListValue + (MtlsEnforcementMode)(0), // 0: pomerium.config.MtlsEnforcementMode + (Route_AuthorizationHeaderMode)(0), // 1: pomerium.config.Route.AuthorizationHeaderMode + (*Config)(nil), // 2: pomerium.config.Config + (*RouteRewriteHeader)(nil), // 3: pomerium.config.RouteRewriteHeader + (*RouteRedirect)(nil), // 4: pomerium.config.RouteRedirect + (*Route)(nil), // 5: pomerium.config.Route + (*Policy)(nil), // 6: pomerium.config.Policy + (*Settings)(nil), // 7: pomerium.config.Settings + (*DownstreamMtlsSettings)(nil), // 8: pomerium.config.DownstreamMtlsSettings + nil, // 9: pomerium.config.Route.AllowedIdpClaimsEntry + nil, // 10: pomerium.config.Route.SetRequestHeadersEntry + nil, // 11: pomerium.config.Route.SetResponseHeadersEntry + nil, // 12: pomerium.config.Policy.AllowedIdpClaimsEntry + (*Settings_Certificate)(nil), // 13: pomerium.config.Settings.Certificate + (*Settings_StringList)(nil), // 14: pomerium.config.Settings.StringList + nil, // 15: pomerium.config.Settings.RequestParamsEntry + nil, // 16: pomerium.config.Settings.SetResponseHeadersEntry + nil, // 17: pomerium.config.Settings.JwtClaimsHeadersEntry + (*durationpb.Duration)(nil), // 18: google.protobuf.Duration + (*v3.Cluster)(nil), // 19: envoy.config.cluster.v3.Cluster + (v31.HttpConnectionManager_CodecType)(0), // 20: envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType + (*crypt.PublicKeyEncryptionKey)(nil), // 21: pomerium.crypt.PublicKeyEncryptionKey + (*structpb.ListValue)(nil), // 22: google.protobuf.ListValue } var file_config_proto_depIdxs = []int32{ - 4, // 0: pomerium.config.Config.routes:type_name -> pomerium.config.Route - 6, // 1: pomerium.config.Config.settings:type_name -> pomerium.config.Settings - 3, // 2: pomerium.config.Route.redirect:type_name -> pomerium.config.RouteRedirect - 8, // 3: pomerium.config.Route.allowed_idp_claims:type_name -> pomerium.config.Route.AllowedIdpClaimsEntry - 17, // 4: pomerium.config.Route.timeout:type_name -> google.protobuf.Duration - 17, // 5: pomerium.config.Route.idle_timeout:type_name -> google.protobuf.Duration - 9, // 6: pomerium.config.Route.set_request_headers:type_name -> pomerium.config.Route.SetRequestHeadersEntry - 10, // 7: pomerium.config.Route.set_response_headers:type_name -> pomerium.config.Route.SetResponseHeadersEntry - 2, // 8: pomerium.config.Route.rewrite_response_headers:type_name -> pomerium.config.RouteRewriteHeader - 0, // 9: pomerium.config.Route.set_authorization_header:type_name -> pomerium.config.Route.AuthorizationHeaderMode - 18, // 10: pomerium.config.Route.envoy_opts:type_name -> envoy.config.cluster.v3.Cluster - 5, // 11: pomerium.config.Route.policies:type_name -> pomerium.config.Policy - 11, // 12: pomerium.config.Policy.allowed_idp_claims:type_name -> pomerium.config.Policy.AllowedIdpClaimsEntry - 13, // 13: pomerium.config.Settings.access_log_fields:type_name -> pomerium.config.Settings.StringList - 13, // 14: pomerium.config.Settings.authorize_log_fields:type_name -> pomerium.config.Settings.StringList - 12, // 15: pomerium.config.Settings.certificates:type_name -> pomerium.config.Settings.Certificate - 17, // 16: pomerium.config.Settings.timeout_read:type_name -> google.protobuf.Duration - 17, // 17: pomerium.config.Settings.timeout_write:type_name -> google.protobuf.Duration - 17, // 18: pomerium.config.Settings.timeout_idle:type_name -> google.protobuf.Duration - 17, // 19: pomerium.config.Settings.cookie_expire:type_name -> google.protobuf.Duration - 14, // 20: pomerium.config.Settings.request_params:type_name -> pomerium.config.Settings.RequestParamsEntry - 15, // 21: pomerium.config.Settings.set_response_headers:type_name -> pomerium.config.Settings.SetResponseHeadersEntry - 16, // 22: pomerium.config.Settings.jwt_claims_headers:type_name -> pomerium.config.Settings.JwtClaimsHeadersEntry - 17, // 23: pomerium.config.Settings.default_upstream_timeout:type_name -> google.protobuf.Duration - 12, // 24: pomerium.config.Settings.metrics_certificate:type_name -> pomerium.config.Settings.Certificate - 17, // 25: pomerium.config.Settings.grpc_client_timeout:type_name -> google.protobuf.Duration - 7, // 26: pomerium.config.Settings.downstream_mtls:type_name -> pomerium.config.DownstreamMtlsSettings - 19, // 27: pomerium.config.Settings.codec_type:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType - 20, // 28: pomerium.config.Settings.audit_key:type_name -> pomerium.crypt.PublicKeyEncryptionKey - 21, // 29: pomerium.config.Route.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue - 21, // 30: pomerium.config.Policy.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue - 31, // [31:31] is the sub-list for method output_type - 31, // [31:31] is the sub-list for method input_type - 31, // [31:31] is the sub-list for extension type_name - 31, // [31:31] is the sub-list for extension extendee - 0, // [0:31] is the sub-list for field type_name + 5, // 0: pomerium.config.Config.routes:type_name -> pomerium.config.Route + 7, // 1: pomerium.config.Config.settings:type_name -> pomerium.config.Settings + 4, // 2: pomerium.config.Route.redirect:type_name -> pomerium.config.RouteRedirect + 9, // 3: pomerium.config.Route.allowed_idp_claims:type_name -> pomerium.config.Route.AllowedIdpClaimsEntry + 18, // 4: pomerium.config.Route.timeout:type_name -> google.protobuf.Duration + 18, // 5: pomerium.config.Route.idle_timeout:type_name -> google.protobuf.Duration + 10, // 6: pomerium.config.Route.set_request_headers:type_name -> pomerium.config.Route.SetRequestHeadersEntry + 11, // 7: pomerium.config.Route.set_response_headers:type_name -> pomerium.config.Route.SetResponseHeadersEntry + 3, // 8: pomerium.config.Route.rewrite_response_headers:type_name -> pomerium.config.RouteRewriteHeader + 1, // 9: pomerium.config.Route.set_authorization_header:type_name -> pomerium.config.Route.AuthorizationHeaderMode + 19, // 10: pomerium.config.Route.envoy_opts:type_name -> envoy.config.cluster.v3.Cluster + 6, // 11: pomerium.config.Route.policies:type_name -> pomerium.config.Policy + 12, // 12: pomerium.config.Policy.allowed_idp_claims:type_name -> pomerium.config.Policy.AllowedIdpClaimsEntry + 14, // 13: pomerium.config.Settings.access_log_fields:type_name -> pomerium.config.Settings.StringList + 14, // 14: pomerium.config.Settings.authorize_log_fields:type_name -> pomerium.config.Settings.StringList + 13, // 15: pomerium.config.Settings.certificates:type_name -> pomerium.config.Settings.Certificate + 18, // 16: pomerium.config.Settings.timeout_read:type_name -> google.protobuf.Duration + 18, // 17: pomerium.config.Settings.timeout_write:type_name -> google.protobuf.Duration + 18, // 18: pomerium.config.Settings.timeout_idle:type_name -> google.protobuf.Duration + 18, // 19: pomerium.config.Settings.cookie_expire:type_name -> google.protobuf.Duration + 15, // 20: pomerium.config.Settings.request_params:type_name -> pomerium.config.Settings.RequestParamsEntry + 16, // 21: pomerium.config.Settings.set_response_headers:type_name -> pomerium.config.Settings.SetResponseHeadersEntry + 17, // 22: pomerium.config.Settings.jwt_claims_headers:type_name -> pomerium.config.Settings.JwtClaimsHeadersEntry + 18, // 23: pomerium.config.Settings.default_upstream_timeout:type_name -> google.protobuf.Duration + 13, // 24: pomerium.config.Settings.metrics_certificate:type_name -> pomerium.config.Settings.Certificate + 18, // 25: pomerium.config.Settings.grpc_client_timeout:type_name -> google.protobuf.Duration + 8, // 26: pomerium.config.Settings.downstream_mtls:type_name -> pomerium.config.DownstreamMtlsSettings + 20, // 27: pomerium.config.Settings.codec_type:type_name -> envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType + 21, // 28: pomerium.config.Settings.audit_key:type_name -> pomerium.crypt.PublicKeyEncryptionKey + 0, // 29: pomerium.config.DownstreamMtlsSettings.enforcement:type_name -> pomerium.config.MtlsEnforcementMode + 22, // 30: pomerium.config.Route.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue + 22, // 31: pomerium.config.Policy.AllowedIdpClaimsEntry.value:type_name -> google.protobuf.ListValue + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_config_proto_init() } @@ -2812,7 +2887,7 @@ func file_config_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_config_proto_rawDesc, - NumEnums: 1, + NumEnums: 2, NumMessages: 16, NumExtensions: 0, NumServices: 0, diff --git a/pkg/grpc/config/config.proto b/pkg/grpc/config/config.proto index d2db14056..464dec63b 100644 --- a/pkg/grpc/config/config.proto +++ b/pkg/grpc/config/config.proto @@ -245,4 +245,12 @@ message Settings { message DownstreamMtlsSettings { optional string ca = 1; optional string crl = 2; + optional MtlsEnforcementMode enforcement = 3; +} + +enum MtlsEnforcementMode { + UNKNOWN = 0; + POLICY = 1; + POLICY_WITH_DEFAULT_DENY = 2; + REJECT_CONNECTION = 3; } From f7e0b61c03f33b02fa686ad32dafc5c32bf3b5b4 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Wed, 9 Aug 2023 08:34:51 -0700 Subject: [PATCH 05/12] authorize: client cert fingerprint in set_request_headers (#4447) Add support for a new token $pomerium.client_cert_fingerprint in the set_request_headers option. This token will be replaced with the SHA-256 hash of the presented leaf client certificate. --- authorize/evaluator/evaluator.go | 2 +- authorize/evaluator/headers_evaluator.go | 24 +++++++------ authorize/evaluator/headers_evaluator_test.go | 36 ++++++++++++++++--- authorize/evaluator/opa/policy/headers.rego | 10 +++++- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/authorize/evaluator/evaluator.go b/authorize/evaluator/evaluator.go index 926e6491f..141561d97 100644 --- a/authorize/evaluator/evaluator.go +++ b/authorize/evaluator/evaluator.go @@ -225,7 +225,7 @@ func (e *Evaluator) evaluatePolicy(ctx context.Context, req *Request) (*PolicyRe } func (e *Evaluator) evaluateHeaders(ctx context.Context, req *Request) (*HeadersResponse, error) { - headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP.Hostname) + headersReq := NewHeadersRequestFromPolicy(req.Policy, req.HTTP) headersReq.Session = req.Session res, err := e.headersEvaluators.Evaluate(ctx, headersReq) if err != nil { diff --git a/authorize/evaluator/headers_evaluator.go b/authorize/evaluator/headers_evaluator.go index 8b6dbccf3..053ea46ee 100644 --- a/authorize/evaluator/headers_evaluator.go +++ b/authorize/evaluator/headers_evaluator.go @@ -17,21 +17,22 @@ import ( // HeadersRequest is the input to the headers.rego script. type HeadersRequest struct { - EnableGoogleCloudServerlessAuthentication bool `json:"enable_google_cloud_serverless_authentication"` - EnableRoutingKey bool `json:"enable_routing_key"` - Issuer string `json:"issuer"` - KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"` - ToAudience string `json:"to_audience"` - Session RequestSession `json:"session"` - PassAccessToken bool `json:"pass_access_token"` - PassIDToken bool `json:"pass_id_token"` - SetRequestHeaders map[string]string `json:"set_request_headers"` + EnableGoogleCloudServerlessAuthentication bool `json:"enable_google_cloud_serverless_authentication"` + EnableRoutingKey bool `json:"enable_routing_key"` + Issuer string `json:"issuer"` + KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"` + ToAudience string `json:"to_audience"` + Session RequestSession `json:"session"` + ClientCertificate ClientCertificateInfo `json:"client_certificate"` + PassAccessToken bool `json:"pass_access_token"` + PassIDToken bool `json:"pass_id_token"` + SetRequestHeaders map[string]string `json:"set_request_headers"` } // NewHeadersRequestFromPolicy creates a new HeadersRequest from a policy. -func NewHeadersRequestFromPolicy(policy *config.Policy, hostname string) *HeadersRequest { +func NewHeadersRequestFromPolicy(policy *config.Policy, http RequestHTTP) *HeadersRequest { input := new(HeadersRequest) - input.Issuer = hostname + input.Issuer = http.Hostname if policy != nil { input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH || @@ -42,6 +43,7 @@ func NewHeadersRequestFromPolicy(policy *config.Policy, hostname string) *Header } input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN + input.ClientCertificate = http.ClientCertificate input.SetRequestHeaders = policy.SetRequestHeaders } return input diff --git a/authorize/evaluator/headers_evaluator_test.go b/authorize/evaluator/headers_evaluator_test.go index 0b78af231..9a5af5683 100644 --- a/authorize/evaluator/headers_evaluator_test.go +++ b/authorize/evaluator/headers_evaluator_test.go @@ -34,16 +34,24 @@ func TestNewHeadersRequestFromPolicy(t *testing.T) { URL: *mustParseURL("http://to.example.com"), }, }, - }, "from.example.com") + }, RequestHTTP{ + Hostname: "from.example.com", + ClientCertificate: ClientCertificateInfo{ + Leaf: "--- FAKE CERTIFICATE ---", + }, + }) assert.Equal(t, &HeadersRequest{ EnableGoogleCloudServerlessAuthentication: true, Issuer: "from.example.com", ToAudience: "https://to.example.com", + ClientCertificate: ClientCertificateInfo{ + Leaf: "--- FAKE CERTIFICATE ---", + }, }, req) } func TestNewHeadersRequestFromPolicy_nil(t *testing.T) { - req := NewHeadersRequestFromPolicy(nil, "from.example.com") + req := NewHeadersRequestFromPolicy(nil, RequestHTTP{Hostname: "from.example.com"}) assert.Equal(t, &HeadersRequest{ Issuer: "from.example.com", }, req) @@ -184,16 +192,20 @@ func TestHeadersEvaluator(t *testing.T) { ToAudience: "to.example.com", Session: RequestSession{ID: "s1"}, SetRequestHeaders: map[string]string{ - "X-Custom-Header": "CUSTOM_VALUE", - "X-ID-Token": "$pomerium.id_token", - "X-Access-Token": "$pomerium.access_token", + "X-Custom-Header": "CUSTOM_VALUE", + "X-ID-Token": "$pomerium.id_token", + "X-Access-Token": "$pomerium.access_token", + "Client-Cert-Fingerprint": "$pomerium.client_cert_fingerprint", }, + ClientCertificate: ClientCertificateInfo{Leaf: testValidCert}, }) require.NoError(t, err) assert.Equal(t, "CUSTOM_VALUE", output.Headers.Get("X-Custom-Header")) assert.Equal(t, "ID_TOKEN", output.Headers.Get("X-ID-Token")) assert.Equal(t, "ACCESS_TOKEN", output.Headers.Get("X-Access-Token")) + assert.Equal(t, "17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704", + output.Headers.Get("Client-Cert-Fingerprint")) }) t.Run("set_request_headers original behavior", func(t *testing.T) { @@ -217,6 +229,20 @@ func TestHeadersEvaluator(t *testing.T) { assert.Equal(t, "Bearer ID_TOKEN", output.Headers.Get("Authorization")) }) + + t.Run("set_request_headers no client cert", func(t *testing.T) { + output, err := eval(t, nil, + &HeadersRequest{ + Issuer: "from.example.com", + ToAudience: "to.example.com", + SetRequestHeaders: map[string]string{ + "fingerprint": "$pomerium.client_cert_fingerprint", + }, + }) + require.NoError(t, err) + + assert.Equal(t, "", output.Headers.Get("fingerprint")) + }) } func decodeJWSPayload(t *testing.T, jws string) []byte { diff --git a/authorize/evaluator/opa/policy/headers.rego b/authorize/evaluator/opa/policy/headers.rego index 661e5f505..43aa1c86d 100644 --- a/authorize/evaluator/opa/policy/headers.rego +++ b/authorize/evaluator/opa/policy/headers.rego @@ -3,6 +3,8 @@ package pomerium.headers # input: # enable_google_cloud_serverless_authentication: boolean # enable_routing_key: boolean +# client_certificate: +# leaf: string # issuer: string # kubernetes_service_account_token: string # session: @@ -211,13 +213,19 @@ session_access_token = v { v := session.oauth_token.access_token } else = "" +client_cert_fingerprint = v { + cert := crypto.x509.parse_certificates(trim_space(input.client_certificate.leaf))[0] + v := crypto.sha256(base64.decode(cert.Raw)) +} else = "" + set_request_headers = h { h := [[header_name, header_value] | some header_name v1 := input.set_request_headers[header_name] v2 := replace(v1, "$pomerium.id_token", session_id_token) v3 := replace(v2, "$pomerium.access_token", session_access_token) - header_value := v3 + v4 := replace(v3, "$pomerium.client_cert_fingerprint", client_cert_fingerprint) + header_value := v4 ] } else = [] From ac475f4c5daa08087285352c04f4b0e3e5771a0b Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Wed, 9 Aug 2023 09:47:23 -0700 Subject: [PATCH 06/12] ppl: add new client certificate criterion (#4448) Add a new client_certificate criterion that accepts a "Certificate Matcher" object. Start with two certificate match conditions: fingerprint and SPKI hash, each of which can accept either a single string or an array of strings. Add new "client-certificate-ok" and "client-certificate-unauthorized" reason strings. --- pkg/policy/criteria/client_certificate.go | 164 ++++++++++++++ .../criteria/client_certificate_test.go | 203 ++++++++++++++++++ pkg/policy/criteria/criteria.go | 3 + pkg/policy/criteria/criteria_test.go | 3 +- pkg/policy/criteria/reasons.go | 54 ++--- 5 files changed, 400 insertions(+), 27 deletions(-) create mode 100644 pkg/policy/criteria/client_certificate.go create mode 100644 pkg/policy/criteria/client_certificate_test.go diff --git a/pkg/policy/criteria/client_certificate.go b/pkg/policy/criteria/client_certificate.go new file mode 100644 index 000000000..7082b2e80 --- /dev/null +++ b/pkg/policy/criteria/client_certificate.go @@ -0,0 +1,164 @@ +package criteria + +import ( + "encoding/base64" + "errors" + "fmt" + "regexp" + "strings" + + "github.com/open-policy-agent/opa/ast" + + "github.com/pomerium/pomerium/pkg/policy/generator" + "github.com/pomerium/pomerium/pkg/policy/parser" +) + +var clientCertificateBaseBody = ast.MustParseBody(` + cert := crypto.x509.parse_certificates(trim_space(input.http.client_certificate.leaf))[0] + fingerprint := crypto.sha256(base64.decode(cert.Raw)) + spki_hash := base64.encode(hex.decode( + crypto.sha256(base64.decode(cert.RawSubjectPublicKeyInfo)))) +`) + +type clientCertificateCriterion struct { + g *Generator +} + +func (clientCertificateCriterion) DataType() generator.CriterionDataType { + return CriterionDataTypeCertificateMatcher +} + +func (clientCertificateCriterion) Name() string { + return "client_certificate" +} + +func (c clientCertificateCriterion) GenerateRule( + _ string, data parser.Value, +) (*ast.Rule, []*ast.Rule, error) { + body := append(ast.Body(nil), clientCertificateBaseBody...) + + obj, ok := data.(parser.Object) + if !ok { + return nil, nil, fmt.Errorf("expected object for certificate matcher, got: %T", data) + } + + for k, v := range obj { + var err error + + switch k { + case "fingerprint": + err = addCertFingerprintCondition(&body, v) + case "spki_hash": + err = addCertSPKIHashCondition(&body, v) + default: + err = fmt.Errorf("unsupported certificate matcher condition: %s", k) + } + + if err != nil { + return nil, nil, err + } + } + + rule := NewCriterionRule(c.g, c.Name(), + ReasonClientCertificateOK, ReasonClientCertificateUnauthorized, + body) + + return rule, nil, nil +} + +func addCertFingerprintCondition(body *ast.Body, data parser.Value) error { + var pa parser.Array + switch v := data.(type) { + case parser.Array: + pa = v + case parser.String: + pa = parser.Array{data} + default: + return errors.New("certificate fingerprint condition expects a string or array of strings") + } + + ra := ast.NewArray() + for _, v := range pa { + f, err := canonicalCertFingerprint(v) + if err != nil { + return err + } + ra = ra.Append(ast.NewTerm(f)) + } + + *body = append(*body, + ast.Assign.Expr(ast.VarTerm("allowed_fingerprints"), ast.NewTerm(ra)), + ast.Equal.Expr(ast.VarTerm("fingerprint"), ast.VarTerm("allowed_fingerprints[_]"))) + return nil +} + +// The long certificate fingerprint format is 32 uppercase hex-encoded bytes +// separated by colons. +var longCertFingerprintRE = regexp.MustCompile("^[0-9A-F]{2}(:[0-9A-F]{2}){31}$") + +// The short certificate fingerprint format is 32 lowercase hex-encoded bytes. +var shortCertFingerprintRE = regexp.MustCompile("^[0-9a-f]{64}$") + +// canonicalCertFingeprint converts a single fingerprint value into the format +// that our Rego logic generates. +func canonicalCertFingerprint(data parser.Value) (ast.Value, error) { + s, ok := data.(parser.String) + if !ok { + return nil, fmt.Errorf("certificate fingerprint must be a string (was %v)", data) + } + + f := string(s) + if f == "" { + return nil, errors.New("certificate fingerprint must not be empty") + } else if shortCertFingerprintRE.MatchString(f) { + return ast.String(f), nil + } else if longCertFingerprintRE.MatchString(f) { + f = strings.ToLower(strings.ReplaceAll(f, ":", "")) + return ast.String(f), nil + } + return nil, fmt.Errorf("unsupported certificate fingerprint format (%s)", f) +} + +func addCertSPKIHashCondition(body *ast.Body, data parser.Value) error { + var pa parser.Array + switch v := data.(type) { + case parser.Array: + pa = v + case parser.String: + pa = parser.Array{data} + default: + return errors.New("certificate SPKI hash condition expects a string or array of strings") + } + + ra := ast.NewArray() + for _, v := range pa { + s, ok := v.(parser.String) + if !ok { + return fmt.Errorf("certificate SPKI hash must be a string (was %v)", v) + } + + h := string(s) + if h == "" { + return errors.New("certificate SPKI hash must not be empty") + } else if b, err := base64.StdEncoding.DecodeString(h); err != nil || len(b) != 32 { + return fmt.Errorf("certificate SPKI hash must be a base64-encoded SHA-256 hash "+ + "(was %s)", h) + } + + ra = ra.Append(ast.NewTerm(ast.String(h))) + } + + *body = append(*body, + ast.Assign.Expr(ast.VarTerm("allowed_spki_hashes"), ast.NewTerm(ra)), + ast.Equal.Expr(ast.VarTerm("spki_hash"), ast.VarTerm("allowed_spki_hashes[_]"))) + return nil +} + +// ClientCertificate returns a Criterion on a client certificate. +func ClientCertificate(generator *Generator) Criterion { + return clientCertificateCriterion{g: generator} +} + +func init() { + Register(ClientCertificate) +} diff --git a/pkg/policy/criteria/client_certificate_test.go b/pkg/policy/criteria/client_certificate_test.go new file mode 100644 index 000000000..77fc35437 --- /dev/null +++ b/pkg/policy/criteria/client_certificate_test.go @@ -0,0 +1,203 @@ +package criteria + +import ( + "strings" + "testing" + + "github.com/open-policy-agent/opa/ast" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/pkg/policy/parser" +) + +const testCert = ` +-----BEGIN CERTIFICATE----- +MIIBYTCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwNzMxMTUzMzE5WjAeMRww +GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D +AQcDQgAEfAYP3ZwiKJgk9zXpR/CMHYlAxjweJaMJihIS2FTA5gb0xBcTEe5AGpNF +CHWPk4YCB25VeHg9GmY9Q1+qDD1hdqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAUXep6D8FTP6+5ZdR/HjP3pYfmxkwwCgYIKoZIzj0EAwIDRwAw +RAIgProROtxpvKS/qjrjonSvacnhdU0JwoXj2DgYvF/qjrUCIAXlHkdEzyXmTLuu +/YxuOibV35vlaIzj21GRj4pYmVR1 +-----END CERTIFICATE-----` + +func TestClientCertificate(t *testing.T) { + t.Parallel() + + cases := []struct { + label string + policy string + cert string + expected A + }{ + {"no certificate", + `allow: + or: + - client_certificate: + fingerprint: 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704`, + "", + A{false, A{ReasonClientCertificateUnauthorized}, M{}}, + }, + {"no fingerprint match", + `allow: + or: + - client_certificate: + fingerprint: df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a`, + testCert, + A{false, A{ReasonClientCertificateUnauthorized}, M{}}, + }, + {"fingerprint match", + `allow: + or: + - client_certificate: + fingerprint: 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704`, + testCert, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, + {"fingerprint list match", + `allow: + or: + - client_certificate: + fingerprint: + - 17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704 + - df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a`, + testCert, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, + {"spki hash match", + `allow: + or: + - client_certificate: + spki_hash: FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U=`, + testCert, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, + {"spki hash list match", + `allow: + or: + - client_certificate: + spki_hash: + - FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U= + - NvqYIYSbgK2vCJpQhObf77vv+bQWtc5ek5RIOwPiC9A=`, + testCert, + A{true, A{ReasonClientCertificateOK}, M{}}, + }, + } + + for i := range cases { + c := cases[i] + t.Run(c.label, func(t *testing.T) { + t.Parallel() + + input := Input{ + HTTP: InputHTTP{ + ClientCertificate: ClientCertificateInfo{ + Leaf: c.cert, + }, + }, + } + res, err := evaluate(t, c.policy, nil, input) + require.NoError(t, err) + assert.Equal(t, c.expected, res["allow"]) + }) + } +} + +func TestCanonicalCertFingerprint(t *testing.T) { + t.Parallel() + + cases := []struct { + label string + input string + output string + err string + }{ + {"object", + `{}`, "", "certificate fingerprint must be a string (was {})", + }, + {"empty", + `""`, "", "certificate fingerprint must not be empty", + }, + {"SHA-1 fingerprint", + `"B1:E6:A2:DC:DD:6B:87:A4:9B:C5:7C:3B:7C:7F:1C:74:9A:DB:88:36"`, + "", "unsupported certificate fingerprint format (B1:E6:A2:DC:DD:6B:87:A4:9B:C5:7C:3B:7C:7F:1C:74:9A:DB:88:36)", + }, + {"uppercase short", + `"DF6FF72FE9116521268F6F2DD4966F51DF479883FE7037B39F75916AC3049D1A"`, + "", "unsupported certificate fingerprint format (DF6FF72FE9116521268F6F2DD4966F51DF479883FE7037B39F75916AC3049D1A)", + }, + {"valid short", + `"df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a"`, + "df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a", "", + }, + {"lowercase long", + `"df:6f:f7:2f:e9:11:65:21:26:8f:6f:2d:d4:96:6f:51:df:47:98:83:fe:70:37:b3:9f:75:91:6a:c3:04:9d:1a"`, + "", "unsupported certificate fingerprint format (df:6f:f7:2f:e9:11:65:21:26:8f:6f:2d:d4:96:6f:51:df:47:98:83:fe:70:37:b3:9f:75:91:6a:c3:04:9d:1a)", + }, + {"valid long", + `"DF:6F:F7:2F:E9:11:65:21:26:8F:6F:2D:D4:96:6F:51:DF:47:98:83:FE:70:37:B3:9F:75:91:6A:C3:04:9D:1A"`, + "df6ff72fe9116521268f6f2dd4966f51df479883fe7037b39f75916ac3049d1a", "", + }, + } + + for i := range cases { + c := cases[i] + t.Run(c.label, func(t *testing.T) { + t.Parallel() + + value, err := parser.ParseValue(strings.NewReader(c.input)) + require.NoError(t, err) + + f, err := canonicalCertFingerprint(value) + if c.err == "" { + require.NoError(t, err) + assert.Equal(t, ast.String(c.output), f) + } else { + assert.Equal(t, c.err, err.Error()) + } + }) + } +} + +func TestSPKIHashFormatErrors(t *testing.T) { + t.Parallel() + + cases := []struct { + label string + input string + err string + }{ + {"object", + `{}`, "certificate SPKI hash condition expects a string or array of strings", + }, + {"not base64", + `"not%valid%base64%data"`, "certificate SPKI hash must be a base64-encoded SHA-256 hash (was not%valid%base64%data)", + }, + {"SHA-1 hash", + `"VYby3BAoHawLLtsyckwo5Q=="`, "certificate SPKI hash must be a base64-encoded SHA-256 hash (was VYby3BAoHawLLtsyckwo5Q==)", + }, + {"valid", + `"FsDbM0rUYIiL3V339eIKqiz6HPSB+Pz2WeAWhqlqh8U="`, "", + }, + } + + for i := range cases { + c := cases[i] + t.Run(c.label, func(t *testing.T) { + t.Parallel() + + value, err := parser.ParseValue(strings.NewReader(c.input)) + require.NoError(t, err) + + var body ast.Body + err = addCertSPKIHashCondition(&body, value) + if c.err == "" { + assert.NoError(t, err) + } else { + assert.Equal(t, c.err, err.Error()) + } + }) + } +} diff --git a/pkg/policy/criteria/criteria.go b/pkg/policy/criteria/criteria.go index 9779899c3..cd6924c1f 100644 --- a/pkg/policy/criteria/criteria.go +++ b/pkg/policy/criteria/criteria.go @@ -45,6 +45,9 @@ func Register(criterionConstructor CriterionConstructor) { } const ( + // CriterionDataTypeCertificateMatcher indicates the expected data type is + // a certificate matcher. + CriterionDataTypeCertificateMatcher CriterionDataType = "certificate_matcher" // CriterionDataTypeStringListMatcher indicates the expected data type is a string list matcher. CriterionDataTypeStringListMatcher CriterionDataType = "string_list_matcher" // CriterionDataTypeStringMatcher indicates the expected data type is a string matcher. diff --git a/pkg/policy/criteria/criteria_test.go b/pkg/policy/criteria/criteria_test.go index 103a94b8e..decc13b3e 100644 --- a/pkg/policy/criteria/criteria_test.go +++ b/pkg/policy/criteria/criteria_test.go @@ -43,7 +43,8 @@ type ( ID string `json:"id"` } ClientCertificateInfo struct { - Presented bool `json:"presented"` + Presented bool `json:"presented"` + Leaf string `json:"leaf"` } ) diff --git a/pkg/policy/criteria/reasons.go b/pkg/policy/criteria/reasons.go index 906ebfb08..44c28b718 100644 --- a/pkg/policy/criteria/reasons.go +++ b/pkg/policy/criteria/reasons.go @@ -7,32 +7,34 @@ type Reason string // Well-known reasons. const ( - ReasonAccept = "accept" - ReasonClaimOK = "claim-ok" - ReasonClaimUnauthorized = "claim-unauthorized" - ReasonClientCertificateRequired = "client-certificate-required" - ReasonCORSRequest = "cors-request" - ReasonDeviceOK = "device-ok" - ReasonDeviceUnauthenticated = "device-unauthenticated" - ReasonDeviceUnauthorized = "device-unauthorized" - ReasonDomainOK = "domain-ok" - ReasonDomainUnauthorized = "domain-unauthorized" - ReasonEmailOK = "email-ok" - ReasonEmailUnauthorized = "email-unauthorized" - ReasonHTTPMethodOK = "http-method-ok" - ReasonHTTPMethodUnauthorized = "http-method-unauthorized" - ReasonHTTPPathOK = "http-path-ok" - ReasonHTTPPathUnauthorized = "http-path-unauthorized" - ReasonInvalidClientCertificate = "invalid-client-certificate" - ReasonNonCORSRequest = "non-cors-request" - ReasonNonPomeriumRoute = "non-pomerium-route" - ReasonPomeriumRoute = "pomerium-route" - ReasonReject = "reject" - ReasonRouteNotFound = "route-not-found" - ReasonUserOK = "user-ok" - ReasonUserUnauthenticated = "user-unauthenticated" // user needs to log in - ReasonUserUnauthorized = "user-unauthorized" // user does not have access - ReasonValidClientCertificate = "valid-client-certificate" + ReasonAccept = "accept" + ReasonClaimOK = "claim-ok" + ReasonClaimUnauthorized = "claim-unauthorized" + ReasonClientCertificateOK = "client-certificate-ok" + ReasonClientCertificateUnauthorized = "client-certificate-unauthorized" + ReasonClientCertificateRequired = "client-certificate-required" + ReasonCORSRequest = "cors-request" + ReasonDeviceOK = "device-ok" + ReasonDeviceUnauthenticated = "device-unauthenticated" + ReasonDeviceUnauthorized = "device-unauthorized" + ReasonDomainOK = "domain-ok" + ReasonDomainUnauthorized = "domain-unauthorized" + ReasonEmailOK = "email-ok" + ReasonEmailUnauthorized = "email-unauthorized" + ReasonHTTPMethodOK = "http-method-ok" + ReasonHTTPMethodUnauthorized = "http-method-unauthorized" + ReasonHTTPPathOK = "http-path-ok" + ReasonHTTPPathUnauthorized = "http-path-unauthorized" + ReasonInvalidClientCertificate = "invalid-client-certificate" + ReasonNonCORSRequest = "non-cors-request" + ReasonNonPomeriumRoute = "non-pomerium-route" + ReasonPomeriumRoute = "pomerium-route" + ReasonReject = "reject" + ReasonRouteNotFound = "route-not-found" + ReasonUserOK = "user-ok" + ReasonUserUnauthenticated = "user-unauthenticated" // user needs to log in + ReasonUserUnauthorized = "user-unauthorized" // user does not have access + ReasonValidClientCertificate = "valid-client-certificate" ) // Reasons is a collection of reasons. From 0fcc3f16def2af03c54d15c86ad602c81f9ae990 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Thu, 10 Aug 2023 09:33:29 -0700 Subject: [PATCH 07/12] authorize: allow client certificate intermediates (#4451) Update the isValidClientCertificate() method to consider any client-supplied intermediate certificates. Previously, in order to trust client certificates issued by an intermediate CA, users would need to include that intermediate CA's certificate directly in the client_ca setting. After this change, only the trusted root CA needs to be set: as long as the client can supply a set of certificates that chain back to this trusted root, the client's certificate will validate successfully. Rework the previous CRL checking logic to now consider CRLs for all issuers in the verified chains. --- authorize/evaluator/functions.go | 128 +++++++++--------- authorize/evaluator/functions_test.go | 123 ++++++++++++----- authorize/evaluator/gen-test-certs.go | 22 +++ authorize/evaluator/headers_evaluator_test.go | 2 +- 4 files changed, 181 insertions(+), 94 deletions(-) diff --git a/authorize/evaluator/functions.go b/authorize/evaluator/functions.go index 76f631361..fa0ea75d3 100644 --- a/authorize/evaluator/functions.go +++ b/authorize/evaluator/functions.go @@ -12,7 +12,7 @@ import ( "github.com/pomerium/pomerium/internal/log" ) -var isValidClientCertificateCache, _ = lru.New2Q[[3]string, bool](100) +var isValidClientCertificateCache, _ = lru.New2Q[[4]string, bool](100) func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (bool, error) { // when ca is the empty string, client certificates are not required @@ -21,22 +21,24 @@ func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (b } cert := certInfo.Leaf + intermediates := certInfo.Intermediates if cert == "" { return false, nil } - cacheKey := [3]string{ca, crl, cert} + cacheKey := [4]string{ca, crl, cert, intermediates} value, ok := isValidClientCertificateCache.Get(cacheKey) if ok { return value, nil } - roots, err := parseCertificates([]byte(ca)) - if err != nil { - return false, err - } + roots := x509.NewCertPool() + roots.AppendCertsFromPEM([]byte(ca)) + + intermediatesPool := x509.NewCertPool() + intermediatesPool.AppendCertsFromPEM([]byte(intermediates)) xcert, err := parseCertificate(cert) if err != nil { @@ -48,7 +50,7 @@ func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (b return false, err } - verifyErr := verifyClientCertificate(xcert, roots, crls) + verifyErr := verifyClientCertificate(xcert, roots, intermediatesPool, crls) valid := verifyErr == nil if verifyErr != nil { @@ -60,52 +62,73 @@ func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (b return valid, nil } -var errCertificateRevoked = errors.New("certificate revoked") - func verifyClientCertificate( cert *x509.Certificate, - roots map[string]*x509.Certificate, + roots *x509.CertPool, + intermediates *x509.CertPool, crls map[string]*x509.RevocationList, ) error { - rootPool := x509.NewCertPool() - for _, root := range roots { - rootPool.AddCert(root) - } - - if _, err := cert.Verify(x509.VerifyOptions{ - Roots: rootPool, - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - }); err != nil { + chains, err := cert.Verify(x509.VerifyOptions{ + Roots: roots, + Intermediates: intermediates, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }) + if err != nil { return err } - // Consult any CRL for the presented certificate's Issuer. - issuer := string(cert.RawIssuer) - crl := crls[issuer] - if crl == nil { - return nil - } - - // Do we have a corresponding trusted CA certificate? - root, ok := roots[issuer] - if !ok { - return fmt.Errorf("could not check CRL: no matching trusted CA for issuer %s", - cert.Issuer) - } - - // Is the CRL signature itself valid? - if err := crl.CheckSignatureFrom(root); err != nil { - return fmt.Errorf("could not check CRL for issuer %s: signature verification "+ - "error: %w", cert.Issuer, err) - } - - // Is the client certificate listed as revoked in this CRL? - for i := range crl.RevokedCertificates { - if cert.SerialNumber.Cmp(crl.RevokedCertificates[i].SerialNumber) == 0 { - return errCertificateRevoked + // At least one of the verified chains must also pass revocation checking. + err = errors.New("internal error: no verified chains") + for _, chain := range chains { + err = validateClientCertificateChain(chain, crls) + if err == nil { + return nil } } + // Return an error from one of the chains that did not validate. + // (In the common case there will be at most one verified chain.) + return err +} + +func validateClientCertificateChain( + chain []*x509.Certificate, + crls map[string]*x509.RevocationList, +) error { + // Consult CRLs for all CAs in the chain (that is, all certificates except + // for the first one). To match Envoy's behavior, if a CRL is provided for + // any CA in the chain, CRLs must be provided for all CAs in the chain (see + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/common.proto). + var anyIssuerHasCRL bool + var lastIssuerWithoutCRL *x509.Certificate + for i := 0; i < len(chain)-1; i++ { + cert, issuer := chain[i], chain[i+1] + crl := crls[string(issuer.RawSubject)] + if crl == nil { + lastIssuerWithoutCRL = issuer + continue + } + + anyIssuerHasCRL = true + + // Is the CRL signature itself valid? + if err := crl.CheckSignatureFrom(issuer); err != nil { + return fmt.Errorf("CRL signature verification failed for issuer %q: %w", + issuer.Subject, err) + } + + // Is the certificate listed as revoked in the CRL? + for i := range crl.RevokedCertificates { + if cert.SerialNumber.Cmp(crl.RevokedCertificates[i].SerialNumber) == 0 { + return fmt.Errorf("certificate %q was revoked", cert.Subject) + } + } + } + + if anyIssuerHasCRL && lastIssuerWithoutCRL != nil { + return fmt.Errorf("no CRL provided for issuer %q", lastIssuerWithoutCRL.Subject) + } + return nil } @@ -120,25 +143,6 @@ func parseCertificate(pemStr string) (*x509.Certificate, error) { return x509.ParseCertificate(block.Bytes) } -func parseCertificates(certs []byte) (map[string]*x509.Certificate, error) { - m := make(map[string]*x509.Certificate) - for { - var block *pem.Block - block, certs = pem.Decode(certs) - if block == nil { - return m, nil - } - if block.Type != "CERTIFICATE" { - continue - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, err - } - m[string(cert.RawSubject)] = cert - } -} - func parseCRLs(crl []byte) (map[string]*x509.RevocationList, error) { m := make(map[string]*x509.RevocationList) for { diff --git a/authorize/evaluator/functions_test.go b/authorize/evaluator/functions_test.go index f87ec953a..ea3b271f6 100644 --- a/authorize/evaluator/functions_test.go +++ b/authorize/evaluator/functions_test.go @@ -14,60 +14,85 @@ import ( const ( testCA = ` -----BEGIN CERTIFICATE----- -MIIBZzCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwNzMxMTUzMzE5WjAaMRgw +MIIBaTCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAaMRgw FgYDVQQDEw9UcnVzdGVkIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AARGMVCBvgbkVB3OPltnBHAy9s9rtog2rlnzZ4BKzPBbLEM0uPYTOZa0LLxSMtCj -N+Bu3wfGPgHU6/pJ2uEky7/Uo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQUXep6D8FTP6+5ZdR/HjP3pYfmxkwwCgYIKoZIzj0E -AwIDRwAwRAIgSS5J6ii/n0gf2/UAMFb+UVG8n0nb1dcBCG55fSlWlVECIENVK+X3 -6SfUhfYSVBvOdS08AzMVvOM7aZbWaY9UirIf +AATcFCe6i6IqnevuUoR8nTrka8fikGYB3ciKfRyS0NUfm27MGsbuU2ribMYjhuz2 +K4/nU7A2hcu393JNKriXgwoyo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0E +AwIDSQAwRgIhAJIFP4r9Gbo0D2MM/fzPx5wtXsjH1IoQMpn0aw+G1WkmAiEAi56g +gO7l3bJj1YZtBv3tEkZPzaZ+xL3Nllcjv1K12Ac= -----END CERTIFICATE----- ` testValidCert = ` -----BEGIN CERTIFICATE----- MIIBYTCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwNzMxMTUzMzE5WjAeMRww +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAeMRww GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAEfAYP3ZwiKJgk9zXpR/CMHYlAxjweJaMJihIS2FTA5gb0xBcTEe5AGpNF -CHWPk4YCB25VeHg9GmY9Q1+qDD1hdqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw -HwYDVR0jBBgwFoAUXep6D8FTP6+5ZdR/HjP3pYfmxkwwCgYIKoZIzj0EAwIDRwAw -RAIgProROtxpvKS/qjrjonSvacnhdU0JwoXj2DgYvF/qjrUCIAXlHkdEzyXmTLuu -/YxuOibV35vlaIzj21GRj4pYmVR1 +AQcDQgAE3xjPIJPp9v+qu89DNHnqcIfkOSkLb8irRAnFmAYdJJLRqWRNRjmzRHtZ +htT4TWvhEw6VsFbRlsd510+dEASm9aM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0EAwIDRwAw +RAIgYZaCYXhfBBR8w/AfqUm9MVLYrgkz78mndNFFjz+YvpwCIFsfyIjft/vRcuaU +xlJFtrmFMSt4x1TecZfJsWDA0M55 -----END CERTIFICATE----- ` testUntrustedCert = ` -----BEGIN CERTIFICATE----- MIIBZzCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz -dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA3MzExNTMzMTlaMCAx +dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDYyMTEzMjRaMCAx HjAcBgNVBAMTFXVudHJ1c3RlZCBjbGllbnQgY2VydDBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABBG2Qo/l0evcNKjwaJsi04BJJh7ec064lRiKaRMNRUK+UxkKmfbn -0FobVtlioTmzeWCX8OJFPfO7y7/VLMiGVr+jODA2MBMGA1UdJQQMMAoGCCsGAQUF -BwMCMB8GA1UdIwQYMBaAFCd2l26OflZF3LTFUEBB54ZQV3AUMAoGCCqGSM49BAMC -A0kAMEYCIQCYEk3D4nHevIlKFg6f7O2/GdptzKU6F05pz4B3Aa8ahAIhAJcBGUNm -cqQQJNOelJJmMeFOzmmTk7oNFxCGEC00wlGn +SM49AwEHA0IABCLf7g3vlTE+xuIDttn/FYMpAAzlCKqr37rzBnBtuEhCDypW6Y/Y +MbIboFx9o2M9FYLzDJCS7Bj3cp2qd145HdqjODA2MBMGA1UdJQQMMAoGCCsGAQUF +BwMCMB8GA1UdIwQYMBaAFGGTTkLU8r+tXUCs+nMR5Xm3KkgLMAoGCCqGSM49BAMC +A0kAMEYCIQCELFkfQak8X+Yvc7H95j+shSnVgwUuOYT1Lv2NLT1qPAIhAMEvKiwc +ZkWK0F4PJLpSt1wsbnVtfK7kXwHxdp2Z8yy7 -----END CERTIFICATE----- ` testRevokedCert = ` -----BEGIN CERTIFICATE----- -MIIBYzCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwNzMxMTUzMzE5WjAeMRww +MIIBYTCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAeMRww GgYDVQQDExNyZXZva2VkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAEoN/gKhZgyKhTmiC3qLHDQ54TIpgXBTvGKrdIRHO616XMkzj0lFZMHG5u -LGK3qo8wJtyoalOFTkSck0kl3PD/9qM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw -HwYDVR0jBBgwFoAUXep6D8FTP6+5ZdR/HjP3pYfmxkwwCgYIKoZIzj0EAwIDSQAw -RgIhAK6/oLtzvrK2Vrt1MRZJ6aGU2Cz28X0Y/4TOwFSvCK9AAiEAm4XPQXy6L0PE -vfXoV8RW/RnndDhf8iDALvAaAuS82fU= +AQcDQgAENFshpve+if3UmlNyPi5sVz8A03F9AAt6u1LxqiR5cMO6eU+L91Bey/XC +XYrqgpJzbRgTuC4LCFx6cwwl5ff/4KM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0EAwIDRwAw +RAIgappua0SnpXDzp9nml4iHqKtYAHTn/rg0w405ahdqBQwCIHulKmPGFNLDw1dq +1bZyKsG1t58DfFsO9G27sRssvCgV -----END CERTIFICATE----- ` testCRL = ` -----BEGIN X509 CRL----- -MIHfMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD -QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgwMzE1MzMxOVqgMDAuMB8G -A1UdIwQYMBaAFF3qeg/BUz+vuWXUfx4z96WH5sZMMAsGA1UdFAQEAgIgADAKBggq -hkjOPQQDAgNJADBGAiEApMG/hJxlMe9QNF8cCVjOFyTfVVBkfKtrFQDmElO46x4C -IQCX9SYteNaaW+NVmGED6QfHXRWnDqHnXfe/mLxmnPVWzA== +MIHeMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD +QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgwOTIxMTMyNFqgMDAuMB8G +A1UdIwQYMBaAFNEl9Mb/X+oGAHCwvdus/gbfb0wDMAsGA1UdFAQEAgIgADAKBggq +hkjOPQQDAgNIADBFAiEA1QoleqO9qKhxSxKUc+SOQlFTG9sTbs3ztniUhi0CxhAC +IBElm/lXpVVuWrt0PJhcQHhHqbOxnfkx3HUxVEBWMOzX -----END X509 CRL----- +` + testIntermediateCA = ` +-----BEGIN CERTIFICATE----- +MIIBkjCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAiMSAw +HgYDVQQDExdUcnVzdGVkIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABJyxF8EUBMMh/avAul6M8AjoKstuIULPIHOvjYftT/hSqGHNYM6g +0NIBW1g2QX/fnHG9tBy45ReTkVY5HMoO2wujYzBhMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRe0Fh8zjBKzxms/xpbS/vHY5hqujAf +BgNVHSMEGDAWgBTRJfTG/1/qBgBwsL3brP4G329MAzAKBggqhkjOPQQDAgNJADBG +AiEAsnob34JrBGbJjoTZS84mfno2Vb+QPJ1xy3U7AbgyYM4CIQCL3P2A3w1Z87Nr +0A/i8rXw+kiGP1OHbs4k85ZIg6FAtQ== +-----END CERTIFICATE----- +` + testValidIntermediateCert = ` +-----BEGIN CERTIFICATE----- +MIIBdTCCARqgAwIBAgICEAAwCgYIKoZIzj0EAwIwIjEgMB4GA1UEAxMXVHJ1c3Rl +ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDYyMTEz +MjRaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvv9cmqtcuPuSXv6Qup0ycveDtx4PYC4V +UKp5BU1B/1h/IupoIlX165rERkNCxyDXjfw5zkcHXsP1qRbc3LSXT6M4MDYwEwYD +VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUXtBYfM4wSs8ZrP8aW0v7x2OY +arowCgYIKoZIzj0EAwIDSQAwRgIhAJJwdjSiC3avGDc1KZo/AZ1cMPDcFZkI92E6 +BVAnH/e8AiEAjy8cP1msG62BeDaAVU5NcU9RAXDw1Oz4HkpELXQWqK8= +-----END CERTIFICATE----- ` ) @@ -90,6 +115,32 @@ func Test_isValidClientCertificate(t *testing.T) { assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) + t.Run("valid cert with intermediate", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidIntermediateCert, + Intermediates: testIntermediateCA, + }) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + }) + t.Run("valid cert missing intermediate", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidIntermediateCert, + Intermediates: "", + }) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) + t.Run("intermediate CA as root", func(t *testing.T) { + valid, err := isValidClientCertificate(testIntermediateCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidIntermediateCert, + }) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + }) t.Run("unsigned cert", func(t *testing.T) { valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ Presented: true, @@ -129,4 +180,14 @@ func Test_isValidClientCertificate(t *testing.T) { assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) + t.Run("missing CRL", func(t *testing.T) { + // If a CRL is provided for any CA, it must be provided for all CAs. + valid, err := isValidClientCertificate(testCA, testCRL, ClientCertificateInfo{ + Presented: true, + Leaf: testValidIntermediateCert, + Intermediates: testIntermediateCA, + }) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) } diff --git a/authorize/evaluator/gen-test-certs.go b/authorize/evaluator/gen-test-certs.go index 171473509..edbe4e17c 100644 --- a/authorize/evaluator/gen-test-certs.go +++ b/authorize/evaluator/gen-test-certs.go @@ -90,6 +90,26 @@ func main() { ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, rootCA, rootKey) + intermediatePEM, intermediateCA, intermediateKey := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1003), + Subject: pkix.Name{ + CommonName: "Trusted Intermediate CA", + }, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + BasicConstraintsValid: true, + IsCA: true, + }, rootCA, rootKey) + + trustedClientCert2PEM, _, _ := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1000), + Subject: pkix.Name{ + CommonName: "client cert from intermediate", + }, + NotAfter: notAfter, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, intermediateCA, intermediateKey) + _, untrustedCA, untrustedCAKey := newSelfSignedCertificate(&x509.Certificate{ SerialNumber: big.NewInt(0x1000), Subject: pkix.Name{ @@ -135,6 +155,8 @@ const ( testUntrustedCert = ` + "`\n" + untrustedClientCertPEM + "`" + ` testRevokedCert = ` + "`\n" + revokedClientCertPEM + "`" + ` testCRL = ` + "`\n" + crlPEM + "`" + ` + testIntermediateCA = ` + "`\n" + intermediatePEM + "`" + ` + testValidIntermediateCert = ` + "`\n" + trustedClientCert2PEM + "`" + ` ) `) } diff --git a/authorize/evaluator/headers_evaluator_test.go b/authorize/evaluator/headers_evaluator_test.go index 9a5af5683..86cf2973a 100644 --- a/authorize/evaluator/headers_evaluator_test.go +++ b/authorize/evaluator/headers_evaluator_test.go @@ -204,7 +204,7 @@ func TestHeadersEvaluator(t *testing.T) { assert.Equal(t, "CUSTOM_VALUE", output.Headers.Get("X-Custom-Header")) assert.Equal(t, "ID_TOKEN", output.Headers.Get("X-ID-Token")) assert.Equal(t, "ACCESS_TOKEN", output.Headers.Get("X-Access-Token")) - assert.Equal(t, "17859273e8a980631d367b2d5a6a6635412b0f22835f69e47b3f65624546a704", + assert.Equal(t, "f0c7dc2ca5e4b792935bcdb61a8b8f31b6521c686ffd8a6edb414a1e64ab8eb5", output.Headers.Get("Client-Cert-Fingerprint")) }) From 50e6cf7466dd18a14c07d878b2217add0a39e378 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Thu, 10 Aug 2023 10:05:48 -0700 Subject: [PATCH 08/12] config: add support for max_verify_depth (#4452) Add a new max_verify_depth option to the downstream_mtls settings group, with a default value of 1 (to match the behavior of current Pomerium releases). Populate the corresponding setting within Envoy, and also implement a depth check within isValidClientCertificate() in the authorize service. --- authorize/authorize.go | 5 ++++ authorize/evaluator/config.go | 8 ++++++ authorize/evaluator/evaluator.go | 16 ++++++----- authorize/evaluator/functions.go | 38 ++++++++++++++++++++++----- authorize/evaluator/functions_test.go | 35 +++++++++++++++--------- config/envoyconfig/listeners.go | 4 +++ config/envoyconfig/listeners_test.go | 35 ++++++++++++++++++++++++ config/mtls.go | 13 +++++++++ config/mtls_test.go | 18 +++++++++++++ 9 files changed, 147 insertions(+), 25 deletions(-) diff --git a/authorize/authorize.go b/authorize/authorize.go index 7e932ee9f..c648c121a 100644 --- a/authorize/authorize.go +++ b/authorize/authorize.go @@ -120,11 +120,16 @@ func newPolicyEvaluator(opts *config.Options, store *store.Store) (*evaluator.Ev addDefaultClientCertificateRule := opts.DownstreamMTLS.GetEnforcement() != config.MTLSEnforcementPolicy + clientCertConstraints := evaluator.ClientCertConstraints{ + MaxVerifyDepth: opts.DownstreamMTLS.GetMaxVerifyDepth(), + } + return evaluator.New(ctx, store, evaluator.WithPolicies(opts.GetAllPolicies()), evaluator.WithClientCA(clientCA), evaluator.WithAddDefaultClientCertificateRule(addDefaultClientCertificateRule), evaluator.WithClientCRL(clientCRL), + evaluator.WithClientCertConstraints(clientCertConstraints), evaluator.WithSigningKey(signingKey), evaluator.WithAuthenticateURL(authenticateURL.String()), evaluator.WithGoogleCloudServerlessAuthenticationServiceAccount(opts.GetGoogleCloudServerlessAuthenticationServiceAccount()), diff --git a/authorize/evaluator/config.go b/authorize/evaluator/config.go index 8f3733b3b..7cf080c06 100644 --- a/authorize/evaluator/config.go +++ b/authorize/evaluator/config.go @@ -9,6 +9,7 @@ type evaluatorConfig struct { clientCA []byte clientCRL []byte addDefaultClientCertificateRule bool + clientCertConstraints ClientCertConstraints signingKey []byte authenticateURL string googleCloudServerlessAuthenticationServiceAccount string @@ -55,6 +56,13 @@ func WithAddDefaultClientCertificateRule(addDefaultClientCertificateRule bool) O } } +// WithClientCertConstraints sets addition client certificate constraints. +func WithClientCertConstraints(constraints ClientCertConstraints) Option { + return func(cfg *evaluatorConfig) { + cfg.clientCertConstraints = constraints + } +} + // WithSigningKey sets the signing key and algorithm in the config. func WithSigningKey(signingKey []byte) Option { return func(cfg *evaluatorConfig) { diff --git a/authorize/evaluator/evaluator.go b/authorize/evaluator/evaluator.go index 141561d97..4dc073d6e 100644 --- a/authorize/evaluator/evaluator.go +++ b/authorize/evaluator/evaluator.go @@ -89,11 +89,12 @@ type Result struct { // An Evaluator evaluates policies. type Evaluator struct { - store *store.Store - policyEvaluators map[uint64]*PolicyEvaluator - headersEvaluators *HeadersEvaluator - clientCA []byte - clientCRL []byte + store *store.Store + policyEvaluators map[uint64]*PolicyEvaluator + headersEvaluators *HeadersEvaluator + clientCA []byte + clientCRL []byte + clientCertConstraints ClientCertConstraints } // New creates a new Evaluator. @@ -114,6 +115,7 @@ func New(ctx context.Context, store *store.Store, options ...Option) (*Evaluator e.clientCA = cfg.clientCA e.clientCRL = cfg.clientCRL + e.clientCertConstraints = cfg.clientCertConstraints e.policyEvaluators = make(map[uint64]*PolicyEvaluator) for i := range cfg.policies { @@ -211,8 +213,8 @@ func (e *Evaluator) evaluatePolicy(ctx context.Context, req *Request) (*PolicyRe return nil, err } - isValidClientCertificate, err := - isValidClientCertificate(clientCA, string(e.clientCRL), req.HTTP.ClientCertificate) + isValidClientCertificate, err := isValidClientCertificate( + clientCA, string(e.clientCRL), req.HTTP.ClientCertificate, e.clientCertConstraints) if err != nil { return nil, fmt.Errorf("authorize: error validating client certificate: %w", err) } diff --git a/authorize/evaluator/functions.go b/authorize/evaluator/functions.go index fa0ea75d3..d5858dc31 100644 --- a/authorize/evaluator/functions.go +++ b/authorize/evaluator/functions.go @@ -3,6 +3,7 @@ package evaluator import ( "context" "crypto/x509" + "encoding/json" "encoding/pem" "errors" "fmt" @@ -12,9 +13,19 @@ import ( "github.com/pomerium/pomerium/internal/log" ) -var isValidClientCertificateCache, _ = lru.New2Q[[4]string, bool](100) +// ClientCertConstraints contains additional constraints to validate when +// verifying a client certificate. +type ClientCertConstraints struct { + // MaxVerifyDepth is the maximum allowed certificate chain depth (not + // counting the leaf certificate). A value of 0 indicates no maximum. + MaxVerifyDepth uint32 +} -func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (bool, error) { +var isValidClientCertificateCache, _ = lru.New2Q[[5]string, bool](100) + +func isValidClientCertificate( + ca, crl string, certInfo ClientCertificateInfo, constraints ClientCertConstraints, +) (bool, error) { // when ca is the empty string, client certificates are not required if ca == "" { return true, nil @@ -27,7 +38,12 @@ func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (b return false, nil } - cacheKey := [4]string{ca, crl, cert, intermediates} + constraintsJSON, err := json.Marshal(constraints) + if err != nil { + return false, fmt.Errorf("internal error: failed to serialize constraints: %w", err) + } + + cacheKey := [5]string{ca, crl, cert, intermediates, string(constraintsJSON)} value, ok := isValidClientCertificateCache.Get(cacheKey) if ok { @@ -50,7 +66,7 @@ func isValidClientCertificate(ca, crl string, certInfo ClientCertificateInfo) (b return false, err } - verifyErr := verifyClientCertificate(xcert, roots, intermediatesPool, crls) + verifyErr := verifyClientCertificate(xcert, roots, intermediatesPool, crls, constraints) valid := verifyErr == nil if verifyErr != nil { @@ -67,6 +83,7 @@ func verifyClientCertificate( roots *x509.CertPool, intermediates *x509.CertPool, crls map[string]*x509.RevocationList, + constraints ClientCertConstraints, ) error { chains, err := cert.Verify(x509.VerifyOptions{ Roots: roots, @@ -77,10 +94,11 @@ func verifyClientCertificate( return err } - // At least one of the verified chains must also pass revocation checking. + // At least one of the verified chains must also pass revocation checking + // and satisfy any additional constraints. err = errors.New("internal error: no verified chains") for _, chain := range chains { - err = validateClientCertificateChain(chain, crls) + err = validateClientCertificateChain(chain, crls, constraints) if err == nil { return nil } @@ -94,7 +112,15 @@ func verifyClientCertificate( func validateClientCertificateChain( chain []*x509.Certificate, crls map[string]*x509.RevocationList, + constraints ClientCertConstraints, ) error { + if constraints.MaxVerifyDepth > 0 { + if d := uint32(len(chain) - 1); d > constraints.MaxVerifyDepth { + return fmt.Errorf("chain depth %d exceeds max_verify_depth %d", + d, constraints.MaxVerifyDepth) + } + } + // Consult CRLs for all CAs in the chain (that is, all certificates except // for the first one). To match Envoy's behavior, if a CRL is provided for // any CA in the chain, CRLs must be provided for all CAs in the chain (see diff --git a/authorize/evaluator/functions_test.go b/authorize/evaluator/functions_test.go index ea3b271f6..c43b91157 100644 --- a/authorize/evaluator/functions_test.go +++ b/authorize/evaluator/functions_test.go @@ -97,13 +97,15 @@ BVAnH/e8AiEAjy8cP1msG62BeDaAVU5NcU9RAXDw1Oz4HkpELXQWqK8= ) func Test_isValidClientCertificate(t *testing.T) { + var noConstraints ClientCertConstraints t.Run("no ca", func(t *testing.T) { - valid, err := isValidClientCertificate("", "", ClientCertificateInfo{Leaf: "WHATEVER!"}) + valid, err := isValidClientCertificate( + "", "", ClientCertificateInfo{Leaf: "WHATEVER!"}, noConstraints) assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) t.Run("no cert", func(t *testing.T) { - valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{}) + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{}, noConstraints) assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") }) @@ -111,7 +113,7 @@ func Test_isValidClientCertificate(t *testing.T) { valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ Presented: true, Leaf: testValidCert, - }) + }, noConstraints) assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) @@ -120,7 +122,7 @@ func Test_isValidClientCertificate(t *testing.T) { Presented: true, Leaf: testValidIntermediateCert, Intermediates: testIntermediateCA, - }) + }, noConstraints) assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) @@ -129,7 +131,7 @@ func Test_isValidClientCertificate(t *testing.T) { Presented: true, Leaf: testValidIntermediateCert, Intermediates: "", - }) + }, noConstraints) assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") }) @@ -137,7 +139,7 @@ func Test_isValidClientCertificate(t *testing.T) { valid, err := isValidClientCertificate(testIntermediateCA, "", ClientCertificateInfo{ Presented: true, Leaf: testValidIntermediateCert, - }) + }, noConstraints) assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) @@ -145,7 +147,7 @@ func Test_isValidClientCertificate(t *testing.T) { valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ Presented: true, Leaf: testUntrustedCert, - }) + }, noConstraints) assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") }) @@ -153,7 +155,7 @@ func Test_isValidClientCertificate(t *testing.T) { valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ Presented: true, Leaf: "WHATEVER!", - }) + }, noConstraints) assert.Error(t, err, "should return an error") assert.False(t, valid, "should return false") }) @@ -164,11 +166,11 @@ func Test_isValidClientCertificate(t *testing.T) { } // The "revoked cert" should otherwise be valid (when no CRL is specified). - valid, err := isValidClientCertificate(testCA, "", revokedCertInfo) + valid, err := isValidClientCertificate(testCA, "", revokedCertInfo, noConstraints) assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") - valid, err = isValidClientCertificate(testCA, testCRL, revokedCertInfo) + valid, err = isValidClientCertificate(testCA, testCRL, revokedCertInfo, noConstraints) assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") @@ -176,7 +178,7 @@ func Test_isValidClientCertificate(t *testing.T) { valid, err = isValidClientCertificate(testCA, testCRL, ClientCertificateInfo{ Presented: true, Leaf: testValidCert, - }) + }, noConstraints) assert.NoError(t, err, "should not return an error") assert.True(t, valid, "should return true") }) @@ -186,7 +188,16 @@ func Test_isValidClientCertificate(t *testing.T) { Presented: true, Leaf: testValidIntermediateCert, Intermediates: testIntermediateCA, - }) + }, noConstraints) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) + t.Run("chain too deep", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidIntermediateCert, + Intermediates: testIntermediateCA, + }, ClientCertConstraints{MaxVerifyDepth: 1}) assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") }) diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go index 75c0ca428..e05caebd0 100644 --- a/config/envoyconfig/listeners.go +++ b/config/envoyconfig/listeners.go @@ -553,6 +553,10 @@ func (b *Builder) buildDownstreamValidationContext( TrustedCa: b.filemgr.BytesDataSource("client-ca.pem", clientCA), } + if d := cfg.Options.DownstreamMTLS.GetMaxVerifyDepth(); d > 0 { + vc.MaxVerifyDepth = wrapperspb.UInt32(d) + } + if cfg.Options.DownstreamMTLS.GetEnforcement() == config.MTLSEnforcementRejectConnection { dtc.RequireClientCertificate = wrapperspb.Bool(true) } else { diff --git a/config/envoyconfig/listeners_test.go b/config/envoyconfig/listeners_test.go index 09540dbfa..8703817ba 100644 --- a/config/envoyconfig/listeners_test.go +++ b/config/envoyconfig/listeners_test.go @@ -118,6 +118,7 @@ func Test_buildDownstreamTLSContext(t *testing.T) { }, "alpnProtocols": ["h2", "http/1.1"], "validationContext": { + "maxVerifyDepth": 1, "trustChainVerification": "ACCEPT_UNTRUSTED", "trustedCa": { "filename": "`+clientCAFileName+`" @@ -149,6 +150,7 @@ func Test_buildDownstreamTLSContext(t *testing.T) { }, "alpnProtocols": ["h2", "http/1.1"], "validationContext": { + "maxVerifyDepth": 1, "trustedCa": { "filename": "`+clientCAFileName+`" } @@ -183,6 +185,7 @@ func Test_buildDownstreamTLSContext(t *testing.T) { }, "alpnProtocols": ["h2", "http/1.1"], "validationContext": { + "maxVerifyDepth": 1, "trustChainVerification": "ACCEPT_UNTRUSTED", "trustedCa": { "filename": "`+clientCAFileName+`" @@ -191,6 +194,38 @@ func Test_buildDownstreamTLSContext(t *testing.T) { } }`, downstreamTLSContext) }) + t.Run("client-ca-max-verify-depth", func(t *testing.T) { + var maxVerifyDepth uint32 + config := &config.Config{Options: &config.Options{ + DownstreamMTLS: config.DownstreamMTLSSettings{ + MaxVerifyDepth: &maxVerifyDepth, + CA: "VEVTVAo=", // "TEST\n" + }, + }} + + maxVerifyDepth = 10 + downstreamTLSContext, err := + b.buildDownstreamTLSContextMulti(context.Background(), config, nil) + require.NoError(t, err) + testutil.AssertProtoJSONEqual(t, `{ + "maxVerifyDepth": 10, + "trustChainVerification": "ACCEPT_UNTRUSTED", + "trustedCa": { + "filename": "`+clientCAFileName+`" + } + }`, downstreamTLSContext.GetCommonTlsContext().GetValidationContext()) + + maxVerifyDepth = 0 + downstreamTLSContext, err = + b.buildDownstreamTLSContextMulti(context.Background(), config, nil) + require.NoError(t, err) + testutil.AssertProtoJSONEqual(t, `{ + "trustChainVerification": "ACCEPT_UNTRUSTED", + "trustedCa": { + "filename": "`+clientCAFileName+`" + } + }`, downstreamTLSContext.GetCommonTlsContext().GetValidationContext()) + }) t.Run("http1", func(t *testing.T) { downstreamTLSContext, err := b.buildDownstreamTLSContextMulti(context.Background(), &config.Config{Options: &config.Options{ Cert: aExampleComCert, diff --git a/config/mtls.go b/config/mtls.go index 20619362c..e4886f887 100644 --- a/config/mtls.go +++ b/config/mtls.go @@ -54,6 +54,10 @@ type DownstreamMTLSSettings struct { // Enforcement indicates the behavior applied to requests without a valid // client certificate. Enforcement MTLSEnforcement `mapstructure:"enforcement" yaml:"enforcement,omitempty"` + + // MaxVerifyDepth is the maximum allowed depth of a certificate trust chain + // (not counting the leaf certificate). The value 0 indicates no maximum. + MaxVerifyDepth *uint32 `mapstructure:"max_verify_depth" yaml:"max_verify_depth,omitempty"` } // GetCA returns the certificate authority (or nil if unset). @@ -102,6 +106,15 @@ func (s *DownstreamMTLSSettings) GetEnforcement() MTLSEnforcement { return s.Enforcement } +// GetMaxVerifyDepth returns the maximum certificate chain depth. The value 0 +// indicates no maximum. +func (s *DownstreamMTLSSettings) GetMaxVerifyDepth() uint32 { + if s.MaxVerifyDepth == nil { + return 1 + } + return *s.MaxVerifyDepth +} + func (s *DownstreamMTLSSettings) validate() error { if _, err := s.GetCA(); err != nil { return err diff --git a/config/mtls_test.go b/config/mtls_test.go index da2eb5f5f..1f4cc7f98 100644 --- a/config/mtls_test.go +++ b/config/mtls_test.go @@ -95,6 +95,24 @@ func TestDownstreamMTLSSettingsGetEnforcement(t *testing.T) { } } +func TestDownstreamMTLSSettingsGetMaxVerifyDepth(t *testing.T) { + t.Parallel() + + // MaxVerifyDepth should default to 1 if not set explicitly. + var s DownstreamMTLSSettings + assert.Equal(t, uint32(1), s.GetMaxVerifyDepth()) + + var maxVerifyDepth uint32 + s.MaxVerifyDepth = &maxVerifyDepth + assert.Equal(t, uint32(0), s.GetMaxVerifyDepth()) + + maxVerifyDepth = 1 + assert.Equal(t, uint32(1), s.GetMaxVerifyDepth()) + + maxVerifyDepth = 1000 + assert.Equal(t, uint32(1000), s.GetMaxVerifyDepth()) +} + func TestDownstreamMTLSSettingsValidate(t *testing.T) { t.Parallel() From ed9a93fe5b9b9288e6be99da6d3a0b6a2cde470a Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Thu, 10 Aug 2023 16:15:11 -0700 Subject: [PATCH 09/12] config: extra CA and CRL validation (#4455) Return an error from DownstreamMTLSSettings.validate() if both CA and CAFile are populated, or if both CRL and CRLFile are populated. --- config/mtls.go | 7 ++++++- config/mtls_test.go | 4 ++++ config/options_test.go | 41 ++++++++++++++++++++++++++++++----------- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/config/mtls.go b/config/mtls.go index e4886f887..765af6b81 100644 --- a/config/mtls.go +++ b/config/mtls.go @@ -116,10 +116,15 @@ func (s *DownstreamMTLSSettings) GetMaxVerifyDepth() uint32 { } func (s *DownstreamMTLSSettings) validate() error { - if _, err := s.GetCA(); err != nil { + if s.CA != "" && s.CAFile != "" { + return errors.New("cannot set both ca and ca_file") + } else if _, err := s.GetCA(); err != nil { return err } + if s.CRL != "" && s.CRLFile != "" { + return errors.New("cannot set both crl and crl_file") + } crl, err := s.GetCRL() if err != nil { return err diff --git a/config/mtls_test.go b/config/mtls_test.go index 1f4cc7f98..0be3c47b1 100644 --- a/config/mtls_test.go +++ b/config/mtls_test.go @@ -122,10 +122,14 @@ func TestDownstreamMTLSSettingsValidate(t *testing.T) { errorMsg string }{ {"not set", DownstreamMTLSSettings{}, ""}, + {"both CA and CA file", DownstreamMTLSSettings{CA: "CA", CAFile: "CAFile"}, + "cannot set both ca and ca_file"}, {"bad CA", DownstreamMTLSSettings{CA: "not%valid%base64%data"}, "CA: illegal base64 data at input byte 3"}, {"bad CA file", DownstreamMTLSSettings{CAFile: "-"}, "CA file: open -: no such file or directory"}, + {"both CRL and CRL file", DownstreamMTLSSettings{CRL: "CRL", CRLFile: "CRLFile"}, + "cannot set both crl and crl_file"}, {"bad CRL", DownstreamMTLSSettings{CRL: "dGhpc2lzZmluZQo="}, "CRL: cryptutil: invalid crl, no X509 CRL block found"}, {"bad CRL file", DownstreamMTLSSettings{CRLFile: "-"}, diff --git a/config/options_test.go b/config/options_test.go index 23168c9e0..3e88e3e85 100644 --- a/config/options_test.go +++ b/config/options_test.go @@ -705,18 +705,37 @@ func TestDeprecatedClientCAOptions(t *testing.T) { zl := zerolog.New(&logOutput) testutil.SetLogger(t, &zl) - o := NewDefaultOptions() - o.ClientCA = "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0=" - o.ClientCAFile = caFile - o.AutocertOptions.Enable = true // suppress an unrelated warning + t.Run("CA", func(t *testing.T) { + logOutput.Reset() - err := o.Validate() - require.NoError(t, err) - assert.Equal(t, "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0=", o.DownstreamMTLS.CA) - assert.Equal(t, caFile, o.DownstreamMTLS.CAFile) - assert.Equal(t, `{"level":"warn","message":"config: client_ca is deprecated, set downstream_mtls.ca instead"} -{"level":"warn","message":"config: client_ca_file is deprecated, set downstream_mtls.ca_file instead"} -`, logOutput.String()) + o := NewDefaultOptions() + o.AutocertOptions.Enable = true // suppress an unrelated warning + o.ClientCA = "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0=" + + err := o.Validate() + + require.NoError(t, err) + assert.Equal(t, "LS0tIEZBS0UgQ0EgQ0VSVCAtLS0=", o.DownstreamMTLS.CA) + assert.Equal(t, `{"level":"warn","message":"config: client_ca is deprecated, set downstream_mtls.ca instead"} +`, + logOutput.String()) + }) + + t.Run("CAFile", func(t *testing.T) { + logOutput.Reset() + + o := NewDefaultOptions() + o.AutocertOptions.Enable = true // suppress an unrelated warning + o.ClientCAFile = caFile + + err := o.Validate() + + require.NoError(t, err) + assert.Equal(t, caFile, o.DownstreamMTLS.CAFile) + assert.Equal(t, `{"level":"warn","message":"config: client_ca_file is deprecated, set downstream_mtls.ca_file instead"} +`, + logOutput.String()) + }) } func TestOptions_DefaultURL(t *testing.T) { From cc1ef1ae1816275c6a455c281e367df7b1f6477a Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Fri, 11 Aug 2023 08:33:22 -0700 Subject: [PATCH 10/12] cryptutil: update CRL parsing (#4454) Move the parseCRLs() method from package 'authorize/evaluator' to 'pkg/cryptutil', replacing the existing DecodeCRL() method. This method will parse all CRLs found in the PEM input, rather than just the first. (This removes our usage of the deprecated method x509.ParseDERCRL.) Update this method to return an error if there is non-PEM data found in the input, to satisfy the existing test that raw DER-encoded CRLs are not permitted. Delete the CRLFromBase64() and CRLFromFile() methods, as these are no longer used. --- authorize/evaluator/functions.go | 22 +---------- config/mtls.go | 7 +--- config/mtls_test.go | 2 +- pkg/cryptutil/certificates.go | 51 +++++++++----------------- pkg/cryptutil/certificates_test.go | 25 ++++--------- pkg/cryptutil/testdata/example-crl.pem | 13 ------- 6 files changed, 29 insertions(+), 91 deletions(-) delete mode 100644 pkg/cryptutil/testdata/example-crl.pem diff --git a/authorize/evaluator/functions.go b/authorize/evaluator/functions.go index d5858dc31..4c1f351b1 100644 --- a/authorize/evaluator/functions.go +++ b/authorize/evaluator/functions.go @@ -11,6 +11,7 @@ import ( lru "github.com/hashicorp/golang-lru/v2" "github.com/pomerium/pomerium/internal/log" + "github.com/pomerium/pomerium/pkg/cryptutil" ) // ClientCertConstraints contains additional constraints to validate when @@ -61,7 +62,7 @@ func isValidClientCertificate( return false, err } - crls, err := parseCRLs([]byte(crl)) + crls, err := cryptutil.ParseCRLs([]byte(crl)) if err != nil { return false, err } @@ -168,22 +169,3 @@ func parseCertificate(pemStr string) (*x509.Certificate, error) { } return x509.ParseCertificate(block.Bytes) } - -func parseCRLs(crl []byte) (map[string]*x509.RevocationList, error) { - m := make(map[string]*x509.RevocationList) - for { - var block *pem.Block - block, crl = pem.Decode(crl) - if block == nil { - return m, nil - } - if block.Type != "X509 CRL" { - continue - } - l, err := x509.ParseRevocationList(block.Bytes) - if err != nil { - return nil, err - } - m[string(l.RawIssuer)] = l - } -} diff --git a/config/mtls.go b/config/mtls.go index 765af6b81..18ee84f3f 100644 --- a/config/mtls.go +++ b/config/mtls.go @@ -128,11 +128,8 @@ func (s *DownstreamMTLSSettings) validate() error { crl, err := s.GetCRL() if err != nil { return err - } - if len(crl) > 0 { - if _, err := cryptutil.DecodeCRL(crl); err != nil { - return fmt.Errorf("CRL: %w", err) - } + } else if _, err := cryptutil.ParseCRLs(crl); err != nil { + return fmt.Errorf("CRL: %w", err) } switch s.Enforcement { diff --git a/config/mtls_test.go b/config/mtls_test.go index 0be3c47b1..630a69fd2 100644 --- a/config/mtls_test.go +++ b/config/mtls_test.go @@ -131,7 +131,7 @@ func TestDownstreamMTLSSettingsValidate(t *testing.T) { {"both CRL and CRL file", DownstreamMTLSSettings{CRL: "CRL", CRLFile: "CRLFile"}, "cannot set both crl and crl_file"}, {"bad CRL", DownstreamMTLSSettings{CRL: "dGhpc2lzZmluZQo="}, - "CRL: cryptutil: invalid crl, no X509 CRL block found"}, + "CRL: cryptutil: non-PEM data in CRL bundle"}, {"bad CRL file", DownstreamMTLSSettings{CRLFile: "-"}, "CRL file: open -: no such file or directory"}, {"bad enforcement mode", DownstreamMTLSSettings{Enforcement: "whatever"}, diff --git a/pkg/cryptutil/certificates.go b/pkg/cryptutil/certificates.go index 426267249..94d6c81ad 100644 --- a/pkg/cryptutil/certificates.go +++ b/pkg/cryptutil/certificates.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "crypto/tls" "crypto/x509" - "crypto/x509/pkix" "encoding/base64" "encoding/pem" "errors" @@ -16,7 +15,6 @@ import ( ) const ( - crlPemType = "X509 CRL" maxCertFileSize = 1 << 16 ) @@ -41,43 +39,28 @@ func CertificateFromFile(certFile, keyFile string) (*tls.Certificate, error) { return &cert, err } -// CRLFromBase64 parses a certificate revocation list from a base64 encoded blob. -func CRLFromBase64(rawCRL string) (*pkix.CertificateList, error) { - bs, err := base64.StdEncoding.DecodeString(rawCRL) - if err != nil { - return nil, fmt.Errorf("cryptutil: failed to decode base64 crl: %w", err) - } - return DecodeCRL(bs) -} - -// CRLFromFile parses a certificate revocation list from a file. -func CRLFromFile(fileName string) (*pkix.CertificateList, error) { - bs, err := os.ReadFile(fileName) - if err != nil { - return nil, fmt.Errorf("cryptutil: failed to read crl file (%s): %w", fileName, err) - } - return DecodeCRL(bs) -} - -// DecodeCRL decodes a PEM-encoded certificate revocation list. -func DecodeCRL(encodedCRL []byte) (*pkix.CertificateList, error) { - data := encodedCRL - for len(data) > 0 { +// ParseCRLs parses PEM-encoded certificate revocation lists, returning a map +// of the parsed CRLs keyed by the raw issuer name. +func ParseCRLs(crl []byte) (map[string]*x509.RevocationList, error) { + m := make(map[string]*x509.RevocationList) + for { var block *pem.Block - block, data = pem.Decode(data) + block, crl = pem.Decode(crl) if block == nil { - break - } - - if block.Type == crlPemType { - lst, err := x509.ParseDERCRL(block.Bytes) - if err != nil { - return nil, fmt.Errorf("cryptutil: failed to parse crl: %w", err) + if len(crl) > 0 { + return nil, errors.New("cryptutil: non-PEM data in CRL bundle") } - return lst, nil + return m, nil } + if block.Type != "X509 CRL" { + continue + } + l, err := x509.ParseRevocationList(block.Bytes) + if err != nil { + return nil, fmt.Errorf("cryptutil: failed to parse crl: %w", err) + } + m[string(l.RawIssuer)] = l } - return nil, fmt.Errorf("cryptutil: invalid crl, no %s block found", crlPemType) } // DecodePublicKey decodes a PEM-encoded ECDSA public key. diff --git a/pkg/cryptutil/certificates_test.go b/pkg/cryptutil/certificates_test.go index e62e752d8..abac16a9c 100644 --- a/pkg/cryptutil/certificates_test.go +++ b/pkg/cryptutil/certificates_test.go @@ -106,29 +106,18 @@ func TestCertificateFromFile(t *testing.T) { _ = listener } -func TestCRLFromBase64(t *testing.T) { - bs := base64.StdEncoding.EncodeToString([]byte(pemCRL)) - lst, err := CRLFromBase64(bs) +func TestParseCRLs(t *testing.T) { + crls, err := ParseCRLs([]byte(pemCRL)) assert.NoError(t, err) - assert.NotNil(t, lst) -} - -func TestCRLFromFile(t *testing.T) { - lst, err := CRLFromFile("testdata/example-crl.pem") - assert.NoError(t, err) - assert.NotNil(t, lst) -} - -func TestDecodeCRL(t *testing.T) { - lst, err := DecodeCRL([]byte(pemCRL)) - assert.NoError(t, err) - assert.NotNil(t, lst) + assert.Equal(t, 1, len(crls)) + crl := crls["0l1\x1a0\x18\x06\x03U\x04\n\x13\x11RSA Security Inc.1\x1e0\x1c\x06\x03U\x04\x03\x13\x15RSA Public Root CA v11.0,\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\x1frsakeonrootsign@rsasecurity.com"] + assert.NotNil(t, crl) t.Run("der", func(t *testing.T) { bs, _ := base64.StdEncoding.DecodeString(derCRLBase64) - lst, err := DecodeCRL(bs) + crls, err := ParseCRLs(bs) assert.Error(t, err, "should not allow DER encoded CRL") - assert.Nil(t, lst) + assert.Nil(t, crls) }) } diff --git a/pkg/cryptutil/testdata/example-crl.pem b/pkg/cryptutil/testdata/example-crl.pem deleted file mode 100644 index bf4988988..000000000 --- a/pkg/cryptutil/testdata/example-crl.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN X509 CRL----- -MIIB9jCCAV8CAQEwDQYJKoZIhvcNAQEFBQAwbDEaMBgGA1UEChMRUlNBIFNlY3Vy -aXR5IEluYy4xHjAcBgNVBAMTFVJTQSBQdWJsaWMgUm9vdCBDQSB2MTEuMCwGCSqG -SIb3DQEJARYfcnNha2VvbnJvb3RzaWduQHJzYXNlY3VyaXR5LmNvbRcNMTEwMjIz -MTkyODMwWhcNMTEwODIyMTkyODMwWjCBjDBKAhEArDqoh9FHJHXT7OPguun4+BcN -MDkxMTAyMTQyNzA5WjAmMAoGA1UdFQQDCgEJMBgGA1UdGAQRGA8yMDA5MTEwMjE0 -MjQ1NVowPgIRALGznZ095PB5aAOLPg57fMMXDTAyMTAyMzE0NTAxNFowGjAYBgNV -HRgEERgPMjAwMjEwMjMxNDUwMTRaoDAwLjAfBgNVHSMEGDAWgBT1TDF6UQM/LNeL -l5lvqHGQq3g9mzALBgNVHRQEBAICAIQwDQYJKoZIhvcNAQEFBQADgYEAFU5As6Mz -q5PRsifaobQPGh1aJLyC+Ms5Agc0bWyA3GAdxur5SpPZeRWCBjiP/MEHBWJClBHP -GRcq5yId3EjDkaEyxRa+i67LzvhI6c29Ee6K9pSYwji/7RUhmmnPrXtTxlL0lrLr -mQQJ6xhDRa5G3QA4CmUdsHNvbrzgmCYpvVE= ------END X509 CRL----- From 5568606f03023c1f936ed70b5a41fe3679ba6426 Mon Sep 17 00:00:00 2001 From: Kenneth Jenkins <51246568+kenjenkins@users.noreply.github.com> Date: Fri, 11 Aug 2023 13:27:12 -0700 Subject: [PATCH 11/12] config: support client certificate SAN match (#4453) Add a new match_subject_alt_names option to the downstream_mtls settings group. This setting can be used to further constrain the allowed client certificates by requiring that certificates contain a Subject Alternative Name of a particular type, matching a particular regex. When set, populate the corresponding match_typed_subject_alt_names setting within Envoy, and also implement a corresponding check in the authorize service. --- authorize/authorize.go | 6 +- authorize/evaluator/config.go | 4 +- authorize/evaluator/functions.go | 96 +++++ authorize/evaluator/functions_test.go | 354 +++++++++++++++--- authorize/evaluator/gen-test-certs.go | 46 +++ authorize/evaluator/headers_evaluator_test.go | 2 +- config/envoyconfig/listeners.go | 6 + config/envoyconfig/listeners_test.go | 61 +++ config/mtls.go | 103 +++++ config/mtls_test.go | 12 + 10 files changed, 638 insertions(+), 52 deletions(-) diff --git a/authorize/authorize.go b/authorize/authorize.go index c648c121a..1e028d4c3 100644 --- a/authorize/authorize.go +++ b/authorize/authorize.go @@ -120,8 +120,10 @@ func newPolicyEvaluator(opts *config.Options, store *store.Store) (*evaluator.Ev addDefaultClientCertificateRule := opts.DownstreamMTLS.GetEnforcement() != config.MTLSEnforcementPolicy - clientCertConstraints := evaluator.ClientCertConstraints{ - MaxVerifyDepth: opts.DownstreamMTLS.GetMaxVerifyDepth(), + clientCertConstraints, err := evaluator.ClientCertConstraintsFromConfig(&opts.DownstreamMTLS) + if err != nil { + return nil, fmt.Errorf( + "authorize: internal error: couldn't build client cert constraints: %w", err) } return evaluator.New(ctx, store, diff --git a/authorize/evaluator/config.go b/authorize/evaluator/config.go index 7cf080c06..b7fa2263d 100644 --- a/authorize/evaluator/config.go +++ b/authorize/evaluator/config.go @@ -57,9 +57,9 @@ func WithAddDefaultClientCertificateRule(addDefaultClientCertificateRule bool) O } // WithClientCertConstraints sets addition client certificate constraints. -func WithClientCertConstraints(constraints ClientCertConstraints) Option { +func WithClientCertConstraints(constraints *ClientCertConstraints) Option { return func(cfg *evaluatorConfig) { - cfg.clientCertConstraints = constraints + cfg.clientCertConstraints = *constraints } } diff --git a/authorize/evaluator/functions.go b/authorize/evaluator/functions.go index 4c1f351b1..0a4e37311 100644 --- a/authorize/evaluator/functions.go +++ b/authorize/evaluator/functions.go @@ -7,9 +7,12 @@ import ( "encoding/pem" "errors" "fmt" + "regexp" + "strings" lru "github.com/hashicorp/golang-lru/v2" + "github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/pkg/cryptutil" ) @@ -20,6 +23,52 @@ type ClientCertConstraints struct { // MaxVerifyDepth is the maximum allowed certificate chain depth (not // counting the leaf certificate). A value of 0 indicates no maximum. MaxVerifyDepth uint32 + + // SANMatchers is a map of SAN type to regex match expression. When + // non-empty, a client certificate must contain at least one Subject + // Alternative Name that matches one of the expessions. + SANMatchers SANMatchers +} + +// SANMatchers is a map of SAN type to regex match expression. +type SANMatchers = map[config.SANType]*regexp.Regexp + +// ClientCertConstraintsFromConfig populates a new ClientCertConstraints struct +// based on the provided configuration. +func ClientCertConstraintsFromConfig( + cfg *config.DownstreamMTLSSettings, +) (*ClientCertConstraints, error) { + constraints := &ClientCertConstraints{ + MaxVerifyDepth: cfg.GetMaxVerifyDepth(), + } + + // Combine all SAN match patterns for a given type into one expression. + patternsByType := make(map[config.SANType][]string) + for i := range cfg.MatchSubjectAltNames { + m := &cfg.MatchSubjectAltNames[i] + patternsByType[m.Type] = append(patternsByType[m.Type], m.Pattern) + } + matchers := make(SANMatchers) + for k, v := range patternsByType { + var s strings.Builder + s.WriteString("^(") + s.WriteString(v[0]) + for _, p := range v[1:] { + s.WriteString(")|(") + s.WriteString(p) + } + s.WriteString(")$") + r, err := regexp.Compile(s.String()) + if err != nil { + return nil, err + } + matchers[k] = r + } + if len(matchers) > 0 { + constraints.SANMatchers = matchers + } + + return constraints, nil } var isValidClientCertificateCache, _ = lru.New2Q[[5]string, bool](100) @@ -122,6 +171,10 @@ func validateClientCertificateChain( } } + if err := validateClientCertificateSANs(chain, constraints.SANMatchers); err != nil { + return err + } + // Consult CRLs for all CAs in the chain (that is, all certificates except // for the first one). To match Envoy's behavior, if a CRL is provided for // any CA in the chain, CRLs must be provided for all CAs in the chain (see @@ -159,6 +212,49 @@ func validateClientCertificateChain( return nil } +var errNoSANMatch = errors.New("no matching Subject Alternative Name") + +func validateClientCertificateSANs(chain []*x509.Certificate, matchers SANMatchers) error { + if len(matchers) == 0 { + return nil + } else if len(chain) == 0 { + return errors.New("internal error: no certificates in verified chain") + } + + cert := chain[0] + + if r := matchers[config.SANTypeDNS]; r != nil { + for _, name := range cert.DNSNames { + if r.MatchString(name) { + return nil + } + } + } + if r := matchers[config.SANTypeEmail]; r != nil { + for _, email := range cert.EmailAddresses { + if r.MatchString(email) { + return nil + } + } + } + if r := matchers[config.SANTypeIPAddress]; r != nil { + for _, ip := range cert.IPAddresses { + if r.MatchString(ip.String()) { + return nil + } + } + } + if r := matchers[config.SANTypeURI]; r != nil { + for _, uri := range cert.URIs { + if r.MatchString(uri.String()) { + return nil + } + } + } + + return errNoSANMatch +} + func parseCertificate(pemStr string) (*x509.Certificate, error) { block, _ := pem.Decode([]byte(pemStr)) if block == nil { diff --git a/authorize/evaluator/functions_test.go b/authorize/evaluator/functions_test.go index c43b91157..ffb0cb781 100644 --- a/authorize/evaluator/functions_test.go +++ b/authorize/evaluator/functions_test.go @@ -1,9 +1,13 @@ package evaluator import ( + "regexp" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/config" ) // These certificates can be regenerated by running: @@ -14,89 +18,141 @@ import ( const ( testCA = ` -----BEGIN CERTIFICATE----- -MIIBaTCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAaMRgw +MIIBaDCCAQ6gAwIBAgICEAAwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAaMRgw FgYDVQQDEw9UcnVzdGVkIFJvb3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AATcFCe6i6IqnevuUoR8nTrka8fikGYB3ciKfRyS0NUfm27MGsbuU2ribMYjhuz2 -K4/nU7A2hcu393JNKriXgwoyo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0E -AwIDSQAwRgIhAJIFP4r9Gbo0D2MM/fzPx5wtXsjH1IoQMpn0aw+G1WkmAiEAi56g -gO7l3bJj1YZtBv3tEkZPzaZ+xL3Nllcjv1K12Ac= +AAR2/RkzmSK6paoeTKFx1Bd52ZCg29ulJlMxFdSZT8FlmmaK9mN6KWwO+NHYObiW +y3AQuoSTrZXlrlRW5ANvMI+io0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ +BAUwAwEB/zAdBgNVHQ4EFgQUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0E +AwIDSAAwRQIhAMU5/NjpitOSbUobtjeOriPH8JRo9qy1iFyeVNAcdVvgAiAewq2A +PhgzWTw5F9PJg++9i+xGQTqHs3ZirG27cCjvhQ== -----END CERTIFICATE----- ` testValidCert = ` -----BEGIN CERTIFICATE----- -MIIBYTCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAeMRww +MIIBYjCCAQigAwIBAgICEAEwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAeMRww GgYDVQQDExN0cnVzdGVkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAE3xjPIJPp9v+qu89DNHnqcIfkOSkLb8irRAnFmAYdJJLRqWRNRjmzRHtZ -htT4TWvhEw6VsFbRlsd510+dEASm9aM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw -HwYDVR0jBBgwFoAU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0EAwIDRwAw -RAIgYZaCYXhfBBR8w/AfqUm9MVLYrgkz78mndNFFjz+YvpwCIFsfyIjft/vRcuaU -xlJFtrmFMSt4x1TecZfJsWDA0M55 +AQcDQgAEcWa1Bz6mpsLnM1VD8gtzELjzjEp9Dopp/xWScFO9qtay5SBOeX+Ftr0O +8+/RkoKHzGgZ80gr6xQyUJL3MCwVZKM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0EAwIDSAAw +RQIgXM1ogmy0vcz4lYzji5X3In1n2GLOFNTgucFPkM0GtqgCIQCsXPs/0OjSFyDR +FBqAm1NqDJcxq685fS9t3VfHwapcVA== -----END CERTIFICATE----- ` testUntrustedCert = ` -----BEGIN CERTIFICATE----- -MIIBZzCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz -dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDYyMTEzMjRaMCAx +MIIBZjCCAQygAwIBAgICEAEwCgYIKoZIzj0EAwIwHDEaMBgGA1UEAxMRVW50cnVz +dGVkIFJvb3QgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDcxODAzMjFaMCAx HjAcBgNVBAMTFXVudHJ1c3RlZCBjbGllbnQgY2VydDBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABCLf7g3vlTE+xuIDttn/FYMpAAzlCKqr37rzBnBtuEhCDypW6Y/Y -MbIboFx9o2M9FYLzDJCS7Bj3cp2qd145HdqjODA2MBMGA1UdJQQMMAoGCCsGAQUF -BwMCMB8GA1UdIwQYMBaAFGGTTkLU8r+tXUCs+nMR5Xm3KkgLMAoGCCqGSM49BAMC -A0kAMEYCIQCELFkfQak8X+Yvc7H95j+shSnVgwUuOYT1Lv2NLT1qPAIhAMEvKiwc -ZkWK0F4PJLpSt1wsbnVtfK7kXwHxdp2Z8yy7 +SM49AwEHA0IABJxEIKqLhhMEm5XZXkT+p+hlC2TFyaW0HIZqoE9navJrAcUB8L2M +mVQ+/wLaCznJHLeSLn46uGH5p1hoGFqOrdajODA2MBMGA1UdJQQMMAoGCCsGAQUF +BwMCMB8GA1UdIwQYMBaAFIp2rlIiSnr33ea3cGyLsX4LEYwWMAoGCCqGSM49BAMC +A0gAMEUCIDtJIZJDcqIYaDXhZFs0nd0nHER8IGP9n4BBFMWewAb2AiEAlQyavOxw +iTQQxt0rXB4Ox5zWpU9q68+F9BGBkQKTsBs= -----END CERTIFICATE----- ` testRevokedCert = ` -----BEGIN CERTIFICATE----- -MIIBYTCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAeMRww +MIIBYjCCAQigAwIBAgICEAIwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAeMRww GgYDVQQDExNyZXZva2VkIGNsaWVudCBjZXJ0MFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAENFshpve+if3UmlNyPi5sVz8A03F9AAt6u1LxqiR5cMO6eU+L91Bey/XC -XYrqgpJzbRgTuC4LCFx6cwwl5ff/4KM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw -HwYDVR0jBBgwFoAU0SX0xv9f6gYAcLC926z+Bt9vTAMwCgYIKoZIzj0EAwIDRwAw -RAIgappua0SnpXDzp9nml4iHqKtYAHTn/rg0w405ahdqBQwCIHulKmPGFNLDw1dq -1bZyKsG1t58DfFsO9G27sRssvCgV +AQcDQgAEcnoO4EM72C7xL31RE9e6m9YJYyF6E4JloASECd8mdiXPlMXIjq8MZHB5 +28mFAVQNE7erAtBftID1SbuY4IpXxqM4MDYwEwYDVR0lBAwwCgYIKwYBBQUHAwIw +HwYDVR0jBBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwCgYIKoZIzj0EAwIDSAAw +RQIgUUETSO064YIu+VKnyRb0yBnNTjXLy3TvGuYgZI8VX0YCIQDd0gyNEC5YLvRN +njxfnLoimp+TzTVzvsCokUbNSNRKJA== -----END CERTIFICATE----- ` testCRL = ` -----BEGIN X509 CRL----- MIHeMIGFAgEBMAoGCCqGSM49BAMCMBoxGDAWBgNVBAMTD1RydXN0ZWQgUm9vdCBD -QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgwOTIxMTMyNFqgMDAuMB8G -A1UdIwQYMBaAFNEl9Mb/X+oGAHCwvdus/gbfb0wDMAsGA1UdFAQEAgIgADAKBggq -hkjOPQQDAgNIADBFAiEA1QoleqO9qKhxSxKUc+SOQlFTG9sTbs3ztniUhi0CxhAC -IBElm/lXpVVuWrt0PJhcQHhHqbOxnfkx3HUxVEBWMOzX +QRgPMDAwMTAxMDEwMDAwMDBaMBUwEwICEAIXDTIzMDgxMDE4MDMyMVqgMDAuMB8G +A1UdIwQYMBaAFCRhVVNlDrzlYGmPWHAlIrpxqYgkMAsGA1UdFAQEAgIgADAKBggq +hkjOPQQDAgNIADBFAiEAumtTtjiQt1VsbsEnyr+xbpK0KmzKvkpxIVgE1M9CND0C +IA8zx5clcaGIT5xRnBLZW7RwA37IOmB+7zjAuJQpmKKp -----END X509 CRL----- ` testIntermediateCA = ` -----BEGIN CERTIFICATE----- -MIIBkjCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl -ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA2MjExMzI0WjAiMSAw +MIIBkTCCATegAwIBAgICEAMwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAiMSAw HgYDVQQDExdUcnVzdGVkIEludGVybWVkaWF0ZSBDQTBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABJyxF8EUBMMh/avAul6M8AjoKstuIULPIHOvjYftT/hSqGHNYM6g -0NIBW1g2QX/fnHG9tBy45ReTkVY5HMoO2wujYzBhMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRe0Fh8zjBKzxms/xpbS/vHY5hqujAf -BgNVHSMEGDAWgBTRJfTG/1/qBgBwsL3brP4G329MAzAKBggqhkjOPQQDAgNJADBG -AiEAsnob34JrBGbJjoTZS84mfno2Vb+QPJ1xy3U7AbgyYM4CIQCL3P2A3w1Z87Nr -0A/i8rXw+kiGP1OHbs4k85ZIg6FAtQ== +SM49AwEHA0IABMY+zxL/2dNORuha3uVVOXZYIkTpa9V8N9UVrM15HOHkrdLlz1qk +4wbePkkoGtNRzoayb0iZqeA4YjOxqyPG8emjYzBhMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQpGNmcLLM3vHiOADYGPDQL8AhkyDAf +BgNVHSMEGDAWgBQkYVVTZQ685WBpj1hwJSK6camIJDAKBggqhkjOPQQDAgNIADBF +AiEAnR6xrk7OCk91ymtzU+duZXDqDq35w0oO+MM8nqpac4YCIED+6c9dJKvRCc/C +nP8PMxRaUsbQet1woE7Fckn5tK4N -----END CERTIFICATE----- ` testValidIntermediateCert = ` -----BEGIN CERTIFICATE----- MIIBdTCCARqgAwIBAgICEAAwCgYIKoZIzj0EAwIwIjEgMB4GA1UEAxMXVHJ1c3Rl -ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDYyMTEz -MjRaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw -EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvv9cmqtcuPuSXv6Qup0ycveDtx4PYC4V -UKp5BU1B/1h/IupoIlX165rERkNCxyDXjfw5zkcHXsP1qRbc3LSXT6M4MDYwEwYD -VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUXtBYfM4wSs8ZrP8aW0v7x2OY -arowCgYIKoZIzj0EAwIDSQAwRgIhAJJwdjSiC3avGDc1KZo/AZ1cMPDcFZkI92E6 -BVAnH/e8AiEAjy8cP1msG62BeDaAVU5NcU9RAXDw1Oz4HkpELXQWqK8= +ZCBJbnRlcm1lZGlhdGUgQ0EwIBgPMDAwMTAxMDEwMDAwMDBaFw0zMzA4MDcxODAz +MjFaMCgxJjAkBgNVBAMTHWNsaWVudCBjZXJ0IGZyb20gaW50ZXJtZWRpYXRlMFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5SVyYjNRuuFXGjEmCcuVtMq7e2bmndPK +bRJ7lJ5cc0kZSoNJes5wXOtGRFbx3+admRHq+w1XEBXOe+yRUB8kdKM4MDYwEwYD +VR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0jBBgwFoAUKRjZnCyzN7x4jgA2Bjw0C/AI +ZMgwCgYIKoZIzj0EAwIDSQAwRgIhAMj0O2wDRLoxGIPUDOmUfYxmxglOecQhSkWO +NBtItSxmAiEAy0XCzvpL6XOZU3zxyCjTdJQa2RiC6YnypMaCaETzCaU= +-----END CERTIFICATE----- +` + testValidCertWithDNSSANs = ` +-----BEGIN CERTIFICATE----- +MIIBlTCCATugAwIBAgICEAQwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +FAYDVQQDEw1jbGllbnQgY2VydCAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +LFai39b7TYauNjg4M58f0qY6jTC7xEOhE84wTTcevZvH/t2Y7U0BBNGkvpb14yxh +60vrRKZA9t9G6ZvWKcY/BKNxMG8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwNwYDVR0RBDAwLoIVYS5jbGllbnQz +LmV4YW1wbGUuY29tghViLmNsaWVudDMuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwID +SAAwRQIgBSw8MsKWPPcpGtuVpNJonTEthIOjIGXswxiYG49y2BECIQC5D1DCX/lY +KSwF4aapPx4906VujTL+Ehj8L5ImUYcPbA== +-----END CERTIFICATE----- +` + testValidCertWithEmailSAN = ` +-----BEGIN CERTIFICATE----- +MIIBezCCASKgAwIBAgICEAUwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +FAYDVQQDEw1jbGllbnQgY2VydCA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +WvX8BnCrzUSpLrYka8ed+bz6/HoXUvq5nRqysKe0nGYSsXKRjxLdCG8AKsoGIQIv +KOQScf/4TJUNIUY4XOsFI6NYMFYwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwHgYDVR0RBBcwFYETY2xpZW50NEBl +eGFtcGxlLmNvbTAKBggqhkjOPQQDAgNHADBEAiAMYGTjUBqgnai8UL3B/iQkCkMb +xgCC1ZYdZaJ1RBwFfgIgIhjQZ2s6dTaah/LzYJ9ZwMvSA86XQvzTVSuT6s+RJw0= +-----END CERTIFICATE----- +` + testValidCertWithIPSAN = ` +-----BEGIN CERTIFICATE----- +MIIBbTCCAROgAwIBAgICEAYwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +FAYDVQQDEw1jbGllbnQgY2VydCA1MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +6+9BE+aomrR2Mdnx4iFY63t0hsVD6rYaHBW1b9roFQX6Cor4YeUfkEEF4LrGeAyb +wcqb6G1ExgNyjEh10Ai1M6NJMEcwEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwDwYDVR0RBAgwBocEwKgKCjAKBggq +hkjOPQQDAgNIADBFAiEA/mq32YZZAacOH/P/wjvfD1n74DD/GkhW4kfS72Z0oGQC +IAQ+L8E78JOLaPWXiL7WFpVrb0hOHkV2m9Qw4GB41mUN +-----END CERTIFICATE----- +` + testValidCertWithURISAN = ` +-----BEGIN CERTIFICATE----- +MIIBhTCCASugAwIBAgICEAcwCgYIKoZIzj0EAwIwGjEYMBYGA1UEAxMPVHJ1c3Rl +ZCBSb290IENBMCAYDzAwMDEwMTAxMDAwMDAwWhcNMzMwODA3MTgwMzIxWjAYMRYw +FAYDVQQDEw1jbGllbnQgY2VydCA2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +7mHladapSY5PlVYToL1dHr0tJPGe5XVP8DSZJz+WWyS9tWsuEsK6P5yeZrbWASOX +foH7iVIdx3DMyukGsvMX+KNhMF8wEwYDVR0lBAwwCgYIKwYBBQUHAwIwHwYDVR0j +BBgwFoAUJGFVU2UOvOVgaY9YcCUiunGpiCQwJwYDVR0RBCAwHoYcc3BpZmZlOi8v +ZXhhbXBsZS5jb20vZm9vL2JhcjAKBggqhkjOPQQDAgNIADBFAiAhzElKeGJzp2zP +GOTUEy0f6b2tvMYGDLQxCcp4bc4QuQIhAPwX4Y3Cr7uazQlbwL6D51y9NCcDyj3D +Z18vZNxm9ZR1 -----END CERTIFICATE----- ` ) func Test_isValidClientCertificate(t *testing.T) { + t.Parallel() + var noConstraints ClientCertConstraints t.Run("no ca", func(t *testing.T) { valid, err := isValidClientCertificate( @@ -201,4 +257,208 @@ func Test_isValidClientCertificate(t *testing.T) { assert.NoError(t, err, "should not return an error") assert.False(t, valid, "should return false") }) + t.Run("any SAN", func(t *testing.T) { + matchAnySAN := ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile("^.*$"), + config.SANTypeEmail: regexp.MustCompile("^.*$"), + config.SANTypeIPAddress: regexp.MustCompile("^.*$"), + config.SANTypeURI: regexp.MustCompile("^.*$"), + }} + + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCert, // no SANs + }, matchAnySAN) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithDNSSANs, + }, matchAnySAN) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithEmailSAN, + }, matchAnySAN) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithIPSAN, + }, matchAnySAN) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithURISAN, + }, matchAnySAN) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + }) + t.Run("DNS SAN", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithDNSSANs, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile(`^a\..*\.example\.com$`), + }}) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithDNSSANs, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeEmail: regexp.MustCompile(`^a\..*\.example\.com$`), // mismatched type + }}) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) + t.Run("email SAN", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithEmailSAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeEmail: regexp.MustCompile(`^.*@example\.com$`), + }}) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithEmailSAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeIPAddress: regexp.MustCompile(`^.*@example\.com$`), // mismatched type + }}) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) + t.Run("IP address SAN", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithIPSAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeIPAddress: regexp.MustCompile(`^192\.168\.10\..*$`), + }}) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithIPSAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeURI: regexp.MustCompile(`^192\.168\.10\..*$`), // mismatched type + }}) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) + t.Run("URI SAN", func(t *testing.T) { + valid, err := isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithURISAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeURI: regexp.MustCompile(`^spiffe://example\.com/.*$`), + }}) + assert.NoError(t, err, "should not return an error") + assert.True(t, valid, "should return true") + + valid, err = isValidClientCertificate(testCA, "", ClientCertificateInfo{ + Presented: true, + Leaf: testValidCertWithURISAN, + }, ClientCertConstraints{SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile(`^spiffe://example\.com/.*$`), // mismatched type + }}) + assert.NoError(t, err, "should not return an error") + assert.False(t, valid, "should return false") + }) +} + +func TestClientCertConstraintsFromConfig(t *testing.T) { + t.Parallel() + + t.Run("default constraints", func(t *testing.T) { + var s config.DownstreamMTLSSettings + c, err := ClientCertConstraintsFromConfig(&s) + assert.NoError(t, err) + assert.Equal(t, &ClientCertConstraints{MaxVerifyDepth: 1}, c) + }) + t.Run("no constraints", func(t *testing.T) { + s := config.DownstreamMTLSSettings{ + MaxVerifyDepth: new(uint32), + } + c, err := ClientCertConstraintsFromConfig(&s) + assert.NoError(t, err) + assert.Equal(t, &ClientCertConstraints{}, c) + }) + t.Run("larger max depth", func(t *testing.T) { + depth := uint32(5) + s := config.DownstreamMTLSSettings{ + MaxVerifyDepth: &depth, + } + c, err := ClientCertConstraintsFromConfig(&s) + assert.NoError(t, err) + assert.Equal(t, &ClientCertConstraints{MaxVerifyDepth: 5}, c) + }) + t.Run("one SAN match", func(t *testing.T) { + s := config.DownstreamMTLSSettings{ + MatchSubjectAltNames: []config.SANMatcher{ + {Type: config.SANTypeDNS, Pattern: `.*\.corp\.example\.com`}, + }, + } + c, err := ClientCertConstraintsFromConfig(&s) + assert.NoError(t, err) + assert.Equal(t, &ClientCertConstraints{ + MaxVerifyDepth: 1, + SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile(`^(.*\.corp\.example\.com)$`), + }, + }, c) + }) + t.Run("multiple SAN matches", func(t *testing.T) { + s := config.DownstreamMTLSSettings{ + MatchSubjectAltNames: []config.SANMatcher{ + {Type: config.SANTypeDNS, Pattern: `.*\.foo\.example\.com`}, + {Type: config.SANTypeDNS, Pattern: `.*\.bar\.example\.com`}, + }, + } + c, err := ClientCertConstraintsFromConfig(&s) + assert.NoError(t, err) + assert.Equal(t, &ClientCertConstraints{ + MaxVerifyDepth: 1, + SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile(`^(.*\.foo\.example\.com)|(.*\.bar\.example\.com)$`), + }, + }, c) + }) + t.Run("multiple SAN matches", func(t *testing.T) { + s := config.DownstreamMTLSSettings{ + MatchSubjectAltNames: []config.SANMatcher{ + {Type: config.SANTypeDNS, Pattern: `.*\.foo\.example\.com`}, + {Type: config.SANTypeEmail, Pattern: `.*@example\.com`}, + }, + } + c, err := ClientCertConstraintsFromConfig(&s) + assert.NoError(t, err) + assert.Equal(t, &ClientCertConstraints{ + MaxVerifyDepth: 1, + SANMatchers: SANMatchers{ + config.SANTypeDNS: regexp.MustCompile(`^(.*\.foo\.example\.com)$`), + config.SANTypeEmail: regexp.MustCompile(`^(.*@example\.com)$`), + }, + }, c) + }) + t.Run("bad SAN match expression", func(t *testing.T) { + s := config.DownstreamMTLSSettings{ + MatchSubjectAltNames: []config.SANMatcher{ + {Type: config.SANTypeDNS, Pattern: "["}, + }, + } + _, err := ClientCertConstraintsFromConfig(&s) + require.Error(t, err) + }) } diff --git a/authorize/evaluator/gen-test-certs.go b/authorize/evaluator/gen-test-certs.go index edbe4e17c..0d4f3a7cf 100644 --- a/authorize/evaluator/gen-test-certs.go +++ b/authorize/evaluator/gen-test-certs.go @@ -12,6 +12,8 @@ import ( "fmt" "log" "math/big" + "net" + "net/url" "time" ) @@ -148,6 +150,46 @@ func main() { }, }, rootCA, rootKey) + trustedClientCert3PEM, _, _ := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1004), + Subject: pkix.Name{ + CommonName: "client cert 3", + }, + DNSNames: []string{"a.client3.example.com", "b.client3.example.com"}, + NotAfter: notAfter, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, rootCA, rootKey) + + trustedClientCert4PEM, _, _ := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1005), + Subject: pkix.Name{ + CommonName: "client cert 4", + }, + EmailAddresses: []string{"client4@example.com"}, + NotAfter: notAfter, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, rootCA, rootKey) + + trustedClientCert5PEM, _, _ := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1006), + Subject: pkix.Name{ + CommonName: "client cert 5", + }, + IPAddresses: []net.IP{net.ParseIP("192.168.10.10")}, + NotAfter: notAfter, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, rootCA, rootKey) + + trustedClientCert6PEM, _, _ := newCertificate(&x509.Certificate{ + SerialNumber: big.NewInt(0x1007), + Subject: pkix.Name{ + CommonName: "client cert 6", + }, + URIs: []*url.URL{{Scheme: "spiffe", Host: "example.com", Path: "/foo/bar"}}, + NotAfter: notAfter, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, rootCA, rootKey) + fmt.Println(` const ( testCA = ` + "`\n" + rootPEM + "`" + ` @@ -157,6 +199,10 @@ const ( testCRL = ` + "`\n" + crlPEM + "`" + ` testIntermediateCA = ` + "`\n" + intermediatePEM + "`" + ` testValidIntermediateCert = ` + "`\n" + trustedClientCert2PEM + "`" + ` + testValidCertWithDNSSANs = ` + "`\n" + trustedClientCert3PEM + "`" + ` + testValidCertWithEmailSAN = ` + "`\n" + trustedClientCert4PEM + "`" + ` + testValidCertWithIPSAN = ` + "`\n" + trustedClientCert5PEM + "`" + ` + testValidCertWithURISAN = ` + "`\n" + trustedClientCert6PEM + "`" + ` ) `) } diff --git a/authorize/evaluator/headers_evaluator_test.go b/authorize/evaluator/headers_evaluator_test.go index 86cf2973a..77cfd5522 100644 --- a/authorize/evaluator/headers_evaluator_test.go +++ b/authorize/evaluator/headers_evaluator_test.go @@ -204,7 +204,7 @@ func TestHeadersEvaluator(t *testing.T) { assert.Equal(t, "CUSTOM_VALUE", output.Headers.Get("X-Custom-Header")) assert.Equal(t, "ID_TOKEN", output.Headers.Get("X-ID-Token")) assert.Equal(t, "ACCESS_TOKEN", output.Headers.Get("X-Access-Token")) - assert.Equal(t, "f0c7dc2ca5e4b792935bcdb61a8b8f31b6521c686ffd8a6edb414a1e64ab8eb5", + assert.Equal(t, "ebf421e323e31c3900a7985a16e72c59f45f5a2c15283297567e226b3b17d1a1", output.Headers.Get("Client-Cert-Fingerprint")) }) diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go index e05caebd0..0e8f5e9f6 100644 --- a/config/envoyconfig/listeners.go +++ b/config/envoyconfig/listeners.go @@ -551,6 +551,12 @@ func (b *Builder) buildDownstreamValidationContext( vc := &envoy_extensions_transport_sockets_tls_v3.CertificateValidationContext{ TrustedCa: b.filemgr.BytesDataSource("client-ca.pem", clientCA), + MatchTypedSubjectAltNames: make([]*envoy_extensions_transport_sockets_tls_v3.SubjectAltNameMatcher, + 0, len(cfg.Options.DownstreamMTLS.MatchSubjectAltNames)), + } + for i := range cfg.Options.DownstreamMTLS.MatchSubjectAltNames { + vc.MatchTypedSubjectAltNames = append(vc.MatchTypedSubjectAltNames, + cfg.Options.DownstreamMTLS.MatchSubjectAltNames[i].ToEnvoyProto()) } if d := cfg.Options.DownstreamMTLS.GetMaxVerifyDepth(); d > 0 { diff --git a/config/envoyconfig/listeners_test.go b/config/envoyconfig/listeners_test.go index 8703817ba..17de2815d 100644 --- a/config/envoyconfig/listeners_test.go +++ b/config/envoyconfig/listeners_test.go @@ -226,6 +226,67 @@ func Test_buildDownstreamTLSContext(t *testing.T) { } }`, downstreamTLSContext.GetCommonTlsContext().GetValidationContext()) }) + t.Run("client-ca-san-matchers", func(t *testing.T) { + config := &config.Config{Options: &config.Options{ + DownstreamMTLS: config.DownstreamMTLSSettings{ + CA: "VEVTVAo=", // "TEST\n" + MatchSubjectAltNames: []config.SANMatcher{ + {Type: config.SANTypeDNS, Pattern: `.*\.corp\.example\.com`}, + {Type: config.SANTypeEmail, Pattern: `.*@example\.com`}, + {Type: config.SANTypeIPAddress, Pattern: `10\.10\.42\..*`}, + {Type: config.SANTypeURI, Pattern: `spiffe://example\.com/.*`}, + }, + }, + }} + downstreamTLSContext, err := + b.buildDownstreamTLSContextMulti(context.Background(), config, nil) + require.NoError(t, err) + testutil.AssertProtoJSONEqual(t, `{ + "maxVerifyDepth": 1, + "matchTypedSubjectAltNames": [ + { + "matcher": { + "safeRegex": { + "googleRe2": {}, + "regex": ".*\\.corp\\.example\\.com" + } + }, + "sanType": "DNS" + }, + { + "matcher": { + "safeRegex": { + "googleRe2": {}, + "regex": ".*@example\\.com" + } + }, + "sanType": "EMAIL" + }, + { + "matcher": { + "safeRegex": { + "googleRe2": {}, + "regex": "10\\.10\\.42\\..*" + } + }, + "sanType": "IP_ADDRESS" + }, + { + "matcher": { + "safeRegex": { + "googleRe2": {}, + "regex": "spiffe://example\\.com/.*" + } + }, + "sanType": "URI" + } + ], + "trustChainVerification": "ACCEPT_UNTRUSTED", + "trustedCa": { + "filename": "`+clientCAFileName+`" + } + }`, downstreamTLSContext.GetCommonTlsContext().GetValidationContext()) + }) t.Run("http1", func(t *testing.T) { downstreamTLSContext, err := b.buildDownstreamTLSContextMulti(context.Background(), &config.Config{Options: &config.Options{ Cert: aExampleComCert, diff --git a/config/mtls.go b/config/mtls.go index 18ee84f3f..bfb0d9cb5 100644 --- a/config/mtls.go +++ b/config/mtls.go @@ -3,9 +3,14 @@ package config import ( "context" "encoding/base64" + "encoding/json" "errors" "fmt" "os" + "regexp" + + envoy_tls "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" + envoy_matcher "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/pkg/cryptutil" @@ -31,6 +36,23 @@ const ( MTLSEnforcementRejectConnection MTLSEnforcement = "reject_connection" ) +// SANType represents a certificate Subject Alternative Name type. +type SANType string + +const ( + // SANTypeDNS represents a DNS name. + SANTypeDNS SANType = "dns" + + // SANTypeEmail represents an email address. + SANTypeEmail SANType = "email" + + // SANTypeIPAddress represents an IP address. + SANTypeIPAddress SANType = "ip_address" + + // SANTypeURI represents a URI. + SANTypeURI SANType = "uri" +) + // DownstreamMTLSSettings specify the downstream client certificate requirements. type DownstreamMTLSSettings struct { // CA is the base64-encoded certificate authority (or bundle of certificate @@ -55,6 +77,11 @@ type DownstreamMTLSSettings struct { // client certificate. Enforcement MTLSEnforcement `mapstructure:"enforcement" yaml:"enforcement,omitempty"` + // MatchSubjectAltNames is a list of SAN match expressions. When non-empty, + // a client certificate must contain at least one Subject Alternative Name + // that matches at least one of the expessions. + MatchSubjectAltNames []SANMatcher `mapstructure:"match_subject_alt_names" yaml:"match_subject_alt_names,omitempty"` + // MaxVerifyDepth is the maximum allowed depth of a certificate trust chain // (not counting the leaf certificate). The value 0 indicates no maximum. MaxVerifyDepth *uint32 `mapstructure:"max_verify_depth" yaml:"max_verify_depth,omitempty"` @@ -141,6 +168,12 @@ func (s *DownstreamMTLSSettings) validate() error { return errors.New("unknown enforcement option") } + for i := range s.MatchSubjectAltNames { + if err := s.MatchSubjectAltNames[i].validate(); err != nil { + return err + } + } + return nil } @@ -173,3 +206,73 @@ func mtlsEnforcementFromProtoEnum( return "" } } + +// SANMatcher represents a Subject Alternative Name string matcher condition. A +// certificate satisfies this condition if it contains at least one SAN of the +// given type that matches the regular expression as a full string match. +type SANMatcher struct { + Type SANType + Pattern string +} + +func (s *SANMatcher) validate() error { + if s.envoyType() == envoy_tls.SubjectAltNameMatcher_SAN_TYPE_UNSPECIFIED { + return fmt.Errorf("unknown SAN type %q", s.Type) + } + if _, err := regexp.Compile(s.Pattern); err != nil { + return fmt.Errorf("couldn't parse pattern %q: %w", s.Pattern, err) + } + return nil +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (s *SANMatcher) UnmarshalJSON(b []byte) error { + var m map[string]string + if err := json.Unmarshal(b, &m); err != nil { + return err + } else if len(m) != 1 { + return errors.New("unsupported SAN matcher format: expected {type: pattern}") + } + + for k, v := range m { + s.Type = SANType(k) + s.Pattern = v + } + return nil +} + +// MarshalJSON implements the json.Marshaler interface. +func (s *SANMatcher) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]string{string(s.Type): s.Pattern}) +} + +// ToEnvoyProto rerturns a representation of this matcher as an Envoy +// SubjectAltNameMatcher proto. +func (s *SANMatcher) ToEnvoyProto() *envoy_tls.SubjectAltNameMatcher { + return &envoy_tls.SubjectAltNameMatcher{ + SanType: s.envoyType(), + Matcher: &envoy_matcher.StringMatcher{ + MatchPattern: &envoy_matcher.StringMatcher_SafeRegex{ + SafeRegex: &envoy_matcher.RegexMatcher{ + EngineType: &envoy_matcher.RegexMatcher_GoogleRe2{}, + Regex: s.Pattern, + }, + }, + }, + } +} + +func (s *SANMatcher) envoyType() envoy_tls.SubjectAltNameMatcher_SanType { + switch s.Type { + case SANTypeDNS: + return envoy_tls.SubjectAltNameMatcher_DNS + case SANTypeEmail: + return envoy_tls.SubjectAltNameMatcher_EMAIL + case SANTypeIPAddress: + return envoy_tls.SubjectAltNameMatcher_IP_ADDRESS + case SANTypeURI: + return envoy_tls.SubjectAltNameMatcher_URI + default: + return envoy_tls.SubjectAltNameMatcher_SAN_TYPE_UNSPECIFIED + } +} diff --git a/config/mtls_test.go b/config/mtls_test.go index 630a69fd2..0fdb5a29b 100644 --- a/config/mtls_test.go +++ b/config/mtls_test.go @@ -136,10 +136,22 @@ func TestDownstreamMTLSSettingsValidate(t *testing.T) { "CRL file: open -: no such file or directory"}, {"bad enforcement mode", DownstreamMTLSSettings{Enforcement: "whatever"}, "unknown enforcement option"}, + {"bad SAN type", DownstreamMTLSSettings{MatchSubjectAltNames: []SANMatcher{ + {Type: "whatever"}, + }}, `unknown SAN type "whatever"`}, + {"bad SAN match expression", DownstreamMTLSSettings{MatchSubjectAltNames: []SANMatcher{ + {Type: "dns", Pattern: `[`}, + }}, "couldn't parse pattern \"[\": error parsing regexp: missing closing ]: `[`"}, {"OK", DownstreamMTLSSettings{ CA: "dGhpc2lzZmluZQo=", CRL: "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tCk1JSUNOVENCbmdJQkFUQU5CZ2txaGtpRzl3MEJBUXNGQURBNk1SNHdIQVlEVlFRS0V4VnRhMk5sY25RZ1pHVjIKWld4dmNHMWxiblFnUTBFeEdEQVdCZ05WQkFNVEQyUnZkMjV6ZEhKbFlXMGdRMEVnTWhjTk1qTXdOekU1TWpFMQpNREUxV2hjTk16TXdOekUyTWpFMU1ERTFXcUF3TUM0d0h3WURWUjBqQkJnd0ZvQVVDeFEyY0JhNVl6cVZ6YW1wCmlOQ3g4S3dGRnlRd0N3WURWUjBVQkFRQ0FoQUFNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJnUUNZYW14OHBNK1IKQ2x5c2tjdTdvdWh1L1IxSnkxbldHeVd0S3BoWXEwWEZiT0xsbmsyWjdlRGZBWDhFZWoyRmF2cXh6YXBSMngyTwo0aUpORENtaXdZWVlVUzJYMkxKM3JSUkpYeVh2V2h0ZkhyeFVSZDZCaXRDMklYcHlrQnRWbGYzekFuWjhHWkZRClMxamRmeUxNdUVBaUR3SWFpM1l0OEhzRHAvcUcwODlvWGNvU3R5UWcvdVJwbVd5MDVBOXVDVk9mTkhTTFNadTgKbHI0cWF0bGV1MHdXYlYxYW1MOHRPOXg0Q1JrTzBvMVlhUXE0RG9PcnVQciszTmtUbVB2R2lkaDNGNzFWNklFQQpoK0t6ZGJSWHhGbUNDV0xXbXBKRGNyZ1I3S1VxWk9oVVV0K0RVcWFxaFY0NHFJMG5ycFIrUVpMb2hvRG9yOUx3CksrdWZqM24yOWVTUlgrM1B4K29WV1BUOFlaUDJ1S1BkaXppOTZtZTJqV1RyNTF4OUFqRW9KRHNUbllSbDkrdVkKU2hpVXhXblRkUXNvb2tuSWZjUy8wemZnWjg3R3ZVVnppbkNRekpwd1Z4ZDRBbHQ4QWxSK2ZYQXFOSW9PZ3V5dgpwL0N0UlZualZFN2w3SFcvaFFScTFKMGlqQ0NLd215Zi9LVGQ2RUs0VGRydmJYL1U5bXNWTThZPQotLS0tLUVORCBYNTA5IENSTC0tLS0tCg==", Enforcement: "reject_connection", + MatchSubjectAltNames: []SANMatcher{ + {Type: "dns", Pattern: `.*\.corp\.example\.com`}, + {Type: "email", Pattern: `.*@\.example\.com`}, + {Type: "ip_address", Pattern: `192\.168\.0\..*`}, + {Type: "uri", Pattern: `spiffe://example.com/department/.*`}, + }, }, ""}, } From 2c7955b27d0d8a9917205726c83a4e6fee22eb3c Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Sat, 12 Aug 2023 22:42:30 -0400 Subject: [PATCH 12/12] add retry package --- internal/retry/config.go | 76 +++++++++++++++++++ internal/retry/error.go | 48 ++++++++++++ internal/retry/error_test.go | 29 ++++++++ internal/retry/retry.go | 139 +++++++++++++++++++++++++++++++++++ internal/retry/retry_test.go | 124 +++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 internal/retry/config.go create mode 100644 internal/retry/error.go create mode 100644 internal/retry/error_test.go create mode 100644 internal/retry/retry.go create mode 100644 internal/retry/retry_test.go diff --git a/internal/retry/config.go b/internal/retry/config.go new file mode 100644 index 000000000..a5929c77f --- /dev/null +++ b/internal/retry/config.go @@ -0,0 +1,76 @@ +package retry + +import ( + "context" + "reflect" + "time" + + "github.com/cenkalti/backoff/v4" +) + +type config struct { + maxInterval time.Duration + watches []watch + + backoff.BackOff +} + +// watch is a helper struct to watch multiple channels +type watch struct { + name string + ch reflect.Value + fn func(context.Context) error + this bool +} + +// Option configures the retry handler +type Option func(*config) + +// WithWatch adds a watch to the retry handler +// that will be triggered when a value is received on the channel +// and the function will be called, also within a retry handler +func WithWatch[T any](name string, ch <-chan T, fn func(context.Context) error) Option { + return func(cfg *config) { + cfg.watches = append(cfg.watches, watch{name: name, ch: reflect.ValueOf(ch), fn: fn, this: false}) + } +} + +// WithMaxInterval sets the upper bound for the retry handler +func WithMaxInterval(d time.Duration) Option { + return func(cfg *config) { + cfg.maxInterval = d + } +} + +func newConfig(opts ...Option) ([]watch, backoff.BackOff) { + cfg := new(config) + for _, opt := range []Option{ + WithMaxInterval(time.Minute * 5), + } { + opt(cfg) + } + + for _, opt := range opts { + opt(cfg) + } + + for i, w := range cfg.watches { + cfg.watches[i].fn = withRetry(cfg, w) + } + + bo := backoff.NewExponentialBackOff() + bo.MaxInterval = cfg.maxInterval + bo.MaxElapsedTime = 0 + + return cfg.watches, bo +} + +func withRetry(cfg *config, w watch) func(context.Context) error { + if w.fn == nil { + return func(_ context.Context) error { return nil } + } + + return func(ctx context.Context) error { + return Retry(ctx, w.name, w.fn, WithMaxInterval(cfg.maxInterval)) + } +} diff --git a/internal/retry/error.go b/internal/retry/error.go new file mode 100644 index 000000000..bc7d25a31 --- /dev/null +++ b/internal/retry/error.go @@ -0,0 +1,48 @@ +package retry + +import ( + "errors" + "fmt" +) + +// TerminalError is an error that should not be retried +type TerminalError interface { + error + IsTerminal() +} + +// terminalError is an error that should not be retried +type terminalError struct { + Err error +} + +// Error implements error for terminalError +func (e *terminalError) Error() string { + return fmt.Sprintf("terminal error: %v", e.Err) +} + +// Unwrap implements errors.Unwrap for terminalError +func (e *terminalError) Unwrap() error { + return e.Err +} + +// Is implements errors.Is for terminalError +func (e *terminalError) Is(err error) bool { + //nolint:errorlint + _, ok := err.(*terminalError) + return ok +} + +// IsTerminal implements TerminalError for terminalError +func (e *terminalError) IsTerminal() {} + +// NewTerminalError creates a new terminal error that cannot be retried +func NewTerminalError(err error) error { + return &terminalError{Err: err} +} + +// IsTerminalError returns true if the error is a terminal error +func IsTerminalError(err error) bool { + var te TerminalError + return errors.As(err, &te) +} diff --git a/internal/retry/error_test.go b/internal/retry/error_test.go new file mode 100644 index 000000000..b4f2ddb00 --- /dev/null +++ b/internal/retry/error_test.go @@ -0,0 +1,29 @@ +package retry_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/retry" +) + +type testError string + +func (e testError) Error() string { + return string(e) +} + +func (e testError) IsTerminal() {} + +func TestError(t *testing.T) { + t.Run("local terminal error", func(t *testing.T) { + err := fmt.Errorf("wrap: %w", retry.NewTerminalError(fmt.Errorf("inner"))) + require.True(t, retry.IsTerminalError(err)) + }) + t.Run("external terminal error", func(t *testing.T) { + err := fmt.Errorf("wrap: %w", testError("inner")) + require.True(t, retry.IsTerminalError(err)) + }) +} diff --git a/internal/retry/retry.go b/internal/retry/retry.go new file mode 100644 index 000000000..02042e349 --- /dev/null +++ b/internal/retry/retry.go @@ -0,0 +1,139 @@ +// Package retry provides a retry loop with exponential back-off +// while watching arbitrary signal channels for side effects. +package retry + +import ( + "context" + "fmt" + "reflect" + "time" +) + +// Retry retries a function (with exponential back-off) until it succeeds. +// It additionally watches arbitrary channels and calls the handler function when a value is received. +// Handler functions are also retried with exponential back-off. +// If a terminal error is returned from the handler function, the retry loop is aborted. +// If the context is canceled, the retry loop is aborted. +func Retry( + ctx context.Context, + name string, + fn func(context.Context) error, + opts ...Option, +) error { + watches, backoff := newConfig(opts...) + ticker := time.NewTicker(backoff.NextBackOff()) + defer ticker.Stop() + + s := makeSelect(ctx, watches, name, ticker.C, fn) + +restart: + for { + err := fn(ctx) + if err == nil { + return nil + } + if IsTerminalError(err) { + return err + } + + backoff.Reset() + backoff: + for { + ticker.Reset(backoff.NextBackOff()) + + next, err := s.Exec(ctx) + switch next { + case nextRestart: + continue restart + case nextBackoff: + continue backoff + case nextExit: + return err + default: + panic("unreachable") + } + } + } +} + +type selectCase struct { + watches []watch + cases []reflect.SelectCase +} + +func makeSelect( + ctx context.Context, + watches []watch, + name string, + ch <-chan time.Time, + fn func(context.Context) error, +) *selectCase { + watches = append(watches, + watch{ + name: "context", + fn: func(ctx context.Context) error { + // unreachable, the context handler will never be called + // as its channel can only be closed + return ctx.Err() + }, + ch: reflect.ValueOf(ctx.Done()), + }, + watch{ + name: name, + fn: fn, + ch: reflect.ValueOf(ch), + this: true, + }, + ) + cases := make([]reflect.SelectCase, 0, len(watches)) + for _, w := range watches { + cases = append(cases, reflect.SelectCase{ + Dir: reflect.SelectRecv, + Chan: w.ch, + }) + } + return &selectCase{ + watches: watches, + cases: cases, + } +} + +type next int + +const ( + nextRestart next = iota // try again from the beginning + nextBackoff // backoff and try again + nextExit // exit +) + +func (s *selectCase) Exec(ctx context.Context) (next, error) { + chosen, _, ok := reflect.Select(s.cases) + if !ok { + return nextExit, fmt.Errorf("watch %s closed", s.watches[chosen].name) + } + + w := s.watches[chosen] + + err := w.fn(ctx) + if err != nil { + return onError(w, err) + } + + if !w.this { + return nextRestart, nil + } + + return nextExit, nil +} + +func onError(w watch, err error) (next, error) { + if IsTerminalError(err) { + return nextExit, err + } + + if w.this { + return nextBackoff, fmt.Errorf("retry %s failed: %w", w.name, err) + } + + panic("unreachable, as watches are wrapped in retries and may only return terminal errors") +} diff --git a/internal/retry/retry_test.go b/internal/retry/retry_test.go new file mode 100644 index 000000000..c131653e9 --- /dev/null +++ b/internal/retry/retry_test.go @@ -0,0 +1,124 @@ +package retry_test + +import ( + "context" + "errors" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/pomerium/pomerium/internal/retry" +) + +func TestRetry(t *testing.T) { + t.Parallel() + + ctx := context.Background() + limit := retry.WithMaxInterval(time.Second * 5) + + t.Run("no error", func(t *testing.T) { + t.Parallel() + + err := retry.Retry(ctx, "test", func(_ context.Context) error { + return nil + }, limit) + require.NoError(t, err) + }) + + t.Run("eventually succeeds", func(t *testing.T) { + t.Parallel() + i := 0 + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if i++; i > 2 { + return nil + } + return fmt.Errorf("transient %d", i) + }, limit) + require.NoError(t, err) + }) + + t.Run("eventually fails", func(t *testing.T) { + t.Parallel() + i := 0 + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if i++; i > 2 { + return retry.NewTerminalError(errors.New("the end")) + } + return fmt.Errorf("transient %d", i) + }) + require.Error(t, err) + }) + + t.Run("context canceled", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(ctx) + cancel() + err := retry.Retry(ctx, "test", func(_ context.Context) error { + return fmt.Errorf("retry") + }) + require.Error(t, err) + }) + + t.Run("context canceled after retry", func(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(ctx) + t.Cleanup(cancel) + err := retry.Retry(ctx, "test", func(_ context.Context) error { + cancel() + return fmt.Errorf("retry") + }) + require.Error(t, err) + }) + + t.Run("success after watch hook", func(t *testing.T) { + t.Parallel() + ch := make(chan struct{}, 1) + ch <- struct{}{} + ok := false + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if ok { + return nil + } + return fmt.Errorf("retry") + }, retry.WithWatch("watch", ch, func(_ context.Context) error { + ok = true + return nil + }), limit) + require.NoError(t, err) + }) + + t.Run("success after watch hook retried", func(t *testing.T) { + t.Parallel() + ch := make(chan struct{}, 1) + ch <- struct{}{} + ok := false + i := 0 + err := retry.Retry(ctx, "test", func(_ context.Context) error { + if ok { + return nil + } + return fmt.Errorf("retry test") + }, retry.WithWatch("watch", ch, func(_ context.Context) error { + if i++; i > 1 { + ok = true + return nil + } + return fmt.Errorf("retry watch") + }), limit) + require.NoError(t, err) + }) + + t.Run("watch hook fails", func(t *testing.T) { + t.Parallel() + ch := make(chan struct{}, 1) + ch <- struct{}{} + err := retry.Retry(ctx, "test", func(_ context.Context) error { + return fmt.Errorf("retry") + }, retry.WithWatch("watch", ch, func(_ context.Context) error { + return retry.NewTerminalError(fmt.Errorf("watch")) + }), limit) + require.Error(t, err) + }) +}