From 47fa4d7dad0e62f38aa2e7e119359872c215eae8 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Fri, 9 Apr 2021 12:09:29 +0100 Subject: gitlab-sshd: Support the PROXY protocol --- Makefile | 2 +- cmd/gitlab-sshd/acceptance_test.go | 29 ++++++++++++++++++++++++++++- config.yml.example | 3 +++ go.mod | 1 + go.sum | 2 ++ internal/config/config.go | 11 ++++++----- internal/sshd/sshd.go | 15 ++++++++++++--- 7 files changed, 53 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 749674b..4bd94e6 100644 --- a/Makefile +++ b/Makefile @@ -43,4 +43,4 @@ check: bin/check clean: - rm -f bin/check bin/gitlab-shell bin/gitlab-shell-authorized-keys-check bin/gitlab-shell-authorized-principals-check + rm -f bin/check bin/gitlab-shell bin/gitlab-shell-authorized-keys-check bin/gitlab-shell-authorized-principals-check bin/gitlab-sshd diff --git a/cmd/gitlab-sshd/acceptance_test.go b/cmd/gitlab-sshd/acceptance_test.go index 1b6931b..949afe8 100644 --- a/cmd/gitlab-sshd/acceptance_test.go +++ b/cmd/gitlab-sshd/acceptance_test.go @@ -8,6 +8,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "net/http" "net/http/httptest" "os" @@ -18,6 +19,7 @@ import ( "testing" "github.com/mikesmitty/edkey" + "github.com/pires/go-proxyproto" "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" ) @@ -72,6 +74,7 @@ secret: "0123456789abcdef" gitlab_url: "` + gitlabUrl + `" sshd: listen: "127.0.0.1:0" + proxy_protocol: true web_listen: "" host_key_files: - "` + hostKeyPath + `"`) @@ -89,13 +92,37 @@ func buildClient(t *testing.T, addr string, hostKey ed25519.PublicKey) *ssh.Clie clientSigner, err := ssh.NewSignerFromKey(clientPrivKey) require.NoError(t, err) - client, err := ssh.Dial("tcp", addr, &ssh.ClientConfig{ + // Use the proxy protocol to spoof our client address + target, err := net.ResolveTCPAddr("tcp", addr) + require.NoError(t, err) + conn, err := net.DialTCP("tcp", nil, target) + require.NoError(t, err) + t.Cleanup(func() { conn.Close() }) + + // Create a proxyprotocol header or use HeaderProxyFromAddrs() if you + // have two conn's + header := &proxyproto.Header{ + Version: 2, + Command: proxyproto.PROXY, + TransportProtocol: proxyproto.TCPv4, + SourceAddr: &net.TCPAddr{ + IP: net.ParseIP("10.1.1.1"), + Port: 1000, + }, + DestinationAddr: target, + } + // After the connection was created write the proxy headers first + _, err = header.WriteTo(conn) + require.NoError(t, err) + + sshConn, chans, reqs, err := ssh.NewClientConn(conn, addr, &ssh.ClientConfig{ User: "git", Auth: []ssh.AuthMethod{ssh.PublicKeys(clientSigner)}, HostKeyCallback: ssh.FixedHostKey(pubKey), }) require.NoError(t, err) + client := ssh.NewClient(sshConn, chans, reqs) t.Cleanup(func() { client.Close() }) return client diff --git a/config.yml.example b/config.yml.example index 8a00375..248136b 100644 --- a/config.yml.example +++ b/config.yml.example @@ -66,6 +66,9 @@ audit_usernames: false sshd: # Address which the SSH server listens on. Defaults to [::]:22. listen: "[::]:22" + # Set to true if gitlab-sshd is being fronted by a load balancer that implements + # the PROXY protocol + proxy_protocol: false # Address which the server listens on HTTP for monitoring/health checks. Defaults to localhost:9122. web_listen: "localhost:9122" # Maximum number of concurrent sessions allowed on a single SSH connection. Defaults to 10. diff --git a/go.mod b/go.mod index 6016ae4..2eeb0ad 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/mattn/go-shellwords v1.0.11 github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a github.com/otiai10/copy v1.4.2 + github.com/pires/go-proxyproto v0.5.0 github.com/prometheus/client_golang v1.9.0 github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index df15888..3bb8cc4 100644 --- a/go.sum +++ b/go.sum @@ -396,6 +396,8 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo= +github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= diff --git a/internal/config/config.go b/internal/config/config.go index 36f8625..2709277 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -18,6 +18,7 @@ const ( type ServerConfig struct { Listen string `yaml:"listen,omitempty"` + ProxyProtocol bool `yaml:"proxy_protocol,omitempty"` WebListen string `yaml:"web_listen,omitempty"` ConcurrentSessionsLimit int64 `yaml:"concurrent_sessions_limit,omitempty"` HostKeyFiles []string `yaml:"host_key_files,omitempty"` @@ -52,15 +53,15 @@ type Config struct { // The defaults to apply before parsing the config file(s). var ( DefaultConfig = Config{ - LogFile: "gitlab-shell.log", + LogFile: "gitlab-shell.log", LogFormat: "text", - Server: DefaultServerConfig, - User: "git", + Server: DefaultServerConfig, + User: "git", } DefaultServerConfig = ServerConfig{ - Listen: "[::]:22", - WebListen: "localhost:9122", + Listen: "[::]:22", + WebListen: "localhost:9122", ConcurrentSessionsLimit: 10, HostKeyFiles: []string{ "/run/secrets/ssh-hostkeys/ssh_host_rsa_key", diff --git a/internal/sshd/sshd.go b/internal/sshd/sshd.go index 74029b0..7bd81ff 100644 --- a/internal/sshd/sshd.go +++ b/internal/sshd/sshd.go @@ -10,17 +10,20 @@ import ( "strconv" "time" + log "github.com/sirupsen/logrus" + + "github.com/pires/go-proxyproto" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" - log "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh" + "golang.org/x/sync/semaphore" + "gitlab.com/gitlab-org/gitlab-shell/internal/command" "gitlab.com/gitlab-org/gitlab-shell/internal/command/commandargs" "gitlab.com/gitlab-org/gitlab-shell/internal/command/readwriter" "gitlab.com/gitlab-org/gitlab-shell/internal/config" "gitlab.com/gitlab-org/gitlab-shell/internal/gitlabnet/authorizedkeys" "gitlab.com/gitlab-org/gitlab-shell/internal/sshenv" - "golang.org/x/crypto/ssh" - "golang.org/x/sync/semaphore" ) const ( @@ -73,6 +76,12 @@ func Run(cfg *config.Config) error { if err != nil { return fmt.Errorf("failed to listen for connection: %w", err) } + if cfg.Server.ProxyProtocol { + sshListener = &proxyproto.Listener{Listener: sshListener} + + log.Info("Proxy protocol is enabled") + } + defer sshListener.Close() log.Infof("Listening on %v", sshListener.Addr().String()) -- cgit v1.2.1