diff --git a/config/codec_type.go b/config/codec_type.go index 860b15011..82152ec42 100644 --- a/config/codec_type.go +++ b/config/codec_type.go @@ -19,6 +19,7 @@ const ( CodecTypeAuto CodecType = "auto" CodecTypeHTTP1 CodecType = "http1" CodecTypeHTTP2 CodecType = "http2" + CodecTypeHTTP3 CodecType = "http3" ) // ParseCodecType parses the codec type. @@ -30,6 +31,8 @@ func ParseCodecType(raw string) (CodecType, error) { return CodecTypeHTTP1, nil case CodecTypeHTTP2: return CodecTypeHTTP2, nil + case CodecTypeHTTP3: + return CodecTypeHTTP3, nil } return CodecTypeAuto, fmt.Errorf("invalid codec type: %s", raw) } @@ -41,6 +44,8 @@ func CodecTypeFromEnvoy(envoyCodecType envoy_http_connection_manager.HttpConnect return CodecTypeHTTP1 case envoy_http_connection_manager.HttpConnectionManager_HTTP2: return CodecTypeHTTP2 + case envoy_http_connection_manager.HttpConnectionManager_HTTP3: + return CodecTypeHTTP3 } return CodecTypeAuto } @@ -52,6 +57,8 @@ func (codecType CodecType) ToEnvoy() envoy_http_connection_manager.HttpConnectio return envoy_http_connection_manager.HttpConnectionManager_HTTP1 case CodecTypeHTTP2: return envoy_http_connection_manager.HttpConnectionManager_HTTP2 + case CodecTypeHTTP3: + return envoy_http_connection_manager.HttpConnectionManager_HTTP3 } return envoy_http_connection_manager.HttpConnectionManager_AUTO } diff --git a/config/envoyconfig/acmetlsalpn.go b/config/envoyconfig/acmetlsalpn.go index a7ccb2385..c29185578 100644 --- a/config/envoyconfig/acmetlsalpn.go +++ b/config/envoyconfig/acmetlsalpn.go @@ -32,7 +32,7 @@ func (b *Builder) buildACMETLSALPNCluster( LbEndpoints: []*envoy_config_endpoint_v3.LbEndpoint{{ HostIdentifier: &envoy_config_endpoint_v3.LbEndpoint_Endpoint{ Endpoint: &envoy_config_endpoint_v3.Endpoint{ - Address: buildAddress("127.0.0.1", uint32(port)), + Address: buildTCPAddress("127.0.0.1", uint32(port)), }, }, }}, diff --git a/config/envoyconfig/clusters.go b/config/envoyconfig/clusters.go index 2d50ddbb0..d0d1fd7f9 100644 --- a/config/envoyconfig/clusters.go +++ b/config/envoyconfig/clusters.go @@ -463,7 +463,7 @@ func (b *Builder) buildLbEndpoints(endpoints []Endpoint) ([]*envoy_config_endpoi lbe := &envoy_config_endpoint_v3.LbEndpoint{ HostIdentifier: &envoy_config_endpoint_v3.LbEndpoint_Endpoint{ Endpoint: &envoy_config_endpoint_v3.Endpoint{ - Address: buildAddress(u.Host, defaultPort), + Address: buildTCPAddress(u.Host, defaultPort), Hostname: e.url.Host, }, }, diff --git a/config/envoyconfig/envoyconfig.go b/config/envoyconfig/envoyconfig.go index 494fd5fc8..571112d41 100644 --- a/config/envoyconfig/envoyconfig.go +++ b/config/envoyconfig/envoyconfig.go @@ -115,7 +115,15 @@ func buildAccessLogs(options *config.Options) []*envoy_config_accesslog_v3.Acces }} } -func buildAddress(hostport string, defaultPort uint32) *envoy_config_core_v3.Address { +func buildTCPAddress(hostport string, defaultPort uint32) *envoy_config_core_v3.Address { + return buildAddress(envoy_config_core_v3.SocketAddress_TCP, hostport, defaultPort) +} + +func buildUDPAddress(hostport string, defaultPort uint32) *envoy_config_core_v3.Address { + return buildAddress(envoy_config_core_v3.SocketAddress_UDP, hostport, defaultPort) +} + +func buildAddress(protocol envoy_config_core_v3.SocketAddress_Protocol, hostport string, defaultPort uint32) *envoy_config_core_v3.Address { host, strport, err := net.SplitHostPort(hostport) if err != nil { host = hostport @@ -140,6 +148,7 @@ func buildAddress(hostport string, defaultPort uint32) *envoy_config_core_v3.Add return &envoy_config_core_v3.Address{ Address: &envoy_config_core_v3.Address_SocketAddress{SocketAddress: &envoy_config_core_v3.SocketAddress{ + Protocol: protocol, Address: host, PortSpecifier: &envoy_config_core_v3.SocketAddress_PortValue{PortValue: port}, Ipv4Compat: host == "::" || is4in6, diff --git a/config/envoyconfig/filters.go b/config/envoyconfig/filters.go index 8150d3c4b..2292f73e4 100644 --- a/config/envoyconfig/filters.go +++ b/config/envoyconfig/filters.go @@ -4,6 +4,7 @@ import ( envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_extensions_filters_http_ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3" + envoy_extensions_filters_http_header_mutation_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_mutation/v3" envoy_extensions_filters_http_lua_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3" envoy_extensions_filters_http_router_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" envoy_extensions_filters_listener_proxy_protocol_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/proxy_protocol/v3" @@ -54,6 +55,16 @@ func HTTPConnectionManagerFilter( } } +// HTTPHeaderMutationsFilter creates a new HTTP header mutations filter. +func HTTPHeaderMutationsFilter(mutation *envoy_extensions_filters_http_header_mutation_v3.HeaderMutation) *envoy_extensions_filters_network_http_connection_manager.HttpFilter { + return &envoy_extensions_filters_network_http_connection_manager.HttpFilter{ + Name: "envoy.filters.http.header_mutation", + ConfigType: &envoy_extensions_filters_network_http_connection_manager.HttpFilter_TypedConfig{ + TypedConfig: protoutil.NewAny(mutation), + }, + } +} + // HTTPRouterFilter creates a new HTTP router filter. func HTTPRouterFilter() *envoy_extensions_filters_network_http_connection_manager.HttpFilter { return &envoy_extensions_filters_network_http_connection_manager.HttpFilter{ diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go index 75bab50a3..f017c0834 100644 --- a/config/envoyconfig/listeners.go +++ b/config/envoyconfig/listeners.go @@ -4,6 +4,7 @@ import ( "context" "runtime" + envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" "google.golang.org/protobuf/types/known/wrapperspb" @@ -25,11 +26,19 @@ func (b *Builder) BuildListeners( var listeners []*envoy_config_listener_v3.Listener if shouldStartMainListener(cfg.Options) { - li, err := b.buildMainListener(ctx, cfg, fullyStatic) + li, err := b.buildMainListener(ctx, cfg, fullyStatic, false) if err != nil { return nil, err } listeners = append(listeners, li) + // for HTTP/3 we add another main listener that listens on UDP + if cfg.Options.GetCodecType() == config.CodecTypeHTTP3 { + li, err := b.buildMainListener(ctx, cfg, fullyStatic, true) + if err != nil { + return nil, err + } + listeners = append(listeners, li) + } } if shouldStartGRPCListener(cfg.Options) { @@ -77,3 +86,23 @@ func newListener(name string) *envoy_config_listener_v3.Listener { EnableReusePort: wrapperspb.Bool(runtime.GOOS == "linux"), } } + +// newQUICListener creates a new envoy listener that handles QUIC connections. +func newQUICListener(name string, address *envoy_config_core_v3.Address) *envoy_config_listener_v3.Listener { + li := newListener(name) + li.Address = address + li.UdpListenerConfig = &envoy_config_listener_v3.UdpListenerConfig{ + QuicOptions: &envoy_config_listener_v3.QuicProtocolOptions{}, + DownstreamSocketConfig: &envoy_config_core_v3.UdpSocketConfig{ + PreferGro: &wrapperspb.BoolValue{Value: true}, + }, + } + return li +} + +// newTCPListener creates a new envoy listener that handles TCP connections. +func newTCPListener(name string, address *envoy_config_core_v3.Address) *envoy_config_listener_v3.Listener { + li := newListener(name) + li.Address = address + return li +} diff --git a/config/envoyconfig/listeners_envoy_admin.go b/config/envoyconfig/listeners_envoy_admin.go index f84205e57..f2bffb611 100644 --- a/config/envoyconfig/listeners_envoy_admin.go +++ b/config/envoyconfig/listeners_envoy_admin.go @@ -25,8 +25,7 @@ func (b *Builder) buildEnvoyAdminListener(_ context.Context, cfg *config.Config) return nil, fmt.Errorf("envoy_admin_addr %s: %w", cfg.Options.EnvoyAdminAddress, err) } - li := newListener("envoy-admin") - li.Address = addr + li := newTCPListener("envoy-admin", addr) li.FilterChains = []*envoy_config_listener_v3.FilterChain{filterChain} return li, nil } diff --git a/config/envoyconfig/listeners_grpc.go b/config/envoyconfig/listeners_grpc.go index 69eff2965..802947cf1 100644 --- a/config/envoyconfig/listeners_grpc.go +++ b/config/envoyconfig/listeners_grpc.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_http_connection_manager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" @@ -20,15 +21,20 @@ func (b *Builder) buildGRPCListener(ctx context.Context, cfg *config.Config) (*e Filters: []*envoy_config_listener_v3.Filter{filter}, } - li := newListener("grpc-ingress") + var address *envoy_config_core_v3.Address + if cfg.Options.GetGRPCInsecure() { + address = buildTCPAddress(cfg.Options.GetGRPCAddr(), 80) + } else { + address = buildTCPAddress(cfg.Options.GetGRPCAddr(), 443) + } + + li := newTCPListener("grpc-ingress", address) li.FilterChains = []*envoy_config_listener_v3.FilterChain{&filterChain} if cfg.Options.GetGRPCInsecure() { - li.Address = buildAddress(cfg.Options.GetGRPCAddr(), 80) return li, nil } - li.Address = buildAddress(cfg.Options.GetGRPCAddr(), 443) li.ListenerFilters = []*envoy_config_listener_v3.ListenerFilter{ TLSInspectorFilter(), } diff --git a/config/envoyconfig/listeners_main.go b/config/envoyconfig/listeners_main.go index 0bbd14134..167714f82 100644 --- a/config/envoyconfig/listeners_main.go +++ b/config/envoyconfig/listeners_main.go @@ -2,6 +2,7 @@ package envoyconfig import ( "context" + "fmt" "time" envoy_config_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" @@ -20,8 +21,11 @@ func (b *Builder) buildMainListener( ctx context.Context, cfg *config.Config, fullyStatic bool, + useQUIC bool, ) (*envoy_config_listener_v3.Listener, error) { - if cfg.Options.InsecureServer { + if useQUIC { + return b.buildMainQUICListener(ctx, cfg, fullyStatic) + } else if cfg.Options.InsecureServer { return b.buildMainInsecureListener(ctx, cfg, fullyStatic) } return b.buildMainTLSListener(ctx, cfg, fullyStatic) @@ -32,15 +36,49 @@ func (b *Builder) buildMainInsecureListener( cfg *config.Config, fullyStatic bool, ) (*envoy_config_listener_v3.Listener, error) { - li := newListener("http-ingress") - li.Address = buildAddress(cfg.Options.Addr, 80) + li := newTCPListener("http-ingress", buildTCPAddress(cfg.Options.Addr, 80)) // listener filters if cfg.Options.UseProxyProtocol { li.ListenerFilters = append(li.ListenerFilters, ProxyProtocolFilter()) } - filterChain, err := b.buildMainHTTPConnectionManagerFilterChain(ctx, cfg, fullyStatic, nil) + filterChain, err := b.buildMainHTTPConnectionManagerFilterChain(ctx, cfg, fullyStatic, false, nil) + if err != nil { + return nil, err + } + li.FilterChains = append(li.FilterChains, filterChain) + + return li, nil +} + +func (b *Builder) buildMainQUICListener( + ctx context.Context, + cfg *config.Config, + fullyStatic bool, +) (*envoy_config_listener_v3.Listener, error) { + li := newQUICListener("quic-ingress", buildUDPAddress(cfg.Options.Addr, 443)) + + // listener filters + if cfg.Options.UseProxyProtocol { + li.ListenerFilters = append(li.ListenerFilters, ProxyProtocolFilter()) + } + // access log + if cfg.Options.DownstreamMTLS.Enforcement == config.MTLSEnforcementRejectConnection { + li.AccessLog = append(li.AccessLog, newListenerAccessLog()) + } + + allCertificates, err := getAllCertificates(cfg) + if err != nil { + return nil, err + } + + transportSocket, err := b.buildDownstreamQUICTransportSocket(ctx, cfg, allCertificates) + if err != nil { + return nil, fmt.Errorf("error building quic socket: %w", err) + } + + filterChain, err := b.buildMainHTTPConnectionManagerFilterChain(ctx, cfg, fullyStatic, true, transportSocket) if err != nil { return nil, err } @@ -54,8 +92,7 @@ func (b *Builder) buildMainTLSListener( cfg *config.Config, fullyStatic bool, ) (*envoy_config_listener_v3.Listener, error) { - li := newListener("https-ingress") - li.Address = buildAddress(cfg.Options.Addr, 443) + li := newTCPListener("https-ingress", buildTCPAddress(cfg.Options.Addr, 443)) // listener filters if cfg.Options.UseProxyProtocol { @@ -81,7 +118,8 @@ func (b *Builder) buildMainTLSListener( return nil, err } - filterChain, err := b.buildMainHTTPConnectionManagerFilterChain(ctx, cfg, fullyStatic, newDownstreamTLSTransportSocket(tlsContext)) + transportSocket := newDownstreamTLSTransportSocket(tlsContext) + filterChain, err := b.buildMainHTTPConnectionManagerFilterChain(ctx, cfg, fullyStatic, false, transportSocket) if err != nil { return nil, err } @@ -94,9 +132,10 @@ func (b *Builder) buildMainHTTPConnectionManagerFilterChain( ctx context.Context, cfg *config.Config, fullyStatic bool, + useQUIC bool, transportSocket *envoy_config_core_v3.TransportSocket, ) (*envoy_config_listener_v3.FilterChain, error) { - filter, err := b.buildMainHTTPConnectionManagerFilter(ctx, cfg, fullyStatic) + filter, err := b.buildMainHTTPConnectionManagerFilter(ctx, cfg, fullyStatic, useQUIC) if err != nil { return nil, err } @@ -110,6 +149,7 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter( ctx context.Context, cfg *config.Config, fullyStatic bool, + useQUIC bool, ) (*envoy_config_listener_v3.Filter, error) { var grpcClientTimeout *durationpb.Duration if cfg.Options.GRPCClientTimeout != 0 { @@ -126,6 +166,10 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter( LuaFilter(luascripts.CleanUpstream), LuaFilter(luascripts.RewriteHeaders), } + // if we support http3 and this is the non-quic listener, add an alt-svc header indicating h3 is available + if !useQUIC && cfg.Options.CodecType == config.CodecTypeHTTP3 { + filters = append(filters, newQUICAltSvcHeaderFilter(cfg)) + } filters = append(filters, HTTPRouterFilter()) var maxStreamDuration *durationpb.Duration @@ -145,7 +189,6 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter( mgr := &envoy_extensions_filters_network_http_connection_manager.HttpConnectionManager{ AlwaysSetRequestIdInResponse: true, - CodecType: cfg.Options.GetCodecType().ToEnvoy(), StatPrefix: "ingress", HttpFilters: filters, AccessLog: buildAccessLogs(cfg.Options), @@ -167,6 +210,15 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter( NormalizePath: wrapperspb.Bool(true), } + if useQUIC { + mgr.CodecType = envoy_extensions_filters_network_http_connection_manager.HttpConnectionManager_HTTP3 + mgr.Http3ProtocolOptions = &envoy_config_core_v3.Http3ProtocolOptions{} + } else if cfg.Options.GetCodecType() == config.CodecTypeHTTP3 { + mgr.CodecType = envoy_extensions_filters_network_http_connection_manager.HttpConnectionManager_AUTO + } else { + mgr.CodecType = cfg.Options.GetCodecType().ToEnvoy() + } + if fullyStatic { routeConfiguration, err := b.buildMainRouteConfiguration(ctx, cfg) if err != nil { diff --git a/config/envoyconfig/listeners_main_test.go b/config/envoyconfig/listeners_main_test.go index af5d18ae3..0a77de4a3 100644 --- a/config/envoyconfig/listeners_main_test.go +++ b/config/envoyconfig/listeners_main_test.go @@ -17,7 +17,7 @@ func Test_requireProxyProtocol(t *testing.T) { li, err := b.buildMainListener(context.Background(), &config.Config{Options: &config.Options{ UseProxyProtocol: true, InsecureServer: true, - }}, false) + }}, false, false) require.NoError(t, err) testutil.AssertProtoJSONEqual(t, `[ { @@ -32,7 +32,7 @@ func Test_requireProxyProtocol(t *testing.T) { li, err := b.buildMainListener(context.Background(), &config.Config{Options: &config.Options{ UseProxyProtocol: false, InsecureServer: true, - }}, false) + }}, false, false) require.NoError(t, err) assert.Len(t, li.GetListenerFilters(), 0) }) diff --git a/config/envoyconfig/listeners_metrics.go b/config/envoyconfig/listeners_metrics.go index 1abf7d1af..5ceff5af1 100644 --- a/config/envoyconfig/listeners_metrics.go +++ b/config/envoyconfig/listeners_metrics.go @@ -80,9 +80,8 @@ func (b *Builder) buildMetricsListener(cfg *config.Config) (*envoy_config_listen host = "" } - addr := buildAddress(net.JoinHostPort(host, port), 9902) - li := newListener(fmt.Sprintf("metrics-ingress-%d", hashutil.MustHash(addr))) - li.Address = addr + addr := buildTCPAddress(net.JoinHostPort(host, port), 9902) + li := newTCPListener(fmt.Sprintf("metrics-ingress-%d", hashutil.MustHash(addr)), addr) li.FilterChains = []*envoy_config_listener_v3.FilterChain{filterChain} return li, nil } diff --git a/config/envoyconfig/listeners_test.go b/config/envoyconfig/listeners_test.go index b968ce946..b26925e28 100644 --- a/config/envoyconfig/listeners_test.go +++ b/config/envoyconfig/listeners_test.go @@ -12,6 +12,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" + "google.golang.org/protobuf/encoding/protojson" "github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config/envoyconfig/filemgr" @@ -71,6 +73,49 @@ func TestBuildListeners(t *testing.T) { } assert.False(t, hasGRPC, "expected grpc-ingress to be disabled when grpc address is set to the empty string") }) + t.Run("quic", func(t *testing.T) { + t.Parallel() + + cfg := cfg.Clone() + cfg.Options.CodecType = config.CodecTypeHTTP3 + lis, err := b.BuildListeners(ctx, cfg, false) + assert.NoError(t, err) + + var hasHTTPS, hasQUIC bool + for _, li := range lis { + switch li.GetName() { + case "https-ingress": + hasHTTPS = true + httpConfig := gjson.Get(protojson.Format(li), "filterChains.1.filters.0.typedConfig") + assert.Equal(t, "", httpConfig.Get("codecType").String()) + assert.JSONEq(t, `{ + "name": "envoy.filters.http.header_mutation", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.http.header_mutation.v3.HeaderMutation", + "mutations": { + "responseMutations": [{ + "append": { + "header": { + "key": "alt-svc", + "value": "h3=\":443\"; ma=86400" + } + } + }] + } + } + }`, httpConfig.Get("httpFilters.6").String(), + "should add alt-svc header") + case "quic-ingress": + hasQUIC = true + httpConfig := gjson.Get(protojson.Format(li), "filterChains.0.filters.0.typedConfig") + assert.Equal(t, "HTTP3", httpConfig.Get("codecType").String()) + assert.Equal(t, `{}`, httpConfig.Get("http3ProtocolOptions").String()) + } + } + + assert.True(t, hasHTTPS, "should have https-ingress listener") + assert.True(t, hasQUIC, "should have quic-ingress listener") + }) } func Test_buildMetricsHTTPConnectionManagerFilter(t *testing.T) { @@ -102,7 +147,7 @@ func Test_buildMainHTTPConnectionManagerFilter(t *testing.T) { options.SkipXffAppend = true options.XffNumTrustedHops = 1 options.AuthenticateURLString = "https://authenticate.example.com" - filter, err := b.buildMainHTTPConnectionManagerFilter(context.Background(), &config.Config{Options: options}, false) + filter, err := b.buildMainHTTPConnectionManagerFilter(context.Background(), &config.Config{Options: options}, false, false) require.NoError(t, err) testutil.AssertProtoJSONEqual(t, testData(t, "main_http_connection_manager_filter.json", nil), filter) } diff --git a/config/envoyconfig/outbound.go b/config/envoyconfig/outbound.go index 42c51c03d..bcf0b93f7 100644 --- a/config/envoyconfig/outbound.go +++ b/config/envoyconfig/outbound.go @@ -22,8 +22,7 @@ func (b *Builder) buildOutboundListener(cfg *config.Config) (*envoy_config_liste filter := b.buildOutboundHTTPConnectionManager() - li := newListener("outbound-ingress") - li.Address = &envoy_config_core_v3.Address{ + li := newTCPListener("outbound-ingress", &envoy_config_core_v3.Address{ Address: &envoy_config_core_v3.Address_SocketAddress{ SocketAddress: &envoy_config_core_v3.SocketAddress{ Address: "127.0.0.1", @@ -32,7 +31,7 @@ func (b *Builder) buildOutboundListener(cfg *config.Config) (*envoy_config_liste }, }, }, - } + }) li.FilterChains = []*envoy_config_listener_v3.FilterChain{{ Name: "outbound-ingress", Filters: []*envoy_config_listener_v3.Filter{filter}, diff --git a/config/envoyconfig/quic.go b/config/envoyconfig/quic.go new file mode 100644 index 000000000..c09d7c7eb --- /dev/null +++ b/config/envoyconfig/quic.go @@ -0,0 +1,55 @@ +package envoyconfig + +import ( + "context" + "crypto/tls" + "fmt" + + envoy_config_common_mutation_rules_v3 "github.com/envoyproxy/go-control-plane/envoy/config/common/mutation_rules/v3" + envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_extensions_filters_http_header_mutation_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/header_mutation/v3" + envoy_extensions_filters_network_http_connection_manager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + envoy_extensions_transport_sockets_quic_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/quic/v3" + + "github.com/pomerium/pomerium/config" +) + +func (b *Builder) buildDownstreamQUICTransportSocket( + ctx context.Context, + cfg *config.Config, + certs []tls.Certificate, +) (*envoy_config_core_v3.TransportSocket, error) { + tlsContext, err := b.buildDownstreamTLSContextMulti(ctx, cfg, certs) + if err != nil { + return nil, err + } + tlsContext.CommonTlsContext.AlpnProtocols = nil + + return &envoy_config_core_v3.TransportSocket{ + Name: "envoy.transport_sockets.quic", + ConfigType: &envoy_config_core_v3.TransportSocket_TypedConfig{ + TypedConfig: marshalAny(&envoy_extensions_transport_sockets_quic_v3.QuicDownstreamTransport{ + DownstreamTlsContext: tlsContext, + }), + }, + }, nil +} + +func newQUICAltSvcHeaderFilter(cfg *config.Config) *envoy_extensions_filters_network_http_connection_manager.HttpFilter { + listenAddr := buildUDPAddress(cfg.Options.Addr, 443) + listenPort := listenAddr.GetSocketAddress().GetPortValue() + return HTTPHeaderMutationsFilter(&envoy_extensions_filters_http_header_mutation_v3.HeaderMutation{ + Mutations: &envoy_extensions_filters_http_header_mutation_v3.Mutations{ + ResponseMutations: []*envoy_config_common_mutation_rules_v3.HeaderMutation{{ + Action: &envoy_config_common_mutation_rules_v3.HeaderMutation_Append{ + Append: &envoy_config_core_v3.HeaderValueOption{ + Header: &envoy_config_core_v3.HeaderValue{ + Key: "alt-svc", + Value: fmt.Sprintf(`h3=":%d"; ma=86400`, listenPort), + }, + }, + }, + }}, + }, + }) +} diff --git a/go.mod b/go.mod index 48df6e6fc..9254b9557 100644 --- a/go.mod +++ b/go.mod @@ -56,6 +56,7 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.60.1 github.com/prometheus/procfs v0.15.1 + github.com/quic-go/quic-go v0.48.1 github.com/rs/cors v1.11.1 github.com/rs/zerolog v1.33.0 github.com/shirou/gopsutil/v3 v3.24.5 @@ -63,6 +64,7 @@ require ( github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.34.0 + github.com/tidwall/gjson v1.18.0 github.com/tniswong/go.rfcx v0.0.0-20181019234604-07783c52761f github.com/volatiletech/null/v9 v9.0.0 github.com/yuin/gopher-lua v1.1.1 @@ -150,6 +152,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -157,6 +160,7 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v23.5.26+incompatible // indirect github.com/google/go-tpm v0.9.0 // indirect + github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect @@ -186,6 +190,7 @@ require ( github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/onsi/ginkgo v1.16.5 // indirect + github.com/onsi/ginkgo/v2 v2.19.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect @@ -195,6 +200,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/statsd_exporter v0.22.7 // indirect + github.com/quic-go/qpack v0.5.1 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/xid v1.6.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -209,6 +215,8 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect github.com/tinylib/msgp v1.1.8 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/numcpus v0.8.0 // indirect diff --git a/go.sum b/go.sum index 24e5fe85b..31044cd99 100644 --- a/go.sum +++ b/go.sum @@ -268,7 +268,6 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -590,6 +589,10 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA= +github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= @@ -662,6 +665,12 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= diff --git a/integration/authentication_test.go b/integration/authentication_test.go index 39c2b2096..62c36e148 100644 --- a/integration/authentication_test.go +++ b/integration/authentication_test.go @@ -19,45 +19,45 @@ func TestRouteSessions(t *testing.T) { ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*30) defer clearTimeout() - client := getClient(t) + testHTTPClient(t, func(t *testing.T, client *http.Client) { + // Sign in to access one route. + url1 := mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain") + res, err := flows.Authenticate(ctx, client, url1, flows.WithEmail("user1@dogs.test")) + require.NoError(t, err) + require.Equal(t, http.StatusOK, res.StatusCode, "expected OK for httpdetails") - // Sign in to access one route. - url1 := mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain") - res, err := flows.Authenticate(ctx, client, url1, flows.WithEmail("user1@dogs.test")) - require.NoError(t, err) - require.Equal(t, http.StatusOK, res.StatusCode, "expected OK for httpdetails") + // Now request a different route. This should not require signing in again, + // but will redirect through the authenticate service if using the + // stateless authentication flow. + client.CheckRedirect = nil + url2 := mustParseURL("https://restricted-httpdetails.localhost.pomerium.io/by-domain") + req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url2.String(), nil) + res, err = client.Do(req) + require.NoError(t, err) + require.Equal(t, http.StatusOK, res.StatusCode, "expected OK for restricted-httpdetails") - // Now request a different route. This should not require signing in again, - // but will redirect through the authenticate service if using the - // stateless authentication flow. - client.CheckRedirect = nil - url2 := mustParseURL("https://restricted-httpdetails.localhost.pomerium.io/by-domain") - req, _ := http.NewRequestWithContext(ctx, http.MethodGet, url2.String(), nil) - res, err = client.Do(req) - require.NoError(t, err) - require.Equal(t, http.StatusOK, res.StatusCode, "expected OK for restricted-httpdetails") + // Now examine the session cookies saved for each route. + claims1 := getSessionCookieJWTClaims(t, client, url1) + claims2 := getSessionCookieJWTClaims(t, client, url2) - // Now examine the session cookies saved for each route. - claims1 := getSessionCookieJWTClaims(t, client, url1) - claims2 := getSessionCookieJWTClaims(t, client, url2) + if AuthenticateFlow == "stateless" { + // Under the stateless authenticate flow, each route should have its + // own session. + assert.NotEqual(t, claims1.ID, claims2.ID) + } else { + // Under the stateful authenticate flow, the two routes should share + // the same session. + assert.Equal(t, claims1.ID, claims2.ID) - if AuthenticateFlow == "stateless" { - // Under the stateless authenticate flow, each route should have its - // own session. - assert.NotEqual(t, claims1.ID, claims2.ID) - } else { - // Under the stateful authenticate flow, the two routes should share - // the same session. - assert.Equal(t, claims1.ID, claims2.ID) - - // The only cookies set on the authenticate service domain should be - // "_pomerium_authenticate" and "_pomerium_csrf". (No identity profile - // cookies should be present.) - c := client.Jar.Cookies(mustParseURL("https://authenticate.localhost.pomerium.io")) - assert.Equal(t, 2, len(c)) - cookieNames := slices.Map(c, func(c *http.Cookie) string { return c.Name }) - assert.ElementsMatch(t, []string{"_pomerium_authenticate", "_pomerium_csrf"}, cookieNames) - } + // The only cookies set on the authenticate service domain should be + // "_pomerium_authenticate" and "_pomerium_csrf". (No identity profile + // cookies should be present.) + c := client.Jar.Cookies(mustParseURL("https://authenticate.localhost.pomerium.io")) + assert.Equal(t, 2, len(c)) + cookieNames := slices.Map(c, func(c *http.Cookie) string { return c.Name }) + assert.ElementsMatch(t, []string{"_pomerium_authenticate", "_pomerium_csrf"}, cookieNames) + } + }) } func getSessionCookieJWTClaims(t *testing.T, client *http.Client, u *url.URL) *jwt.Claims { diff --git a/integration/authorization_test.go b/integration/authorization_test.go index 8e49c472d..5706bf7a3 100644 --- a/integration/authorization_test.go +++ b/integration/authorization_test.go @@ -28,7 +28,7 @@ func TestAuthorization(t *testing.T) { } t.Run("public", func(t *testing.T) { - client := getClient(t) + client := getClient(t, false) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io", nil) if err != nil { @@ -46,7 +46,7 @@ func TestAuthorization(t *testing.T) { t.Run("domains", func(t *testing.T) { t.Run("allowed", func(t *testing.T) { - client := getClient(t) + client := getClient(t, false) res, err := flows.Authenticate(ctx, client, mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain"), withAPI, flows.WithEmail("user1@dogs.test"), withBrowserAcceptHeader) if assert.NoError(t, err) { @@ -54,7 +54,7 @@ func TestAuthorization(t *testing.T) { } }) t.Run("not allowed", func(t *testing.T) { - client := getClient(t) + client := getClient(t, false) res, err := flows.Authenticate(ctx, client, mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain"), withAPI, flows.WithEmail("user1@cats.test"), withBrowserAcceptHeader) if assert.NoError(t, err) { diff --git a/integration/benchmark_test.go b/integration/benchmark_test.go index c6d976613..1b5d9b09f 100644 --- a/integration/benchmark_test.go +++ b/integration/benchmark_test.go @@ -12,7 +12,7 @@ import ( func BenchmarkLoggedInUserAccess(b *testing.B) { ctx := context.Background() - client := getClient(b) + client := getClient(b, false) res, err := flows.Authenticate(ctx, client, mustParseURL("https://httpdetails.localhost.pomerium.io/by-domain"), flows.WithEmail("user1@dogs.test")) require.NoError(b, err) @@ -30,7 +30,7 @@ func BenchmarkLoggedInUserAccess(b *testing.B) { func BenchmarkLoggedOutUserAccess(b *testing.B) { ctx := context.Background() - client := getClient(b) + client := getClient(b, false) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/integration/clusters/kubernetes/compose.yml b/integration/clusters/kubernetes/compose.yml index 1dbebd27d..1a8801c48 100644 --- a/integration/clusters/kubernetes/compose.yml +++ b/integration/clusters/kubernetes/compose.yml @@ -139,6 +139,10 @@ services: "name": "CERTIFICATE_KEY", "value": "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K" }, + { + "name": "CODEC_TYPE", + "value": "http3" + }, { "name": "COOKIE_SECRET", "value": "UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=" @@ -220,6 +224,11 @@ services: "containerPort": 443, "name": "https" }, + { + "containerPort": 443, + "name": "quic", + "protocol": "UDP" + }, { "containerPort": 5443, "name": "grpc" @@ -727,6 +736,13 @@ services: "port": 443, "targetPort": "https" }, + { + "name": "quic", + "nodePort": 443, + "port": 443, + "protocol": "UDP", + "targetPort": "quic" + }, { "name": "grpc", "nodePort": 5443, diff --git a/integration/clusters/multi-stateful/compose.yml b/integration/clusters/multi-stateful/compose.yml index 255bca3e9..8816318de 100644 --- a/integration/clusters/multi-stateful/compose.yml +++ b/integration/clusters/multi-stateful/compose.yml @@ -162,6 +162,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TXpFeE1UQXkKTURBNE5EUmFGdzB6TXpFeE1EY3lNREE0TkRSYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBcHFWekozUWY5VnFrdWpGYmMwTUJEcVdELzhnamZkN21XMjlmUnRNSVAzemRKbGl5ZXZSajczQUwKaWZYNVpadW5UN24vajUyWnppRmliNGo4dWM0UjZWd0FFN2xMcERlc2ZzTDRBZ3ZHNnVqSmFKTGgrcTZmUEZWbQo4VXdJcjMvSGpaQUdQdmJ3Y2VBTzAwbXRmcW44YUsxS2VLeGZFazlVaFRVV2hzcXVieTg4RWNKVmh4a1RzQUhvCmtLUWtFYWY5TkxhemhaMFAwdTlKLzE0VkdoTU44UVVIdklMVmpja0NEaElqMzhJVUs3VXRaSGtNNzJHbUtyajIKU0M0MElEZE50NHpiMUFUTFZleU9MZHdLandFRmdLV3prdkkvN1VqOXBBMjYvZVlHUFE3b3hSRitJRXhWSWhEcgpFSnZIcldRMHMwRUtOUGRwVS9JaHF0azByWWtqODFwZXFNOFRtSTZ2cXJacUFFUHphMXRZazZXUXN6RG9ucFBXCnVLbGZyOUdZWWY1TXU5YTJ5MjZBZ2x1RG5pQWNuZldqUlhtcjFydlJIQnB6c0xTRDNTVG5QRTV0NkhKaWVQN3IKdjZrL2ZsWFE5U0V3MFUzbEkvblpLS3dpTGZXQzJPNUJwS3dNejE5Y1o4L2tMU0pXSGc0bGtEYjJVbzFKS25pVwora01FSTluTgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -223,6 +224,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYekNDQXNlZ0F3SUJBZ0lSQU5NalQzSmp2cEdtbjRUTzVPWUFyazh3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB4T1RBMk1ERXcKTURBd01EQmFGdzB6TVRFeE1UVXhPVEV4TlRWYU1Gc3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXdNQzRHQTFVRUN3d25ZMkZzWldKQVkyRnNaV0l0YkdGd2RHOXdMV3hwCmJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0MKQVFFQTlqUkR2Mkl5V1FaV3V1STkxdWdlbWF3R0RxK1BERUpUaVNDZ3ZhWWkwSEc0SEowNndwVjk4cDNTYWZVbgpwWU12ajlmY3lqbkJaOHlvcGU5VUQrakFSMVhEYmI1YlUvVU1iV3JkU29LWVM4a0dzYllDdDAzMHk4QWRaejY5CmI3Q2t2Y25IQUpPcDV4VXRYU1ExaEo3ZjQzZ29aaEdSbHJKU2cxSkpWKzJTTlUxMHNnM1V3YjNieGdBL1hrRk0KaUIzWnF2STRJNTh3RzBqakxVSEFmQ043eTFEQWFiVlNJQll6b1B5cjJxUUEvR0lHMWJsbzljWWdrRFlkb1d3UgpwZ21maHBLQ2NrNUxWZWdZejNBcSs4NkpPVGxHbWc5WlIxaUdNSEpNYklsUGJNdEU2MXovQjhtWUc5aUptUHJTCmYzNHVZVlJ4VDZTMkZWTkU1cDZVbHZXNHd3SURBUUFCbzNVd2N6QU9CZ05WSFE4QkFmOEVCQU1DQmFBd0V3WUQKVlIwbEJBd3dDZ1lJS3dZQkJRVUhBd0V3REFZRFZSMFRBUUgvQkFJd0FEQWZCZ05WSFNNRUdEQVdnQlNGaGxoWQpnRWZLVHBsVk9lbmVWRzMrM0lFL1RUQWRCZ05WSFJFRUZqQVVnaEp3YjIxbGNtbDFiUzFoZFhSb2IzSnBlbVV3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFESHNQT1p6WHh5eWMrZ1EwYkxSbG5WejNZMDV6cTNmV2gxeWp0U04KMmtQTFJ1aU02ZkZWUGlDV1VuUzk0cnNDeDV6Sk0xQnRNR3VNcUNMWGxmZERWYlZNcDM2d0NHMms5TWt3aE01dgpuTFRXTkkzclFZSUJnU0xvdFNTeTZYdUV5U1pJQlhUZ2RHODFYcG5pVjBUc1dKMjJEdGxlMlhOZUo1cHNnMS93CjdMbTZLdElkL3FEYUF5T1pLR1Y2Z21GL0YyNTB4RDkxYys3Mk9DUkFrK0x2WXhydFBVY3lwV0lNV2lMUEtlRkMKQnB6Q3BsNDB3c0o5YzVFMTdJcWNjMVVzanRwUnVXSlFRb0FBb3NhQ2RVUThXN04yMXdyNTcyTjFKcmNVOElBeAprN3U3T0Rrc24vK0NaODQ2N3dYclB5OXZGL3F2QkxtYWVFMWswd3VnTldlSnZNT0JLcGRKNG5iS0VCbU5xcmpMCmRRM21aTW90ZWNlelRDY29WYmRhdC9NVHNKR3hPSG42ZExoeHlKQzQ5K0FxbjJ2OGJ1ajZEbk56M3d3VHZxdXUKOGJEOW83SzlHdmRaaEtBNUxISmpFOXNkdmxzZVpNUnZqUGVnUDRWeWpaWmlBejBFUW04d292MHYvK2J5d2RUZgp5QjQ4eVJjSVdzdzZyU3BNdHR5ZU1neGJiQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRDJORU8vWWpKWkJsYTYKNGozVzZCNlpyQVlPcjQ4TVFsT0pJS0M5cGlMUWNiZ2NuVHJDbFgzeW5kSnA5U2VsZ3krUDE5ektPY0ZuektpbAo3MVFQNk1CSFZjTnR2bHRUOVF4dGF0MUtncGhMeVFheHRnSzNUZlRMd0IxblByMXZzS1M5eWNjQWs2bm5GUzFkCkpEV0VudC9qZUNobUVaR1dzbEtEVWtsWDdaSTFUWFN5RGRUQnZkdkdBRDllUVV5SUhkbXE4amdqbnpBYlNPTXQKUWNCOEkzdkxVTUJwdFZJZ0ZqT2cvS3ZhcEFEOFlnYlZ1V2oxeGlDUU5oMmhiQkdtQ1orR2tvSnlUa3RWNkJqUApjQ3I3em9rNU9VYWFEMWxIV0lZd2NreHNpVTlzeTBUclhQOEh5WmdiMkltWSt0Si9maTVoVkhGUHBMWVZVMFRtCm5wU1c5YmpEQWdNQkFBRUNnZ0VBWk9ML3krSEtMYzY2SlhSbDBMVm5MS05nREE2R2VJaUpRSXk5TVZsdHRLcVUKdml6emtoNjk4SGdaYTgvSWdqc2lkemdHUWx1dVZBRjZMaHpHL29ucW1KQ2ROMUFramwvQXl1bGFhcGpmSFZidgp4a3lHQ21VSVphR0Fxdmo3bVZQeC9jODhHNUZtc3NscndGb0JFQ29tSmtqcmpQK3YvK2RzaG1GbXFvQXdSR1IrCkp0ZW1xTlo3QWNwajhzV09ZeWQxZDlLbWtxS1RlaFZTSmpUaExlNjhvWXpxV3JxZWtVY2psbkI0a2ppbEU5VzAKYm15N0JqQlJqTlRGTWtSRUQ0eWtWUXlscGVZd1JXQkhYdzUxMlVVQVYxQnEyb0k0TGJoc0xYR1U2YXZSKy9XagpoemhKQ3ZpUTlxSUJ1OVhsRWtkcjdrQjVOQVVOd25pZUJhRE1VZmpxd1FLQmdRRDVrV3RtWmk0T3U4T1Z3QjF5CkZraE93T3RyM1oyTjBTOVZJbjAwc3ROTVlUVGNxT0J1Ynl2Y3RPdzA4UDJDVTVRcUtxRzY5QUtPRlBubm0vOEkKR1Vvc2k1eEh3S2Q3MTdoN1VhK0VTdG9GNkRDemZhR1BadXkrN0JnVFloeS96cjA5L0VJYkRPZHBOZEpmbis2ago1K0lDSjVqdEJRMUVUZXRJRTVQbWZVMXRvUUtCZ1FEOGpLYXYrZGRIUmwvMGZGN1plWit5TnZheGlCWGhXZkh2ClVXZVVaOXlKK04vbXhOckpqYTkzam5aZzFCTnIrdlYxUzNXTEwwN01wZm9HZ0IzWW82K3hXSlVhamRreTJMdE0KeGwxU1VmSERBRmQ1bWpGWVNTM3IvUGJxNDdJQ2hFK0ZmYndWNk9PNlpkYW1US3Y0TjFncUpvZWl0d2hvcEVGeApPYjBybm9DajR3S0JnQVB6NTYyUmhMMDdBQmdKR081THFBMkhSTE5LcVVvcnZBSXZMNmg4QkppN0dXazlTNEMwClJnK1MxZ0xvcGp0QU02S1BWa3h0dlBTZkpHNGtyRjltZHlSSmVjb3hKUFh6THJyVlNtQmdCeC84MDNpa1RzREUKc284N2tnNE1pY1FIOUR0ZEdYYndOMklWTmlYSmxMQUg1aTUramcrQyt2alAreUFESDE2Uk1YN0JBb0dCQU80WQpSRVgveTMvOHp4WVY4dTRoQmZma2JhdHQyd0w3NDJWMlg2ckFTR0VqYXlPTFQ2RlpuS1dnZitaRnlwR3dwWjBSCnFSMnhXaDhDRTdmeExUSkRENjZwRHRsY0Jmc3o3VkV5YW1UanNJUGhYSmJlVWNza0l0RXJTamFrdC93N2RTVXIKSnhtWTJLbXd2UzlIZFpHcnk4amhUbHFQS0xST3R3dmlIWUIwREY5MUFvR0FkeDJ3TGllTzVUcThBdkZhOXVydApCaVhwVFlMQ2JaWVBGZkVYTmZCYkt2NmFiWExBd2ZyRkQ1dkdrM0pFY2JqUTlnbWN6YW53QTFkUVhmN1NJLzRwCkZrUWhaSjhhdGJEaWl3SEVaeHpUeSt3cGx6OVlSN2tNQ0hkMndacXBsRCt1eXVxRWtZVVE5R09YZ2IzSUthVk4KVmd4QnJWSzlCNjdXM3FQZVRYMWxJdFU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -283,6 +285,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYekNDQXNlZ0F3SUJBZ0lRWDBCTFVEUmE0cHI3TFlueGRkYzRKREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEU1TURZd01UQXcKTURBd01Gb1hEVE14TVRFeE5URTVNVEl5TjFvd1d6RW5NQ1VHQTFVRUNoTWViV3RqWlhKMElHUmxkbVZzYjNCdApaVzUwSUdObGNuUnBabWxqWVhSbE1UQXdMZ1lEVlFRTERDZGpZV3hsWWtCallXeGxZaTFzWVhCMGIzQXRiR2x1CmRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNrd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUIKQVFEYVhWcVp4MXNXLzREdTlBdTJnSjNsT2JubVlzQzhpV2JLSTJ4UWVrSWljc0NHMm5qYkVPejMza2FuL3VsaQpNbXh2TEF2UUcwTjhDU3djSzlhakFydHJvbE96S2NZajFmUzNmK25yRWZpa3NiemNHNFFiMDc0MUtlcTJLZFJkCllwR0E0TSs2Y0c0QTQvVnhDOWxnNU5UMDJXd2tYUlRUVWVjUWJIdTV5blEzcjR1MWk3R3RCb3RDODRQVGlFdXUKUVpEbFcxMUx2UjlTUW5ZenVZUmM0aG9QR1pKN1dBNE1jdlIxY3MyUHZWU3FOWjc4aGZDRGgyQ21mYUxGQVhxVgpSUk5sbUVlclMvWjQ1N3JnbHhrOTlKSlBrd1FTWm43KzBrcHJKMm9ubmRtUFpxN2JnT1FOdG13WFQ0cTRjSEFCCjFyTWRGTEdXODJqMGFnbmhMdXl4MEtuTkFnTUJBQUdqZGpCME1BNEdBMVVkRHdFQi93UUVBd0lGb0RBVEJnTlYKSFNVRUREQUtCZ2dyQmdFRkJRY0RBVEFNQmdOVkhSTUJBZjhFQWpBQU1COEdBMVVkSXdRWU1CYUFGSVdHV0ZpQQpSOHBPbVZVNTZkNVViZjdjZ1Q5Tk1CNEdBMVVkRVFRWE1CV0NFM0J2YldWeWFYVnRMV1JoZEdGaWNtOXJaWEl3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFGSkovQzVRT3hBRnFxMXRjZDN5REdPbzB6V29NejBzZDZEZnQ1cVAKTXRaOWx5WTU4dnpZN1pIWEVwVzU2Mm9hYmpuRmRTMVYyZTFtTFM5ZGRFQkZIdzRxQm1hZmZHcFVQdzZkTndXawpCSmJVRUhXVFhvVmRaejhYMUExei9mUkNEY0NxRW50amZkUXo3NFkrZFZZcDhzQ215UmVIdy8rQ2tCMmtNblRZCmticzJBMUxpYnZ4ZUlrOVRmN0k0THZFbjNSRXZ1bjZjcSs4R1gvN1QxTm1KUS9sSkZjS0hrSXpvZFVBUTR4TUcKL29xUXFFalhHalM1cTRKMGNjVExDMlQ1Y0VsSDlHdnFwV3Q1L3RJSi96aTlFajl2UVROZVJ4Nllsc2RGNXA4OQpmbm9EWkZUb3BteXF3MGdySk5EN25mNU93R3BTNzlwN1NFcmUvWi9QTmlTSmdWNlU0L21IY25PVXk2UVk0SjBjClhqd2laK1J6cjlRam5DS2xPMEprNDI1U0J2Q0M2L0ZDcHViQ1JpSjViR0Rkd1k3bE9ydmNRV3R2NDJ1WUp4UEEKeG0yM0Q2WWowdjQrVWhNMWwvVURGdjlFcGgwSXdGM1I5Sk5WOGYvZUszM0VDc2QyQW5PWDkrTVljMG0weHRJMwpVMUYwM3czTlR5c2hKYXQ0ZHp4QytBbTBaZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRGFYVnFaeDFzVy80RHUKOUF1MmdKM2xPYm5tWXNDOGlXYktJMnhRZWtJaWNzQ0cybmpiRU96MzNrYW4vdWxpTW14dkxBdlFHME44Q1N3YwpLOWFqQXJ0cm9sT3pLY1lqMWZTM2YrbnJFZmlrc2J6Y0c0UWIwNzQxS2VxMktkUmRZcEdBNE0rNmNHNEE0L1Z4CkM5bGc1TlQwMld3a1hSVFRVZWNRYkh1NXluUTNyNHUxaTdHdEJvdEM4NFBUaUV1dVFaRGxXMTFMdlI5U1FuWXoKdVlSYzRob1BHWko3V0E0TWN2UjFjczJQdlZTcU5aNzhoZkNEaDJDbWZhTEZBWHFWUlJObG1FZXJTL1o0NTdyZwpseGs5OUpKUGt3UVNabjcrMGtwckoyb25uZG1QWnE3YmdPUU50bXdYVDRxNGNIQUIxck1kRkxHVzgyajBhZ25oCkx1eXgwS25OQWdNQkFBRUNnZ0VCQUxmYjlLWVllalVDQ3dDbmtaa244RXFLY1dGdmN0TU1hZlQzSUlNZWZjTGQKbGdlMXVoN1J3SVR2cmRSVHlSUTZXcHk4bkhHc0V3VkQvOVQwZ0hPZW9Fbi9VclEvRHlzclFqS2pURVQyUzhINApPWW1telhSVXRBbTFjbi9ROXBOdXBBNjh1NHRDa1F6RTQrczhTOXJPc2hRN1lWZTQzQWtXSkJUckVyNGJuZ0VuCmlyU1ZOdDhPQ0E0emxtR0VEYzBEOE1mMTNCbUpOditmNmtpa1p4d1pTUzNKSUwwcWUvcENvYWE5KzMyaVJGRngKVGo2cmlrTHkyYktxMUgwYW02dXViR2UxUEx0ZWpXS3AvcmRQejIza0RVTEJUSS9IZXU5dVlxY1pGZUNlQ2dIeApnWkxvd0d1emJVd0xUQUZqZ3FqSStMdlBuazZyR0dxaURpTkV5SEdqUWFFQ2dZRUE2dnNwQ212SE9iT3JtaU1MCkhpNWFNSTlDQW80c2RXWU5sd2tZdVQyK2tyT0VpM3d6RHNLaVpuUmxTWE1SV2YzVHR1OW9ETkVuNVk2dE9NdHUKVzg3OTUwNEtONmU3Ulk3aXZCQThPSlQxUFd3M3BySEJ4QkNmeEQxd2J3V2MyUGIyQlZnQzd1U01KWHR4UVNaOApLRStmaEwyMHVmL1NMZ0Z3ZTEwVURMbUVqVWtDZ1lFQTdlV3hnbnBpbUZ2UFRZYTBvNHFGQ2RueThPSmQ5V2VMCitZQkkxOWw0Q29yU2pqNkJ1ZmdzcWRDYUZKbDEwTnMwSzFTRldIMEM4Q3JkZkpvV3I4RllsVUphbXpWQUkvTGMKTnQrZG8xTmRCV2Q5ZnBTWk94S0dxOFMxNTZCRVV4cm9lbmdzNFU2bXRlWDMvMzhKbU9va3pYUlVMLzVOQS9BVgpGbFdEV1I0NWpHVUNnWUVBdVFaVFpwVS9KanhIWHZOQkNkN052WGJRTDFycnZ2VkF0akMvZXYwWFZrUnNsYmlTCks0a3VmeVlmcXhva0M0eEdiTUZPV09sSVZRVm5lRlVXN011c09ibkZhZFYxTEd3Nk5JTkVKL21Dc1g3SXcwVXMKb2NuRlE2eHRINzUzcG8rdllMM1FjWE1jdDFiZjhzRHUrbjdYeUpubitKMXJUalpyNzNkM0ZoM3VOYUVDZ1lBQwpCM2FISldJWDZKaVJFYmdTdVFpL2Q4Tnh1SXlTbmtwOFcxTER2SkFnanVTUzRBZWRQWGVzRHlKeFVJbkh1VWJ0Cm82L3JmRTE5SFBCOHNwZkU4Z0krYTYxNEszRWJuV094ZUNEUkZ0SHhUTnV1SzdoTzM2NlVZbjRtdHFBK2tUa3cKYWp2L3cvcjZERGNjVDhvV0t0RWJpNFY0VDFQNjE1NW1iVjVNY1V5T1hRS0JnUURJRFRnYnJGRE9sZ0ozOTVRNApwRWdsdjNUNDA0Wll0N05oUVFNd3FMaC9aYmZMVVhqUk8wZmI2VDZmOEV4L3Q4V1NaQnlVQ1NKcGloRzRXWTFPCnJlYWFhbzIwanVSMVdudU5mWDNiTVhNSEZ0ZEpDOFd1V3ZCK2MvcGttSDZRVFRjMXIySlJ3cEYzWXF0Mi9vVzcKRmFRbUdNNG8vVVBlSWhxaUhqMzZleDBwOGc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg== + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -343,6 +346,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TXpFeE1UQXkKTURBNE5EUmFGdzB6TXpFeE1EY3lNREE0TkRSYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBcHFWekozUWY5VnFrdWpGYmMwTUJEcVdELzhnamZkN21XMjlmUnRNSVAzemRKbGl5ZXZSajczQUwKaWZYNVpadW5UN24vajUyWnppRmliNGo4dWM0UjZWd0FFN2xMcERlc2ZzTDRBZ3ZHNnVqSmFKTGgrcTZmUEZWbQo4VXdJcjMvSGpaQUdQdmJ3Y2VBTzAwbXRmcW44YUsxS2VLeGZFazlVaFRVV2hzcXVieTg4RWNKVmh4a1RzQUhvCmtLUWtFYWY5TkxhemhaMFAwdTlKLzE0VkdoTU44UVVIdklMVmpja0NEaElqMzhJVUs3VXRaSGtNNzJHbUtyajIKU0M0MElEZE50NHpiMUFUTFZleU9MZHdLandFRmdLV3prdkkvN1VqOXBBMjYvZVlHUFE3b3hSRitJRXhWSWhEcgpFSnZIcldRMHMwRUtOUGRwVS9JaHF0azByWWtqODFwZXFNOFRtSTZ2cXJacUFFUHphMXRZazZXUXN6RG9ucFBXCnVLbGZyOUdZWWY1TXU5YTJ5MjZBZ2x1RG5pQWNuZldqUlhtcjFydlJIQnB6c0xTRDNTVG5QRTV0NkhKaWVQN3IKdjZrL2ZsWFE5U0V3MFUzbEkvblpLS3dpTGZXQzJPNUJwS3dNejE5Y1o4L2tMU0pXSGc0bGtEYjJVbzFKS25pVwora01FSTluTgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -373,6 +377,7 @@ services: ports: - 80:80/tcp - 443:443/tcp + - 443:443/udp - 5443:5443/tcp - 9901:9901/tcp postgres: diff --git a/integration/clusters/multi-stateless/compose.yml b/integration/clusters/multi-stateless/compose.yml index e1466aaf3..31cf94ca2 100644 --- a/integration/clusters/multi-stateless/compose.yml +++ b/integration/clusters/multi-stateless/compose.yml @@ -162,6 +162,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TXpFeE1UQXkKTURBNE5EUmFGdzB6TXpFeE1EY3lNREE0TkRSYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBcHFWekozUWY5VnFrdWpGYmMwTUJEcVdELzhnamZkN21XMjlmUnRNSVAzemRKbGl5ZXZSajczQUwKaWZYNVpadW5UN24vajUyWnppRmliNGo4dWM0UjZWd0FFN2xMcERlc2ZzTDRBZ3ZHNnVqSmFKTGgrcTZmUEZWbQo4VXdJcjMvSGpaQUdQdmJ3Y2VBTzAwbXRmcW44YUsxS2VLeGZFazlVaFRVV2hzcXVieTg4RWNKVmh4a1RzQUhvCmtLUWtFYWY5TkxhemhaMFAwdTlKLzE0VkdoTU44UVVIdklMVmpja0NEaElqMzhJVUs3VXRaSGtNNzJHbUtyajIKU0M0MElEZE50NHpiMUFUTFZleU9MZHdLandFRmdLV3prdkkvN1VqOXBBMjYvZVlHUFE3b3hSRitJRXhWSWhEcgpFSnZIcldRMHMwRUtOUGRwVS9JaHF0azByWWtqODFwZXFNOFRtSTZ2cXJacUFFUHphMXRZazZXUXN6RG9ucFBXCnVLbGZyOUdZWWY1TXU5YTJ5MjZBZ2x1RG5pQWNuZldqUlhtcjFydlJIQnB6c0xTRDNTVG5QRTV0NkhKaWVQN3IKdjZrL2ZsWFE5U0V3MFUzbEkvblpLS3dpTGZXQzJPNUJwS3dNejE5Y1o4L2tMU0pXSGc0bGtEYjJVbzFKS25pVwora01FSTluTgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -224,6 +225,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYekNDQXNlZ0F3SUJBZ0lSQU5NalQzSmp2cEdtbjRUTzVPWUFyazh3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB4T1RBMk1ERXcKTURBd01EQmFGdzB6TVRFeE1UVXhPVEV4TlRWYU1Gc3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXdNQzRHQTFVRUN3d25ZMkZzWldKQVkyRnNaV0l0YkdGd2RHOXdMV3hwCmJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0MKQVFFQTlqUkR2Mkl5V1FaV3V1STkxdWdlbWF3R0RxK1BERUpUaVNDZ3ZhWWkwSEc0SEowNndwVjk4cDNTYWZVbgpwWU12ajlmY3lqbkJaOHlvcGU5VUQrakFSMVhEYmI1YlUvVU1iV3JkU29LWVM4a0dzYllDdDAzMHk4QWRaejY5CmI3Q2t2Y25IQUpPcDV4VXRYU1ExaEo3ZjQzZ29aaEdSbHJKU2cxSkpWKzJTTlUxMHNnM1V3YjNieGdBL1hrRk0KaUIzWnF2STRJNTh3RzBqakxVSEFmQ043eTFEQWFiVlNJQll6b1B5cjJxUUEvR0lHMWJsbzljWWdrRFlkb1d3UgpwZ21maHBLQ2NrNUxWZWdZejNBcSs4NkpPVGxHbWc5WlIxaUdNSEpNYklsUGJNdEU2MXovQjhtWUc5aUptUHJTCmYzNHVZVlJ4VDZTMkZWTkU1cDZVbHZXNHd3SURBUUFCbzNVd2N6QU9CZ05WSFE4QkFmOEVCQU1DQmFBd0V3WUQKVlIwbEJBd3dDZ1lJS3dZQkJRVUhBd0V3REFZRFZSMFRBUUgvQkFJd0FEQWZCZ05WSFNNRUdEQVdnQlNGaGxoWQpnRWZLVHBsVk9lbmVWRzMrM0lFL1RUQWRCZ05WSFJFRUZqQVVnaEp3YjIxbGNtbDFiUzFoZFhSb2IzSnBlbVV3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFESHNQT1p6WHh5eWMrZ1EwYkxSbG5WejNZMDV6cTNmV2gxeWp0U04KMmtQTFJ1aU02ZkZWUGlDV1VuUzk0cnNDeDV6Sk0xQnRNR3VNcUNMWGxmZERWYlZNcDM2d0NHMms5TWt3aE01dgpuTFRXTkkzclFZSUJnU0xvdFNTeTZYdUV5U1pJQlhUZ2RHODFYcG5pVjBUc1dKMjJEdGxlMlhOZUo1cHNnMS93CjdMbTZLdElkL3FEYUF5T1pLR1Y2Z21GL0YyNTB4RDkxYys3Mk9DUkFrK0x2WXhydFBVY3lwV0lNV2lMUEtlRkMKQnB6Q3BsNDB3c0o5YzVFMTdJcWNjMVVzanRwUnVXSlFRb0FBb3NhQ2RVUThXN04yMXdyNTcyTjFKcmNVOElBeAprN3U3T0Rrc24vK0NaODQ2N3dYclB5OXZGL3F2QkxtYWVFMWswd3VnTldlSnZNT0JLcGRKNG5iS0VCbU5xcmpMCmRRM21aTW90ZWNlelRDY29WYmRhdC9NVHNKR3hPSG42ZExoeHlKQzQ5K0FxbjJ2OGJ1ajZEbk56M3d3VHZxdXUKOGJEOW83SzlHdmRaaEtBNUxISmpFOXNkdmxzZVpNUnZqUGVnUDRWeWpaWmlBejBFUW04d292MHYvK2J5d2RUZgp5QjQ4eVJjSVdzdzZyU3BNdHR5ZU1neGJiQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRDJORU8vWWpKWkJsYTYKNGozVzZCNlpyQVlPcjQ4TVFsT0pJS0M5cGlMUWNiZ2NuVHJDbFgzeW5kSnA5U2VsZ3krUDE5ektPY0ZuektpbAo3MVFQNk1CSFZjTnR2bHRUOVF4dGF0MUtncGhMeVFheHRnSzNUZlRMd0IxblByMXZzS1M5eWNjQWs2bm5GUzFkCkpEV0VudC9qZUNobUVaR1dzbEtEVWtsWDdaSTFUWFN5RGRUQnZkdkdBRDllUVV5SUhkbXE4amdqbnpBYlNPTXQKUWNCOEkzdkxVTUJwdFZJZ0ZqT2cvS3ZhcEFEOFlnYlZ1V2oxeGlDUU5oMmhiQkdtQ1orR2tvSnlUa3RWNkJqUApjQ3I3em9rNU9VYWFEMWxIV0lZd2NreHNpVTlzeTBUclhQOEh5WmdiMkltWSt0Si9maTVoVkhGUHBMWVZVMFRtCm5wU1c5YmpEQWdNQkFBRUNnZ0VBWk9ML3krSEtMYzY2SlhSbDBMVm5MS05nREE2R2VJaUpRSXk5TVZsdHRLcVUKdml6emtoNjk4SGdaYTgvSWdqc2lkemdHUWx1dVZBRjZMaHpHL29ucW1KQ2ROMUFramwvQXl1bGFhcGpmSFZidgp4a3lHQ21VSVphR0Fxdmo3bVZQeC9jODhHNUZtc3NscndGb0JFQ29tSmtqcmpQK3YvK2RzaG1GbXFvQXdSR1IrCkp0ZW1xTlo3QWNwajhzV09ZeWQxZDlLbWtxS1RlaFZTSmpUaExlNjhvWXpxV3JxZWtVY2psbkI0a2ppbEU5VzAKYm15N0JqQlJqTlRGTWtSRUQ0eWtWUXlscGVZd1JXQkhYdzUxMlVVQVYxQnEyb0k0TGJoc0xYR1U2YXZSKy9XagpoemhKQ3ZpUTlxSUJ1OVhsRWtkcjdrQjVOQVVOd25pZUJhRE1VZmpxd1FLQmdRRDVrV3RtWmk0T3U4T1Z3QjF5CkZraE93T3RyM1oyTjBTOVZJbjAwc3ROTVlUVGNxT0J1Ynl2Y3RPdzA4UDJDVTVRcUtxRzY5QUtPRlBubm0vOEkKR1Vvc2k1eEh3S2Q3MTdoN1VhK0VTdG9GNkRDemZhR1BadXkrN0JnVFloeS96cjA5L0VJYkRPZHBOZEpmbis2ago1K0lDSjVqdEJRMUVUZXRJRTVQbWZVMXRvUUtCZ1FEOGpLYXYrZGRIUmwvMGZGN1plWit5TnZheGlCWGhXZkh2ClVXZVVaOXlKK04vbXhOckpqYTkzam5aZzFCTnIrdlYxUzNXTEwwN01wZm9HZ0IzWW82K3hXSlVhamRreTJMdE0KeGwxU1VmSERBRmQ1bWpGWVNTM3IvUGJxNDdJQ2hFK0ZmYndWNk9PNlpkYW1US3Y0TjFncUpvZWl0d2hvcEVGeApPYjBybm9DajR3S0JnQVB6NTYyUmhMMDdBQmdKR081THFBMkhSTE5LcVVvcnZBSXZMNmg4QkppN0dXazlTNEMwClJnK1MxZ0xvcGp0QU02S1BWa3h0dlBTZkpHNGtyRjltZHlSSmVjb3hKUFh6THJyVlNtQmdCeC84MDNpa1RzREUKc284N2tnNE1pY1FIOUR0ZEdYYndOMklWTmlYSmxMQUg1aTUramcrQyt2alAreUFESDE2Uk1YN0JBb0dCQU80WQpSRVgveTMvOHp4WVY4dTRoQmZma2JhdHQyd0w3NDJWMlg2ckFTR0VqYXlPTFQ2RlpuS1dnZitaRnlwR3dwWjBSCnFSMnhXaDhDRTdmeExUSkRENjZwRHRsY0Jmc3o3VkV5YW1UanNJUGhYSmJlVWNza0l0RXJTamFrdC93N2RTVXIKSnhtWTJLbXd2UzlIZFpHcnk4amhUbHFQS0xST3R3dmlIWUIwREY5MUFvR0FkeDJ3TGllTzVUcThBdkZhOXVydApCaVhwVFlMQ2JaWVBGZkVYTmZCYkt2NmFiWExBd2ZyRkQ1dkdrM0pFY2JqUTlnbWN6YW53QTFkUVhmN1NJLzRwCkZrUWhaSjhhdGJEaWl3SEVaeHpUeSt3cGx6OVlSN2tNQ0hkMndacXBsRCt1eXVxRWtZVVE5R09YZ2IzSUthVk4KVmd4QnJWSzlCNjdXM3FQZVRYMWxJdFU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -285,6 +287,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYekNDQXNlZ0F3SUJBZ0lRWDBCTFVEUmE0cHI3TFlueGRkYzRKREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEU1TURZd01UQXcKTURBd01Gb1hEVE14TVRFeE5URTVNVEl5TjFvd1d6RW5NQ1VHQTFVRUNoTWViV3RqWlhKMElHUmxkbVZzYjNCdApaVzUwSUdObGNuUnBabWxqWVhSbE1UQXdMZ1lEVlFRTERDZGpZV3hsWWtCallXeGxZaTFzWVhCMGIzQXRiR2x1CmRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNrd2dnRWlNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0SUJEd0F3Z2dFS0FvSUIKQVFEYVhWcVp4MXNXLzREdTlBdTJnSjNsT2JubVlzQzhpV2JLSTJ4UWVrSWljc0NHMm5qYkVPejMza2FuL3VsaQpNbXh2TEF2UUcwTjhDU3djSzlhakFydHJvbE96S2NZajFmUzNmK25yRWZpa3NiemNHNFFiMDc0MUtlcTJLZFJkCllwR0E0TSs2Y0c0QTQvVnhDOWxnNU5UMDJXd2tYUlRUVWVjUWJIdTV5blEzcjR1MWk3R3RCb3RDODRQVGlFdXUKUVpEbFcxMUx2UjlTUW5ZenVZUmM0aG9QR1pKN1dBNE1jdlIxY3MyUHZWU3FOWjc4aGZDRGgyQ21mYUxGQVhxVgpSUk5sbUVlclMvWjQ1N3JnbHhrOTlKSlBrd1FTWm43KzBrcHJKMm9ubmRtUFpxN2JnT1FOdG13WFQ0cTRjSEFCCjFyTWRGTEdXODJqMGFnbmhMdXl4MEtuTkFnTUJBQUdqZGpCME1BNEdBMVVkRHdFQi93UUVBd0lGb0RBVEJnTlYKSFNVRUREQUtCZ2dyQmdFRkJRY0RBVEFNQmdOVkhSTUJBZjhFQWpBQU1COEdBMVVkSXdRWU1CYUFGSVdHV0ZpQQpSOHBPbVZVNTZkNVViZjdjZ1Q5Tk1CNEdBMVVkRVFRWE1CV0NFM0J2YldWeWFYVnRMV1JoZEdGaWNtOXJaWEl3CkRRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFGSkovQzVRT3hBRnFxMXRjZDN5REdPbzB6V29NejBzZDZEZnQ1cVAKTXRaOWx5WTU4dnpZN1pIWEVwVzU2Mm9hYmpuRmRTMVYyZTFtTFM5ZGRFQkZIdzRxQm1hZmZHcFVQdzZkTndXawpCSmJVRUhXVFhvVmRaejhYMUExei9mUkNEY0NxRW50amZkUXo3NFkrZFZZcDhzQ215UmVIdy8rQ2tCMmtNblRZCmticzJBMUxpYnZ4ZUlrOVRmN0k0THZFbjNSRXZ1bjZjcSs4R1gvN1QxTm1KUS9sSkZjS0hrSXpvZFVBUTR4TUcKL29xUXFFalhHalM1cTRKMGNjVExDMlQ1Y0VsSDlHdnFwV3Q1L3RJSi96aTlFajl2UVROZVJ4Nllsc2RGNXA4OQpmbm9EWkZUb3BteXF3MGdySk5EN25mNU93R3BTNzlwN1NFcmUvWi9QTmlTSmdWNlU0L21IY25PVXk2UVk0SjBjClhqd2laK1J6cjlRam5DS2xPMEprNDI1U0J2Q0M2L0ZDcHViQ1JpSjViR0Rkd1k3bE9ydmNRV3R2NDJ1WUp4UEEKeG0yM0Q2WWowdjQrVWhNMWwvVURGdjlFcGgwSXdGM1I5Sk5WOGYvZUszM0VDc2QyQW5PWDkrTVljMG0weHRJMwpVMUYwM3czTlR5c2hKYXQ0ZHp4QytBbTBaZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRRGFYVnFaeDFzVy80RHUKOUF1MmdKM2xPYm5tWXNDOGlXYktJMnhRZWtJaWNzQ0cybmpiRU96MzNrYW4vdWxpTW14dkxBdlFHME44Q1N3YwpLOWFqQXJ0cm9sT3pLY1lqMWZTM2YrbnJFZmlrc2J6Y0c0UWIwNzQxS2VxMktkUmRZcEdBNE0rNmNHNEE0L1Z4CkM5bGc1TlQwMld3a1hSVFRVZWNRYkh1NXluUTNyNHUxaTdHdEJvdEM4NFBUaUV1dVFaRGxXMTFMdlI5U1FuWXoKdVlSYzRob1BHWko3V0E0TWN2UjFjczJQdlZTcU5aNzhoZkNEaDJDbWZhTEZBWHFWUlJObG1FZXJTL1o0NTdyZwpseGs5OUpKUGt3UVNabjcrMGtwckoyb25uZG1QWnE3YmdPUU50bXdYVDRxNGNIQUIxck1kRkxHVzgyajBhZ25oCkx1eXgwS25OQWdNQkFBRUNnZ0VCQUxmYjlLWVllalVDQ3dDbmtaa244RXFLY1dGdmN0TU1hZlQzSUlNZWZjTGQKbGdlMXVoN1J3SVR2cmRSVHlSUTZXcHk4bkhHc0V3VkQvOVQwZ0hPZW9Fbi9VclEvRHlzclFqS2pURVQyUzhINApPWW1telhSVXRBbTFjbi9ROXBOdXBBNjh1NHRDa1F6RTQrczhTOXJPc2hRN1lWZTQzQWtXSkJUckVyNGJuZ0VuCmlyU1ZOdDhPQ0E0emxtR0VEYzBEOE1mMTNCbUpOditmNmtpa1p4d1pTUzNKSUwwcWUvcENvYWE5KzMyaVJGRngKVGo2cmlrTHkyYktxMUgwYW02dXViR2UxUEx0ZWpXS3AvcmRQejIza0RVTEJUSS9IZXU5dVlxY1pGZUNlQ2dIeApnWkxvd0d1emJVd0xUQUZqZ3FqSStMdlBuazZyR0dxaURpTkV5SEdqUWFFQ2dZRUE2dnNwQ212SE9iT3JtaU1MCkhpNWFNSTlDQW80c2RXWU5sd2tZdVQyK2tyT0VpM3d6RHNLaVpuUmxTWE1SV2YzVHR1OW9ETkVuNVk2dE9NdHUKVzg3OTUwNEtONmU3Ulk3aXZCQThPSlQxUFd3M3BySEJ4QkNmeEQxd2J3V2MyUGIyQlZnQzd1U01KWHR4UVNaOApLRStmaEwyMHVmL1NMZ0Z3ZTEwVURMbUVqVWtDZ1lFQTdlV3hnbnBpbUZ2UFRZYTBvNHFGQ2RueThPSmQ5V2VMCitZQkkxOWw0Q29yU2pqNkJ1ZmdzcWRDYUZKbDEwTnMwSzFTRldIMEM4Q3JkZkpvV3I4RllsVUphbXpWQUkvTGMKTnQrZG8xTmRCV2Q5ZnBTWk94S0dxOFMxNTZCRVV4cm9lbmdzNFU2bXRlWDMvMzhKbU9va3pYUlVMLzVOQS9BVgpGbFdEV1I0NWpHVUNnWUVBdVFaVFpwVS9KanhIWHZOQkNkN052WGJRTDFycnZ2VkF0akMvZXYwWFZrUnNsYmlTCks0a3VmeVlmcXhva0M0eEdiTUZPV09sSVZRVm5lRlVXN011c09ibkZhZFYxTEd3Nk5JTkVKL21Dc1g3SXcwVXMKb2NuRlE2eHRINzUzcG8rdllMM1FjWE1jdDFiZjhzRHUrbjdYeUpubitKMXJUalpyNzNkM0ZoM3VOYUVDZ1lBQwpCM2FISldJWDZKaVJFYmdTdVFpL2Q4Tnh1SXlTbmtwOFcxTER2SkFnanVTUzRBZWRQWGVzRHlKeFVJbkh1VWJ0Cm82L3JmRTE5SFBCOHNwZkU4Z0krYTYxNEszRWJuV094ZUNEUkZ0SHhUTnV1SzdoTzM2NlVZbjRtdHFBK2tUa3cKYWp2L3cvcjZERGNjVDhvV0t0RWJpNFY0VDFQNjE1NW1iVjVNY1V5T1hRS0JnUURJRFRnYnJGRE9sZ0ozOTVRNApwRWdsdjNUNDA0Wll0N05oUVFNd3FMaC9aYmZMVVhqUk8wZmI2VDZmOEV4L3Q4V1NaQnlVQ1NKcGloRzRXWTFPCnJlYWFhbzIwanVSMVdudU5mWDNiTVhNSEZ0ZEpDOFd1V3ZCK2MvcGttSDZRVFRjMXIySlJ3cEYzWXF0Mi9vVzcKRmFRbUdNNG8vVVBlSWhxaUhqMzZleDBwOGc9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg== + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -346,6 +349,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TXpFeE1UQXkKTURBNE5EUmFGdzB6TXpFeE1EY3lNREE0TkRSYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBcHFWekozUWY5VnFrdWpGYmMwTUJEcVdELzhnamZkN21XMjlmUnRNSVAzemRKbGl5ZXZSajczQUwKaWZYNVpadW5UN24vajUyWnppRmliNGo4dWM0UjZWd0FFN2xMcERlc2ZzTDRBZ3ZHNnVqSmFKTGgrcTZmUEZWbQo4VXdJcjMvSGpaQUdQdmJ3Y2VBTzAwbXRmcW44YUsxS2VLeGZFazlVaFRVV2hzcXVieTg4RWNKVmh4a1RzQUhvCmtLUWtFYWY5TkxhemhaMFAwdTlKLzE0VkdoTU44UVVIdklMVmpja0NEaElqMzhJVUs3VXRaSGtNNzJHbUtyajIKU0M0MElEZE50NHpiMUFUTFZleU9MZHdLandFRmdLV3prdkkvN1VqOXBBMjYvZVlHUFE3b3hSRitJRXhWSWhEcgpFSnZIcldRMHMwRUtOUGRwVS9JaHF0azByWWtqODFwZXFNOFRtSTZ2cXJacUFFUHphMXRZazZXUXN6RG9ucFBXCnVLbGZyOUdZWWY1TXU5YTJ5MjZBZ2x1RG5pQWNuZldqUlhtcjFydlJIQnB6c0xTRDNTVG5QRTV0NkhKaWVQN3IKdjZrL2ZsWFE5U0V3MFUzbEkvblpLS3dpTGZXQzJPNUJwS3dNejE5Y1o4L2tMU0pXSGc0bGtEYjJVbzFKS25pVwora01FSTluTgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_SERVICE_URL: https://pomerium-databroker:5443 DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test @@ -377,6 +381,7 @@ services: ports: - 80:80/tcp - 443:443/tcp + - 443:443/udp - 5443:5443/tcp - 9901:9901/tcp postgres: diff --git a/integration/clusters/single-stateful/compose.yml b/integration/clusters/single-stateful/compose.yml index 19256e468..d36ff0379 100644 --- a/integration/clusters/single-stateful/compose.yml +++ b/integration/clusters/single-stateful/compose.yml @@ -160,6 +160,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TXpFeE1UQXkKTURBNE5EUmFGdzB6TXpFeE1EY3lNREE0TkRSYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBcHFWekozUWY5VnFrdWpGYmMwTUJEcVdELzhnamZkN21XMjlmUnRNSVAzemRKbGl5ZXZSajczQUwKaWZYNVpadW5UN24vajUyWnppRmliNGo4dWM0UjZWd0FFN2xMcERlc2ZzTDRBZ3ZHNnVqSmFKTGgrcTZmUEZWbQo4VXdJcjMvSGpaQUdQdmJ3Y2VBTzAwbXRmcW44YUsxS2VLeGZFazlVaFRVV2hzcXVieTg4RWNKVmh4a1RzQUhvCmtLUWtFYWY5TkxhemhaMFAwdTlKLzE0VkdoTU44UVVIdklMVmpja0NEaElqMzhJVUs3VXRaSGtNNzJHbUtyajIKU0M0MElEZE50NHpiMUFUTFZleU9MZHdLandFRmdLV3prdkkvN1VqOXBBMjYvZVlHUFE3b3hSRitJRXhWSWhEcgpFSnZIcldRMHMwRUtOUGRwVS9JaHF0azByWWtqODFwZXFNOFRtSTZ2cXJacUFFUHphMXRZazZXUXN6RG9ucFBXCnVLbGZyOUdZWWY1TXU5YTJ5MjZBZ2x1RG5pQWNuZldqUlhtcjFydlJIQnB6c0xTRDNTVG5QRTV0NkhKaWVQN3IKdjZrL2ZsWFE5U0V3MFUzbEkvblpLS3dpTGZXQzJPNUJwS3dNejE5Y1o4L2tMU0pXSGc0bGtEYjJVbzFKS25pVwora01FSTluTgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres @@ -186,6 +187,7 @@ services: ports: - 80:80/tcp - 443:443/tcp + - 443:443/udp - 9901:9901/tcp postgres: environment: diff --git a/integration/clusters/single-stateless/compose.yml b/integration/clusters/single-stateless/compose.yml index 5aa912a27..c72b46f50 100644 --- a/integration/clusters/single-stateless/compose.yml +++ b/integration/clusters/single-stateless/compose.yml @@ -160,6 +160,7 @@ services: CERTIFICATE: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVakNDQXJxZ0F3SUJBZ0lSQUtOYUVxQ21tWmZobWNZZ1p5MDFXQ3N3RFFZSktvWklodmNOQVFFTEJRQXcKZ1lNeEhqQWNCZ05WQkFvVEZXMXJZMlZ5ZENCa1pYWmxiRzl3YldWdWRDQkRRVEVzTUNvR0ExVUVDd3dqWTJGcwpaV0pBWTJGc1pXSXRjR010YkdsdWRYZ2dLRU5oYkdWaUlFUnZlSE5sZVNreE16QXhCZ05WQkFNTUttMXJZMlZ5CmRDQmpZV3hsWWtCallXeGxZaTF3WXkxc2FXNTFlQ0FvUTJGc1pXSWdSRzk0YzJWNUtUQWVGdzB5TXpFeE1UQXkKTURBNE5EUmFGdzB6TXpFeE1EY3lNREE0TkRSYU1GY3hKekFsQmdOVkJBb1RIbTFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCalpYSjBhV1pwWTJGMFpURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnCktFTmhiR1ZpSUVSdmVITmxlU2t3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQzgKSExCQUl6WGtQZWVnbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRAp5VmhEVDBRbEkvTy9FS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2Cld5bCtGb1BEVi92c1ozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXQKak9ic2FRZ0o1TEx4Q1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcQpiWlVERytaaW9BclBtcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkCjlyNnJPQnhweHduVER2SGtCbjZ2QWdNQkFBR2piREJxTUE0R0ExVWREd0VCL3dRRUF3SUZvREFUQmdOVkhTVUUKRERBS0JnZ3JCZ0VGQlFjREFUQWZCZ05WSFNNRUdEQVdnQlNGaGxoWWdFZktUcGxWT2VuZVZHMyszSUUvVFRBaQpCZ05WSFJFRUd6QVpnaGNxTG14dlkyRnNhRzl6ZEM1d2IyMWxjbWwxYlM1cGJ6QU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBWUVBcHFWekozUWY5VnFrdWpGYmMwTUJEcVdELzhnamZkN21XMjlmUnRNSVAzemRKbGl5ZXZSajczQUwKaWZYNVpadW5UN24vajUyWnppRmliNGo4dWM0UjZWd0FFN2xMcERlc2ZzTDRBZ3ZHNnVqSmFKTGgrcTZmUEZWbQo4VXdJcjMvSGpaQUdQdmJ3Y2VBTzAwbXRmcW44YUsxS2VLeGZFazlVaFRVV2hzcXVieTg4RWNKVmh4a1RzQUhvCmtLUWtFYWY5TkxhemhaMFAwdTlKLzE0VkdoTU44UVVIdklMVmpja0NEaElqMzhJVUs3VXRaSGtNNzJHbUtyajIKU0M0MElEZE50NHpiMUFUTFZleU9MZHdLandFRmdLV3prdkkvN1VqOXBBMjYvZVlHUFE3b3hSRitJRXhWSWhEcgpFSnZIcldRMHMwRUtOUGRwVS9JaHF0azByWWtqODFwZXFNOFRtSTZ2cXJacUFFUHphMXRZazZXUXN6RG9ucFBXCnVLbGZyOUdZWWY1TXU5YTJ5MjZBZ2x1RG5pQWNuZldqUlhtcjFydlJIQnB6c0xTRDNTVG5QRTV0NkhKaWVQN3IKdjZrL2ZsWFE5U0V3MFUzbEkvblpLS3dpTGZXQzJPNUJwS3dNejE5Y1o4L2tMU0pXSGc0bGtEYjJVbzFKS25pVwora01FSTluTgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== CERTIFICATE_AUTHORITY: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUxekNDQXorZ0F3SUJBZ0lRWjEzOWNkL3BhUGRrUzJKeUF1N2tFREFOQmdrcWhraUc5dzBCQVFzRkFEQ0IKZ3pFZU1Cd0dBMVVFQ2hNVmJXdGpaWEowSUdSbGRtVnNiM0J0Wlc1MElFTkJNU3d3S2dZRFZRUUxEQ05qWVd4bApZa0JqWVd4bFlpMXdZeTFzYVc1MWVDQW9RMkZzWldJZ1JHOTRjMlY1S1RFek1ERUdBMVVFQXd3cWJXdGpaWEowCklHTmhiR1ZpUUdOaGJHVmlMWEJqTFd4cGJuVjRJQ2hEWVd4bFlpQkViM2h6WlhrcE1CNFhEVEl4TURneE1ERTMKTXpJd09Wb1hEVE14TURneE1ERTNNekl3T1Zvd2dZTXhIakFjQmdOVkJBb1RGVzFyWTJWeWRDQmtaWFpsYkc5dwpiV1Z1ZENCRFFURXNNQ29HQTFVRUN3d2pZMkZzWldKQVkyRnNaV0l0Y0dNdGJHbHVkWGdnS0VOaGJHVmlJRVJ2CmVITmxlU2t4TXpBeEJnTlZCQU1NS20xclkyVnlkQ0JqWVd4bFlrQmpZV3hsWWkxd1l5MXNhVzUxZUNBb1EyRnMKWldJZ1JHOTRjMlY1S1RDQ0FhSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnR1BBRENDQVlvQ2dnR0JBTmJLeU16NQpNVlc2WUtkamgxb0lOMU1uN1BFMnBINVNiSlNwV3hkQUdoZEJrQmtwQWE3T3hhcmpINUtWa0NUU2E3b25jbGE3CnFOdUpaUzZtQm1veEYrUitjUjNqeUdkVUFZbG96bDFqbGZxTElmQy8rZzdWN1ZtT0puOTh0akI0MmZhdHhMbDYKV1BBdzFKRE5zV3RRZmhLaGJjSHV0N1JzRjByTU9PSGN3eXdUUjdMT3lDbUllbDFwY21wVjRoYlZjVDZlVndvUApIWHlKU2E5Y3FhTVE1WHJkb2dhaTRJcVpaSUdMSGVMc1RWdXRPZ0pGWEVldmxYL1FUM3NXb21FY3R6aDM4SnM0CjlEaUFQRDZkNFk3L0NQTFlFZmsyOUpROU5aaHBnRHNpOWh1NUZISFpjWHdmMUlIbHcvQ0JWZ242aitqbXZLS3oKOTBNYTFvcXV2M1c2ZHR0aWQveENjTEd1MlMrOTZUenJ5a21veTVWYWNMdFZFUDQxWW1vVmxzOTFybG83b2xwZQpRV0Zibm1jbzczOVRJLzRoK0hvZG9scGVyUUVSUWw3dUNucEtWUFozV29rS3VSaDVwa3FrUXAvYXJRanR3Y1J0Ckc0M0NyRHBibCt1U2pNQ0F4aGE5NThlVFl2dG9qVE1udkx0c0dJRDFoR1hucWx3KzVLaktyZ1JIclFJREFRQUIKbzBVd1F6QU9CZ05WSFE4QkFmOEVCQU1DQWdRd0VnWURWUjBUQVFIL0JBZ3dCZ0VCL3dJQkFEQWRCZ05WSFE0RQpGZ1FVaFlaWVdJQkh5azZaVlRucDNsUnQvdHlCUDAwd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBMUYvYXByCmw2cE5UM01wL014aFVVZ282dXNFSkNyeUdRY0xSZmV4eVFYR04zaHVDbUlyUDU1VkZhOEVUUEF0anNyNlBNZTcKN3Z2RWo4ZUZ1Mkp0S292bFF3TmV3WVU5Y2pBTUNWYUZpTmJyUWEyMGh6aFdjMmpzNmR5aWxkRTYvRFB6YmVkcwpLREF4aEZOcDM1U2x3dFJ0S2sxU3p4SnhzcVN3amZ4SThmcCtSLzB3TzhnMGZXVGRNMmdDcFJ3WU1Od0pFTEVnCitkU2x2SkN3dXUrcnp4TGFsemFQRjFQTVRXNzJPRUxhbC9qNXNEKzJWeXRRNGsrSFVEYnl0MkRuUVQ3WVEzem8KcTAyeDJ1MnNtMVdXL28vdWg4cGpQeGtHUXFMMm1yeVpzNlZIOVZDVTNRa0tORHNzTmQ3MWxyM3dQb0U0WVJIZQpVdnpEMWVEZWVsekJVRk5JcERDamRDc0w1NXlJUHFVc3I2bG1qcEJQTDB2ZWEzM1FUTWJjc1N4dTB1bUdYRGJVCjY2anVVNFoxak9FMHdDbEl2YU82OTlKK0UyZ0JlMWpVTjZBdDZiOEJTb1pxQ3FYWW9ESEdlaTlSQlVkdmdxdG8Ka1Zzb0pmREkvVEZNZWtZZ3BMNVVWWW1MZGZncUxQUFJQOXBRQkxEeDNtc3plQXFudmZUSUNBemZYZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K CERTIFICATE_KEY: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQzhITEJBSXpYa1BlZWcKbGRVZlJLSzJqUXhTVlpENWcrcXNqQXpwbXJxL0F0bXdlSzFjR2NPdFo2ZU9MK3A4YnJQRHlWaERUMFFsSS9PLwpFS2dDT0ZGeFVEcW9SODJpWTA2U2FjQWpIbmk2K1BPOXRWUmJGVjB3MTRCREFKU3BCK1Z2V3lsK0ZvUERWL3ZzClozMUZ0WXcrRXdxa2JEeC9rYVQ5dXpmK0xKZGxrZjE0blFRajhFa3kvOGQzbVdKYmIvOXRqT2JzYVFnSjVMTHgKQ1lkSW1rcjc3WDJMTXVEdy8xdHBINjQyR0UyNU5yZ202UUhseUtTZllYbzM4djgzZWJFcWJaVURHK1ppb0FyUAptcW1rYXdVV3czZWtoajgwU0pnL1RLOVBSYU4vVnZjSTFQZ0FkN0xaenRVUmVTbVR5NWhkOXI2ck9CeHB4d25UCkR2SGtCbjZ2QWdNQkFBRUNnZ0VBQjI4aTBBWVVOU2IxSm5XRmJLenJ1VWN0dTN0Q05Yb3ZKZzZLM0JpUFZNa3EKRFQxWHJKSWdGNVJISE9scjNPc0xFNnU3WHoyY3RkTUw2UHNoaUtUdEl3dEdwaXZnUnBDaUpFc2xtcjJ6aThBVwo4ZUplcVJMWkVmc1NTSk9YVEc3UmRHc240cUhGSjAwczJaVGxjSUhTUHduRm0rWGpKaTk5VThHNFhzVW9YbzByCkd5KzBWQ3VVN004Z0lDRUhIc3JRTzlYREQzblQyaml1NVRqckt3anV0M0Vtb0pzc0k1YnF4MzMrT0J1NUJwQ1AKQ1Q0NzNENDNQOXAzcWkvWG5mdnFHU0cyT2o0T2FqVjRmcjBvOUIzS3ZJeGtNZW03V2xJM2p5eTFrQXB5WHFWVApiTGtMRnlXQk5UV1VaMlIvMnd4bXVvQzZtTFp3ODc5TUxDS012azFkb1FLQmdRRGhtd0dhZkpOeW1UaUVRWlJJClNzUXg0c2VxZk9LZmdGQzdvaHFIOWNST091OElKMW83cTJwTTJXNFhpVitTM3dUZFBHbWNhNklPalgyM2lzVkIKMnVxTmk5UzRNbkkyL2QyMkdkL0JSOXJ2QncxZUdKb0ticld4MjJmRThRQ0VXVDFBbk8rRHVEMGpDODV5UmxzNwpheHpsYU1yeEV1M0xJOVVFN050cmRRaUJ5UUtCZ1FEVmRJNmNlSVZCVDZSZ3ZWR3Q4emtMalBJRmpoUUVIQUlwCnVoaXJncXBTNkNYOUJseWYyK280MHptZmozaGU1ckNjRW9CNU1zZU0rRGdGYmNWaDJlL01WbllpTk53NkpDREIKQlFrRjQwOHBacFNlS1h2TC9veVYva0ltTVRKL3RVRFkwRVh4TXdTUEpCMFdsdGJXcmVWSUhvcGlnWFJDYmFleQp1QkhWQnYvNHR3S0JnSHdIdWVQeTVTVTFzMnFTbXpEN1djMkxQZll1M25DT0hOUnJGR2IyNk11UmZ1UmVyaTdyCjJHOFRnb0VTRnljcDBRVElOOCsxSk0wWFlLeE5jSkQ2QjhWMXdLYmJwUXN5bW5lSTFnanV0aUIvSWd3L1BrREsKQ0w0VlA0RjRkYTVOV1cxeVdnTnlnTG9KdlovNXFpS0tpc0pjMEdXazRIS3o2bUxnek9qUTJMSnhBb0dCQUxIWgpmTjJZZVlieVljYU0xMXAxVmlsdWxWVFZqWTNpL0ZaaURSNFNML0lHSldqTi9Temc0aVhZc0tGbXUrZHVsT1psCmNCQUxwRUtycXBtelhZdHJONmJzdjE4KzVlTzNxR2JLMkRyRXEzZVdWZXYyS29UTW9ieHo3ZysrWEJJV0ptTEEKSGhhYTZJaVBrWUQ1eXlWeUhLRGJlWGdiM285ZXFDUjd3N2ZZTGp5L0FvR0FJNEQrTUZraXZ3VUY3aHFmNWVkUwpLcmx0d21vZEhpcVhOYlZrd2JXMUFGUEpiaVlhaTRZRmZLNElBYmlmL1lteGY5Rzc4YU9rcjlacENJek9rRFBaCllwRXdRR1dzQWhFbENGdmM4RS81ZEhFU1NwK3RXdFArTmx1aW1wRnFpRGczL1NVbk13TzJ4SDBuaExhMHplamgKZ21MaDR3L0NjUHliOVp5WGNlV1UvblU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K + CODEC_TYPE: http3 COOKIE_SECRET: UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w= DATABROKER_STORAGE_CONNECTION_STRING: postgres://pomerium:password@postgres:5432/test DATABROKER_STORAGE_TYPE: postgres @@ -187,6 +188,7 @@ services: ports: - 80:80/tcp - 443:443/tcp + - 443:443/udp - 9901:9901/tcp postgres: environment: diff --git a/integration/control_plane_test.go b/integration/control_plane_test.go index 94747f7a1..e639c868f 100644 --- a/integration/control_plane_test.go +++ b/integration/control_plane_test.go @@ -16,34 +16,38 @@ func TestDashboard(t *testing.T) { defer clearTimeout() t.Run("user dashboard", func(t *testing.T) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://authenticate.localhost.pomerium.io/.pomerium/", nil) - if err != nil { - t.Fatal(err) - } + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://authenticate.localhost.pomerium.io/.pomerium/", nil) + if err != nil { + t.Fatal(err) + } - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - body, _ := io.ReadAll(res.Body) + body, _ := io.ReadAll(res.Body) - assert.Equal(t, http.StatusFound, res.StatusCode, "unexpected status code: %s", body) + assert.Equal(t, http.StatusFound, res.StatusCode, "unexpected status code: %s", body) + }) }) t.Run("dashboard strict slash redirect", func(t *testing.T) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://authenticate.localhost.pomerium.io/.pomerium", nil) - if err != nil { - t.Fatal(err) - } + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://authenticate.localhost.pomerium.io/.pomerium", nil) + if err != nil { + t.Fatal(err) + } - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - assert.Equal(t, 3, res.StatusCode/100, "unexpected status code") + assert.Equal(t, 3, res.StatusCode/100, "unexpected status code") + }) }) } @@ -69,7 +73,7 @@ func TestHealth(t *testing.T) { t.Fatal(err) } - res, err := getClient(t).Do(req) + res, err := getClient(t, false).Do(req) if !assert.NoError(t, err, "unexpected http error") { return } diff --git a/integration/main_test.go b/integration/main_test.go index cc3dec380..3a3624b4b 100644 --- a/integration/main_test.go +++ b/integration/main_test.go @@ -18,6 +18,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "github.com/quic-go/quic-go/http3" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "golang.org/x/net/publicsuffix" @@ -62,7 +63,7 @@ func (l loggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error return l.transport.RoundTrip(req) } -func getTransport(t testing.TB) http.RoundTripper { +func getTransport(t testing.TB, useHTTP3 bool) http.RoundTripper { if t != nil { t.Helper() } @@ -77,16 +78,27 @@ func getTransport(t testing.TB) http.RoundTripper { panic(err) } _ = rootCAs.AppendCertsFromPEM(bs) - transport := &http.Transport{ - DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - RootCAs: rootCAs, - }, + + var transport http.RoundTripper + if useHTTP3 { + transport = &http3.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: rootCAs, + }, + } + } else { + transport = &http.Transport{ + DisableKeepAlives: true, + TLSClientConfig: &tls.Config{ + RootCAs: rootCAs, + }, + } } + return loggingRoundTripper{t, transport} } -func getClient(t testing.TB) *http.Client { +func getClient(t testing.TB, useHTTP3 bool) *http.Client { if t != nil { t.Helper() } @@ -100,7 +112,7 @@ func getClient(t testing.TB) *http.Client { CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse }, - Transport: getTransport(t), + Transport: getTransport(t, useHTTP3), Jar: jar, } } @@ -109,12 +121,12 @@ func getClient(t testing.TB) *http.Client { // as well as a pointer to the wrapped http.Transport, so that the // http.Transport can be easily customized. func getClientWithTransport(t testing.TB) (*http.Client, *http.Transport) { - client := getClient(t) + client := getClient(t, false) return client, client.Transport.(loggingRoundTripper).transport.(*http.Transport) } func waitForHealthy(ctx context.Context) error { - client := getClient(nil) + client := getClient(nil, false) check := func(endpoint string) error { reqCtx, clearTimeout := context.WithTimeout(ctx, time.Second) defer clearTimeout() @@ -219,3 +231,9 @@ func loadCertificate(t *testing.T, certName string) tls.Certificate { } return cert } + +func testHTTPClient(t *testing.T, f func(t *testing.T, client *http.Client)) { + t.Helper() + t.Run("http2", func(t *testing.T) { f(t, getClient(t, false)) }) + t.Run("http3", func(t *testing.T) { f(t, getClient(t, true)) }) +} diff --git a/integration/policy_test.go b/integration/policy_test.go index b9899847d..78cfd0277 100644 --- a/integration/policy_test.go +++ b/integration/policy_test.go @@ -37,7 +37,7 @@ func TestQueryStringParams(t *testing.T) { t.Fatal(err) } - res, err := getClient(t).Do(req) + res, err := getClient(t, false).Do(req) if !assert.NoError(t, err, "unexpected http error") { return } @@ -64,36 +64,40 @@ func TestCORS(t *testing.T) { defer clearTimeout() t.Run("enabled", func(t *testing.T) { - req, err := http.NewRequestWithContext(ctx, http.MethodOptions, "https://httpdetails.localhost.pomerium.io/cors-enabled", nil) - if err != nil { - t.Fatal(err) - } - req.Header.Set("Access-Control-Request-Method", http.MethodGet) - req.Header.Set("Origin", "https://httpdetails.localhost.pomerium.io") + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodOptions, "https://httpdetails.localhost.pomerium.io/cors-enabled", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Access-Control-Request-Method", http.MethodGet) + req.Header.Set("Origin", "https://httpdetails.localhost.pomerium.io") - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - assert.Equal(t, http.StatusOK, res.StatusCode, "unexpected status code") + assert.Equal(t, http.StatusOK, res.StatusCode, "unexpected status code") + }) }) t.Run("disabled", func(t *testing.T) { - req, err := http.NewRequestWithContext(ctx, http.MethodOptions, "https://httpdetails.localhost.pomerium.io/cors-disabled", nil) - if err != nil { - t.Fatal(err) - } - req.Header.Set("Access-Control-Request-Method", http.MethodGet) - req.Header.Set("Origin", "https://httpdetails.localhost.pomerium.io") + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodOptions, "https://httpdetails.localhost.pomerium.io/cors-disabled", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Set("Access-Control-Request-Method", http.MethodGet) + req.Header.Set("Origin", "https://httpdetails.localhost.pomerium.io") - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - assert.NotEqual(t, http.StatusOK, res.StatusCode, "unexpected status code") + assert.NotEqual(t, http.StatusOK, res.StatusCode, "unexpected status code") + }) }) } @@ -103,54 +107,57 @@ func TestPreserveHostHeader(t *testing.T) { defer clearTimeout() t.Run("enabled", func(t *testing.T) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/preserve-host-header-enabled", nil) - if err != nil { - t.Fatal(err) - } + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/preserve-host-header-enabled", nil) + if err != nil { + t.Fatal(err) + } + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + var result struct { + Headers struct { + Host string `json:"host"` + } `json:"headers"` + } + err = json.NewDecoder(res.Body).Decode(&result) + if !assert.NoError(t, err) { + return + } - var result struct { - Headers struct { - Host string `json:"host"` - } `json:"headers"` - } - err = json.NewDecoder(res.Body).Decode(&result) - if !assert.NoError(t, err) { - return - } - - assert.Equal(t, "httpdetails.localhost.pomerium.io", result.Headers.Host, - "destination host should be preserved in %v", result) + assert.Equal(t, "httpdetails.localhost.pomerium.io", result.Headers.Host, + "destination host should be preserved in %v", result) + }) }) t.Run("disabled", func(t *testing.T) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/preserve-host-header-disabled", nil) - if err != nil { - t.Fatal(err) - } + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/preserve-host-header-disabled", nil) + if err != nil { + t.Fatal(err) + } - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - var result struct { - Headers struct { - Host string `json:"host"` - } `json:"headers"` - } - err = json.NewDecoder(res.Body).Decode(&result) - if !assert.NoError(t, err) { - return - } + var result struct { + Headers struct { + Host string `json:"host"` + } `json:"headers"` + } + err = json.NewDecoder(res.Body).Decode(&result) + if !assert.NoError(t, err) { + return + } - assert.NotEqual(t, "httpdetails.localhost.pomerium.io", result.Headers.Host, - "destination host should not be preserved in %v", result) + assert.NotEqual(t, "httpdetails.localhost.pomerium.io", result.Headers.Host, + "destination host should not be preserved in %v", result) + }) }) } @@ -159,27 +166,29 @@ func TestSetRequestHeaders(t *testing.T) { ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/", nil) - if err != nil { - t.Fatal(err) - } + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/", nil) + if err != nil { + t.Fatal(err) + } - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - var result struct { - Headers map[string]string `json:"headers"` - } - err = json.NewDecoder(res.Body).Decode(&result) - if !assert.NoError(t, err) { - return - } + var result struct { + Headers map[string]string `json:"headers"` + } + err = json.NewDecoder(res.Body).Decode(&result) + if !assert.NoError(t, err) { + return + } - assert.Equal(t, "custom-request-header-value", result.Headers["x-custom-request-header"], - "expected custom request header to be sent upstream") + assert.Equal(t, "custom-request-header-value", result.Headers["x-custom-request-header"], + "expected custom request header to be sent upstream") + }) } func TestRemoveRequestHeaders(t *testing.T) { @@ -187,28 +196,30 @@ func TestRemoveRequestHeaders(t *testing.T) { ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/", nil) - if err != nil { - t.Fatal(err) - } - req.Header.Add("X-Custom-Request-Header-To-Remove", "foo") + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://httpdetails.localhost.pomerium.io/", nil) + if err != nil { + t.Fatal(err) + } + req.Header.Add("X-Custom-Request-Header-To-Remove", "foo") - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - var result struct { - Headers map[string]string `json:"headers"` - } - err = json.NewDecoder(res.Body).Decode(&result) - if !assert.NoError(t, err) { - return - } + var result struct { + Headers map[string]string `json:"headers"` + } + err = json.NewDecoder(res.Body).Decode(&result) + if !assert.NoError(t, err) { + return + } - _, exist := result.Headers["X-Custom-Request-Header-To-Remove"] - assert.False(t, exist, "expected X-Custom-Request-Header-To-Remove not to be present.") + _, exist := result.Headers["X-Custom-Request-Header-To-Remove"] + assert.False(t, exist, "expected X-Custom-Request-Header-To-Remove not to be present.") + }) } func TestWebsocket(t *testing.T) { @@ -251,28 +262,30 @@ func TestGoogleCloudRun(t *testing.T) { ctx, clearTimeout := context.WithTimeout(ctx, time.Second*30) defer clearTimeout() - req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://cloudrun.localhost.pomerium.io/", nil) - if err != nil { - t.Fatal(err) - } + testHTTPClient(t, func(t *testing.T, client *http.Client) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://cloudrun.localhost.pomerium.io/", nil) + if err != nil { + t.Fatal(err) + } - res, err := getClient(t).Do(req) - if !assert.NoError(t, err, "unexpected http error") { - return - } - defer res.Body.Close() + res, err := client.Do(req) + if !assert.NoError(t, err, "unexpected http error") { + return + } + defer res.Body.Close() - var result struct { - Headers map[string]string `json:"headers"` - } - err = json.NewDecoder(res.Body).Decode(&result) - if !assert.NoError(t, err) { - return - } + var result struct { + Headers map[string]string `json:"headers"` + } + err = json.NewDecoder(res.Body).Decode(&result) + if !assert.NoError(t, err) { + return + } - if result.Headers["x-idp"] == "google" { - assert.NotEmpty(t, result.Headers["authorization"], "expected authorization header when cloudrun is enabled") - } + if result.Headers["x-idp"] == "google" { + assert.NotEmpty(t, result.Headers["authorization"], "expected authorization header when cloudrun is enabled") + } + }) } func TestLoadBalancer(t *testing.T) { @@ -280,9 +293,10 @@ func TestLoadBalancer(t *testing.T) { defer clearTimeout() getDistribution := func(t *testing.T, path string) map[string]float64 { - client := getClient(t) distribution := map[string]float64{} + client := getClient(t, false) + res, err := flows.Authenticate(ctx, client, mustParseURL("https://httpdetails.localhost.pomerium.io/"+path), flows.WithEmail("user1@dogs.test")) @@ -355,7 +369,7 @@ func TestDownstreamClientCA(t *testing.T) { "https://client-cert-required.localhost.pomerium.io/", nil) require.NoError(t, err) - res, err := getClient(t).Do(req) + res, err := getClient(t, false).Do(req) require.NoError(t, err) res.Body.Close() assert.Equal(t, httputil.StatusInvalidClientCertificate, res.StatusCode) @@ -479,7 +493,7 @@ func TestMultipleDownstreamClientCAs(t *testing.T) { assertOK(t, res, err, "/ca2") }) t.Run("no cert", func(t *testing.T) { - client := getClient(t) + client := getClient(t, false) // Without a client certificate, both paths should return an HTML error // page (no login redirect). @@ -505,55 +519,55 @@ func TestPomeriumJWT(t *testing.T) { ctx, clearTimeout := context.WithTimeout(context.Background(), time.Second*30) defer clearTimeout() - client := getClient(t) + testHTTPClient(t, func(t *testing.T, client *http.Client) { + // Obtain a Pomerium attestation JWT from the httpdetails service. + res, err := flows.Authenticate(ctx, client, + mustParseURL("https://restricted-httpdetails.localhost.pomerium.io/"), + flows.WithEmail("user1@dogs.test")) + require.NoError(t, err) + defer res.Body.Close() - // Obtain a Pomerium attestation JWT from the httpdetails service. - res, err := flows.Authenticate(ctx, client, - mustParseURL("https://restricted-httpdetails.localhost.pomerium.io/"), - flows.WithEmail("user1@dogs.test")) - require.NoError(t, err) - defer res.Body.Close() + var m map[string]any + err = json.NewDecoder(res.Body).Decode(&m) + require.NoError(t, err) - var m map[string]any - err = json.NewDecoder(res.Body).Decode(&m) - require.NoError(t, err) + headers, ok := m["headers"].(map[string]any) + require.True(t, ok) + headerJWT, ok := headers["x-pomerium-jwt-assertion"].(string) + require.True(t, ok) - headers, ok := m["headers"].(map[string]any) - require.True(t, ok) - headerJWT, ok := headers["x-pomerium-jwt-assertion"].(string) - require.True(t, ok) + // Manually decode the payload section of the JWT in order to verify the + // format of the iat and exp timestamps. + // (https://github.com/pomerium/pomerium/issues/4149) + p := rawJWTPayload(t, headerJWT) + digitsOnly := regexp.MustCompile(`^\d+$`) + assert.Regexp(t, digitsOnly, p["iat"]) + assert.Regexp(t, digitsOnly, p["exp"]) - // Manually decode the payload section of the JWT in order to verify the - // format of the iat and exp timestamps. - // (https://github.com/pomerium/pomerium/issues/4149) - p := rawJWTPayload(t, headerJWT) - digitsOnly := regexp.MustCompile(`^\d+$`) - assert.Regexp(t, digitsOnly, p["iat"]) - assert.Regexp(t, digitsOnly, p["exp"]) + // Also verify the issuer and audience claims. + assert.Equal(t, "restricted-httpdetails.localhost.pomerium.io", p["iss"]) + assert.Equal(t, "restricted-httpdetails.localhost.pomerium.io", p["aud"]) - // Also verify the issuer and audience claims. - assert.Equal(t, "restricted-httpdetails.localhost.pomerium.io", p["iss"]) - assert.Equal(t, "restricted-httpdetails.localhost.pomerium.io", p["aud"]) + // Obtain a Pomerium attestation JWT from the /.pomerium/jwt endpoint. The + // contents should be identical to the JWT header (except possibly the + // timestamps and the jtis). (https://github.com/pomerium/pomerium/issues/4210) + res, err = client.Get("https://restricted-httpdetails.localhost.pomerium.io/.pomerium/jwt") + require.NoError(t, err) + defer res.Body.Close() + spaJWT, err := io.ReadAll(res.Body) + require.NoError(t, err) - // Obtain a Pomerium attestation JWT from the /.pomerium/jwt endpoint. The - // contents should be identical to the JWT header (except possibly the - // timestamps and the jtis). (https://github.com/pomerium/pomerium/issues/4210) - res, err = client.Get("https://restricted-httpdetails.localhost.pomerium.io/.pomerium/jwt") - require.NoError(t, err) - defer res.Body.Close() - spaJWT, err := io.ReadAll(res.Body) - require.NoError(t, err) + p2 := rawJWTPayload(t, string(spaJWT)) - p2 := rawJWTPayload(t, string(spaJWT)) - - // Remove timestamps before comparing. - delete(p, "iat") - delete(p, "exp") - delete(p, "jti") - delete(p2, "iat") - delete(p2, "exp") - delete(p2, "jti") - assert.Equal(t, p, p2) + // Remove timestamps before comparing. + delete(p, "iat") + delete(p, "exp") + delete(p, "jti") + delete(p2, "iat") + delete(p2, "exp") + delete(p2, "jti") + assert.Equal(t, p, p2) + }) } func rawJWTPayload(t *testing.T, jwt string) map[string]any { @@ -571,18 +585,19 @@ func rawJWTPayload(t *testing.T, jwt string) map[string]any { } func TestUpstreamViaIPAddress(t *testing.T) { - // Verify that we can make a successful request to a route with a 'to' URL - // that uses https with an IP address. - client := getClient(t) - res, err := client.Get("https://httpdetails-ip-address.localhost.pomerium.io/") - require.NoError(t, err, "unexpected http error") - defer res.Body.Close() + testHTTPClient(t, func(t *testing.T, client *http.Client) { + // Verify that we can make a successful request to a route with a 'to' URL + // that uses https with an IP address. + res, err := client.Get("https://httpdetails-ip-address.localhost.pomerium.io/") + require.NoError(t, err, "unexpected http error") + defer res.Body.Close() - var result struct { - Headers map[string]string `json:"headers"` - Protocol string `json:"protocol"` - } - err = json.NewDecoder(res.Body).Decode(&result) - require.NoError(t, err) - assert.Equal(t, "https", result.Protocol) + var result struct { + Headers map[string]string `json:"headers"` + Protocol string `json:"protocol"` + } + err = json.NewDecoder(res.Body).Decode(&result) + require.NoError(t, err) + assert.Equal(t, "https", result.Protocol) + }) } diff --git a/integration/tpl/backends/pomerium.libsonnet b/integration/tpl/backends/pomerium.libsonnet index 062ea7b28..681e87f05 100644 --- a/integration/tpl/backends/pomerium.libsonnet +++ b/integration/tpl/backends/pomerium.libsonnet @@ -38,6 +38,7 @@ local KubernetesDeployment(name, image, environment) = ports: [ { name: 'http', containerPort: 80 }, { name: 'https', containerPort: 443 }, + { name: 'quic', containerPort: 443, protocol: 'UDP' }, { name: 'grpc', containerPort: 5443 }, ], env: [ @@ -68,6 +69,7 @@ local KubernetesService(name) = ports: [ { name: 'http', port: 80, targetPort: 'http', nodePort: 80 }, { name: 'https', port: 443, targetPort: 'https', nodePort: 443 }, + { name: 'quic', port: 443, targetPort: 'quic', nodePort: 443, protocol: 'UDP' }, { name: 'grpc', port: 5443, targetPort: 'grpc', nodePort: 5443 }, ], }, @@ -80,6 +82,7 @@ local Environment(mode, idp, authentication_flow, 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'), + CODEC_TYPE: 'http3', COOKIE_SECRET: 'UYgnt8bxxK5G2sFaNzyqi5Z+OgF8m2akNc0xdQx718w=', DATABROKER_STORAGE_TYPE: 'postgres', DATABROKER_STORAGE_CONNECTION_STRING: 'postgres://pomerium:password@postgres:5432/test', @@ -184,6 +187,7 @@ function(mode, idp, authentication_flow, dns_suffix='') { ports: [ '80:80/tcp', '443:443/tcp', + '443:443/udp', '5443:5443/tcp', '9901:9901/tcp', ], @@ -195,6 +199,7 @@ function(mode, idp, authentication_flow, dns_suffix='') { ports: [ '80:80/tcp', '443:443/tcp', + '443:443/udp', '9901:9901/tcp', ], }, ['authenticate.localhost.pomerium.io']),