Files
honeydany/app/services/ssh.go
T

82 lines
2.9 KiB
Go

package services
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"net"
"strconv"
"time"
"golang.org/x/crypto/ssh"
)
// NewSSHHandler returns a TCP handler that performs SSH handshake and logs auth attempts.
// getSigner is used to obtain a host key signer (so the app can reuse/generate one).
func NewSSHHandler(log LoggerFunc, getSigner func() (ssh.Signer, error)) Handler {
return func(conn net.Conn) {
defer conn.Close()
remote := conn.RemoteAddr().String()
sessionID := fmt.Sprintf("%x", time.Now().UnixNano())
start := time.Now()
signer, err := getSigner()
if err != nil {
log(Record{Timestamp: Now(), RemoteAddr: remoteIP(remote), RemotePort: remotePort(remote), Service: "ssh", Details: map[string]string{"error": "hostkey"}, RawPayload: err.Error()})
return
}
var authAttempts int
var lastUser, lastPass string
cfg := &ssh.ServerConfig{
NoClientAuth: false,
ServerVersion: "SSH-2.0-OpenSSH_7.9p1 Ubuntu-10",
PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
authAttempts++
lastUser = c.User()
lastPass = string(pass)
log(Record{Timestamp: Now(), RemoteAddr: remoteIP(remote), RemotePort: remotePort(remote), Service: "ssh", Details: map[string]string{
"event": "auth_attempt",
"attempt": strconv.Itoa(authAttempts),
"username": lastUser,
"password": lastPass,
"client": string(c.ClientVersion()),
}})
return nil, fmt.Errorf("permission denied")
},
}
cfg.AddHostKey(signer)
_ = conn.SetDeadline(time.Now().Add(2 * time.Minute))
sc, chans, reqs, err := ssh.NewServerConn(conn, cfg)
if err != nil {
log(Record{Timestamp: Now(), RemoteAddr: remoteIP(remote), RemotePort: remotePort(remote), Service: "ssh", Details: map[string]string{
"event": "session_end",
"session_id": sessionID,
"auth_attempts": strconv.Itoa(authAttempts),
"duration_sec": fmt.Sprintf("%.2f", time.Since(start).Seconds()),
"last_username": lastUser,
"last_password": lastPass,
"error": err.Error(),
}})
return
}
go ssh.DiscardRequests(reqs)
for ch := range chans {
_ = ch.Reject(ssh.Prohibited, "not allowed")
}
_ = sc.Close()
}
}
// DefaultSigner provides a simple RSA signer if caller doesn't have one.
func DefaultSigner() (ssh.Signer, error) {
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
return ssh.NewSignerFromKey(key)
}