Skip to content
This repository was archived by the owner on Jan 6, 2025. It is now read-only.

Commit e03b685

Browse files
committed
Implement support for the PROXY protocol
1 parent 625361f commit e03b685

File tree

5 files changed

+26
-3
lines changed

5 files changed

+26
-3
lines changed

config/ssh.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ type SSHConfig struct {
2525
// See https://tools.ietf.org/html/rfc4253#page-4 section 4.2. Protocol Version Exchange
2626
// The trailing CR and LF characters should NOT be added to this string.
2727
ServerVersion SSHServerVersion `json:"serverVersion" yaml:"serverVersion" default:"SSH-2.0-ContainerSSH"`
28+
// AllowedProxies is a list of IP addresses or CIDR ranges that are allowed to use the
29+
// PROXY protocol to override the connection originator IP address.
30+
AllowedProxies []string `json:"allowedProxies" yaml:"allowedProxies"`
2831
// Ciphers are the ciphers offered to the client.
2932
Ciphers SSHCipherList `json:"ciphers" yaml:"ciphers" default:"[\"chacha20-poly1305@openssh.com\",\"aes256-gcm@openssh.com\",\"aes128-gcm@openssh.com\",\"aes256-ctr\",\"aes192-ctr\",\"aes128-ctr\"]" comment:"SSHCipher suites to use"`
3033
// KexAlgorithms are the key exchange algorithms offered to the client.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require (
1818
github.com/mitchellh/mapstructure v1.4.3
1919
github.com/opencontainers/image-spec v1.0.2
2020
github.com/oschwald/geoip2-golang v1.5.0
21+
github.com/pires/go-proxyproto v0.6.1
2122
github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494
2223
github.com/stretchr/testify v1.7.0
2324
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
627627
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
628628
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
629629
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
630+
github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw=
631+
github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
630632
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
631633
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
632634
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

internal/sshserver/Server_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/containerssh/libcontainerssh/service"
2323
"github.com/stretchr/testify/assert"
2424
"golang.org/x/crypto/ssh"
25+
"github.com/pires/go-proxyproto"
2526
)
2627

2728
//region Tests

internal/sshserver/serverImpl.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
messageCodes "github.com/containerssh/libcontainerssh/message"
1616
"github.com/containerssh/libcontainerssh/service"
1717
"golang.org/x/crypto/ssh"
18+
"github.com/pires/go-proxyproto"
1819
)
1920

2021
type serverImpl struct {
@@ -54,11 +55,20 @@ func (s *serverImpl) RunWithLifecycle(lifecycle service.Lifecycle) error {
5455
Control: s.socketControl,
5556
}
5657

58+
useProxy := len(s.cfg.AllowedProxies) > 0
59+
5760
netListener, err := listenConfig.Listen(lifecycle.Context(), "tcp", s.cfg.Listen)
5861
if err != nil {
5962
s.lock.Unlock()
6063
return messageCodes.Wrap(err, messageCodes.ESSHStartFailed, "failed to start SSH server on %s", s.cfg.Listen)
6164
}
65+
if useProxy {
66+
policy := proxyproto.MustStrictWhiteListPolicy(s.cfg.AllowedProxies)
67+
netListener = &proxyproto.Listener{
68+
Listener: netListener,
69+
Policy: policy,
70+
}
71+
}
6272
s.listenSocket = netListener
6373
s.lock.Unlock()
6474
if err := s.handler.OnReady(); err != nil {
@@ -84,7 +94,13 @@ func (s *serverImpl) RunWithLifecycle(lifecycle service.Lifecycle) error {
8494
break
8595
}
8696
s.wg.Add(1)
87-
go s.handleConnection(tcpConn)
97+
logger := s.logger
98+
if useProxy {
99+
proxyConn := tcpConn.(*proxyproto.Conn)
100+
proxyIp := proxyConn.Raw().RemoteAddr()
101+
logger = logger.WithLabel("fromProxy", proxyIp.(*net.TCPAddr).IP.String())
102+
}
103+
go s.handleConnection(logger, tcpConn)
88104
}
89105
lifecycle.Stopping()
90106
s.shuttingDown = true
@@ -501,10 +517,10 @@ func (s *serverImpl) createPasswordCallback(
501517
return passwordCallback
502518
}
503519

504-
func (s *serverImpl) handleConnection(conn net.Conn) {
520+
func (s *serverImpl) handleConnection(logger log.Logger, conn net.Conn) {
505521
addr := conn.RemoteAddr().(*net.TCPAddr)
506522
connectionID := GenerateConnectionID()
507-
logger := s.logger.
523+
logger = logger.
508524
WithLabel("remoteAddr", addr.IP.String()).
509525
WithLabel("connectionId", connectionID)
510526
handlerNetworkConnection, err := s.handler.OnNetworkConnection(*addr, connectionID)

0 commit comments

Comments
 (0)