config: add circuit breaker thresholds (#5650)

## Summary
Add a new `circuit_breaker_thresholds` option:

```yaml
circuit_breaker_thresholds:
  max_connections: 1
  max_pending_requests: 2
  max_requests: 3
  max_retries: 4
  max_connection_pools: 5
```

This option can be set at the global level or at the route level. Each
threshold is optional and when not set a default will be used. For
internal clusters we will disable the circuit breaker. For normal routes
we will use the envoy defaults.

## Related issues
-
[ENG-2310](https://linear.app/pomerium/issue/ENG-2310/add-circuit-breaker-settings-per-route)

## Checklist
- [x] reference any related issues
- [x] updated unit tests
- [x] add appropriate label (`enhancement`, `bug`, `breaking`,
`dependencies`, `ci`)
- [x] ready for review
This commit is contained in:
Caleb Doxsey 2025-06-16 09:38:39 -06:00 committed by GitHub
parent e320a532de
commit 5ac7ae9c26
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1571 additions and 1127 deletions

6
.clang-format Normal file
View file

@ -0,0 +1,6 @@
---
Language: Proto
BasedOnStyle: Google
AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true
ColumnLimit: 0

View file

@ -85,7 +85,7 @@ func Test_populateLogEvent(t *testing.T) {
{log.AuthorizeLogFieldQuery, s, `{"query":"a=b"}`},
{log.AuthorizeLogFieldRemovedGroupsCount, s, `{"removed-groups-count":42}`},
{log.AuthorizeLogFieldRequestID, s, `{"request-id":"REQUEST-ID"}`},
{log.AuthorizeLogFieldRouteChecksum, s, `{"route-checksum":14294378844626301341}`},
{log.AuthorizeLogFieldRouteChecksum, s, `{"route-checksum":7416256365460802121}`},
{log.AuthorizeLogFieldRouteID, s, `{"route-id":"POLICY-ID"}`},
{log.AuthorizeLogFieldServiceAccountID, sa, `{"service-account-id":"SERVICE-ACCOUNT-ID"}`},
{log.AuthorizeLogFieldSessionID, s, `{"session-id":"SESSION-ID"}`},

View file

@ -0,0 +1,56 @@
package config
import (
"github.com/volatiletech/null/v9"
configpb "github.com/pomerium/pomerium/pkg/grpc/config"
)
// CircuitBreakerThresholds define thresholds for circuit breaking.
type CircuitBreakerThresholds struct {
MaxConnections null.Uint32 `mapstructure:"max_connections" yaml:"max_connections,omitempty" json:"max_connections,omitempty"`
MaxPendingRequests null.Uint32 `mapstructure:"max_pending_requests" yaml:"max_pending_requests,omitempty" json:"max_pending_requests,omitempty"`
MaxRequests null.Uint32 `mapstructure:"max_requests" yaml:"max_requests,omitempty" json:"max_requests,omitempty"`
MaxRetries null.Uint32 `mapstructure:"max_retries" yaml:"max_retries,omitempty" json:"max_retries,omitempty"`
MaxConnectionPools null.Uint32 `mapstructure:"max_connection_pools" yaml:"max_connection_pools,omitempty" json:"max_connection_pools,omitempty"`
}
// CircuitBreakerThresholdsFromPB converts the CircuitBreakerThresholds from a protobuf type.
func CircuitBreakerThresholdsFromPB(src *configpb.CircuitBreakerThresholds) *CircuitBreakerThresholds {
if src == nil {
return nil
}
dst := &CircuitBreakerThresholds{}
if src.MaxConnections != nil {
dst.MaxConnections = null.Uint32From(*src.MaxConnections)
}
if src.MaxPendingRequests != nil {
dst.MaxPendingRequests = null.Uint32From(*src.MaxPendingRequests)
}
if src.MaxRequests != nil {
dst.MaxRequests = null.Uint32From(*src.MaxRequests)
}
if src.MaxRetries != nil {
dst.MaxRetries = null.Uint32From(*src.MaxRetries)
}
if src.MaxConnectionPools != nil {
dst.MaxConnectionPools = null.Uint32From(*src.MaxConnectionPools)
}
return dst
}
// CircuitBreakerThresholdsToPB converts the CircuitBreakerThresholds into a protobuf type.
func CircuitBreakerThresholdsToPB(src *CircuitBreakerThresholds) *configpb.CircuitBreakerThresholds {
if src == nil {
return nil
}
return &configpb.CircuitBreakerThresholds{
MaxConnections: src.MaxConnections.Ptr(),
MaxPendingRequests: src.MaxPendingRequests.Ptr(),
MaxRequests: src.MaxRequests.Ptr(),
MaxRetries: src.MaxRetries.Ptr(),
MaxConnectionPools: src.MaxConnectionPools.Ptr(),
}
}

View file

@ -37,6 +37,7 @@ var ViperPolicyHooks = viper.DecodeHook(mapstructure.ComposeDecodeHookFunc(
// parse base-64 encoded POLICY that is bound to environment variable
DecodePolicyBase64Hook(),
decodeNullBoolHookFunc(),
decodeNullUint32HookFunc(),
decodeJWTClaimHeadersHookFunc(),
decodeBearerTokenFormatHookFunc(),
decodeCodecTypeHookFunc(),

View file

@ -46,6 +46,25 @@ func decodeNullBoolHookFunc() mapstructure.DecodeHookFunc {
}
}
func decodeNullUint32HookFunc() mapstructure.DecodeHookFunc {
return func(_, t reflect.Type, data any) (any, error) {
if t != reflect.TypeOf(null.Uint32{}) {
return data, nil
}
bs, err := json.Marshal(data)
if err != nil {
return nil, err
}
var value null.Uint32
err = json.Unmarshal(bs, &value)
if err != nil {
return nil, err
}
return value, nil
}
}
// JWTClaimHeaders are headers to add to a request based on IDP claims.
type JWTClaimHeaders map[string]string

View file

@ -260,6 +260,7 @@ func (b *Builder) BuildBootstrapStaticResources(
},
},
TypedExtensionProtocolOptions: buildTypedExtensionProtocolOptions(nil, upstreamProtocolHTTP2, Keepalive(false)),
CircuitBreakers: buildInternalCircuitBreakers(cfg),
}
staticResources.Clusters = append(staticResources.Clusters, controlPlaneCluster)

View file

@ -71,6 +71,14 @@ func TestBuilder_BuildBootstrapStaticResources(t *testing.T) {
"name": "pomerium-control-plane-grpc",
"type": "STATIC",
"connectTimeout": "5s",
"circuitBreakers": {
"thresholds": [{
"maxConnectionPools": 4294967295,
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295
}]
},
"loadAssignment": {
"clusterName": "pomerium-control-plane-grpc",
"endpoints": [{

View file

@ -0,0 +1,83 @@
package envoyconfig
import (
"math"
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/pomerium/pomerium/config"
)
// unlimitedCircuitBreakersThreshold sets the circuit breaking thresholds to the maximum value, effectively disabling them
var unlimitedCircuitBreakersThreshold = &envoy_config_cluster_v3.CircuitBreakers_Thresholds{
Priority: envoy_config_core_v3.RoutingPriority_DEFAULT,
MaxConnections: wrapperspb.UInt32(math.MaxUint32),
MaxPendingRequests: wrapperspb.UInt32(math.MaxUint32),
MaxRequests: wrapperspb.UInt32(math.MaxUint32),
MaxConnectionPools: wrapperspb.UInt32(math.MaxUint32),
}
func buildInternalCircuitBreakers(cfg *config.Config) *envoy_config_cluster_v3.CircuitBreakers {
threshold := unlimitedCircuitBreakersThreshold
if cfg != nil && cfg.Options != nil {
threshold = buildCircuitBreakersThreshold(threshold, cfg.Options.CircuitBreakerThresholds)
}
if threshold == nil {
return nil
}
return &envoy_config_cluster_v3.CircuitBreakers{
Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{threshold},
}
}
func buildRouteCircuitBreakers(cfg *config.Config, policy *config.Policy) *envoy_config_cluster_v3.CircuitBreakers {
threshold := (*envoy_config_cluster_v3.CircuitBreakers_Thresholds)(nil)
if cfg != nil && cfg.Options != nil {
threshold = buildCircuitBreakersThreshold(threshold, cfg.Options.CircuitBreakerThresholds)
}
if policy != nil {
threshold = buildCircuitBreakersThreshold(threshold, policy.CircuitBreakerThresholds)
}
if threshold == nil {
return nil
}
return &envoy_config_cluster_v3.CircuitBreakers{
Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{threshold},
}
}
func buildCircuitBreakersThreshold(dst *envoy_config_cluster_v3.CircuitBreakers_Thresholds, src *config.CircuitBreakerThresholds) *envoy_config_cluster_v3.CircuitBreakers_Thresholds {
if src == nil {
return dst
}
if dst == nil {
dst = new(envoy_config_cluster_v3.CircuitBreakers_Thresholds)
} else {
dst = proto.CloneOf(dst)
}
if src.MaxConnections.IsSet() {
dst.MaxConnections = wrapperspb.UInt32(src.MaxConnections.Uint32)
}
if src.MaxPendingRequests.IsSet() {
dst.MaxPendingRequests = wrapperspb.UInt32(src.MaxPendingRequests.Uint32)
}
if src.MaxRequests.IsSet() {
dst.MaxRequests = wrapperspb.UInt32(src.MaxRequests.Uint32)
}
if src.MaxRetries.IsSet() {
dst.MaxRetries = wrapperspb.UInt32(src.MaxRetries.Uint32)
}
if src.MaxConnectionPools.IsSet() {
dst.MaxConnectionPools = wrapperspb.UInt32(src.MaxConnectionPools.Uint32)
}
return dst
}

View file

@ -152,6 +152,7 @@ func (b *Builder) buildInternalCluster(
if err := b.buildCluster(cluster, name, endpoints, upstreamProtocol, keepalive); err != nil {
return nil, err
}
cluster.CircuitBreakers = buildInternalCircuitBreakers(cfg)
return cluster, nil
}
@ -205,6 +206,7 @@ func (b *Builder) buildPolicyCluster(ctx context.Context, cfg *config.Config, po
if err := b.buildCluster(cluster, name, endpoints, upstreamProtocol, Keepalive(false)); err != nil {
return nil, err
}
cluster.CircuitBreakers = buildRouteCircuitBreakers(cfg, policy)
return cluster, nil
}

View file

@ -100,7 +100,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "4844561823473827050",
"route_checksum": "3842393772597897044",
"route_id": "5fbd81d8f19363f4"
}
}
@ -158,7 +158,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "4844561823473827050",
"route_checksum": "3842393772597897044",
"route_id": "5fbd81d8f19363f4"
}
}

View file

@ -405,14 +405,14 @@ func Test_buildPolicyRoutes(t *testing.T) {
8: "301084c3bd94c1ed",
}
routeChecksums := []string{
1: "1550825365439203068",
2: "1743754064510868859",
3: "5973469357905660470",
4: "10629393024495644652",
5: "17050190873730880526",
6: "4829848755825381466",
7: "7941222915300424536",
8: "8084661313119959810",
1: "12033848814082772143",
2: "17258478527403939037",
3: "8444406770556299357",
4: "4105318140299980592",
5: "10806704411058754499",
6: "3851124977091699412",
7: "4628039018923516807",
8: "2191103304818138823",
}
b := &Builder{filemgr: filemgr.NewManager(), reproxy: reproxy.New()}
@ -1217,7 +1217,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "16194904053254305772",
"route_checksum": "6606147546948811510",
"route_id": "98f90d58022ca963"
}
}
@ -1294,7 +1294,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "10734663134999137402",
"route_checksum": "17040385240945756229",
"route_id": "81175a3a9df11dd8"
}
}
@ -1392,7 +1392,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "6431934433938620139",
"route_checksum": "8569669228737044894",
"route_id": "ad0a23467bbdb773"
}
}
@ -1495,7 +1495,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "185312241489123079",
"route_checksum": "17193711666127451236",
"route_id": "1013c6be524d7fbd"
}
}
@ -1611,7 +1611,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "10135716377738705841",
"route_checksum": "5598645225002145886",
"route_id": "a81e6b1e66c1e2cd"
}
}
@ -1746,7 +1746,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "14883526649905267120",
"route_checksum": "12006715121823499564",
"route_id": "4d5ee69fcc359f45"
}
}
@ -1822,7 +1822,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "10046758419081543299",
"route_checksum": "16841768668336313196",
"route_id": "4d5ee69fcc359f45"
}
}
@ -1903,7 +1903,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "2754443979682419848",
"route_checksum": "4748097581015346493",
"route_id": "4d5ee69fcc359f45"
}
}
@ -1979,7 +1979,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "1546259218106347577",
"route_checksum": "17768398617143399322",
"route_id": "4d5ee69fcc359f45"
}
}
@ -2055,7 +2055,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "8023363204239692999",
"route_checksum": "16285305135509499281",
"route_id": "4d5ee69fcc359f45"
}
}
@ -2136,7 +2136,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
"checkSettings": {
"contextExtensions": {
"internal": "false",
"route_checksum": "5173412791633652167",
"route_checksum": "5072687206675243600",
"route_id": "4d5ee69fcc359f45"
}
}

View file

@ -23,6 +23,16 @@
},
{
"connectTimeout": "10s",
"circuitBreakers": {
"thresholds": [
{
"maxConnectionPools": 4294967295,
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295
}
]
},
"dnsLookupFamily": "V4_PREFERRED",
"loadAssignment": {
"clusterName": "pomerium-control-plane-grpc",
@ -71,6 +81,16 @@
},
{
"connectTimeout": "10s",
"circuitBreakers": {
"thresholds": [
{
"maxConnectionPools": 4294967295,
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295
}
]
},
"dnsLookupFamily": "V4_PREFERRED",
"loadAssignment": {
"clusterName": "pomerium-control-plane-http",
@ -123,6 +143,16 @@
},
{
"connectTimeout": "10s",
"circuitBreakers": {
"thresholds": [
{
"maxConnectionPools": 4294967295,
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295
}
]
},
"dnsLookupFamily": "V4_PREFERRED",
"loadAssignment": {
"clusterName": "pomerium-control-plane-metrics",
@ -175,6 +205,16 @@
},
{
"connectTimeout": "10s",
"circuitBreakers": {
"thresholds": [
{
"maxConnectionPools": 4294967295,
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295
}
]
},
"dnsLookupFamily": "V4_PREFERRED",
"loadAssignment": {
"clusterName": "pomerium-authorize",
@ -223,6 +263,16 @@
},
{
"connectTimeout": "10s",
"circuitBreakers": {
"thresholds": [
{
"maxConnectionPools": 4294967295,
"maxConnections": 4294967295,
"maxPendingRequests": 4294967295,
"maxRequests": 4294967295
}
]
},
"dnsLookupFamily": "V4_PREFERRED",
"loadAssignment": {
"clusterName": "pomerium-databroker",

View file

@ -291,7 +291,8 @@ type Options struct {
RuntimeFlags RuntimeFlags `mapstructure:"runtime_flags" yaml:"runtime_flags,omitempty"`
HTTP3AdvertisePort null.Uint32 `mapstructure:"-" yaml:"-" json:"-"`
HTTP3AdvertisePort null.Uint32 `mapstructure:"-" yaml:"-" json:"-"`
CircuitBreakerThresholds *CircuitBreakerThresholds `mapstructure:"circuit_breaker_thresholds" yaml:"circuit_breaker_thresholds" json:"circuit_breaker_thresholds"`
}
type certificateFilePair struct {
@ -1593,6 +1594,9 @@ func (o *Options) ApplySettings(ctx context.Context, certsIndex *cryptutil.Certi
return RuntimeFlag(k), v
})
o.HTTP3AdvertisePort = null.Uint32FromPtr(settings.Http3AdvertisePort)
if settings.CircuitBreakerThresholds != nil {
o.CircuitBreakerThresholds = CircuitBreakerThresholdsFromPB(settings.CircuitBreakerThresholds)
}
}
func (o *Options) ToProto() *config.Config {
@ -1720,6 +1724,9 @@ func (o *Options) ToProto() *config.Config {
return string(k), v
})
settings.Http3AdvertisePort = o.HTTP3AdvertisePort.Ptr()
if o.CircuitBreakerThresholds != nil {
settings.CircuitBreakerThresholds = CircuitBreakerThresholdsToPB(o.CircuitBreakerThresholds)
}
routes := make([]*config.Route, 0, o.NumPolicies())
for p := range o.GetAllPolicies() {

View file

@ -32,6 +32,7 @@ import (
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/volatiletech/null/v9"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/fieldmaskpb"
@ -1035,6 +1036,21 @@ func TestOptions_ApplySettings(t *testing.T) {
assert.Equal(t, ptr([]string{"x", "y", "z"}), options.IDPAccessTokenAllowedAudiences,
"should preserve idp access token allowed audiences")
})
t.Run("circuit_breaker_thresholds", func(t *testing.T) {
t.Parallel()
options := NewDefaultOptions()
assert.Nil(t, options.CircuitBreakerThresholds)
options.ApplySettings(ctx, nil, &configpb.Settings{
CircuitBreakerThresholds: &configpb.CircuitBreakerThresholds{
MaxConnections: proto.Uint32(3),
},
})
assert.Equal(t, &CircuitBreakerThresholds{MaxConnections: null.Uint32From(3)}, options.CircuitBreakerThresholds)
options.ApplySettings(ctx, nil, &configpb.Settings{})
assert.Equal(t, &CircuitBreakerThresholds{MaxConnections: null.Uint32From(3)}, options.CircuitBreakerThresholds,
"should not erase existing circuit breaker thresholds")
})
}
func TestOptions_GetSetResponseHeaders(t *testing.T) {

View file

@ -206,7 +206,8 @@ type Policy struct {
DependsOn []string `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
// MCP is an experimental support for Model Context Protocol upstreams
MCP *MCP `mapstructure:"mcp" yaml:"mcp,omitempty" json:"mcp,omitempty"`
MCP *MCP `mapstructure:"mcp" yaml:"mcp,omitempty" json:"mcp,omitempty"`
CircuitBreakerThresholds *CircuitBreakerThresholds `mapstructure:"circuit_breaker_thresholds" yaml:"circuit_breaker_thresholds,omitempty" json:"circuit_breaker_thresholds,omitempty"`
}
// MCP is an experimental support for Model Context Protocol upstreams configuration
@ -345,6 +346,7 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
AllowPublicUnauthenticatedAccess: pb.GetAllowPublicUnauthenticatedAccess(),
AllowSPDY: pb.GetAllowSpdy(),
AllowWebsockets: pb.GetAllowWebsockets(),
CircuitBreakerThresholds: CircuitBreakerThresholdsFromPB(pb.CircuitBreakerThresholds),
CORSAllowPreflight: pb.GetCorsAllowPreflight(),
Description: pb.GetDescription(),
DependsOn: pb.GetDependsOn(),
@ -502,6 +504,7 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
AllowPublicUnauthenticatedAccess: p.AllowPublicUnauthenticatedAccess,
AllowSpdy: p.AllowSPDY,
AllowWebsockets: p.AllowWebsockets,
CircuitBreakerThresholds: CircuitBreakerThresholdsToPB(p.CircuitBreakerThresholds),
CorsAllowPreflight: p.CORSAllowPreflight,
Description: p.Description,
DependsOn: p.DependsOn,

File diff suppressed because it is too large Load diff

View file

@ -17,7 +17,7 @@ message Config {
message RouteRewriteHeader {
string header = 1;
oneof matcher {
oneof matcher {
string prefix = 3;
}
string value = 2;
@ -55,7 +55,29 @@ enum BearerTokenFormat {
BEARER_TOKEN_FORMAT_IDP_IDENTITY_TOKEN = 3;
}
// Next ID: 73.
// CircuitBreakerThresholds defines CircuitBreaker settings.
message CircuitBreakerThresholds {
// The maximum number of connections that Envoy will make to the upstream
// cluster. If not specified, the default is 1024.
optional uint32 max_connections = 1;
// The maximum number of pending requests that Envoy will allow to the
// upstream cluster. If not specified, the default is 1024. This limit is
// applied as a connection limit for non-HTTP traffic.
optional uint32 max_pending_requests = 2;
// The maximum number of parallel requests that Envoy will make to the
// upstream cluster. If not specified, the default is 1024. This limit does
// not apply to non-HTTP traffic.
optional uint32 max_requests = 3;
// The maximum number of parallel retries that Envoy will allow to the
// upstream cluster. If not specified, the default is 3.
optional uint32 max_retries = 4;
// The maximum number of connection pools per cluster that Envoy will
// concurrently support at once. If not specified, the default is unlimited.
// Set this for clusters which create a large number of connection pools.
optional uint32 max_connection_pools = 5;
}
// Next ID: 74.
message Route {
message StringList {
repeated string values = 1;
@ -79,7 +101,7 @@ message Route {
repeated string allowed_users = 4 [deprecated = true];
// repeated string allowed_groups = 5 [ deprecated = true ];
repeated string allowed_domains = 6 [deprecated = true];
repeated string allowed_domains = 6 [deprecated = true];
map<string, google.protobuf.ListValue> allowed_idp_claims = 32 [deprecated = true];
string prefix = 7;
@ -148,7 +170,8 @@ message Route {
optional StringList idp_access_token_allowed_audiences = 69;
bool show_error_details = 59;
optional MCP mcp = 72;
optional MCP mcp = 72;
optional CircuitBreakerThresholds circuit_breaker_thresholds = 73;
}
message MCP {
@ -202,7 +225,7 @@ message Policy {
string remediation = 9;
}
// Next ID: 140.
// Next ID: 141.
message Settings {
message Certificate {
bytes cert_bytes = 3;
@ -213,18 +236,18 @@ message Settings {
repeated string values = 1;
}
optional string installation_id = 71;
optional string log_level = 3;
optional StringList access_log_fields = 114;
optional StringList authorize_log_fields = 115;
optional string proxy_log_level = 4;
optional string shared_secret = 5;
optional string services = 6;
optional string address = 7;
optional bool insecure_server = 8;
optional string dns_lookup_family = 60;
repeated Certificate certificates = 9;
optional string http_redirect_addr = 10;
optional string installation_id = 71;
optional string log_level = 3;
optional StringList access_log_fields = 114;
optional StringList authorize_log_fields = 115;
optional string proxy_log_level = 4;
optional string shared_secret = 5;
optional string services = 6;
optional string address = 7;
optional bool insecure_server = 8;
optional string dns_lookup_family = 60;
repeated Certificate certificates = 9;
optional string http_redirect_addr = 10;
optional google.protobuf.Duration timeout_read = 11;
optional google.protobuf.Duration timeout_write = 12;
optional google.protobuf.Duration timeout_idle = 13;
@ -236,7 +259,7 @@ message Settings {
optional string cookie_secret = 17;
optional string cookie_domain = 18;
// optional bool cookie_secure = 19;
optional bool cookie_http_only = 20;
optional bool cookie_http_only = 20;
optional google.protobuf.Duration cookie_expire = 21;
optional string cookie_same_site = 113;
optional string idp_client_id = 22;
@ -257,10 +280,10 @@ message Settings {
optional string signing_key = 36;
map<string, string> set_response_headers = 69;
// repeated string jwt_claims_headers = 37;
map<string, string> jwt_claims_headers = 63;
optional IssuerFormat jwt_issuer_format = 139;
repeated string jwt_groups_filter = 119;
optional BearerTokenFormat bearer_token_format = 138;
map<string, string> jwt_claims_headers = 63;
optional IssuerFormat jwt_issuer_format = 139;
repeated string jwt_groups_filter = 119;
optional BearerTokenFormat bearer_token_format = 138;
optional google.protobuf.Duration default_upstream_timeout = 39;
optional string metrics_address = 40;
optional string metrics_basic_auth = 64;
@ -281,17 +304,17 @@ message Settings {
optional google.protobuf.Duration otel_exporter_otlp_traces_timeout = 133;
optional google.protobuf.Duration otel_bsp_schedule_delay = 134;
optional int32 otel_bsp_max_export_batch_size = 135;
reserved 41 to 45, 98; // legacy tracing fields
optional string grpc_address = 46;
optional bool grpc_insecure = 47;
reserved 41 to 45, 98; // legacy tracing fields
optional string grpc_address = 46;
optional bool grpc_insecure = 47;
optional google.protobuf.Duration grpc_client_timeout = 99;
reserved 100; // grpc_client_dns_roundrobin
reserved 100; // grpc_client_dns_roundrobin
// optional string forward_auth_url = 50;
repeated string databroker_service_urls = 52;
optional string databroker_internal_service_url = 84;
optional string databroker_storage_type = 101;
optional string databroker_storage_connection_string = 102;
reserved 106; // databroker_storage_tls_skip_verify
reserved 106; // databroker_storage_tls_skip_verify
optional DownstreamMtlsSettings downstream_mtls = 116;
// optional string client_ca = 53;
// optional string client_crl = 74;
@ -314,18 +337,21 @@ message Settings {
optional string envoy_bind_config_source_address = 111;
optional bool envoy_bind_config_freebind = 112;
repeated string programmatic_redirect_domain_whitelist = 68;
optional envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager.CodecType codec_type = 73;
// optional pomerium.crypt.PublicKeyEncryptionKey audit_key = 72;
optional string primary_color = 85;
optional string secondary_color = 86;
optional string darkmode_primary_color = 87;
optional string darkmode_secondary_color = 88;
optional string logo_url = 89;
optional string favicon_url = 90;
optional string error_message_first_paragraph = 91;
optional bool pass_identity_headers = 117;
map<string, bool> runtime_flags = 118;
optional uint32 http3_advertise_port = 136;
optional string primary_color = 85;
optional string secondary_color = 86;
optional string darkmode_primary_color = 87;
optional string darkmode_secondary_color = 88;
optional string logo_url = 89;
optional string favicon_url = 90;
optional string error_message_first_paragraph = 91;
optional bool pass_identity_headers = 117;
map<string, bool> runtime_flags = 118;
optional uint32 http3_advertise_port = 136;
optional CircuitBreakerThresholds circuit_breaker_thresholds = 140;
}
message DownstreamMtlsSettings {