rough changes for ssh to work with latest envoy-custom from main

This commit is contained in:
Joe Kralicky 2025-06-17 15:44:12 +00:00
parent 4e6aa84ca4
commit b5e70f7a6a
No known key found for this signature in database
GPG key ID: 75C4875F34A9FB79
6 changed files with 40 additions and 46 deletions

View file

@ -469,7 +469,7 @@ func (a *Authorize) ManageStream(
func (a *Authorize) getSSHRouteForHostname(hostname string) *config.Policy { func (a *Authorize) getSSHRouteForHostname(hostname string) *config.Policy {
opts := a.currentConfig.Load().Options opts := a.currentConfig.Load().Options
from := "ssh://" + strings.TrimSuffix(strings.Join([]string{hostname, opts.SSHHostname}, "."), ".") from := "ssh://" + hostname
for r := range opts.GetAllPolicies() { for r := range opts.GetAllPolicies() {
if r.From == from { if r.From == from {
return r return r
@ -1221,18 +1221,18 @@ func (a *Authorize) NewPortalCommand(
var routes []string var routes []string
for r := range cfg.Options.GetAllPolicies() { for r := range cfg.Options.GetAllPolicies() {
if strings.HasPrefix(r.From, "ssh://") { if strings.HasPrefix(r.From, "ssh://") {
routes = append(routes, fmt.Sprintf("%s@%s", state.Username, strings.TrimSuffix(strings.TrimPrefix(r.From, "ssh://"), "."+cfg.Options.SSHHostname))) routes = append(routes, fmt.Sprintf("%s@%s", state.Username, strings.TrimPrefix(r.From, "ssh://")))
} }
} }
items := []list.Item{} items := []list.Item{}
for _, route := range routes { for _, route := range routes {
items = append(items, item(route)) items = append(items, item(route))
} }
a.activeStreams.Range(func(id uint64, _ *StreamState) { // a.activeStreams.Range(func(id uint64, _ *StreamState) {
if id != state.StreamID { // if id != state.StreamID {
items = append(items, item(fmt.Sprintf("[demo] mirror session: %v", id))) // items = append(items, item(fmt.Sprintf("[demo] mirror session: %v", id)))
} // }
}) // })
l := list.New(items, itemDelegate{}, int(ptyInfo.WidthColumns-2), int(ptyInfo.HeightRows-2)) l := list.New(items, itemDelegate{}, int(ptyInfo.WidthColumns-2), int(ptyInfo.HeightRows-2))
l.Title = "Connect to which server?" l.Title = "Connect to which server?"

View file

@ -5,24 +5,20 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"strings"
"time" "time"
xds_core_v3 "github.com/cncf/xds/go/xds/core/v3" xds_core_v3 "github.com/cncf/xds/go/xds/core/v3"
xds_matcher_v3 "github.com/cncf/xds/go/xds/type/matcher/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_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" envoy_config_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
extensions_compressor_zstd_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/compression/zstd/compressor/v3"
envoy_generic_proxy_action_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/generic_proxy/action/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_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_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" 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" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
extensions_ssh "github.com/pomerium/envoy-custom/api/extensions/filters/network/ssh" extensions_ssh "github.com/pomerium/envoy-custom/api/extensions/filters/network/ssh"
extensions_ssh_session_recording "github.com/pomerium/envoy-custom/api/extensions/filters/network/ssh/filters/session_recording"
"github.com/pomerium/pomerium/config" "github.com/pomerium/pomerium/config"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb"
) )
func (b *Builder) buildSSHListener(ctx context.Context, cfg *config.Config) (*envoy_config_listener_v3.Listener, error) { func (b *Builder) buildSSHListener(ctx context.Context, cfg *config.Config) (*envoy_config_listener_v3.Listener, error) {
@ -65,29 +61,29 @@ func (b *Builder) buildSSHListener(ctx context.Context, cfg *config.Config) (*en
}), }),
}, },
Filters: []*envoy_config_core_v3.TypedExtensionConfig{ Filters: []*envoy_config_core_v3.TypedExtensionConfig{
{ // {
Name: "envoy.filters.generic.ssh.session_recording", // Name: "envoy.filters.generic.ssh.session_recording",
TypedConfig: marshalAny(&extensions_ssh_session_recording.Config{ // TypedConfig: marshalAny(&extensions_ssh_session_recording.Config{
StorageDir: "/tmp/recordings", // StorageDir: "/tmp/recordings",
GrpcService: authorizeService, // GrpcService: authorizeService,
CompressorLibrary: &envoy_config_core_v3.TypedExtensionConfig{ // CompressorLibrary: &envoy_config_core_v3.TypedExtensionConfig{
Name: "envoy.compression.zstd.compressor", // Name: "envoy.compression.zstd.compressor",
TypedConfig: marshalAny(&extensions_compressor_zstd_v3.Zstd{ // TypedConfig: marshalAny(&extensions_compressor_zstd_v3.Zstd{
CompressionLevel: wrapperspb.UInt32(19), // CompressionLevel: wrapperspb.UInt32(19),
EnableChecksum: false, // EnableChecksum: false,
Strategy: extensions_compressor_zstd_v3.Zstd_BTULTRA2, // Strategy: extensions_compressor_zstd_v3.Zstd_BTULTRA2,
ChunkSize: wrapperspb.UInt32(8192), // ChunkSize: wrapperspb.UInt32(8192),
}), // }),
}, // },
// FileManagerConfig: &async_filesv3.AsyncFileManagerConfig{ // // FileManagerConfig: &async_filesv3.AsyncFileManagerConfig{
// ManagerType: &async_filesv3.AsyncFileManagerConfig_ThreadPool_{ // // ManagerType: &async_filesv3.AsyncFileManagerConfig_ThreadPool_{
// ThreadPool: &async_filesv3.AsyncFileManagerConfig_ThreadPool{ // // ThreadPool: &async_filesv3.AsyncFileManagerConfig_ThreadPool{
// ThreadCount: 2, // // ThreadCount: 2,
// }, // // },
// }, // // },
// }, // // },
}), // }),
}, // },
// { // {
// Name: "envoy.filters.generic.ssh.session_multiplexing", // Name: "envoy.filters.generic.ssh.session_multiplexing",
// TypedConfig: marshalAny(&extensions_ssh_session_multiplexing.Config{}), // TypedConfig: marshalAny(&extensions_ssh_session_multiplexing.Config{}),
@ -123,10 +119,6 @@ func (b *Builder) buildRouteConfig(_ context.Context, cfg *config.Config) (*envo
continue continue
} }
fromHost := from.Hostname() fromHost := from.Hostname()
if cfg.Options.SSHHostname != "" {
suffix := "." + cfg.Options.SSHHostname
fromHost = strings.TrimSuffix(fromHost, suffix)
}
if len(route.To) > 1 { if len(route.To) > 1 {
return nil, fmt.Errorf("only one 'to' entry allowed for ssh routes") return nil, fmt.Errorf("only one 'to' entry allowed for ssh routes")
} }

View file

@ -246,7 +246,6 @@ type Options struct {
GRPCClientTimeout time.Duration `mapstructure:"grpc_client_timeout" yaml:"grpc_client_timeout,omitempty"` GRPCClientTimeout time.Duration `mapstructure:"grpc_client_timeout" yaml:"grpc_client_timeout,omitempty"`
SSHAddr string `mapstructure:"ssh_address" yaml:"ssh_address,omitempty"` SSHAddr string `mapstructure:"ssh_address" yaml:"ssh_address,omitempty"`
SSHHostname string `mapstructure:"ssh_hostname" yaml:"ssh_hostname,omitempty"`
SSHHostKeys []string `mapstructure:"ssh_host_keys" yaml:"ssh_host_keys,omitempty"` SSHHostKeys []string `mapstructure:"ssh_host_keys" yaml:"ssh_host_keys,omitempty"`
SSHUserCAKey string `mapstructure:"ssh_user_ca_key" yaml:"ssh_user_ca_key,omitempty"` SSHUserCAKey string `mapstructure:"ssh_user_ca_key" yaml:"ssh_user_ca_key,omitempty"`

View file

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"crypto/ed25519" "crypto/ed25519"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"strings" "strings"
@ -20,6 +21,7 @@ import (
"github.com/pomerium/pomerium/internal/testenv/scenarios" "github.com/pomerium/pomerium/internal/testenv/scenarios"
"github.com/pomerium/pomerium/internal/testenv/snippets" "github.com/pomerium/pomerium/internal/testenv/snippets"
"github.com/pomerium/pomerium/internal/testenv/upstreams" "github.com/pomerium/pomerium/internal/testenv/upstreams"
"github.com/pomerium/pomerium/internal/testenv/values"
) )
func TestSSH(t *testing.T) { func TestSSH(t *testing.T) {
@ -65,7 +67,9 @@ func TestSSH(t *testing.T) {
) )
up.SetServerConnCallback(echoShell{t}.handleConnection) up.SetServerConnCallback(echoShell{t}.handleConnection)
r := up.Route(). r := up.Route().
From(env.SubdomainURLWithScheme("example", "ssh")). From(values.Bind(env.Ports().ProxySSH, func(port int) string {
return fmt.Sprintf("ssh://example:%d", port)
})).
Policy(func(p *config.Policy) { p.AllowAnyAuthenticatedUser = true }) Policy(func(p *config.Policy) { p.AllowAnyAuthenticatedUser = true })
env.AddUpstream(up) env.AddUpstream(up)
env.Start() env.Start()
@ -121,7 +125,9 @@ func TestSSH_JumpHostMode(t *testing.T) {
) )
up.SetServerConnCallback(echoShell{t}.handleConnection) up.SetServerConnCallback(echoShell{t}.handleConnection)
r := up.Route(). r := up.Route().
From(env.SubdomainURLWithScheme("example", "ssh")). From(values.Bind(env.Ports().ProxySSH, func(port int) string {
return fmt.Sprintf("ssh://example:%d", port)
})).
Policy(func(p *config.Policy) { p.AllowAnyAuthenticatedUser = true }) Policy(func(p *config.Policy) { p.AllowAnyAuthenticatedUser = true })
env.AddUpstream(up) env.AddUpstream(up)
env.Start() env.Start()

View file

@ -635,7 +635,6 @@ func (e *environment) Start() {
cfg.Options.GRPCAddr = fmt.Sprintf("%s:%d", e.host, e.ports.ProxyGRPC.Value()) cfg.Options.GRPCAddr = fmt.Sprintf("%s:%d", e.host, e.ports.ProxyGRPC.Value())
cfg.Options.SSHAddr = fmt.Sprintf("%s:%d", e.host, e.ports.ProxySSH.Value()) cfg.Options.SSHAddr = fmt.Sprintf("%s:%d", e.host, e.ports.ProxySSH.Value())
cfg.Options.EnvoyAdminAddress = fmt.Sprintf("%s:%d", e.host, e.ports.EnvoyAdmin.Value()) cfg.Options.EnvoyAdminAddress = fmt.Sprintf("%s:%d", e.host, e.ports.EnvoyAdmin.Value())
cfg.Options.SSHHostname = localDomainName
cfg.Options.MetricsAddr = fmt.Sprintf("%s:%d", e.host, e.ports.ProxyMetrics.Value()) cfg.Options.MetricsAddr = fmt.Sprintf("%s:%d", e.host, e.ports.ProxyMetrics.Value())
cfg.Options.CAFile = filepath.Join(e.tempDir, "certs", "ca.pem") cfg.Options.CAFile = filepath.Join(e.tempDir, "certs", "ca.pem")
cfg.Options.CertFile = filepath.Join(e.tempDir, "certs", "trusted.pem") cfg.Options.CertFile = filepath.Join(e.tempDir, "certs", "trusted.pem")

View file

@ -5,7 +5,6 @@ import (
"fmt" "fmt"
"log" "log"
"net" "net"
"strings"
"github.com/pomerium/pomerium/internal/testenv" "github.com/pomerium/pomerium/internal/testenv"
"github.com/pomerium/pomerium/internal/testenv/values" "github.com/pomerium/pomerium/internal/testenv/values"
@ -109,7 +108,7 @@ type sshUpstream struct {
serverPort values.MutableValue[int] serverPort values.MutableValue[int]
// XXX: does it make sense to cache clients? // XXX: does it make sense to cache clients?
//clientCache sync.Map // map[testenv.Route]*ssh.Client // clientCache sync.Map // map[testenv.Route]*ssh.Client
serverConnCallback ServerConnCallback serverConnCallback ServerConnCallback
} }
@ -202,8 +201,7 @@ func (h *sshUpstream) handleConnection(ctx context.Context, conn net.Conn) {
// Dial implements SSHUpstream. // Dial implements SSHUpstream.
func (h *sshUpstream) Dial(r testenv.Route, config *ssh.ClientConfig) (*ssh.Client, error) { func (h *sshUpstream) Dial(r testenv.Route, config *ssh.ClientConfig) (*ssh.Client, error) {
return ssh.Dial("tcp", strings.TrimPrefix(r.URL().Value(), "ssh://"), config) return ssh.Dial("tcp", h.Env().Config().Options.SSHAddr, config)
//return ssh.Dial("tcp", h.Env().Config().Options.SSHAddr, config)
} }
// DirectDial implements SSHUpstream. // DirectDial implements SSHUpstream.