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 493add9e authored by Igor Drozdov's avatar Igor Drozdov Committed by Ash McKenzie
Browse files

Abort long-running unauthenticated SSH connections

parent 5aa16a0b
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -84,6 +84,8 @@ sshd:
readiness_probe: "/start"
# The endpoint that returns 200 OK if the server is alive. Defaults to "/health".
liveness_probe: "/health"
# The server disconnects after this time (in seconds) if the user has not successfully logged in. Defaults to 60.
login_grace_time: 60
# SSH host key files.
host_key_files:
- /run/secrets/ssh-hostkeys/ssh_host_rsa_key
Loading
Loading
Loading
Loading
@@ -26,6 +26,7 @@ type ServerConfig struct {
ProxyProtocol bool `yaml:"proxy_protocol,omitempty"`
WebListen string `yaml:"web_listen,omitempty"`
ConcurrentSessionsLimit int64 `yaml:"concurrent_sessions_limit,omitempty"`
LoginGraceTimeSeconds uint64 `yaml:"login_grace_time"`
GracePeriodSeconds uint64 `yaml:"grace_period"`
ReadinessProbe string `yaml:"readiness_probe"`
LivenessProbe string `yaml:"liveness_probe"`
Loading
Loading
@@ -78,6 +79,7 @@ var (
Listen: "[::]:22",
WebListen: "localhost:9122",
ConcurrentSessionsLimit: 10,
LoginGraceTimeSeconds: 60,
GracePeriodSeconds: 10,
ReadinessProbe: "/start",
LivenessProbe: "/health",
Loading
Loading
@@ -89,6 +91,10 @@ var (
}
)
func (sc *ServerConfig) LoginGraceTime() time.Duration {
return time.Duration(sc.LoginGraceTimeSeconds) * time.Second
}
func (sc *ServerConfig) GracePeriod() time.Duration {
return time.Duration(sc.GracePeriodSeconds) * time.Second
}
Loading
Loading
Loading
Loading
@@ -164,7 +164,7 @@ func (s *Server) handleConn(ctx context.Context, nconn net.Conn) {
ctxlog.Info("server: handleConn: start")
sconn, chans, reqs, err := ssh.NewServerConn(nconn, s.serverConfig.get(ctx))
sconn, chans, reqs, err := s.initSSHConnection(ctx, nconn)
if err != nil {
ctxlog.WithError(err).Error("server: handleConn: failed to initialize SSH connection")
return
Loading
Loading
@@ -187,6 +187,20 @@ func (s *Server) handleConn(ctx context.Context, nconn net.Conn) {
ctxlog.Info("server: handleConn: done")
}
func (s *Server) initSSHConnection(ctx context.Context, nconn net.Conn) (sconn *ssh.ServerConn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request, err error) {
timer := time.AfterFunc(s.Config.Server.LoginGraceTime(), func() {
nconn.Close()
})
defer func() {
// If time.Stop() equals false, that means that AfterFunc has been executed
if !timer.Stop() {
err = fmt.Errorf("initSSHConnection: ssh handshake timeout")
}
}()
return ssh.NewServerConn(nconn, s.serverConfig.get(ctx))
}
func unconditionalRequirePolicy(_ net.Addr) (proxyproto.Policy, error) {
return proxyproto.REQUIRE, nil
}
Loading
Loading
@@ -3,6 +3,7 @@ package sshd
import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptest"
"os"
Loading
Loading
@@ -134,6 +135,33 @@ func TestInvalidServerConfig(t *testing.T) {
require.Nil(t, s.Shutdown())
}
func TestLoginGraceTime(t *testing.T) {
s := setupServer(t)
unauthenticatedRequestStatus := make(chan string)
completed := make(chan bool)
clientCfg := clientConfig(t)
clientCfg.HostKeyCallback = func(_ string, _ net.Addr, _ ssh.PublicKey) error {
unauthenticatedRequestStatus <- "authentication-started"
<-completed // Wait infinitely
return nil
}
go func() {
// Start an SSH connection that never ends
ssh.Dial("tcp", serverUrl, clientCfg)
}()
require.Equal(t, "authentication-started", <-unauthenticatedRequestStatus)
// Verify that the server shutdowns instantly without waiting for any connection to execute
// That means that the server doesn't have any connections to wait for
require.NoError(t, s.Shutdown())
verifyStatus(t, s, StatusClosed)
}
func setupServer(t *testing.T) *Server {
t.Helper()
Loading
Loading
@@ -183,6 +211,7 @@ func setupServerWithConfig(t *testing.T, cfg *config.Config) *Server {
cfg.User = user
cfg.Server.Listen = serverUrl
cfg.Server.ConcurrentSessionsLimit = 1
cfg.Server.LoginGraceTimeSeconds = 1
cfg.Server.HostKeyFiles = []string{path.Join(testhelper.TestRoot, "certs/valid/server.key")}
s, err := NewServer(cfg)
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