From 59c5a6c4950d340d248431b84452d86329024d76 Mon Sep 17 00:00:00 2001 From: runoneall Date: Thu, 11 Sep 2025 17:19:12 +0800 Subject: add ssh server --- .gitignore | 5 ++++- go.mod | 8 ++++++- go.sum | 6 ++++++ init.go | 41 ++++++++++++++++++++++++++++++++++++ main.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ shell.go | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 go.sum create mode 100644 init.go create mode 100644 main.go create mode 100644 shell.go diff --git a/.gitignore b/.gitignore index 78b09d0..9a2c438 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ server.properties Starter.class starter.jar -tmp/ \ No newline at end of file + +tmp/ + +mc-ssh-server.exe \ No newline at end of file diff --git a/go.mod b/go.mod index d9e8f04..585f2f0 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,9 @@ -module ssh-server +module mc-ssh-server go 1.25.1 + +require ( + github.com/magiconair/properties v1.8.10 // indirect + golang.org/x/crypto v0.42.0 // indirect + golang.org/x/sys v0.36.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6edb2b3 --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= diff --git a/init.go b/init.go new file mode 100644 index 0000000..37ed53a --- /dev/null +++ b/init.go @@ -0,0 +1,41 @@ +package main + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "fmt" +) + +func init_genkey() (*pem.Block, *pem.Block) { + privateKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + panic(fmt.Errorf("不能生成密钥: %v", err)) + } + + privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey) + privateKeyBlock := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: privateKeyBytes, + } + + publicKeyBytes, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) + if err != nil { + panic(fmt.Errorf("不能解析公钥: %v", err)) + } + + publicKeyBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: publicKeyBytes, + } + + return privateKeyBlock, publicKeyBlock +} + +var privatePEM []byte + +func init() { + privateKeyBlock, _ := init_genkey() + privatePEM = pem.EncodeToMemory(privateKeyBlock) +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..1bbe285 --- /dev/null +++ b/main.go @@ -0,0 +1,67 @@ +package main + +import ( + "fmt" + "net" + + "github.com/magiconair/properties" + "golang.org/x/crypto/ssh" +) + +func main() { + + // 解析 server.properties + conf := properties.MustLoadFile("server.properties", properties.UTF8) + var SSH_SERVER struct { + Host string + Port string + User string + Pass string + config *ssh.ServerConfig + } + + SSH_SERVER.Host = conf.MustGetString("server-ip") + SSH_SERVER.Port = conf.MustGetString("server-port") + SSH_SERVER.User = conf.MustGetString("term-user") + SSH_SERVER.Pass = conf.MustGetString("term-pass") + + // 创建 ssh 密码认证 + SSH_SERVER.config = &ssh.ServerConfig{ + PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (*ssh.Permissions, error) { + if conn.User() == SSH_SERVER.User && string(password) == SSH_SERVER.Pass { + return nil, nil + } + + return nil, ssh.ErrNoAuth + }, + } + + // 创建 ssh 服务器密钥 + privateKeySigner, err := ssh.ParsePrivateKey(privatePEM) + if err != nil { + panic(fmt.Errorf("不能解析私钥: %v", err)) + } + + SSH_SERVER.config.AddHostKey(privateKeySigner) + + // 在指定端口开启服务 + address := net.JoinHostPort(SSH_SERVER.Host, SSH_SERVER.Port) + + listener, err := net.Listen("tcp", address) + if err != nil { + panic(fmt.Errorf("不能在 %s 上创建服务: %v", address, err)) + } + + fmt.Println("Server Address:", address) + + // 连接到系统 shell + for { + conn, err := listener.Accept() + if err != nil { + fmt.Println("Can not accept connection:", err) + } + + go shell(conn, SSH_SERVER.config) + } + +} diff --git a/shell.go b/shell.go new file mode 100644 index 0000000..3492b33 --- /dev/null +++ b/shell.go @@ -0,0 +1,70 @@ +package main + +import ( + "fmt" + "net" + "os" + "os/exec" + + "golang.org/x/crypto/ssh" +) + +func shell(conn net.Conn, config *ssh.ServerConfig) { + sshConn, chans, reqs, err := ssh.NewServerConn(conn, config) + if err != nil { + fmt.Println("不能创建连接:", err) + return + } + defer sshConn.Close() + + fmt.Println("New connection from", sshConn.RemoteAddr(), "with client version", sshConn.ClientVersion()) + + go ssh.DiscardRequests(reqs) + + for newChannel := range chans { + if newChannel.ChannelType() != "session" { + newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") + continue + } + + channel, requests, err := newChannel.Accept() + if err != nil { + fmt.Println("Can not accept channel:", err) + continue + } + defer channel.Close() + + shell := os.Getenv("SHELL") + if shell == "" { + shell = "cmd.exe" + } + + command := exec.Command(shell) + command.Stdin = channel + command.Stdout = channel + command.Stderr = channel + + if err := command.Start(); err != nil { + fmt.Println("Failed to start shell:", err) + return + } + + go func() { + if err := command.Wait(); err != nil { + fmt.Println("Run shell failed:", err) + } + channel.Close() + }() + + go func() { + for req := range requests { + switch req.Type { + case "shell": + req.Reply(true, nil) + default: + req.Reply(false, nil) + } + } + }() + } +} -- cgit v1.2.3