go 中的 ssh 服务器:如何提供与 rsa 不同的 public 密钥类型?

ssh server in go : how to offer public key types different than rsa?

我正在尝试使用 x/crypto/ssh 模块在 go 中创建一个 ssh 服务器,但我无法使 public 密钥验证工作。
我尝试了 ssh/example_test.go 文件中的 ExampleNewServerConn() 函数(在 https://go.googlesource.com/crypto 存储库中)但是 public 键方法不起作用,看起来服务器没有做广告正确的算法,因为我在尝试连接 ssh 客户端时得到了这条线:

debug1: send_pubkey_test: no mutual signature algorithm

如果我添加 -o PubkeyAcceptedKeyTypes=+ssh-rsa public 密钥登录有效,但此 rsa 方法已弃用,我想使用另一种 public 密钥类型,我该怎么做?

提前致谢。

编辑:这是我用来测试的代码

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net"

    "golang.org/x/crypto/ssh"
    terminal "golang.org/x/term"
)

func main() {
    authorizedKeysBytes, err := ioutil.ReadFile("authorized_keys")
    if err != nil {
        log.Fatalf("Failed to load authorized_keys, err: %v", err)
    }

    authorizedKeysMap := map[string]bool{}
    for len(authorizedKeysBytes) > 0 {
        pubKey, _, _, rest, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
        if err != nil {
            log.Fatal(err)
        }

        authorizedKeysMap[string(pubKey.Marshal())] = true
        authorizedKeysBytes = rest
    }
    config := &ssh.ServerConfig{
        PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
            if c.User() == "testuser" && string(pass) == "tiger" {
                return nil, nil
            }
            return nil, fmt.Errorf("password rejected for %q", c.User())
        },
        PublicKeyCallback: func(c ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
            if authorizedKeysMap[string(pubKey.Marshal())] {
                return &ssh.Permissions{
                    // Record the public key used for authentication.
                    Extensions: map[string]string{
                        "pubkey-fp": ssh.FingerprintSHA256(pubKey),
                    },
                }, nil
            }
            return nil, fmt.Errorf("unknown public key for %q", c.User())
        },
    }

    privateBytes, err := ioutil.ReadFile("id_rsa")
    if err != nil {
        log.Fatal("Failed to load private key: ", err)
    }

    private, err := ssh.ParsePrivateKey(privateBytes)
    if err != nil {
        log.Fatal("Failed to parse private key: ", err)
    }

    config.AddHostKey(private)

    listener, err := net.Listen("tcp", "0.0.0.0:2022")
    if err != nil {
        log.Fatal("failed to listen for connection: ", err)
    }
    nConn, err := listener.Accept()
    if err != nil {
        log.Fatal("failed to accept incoming connection: ", err)
    }

    conn, chans, reqs, err := ssh.NewServerConn(nConn, config)
    if err != nil {
        log.Fatal("failed to handshake: ", err)
    }
    log.Printf("logged in with key %s", conn.Permissions.Extensions["pubkey-fp"])

    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 {
            log.Fatalf("Could not accept channel: %v", err)
        }

        go func(in <-chan *ssh.Request) {
            for req := range in {
                req.Reply(req.Type == "shell", nil)
            }
        }(requests)

        term := terminal.NewTerminal(channel, "> ")

        go func() {
            defer channel.Close()
            for {
                line, err := term.ReadLine()
                if err != nil {
                    break
                }
                fmt.Println(line)
            }
        }()
    }
}

这是最简单的方法,让 letsencrypt 为您处理证书:)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/index", index)
    certManager := autocert.Manager{
        Prompt:     autocert.AcceptTOS,
        HostPolicy: autocert.HostWhitelist("www.example.com"), // replace with your domain
        Cache:      autocert.DirCache("certs"),
    }
    srv := &http.Server{
        Handler:      r,
        Addr:         ":https",
        WriteTimeout: 5 * time.Second,
        ReadTimeout:  5 * time.Second,
        TLSConfig: &tls.Config{
            GetCertificate: certManager.GetCertificate,
        },
    }
    go http.ListenAndServe(":http", certManager.HTTPHandler(nil)) //nolint
    log.Fatal(srv.ListenAndServeTLS("", ""))
}

我发现为什么客户端和服务器无法通信,x/crypto库中还没有实现rsa-sha2算法。 github 上有一个问题:https://github.com/golang/go/issues/49952 .

一个临时解决方案是添加

replace golang.org/x/crypto => github.com/rmohr/crypto v0.0.0-20211203105847-e4ed9664ac54

在你的 go.mod 文件的末尾,它使用了来自 @rmohr 的一个 x/crypto 分支,它与 rsa-sha2 一起工作。