Reputation: 31
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)
}
}()
}
}
Upvotes: 1
Views: 1263
Reputation: 31
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.
Upvotes: 2
Reputation: 1388
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("", ""))
}
Upvotes: 0