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 709c5dd7 authored by Igor Drozdov's avatar Igor Drozdov
Browse files

Make PROXY policy configurable

It would give us more flexibility when we decide to enable
PROXY protocol
parent 733845f9
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
@@ -210,6 +211,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