As we reevaluate how to best support and maintain Staging Ref in the future, we encourage development teams using this environment to highlight their use cases in the following issue: https://gitlab.com/gitlab-com/gl-infra/software-delivery/framework/software-delivery-framework-issue-tracker/-/issues/36.

Skip to content
Snippets Groups Projects
Commit 42715b4c authored by Stan Hu's avatar Stan Hu
Browse files

Merge branch 'id-make-proxy-policy-configurable' into 'main'

Make PROXY policy configurable

See merge request gitlab-org/gitlab-shell!619
parents 4c2d7ca9 709c5dd7
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -69,6 +69,9 @@ sshd:
# Set to true if gitlab-sshd is being fronted by a load balancer that implements
# the PROXY protocol.
proxy_protocol: false
# Proxy protocol policy ("use", "require", "reject", "ignore"), "use" is the default value
# Values: https://github.com/pires/go-proxyproto/blob/195fedcfbfc1be163f3a0d507fac1709e9d81fed/policy.go#L20
proxy_policy: "use"
# 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.
Loading
Loading
Loading
Loading
@@ -24,6 +24,7 @@ const (
type ServerConfig struct {
Listen string `yaml:"listen,omitempty"`
ProxyProtocol bool `yaml:"proxy_protocol,omitempty"`
ProxyPolicy string `yaml:"proxy_policy,omitempty"`
WebListen string `yaml:"web_listen,omitempty"`
ConcurrentSessionsLimit int64 `yaml:"concurrent_sessions_limit,omitempty"`
GracePeriodSeconds uint64 `yaml:"grace_period"`
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ import (
"fmt"
"net"
"net/http"
"strings"
"sync"
"time"
Loading
Loading
@@ -95,7 +96,7 @@ func (s *Server) listen(ctx context.Context) error {
if s.Config.Server.ProxyProtocol {
sshListener = &proxyproto.Listener{
Listener: sshListener,
Policy: unconditionalRequirePolicy,
Policy: s.requirePolicy,
ReadHeaderTimeout: ProxyHeaderTimeout,
}
Loading
Loading
@@ -204,6 +205,17 @@ func (s *Server) handleConn(ctx context.Context, nconn net.Conn) {
}).Info("server: handleConn: done")
}
func unconditionalRequirePolicy(_ net.Addr) (proxyproto.Policy, error) {
return proxyproto.REQUIRE, nil
func (s *Server) requirePolicy(_ net.Addr) (proxyproto.Policy, error) {
// Set the Policy value based on config
// Values are taken from https://github.com/pires/go-proxyproto/blob/195fedcfbfc1be163f3a0d507fac1709e9d81fed/policy.go#L20
switch strings.ToLower(s.Config.Server.ProxyPolicy) {
case "require":
return proxyproto.REQUIRE, nil
case "ignore":
return proxyproto.IGNORE, nil
case "reject":
return proxyproto.REJECT, nil
default:
return proxyproto.USE, nil
}
}
Loading
Loading
@@ -3,6 +3,7 @@ package sshd
import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptest"
"os"
Loading
Loading
@@ -10,6 +11,7 @@ import (
"testing"
"time"
"github.com/pires/go-proxyproto"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
Loading
Loading
@@ -48,15 +50,101 @@ func TestListenAndServe(t *testing.T) {
}
func TestListenAndServeRejectsPlainConnectionsWhenProxyProtocolEnabled(t *testing.T) {
setupServerWithProxyProtocolEnabled(t)
target, err := net.ResolveTCPAddr("tcp", serverUrl)
require.NoError(t, err)
client, err := ssh.Dial("tcp", serverUrl, clientConfig(t))
if client != nil {
client.Close()
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,
}
testCases := []struct {
desc string
proxyPolicy string
header *proxyproto.Header
isRejected bool
}{
{
desc: "USE (default) without a header",
proxyPolicy: "",
header: nil,
isRejected: false,
},
{
desc: "USE (default) with a header",
proxyPolicy: "",
header: header,
isRejected: false,
},
{
desc: "REQUIRE without a header",
proxyPolicy: "require",
header: nil,
isRejected: true,
},
{
desc: "REQUIRE with a header",
proxyPolicy: "require",
header: header,
isRejected: false,
},
{
desc: "REJECT without a header",
proxyPolicy: "reject",
header: nil,
isRejected: false,
},
{
desc: "REJECT with a header",
proxyPolicy: "reject",
header: header,
isRejected: true,
},
{
desc: "IGNORE without a header",
proxyPolicy: "ignore",
header: nil,
isRejected: false,
},
{
desc: "IGNORE with a header",
proxyPolicy: "ignore",
header: header,
isRejected: false,
},
}
require.Error(t, err, "Expected plain SSH request to be failed")
require.Regexp(t, "ssh: handshake failed", err.Error())
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
setupServerWithConfig(t, &config.Config{Server: config.ServerConfig{ProxyProtocol: true, ProxyPolicy: tc.proxyPolicy}})
conn, err := net.DialTCP("tcp", nil, target)
require.NoError(t, err)
if tc.header != nil {
_, err := header.WriteTo(conn)
require.NoError(t, err)
}
sshConn, _, _, err := ssh.NewClientConn(conn, serverUrl, clientConfig(t))
if sshConn != nil {
sshConn.Close()
}
if tc.isRejected {
require.Error(t, err, "Expected plain SSH request to be failed")
require.Regexp(t, "ssh: handshake failed", err.Error())
} else {
require.NoError(t, err)
}
})
}
}
func TestCorrelationId(t *testing.T) {
Loading
Loading
@@ -140,12 +228,6 @@ func setupServer(t *testing.T) *Server {
return setupServerWithConfig(t, nil)
}
func setupServerWithProxyProtocolEnabled(t *testing.T) *Server {
t.Helper()
return setupServerWithConfig(t, &config.Config{Server: config.ServerConfig{ProxyProtocol: true}})
}
func setupServerWithConfig(t *testing.T, cfg *config.Config) *Server {
t.Helper()
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment