mirror of
https://git.sr.ht/~hamburghammer/sshlog
synced 2024-12-22 21:17:40 +01:00
150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
|
|
flags "github.com/spf13/pflag"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
var (
|
|
port string
|
|
isHelp bool
|
|
keyPath string
|
|
onlyIPv4 bool
|
|
isJson bool
|
|
)
|
|
|
|
func init() {
|
|
flags.StringVarP(&port, "port", "p", "22", "Port to listen for incoming connections.")
|
|
flags.StringVarP(&keyPath, "key", "k", "", "Path to the host key for the ssh server.\nIf absent it will automatically generate a new one for each run.")
|
|
flags.BoolVarP(&isHelp, "help", "h", false, "Prints this help message and exits.")
|
|
flags.BoolVarP(&onlyIPv4, "onlyIPv4", "4", false, "Only listens on IPv4.")
|
|
flags.BoolVar(&isJson, "json", false, "Log in JSON instead of plain text.")
|
|
flags.Parse()
|
|
}
|
|
|
|
func main() {
|
|
|
|
if isHelp {
|
|
printHelp()
|
|
os.Exit(0)
|
|
}
|
|
|
|
privateKey := getKey(keyPath)
|
|
hostKey, err := ssh.ParsePrivateKey(privateKey)
|
|
if err != nil {
|
|
log.Fatal("Failed to parse private key")
|
|
}
|
|
|
|
serverConfig := ssh.ServerConfig{PasswordCallback: printConnectionData}
|
|
serverConfig.AddHostKey(hostKey)
|
|
|
|
log.Printf("Starting ssh logger on port %s...\n", port)
|
|
ipv4Listener, err := net.Listen("tcp", "0.0.0.0:"+port)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer ipv4Listener.Close()
|
|
|
|
wg := new(sync.WaitGroup)
|
|
wg.Add(1)
|
|
go startAccepting(ipv4Listener, serverConfig)
|
|
|
|
if !onlyIPv4 {
|
|
ipv6Listener, err := net.Listen("tcp", "[::1]:"+port)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer ipv6Listener.Close()
|
|
|
|
wg.Add(1)
|
|
go startAccepting(ipv6Listener, serverConfig)
|
|
}
|
|
|
|
wg.Wait() // Waits until it gets terminated
|
|
}
|
|
|
|
func startAccepting(listener net.Listener, serverConfig ssh.ServerConfig) {
|
|
for {
|
|
con, err := listener.Accept()
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
go connectionHandler(con, serverConfig)
|
|
}
|
|
}
|
|
|
|
func getKey(path string) []byte {
|
|
if path != "" {
|
|
privateBytes, err := os.ReadFile(path)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load private key: %s", path)
|
|
}
|
|
return privateBytes
|
|
}
|
|
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
log.Fatalf("Cannot generate RSA key\n")
|
|
}
|
|
|
|
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
|
privateKeyPem := pem.EncodeToMemory(
|
|
&pem.Block{
|
|
Type: "RSA PRIVATE KEY",
|
|
Bytes: privateKeyBytes,
|
|
},
|
|
)
|
|
return privateKeyPem
|
|
}
|
|
|
|
func connectionHandler(con net.Conn, serverConfig ssh.ServerConfig) {
|
|
_, _, _, err := ssh.NewServerConn(con, &serverConfig)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
func printConnectionData(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) {
|
|
responseErr := fmt.Errorf("password rejected for %s", conn.User())
|
|
ip, _, err := net.SplitHostPort(conn.RemoteAddr().String())
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return nil, responseErr
|
|
}
|
|
|
|
if isJson {
|
|
fmt.Printf(
|
|
"{\"date\": \"%s\", \"src\": \"%s\", \"username\": \"%s\", \"password\": \"%s\"}\n",
|
|
time.Now().Format(time.RFC3339),
|
|
ip,
|
|
conn.User(),
|
|
string(password),
|
|
)
|
|
} else {
|
|
log.Printf("SRC=%s USERNAME=%s PASSWORD=%s\n", ip, conn.User(), string(password))
|
|
}
|
|
|
|
return nil, responseErr
|
|
}
|
|
|
|
func printHelp() {
|
|
fmt.Println(`A small tool to log IPs, usernames and passwords from incoming ssh-auth requests.
|
|
|
|
USAGE:
|
|
sshlog [FLAGS]
|
|
|
|
FLAGS:`)
|
|
flags.PrintDefaults()
|
|
}
|