Skip to content

Commit

Permalink
[SSH] Minor Server Changes (#2088)
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesHarness authored and Harness committed Jun 10, 2024
1 parent 7899ebd commit 90680e2
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 41 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ web/dist
release
.idea
coverage.out
*.rsa
*.rsa.pub

# ignore any executables we build
/gitness
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ release
coverage.out
gitness.session.sql
web/cypress/node_modules
*.rsa
*.rsa.pub
node_modules/

# ignore any executables we build
/gitness

node_modules/

ssh/gitness.rsa
ssh/gitness.rsa.pub
81 changes: 47 additions & 34 deletions ssh/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,8 @@ var (
"hmac-sha2-256",
"hmac-sha2-512",
}
defaultServerKeys = []string{"ssh/gitness.rsa"}
KeepAliveMsg = "[email protected]"
)

var (
ErrHostKeysAreRequired = errors.New("host keys are required")
defaultServerKeyPath = "ssh/gitness.rsa"
KeepAliveMsg = "[email protected]"
)

type Server struct {
Expand All @@ -96,7 +92,7 @@ type Server struct {
KeyExchanges []string
MACs []string
HostKeys []string
KeepAliveInterval int
KeepAliveInterval time.Duration

Verifier publickey.Service
RepoCtrl *repo.Controller
Expand All @@ -119,10 +115,6 @@ func (s *Server) sanitize() error {
s.MACs = defaultMACs
}

if len(s.HostKeys) == 0 {
s.HostKeys = defaultServerKeys
}

if s.KeepAliveInterval == 0 {
s.KeepAliveInterval = 5000
}
Expand Down Expand Up @@ -169,34 +161,22 @@ func (s *Server) ListenAndServe() error {
}

func (s *Server) setupHostKeys() error {
if len(s.HostKeys) == 0 {
return ErrHostKeysAreRequired
}
keys := make([]string, 0, len(s.HostKeys))
// check if file exists and append to slice keys
for _, key := range s.HostKeys {
_, err := os.Stat(key)
if err != nil {
log.Err(err).Msgf("unable to check if %s exists", key)
continue
return fmt.Errorf("failed to read provided host key %q: %w", key, err)
}
keys = append(keys, key)
}

// if there is no keys found then create one from HostKeys field
if len(keys) == 0 {
fullpath := s.HostKeys[0]
filePath := filepath.Dir(fullpath)

if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
return fmt.Errorf("failed to create dir %s: %w", filePath, err)
}

err := GenerateKeyPair(fullpath)
log.Debug().Msg("no host key provided - setup default key if it doesn't exist yet")
err := createKeyIfNotExists(defaultServerKeyPath)
if err != nil {
return fmt.Errorf("failed to generate private key: %w", err)
return fmt.Errorf("failed to setup default key %q: %w", defaultServerKeyPath, err)
}
keys = append(keys, fullpath)
keys = append(keys, defaultServerKeyPath)
}

// set keys to internal ssh server
Expand All @@ -206,6 +186,7 @@ func (s *Server) setupHostKeys() error {
log.Err(err).Msg("failed to set host key to ssh server")
}
}

return nil
}

Expand Down Expand Up @@ -268,7 +249,7 @@ func (s *Server) sessionHandler(session ssh.Session) {

// set keep alive connection
if s.KeepAliveInterval > 0 {
go s.sendKeepAliveMsg(ctx, session)
go sendKeepAliveMsg(ctx, session, s.KeepAliveInterval)
}

err = s.RepoCtrl.GitServicePack(
Expand Down Expand Up @@ -302,8 +283,8 @@ func (s *Server) sessionHandler(session ssh.Session) {
}
}

func (s *Server) sendKeepAliveMsg(ctx context.Context, session ssh.Session) {
ticker := time.NewTicker(time.Duration(s.KeepAliveInterval))
func sendKeepAliveMsg(ctx context.Context, session ssh.Session, interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
log.Ctx(ctx).Debug().Str("remote_addr", session.RemoteAddr().String()).Msgf("sendKeepAliveMsg")

Expand All @@ -312,8 +293,11 @@ func (s *Server) sendKeepAliveMsg(ctx context.Context, session ssh.Session) {
case <-ctx.Done():
return
case <-ticker.C:
log.Ctx(ctx).Debug().Msg("connection: sendKeepAliveMsg: send keepalive message to a client")
_, _ = session.SendRequest(KeepAliveMsg, true, nil)
log.Ctx(ctx).Debug().Msg("send keepalive message to ssh client")
_, err := session.SendRequest(KeepAliveMsg, true, nil)
if err != nil {
log.Ctx(ctx).Debug().Err(err).Msg("failed to send keepalive message to ssh client")
}
}
}
}
Expand All @@ -332,8 +316,12 @@ func (s *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
}

principal, err := s.Verifier.ValidateKey(ctx, key, enum.PublicKeyUsageAuth)
if errors.IsNotFound(err) {
log.Debug().Err(err).Msg("public key is unknown")
return false
}
if err != nil {
log.Error().Err(err).Msg("failed to validate public key")
log.Warn().Err(err).Msg("failed to validate public key")
return false
}

Expand Down Expand Up @@ -365,6 +353,31 @@ func sshConnectionFailed(conn net.Conn, err error) {
log.Err(err).Msgf("failed connection from %s with error: %v", conn.RemoteAddr(), err)
}

func createKeyIfNotExists(path string) error {
_, err := os.Stat(path)
if err == nil {
// if the path already exists there's nothing we have to do
return nil
}
if !os.IsNotExist(err) {
return fmt.Errorf("failed to check for for existence of key: %w", err)
}

log.Debug().Msgf("generate new key at %q", path)

dir := filepath.Dir(path)
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return fmt.Errorf("failed to create dir %q for key: %w", dir, err)
}

err = GenerateKeyPair(path)
if err != nil {
return fmt.Errorf("failed to generate key pair: %w", err)
}

return nil
}

// GenerateKeyPair make a pair of public and private keys for SSH access.
func GenerateKeyPair(keyPath string) error {
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
Expand Down
4 changes: 2 additions & 2 deletions types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ type Config struct {
}

SSH struct {
Enable bool `envconfig:"GITNESS_SSH_ENABLE" default:"true"`
Enable bool `envconfig:"GITNESS_SSH_ENABLE" default:"false"`
Host string `envconfig:"GITNESS_SSH_HOST"`
Port int `envconfig:"GITNESS_SSH_PORT" default:"22"`
// DefaultUser holds value for generating urls {user}@host:path and force check
Expand All @@ -152,7 +152,7 @@ type Config struct {
TrustedUserCAKeys []string `envconfig:"GITNESS_SSH_TRUSTED_USER_CA_KEYS"`
TrustedUserCAKeysFile string `envconfig:"GITNESS_SSH_TRUSTED_USER_CA_KEYS_FILENAME"`
TrustedUserCAKeysParsed []gossh.PublicKey
KeepAliveInterval int `envconfig:"GITNESS_SSH_KEEP_ALIVE_INTERVAL" default:"5000"`
KeepAliveInterval time.Duration `envconfig:"GITNESS_SSH_KEEP_ALIVE_INTERVAL" default:"5s"`
}

// CI defines configuration related to build executions.
Expand Down

0 comments on commit 90680e2

Please sign in to comment.