From b3bd3a6586d1ae6c4a5d3c394d2eea707e164973 Mon Sep 17 00:00:00 2001 From: Dan Stough Date: Thu, 22 Dec 2022 15:18:15 -0500 Subject: [PATCH] [OSS] feat: access logs for listeners and listener filters (#15864) * feat: access logs for listeners and listener filters * changelog * fix integration test --- .changelog/15864.txt | 3 + agent/configentry/merge_service_config.go | 3 + .../configentry/merge_service_config_test.go | 44 +- agent/configentry/resolve.go | 1 + agent/configentry/resolve_test.go | 56 ++ .../services/subscribe/subscribe_test.go | 9 + agent/structs/config_entry.go | 28 +- agent/structs/connect_proxy_config.go | 80 ++- agent/structs/connect_proxy_config_test.go | 18 +- agent/structs/structs_filtering_test.go | 37 ++ agent/xds/accesslogs/accesslogs.go | 184 ++++++ agent/xds/listeners.go | 289 +++++++-- agent/xds/listeners_ingress.go | 30 +- agent/xds/listeners_test.go | 42 ++ .../access-logs-defaults.latest.golden | 356 +++++++++++ .../access-logs-json-file.latest.golden | 224 +++++++ ...t-stderr-disablelistenerlogs.latest.golden | 158 +++++ api/agent.go | 1 + api/config_entry.go | 9 +- command/services/config.go | 10 + command/services/config_test.go | 51 ++ proto/pbservice/service.gen.go | 30 + proto/pbservice/service.pb.binary.go | 10 + proto/pbservice/service.pb.go | 577 +++++++++++------- proto/pbservice/service.proto | 21 + 25 files changed, 1949 insertions(+), 322 deletions(-) create mode 100644 .changelog/15864.txt create mode 100644 agent/xds/accesslogs/accesslogs.go create mode 100644 agent/xds/testdata/listeners/access-logs-defaults.latest.golden create mode 100644 agent/xds/testdata/listeners/access-logs-json-file.latest.golden create mode 100644 agent/xds/testdata/listeners/access-logs-text-stderr-disablelistenerlogs.latest.golden diff --git a/.changelog/15864.txt b/.changelog/15864.txt new file mode 100644 index 0000000000..049d6cf06a --- /dev/null +++ b/.changelog/15864.txt @@ -0,0 +1,3 @@ +```release-note:feature +connect: adds support for Envoy access logging. Access logging can be enabled using the [`proxy-defaults`](https://developer.hashicorp.com/consul/docs/connect/config-entries/proxy-defaults#accesslogs) config entry. +``` diff --git a/agent/configentry/merge_service_config.go b/agent/configentry/merge_service_config.go index 341d907cc1..17e68ce1d2 100644 --- a/agent/configentry/merge_service_config.go +++ b/agent/configentry/merge_service_config.go @@ -112,6 +112,9 @@ func MergeServiceConfig(defaults *structs.ServiceConfigResponse, service *struct if err := mergo.Merge(&ns.Proxy.Expose, defaults.Expose); err != nil { return nil, err } + if err := mergo.Merge(&ns.Proxy.AccessLogs, defaults.AccessLogs); err != nil { + return nil, err + } // defaults.EnvoyExtensions contains the extensions from the proxy defaults config entry followed by extensions from // the service defaults config entry. This adds the extensions to structs.NodeService.Proxy which in turn is copied diff --git a/agent/configentry/merge_service_config_test.go b/agent/configentry/merge_service_config_test.go index b9e5f60180..c6a6d3ae03 100644 --- a/agent/configentry/merge_service_config_test.go +++ b/agent/configentry/merge_service_config_test.go @@ -21,7 +21,7 @@ func Test_MergeServiceConfig_TransparentProxy(t *testing.T) { want *structs.NodeService }{ { - name: "inherit transparent proxy settings", + name: "inherit transparent proxy settings + kitchen sink", args: args{ defaults: &structs.ServiceConfigResponse{ Mode: structs.ProxyModeTransparent, @@ -29,6 +29,27 @@ func Test_MergeServiceConfig_TransparentProxy(t *testing.T) { OutboundListenerPort: 10101, DialedDirectly: true, }, + ProxyConfig: map[string]interface{}{ + "foo": "bar", + }, + Expose: structs.ExposeConfig{ + Checks: true, + Paths: []structs.ExposePath{ + { + ListenerPort: 8080, + Path: "/", + Protocol: "http", + }, + }, + }, + MeshGateway: structs.MeshGatewayConfig{Mode: structs.MeshGatewayModeRemote}, + AccessLogs: structs.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: structs.FileLogSinkType, + Path: "/tmp/accesslog.txt", + JSONFormat: "{ \"custom_start_time\": \"%START_TIME%\" }", + }, }, service: &structs.NodeService{ ID: "foo-proxy", @@ -52,6 +73,27 @@ func Test_MergeServiceConfig_TransparentProxy(t *testing.T) { OutboundListenerPort: 10101, DialedDirectly: true, }, + Config: map[string]interface{}{ + "foo": "bar", + }, + Expose: structs.ExposeConfig{ + Checks: true, + Paths: []structs.ExposePath{ + { + ListenerPort: 8080, + Path: "/", + Protocol: "http", + }, + }, + }, + MeshGateway: structs.MeshGatewayConfig{Mode: structs.MeshGatewayModeRemote}, + AccessLogs: structs.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: structs.FileLogSinkType, + Path: "/tmp/accesslog.txt", + JSONFormat: "{ \"custom_start_time\": \"%START_TIME%\" }", + }, }, }, }, diff --git a/agent/configentry/resolve.go b/agent/configentry/resolve.go index 537b0b6208..ee108ee70a 100644 --- a/agent/configentry/resolve.go +++ b/agent/configentry/resolve.go @@ -38,6 +38,7 @@ func ComputeResolvedServiceConfig( thisReply.MeshGateway = proxyConf.MeshGateway thisReply.Expose = proxyConf.Expose thisReply.EnvoyExtensions = proxyConf.EnvoyExtensions + thisReply.AccessLogs = proxyConf.AccessLogs // Extract the global protocol from proxyConf for upstream configs. rawProtocol := proxyConf.Config["protocol"] diff --git a/agent/configentry/resolve_test.go b/agent/configentry/resolve_test.go index 1051a2d60a..151cf06368 100644 --- a/agent/configentry/resolve_test.go +++ b/agent/configentry/resolve_test.go @@ -129,6 +129,62 @@ func Test_ComputeResolvedServiceConfig(t *testing.T) { }, }, }, + { + name: "proxy inherits kitchen sink from proxy-defaults", + args: args{ + scReq: &structs.ServiceConfigRequest{ + Name: "sid", + }, + entries: &ResolvedServiceConfigSet{ + ProxyDefaults: map[string]*structs.ProxyConfigEntry{ + acl.DefaultEnterpriseMeta().PartitionOrDefault(): { + Config: map[string]interface{}{ + "foo": "bar", + }, + Expose: structs.ExposeConfig{ + Checks: true, + Paths: []structs.ExposePath{}, + }, + Mode: structs.ProxyModeTransparent, + MeshGateway: remoteMeshGW, + TransparentProxy: structs.TransparentProxyConfig{ + OutboundListenerPort: 6666, + DialedDirectly: true, + }, + AccessLogs: structs.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: structs.FileLogSinkType, + Path: "/tmp/accesslog.txt", + JSONFormat: "{ \"custom_start_time\": \"%START_TIME%\" }", + }, + }, + }, + }, + }, + want: &structs.ServiceConfigResponse{ + ProxyConfig: map[string]interface{}{ + "foo": "bar", + }, + Expose: structs.ExposeConfig{ + Checks: true, + Paths: []structs.ExposePath{}, + }, + Mode: structs.ProxyModeTransparent, + MeshGateway: remoteMeshGW, + TransparentProxy: structs.TransparentProxyConfig{ + OutboundListenerPort: 6666, + DialedDirectly: true, + }, + AccessLogs: structs.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: structs.FileLogSinkType, + Path: "/tmp/accesslog.txt", + JSONFormat: "{ \"custom_start_time\": \"%START_TIME%\" }", + }, + }, + }, { name: "proxy upstream mesh-gateway inherits service-defaults", args: args{ diff --git a/agent/grpc-internal/services/subscribe/subscribe_test.go b/agent/grpc-internal/services/subscribe/subscribe_test.go index 72c8b5e1bf..5d367c87e5 100644 --- a/agent/grpc-internal/services/subscribe/subscribe_test.go +++ b/agent/grpc-internal/services/subscribe/subscribe_test.go @@ -164,6 +164,7 @@ func TestServer_Subscribe_IntegrationWithBackend(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, RaftIndex: raftIndex(ids, "reg2", "reg2"), @@ -197,6 +198,7 @@ func TestServer_Subscribe_IntegrationWithBackend(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, RaftIndex: raftIndex(ids, "reg3", "reg3"), @@ -249,6 +251,7 @@ func TestServer_Subscribe_IntegrationWithBackend(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, RaftIndex: raftIndex(ids, "reg3", "reg3"), @@ -547,6 +550,7 @@ func TestServer_Subscribe_IntegrationWithBackend_ForwardToDC(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, EnterpriseMeta: pbcommon.DefaultEnterpriseMeta, @@ -580,6 +584,7 @@ func TestServer_Subscribe_IntegrationWithBackend_ForwardToDC(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, EnterpriseMeta: pbcommon.DefaultEnterpriseMeta, @@ -633,6 +638,7 @@ func TestServer_Subscribe_IntegrationWithBackend_ForwardToDC(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, EnterpriseMeta: pbcommon.DefaultEnterpriseMeta, @@ -1046,6 +1052,7 @@ func TestNewEventFromSteamEvent(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, EnterpriseMeta: &pbcommon.EnterpriseMeta{}, @@ -1068,6 +1075,7 @@ func TestNewEventFromSteamEvent(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, EnterpriseMeta: &pbcommon.EnterpriseMeta{}, @@ -1107,6 +1115,7 @@ func TestNewEventFromSteamEvent(t *testing.T) { MeshGateway: &pbservice.MeshGatewayConfig{}, Expose: &pbservice.ExposeConfig{}, TransparentProxy: &pbservice.TransparentProxyConfig{}, + AccessLogs: &pbservice.AccessLogsConfig{}, }, Connect: &pbservice.ServiceConnect{}, EnterpriseMeta: &pbcommon.EnterpriseMeta{}, diff --git a/agent/structs/config_entry.go b/agent/structs/config_entry.go index 8462572780..f42356becf 100644 --- a/agent/structs/config_entry.go +++ b/agent/structs/config_entry.go @@ -1,7 +1,6 @@ package structs import ( - "encoding/json" "errors" "fmt" "net" @@ -453,30 +452,8 @@ func (e *ProxyConfigEntry) Validate() error { return fmt.Errorf("invalid name (%q), only %q is supported", e.Name, ProxyConfigGlobal) } - switch e.AccessLogs.Type { - case "", StdErrLogSinkType, StdOutLogSinkType: - // OK - case FileLogSinkType: - if e.AccessLogs.Path == "" { - return errors.New("path must be specified when using file type access logs") - } - default: - return fmt.Errorf("invalid access log type: %s", e.AccessLogs.Type) - } - - if e.AccessLogs.JSONFormat != "" && e.AccessLogs.TextFormat != "" { - return errors.New("cannot specify both access log JSONFormat and TextFormat") - } - - if e.AccessLogs.Type != FileLogSinkType && e.AccessLogs.Path != "" { - return errors.New("path is only valid for file type access logs") - } - - if e.AccessLogs.JSONFormat != "" { - msg := json.RawMessage{} - if err := json.Unmarshal([]byte(e.AccessLogs.JSONFormat), &msg); err != nil { - return fmt.Errorf("invalid access log json for JSON format: %w", err) - } + if err := e.AccessLogs.Validate(); err != nil { + return err } if err := validateConfigEntryMeta(e.Meta); err != nil { @@ -1186,6 +1163,7 @@ type ServiceConfigResponse struct { TransparentProxy TransparentProxyConfig `json:",omitempty"` Mode ProxyMode `json:",omitempty"` Destination DestinationConfig `json:",omitempty"` + AccessLogs AccessLogsConfig `json:",omitempty"` Meta map[string]string `json:",omitempty"` EnvoyExtensions []EnvoyExtension `json:",omitempty"` QueryMeta diff --git a/agent/structs/connect_proxy_config.go b/agent/structs/connect_proxy_config.go index b6a31c0f34..b127b1ce10 100644 --- a/agent/structs/connect_proxy_config.go +++ b/agent/structs/connect_proxy_config.go @@ -2,6 +2,7 @@ package structs import ( "encoding/json" + "errors" "fmt" "net" @@ -39,9 +40,10 @@ const ( type LogSinkType string const ( - FileLogSinkType LogSinkType = "file" - StdErrLogSinkType LogSinkType = "stderr" - StdOutLogSinkType LogSinkType = "stdout" + DefaultLogSinkType LogSinkType = "" + FileLogSinkType LogSinkType = "file" + StdErrLogSinkType LogSinkType = "stderr" + StdOutLogSinkType LogSinkType = "stdout" ) const ( @@ -147,6 +149,9 @@ func (c TransparentProxyConfig) ToAPI() *api.TransparentProxyConfig { } func (c *TransparentProxyConfig) IsZero() bool { + if c == nil { + return true + } zeroVal := TransparentProxyConfig{} return *c == zeroVal } @@ -172,7 +177,15 @@ type AccessLogsConfig struct { TextFormat string `json:",omitempty" alias:"text_format"` } -func (c AccessLogsConfig) ToAPI() *api.AccessLogsConfig { +func (c *AccessLogsConfig) IsZero() bool { + if c == nil { + return true + } + zeroVal := AccessLogsConfig{} + return *c == zeroVal +} + +func (c *AccessLogsConfig) ToAPI() *api.AccessLogsConfig { if c.IsZero() { return nil } @@ -186,9 +199,33 @@ func (c AccessLogsConfig) ToAPI() *api.AccessLogsConfig { } } -func (c *AccessLogsConfig) IsZero() bool { - zeroVal := AccessLogsConfig{} - return *c == zeroVal +func (c *AccessLogsConfig) Validate() error { + switch c.Type { + case DefaultLogSinkType, StdErrLogSinkType, StdOutLogSinkType: + // OK + case FileLogSinkType: + if c.Path == "" { + return errors.New("path must be specified when using file type access logs") + } + default: + return fmt.Errorf("invalid access log type: %s", c.Type) + } + + if c.JSONFormat != "" && c.TextFormat != "" { + return errors.New("cannot specify both access log JSONFormat and TextFormat") + } + + if c.Type != FileLogSinkType && c.Path != "" { + return errors.New("path is only valid for file type access logs") + } + + if c.JSONFormat != "" { + msg := json.RawMessage{} + if err := json.Unmarshal([]byte(c.JSONFormat), &msg); err != nil { + return fmt.Errorf("invalid access log json for JSON format: %w", err) + } + } + return nil } // ConnectProxyConfig describes the configuration needed for any proxy managed @@ -248,6 +285,9 @@ type ConnectProxyConfig struct { // TransparentProxy defines configuration for when the proxy is in // transparent mode. TransparentProxy TransparentProxyConfig `json:",omitempty" alias:"transparent_proxy"` + + // AccessLogs configures the output and format of Envoy access logs + AccessLogs AccessLogsConfig `json:",omitempty" alias:"access_logs"` } func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) { @@ -260,6 +300,7 @@ func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) { LocalServiceSocketPathSnake string `json:"local_service_socket_path"` MeshGatewaySnake MeshGatewayConfig `json:"mesh_gateway"` TransparentProxySnake TransparentProxyConfig `json:"transparent_proxy"` + AccessLogsSnake AccessLogsConfig `json:"access_logs"` *Alias }{ Alias: (*Alias)(t), @@ -291,7 +332,24 @@ func (t *ConnectProxyConfig) UnmarshalJSON(data []byte) (err error) { if !t.TransparentProxy.DialedDirectly { t.TransparentProxy.DialedDirectly = aux.TransparentProxySnake.DialedDirectly } - + if !t.AccessLogs.Enabled { + t.AccessLogs.Enabled = aux.AccessLogsSnake.Enabled + } + if !t.AccessLogs.DisableListenerLogs { + t.AccessLogs.DisableListenerLogs = aux.AccessLogsSnake.DisableListenerLogs + } + if t.AccessLogs.Type == "" { + t.AccessLogs.Type = aux.AccessLogsSnake.Type + } + if t.AccessLogs.Path == "" { + t.AccessLogs.Path = aux.AccessLogsSnake.Path + } + if t.AccessLogs.JSONFormat == "" { + t.AccessLogs.JSONFormat = aux.AccessLogsSnake.JSONFormat + } + if t.AccessLogs.TextFormat == "" { + t.AccessLogs.TextFormat = aux.AccessLogsSnake.TextFormat + } return nil } @@ -299,6 +357,7 @@ func (c *ConnectProxyConfig) MarshalJSON() ([]byte, error) { type Alias ConnectProxyConfig out := struct { TransparentProxy *TransparentProxyConfig `json:",omitempty"` + AccessLogs *AccessLogsConfig `json:",omitempty"` Alias }{ Alias: (Alias)(*c), @@ -314,6 +373,10 @@ func (c *ConnectProxyConfig) MarshalJSON() ([]byte, error) { out.TransparentProxy = &out.Alias.TransparentProxy } + if !c.AccessLogs.IsZero() { + out.AccessLogs = &out.Alias.AccessLogs + } + return json.Marshal(&out) } @@ -334,6 +397,7 @@ func (c *ConnectProxyConfig) ToAPI() *api.AgentServiceConnectProxyConfig { Upstreams: c.Upstreams.ToAPI(), MeshGateway: c.MeshGateway.ToAPI(), Expose: c.Expose.ToAPI(), + AccessLogs: c.AccessLogs.ToAPI(), } } diff --git a/agent/structs/connect_proxy_config_test.go b/agent/structs/connect_proxy_config_test.go index b4c091fe23..f55ba1d2b9 100644 --- a/agent/structs/connect_proxy_config_test.go +++ b/agent/structs/connect_proxy_config_test.go @@ -4,8 +4,9 @@ import ( "encoding/json" "testing" - "github.com/hashicorp/consul/api" "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/api" ) func TestConnectProxyConfig_ToAPI(t *testing.T) { @@ -125,6 +126,9 @@ func TestConnectProxyConfig_MarshalJSON(t *testing.T) { // No transparent proxy config, since proxy is set to "direct" mode. // Field should be omitted from json output. // TransparentProxy: TransparentProxyConfig{}, + + // No access logs config provided, so this should be omitted. + // AccessLogs: AccessLogsConfig{}, }, want: `{ "DestinationServiceName": "api", @@ -170,6 +174,12 @@ func TestConnectProxyConfig_MarshalJSON(t *testing.T) { DialedDirectly: true, OutboundListenerPort: 16001, }, + AccessLogs: AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: FileLogSinkType, + Path: "/var/log/access.log", + }, }, want: `{ "DestinationServiceName": "billing", @@ -189,6 +199,12 @@ func TestConnectProxyConfig_MarshalJSON(t *testing.T) { }, "Expose": { "Checks": true + }, + "AccessLogs": { + "Enabled": true, + "DisableListenerLogs": true, + "Type": "file", + "Path": "/var/log/access.log" } }`, wantErr: false, diff --git a/agent/structs/structs_filtering_test.go b/agent/structs/structs_filtering_test.go index 8e63fb0dc9..cdc84db152 100644 --- a/agent/structs/structs_filtering_test.go +++ b/agent/structs/structs_filtering_test.go @@ -109,6 +109,39 @@ var expectedFieldConfigTransparentProxyConfig bexpr.FieldConfigurations = bexpr. }, } +var expectedFieldConfigAccessLogsConfig bexpr.FieldConfigurations = bexpr.FieldConfigurations{ + "Enabled": &bexpr.FieldConfiguration{ + StructFieldName: "Enabled", + CoerceFn: bexpr.CoerceBool, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual}, + }, + "DisableListenerLogs": &bexpr.FieldConfiguration{ + StructFieldName: "DisableListenerLogs", + CoerceFn: bexpr.CoerceBool, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual}, + }, + "Type": &bexpr.FieldConfiguration{ + StructFieldName: "Type", + CoerceFn: bexpr.CoerceString, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches}, + }, + "Path": &bexpr.FieldConfiguration{ + StructFieldName: "Path", + CoerceFn: bexpr.CoerceString, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches}, + }, + "JSONPath": &bexpr.FieldConfiguration{ + StructFieldName: "JSONPath", + CoerceFn: bexpr.CoerceString, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches}, + }, + "TextPath": &bexpr.FieldConfiguration{ + StructFieldName: "TextPath", + CoerceFn: bexpr.CoerceString, + SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches}, + }, +} + var expectedFieldConfigExposeConfig bexpr.FieldConfigurations = bexpr.FieldConfigurations{ "Checks": &bexpr.FieldConfiguration{ StructFieldName: "Checks", @@ -272,6 +305,10 @@ var expectedFieldConfigConnectProxyConfig bexpr.FieldConfigurations = bexpr.Fiel CoerceFn: bexpr.CoerceString, SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual, bexpr.MatchIn, bexpr.MatchNotIn, bexpr.MatchMatches, bexpr.MatchNotMatches}, }, + "AccessLogs": &bexpr.FieldConfiguration{ + StructFieldName: "AccessLogs", + SubFields: expectedFieldConfigAccessLogsConfig, + }, } var expectedFieldConfigServiceConnect bexpr.FieldConfigurations = bexpr.FieldConfigurations{ diff --git a/agent/xds/accesslogs/accesslogs.go b/agent/xds/accesslogs/accesslogs.go new file mode 100644 index 0000000000..7efa1f76c2 --- /dev/null +++ b/agent/xds/accesslogs/accesslogs.go @@ -0,0 +1,184 @@ +package accesslogs + +import ( + "fmt" + + envoy_accesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/config/accesslog/v3" + envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_fileaccesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3" + envoy_streamaccesslog_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/stream/v3" + "google.golang.org/protobuf/types/known/anypb" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/lib" +) + +const ( + defaultJSONFormat = ` +{ + "start_time": "%START_TIME%", + "route_name": "%ROUTE_NAME%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "response_code": "%RESPONSE_CODE%", + "response_flags": "%RESPONSE_FLAGS%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "duration": "%DURATION%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%", + "user_agent": "%REQ(USER-AGENT)%", + "request_id": "%REQ(X-REQUEST-ID)%", + "authority": "%REQ(:AUTHORITY)%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%" +} +` +) + +// MakeAccessLogs returns a fully-hydrated slice of Envoy Access log configurations based +// on the proxy-defaults settings. Currently only one access logger is supported. +// Listeners (as opposed to listener filters) can trigger an access log filter with the boolean. +// Tests are located in agent/xds/listeners_test.go. +func MakeAccessLogs(logs *structs.AccessLogsConfig, isListener bool) ([]*envoy_accesslog_v3.AccessLog, error) { + if logs == nil || !logs.Enabled { + return nil, nil + } + + if isListener && logs.DisableListenerLogs { + return nil, nil + } + + config, err := getLogger(logs) + if err != nil { + return nil, fmt.Errorf("failed to get logger: %w", err) + } + + var filter *envoy_accesslog_v3.AccessLogFilter + name := "Consul Listener Filter Log" + if isListener { + name = "Consul Listener Log" + filter = getListenerAccessLogFilter() + } + + newFilter := &envoy_accesslog_v3.AccessLog{ + Name: name, + Filter: filter, + ConfigType: &envoy_accesslog_v3.AccessLog_TypedConfig{ + TypedConfig: config, + }, + } + + return []*envoy_accesslog_v3.AccessLog{newFilter}, nil +} + +// getLogger returns an individual instance of an Envoy logger based on proxy-defaults +func getLogger(logs *structs.AccessLogsConfig) (*anypb.Any, error) { + logFormat, err := getLogFormat(logs) + if err != nil { + return nil, fmt.Errorf("could not get envoy log format: %w", err) + } + + switch logs.Type { + case structs.DefaultLogSinkType, structs.StdOutLogSinkType: + return getStdoutLogger(logFormat) + case structs.StdErrLogSinkType: + return getStderrLogger(logFormat) + case structs.FileLogSinkType: + return getFileLogger(logFormat, logs.Path) + default: + return nil, fmt.Errorf("unsupported log format: %s", logs.Type) + } +} + +// getLogFormat returns an Envoy log format object that is compatible with all log sinks. +// If a format is not provided in the proxy-defaults, the default JSON format is used. +func getLogFormat(logs *structs.AccessLogsConfig) (*envoy_core_v3.SubstitutionFormatString, error) { + + var format, formatType string + if logs.TextFormat == "" && logs.JSONFormat == "" { + format = defaultJSONFormat + formatType = "json" + } else if logs.JSONFormat != "" { + format = logs.JSONFormat + formatType = "json" + } else { + format = logs.TextFormat + formatType = "text" + } + + switch formatType { + case "json": + jsonFormat := structpb.Struct{} + if err := jsonFormat.UnmarshalJSON([]byte(format)); err != nil { + return nil, fmt.Errorf("could not unmarshal JSON format string: %w", err) + } + + return &envoy_core_v3.SubstitutionFormatString{ + Format: &envoy_core_v3.SubstitutionFormatString_JsonFormat{ + JsonFormat: &jsonFormat, + }, + }, nil + case "text": + textFormat := lib.EnsureTrailingNewline(format) + return &envoy_core_v3.SubstitutionFormatString{ + Format: &envoy_core_v3.SubstitutionFormatString_TextFormatSource{ + TextFormatSource: &envoy_core_v3.DataSource{ + Specifier: &envoy_core_v3.DataSource_InlineString{ + InlineString: textFormat, + }, + }, + }, + }, nil + default: + return nil, fmt.Errorf("invalid log format type") + } +} + +// getStdoutLogger returns Envoy's representation of a stdout log sink with the provided format. +func getStdoutLogger(logFormat *envoy_core_v3.SubstitutionFormatString) (*anypb.Any, error) { + return anypb.New(&envoy_streamaccesslog_v3.StdoutAccessLog{ + AccessLogFormat: &envoy_streamaccesslog_v3.StdoutAccessLog_LogFormat{ + LogFormat: logFormat, + }, + }) +} + +// getStderrLogger returns Envoy's representation of a stderr log sink with the provided format. +func getStderrLogger(logFormat *envoy_core_v3.SubstitutionFormatString) (*anypb.Any, error) { + return anypb.New(&envoy_streamaccesslog_v3.StderrAccessLog{ + AccessLogFormat: &envoy_streamaccesslog_v3.StderrAccessLog_LogFormat{ + LogFormat: logFormat, + }, + }) +} + +// getFileLogger returns Envoy's representation of a file log sink with the provided format and path to a file. +func getFileLogger(logFormat *envoy_core_v3.SubstitutionFormatString, path string) (*anypb.Any, error) { + return anypb.New(&envoy_fileaccesslog_v3.FileAccessLog{ + AccessLogFormat: &envoy_fileaccesslog_v3.FileAccessLog_LogFormat{ + LogFormat: logFormat, + }, + Path: path, + }) +} + +// getListenerAccessLogFilter returns a filter that will be used on listeners to decide when a log is emitted. +// Set to "NR" which corresponds to "No route configured for a given request in addition +// to 404 response code, or no matching filter chain for a downstream connection." +func getListenerAccessLogFilter() *envoy_accesslog_v3.AccessLogFilter { + return &envoy_accesslog_v3.AccessLogFilter{ + FilterSpecifier: &envoy_accesslog_v3.AccessLogFilter_ResponseFlagFilter{ + ResponseFlagFilter: &envoy_accesslog_v3.ResponseFlagFilter{Flags: []string{"NR"}}, + }, + } +} diff --git a/agent/xds/listeners.go b/agent/xds/listeners.go index e876b2dd1f..784dc656d5 100644 --- a/agent/xds/listeners.go +++ b/agent/xds/listeners.go @@ -11,14 +11,13 @@ import ( "strings" "time" - envoy_extensions_filters_listener_http_inspector_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3" - envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_grpc_http1_bridge_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_http1_bridge/v3" envoy_grpc_stats_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_stats/v3" envoy_http_router_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" + envoy_extensions_filters_listener_http_inspector_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3" envoy_original_dst_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3" envoy_tls_inspector_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" envoy_connection_limit_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/connection_limit/v3" @@ -33,6 +32,7 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes/any" "github.com/golang/protobuf/ptypes/wrappers" + "github.com/hashicorp/go-hclog" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" @@ -40,6 +40,7 @@ import ( "github.com/hashicorp/consul/agent/connect" "github.com/hashicorp/consul/agent/proxycfg" "github.com/hashicorp/consul/agent/structs" + "github.com/hashicorp/consul/agent/xds/accesslogs" "github.com/hashicorp/consul/lib" "github.com/hashicorp/consul/lib/stringslice" "github.com/hashicorp/consul/proto/pbpeering" @@ -96,7 +97,15 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. return nil, err } - outboundListener = makePortListener(OutboundListenerName, "127.0.0.1", port, envoy_core_v3.TrafficDirection_OUTBOUND) + opts := makeListenerOpts{ + name: OutboundListenerName, + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: "127.0.0.1", + port: port, + direction: envoy_core_v3.TrafficDirection_OUTBOUND, + logger: s.Logger, + } + outboundListener = makeListener(opts) outboundListener.FilterChains = make([]*envoy_listener_v3.FilterChain, 0) outboundListener.ListenerFilters = []*envoy_listener_v3.ListenerFilter{ @@ -178,6 +187,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. // Generate the upstream listeners for when they are explicitly set with a local bind port or socket path if upstreamCfg != nil && upstreamCfg.HasLocalPortOrSocket() { filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: uid.EnvoyID(), clusterName: targetClusterData.clusterName, filterName: filterName, @@ -189,7 +199,14 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. return nil, err } - upstreamListener := makeListener(uid.EnvoyID(), upstreamCfg, envoy_core_v3.TrafficDirection_OUTBOUND) + opts := makeListenerOpts{ + name: uid.EnvoyID(), + accessLogs: cfgSnap.Proxy.AccessLogs, + direction: envoy_core_v3.TrafficDirection_OUTBOUND, + logger: s.Logger, + upstream: upstreamCfg, + } + upstreamListener := makeListener(opts) s.injectConnectionBalanceConfig(cfg.BalanceOutboundConnections, upstreamListener) upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{ filterChain, @@ -205,6 +222,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. // as we do for explicit upstreams above. filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: uid.EnvoyID(), clusterName: targetClusterData.clusterName, filterName: filterName, @@ -278,6 +296,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. const name = "~http" // name used for the shared route name routeName := clusterNameForDestination(cfgSnap, name, fmt.Sprintf("%d", svcConfig.Destination.Port), svcConfig.NamespaceOrDefault(), svcConfig.PartitionOrDefault()) filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: routeName, filterName: routeName, protocol: svcConfig.Protocol, @@ -295,6 +314,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. clusterName := clusterNameForDestination(cfgSnap, uid.Name, address, uid.NamespaceOrDefault(), uid.PartitionOrDefault()) filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: uid.EnvoyID(), clusterName: clusterName, filterName: clusterName, @@ -371,6 +391,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. // Generate the upstream listeners for when they are explicitly set with a local bind port or socket path if upstreamCfg != nil && upstreamCfg.HasLocalPortOrSocket() { filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, clusterName: clusterName, filterName: fmt.Sprintf("%s.%s.%s", upstreamCfg.DestinationName, @@ -385,7 +406,14 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. return nil, err } - upstreamListener := makeListener(uid.EnvoyID(), upstreamCfg, envoy_core_v3.TrafficDirection_OUTBOUND) + opts := makeListenerOpts{ + name: uid.EnvoyID(), + accessLogs: cfgSnap.Proxy.AccessLogs, + direction: envoy_core_v3.TrafficDirection_OUTBOUND, + logger: s.Logger, + upstream: upstreamCfg, + } + upstreamListener := makeListener(opts) s.injectConnectionBalanceConfig(cfg.BalanceOutboundConnections, upstreamListener) upstreamListener.FilterChains = []*envoy_listener_v3.FilterChain{ @@ -402,6 +430,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. // as we do for explicit upstreams above. filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: uid.EnvoyID(), clusterName: clusterName, filterName: fmt.Sprintf("%s.%s.%s", @@ -468,6 +497,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. filterName := fmt.Sprintf("%s.%s.%s.%s", uid.Name, uid.NamespaceOrDefault(), uid.PartitionOrDefault(), cfgSnap.Datacenter) filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, clusterName: "passthrough~" + sni, filterName: filterName, protocol: "tcp", @@ -519,6 +549,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. !meshConf.TransparentProxy.MeshDestinationsOnly { filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, clusterName: OriginalDestinationClusterName, filterName: OriginalDestinationClusterName, protocol: "tcp", @@ -561,11 +592,19 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg. continue } - upstreamListener := makeListener(uid.EnvoyID(), u, envoy_core_v3.TrafficDirection_OUTBOUND) + opts := makeListenerOpts{ + name: uid.EnvoyID(), + accessLogs: cfgSnap.Proxy.AccessLogs, + direction: envoy_core_v3.TrafficDirection_OUTBOUND, + logger: s.Logger, + upstream: u, + } + upstreamListener := makeListener(opts) s.injectConnectionBalanceConfig(cfg.BalanceOutboundConnections, upstreamListener) filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ // TODO (SNI partition) add partition for upstream SNI + accessLogs: &cfgSnap.Proxy.AccessLogs, clusterName: connect.UpstreamSNI(u, "", cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain), filterName: uid.EnvoyID(), routeName: uid.EnvoyID(), @@ -848,39 +887,66 @@ func (s *ResourceGenerator) listenersFromSnapshotGateway(cfgSnap *proxycfg.Confi // changes them, we actually create a whole new listener on the new address and // port. Envoy should take care of closing the old one once it sees it's no // longer in the config. -func makeListener(name string, upstream *structs.Upstream, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { - if upstream.LocalBindPort == 0 && upstream.LocalBindSocketPath != "" { - return makePipeListener(name, upstream.LocalBindSocketPath, upstream.LocalBindSocketMode, trafficDirection) - } - - return makePortListenerWithDefault(name, upstream.LocalBindAddress, upstream.LocalBindPort, trafficDirection) +type makeListenerOpts struct { + addr string + accessLogs structs.AccessLogsConfig + logger hclog.Logger + mode string + name string + path string + port int + direction envoy_core_v3.TrafficDirection + upstream *structs.Upstream } -func makePortListener(name, addr string, port int, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { +func makeListener(opts makeListenerOpts) *envoy_listener_v3.Listener { + if opts.upstream != nil && opts.upstream.LocalBindPort == 0 && opts.upstream.LocalBindSocketPath != "" { + opts.path = opts.upstream.LocalBindSocketPath + opts.mode = opts.upstream.LocalBindSocketMode + return makePipeListener(opts) + } + if opts.upstream != nil { + opts.port = opts.upstream.LocalBindPort + opts.addr = opts.upstream.LocalBindAddress + return makeListenerWithDefault(opts) + } + + return makeListenerWithDefault(opts) +} + +func makeListenerWithDefault(opts makeListenerOpts) *envoy_listener_v3.Listener { + if opts.addr == "" { + opts.addr = "127.0.0.1" + } + accessLog, err := accesslogs.MakeAccessLogs(&opts.accessLogs, true) + if err != nil && opts.logger != nil { + // Since access logging is non-essential for routing, warn and move on + opts.logger.Warn("error generating access log xds", err) + } return &envoy_listener_v3.Listener{ - Name: fmt.Sprintf("%s:%s:%d", name, addr, port), - Address: makeAddress(addr, port), - TrafficDirection: trafficDirection, + Name: fmt.Sprintf("%s:%s:%d", opts.name, opts.addr, opts.port), + AccessLog: accessLog, + Address: makeAddress(opts.addr, opts.port), + TrafficDirection: opts.direction, } } -func makePortListenerWithDefault(name, addr string, port int, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { - if addr == "" { - addr = "127.0.0.1" - } - return makePortListener(name, addr, port, trafficDirection) -} - -func makePipeListener(name, path string, mode_str string, trafficDirection envoy_core_v3.TrafficDirection) *envoy_listener_v3.Listener { +func makePipeListener(opts makeListenerOpts) *envoy_listener_v3.Listener { // We've already validated this, so it should not fail. - mode, err := strconv.ParseUint(mode_str, 0, 32) + modeInt, err := strconv.ParseUint(opts.mode, 0, 32) if err != nil { - mode = 0 + modeInt = 0 + } + accessLog, err := accesslogs.MakeAccessLogs(&opts.accessLogs, true) + if err != nil && opts.logger != nil { + // Since access logging is non-essential for routing, warn and move on + opts.logger.Warn("error generating access log xds", err) } return &envoy_listener_v3.Listener{ - Name: fmt.Sprintf("%s:%s", name, path), - Address: makePipeAddress(path, uint32(mode)), - TrafficDirection: trafficDirection, + Name: fmt.Sprintf("%s:%s", opts.name, opts.path), + AccessLog: accessLog, + Address: makePipeAddress(opts.path, uint32(modeInt)), + TrafficDirection: opts.direction, } } @@ -1259,7 +1325,15 @@ func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot port = cfg.BindPort } - l = makePortListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) + opts := makeListenerOpts{ + name: name, + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: addr, + port: port, + direction: envoy_core_v3.TrafficDirection_INBOUND, + logger: s.Logger, + } + l = makeListener(opts) s.injectConnectionBalanceConfig(cfg.BalanceInboundConnections, l) var tracing *envoy_http_v3.HttpConnectionManager_Tracing @@ -1277,6 +1351,8 @@ func (s *ResourceGenerator) makeInboundListener(cfgSnap *proxycfg.ConfigSnapshot requestTimeoutMs: cfg.LocalRequestTimeoutMs, idleTimeoutMs: cfg.LocalIdleTimeoutMs, tracing: tracing, + accessLogs: &cfgSnap.Proxy.AccessLogs, + logger: s.Logger, } if useHTTPFilter { filterOpts.httpAuthzFilter, err = makeRBACHTTPFilter( @@ -1380,11 +1456,19 @@ func (s *ResourceGenerator) makeExposedCheckListener(cfgSnap *proxycfg.ConfigSna strippedPath := r.ReplaceAllString(path.Path, "") listenerName := fmt.Sprintf("exposed_path_%s", strippedPath) - l := makePortListener(listenerName, addr, path.ListenerPort, envoy_core_v3.TrafficDirection_INBOUND) + listenerOpts := makeListenerOpts{ + name: listenerName, + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: addr, + port: path.ListenerPort, + direction: envoy_core_v3.TrafficDirection_INBOUND, + logger: s.Logger, + } + l := makeListener(listenerOpts) filterName := fmt.Sprintf("exposed_path_filter_%s_%d", strippedPath, path.ListenerPort) - opts := listenerFilterOpts{ + filterOpts := listenerFilterOpts{ useRDS: false, protocol: path.Protocol, filterName: filterName, @@ -1393,9 +1477,11 @@ func (s *ResourceGenerator) makeExposedCheckListener(cfgSnap *proxycfg.ConfigSna statPrefix: "", routePath: path.Path, httpAuthzFilter: nil, + accessLogs: &cfgSnap.Proxy.AccessLogs, + logger: s.Logger, // in the exposed check listener we don't set the tracing configuration } - f, err := makeListenerFilter(opts) + f, err := makeListenerFilter(filterOpts) if err != nil { return nil, err } @@ -1446,7 +1532,16 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener( name, addr string, port int, ) (*envoy_listener_v3.Listener, error) { - l := makePortListener(name, addr, port, envoy_core_v3.TrafficDirection_INBOUND) + + listenerOpts := makeListenerOpts{ + name: name, + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: addr, + port: port, + direction: envoy_core_v3.TrafficDirection_INBOUND, + logger: s.Logger, + } + l := makeListener(listenerOpts) tlsInspector, err := makeTLSInspectorListenerFilter() if err != nil { @@ -1555,7 +1650,14 @@ func (s *ResourceGenerator) makeTerminatingGatewayListener( // This fallback catch-all filter ensures a listener will be present for health checks to pass // Envoy will reset these connections since known endpoints are caught by filter chain matches above - tcpProxy, err := makeTCPProxyFilter(name, "", "terminating_gateway.") + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: "", + filterName: name, + logger: s.Logger, + statPrefix: "terminating_gateway.", + } + tcpProxy, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1650,6 +1752,8 @@ func (s *ResourceGenerator) makeFilterChainTerminatingGateway(cfgSnap *proxycfg. statPrefix: "upstream.", routePath: "", tracing: tracing, + accessLogs: &cfgSnap.Proxy.AccessLogs, + logger: s.Logger, } if useHTTPFilter { @@ -1701,7 +1805,14 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, // The cluster name here doesn't matter as the sni_cluster // filter will fill it in for us. - tcpProxy, err := makeTCPProxyFilter(name, "", "mesh_gateway_local.") + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: "", + filterName: name, + logger: s.Logger, + statPrefix: "mesh_gateway_local.", + } + tcpProxy, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1713,7 +1824,15 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, }, } - l := makePortListener(name, addr, port, envoy_core_v3.TrafficDirection_UNSPECIFIED) + opts := makeListenerOpts{ + name: name, + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: addr, + port: port, + direction: envoy_core_v3.TrafficDirection_UNSPECIFIED, + logger: s.Logger, + } + l := makeListener(opts) l.ListenerFilters = []*envoy_listener_v3.ListenerFilter{tlsInspector} for _, svc := range cfgSnap.MeshGatewayValidExportedServices() { @@ -1739,7 +1858,15 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, clusterName := connect.GatewaySNI(key.Datacenter, key.Partition, cfgSnap.Roots.TrustDomain) filterName := fmt.Sprintf("%s.%s", name, key.String()) - dcTCPProxy, err := makeTCPProxyFilter(filterName, clusterName, "mesh_gateway_remote.") + + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: clusterName, + filterName: filterName, + logger: s.Logger, + statPrefix: "mesh_gateway_remote.", + } + dcTCPProxy, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1768,7 +1895,14 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, } clusterName := cfgSnap.ServerSNIFn(key.Datacenter, "") filterName := fmt.Sprintf("%s.%s", name, key.String()) - dcTCPProxy, err := makeTCPProxyFilter(filterName, clusterName, "mesh_gateway_remote.") + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: clusterName, + filterName: filterName, + logger: s.Logger, + statPrefix: "mesh_gateway_remote.", + } + dcTCPProxy, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1789,7 +1923,14 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, clusterName := cfgSnap.ServerSNIFn(cfgSnap.Datacenter, srv.Node.Node) filterName := fmt.Sprintf("%s.%s", name, cfgSnap.Datacenter) - dcTCPProxy, err := makeTCPProxyFilter(filterName, clusterName, "mesh_gateway_local_server.") + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: clusterName, + filterName: filterName, + logger: s.Logger, + statPrefix: "mesh_gateway_local_server.", + } + dcTCPProxy, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1820,7 +1961,14 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, clusterName := connect.PeeringServerSAN(cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain) filterName := fmt.Sprintf("%s.%s", name, cfgSnap.Datacenter) - filter, err := makeTCPProxyFilter(filterName, clusterName, "mesh_gateway_local_peering_server.") + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: clusterName, + filterName: filterName, + logger: s.Logger, + statPrefix: "mesh_gateway_local_peering_server.", + } + filter, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1841,7 +1989,14 @@ func (s *ResourceGenerator) makeMeshGatewayListener(name, addr string, port int, var peerServerFilterChains []*envoy_listener_v3.FilterChain for name := range cfgSnap.MeshGateway.PeerServers { - dcTCPProxy, err := makeTCPProxyFilter(name, name, "mesh_gateway_remote_peering_servers.") + filterOpts := listenerFilterOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, + cluster: name, + filterName: name, + logger: s.Logger, + statPrefix: "mesh_gateway_remote_peering_servers.", + } + dcTCPProxy, err := makeTCPProxyFilter(filterOpts) if err != nil { return nil, err } @@ -1899,6 +2054,7 @@ func (s *ResourceGenerator) makeMeshGatewayPeerFilterChain( filterName := fmt.Sprintf("%s.%s.%s.%s", chain.ServiceName, chain.Namespace, chain.Partition, chain.Datacenter) filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: uid.EnvoyID(), clusterName: clusterName, filterName: filterName, @@ -1947,6 +2103,7 @@ func (s *ResourceGenerator) makeMeshGatewayPeerFilterChain( } type filterChainOpts struct { + accessLogs *structs.AccessLogsConfig routeName string clusterName string filterName string @@ -1973,6 +2130,8 @@ func (s *ResourceGenerator) makeUpstreamFilterChain(opts filterChainOpts) (*envo forwardClientDetails: opts.forwardClientDetails, forwardClientPolicy: opts.forwardClientPolicy, tracing: opts.tracing, + accessLogs: opts.accessLogs, + logger: s.Logger, }) if err != nil { return nil, err @@ -2107,20 +2266,24 @@ func (s *ResourceGenerator) getAndModifyUpstreamConfigForPeeredListener( } type listenerFilterOpts struct { - useRDS bool - protocol string - filterName string - routeName string - cluster string - statPrefix string - routePath string - requestTimeoutMs *int - idleTimeoutMs *int - ingressGateway bool - httpAuthzFilter *envoy_http_v3.HttpFilter + // All listener filters + accessLogs *structs.AccessLogsConfig + cluster string + filterName string + logger hclog.Logger + protocol string + statPrefix string + + // HTTP listener filter options forwardClientDetails bool forwardClientPolicy envoy_http_v3.HttpConnectionManager_ForwardClientCertDetails + httpAuthzFilter *envoy_http_v3.HttpFilter + idleTimeoutMs *int + requestTimeoutMs *int + routeName string + routePath string tracing *envoy_http_v3.HttpConnectionManager_Tracing + useRDS bool } func makeListenerFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error) { @@ -2135,7 +2298,7 @@ func makeListenerFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, err } else if opts.cluster == "" { return nil, fmt.Errorf("cluster name is required for a tcp proxy filter") } - return makeTCPProxyFilter(opts.filterName, opts.cluster, opts.statPrefix) + return makeTCPProxyFilter(opts) } } @@ -2157,10 +2320,16 @@ func makeSNIClusterFilter() (*envoy_listener_v3.Filter, error) { return makeFilter("envoy.filters.network.sni_cluster", &envoy_sni_cluster_v3.SniCluster{}) } -func makeTCPProxyFilter(filterName, cluster, statPrefix string) (*envoy_listener_v3.Filter, error) { +func makeTCPProxyFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error) { + accessLogs, err := accesslogs.MakeAccessLogs(opts.accessLogs, false) + if err != nil && opts.logger != nil { + opts.logger.Warn("could not make access log xds for tcp proxy", err) + } + cfg := &envoy_tcp_proxy_v3.TcpProxy{ - StatPrefix: makeStatPrefix(statPrefix, filterName), - ClusterSpecifier: &envoy_tcp_proxy_v3.TcpProxy_Cluster{Cluster: cluster}, + AccessLog: accessLogs, + ClusterSpecifier: &envoy_tcp_proxy_v3.TcpProxy_Cluster{Cluster: opts.cluster}, + StatPrefix: makeStatPrefix(opts.statPrefix, opts.filterName), } return makeFilter("envoy.filters.network.tcp_proxy", cfg) } @@ -2199,7 +2368,13 @@ func makeHTTPFilter(opts listenerFilterOpts) (*envoy_listener_v3.Filter, error) return nil, err } + accessLogs, err := accesslogs.MakeAccessLogs(opts.accessLogs, false) + if err != nil && opts.logger != nil { + opts.logger.Warn("could not make access log xds for http connection manager", err) + } + cfg := &envoy_http_v3.HttpConnectionManager{ + AccessLog: accessLogs, StatPrefix: makeStatPrefix(opts.statPrefix, opts.filterName), CodecType: envoy_http_v3.HttpConnectionManager_AUTO, HttpFilters: []*envoy_http_v3.HttpFilter{ diff --git a/agent/xds/listeners_ingress.go b/agent/xds/listeners_ingress.go index 2b9be1de1b..ea3bee476f 100644 --- a/agent/xds/listeners_ingress.go +++ b/agent/xds/listeners_ingress.go @@ -63,8 +63,17 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap filterName := fmt.Sprintf("%s.%s.%s.%s", chain.ServiceName, chain.Namespace, chain.Partition, chain.Datacenter) - l := makePortListenerWithDefault(uid.EnvoyID(), address, u.LocalBindPort, envoy_core_v3.TrafficDirection_OUTBOUND) + opts := makeListenerOpts{ + name: uid.EnvoyID(), + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: address, + port: u.LocalBindPort, + direction: envoy_core_v3.TrafficDirection_OUTBOUND, + logger: s.Logger, + } + l := makeListener(opts) filterChain, err := s.makeUpstreamFilterChain(filterChainOpts{ + accessLogs: &cfgSnap.Proxy.AccessLogs, routeName: uid.EnvoyID(), useRDS: useRDS, clusterName: clusterName, @@ -82,8 +91,17 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap } else { // If multiple upstreams share this port, make a special listener for the protocol. - listener := makePortListener(listenerKey.Protocol, address, listenerKey.Port, envoy_core_v3.TrafficDirection_OUTBOUND) - opts := listenerFilterOpts{ + listenerOpts := makeListenerOpts{ + name: listenerKey.Protocol, + accessLogs: cfgSnap.Proxy.AccessLogs, + addr: address, + port: listenerKey.Port, + direction: envoy_core_v3.TrafficDirection_OUTBOUND, + logger: s.Logger, + } + listener := makeListener(listenerOpts) + + filterOpts := listenerFilterOpts{ useRDS: true, protocol: listenerKey.Protocol, filterName: listenerKey.RouteName(), @@ -92,11 +110,13 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap statPrefix: "ingress_upstream_", routePath: "", httpAuthzFilter: nil, + accessLogs: &cfgSnap.Proxy.AccessLogs, + logger: s.Logger, } // Generate any filter chains needed for services with custom TLS certs // via SDS. - sniFilterChains, err := makeSDSOverrideFilterChains(cfgSnap, listenerKey, opts) + sniFilterChains, err := makeSDSOverrideFilterChains(cfgSnap, listenerKey, filterOpts) if err != nil { return nil, err } @@ -115,7 +135,7 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap // See if there are other services that didn't have specific SNI-matching // filter chains. If so add a default filterchain to serve them. if len(sniFilterChains) < len(upstreams) { - defaultFilter, err := makeListenerFilter(opts) + defaultFilter, err := makeListenerFilter(filterOpts) if err != nil { return nil, err } diff --git a/agent/xds/listeners_test.go b/agent/xds/listeners_test.go index 309dfaab5d..d94c117df4 100644 --- a/agent/xds/listeners_test.go +++ b/agent/xds/listeners_test.go @@ -842,6 +842,48 @@ func TestListenersFromSnapshot(t *testing.T) { }, nil) }, }, + { + name: "access-logs-defaults", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + // This should be passed into the snapshot through proxy-defaults + ns.Proxy.AccessLogs = structs.AccessLogsConfig{ + Enabled: true, + } + }, + nil) + }, + }, + { + name: "access-logs-json-file", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + // This should be passed into the snapshot through proxy-defaults + ns.Proxy.AccessLogs = structs.AccessLogsConfig{ + Enabled: true, + Type: structs.FileLogSinkType, + Path: "/tmp/accesslog.txt", + JSONFormat: "{ \"custom_start_time\": \"%START_TIME%\" }", + } + }, + nil) + }, + }, + { + name: "access-logs-text-stderr-disablelistenerlogs", + create: func(t testinf.T) *proxycfg.ConfigSnapshot { + return proxycfg.TestConfigSnapshot(t, func(ns *structs.NodeService) { + // This should be passed into the snapshot through proxy-defaults + ns.Proxy.AccessLogs = structs.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: structs.StdErrLogSinkType, + TextFormat: "CUSTOM FORMAT %START_TIME%", + } + }, + nil) + }, + }, } latestEnvoyVersion := proxysupport.EnvoyVersions[0] diff --git a/agent/xds/testdata/listeners/access-logs-defaults.latest.golden b/agent/xds/testdata/listeners/access-logs-defaults.latest.golden new file mode 100644 index 0000000000..6a6bb2b7ed --- /dev/null +++ b/agent/xds/testdata/listeners/access-logs-defaults.latest.golden @@ -0,0 +1,356 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog", + "logFormat": { + "jsonFormat": { + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "response_flags": "%RESPONSE_FLAGS%", + "route_name": "%ROUTE_NAME%", + "start_time": "%START_TIME%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" + } + } + } + } + ] + } + } + ] + } + ], + "trafficDirection": "OUTBOUND", + "accessLog": [ + { + "name": "Consul Listener Log", + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog", + "logFormat": { + "jsonFormat": { + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "response_flags": "%RESPONSE_FLAGS%", + "route_name": "%ROUTE_NAME%", + "start_time": "%START_TIME%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" + } + } + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog", + "logFormat": { + "jsonFormat": { + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "response_flags": "%RESPONSE_FLAGS%", + "route_name": "%ROUTE_NAME%", + "start_time": "%START_TIME%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" + } + } + } + } + ] + } + } + ] + } + ], + "trafficDirection": "OUTBOUND", + "accessLog": [ + { + "name": "Consul Listener Log", + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog", + "logFormat": { + "jsonFormat": { + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "response_flags": "%RESPONSE_FLAGS%", + "route_name": "%ROUTE_NAME%", + "start_time": "%START_TIME%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" + } + } + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog", + "logFormat": { + "jsonFormat": { + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "response_flags": "%RESPONSE_FLAGS%", + "route_name": "%ROUTE_NAME%", + "start_time": "%START_TIME%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" + } + } + } + } + ] + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND", + "accessLog": [ + { + "name": "Consul Listener Log", + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog", + "logFormat": { + "jsonFormat": { + "authority": "%REQ(:AUTHORITY)%", + "bytes_received": "%BYTES_RECEIVED%", + "bytes_sent": "%BYTES_SENT%", + "connection_termination_details": "%CONNECTION_TERMINATION_DETAILS%", + "downstream_local_address": "%DOWNSTREAM_LOCAL_ADDRESS%", + "downstream_remote_address": "%DOWNSTREAM_REMOTE_ADDRESS%", + "duration": "%DURATION%", + "method": "%REQ(:METHOD)%", + "path": "%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%", + "protocol": "%PROTOCOL%", + "request_id": "%REQ(X-REQUEST-ID)%", + "requested_server_name": "%REQUESTED_SERVER_NAME%", + "response_code": "%RESPONSE_CODE%", + "response_code_details": "%RESPONSE_CODE_DETAILS%", + "response_flags": "%RESPONSE_FLAGS%", + "route_name": "%ROUTE_NAME%", + "start_time": "%START_TIME%", + "upstream_cluster": "%UPSTREAM_CLUSTER%", + "upstream_host": "%UPSTREAM_HOST%", + "upstream_local_address": "%UPSTREAM_LOCAL_ADDRESS%", + "upstream_service_time": "%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%", + "upstream_transport_failure_reason": "%UPSTREAM_TRANSPORT_FAILURE_REASON%", + "user_agent": "%REQ(USER-AGENT)%", + "x_forwarded_for": "%REQ(X-FORWARDED-FOR)%" + } + } + } + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/access-logs-json-file.latest.golden b/agent/xds/testdata/listeners/access-logs-json-file.latest.golden new file mode 100644 index 0000000000..c4cfdd467f --- /dev/null +++ b/agent/xds/testdata/listeners/access-logs-json-file.latest.golden @@ -0,0 +1,224 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/tmp/accesslog.txt", + "logFormat": { + "jsonFormat": { + "custom_start_time": "%START_TIME%" + } + } + } + } + ] + } + } + ] + } + ], + "trafficDirection": "OUTBOUND", + "accessLog": [ + { + "name": "Consul Listener Log", + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/tmp/accesslog.txt", + "logFormat": { + "jsonFormat": { + "custom_start_time": "%START_TIME%" + } + } + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/tmp/accesslog.txt", + "logFormat": { + "jsonFormat": { + "custom_start_time": "%START_TIME%" + } + } + } + } + ] + } + } + ] + } + ], + "trafficDirection": "OUTBOUND", + "accessLog": [ + { + "name": "Consul Listener Log", + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/tmp/accesslog.txt", + "logFormat": { + "jsonFormat": { + "custom_start_time": "%START_TIME%" + } + } + } + } + ] + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/tmp/accesslog.txt", + "logFormat": { + "jsonFormat": { + "custom_start_time": "%START_TIME%" + } + } + } + } + ] + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND", + "accessLog": [ + { + "name": "Consul Listener Log", + "filter": { + "responseFlagFilter": { + "flags": [ + "NR" + ] + } + }, + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog", + "path": "/tmp/accesslog.txt", + "logFormat": { + "jsonFormat": { + "custom_start_time": "%START_TIME%" + } + } + } + } + ] + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/agent/xds/testdata/listeners/access-logs-text-stderr-disablelistenerlogs.latest.golden b/agent/xds/testdata/listeners/access-logs-text-stderr-disablelistenerlogs.latest.golden new file mode 100644 index 0000000000..2a83b761df --- /dev/null +++ b/agent/xds/testdata/listeners/access-logs-text-stderr-disablelistenerlogs.latest.golden @@ -0,0 +1,158 @@ +{ + "versionInfo": "00000001", + "resources": [ + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "db:127.0.0.1:9191", + "address": { + "socketAddress": { + "address": "127.0.0.1", + "portValue": 9191 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.db.default.default.dc1", + "cluster": "db.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StderrAccessLog", + "logFormat": { + "textFormatSource": { + "inlineString": "CUSTOM FORMAT %START_TIME%\n" + } + } + } + } + ] + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "prepared_query:geo-cache:127.10.10.10:8181", + "address": { + "socketAddress": { + "address": "127.10.10.10", + "portValue": 8181 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "upstream.prepared_query_geo-cache", + "cluster": "geo-cache.default.dc1.query.11111111-2222-3333-4444-555555555555.consul", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StderrAccessLog", + "logFormat": { + "textFormatSource": { + "inlineString": "CUSTOM FORMAT %START_TIME%\n" + } + } + } + } + ] + } + } + ] + } + ], + "trafficDirection": "OUTBOUND" + }, + { + "@type": "type.googleapis.com/envoy.config.listener.v3.Listener", + "name": "public_listener:0.0.0.0:9999", + "address": { + "socketAddress": { + "address": "0.0.0.0", + "portValue": 9999 + } + }, + "filterChains": [ + { + "filters": [ + { + "name": "envoy.filters.network.rbac", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.rbac.v3.RBAC", + "rules": { + + }, + "statPrefix": "connect_authz" + } + }, + { + "name": "envoy.filters.network.tcp_proxy", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy", + "statPrefix": "public_listener", + "cluster": "local_app", + "accessLog": [ + { + "name": "Consul Listener Filter Log", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StderrAccessLog", + "logFormat": { + "textFormatSource": { + "inlineString": "CUSTOM FORMAT %START_TIME%\n" + } + } + } + } + ] + } + } + ], + "transportSocket": { + "name": "tls", + "typedConfig": { + "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext", + "commonTlsContext": { + "tlsParams": { + + }, + "tlsCertificates": [ + { + "certificateChain": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICjDCCAjKgAwIBAgIIC5llxGV1gB8wCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowDjEMMAoG\nA1UEAxMDd2ViMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEADPv1RHVNRfa2VKR\nAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Favq5E0ivpNtv1QnFhxtPd7d5k4e+T7\nSkW1TaOCAXIwggFuMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcD\nAgYIKwYBBQUHAwEwDAYDVR0TAQH/BAIwADBoBgNVHQ4EYQRfN2Q6MDc6ODc6M2E6\nNDA6MTk6NDc6YzM6NWE6YzA6YmE6NjI6ZGY6YWY6NGI6ZDQ6MDU6MjU6NzY6M2Q6\nNWE6OGQ6MTY6OGQ6Njc6NWU6MmU6YTA6MzQ6N2Q6ZGM6ZmYwagYDVR0jBGMwYYBf\nZDE6MTE6MTE6YWM6MmE6YmE6OTc6YjI6M2Y6YWM6N2I6YmQ6ZGE6YmU6YjE6OGE6\nZmM6OWE6YmE6YjU6YmM6ODM6ZTc6NWU6NDE6NmY6ZjI6NzM6OTU6NTg6MGM6ZGIw\nWQYDVR0RBFIwUIZOc3BpZmZlOi8vMTExMTExMTEtMjIyMi0zMzMzLTQ0NDQtNTU1\nNTU1NTU1NTU1LmNvbnN1bC9ucy9kZWZhdWx0L2RjL2RjMS9zdmMvd2ViMAoGCCqG\nSM49BAMCA0gAMEUCIGC3TTvvjj76KMrguVyFf4tjOqaSCRie3nmHMRNNRav7AiEA\npY0heYeK9A6iOLrzqxSerkXXQyj5e9bE4VgUnxgPU6g=\n-----END CERTIFICATE-----\n" + }, + "privateKey": { + "inlineString": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIMoTkpRggp3fqZzFKh82yS4LjtJI+XY+qX/7DefHFrtdoAoGCCqGSM49\nAwEHoUQDQgAEADPv1RHVNRfa2VKRAB16b6rZnEt7tuhaxCFpQXPj7M2omb0B9Fav\nq5E0ivpNtv1QnFhxtPd7d5k4e+T7SkW1TQ==\n-----END EC PRIVATE KEY-----\n" + } + } + ], + "validationContext": { + "trustedCa": { + "inlineString": "-----BEGIN CERTIFICATE-----\nMIICXDCCAgKgAwIBAgIICpZq70Z9LyUwCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ\nVGVzdCBDQSAyMB4XDTE5MDMyMjEzNTgyNloXDTI5MDMyMjEzNTgyNlowFDESMBAG\nA1UEAxMJVGVzdCBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIhywH1gx\nAsMwuF3ukAI5YL2jFxH6Usnma1HFSfVyxbXX1/uoZEYrj8yCAtdU2yoHETyd+Zx2\nThhRLP79pYegCaOCATwwggE4MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTAD\nAQH/MGgGA1UdDgRhBF9kMToxMToxMTphYzoyYTpiYTo5NzpiMjozZjphYzo3Yjpi\nZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1ZTo0MTo2ZjpmMjo3\nMzo5NTo1ODowYzpkYjBqBgNVHSMEYzBhgF9kMToxMToxMTphYzoyYTpiYTo5Nzpi\nMjozZjphYzo3YjpiZDpkYTpiZTpiMTo4YTpmYzo5YTpiYTpiNTpiYzo4MzplNzo1\nZTo0MTo2ZjpmMjo3Mzo5NTo1ODowYzpkYjA/BgNVHREEODA2hjRzcGlmZmU6Ly8x\nMTExMTExMS0yMjIyLTMzMzMtNDQ0NC01NTU1NTU1NTU1NTUuY29uc3VsMAoGCCqG\nSM49BAMCA0gAMEUCICOY0i246rQHJt8o8Oya0D5PLL1FnmsQmQqIGCi31RwnAiEA\noR5f6Ku+cig2Il8T8LJujOp2/2A72QcHZA57B13y+8o=\n-----END CERTIFICATE-----\n" + } + } + }, + "requireClientCertificate": true + } + } + } + ], + "trafficDirection": "INBOUND" + } + ], + "typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener", + "nonce": "00000001" +} \ No newline at end of file diff --git a/api/agent.go b/api/agent.go index 39eccc296f..c343308b7b 100644 --- a/api/agent.go +++ b/api/agent.go @@ -130,6 +130,7 @@ type AgentServiceConnectProxyConfig struct { Upstreams []Upstream `json:",omitempty"` MeshGateway MeshGatewayConfig `json:",omitempty"` Expose ExposeConfig `json:",omitempty"` + AccessLogs *AccessLogsConfig `json:",omitempty"` } const ( diff --git a/api/config_entry.go b/api/config_entry.go index 557db8fef0..0b905a1822 100644 --- a/api/config_entry.go +++ b/api/config_entry.go @@ -132,9 +132,10 @@ type ExposePath struct { type LogSinkType string const ( - FileLogSinkType LogSinkType = "file" - StdErrLogSinkType LogSinkType = "stderr" - StdOutLogSinkType LogSinkType = "stdout" + DefaultLogSinkType LogSinkType = "" + FileLogSinkType LogSinkType = "file" + StdErrLogSinkType LogSinkType = "stderr" + StdOutLogSinkType LogSinkType = "stdout" ) // AccessLogsConfig contains the associated default settings for all Envoy instances within the datacenter or partition @@ -303,7 +304,7 @@ type ProxyConfigEntry struct { Config map[string]interface{} `json:",omitempty"` MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"` Expose ExposeConfig `json:",omitempty"` - AccessLogs *AccessLogsConfig `json:",omitempty"` + AccessLogs *AccessLogsConfig `json:",omitempty" alias:"access_logs"` EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"` Meta map[string]string `json:",omitempty"` diff --git a/command/services/config.go b/command/services/config.go index 41341a379b..56a9562234 100644 --- a/command/services/config.go +++ b/command/services/config.go @@ -71,6 +71,16 @@ func serviceToAgentService(svc *structs.ServiceDefinition) (*api.AgentServiceReg result.Check = nil } + // The structs version has non-pointer Proxy.TransparentProxy and Proxy.AccessLogs + // The destination has pointers, so we need to set the destination to nil if there + // is a zero-value field. + if result.Proxy != nil && result.Proxy.TransparentProxy != nil && reflect.DeepEqual(*result.Proxy.TransparentProxy, api.TransparentProxyConfig{}) { + result.Proxy.TransparentProxy = nil + } + if result.Proxy != nil && result.Proxy.AccessLogs != nil && reflect.DeepEqual(*result.Proxy.AccessLogs, api.AccessLogsConfig{}) { + result.Proxy.AccessLogs = nil + } + return &result, nil } diff --git a/command/services/config_test.go b/command/services/config_test.go index 010df66798..1647b92934 100644 --- a/command/services/config_test.go +++ b/command/services/config_test.go @@ -127,6 +127,43 @@ func TestStructsToAgentService(t *testing.T) { }, { "Proxy service", + &structs.ServiceDefinition{ + Name: "web-proxy", + Kind: structs.ServiceKindConnectProxy, + Tags: []string{"leader"}, + Port: 1234, + Proxy: &structs.ConnectProxyConfig{ + DestinationServiceID: "web1", + DestinationServiceName: "web", + LocalServiceAddress: "127.0.0.1", + LocalServicePort: 8181, + Upstreams: structs.TestUpstreams(t), + Mode: structs.ProxyModeTransparent, + Config: map[string]interface{}{ + "foo": "bar", + }, + }, + }, + &api.AgentServiceRegistration{ + Name: "web-proxy", + Tags: []string{"leader"}, + Port: 1234, + Kind: api.ServiceKindConnectProxy, + Proxy: &api.AgentServiceConnectProxyConfig{ + DestinationServiceID: "web1", + DestinationServiceName: "web", + LocalServiceAddress: "127.0.0.1", + LocalServicePort: 8181, + Upstreams: structs.TestUpstreams(t).ToAPI(), + Mode: api.ProxyModeTransparent, + Config: map[string]interface{}{ + "foo": "bar", + }, + }, + }, + }, + { + "TProxy proxy service w/ access logs", &structs.ServiceDefinition{ Name: "web-proxy", Kind: structs.ServiceKindConnectProxy, @@ -142,6 +179,13 @@ func TestStructsToAgentService(t *testing.T) { TransparentProxy: structs.TransparentProxyConfig{ OutboundListenerPort: 808, }, + AccessLogs: structs.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: structs.FileLogSinkType, + Path: "/var/logs/envoy.logs", + TextFormat: "MY START TIME %START_TIME%", + }, Config: map[string]interface{}{ "foo": "bar", }, @@ -162,6 +206,13 @@ func TestStructsToAgentService(t *testing.T) { TransparentProxy: &api.TransparentProxyConfig{ OutboundListenerPort: 808, }, + AccessLogs: &api.AccessLogsConfig{ + Enabled: true, + DisableListenerLogs: true, + Type: api.FileLogSinkType, + Path: "/var/logs/envoy.logs", + TextFormat: "MY START TIME %START_TIME%", + }, Config: map[string]interface{}{ "foo": "bar", }, diff --git a/proto/pbservice/service.gen.go b/proto/pbservice/service.gen.go index ffc5db0384..cca5b995a7 100644 --- a/proto/pbservice/service.gen.go +++ b/proto/pbservice/service.gen.go @@ -4,6 +4,28 @@ package pbservice import "github.com/hashicorp/consul/agent/structs" +func AccessLogsConfigToStructs(s *AccessLogsConfig, t *structs.AccessLogsConfig) { + if s == nil { + return + } + t.Enabled = s.Enabled + t.DisableListenerLogs = s.DisableListenerLogs + t.Type = structs.LogSinkType(s.Type) + t.Path = s.Path + t.JSONFormat = s.JSONFormat + t.TextFormat = s.TextFormat +} +func AccessLogsConfigFromStructs(t *structs.AccessLogsConfig, s *AccessLogsConfig) { + if s == nil { + return + } + s.Enabled = t.Enabled + s.DisableListenerLogs = t.DisableListenerLogs + s.Type = string(t.Type) + s.Path = t.Path + s.JSONFormat = t.JSONFormat + s.TextFormat = t.TextFormat +} func ConnectProxyConfigToStructs(s *ConnectProxyConfig, t *structs.ConnectProxyConfig) { if s == nil { return @@ -26,6 +48,9 @@ func ConnectProxyConfigToStructs(s *ConnectProxyConfig, t *structs.ConnectProxyC if s.TransparentProxy != nil { TransparentProxyConfigToStructs(s.TransparentProxy, &t.TransparentProxy) } + if s.AccessLogs != nil { + AccessLogsConfigToStructs(s.AccessLogs, &t.AccessLogs) + } } func ConnectProxyConfigFromStructs(t *structs.ConnectProxyConfig, s *ConnectProxyConfig) { if s == nil { @@ -55,6 +80,11 @@ func ConnectProxyConfigFromStructs(t *structs.ConnectProxyConfig, s *ConnectProx TransparentProxyConfigFromStructs(&t.TransparentProxy, &x) s.TransparentProxy = &x } + { + var x AccessLogsConfig + AccessLogsConfigFromStructs(&t.AccessLogs, &x) + s.AccessLogs = &x + } } func ExposeConfigToStructs(s *ExposeConfig, t *structs.ExposeConfig) { if s == nil { diff --git a/proto/pbservice/service.pb.binary.go b/proto/pbservice/service.pb.binary.go index 9819f4dcdd..b3c7437673 100644 --- a/proto/pbservice/service.pb.binary.go +++ b/proto/pbservice/service.pb.binary.go @@ -87,6 +87,16 @@ func (msg *TransparentProxyConfig) UnmarshalBinary(b []byte) error { return proto.Unmarshal(b, msg) } +// MarshalBinary implements encoding.BinaryMarshaler +func (msg *AccessLogsConfig) MarshalBinary() ([]byte, error) { + return proto.Marshal(msg) +} + +// UnmarshalBinary implements encoding.BinaryUnmarshaler +func (msg *AccessLogsConfig) UnmarshalBinary(b []byte) error { + return proto.Unmarshal(b, msg) +} + // MarshalBinary implements encoding.BinaryMarshaler func (msg *ServiceDefinition) MarshalBinary() ([]byte, error) { return proto.Marshal(msg) diff --git a/proto/pbservice/service.pb.go b/proto/pbservice/service.pb.go index 83ee455dd4..d4040f31e2 100644 --- a/proto/pbservice/service.pb.go +++ b/proto/pbservice/service.pb.go @@ -82,6 +82,8 @@ type ConnectProxyConfig struct { LocalServiceSocketPath string `protobuf:"bytes,11,opt,name=LocalServiceSocketPath,proto3" json:"LocalServiceSocketPath,omitempty"` // mog: func-to=EnvoyExtensionsToStructs func-from=EnvoyExtensionsFromStructs EnvoyExtensions []*pbconfigentry.EnvoyExtension `protobuf:"bytes,12,rep,name=EnvoyExtensions,proto3" json:"EnvoyExtensions,omitempty"` + // AccessLogsConfig defines envoys access log configuration. + AccessLogs *AccessLogsConfig `protobuf:"bytes,13,opt,name=AccessLogs,proto3" json:"AccessLogs,omitempty"` } func (x *ConnectProxyConfig) Reset() { @@ -200,6 +202,13 @@ func (x *ConnectProxyConfig) GetEnvoyExtensions() []*pbconfigentry.EnvoyExtensio return nil } +func (x *ConnectProxyConfig) GetAccessLogs() *AccessLogsConfig { + if x != nil { + return x.AccessLogs + } + return nil +} + // Upstream represents a single upstream dependency for a service or proxy. It // describes the mechanism used to discover instances to communicate with (the // Target) as well as any potential client configuration that may be useful such @@ -802,6 +811,100 @@ func (x *TransparentProxyConfig) GetDialedDirectly() bool { return false } +// mog annotation: +// +// target=github.com/hashicorp/consul/agent/structs.AccessLogsConfig +// output=service.gen.go +// name=Structs +type AccessLogsConfig struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Enabled bool `protobuf:"varint,1,opt,name=Enabled,proto3" json:"Enabled,omitempty"` + DisableListenerLogs bool `protobuf:"varint,2,opt,name=DisableListenerLogs,proto3" json:"DisableListenerLogs,omitempty"` + // Type represents the desired envoy log sink (e.g. stdout, stderr, file ...). + // mog: func-to=structs.LogSinkType func-from=string + Type string `protobuf:"bytes,3,opt,name=Type,proto3" json:"Type,omitempty"` + Path string `protobuf:"bytes,4,opt,name=Path,proto3" json:"Path,omitempty"` + JSONFormat string `protobuf:"bytes,5,opt,name=JSONFormat,proto3" json:"JSONFormat,omitempty"` + TextFormat string `protobuf:"bytes,6,opt,name=TextFormat,proto3" json:"TextFormat,omitempty"` +} + +func (x *AccessLogsConfig) Reset() { + *x = AccessLogsConfig{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_pbservice_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccessLogsConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccessLogsConfig) ProtoMessage() {} + +func (x *AccessLogsConfig) ProtoReflect() protoreflect.Message { + mi := &file_proto_pbservice_service_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccessLogsConfig.ProtoReflect.Descriptor instead. +func (*AccessLogsConfig) Descriptor() ([]byte, []int) { + return file_proto_pbservice_service_proto_rawDescGZIP(), []int{8} +} + +func (x *AccessLogsConfig) GetEnabled() bool { + if x != nil { + return x.Enabled + } + return false +} + +func (x *AccessLogsConfig) GetDisableListenerLogs() bool { + if x != nil { + return x.DisableListenerLogs + } + return false +} + +func (x *AccessLogsConfig) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *AccessLogsConfig) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *AccessLogsConfig) GetJSONFormat() string { + if x != nil { + return x.JSONFormat + } + return "" +} + +func (x *AccessLogsConfig) GetTextFormat() string { + if x != nil { + return x.TextFormat + } + return "" +} + // ServiceDefinition is used to JSON decode the Service definitions. For // documentation on specific fields see NodeService which is better documented. // @@ -856,7 +959,7 @@ type ServiceDefinition struct { func (x *ServiceDefinition) Reset() { *x = ServiceDefinition{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbservice_service_proto_msgTypes[8] + mi := &file_proto_pbservice_service_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -869,7 +972,7 @@ func (x *ServiceDefinition) String() string { func (*ServiceDefinition) ProtoMessage() {} func (x *ServiceDefinition) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbservice_service_proto_msgTypes[8] + mi := &file_proto_pbservice_service_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -882,7 +985,7 @@ func (x *ServiceDefinition) ProtoReflect() protoreflect.Message { // Deprecated: Use ServiceDefinition.ProtoReflect.Descriptor instead. func (*ServiceDefinition) Descriptor() ([]byte, []int) { - return file_proto_pbservice_service_proto_rawDescGZIP(), []int{8} + return file_proto_pbservice_service_proto_rawDescGZIP(), []int{9} } func (x *ServiceDefinition) GetKind() string { @@ -1018,7 +1121,7 @@ type ServiceAddress struct { func (x *ServiceAddress) Reset() { *x = ServiceAddress{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbservice_service_proto_msgTypes[9] + mi := &file_proto_pbservice_service_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1031,7 +1134,7 @@ func (x *ServiceAddress) String() string { func (*ServiceAddress) ProtoMessage() {} func (x *ServiceAddress) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbservice_service_proto_msgTypes[9] + mi := &file_proto_pbservice_service_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1044,7 +1147,7 @@ func (x *ServiceAddress) ProtoReflect() protoreflect.Message { // Deprecated: Use ServiceAddress.ProtoReflect.Descriptor instead. func (*ServiceAddress) Descriptor() ([]byte, []int) { - return file_proto_pbservice_service_proto_rawDescGZIP(), []int{9} + return file_proto_pbservice_service_proto_rawDescGZIP(), []int{10} } func (x *ServiceAddress) GetAddress() string { @@ -1076,7 +1179,7 @@ type Weights struct { func (x *Weights) Reset() { *x = Weights{} if protoimpl.UnsafeEnabled { - mi := &file_proto_pbservice_service_proto_msgTypes[10] + mi := &file_proto_pbservice_service_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1089,7 +1192,7 @@ func (x *Weights) String() string { func (*Weights) ProtoMessage() {} func (x *Weights) ProtoReflect() protoreflect.Message { - mi := &file_proto_pbservice_service_proto_msgTypes[10] + mi := &file_proto_pbservice_service_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1102,7 +1205,7 @@ func (x *Weights) ProtoReflect() protoreflect.Message { // Deprecated: Use Weights.ProtoReflect.Descriptor instead. func (*Weights) Descriptor() ([]byte, []int) { - return file_proto_pbservice_service_proto_rawDescGZIP(), []int{10} + return file_proto_pbservice_service_proto_rawDescGZIP(), []int{11} } func (x *Weights) GetPassing() int32 { @@ -1134,7 +1237,7 @@ var file_proto_pbservice_service_proto_rawDesc = []byte{ 0x72, 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8f, 0x06, 0x0a, 0x12, 0x43, 0x6f, 0x6e, + 0x63, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe4, 0x06, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x16, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, @@ -1183,188 +1286,206 @@ var file_proto_pbservice_service_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x76, 0x6f, 0x79, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x45, 0x6e, 0x76, 0x6f, 0x79, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x81, 0x05, 0x0a, 0x08, 0x55, - 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x28, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x32, 0x0a, 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x0f, 0x44, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, - 0x65, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x44, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, - 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x2a, 0x0a, - 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, - 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x6f, 0x63, - 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, - 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x56, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x68, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x47, 0x61, - 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x4d, 0x65, 0x73, - 0x68, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x30, 0x0a, 0x13, 0x43, 0x65, 0x6e, 0x74, - 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x4c, 0x6f, - 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x74, - 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, - 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x30, 0x0a, 0x13, - 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, - 0x6f, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x4c, 0x6f, 0x63, 0x61, 0x6c, - 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0xdf, - 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x06, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x12, 0x5c, 0x0a, 0x0e, 0x53, 0x69, 0x64, - 0x65, 0x63, 0x61, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x66, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4d, - 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x50, 0x65, - 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, - 0x22, 0x5e, 0x0a, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x53, 0x4e, 0x49, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x03, 0x53, 0x4e, 0x49, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x70, 0x69, 0x66, - 0x66, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x53, 0x70, 0x69, 0x66, - 0x66, 0x65, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x22, 0x6b, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x12, 0x16, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x43, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, - 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x45, 0x78, 0x70, 0x6f, - 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0xb0, 0x01, - 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x22, 0x0a, 0x0c, - 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x50, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, - 0x68, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x4c, 0x6f, 0x63, - 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x50, 0x61, 0x72, 0x73, 0x65, 0x64, - 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0f, 0x50, 0x61, 0x72, 0x73, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x22, 0x27, 0x0a, 0x11, 0x4d, 0x65, 0x73, 0x68, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x74, 0x0a, 0x16, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x14, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, - 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x14, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x44, 0x69, 0x61, 0x6c, 0x65, - 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x44, 0x69, 0x61, 0x6c, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x22, - 0xae, 0x08, 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x54, 0x61, 0x67, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x73, 0x0a, 0x0f, 0x54, - 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x10, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, - 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0f, 0x54, 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, - 0x12, 0x52, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x53, 0x0a, 0x0a, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, - 0x4d, 0x65, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x6f, - 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x42, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, - 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x44, 0x0a, 0x06, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x68, + 0x63, 0x65, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x52, 0x0a, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x73, 0x22, + 0x81, 0x05, 0x0a, 0x08, 0x55, 0x70, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x28, 0x0a, 0x0f, + 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x32, 0x0a, 0x14, 0x44, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, + 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x65, + 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x65, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, + 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x4c, 0x6f, + 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x24, + 0x0a, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, + 0x50, 0x6f, 0x72, 0x74, 0x12, 0x2f, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x56, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x68, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x4d, + 0x65, 0x73, 0x68, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x0b, 0x4d, 0x65, 0x73, 0x68, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x30, 0x0a, + 0x13, 0x43, 0x65, 0x6e, 0x74, 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x43, 0x65, 0x6e, 0x74, + 0x72, 0x61, 0x6c, 0x6c, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x12, + 0x30, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x4c, 0x6f, + 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x50, 0x61, 0x74, + 0x68, 0x12, 0x30, 0x0a, 0x13, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, + 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x42, 0x69, 0x6e, 0x64, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, + 0x6f, 0x64, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x12, 0x5c, + 0x0a, 0x0e, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, + 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x53, 0x69, + 0x64, 0x65, 0x63, 0x61, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x08, + 0x50, 0x65, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, + 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, + 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x08, 0x50, 0x65, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x61, 0x4a, + 0x04, 0x08, 0x02, 0x10, 0x03, 0x22, 0x5e, 0x0a, 0x12, 0x50, 0x65, 0x65, 0x72, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x53, + 0x4e, 0x49, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x53, 0x4e, 0x49, 0x12, 0x1a, 0x0a, + 0x08, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x49, 0x44, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x53, 0x70, 0x69, 0x66, 0x66, 0x65, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x6b, 0x0a, 0x0c, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, 0x12, 0x43, 0x0a, + 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x73, 0x12, 0x44, 0x0a, 0x07, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, - 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, - 0x07, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2c, - 0x0a, 0x11, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, - 0x69, 0x64, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x45, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x54, 0x61, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x4b, 0x0a, 0x05, - 0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, - 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x05, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x58, 0x0a, 0x0e, 0x45, 0x6e, 0x74, - 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x11, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, - 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, - 0x65, 0x74, 0x61, 0x12, 0x4b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, - 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x1a, 0x75, 0x0a, 0x14, 0x54, 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x47, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, - 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x3e, 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, - 0x50, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, - 0x22, 0x3d, 0x0a, 0x07, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, - 0x61, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x50, 0x61, - 0x73, 0x73, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x42, - 0x8a, 0x02, 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, + 0x2e, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x50, 0x61, 0x74, 0x68, 0x52, 0x05, 0x50, 0x61, 0x74, + 0x68, 0x73, 0x22, 0xb0, 0x01, 0x0a, 0x0a, 0x45, 0x78, 0x70, 0x6f, 0x73, 0x65, 0x50, 0x61, 0x74, + 0x68, 0x12, 0x22, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, + 0x72, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x24, 0x0a, 0x0d, 0x4c, 0x6f, 0x63, + 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x0d, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x50, 0x6f, 0x72, 0x74, 0x12, + 0x1a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x50, + 0x61, 0x72, 0x73, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x50, 0x61, 0x72, 0x73, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x22, 0x27, 0x0a, 0x11, 0x4d, 0x65, 0x73, 0x68, 0x47, 0x61, 0x74, + 0x65, 0x77, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x4d, 0x6f, + 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4d, 0x6f, 0x64, 0x65, 0x22, 0x74, + 0x0a, 0x16, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, + 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x32, 0x0a, 0x14, 0x4f, 0x75, 0x74, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, + 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x0e, + 0x44, 0x69, 0x61, 0x6c, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6c, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x44, 0x69, 0x61, 0x6c, 0x65, 0x64, 0x44, 0x69, 0x72, 0x65, + 0x63, 0x74, 0x6c, 0x79, 0x22, 0xc6, 0x01, 0x0a, 0x10, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, + 0x6f, 0x67, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x45, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x13, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x65, + 0x72, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, + 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x0a, + 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x1e, 0x0a, + 0x0a, 0x54, 0x65, 0x78, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x54, 0x65, 0x78, 0x74, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0xae, 0x08, + 0x0a, 0x11, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, + 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x73, 0x0a, 0x0f, 0x54, 0x61, 0x67, + 0x67, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, + 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x54, + 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x52, + 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x68, + 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x4d, 0x65, + 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, + 0x50, 0x61, 0x74, 0x68, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x6f, 0x63, 0x6b, + 0x65, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x42, 0x0a, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, - 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x53, 0xaa, 0x02, 0x21, - 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, - 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0xca, 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, - 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, - 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x3a, 0x3a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x05, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x44, 0x0a, 0x06, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x68, 0x61, 0x73, + 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x06, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x73, + 0x12, 0x44, 0x0a, 0x07, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, 0x07, 0x57, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2c, 0x0a, 0x11, + 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, + 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x54, + 0x61, 0x67, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x4b, 0x0a, 0x05, 0x50, 0x72, + 0x6f, 0x78, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x68, 0x61, 0x73, 0x68, + 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x52, 0x05, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x58, 0x0a, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, + 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x30, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x52, 0x0e, 0x45, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x12, 0x4b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, + 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x52, 0x07, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x1a, 0x75, + 0x0a, 0x14, 0x54, 0x61, 0x67, 0x67, 0x65, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x47, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, + 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x4d, 0x65, 0x74, 0x61, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3e, + 0x0a, 0x0e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x6f, + 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x3d, + 0x0a, 0x07, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x61, 0x73, + 0x73, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x50, 0x61, 0x73, 0x73, + 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x42, 0x8a, 0x02, + 0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, + 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, + 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x73, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0xa2, 0x02, 0x04, 0x48, 0x43, 0x49, 0x53, 0xaa, 0x02, 0x21, 0x48, 0x61, + 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xca, + 0x02, 0x21, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, + 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0xe2, 0x02, 0x2d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, + 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5c, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, + 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x3a, 0x3a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1379,7 +1500,7 @@ func file_proto_pbservice_service_proto_rawDescGZIP() []byte { return file_proto_pbservice_service_proto_rawDescData } -var file_proto_pbservice_service_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_proto_pbservice_service_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_proto_pbservice_service_proto_goTypes = []interface{}{ (*ConnectProxyConfig)(nil), // 0: hashicorp.consul.internal.service.ConnectProxyConfig (*Upstream)(nil), // 1: hashicorp.consul.internal.service.Upstream @@ -1389,42 +1510,44 @@ var file_proto_pbservice_service_proto_goTypes = []interface{}{ (*ExposePath)(nil), // 5: hashicorp.consul.internal.service.ExposePath (*MeshGatewayConfig)(nil), // 6: hashicorp.consul.internal.service.MeshGatewayConfig (*TransparentProxyConfig)(nil), // 7: hashicorp.consul.internal.service.TransparentProxyConfig - (*ServiceDefinition)(nil), // 8: hashicorp.consul.internal.service.ServiceDefinition - (*ServiceAddress)(nil), // 9: hashicorp.consul.internal.service.ServiceAddress - (*Weights)(nil), // 10: hashicorp.consul.internal.service.Weights - nil, // 11: hashicorp.consul.internal.service.ServiceDefinition.TaggedAddressesEntry - nil, // 12: hashicorp.consul.internal.service.ServiceDefinition.MetaEntry - (*structpb.Struct)(nil), // 13: google.protobuf.Struct - (*pbconfigentry.EnvoyExtension)(nil), // 14: hashicorp.consul.internal.configentry.EnvoyExtension - (*CheckType)(nil), // 15: hashicorp.consul.internal.service.CheckType - (*pbcommon.EnterpriseMeta)(nil), // 16: hashicorp.consul.internal.common.EnterpriseMeta + (*AccessLogsConfig)(nil), // 8: hashicorp.consul.internal.service.AccessLogsConfig + (*ServiceDefinition)(nil), // 9: hashicorp.consul.internal.service.ServiceDefinition + (*ServiceAddress)(nil), // 10: hashicorp.consul.internal.service.ServiceAddress + (*Weights)(nil), // 11: hashicorp.consul.internal.service.Weights + nil, // 12: hashicorp.consul.internal.service.ServiceDefinition.TaggedAddressesEntry + nil, // 13: hashicorp.consul.internal.service.ServiceDefinition.MetaEntry + (*structpb.Struct)(nil), // 14: google.protobuf.Struct + (*pbconfigentry.EnvoyExtension)(nil), // 15: hashicorp.consul.internal.configentry.EnvoyExtension + (*CheckType)(nil), // 16: hashicorp.consul.internal.service.CheckType + (*pbcommon.EnterpriseMeta)(nil), // 17: hashicorp.consul.internal.common.EnterpriseMeta } var file_proto_pbservice_service_proto_depIdxs = []int32{ - 13, // 0: hashicorp.consul.internal.service.ConnectProxyConfig.Config:type_name -> google.protobuf.Struct + 14, // 0: hashicorp.consul.internal.service.ConnectProxyConfig.Config:type_name -> google.protobuf.Struct 1, // 1: hashicorp.consul.internal.service.ConnectProxyConfig.Upstreams:type_name -> hashicorp.consul.internal.service.Upstream 6, // 2: hashicorp.consul.internal.service.ConnectProxyConfig.MeshGateway:type_name -> hashicorp.consul.internal.service.MeshGatewayConfig 4, // 3: hashicorp.consul.internal.service.ConnectProxyConfig.Expose:type_name -> hashicorp.consul.internal.service.ExposeConfig 7, // 4: hashicorp.consul.internal.service.ConnectProxyConfig.TransparentProxy:type_name -> hashicorp.consul.internal.service.TransparentProxyConfig - 14, // 5: hashicorp.consul.internal.service.ConnectProxyConfig.EnvoyExtensions:type_name -> hashicorp.consul.internal.configentry.EnvoyExtension - 13, // 6: hashicorp.consul.internal.service.Upstream.Config:type_name -> google.protobuf.Struct - 6, // 7: hashicorp.consul.internal.service.Upstream.MeshGateway:type_name -> hashicorp.consul.internal.service.MeshGatewayConfig - 8, // 8: hashicorp.consul.internal.service.ServiceConnect.SidecarService:type_name -> hashicorp.consul.internal.service.ServiceDefinition - 3, // 9: hashicorp.consul.internal.service.ServiceConnect.PeerMeta:type_name -> hashicorp.consul.internal.service.PeeringServiceMeta - 5, // 10: hashicorp.consul.internal.service.ExposeConfig.Paths:type_name -> hashicorp.consul.internal.service.ExposePath - 11, // 11: hashicorp.consul.internal.service.ServiceDefinition.TaggedAddresses:type_name -> hashicorp.consul.internal.service.ServiceDefinition.TaggedAddressesEntry - 12, // 12: hashicorp.consul.internal.service.ServiceDefinition.Meta:type_name -> hashicorp.consul.internal.service.ServiceDefinition.MetaEntry - 15, // 13: hashicorp.consul.internal.service.ServiceDefinition.Check:type_name -> hashicorp.consul.internal.service.CheckType - 15, // 14: hashicorp.consul.internal.service.ServiceDefinition.Checks:type_name -> hashicorp.consul.internal.service.CheckType - 10, // 15: hashicorp.consul.internal.service.ServiceDefinition.Weights:type_name -> hashicorp.consul.internal.service.Weights - 0, // 16: hashicorp.consul.internal.service.ServiceDefinition.Proxy:type_name -> hashicorp.consul.internal.service.ConnectProxyConfig - 16, // 17: hashicorp.consul.internal.service.ServiceDefinition.EnterpriseMeta:type_name -> hashicorp.consul.internal.common.EnterpriseMeta - 2, // 18: hashicorp.consul.internal.service.ServiceDefinition.Connect:type_name -> hashicorp.consul.internal.service.ServiceConnect - 9, // 19: hashicorp.consul.internal.service.ServiceDefinition.TaggedAddressesEntry.value:type_name -> hashicorp.consul.internal.service.ServiceAddress - 20, // [20:20] is the sub-list for method output_type - 20, // [20:20] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 15, // 5: hashicorp.consul.internal.service.ConnectProxyConfig.EnvoyExtensions:type_name -> hashicorp.consul.internal.configentry.EnvoyExtension + 8, // 6: hashicorp.consul.internal.service.ConnectProxyConfig.AccessLogs:type_name -> hashicorp.consul.internal.service.AccessLogsConfig + 14, // 7: hashicorp.consul.internal.service.Upstream.Config:type_name -> google.protobuf.Struct + 6, // 8: hashicorp.consul.internal.service.Upstream.MeshGateway:type_name -> hashicorp.consul.internal.service.MeshGatewayConfig + 9, // 9: hashicorp.consul.internal.service.ServiceConnect.SidecarService:type_name -> hashicorp.consul.internal.service.ServiceDefinition + 3, // 10: hashicorp.consul.internal.service.ServiceConnect.PeerMeta:type_name -> hashicorp.consul.internal.service.PeeringServiceMeta + 5, // 11: hashicorp.consul.internal.service.ExposeConfig.Paths:type_name -> hashicorp.consul.internal.service.ExposePath + 12, // 12: hashicorp.consul.internal.service.ServiceDefinition.TaggedAddresses:type_name -> hashicorp.consul.internal.service.ServiceDefinition.TaggedAddressesEntry + 13, // 13: hashicorp.consul.internal.service.ServiceDefinition.Meta:type_name -> hashicorp.consul.internal.service.ServiceDefinition.MetaEntry + 16, // 14: hashicorp.consul.internal.service.ServiceDefinition.Check:type_name -> hashicorp.consul.internal.service.CheckType + 16, // 15: hashicorp.consul.internal.service.ServiceDefinition.Checks:type_name -> hashicorp.consul.internal.service.CheckType + 11, // 16: hashicorp.consul.internal.service.ServiceDefinition.Weights:type_name -> hashicorp.consul.internal.service.Weights + 0, // 17: hashicorp.consul.internal.service.ServiceDefinition.Proxy:type_name -> hashicorp.consul.internal.service.ConnectProxyConfig + 17, // 18: hashicorp.consul.internal.service.ServiceDefinition.EnterpriseMeta:type_name -> hashicorp.consul.internal.common.EnterpriseMeta + 2, // 19: hashicorp.consul.internal.service.ServiceDefinition.Connect:type_name -> hashicorp.consul.internal.service.ServiceConnect + 10, // 20: hashicorp.consul.internal.service.ServiceDefinition.TaggedAddressesEntry.value:type_name -> hashicorp.consul.internal.service.ServiceAddress + 21, // [21:21] is the sub-list for method output_type + 21, // [21:21] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name } func init() { file_proto_pbservice_service_proto_init() } @@ -1531,7 +1654,7 @@ func file_proto_pbservice_service_proto_init() { } } file_proto_pbservice_service_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServiceDefinition); i { + switch v := v.(*AccessLogsConfig); i { case 0: return &v.state case 1: @@ -1543,7 +1666,7 @@ func file_proto_pbservice_service_proto_init() { } } file_proto_pbservice_service_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ServiceAddress); i { + switch v := v.(*ServiceDefinition); i { case 0: return &v.state case 1: @@ -1555,6 +1678,18 @@ func file_proto_pbservice_service_proto_init() { } } file_proto_pbservice_service_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ServiceAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_pbservice_service_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Weights); i { case 0: return &v.state @@ -1573,7 +1708,7 @@ func file_proto_pbservice_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_pbservice_service_proto_rawDesc, NumEnums: 0, - NumMessages: 13, + NumMessages: 14, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/pbservice/service.proto b/proto/pbservice/service.proto index 1b3ecb9cc2..b819692c83 100644 --- a/proto/pbservice/service.proto +++ b/proto/pbservice/service.proto @@ -73,6 +73,9 @@ message ConnectProxyConfig { // mog: func-to=EnvoyExtensionsToStructs func-from=EnvoyExtensionsFromStructs repeated hashicorp.consul.internal.configentry.EnvoyExtension EnvoyExtensions = 12; + + // AccessLogsConfig defines envoys access log configuration. + AccessLogsConfig AccessLogs = 13; } // Upstream represents a single upstream dependency for a service or proxy. It @@ -239,6 +242,24 @@ message TransparentProxyConfig { bool DialedDirectly = 2; } +// mog annotation: +// +// target=github.com/hashicorp/consul/agent/structs.AccessLogsConfig +// output=service.gen.go +// name=Structs +message AccessLogsConfig { + bool Enabled = 1; + bool DisableListenerLogs = 2; + + // Type represents the desired envoy log sink (e.g. stdout, stderr, file ...). + // mog: func-to=structs.LogSinkType func-from=string + string Type = 3; + + string Path = 4; + string JSONFormat = 5; + string TextFormat = 6; +} + // ServiceDefinition is used to JSON decode the Service definitions. For // documentation on specific fields see NodeService which is better documented. //