grim/hgkeeper

Use Go 1.22 and update dependencies
default tip
2 months ago, aklitzing
f33f223bc8fe
Use Go 1.22 and update dependencies

Reviewed at https://reviews.imfreedom.org/r/2949/
package ssh
import (
"fmt"
"github.com/gliderlabs/ssh"
"go.uber.org/zap"
gossh "golang.org/x/crypto/ssh"
"keep.imfreedom.org/grim/hgkeeper/access"
"keep.imfreedom.org/grim/hgkeeper/ssh/commands"
)
type Server struct {
reposPath string
server *ssh.Server
}
func NewServer(hostKeysPath, reposPath, adminRepo string) (*Server, error) {
s := &Server{
reposPath: reposPath,
}
s.server = &ssh.Server{
PublicKeyHandler: s.publicKeyHandler,
Handler: s.sessionHandler,
PtyCallback: func(ctx ssh.Context, pty ssh.Pty) bool {
return false
},
}
if err := s.setHostKeysPath(hostKeysPath); err != nil {
return nil, err
}
return s, nil
}
func (s *Server) publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool {
pubkey := access.MarshalPublicKey(key)
username := access.UsernameFromPubkey(pubkey)
if username == "" {
zap.S().Warnf("authentication failure, unknown key %s", gossh.FingerprintSHA256(key))
return false
}
ctx.SetValue("username", username)
zap.S().Infof(
"%q authenticated with %s",
username,
gossh.FingerprintSHA256(key),
)
return true
}
func (s *Server) sessionHandler(session ssh.Session) {
username := session.Context().Value("username").(string)
// per the docs, session.Command is empty if the user is requesting a shell.
// we only support execs, which means command should be non-empty.
if len(session.Command()) == 0 {
fmt.Fprintf(session, "logged in as %s\n\nShell access is disabled\n", username)
return
}
zap.S().Infof(
"%s@%s requested command %q",
username,
session.RemoteAddr(),
session.RawCommand(),
)
cmd, err := commands.Find(session.RawCommand(), s.reposPath)
if err != nil {
zap.S().Warnf("failed to find command for %q, %v", session.RawCommand(), err)
return
}
if err := cmd.Run(session, username); err != nil {
zap.S().Warnf(
"%s@%s command %q failed: %v",
username,
session.RemoteAddr(),
session.RawCommand(),
err,
)
if err := session.Exit(255); err != nil {
zap.S().Errorf("session failed to exit: %v", err)
}
} else {
zap.S().Infof(
"%s@%s command %q succeed",
username,
session.RemoteAddr(),
session.RawCommand(),
)
if err := session.Exit(0); err != nil {
zap.S().Errorf("session failed to exit: %v", err)
}
}
}
func (s *Server) Listen(addr string) error {
s.server.Addr = addr
zap.S().Infof("ssh listening on %s", s.server.Addr)
if err := s.server.ListenAndServe(); err != nil {
if err != ssh.ErrServerClosed {
return err
}
}
return nil
}
func (s *Server) Close() error {
if s.server != nil {
if err := s.server.Close(); err != nil {
s.server = nil
}
}
return nil
}