英文:
ssh server in go : how to offer public key types different than rsa?
问题
我正在尝试使用x/crypto/ssh
模块在Go语言中创建一个SSH服务器,但是我无法使公钥身份验证工作。我尝试了ssh/example_test.go
文件中的ExampleNewServerConn()
函数(在https://go.googlesource.com/crypto仓库中),但是公钥方法不起作用,看起来服务器没有广告正确的算法,因为当我尝试使用SSH客户端连接时,会出现以下行:
debug1: send_pubkey_test: no mutual signature algorithm
如果我添加-o PubkeyAcceptedKeyTypes=+ssh-rsa
,公钥登录就可以工作,但是这种RSA方法已经被弃用了,我想使用其他公钥类型,我该如何做到?
谢谢提前帮助。
编辑:这是我用来测试的代码
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)
}
}()
}
}
英文:
I’m trying to create a ssh server in go using the x/crypto/ssh
module but i can’t manage to make the public key authentification work.
I tried the ExampleNewServerConn()
function in the ssh/example_test.go
file (in the https://go.googlesource.com/crypto repo) but the public key method doesn’t work, it looks like the server isn’t advertising the right algorithms because i get this line when trying to connect with a ssh client :
debug1: send_pubkey_test: no mutual signature algorithm
If i add -o PubkeyAcceptedKeyTypes=+ssh-rsa
the public key login works, but this rsa method is deprecated, i would like to use another public key type, how can i do that ?
Thanks in advance.
Edit : here is the code that i used to test
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)
}
}()
}
}
答案1
得分: 2
我发现客户端和服务器无法通信的原因是x/crypto库中尚未实现rsa-sha2算法。在GitHub上有一个相关的问题:https://github.com/golang/go/issues/49952。
一个临时解决方案是在你的go.mod文件末尾添加以下内容:
replace golang.org/x/crypto => github.com/rmohr/crypto v0.0.0-20211203105847-e4ed9664ac54
这将使用@rmohr的x/crypto分支,它支持rsa-sha2算法。
英文:
I found why the client and the server can’t communicate, the rsa-sha2 algorithms are not yet implemented in the x/crypto library. There is an issue about it on github : https://github.com/golang/go/issues/49952 .
A temporary solution is to add
replace golang.org/x/crypto => github.com/rmohr/crypto v0.0.0-20211203105847-e4ed9664ac54
at the end of your go.mod file, it uses a x/crypto fork from @rmohr that works with rsa-sha2.
答案2
得分: 0
这是一个简单的方法,让letsencrypt为您处理证书
func main() {
r := mux.NewRouter()
r.HandleFunc("/index", index)
certManager := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("www.example.com"), // 替换为您的域名
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("", ""))
}
英文:
This is the easy way to do it, let letsencrypt handle the certificates for you
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("", ""))
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论