From ac76aeb279e55e9f4b8d652045e8ae574c717f70 Mon Sep 17 00:00:00 2001 From: Joe Kralicky Date: Mon, 30 Jun 2025 15:15:05 -0400 Subject: [PATCH] ssh: add envoy configuration (#5659) ## Summary Adds the envoy listener config logic for ssh. ## Related issues ## User Explanation ## Checklist - [ ] reference any related issues - [ ] updated unit tests - [ ] add appropriate label (`enhancement`, `bug`, `breaking`, `dependencies`, `ci`) - [ ] ready for review --- authorize/ssh_grpc.go | 16 ++ config/envoyconfig/listeners.go | 10 ++ config/envoyconfig/listeners_ssh.go | 198 +++++++++++++++++++++++ config/envoyconfig/listeners_ssh_test.go | 148 +++++++++++++++++ config/options.go | 21 ++- config/policy.go | 5 + config/policy_test.go | 8 + go.mod | 4 +- go.sum | 8 +- pkg/cmd/pomerium/pomerium.go | 2 + 10 files changed, 409 insertions(+), 11 deletions(-) create mode 100644 authorize/ssh_grpc.go create mode 100644 config/envoyconfig/listeners_ssh.go create mode 100644 config/envoyconfig/listeners_ssh_test.go diff --git a/authorize/ssh_grpc.go b/authorize/ssh_grpc.go new file mode 100644 index 000000000..083dec464 --- /dev/null +++ b/authorize/ssh_grpc.go @@ -0,0 +1,16 @@ +package authorize + +import ( + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + extensions_ssh "github.com/pomerium/envoy-custom/api/extensions/filters/network/ssh" +) + +func (a *Authorize) ManageStream(extensions_ssh.StreamManagement_ManageStreamServer) error { + return status.Errorf(codes.Unimplemented, "method ManageStream not implemented") +} + +func (a *Authorize) ServeChannel(extensions_ssh.StreamManagement_ServeChannelServer) error { + return status.Errorf(codes.Unimplemented, "method ServeChannel not implemented") +} diff --git a/config/envoyconfig/listeners.go b/config/envoyconfig/listeners.go index c8676ee38..5bfee9ce3 100644 --- a/config/envoyconfig/listeners.go +++ b/config/envoyconfig/listeners.go @@ -65,6 +65,16 @@ func (b *Builder) BuildListeners( listeners = append(listeners, li) } + if shouldStartSSHListener(cfg.Options) { + li, err := buildSSHListener(cfg) + if err != nil { + return nil, err + } + if li != nil { + listeners = append(listeners, li) + } + } + li, err := b.buildOutboundListener(cfg) if err != nil { return nil, err diff --git a/config/envoyconfig/listeners_ssh.go b/config/envoyconfig/listeners_ssh.go new file mode 100644 index 000000000..b45fd18e4 --- /dev/null +++ b/config/envoyconfig/listeners_ssh.go @@ -0,0 +1,198 @@ +package envoyconfig + +import ( + "fmt" + "net/url" + + xds_core_v3 "github.com/cncf/xds/go/xds/core/v3" + xds_matcher_v3 "github.com/cncf/xds/go/xds/type/matcher/v3" + envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" + envoy_generic_proxy_action_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/generic_proxy/action/v3" + envoy_generic_proxy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/generic_proxy/matcher/v3" + envoy_generic_router_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/generic_proxy/router/v3" + envoy_generic_proxy_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/generic_proxy/v3" + matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + "google.golang.org/protobuf/types/known/durationpb" + + extensions_ssh "github.com/pomerium/envoy-custom/api/extensions/filters/network/ssh" + "github.com/pomerium/pomerium/config" +) + +func buildSSHListener(cfg *config.Config) (*envoy_config_listener_v3.Listener, error) { + if cfg.Options.SSHAddr == "" { + return nil, nil + } + rc, err := buildRouteConfig(cfg) + if err != nil { + return nil, err + } + if rc == nil { + return nil, nil + } + + authorizeService := &envoy_config_core_v3.GrpcService{ + Timeout: durationpb.New(0), + TargetSpecifier: &envoy_config_core_v3.GrpcService_EnvoyGrpc_{ + EnvoyGrpc: &envoy_config_core_v3.GrpcService_EnvoyGrpc{ + ClusterName: "pomerium-authorize", + }, + }, + } + + var hostKeyDataSources []*envoy_config_core_v3.DataSource + if cfg.Options.SSHHostKeyFiles != nil { + for _, filename := range *cfg.Options.SSHHostKeyFiles { + hostKeyDataSources = append(hostKeyDataSources, &envoy_config_core_v3.DataSource{ + Specifier: &envoy_config_core_v3.DataSource_Filename{ + Filename: filename, + }, + }) + } + } + if cfg.Options.SSHHostKeys != nil { + for _, key := range *cfg.Options.SSHHostKeys { + hostKeyDataSources = append(hostKeyDataSources, &envoy_config_core_v3.DataSource{ + Specifier: &envoy_config_core_v3.DataSource_InlineString{ + InlineString: key, + }, + }) + } + } + var userCaKeyDataSource *envoy_config_core_v3.DataSource + if cfg.Options.SSHUserCAKeyFile != "" { + userCaKeyDataSource = &envoy_config_core_v3.DataSource{ + Specifier: &envoy_config_core_v3.DataSource_Filename{ + Filename: cfg.Options.SSHUserCAKeyFile, + }, + } + } else if cfg.Options.SSHUserCAKey != "" { + userCaKeyDataSource = &envoy_config_core_v3.DataSource{ + Specifier: &envoy_config_core_v3.DataSource_InlineString{ + InlineString: cfg.Options.SSHUserCAKey, + }, + } + } + + li := &envoy_config_listener_v3.Listener{ + Name: "ssh", + Address: buildTCPAddress(cfg.Options.SSHAddr, 22), + FilterChains: []*envoy_config_listener_v3.FilterChain{ + { + Filters: []*envoy_config_listener_v3.Filter{ + { + Name: "generic_proxy", + ConfigType: &envoy_config_listener_v3.Filter_TypedConfig{ + TypedConfig: marshalAny(&envoy_generic_proxy_v3.GenericProxy{ + StatPrefix: "ssh", + CodecConfig: &envoy_config_core_v3.TypedExtensionConfig{ + Name: "envoy.generic_proxy.codecs.ssh", + TypedConfig: marshalAny(&extensions_ssh.CodecConfig{ + HostKeys: hostKeyDataSources, + UserCaKey: userCaKeyDataSource, + GrpcService: authorizeService, + }), + }, + Filters: []*envoy_config_core_v3.TypedExtensionConfig{ + { + Name: "envoy.filters.generic.router", + TypedConfig: marshalAny(&envoy_generic_router_v3.Router{ + BindUpstreamConnection: true, + }), + }, + }, + RouteSpecifier: &envoy_generic_proxy_v3.GenericProxy_RouteConfig{ + RouteConfig: rc, + }, + }), + }, + }, + }, + }, + }, + } + return li, nil +} + +func buildRouteConfig(cfg *config.Config) (*envoy_generic_proxy_v3.RouteConfiguration, error) { + var routeMatchers []*xds_matcher_v3.Matcher_MatcherList_FieldMatcher + for route := range cfg.Options.GetAllPolicies() { + if !route.IsSSH() { + continue + } + from, err := url.Parse(route.From) + if err != nil { + return nil, err + } + fromHost := from.Hostname() + if len(route.To) > 1 { + return nil, fmt.Errorf("only one 'to' entry allowed for ssh routes") + } + to := route.To[0].URL + if to.Scheme != "ssh" { + return nil, fmt.Errorf("'to' route url must have ssh scheme") + } + clusterID := getClusterID(route) + routeMatchers = append(routeMatchers, &xds_matcher_v3.Matcher_MatcherList_FieldMatcher{ + Predicate: &xds_matcher_v3.Matcher_MatcherList_Predicate{ + MatchType: &xds_matcher_v3.Matcher_MatcherList_Predicate_SinglePredicate_{ + SinglePredicate: &xds_matcher_v3.Matcher_MatcherList_Predicate_SinglePredicate{ + Input: &xds_core_v3.TypedExtensionConfig{ + Name: "request", + TypedConfig: marshalAny(&envoy_generic_proxy_matcher_v3.RequestMatchInput{}), + }, + Matcher: &xds_matcher_v3.Matcher_MatcherList_Predicate_SinglePredicate_CustomMatch{ + CustomMatch: &xds_core_v3.TypedExtensionConfig{ + Name: "request", + TypedConfig: marshalAny(&envoy_generic_proxy_matcher_v3.RequestMatcher{ + Host: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: fromHost, + }, + }, + }), + }, + }, + }, + }, + }, + OnMatch: &xds_matcher_v3.Matcher_OnMatch{ + OnMatch: &xds_matcher_v3.Matcher_OnMatch_Action{ + Action: &xds_core_v3.TypedExtensionConfig{ + Name: "route", + TypedConfig: marshalAny(&envoy_generic_proxy_action_v3.RouteAction{ + Name: route.ID, + ClusterSpecifier: &envoy_generic_proxy_action_v3.RouteAction_Cluster{ + Cluster: clusterID, + }, + Timeout: durationpb.New(0), + }), + }, + }, + }, + }) + } + if len(routeMatchers) == 0 { + return nil, nil + } + return &envoy_generic_proxy_v3.RouteConfiguration{ + Name: "route_config", + VirtualHosts: []*envoy_generic_proxy_v3.VirtualHost{ + { + Name: "ssh", + Hosts: []string{"*"}, + Routes: &xds_matcher_v3.Matcher{ + MatcherType: &xds_matcher_v3.Matcher_MatcherList_{ + MatcherList: &xds_matcher_v3.Matcher_MatcherList{ + Matchers: routeMatchers, + }, + }, + }, + }, + }, + }, nil +} + +func shouldStartSSHListener(options *config.Options) bool { + return config.IsAuthorize(options.Services) +} diff --git a/config/envoyconfig/listeners_ssh_test.go b/config/envoyconfig/listeners_ssh_test.go new file mode 100644 index 000000000..f0797707d --- /dev/null +++ b/config/envoyconfig/listeners_ssh_test.go @@ -0,0 +1,148 @@ +package envoyconfig + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/pomerium/pomerium/config" +) + +func TestBuildSSHListener(t *testing.T) { + t.Run("no ssh routes or address set", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.Nil(t, l) + }) + t.Run("address set, but no ssh routes", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.Policies = []config.Policy{ + {From: "https://not-ssh", To: mustParseWeightedURLs(t, "https://dest:22")}, + } + cfg.Options.SSHAddr = "0.0.0.0:22" + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.Nil(t, l) + }) + t.Run("no address set, but routes present", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "ssh://to:22")}, + } + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.Nil(t, l) + }) + t.Run("address and routes both present", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "ssh://to:22")}, + } + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.NoError(t, l.ValidateAll()) + }) + t.Run("multiple routes", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "ssh://to1:22")}, + {From: "ssh://host2", To: mustParseWeightedURLs(t, "ssh://to2:22")}, + {From: "ssh://host3", To: mustParseWeightedURLs(t, "ssh://to3:22")}, + } + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.NoError(t, l.ValidateAll()) + }) + t.Run("keys configured", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.SSHHostKeyFiles = &[]string{ + "/path/to/key1", + "/path/to/key2", + } + cfg.Options.SSHHostKeys = &[]string{ + "key3", + "key4", + } + cfg.Options.SSHUserCAKeyFile = "/path/to/user_ca_key" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "ssh://to1:22")}, + {From: "ssh://host2", To: mustParseWeightedURLs(t, "ssh://to2:22")}, + {From: "ssh://host3", To: mustParseWeightedURLs(t, "ssh://to3:22")}, + } + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.NoError(t, l.ValidateAll()) + }) + t.Run("user ca key inline", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.SSHHostKeyFiles = &[]string{ + "/path/to/key1", + "/path/to/key2", + } + cfg.Options.SSHHostKeys = &[]string{ + "key3", + "key4", + } + cfg.Options.SSHUserCAKey = "key" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "ssh://to1:22")}, + {From: "ssh://host2", To: mustParseWeightedURLs(t, "ssh://to2:22")}, + {From: "ssh://host3", To: mustParseWeightedURLs(t, "ssh://to3:22")}, + } + l, err := buildSSHListener(cfg) + assert.NoError(t, err) + assert.NoError(t, l.ValidateAll()) + }) + t.Run("invalid From url", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://\x7f", To: mustParseWeightedURLs(t, "ssh://to1:22")}, + } + _, err := buildSSHListener(cfg) + assert.Error(t, err) + }) + t.Run("multiple To urls", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "ssh://to1:22", "ssh://to2:22")}, + } + _, err := buildSSHListener(cfg) + assert.Error(t, err) + }) + t.Run("To url missing scheme", func(t *testing.T) { + cfg := &config.Config{ + Options: config.NewDefaultOptions(), + } + cfg.Options.SSHAddr = "0.0.0.0:22" + cfg.Options.Policies = []config.Policy{ + {From: "ssh://host1", To: mustParseWeightedURLs(t, "http://to1:22")}, + } + _, err := buildSSHListener(cfg) + assert.Error(t, err) + }) +} diff --git a/config/options.go b/config/options.go index a319eee66..e628d587d 100644 --- a/config/options.go +++ b/config/options.go @@ -242,11 +242,22 @@ type Options struct { // SSH Settings - SSHAddr string `mapstructure:"ssh_address" yaml:"ssh_address,omitempty"` - SSHHostKeyFiles *[]string `mapstructure:"ssh_host_key_files" yaml:"ssh_host_key_files,omitempty"` - SSHHostKeys *[]string `mapstructure:"ssh_host_keys" yaml:"ssh_host_keys,omitempty"` - SSHUserCAKeyFile string `mapstructure:"ssh_user_ca_key_file" yaml:"ssh_user_ca_key_file,omitempty"` - SSHUserCAKey string `mapstructure:"ssh_user_ca_key" yaml:"ssh_user_ca_key,omitempty"` + // Address/Port to bind to for the SSH server. If unset, SSH will be disabled. + SSHAddr string `mapstructure:"ssh_address" yaml:"ssh_address,omitempty"` + // List of host key files for the SSH server. + // Files must not be group/world-readable on disk. + // If multiple keys are given, they must each have unique algorithms. + SSHHostKeyFiles *[]string `mapstructure:"ssh_host_key_files" yaml:"ssh_host_key_files,omitempty"` + // String contents of host keys for the SSH server. If both ssh_host_keys + // and ssh_host_key_files are set, they will be combined. + SSHHostKeys *[]string `mapstructure:"ssh_host_keys" yaml:"ssh_host_keys,omitempty"` + // SSH key used to sign ephemeral certificate keys for upstream authentication. + // This key must not be group/world-readable on disk, and should not itself be + // a certificate key. + SSHUserCAKeyFile string `mapstructure:"ssh_user_ca_key_file" yaml:"ssh_user_ca_key_file,omitempty"` + // String contents of SSH key used to sign ephemeral certificate keys for + // upstream authentication. Mutually exclusive with ssh_user_ca_key_file. + SSHUserCAKey string `mapstructure:"ssh_user_ca_key" yaml:"ssh_user_ca_key,omitempty"` // DataBrokerURLString is the routable destination of the databroker service's gRPC endpoint. DataBrokerURLString string `mapstructure:"databroker_service_url" yaml:"databroker_service_url,omitempty"` diff --git a/config/policy.go b/config/policy.go index 9a41b44e9..847c3de34 100644 --- a/config/policy.go +++ b/config/policy.go @@ -930,6 +930,11 @@ func (p *Policy) IsUDPUpstream() bool { return len(p.To) > 0 && p.To[0].URL.Scheme == "udp" } +// IsSSH returns true if the route is for SSH. +func (p *Policy) IsSSH() bool { + return len(p.From) > 0 && strings.HasPrefix(p.From, "ssh://") +} + // AllAllowedDomains returns all the allowed domains. func (p *Policy) AllAllowedDomains() []string { var ads []string diff --git a/config/policy_test.go b/config/policy_test.go index a137b4c9f..418bdcbe6 100644 --- a/config/policy_test.go +++ b/config/policy_test.go @@ -460,6 +460,14 @@ func TestPolicy_IsTCPUpstream(t *testing.T) { assert.False(t, p3.IsTCPUpstream()) } +func TestPolicy_IsSSH(t *testing.T) { + p1 := Policy{From: "https://example.com"} + assert.False(t, p1.IsSSH()) + + p2 := Policy{From: "ssh://example.com"} + assert.True(t, p2.IsSSH()) +} + func mustParseWeightedURLs(t testing.TB, urls ...string) WeightedURLs { wu, err := ParseWeightedUrls(urls...) require.NoError(t, err) diff --git a/go.mod b/go.mod index e425bf251..638f9fbac 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/caddyserver/certmagic v0.23.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cloudflare/circl v1.6.1 + github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f github.com/cockroachdb/pebble/v2 v2.0.4 github.com/coreos/go-oidc/v3 v3.14.1 github.com/docker/docker v28.2.2+incompatible @@ -54,7 +55,7 @@ require ( github.com/pires/go-proxyproto v0.8.1 github.com/pomerium/csrf v1.7.0 github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 - github.com/pomerium/envoy-custom v1.33.0 + github.com/pomerium/envoy-custom v1.33.1-0.20250618175753-a0feae248696 github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46 github.com/pomerium/webauthn v0.0.0-20240603205124-0428df511172 github.com/prometheus/client_golang v1.22.0 @@ -145,7 +146,6 @@ require ( github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cenkalti/backoff/v5 v5.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect github.com/cockroachdb/crlib v0.0.0-20241015224233-894974b3ad94 // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect diff --git a/go.sum b/go.sum index 493e5eec1..c308d99aa 100644 --- a/go.sum +++ b/go.sum @@ -171,8 +171,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= -github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= +github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cockroachdb/crlib v0.0.0-20241015224233-894974b3ad94 h1:bvJv505UUfjzbaIPdNS4AEkHreDqQk6yuNpsdRHpwFA= github.com/cockroachdb/crlib v0.0.0-20241015224233-894974b3ad94/go.mod h1:Gq51ZeKaFCXk6QwuGM0w1dnaOqc/F5zKT2zA9D6Xeac= github.com/cockroachdb/datadriven v1.0.3-0.20240530155848-7682d40af056 h1:slXychO2uDM6hYRu4c0pD0udNI8uObfeKN6UInWViS8= @@ -579,8 +579,8 @@ github.com/pomerium/csrf v1.7.0 h1:Qp4t6oyEod3svQtKfJZs589mdUTWKVf7q0PgCKYCshY= github.com/pomerium/csrf v1.7.0/go.mod h1:hAPZV47mEj2T9xFs+ysbum4l7SF1IdrryYaY6PdoIqw= github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524 h1:3YQY1sb54tEEbr0L73rjHkpLB0IB6qh3zl1+XQbMLis= github.com/pomerium/datasource v0.18.2-0.20221108160055-c6134b5ed524/go.mod h1:7fGbUYJnU8RcxZJvUvhukOIBv1G7LWDAHMfDxAf5+Y0= -github.com/pomerium/envoy-custom v1.33.0 h1:WK5f+mGIu6VcxYTTeZqth6If6EWHrxymfvc+Rwm5Vl8= -github.com/pomerium/envoy-custom v1.33.0/go.mod h1:afbaKE6YfshVUOrYc6XWUWfZcXencWmi1jTc00ki0Oo= +github.com/pomerium/envoy-custom v1.33.1-0.20250618175753-a0feae248696 h1:ojei2rggKHZYnDQyCbjeG2mdyqCW8E2tZpxOuiDBwxc= +github.com/pomerium/envoy-custom v1.33.1-0.20250618175753-a0feae248696/go.mod h1:+wpbZvum83bq/OD4cp9/8IZiMV6boBkwDhlFPLOoWoI= github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46 h1:NRTg8JOXCxcIA1lAgD74iYud0rbshbWOB3Ou4+Huil8= github.com/pomerium/protoutil v0.0.0-20240813175624-47b7ac43ff46/go.mod h1:QqZmx6ZgPxz18va7kqoT4t/0yJtP7YFIDiT/W2n2fZ4= github.com/pomerium/webauthn v0.0.0-20240603205124-0428df511172 h1:TqoPqRgXSHpn+tEJq6H72iCS5pv66j3rPprThUEZg0E= diff --git a/pkg/cmd/pomerium/pomerium.go b/pkg/cmd/pomerium/pomerium.go index 255b55d3e..8c12e4cbb 100644 --- a/pkg/cmd/pomerium/pomerium.go +++ b/pkg/cmd/pomerium/pomerium.go @@ -13,6 +13,7 @@ import ( "go.uber.org/automaxprocs/maxprocs" "golang.org/x/sync/errgroup" + extensions_ssh "github.com/pomerium/envoy-custom/api/extensions/filters/network/ssh" "github.com/pomerium/pomerium/authenticate" "github.com/pomerium/pomerium/authorize" "github.com/pomerium/pomerium/config" @@ -268,6 +269,7 @@ func setupAuthorize(ctx context.Context, src config.Source, controlPlane *contro return nil, fmt.Errorf("error creating authorize service: %w", err) } envoy_service_auth_v3.RegisterAuthorizationServer(controlPlane.GRPCServer, svc) + extensions_ssh.RegisterStreamManagementServer(controlPlane.GRPCServer, svc) log.Ctx(ctx).Info().Msg("enabled authorize service") src.OnConfigChange(ctx, svc.OnConfigChange)