I can SSH (using openssh client) to my server using two files: ~/.ssh/id_ed25519{,-cert.pub}
debug1: Trying private key: /home/xavier/.ssh/id_ed25519
debug1: Authentications that can continue: publickey,keyboard-interactive
debug1: Offering ED25519-CERT public key: /home/xavier/.ssh/id_ed25519
debug1: Server accepts key: pkalg ssh-ed25519-cert-v01@openssh.com blen 441
debug1: sign_and_send_pubkey: no separate private key for certificate "/home/xavier/.ssh/id_ed25519"
debug1: Authentication succeeded (publickey).
I would like a go client that does the same thing, but I don't know how to incorporate the id_ed25519-cert.pub
file into the example at https://godoc.org/golang.org/x/crypto/ssh#example-PublicKeys
key, err := ioutil.ReadFile("/home/xavier/.ssh/id_ed25519")
if err != nil {
log.Fatalf("unable to read private key: %v", err)
}
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
// Use the PublicKeys method for remote authentication.
ssh.PublicKeys(signer),
},
}
// Connect to the remote server and perform the SSH handshake.
client, err := ssh.Dial("tcp", "host.com:22", config)
if err != nil {
log.Fatalf("unable to connect: %v", err)
}
defer client.Close()
Part of the problem is I don't know what this file is (PublicKey? Certificate?), part of the problem is even if I did know I don't understand what purpose it is playing in this exchange.
I have confirmed that this file is required: removing it causes the ssh CLI to fail.
That's an SSH certificate file, used to implement SSH certificate-based user authentication. This verifies the authenticity of a user on login by checking for a valid signature from a trusted certificate authority in a public key hierarchy. This approach offers various benefits over standard SSH key-based auth (with authorized_keys
files), such as:
ssh-keygen
)authorized_keys
file for each user on each hostAssuming you're using the built-in golang.org/x/crypto/ssh
library, you can implement this by:
The specified format of the OpenSSH public key certificates is similar to an authorized_keys
file. The ParseAuthorizedKeys
function of the Go library will parse this file and return the corresponding key as an instance of the ssh.PublicKey
interface; for certificates, this is concretely an instance of the ssh.Certificate
struct.
See the code example (note: I added a HostKeyCallback
to your ClientConfig
to make this connect against a test box – however, it uses the InsecureIgnoreHostKey
checker, which I do not recommend in production!).
package main
import (
"bytes"
"io/ioutil"
"log"
"golang.org/x/crypto/ssh"
)
func main() {
key, err := ioutil.ReadFile("/tmp/mycert")
if err != nil {
log.Fatalf("unable to read private key: %v", err)
}
// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("unable to parse private key: %v", err)
}
// Load the certificate
cert, err := ioutil.ReadFile("/tmp/mycert-cert.pub")
if err != nil {
log.Fatalf("unable to read certificate file: %v", err)
}
pk, _, _, _, err := ssh.ParseAuthorizedKey(cert)
if err != nil {
log.Fatalf("unable to parse public key: %v", err)
}
certSigner, err := ssh.NewCertSigner(pk.(*ssh.Certificate), signer)
if err != nil {
log.Fatalf("failed to create cert signer: %v", err)
}
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
// Use the PublicKeys method for remote authentication.
ssh.PublicKeys(certSigner),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
// Connect to the remote server and perform the SSH handshake.
client, err := ssh.Dial("tcp", "host.com:22", config)
if err != nil {
log.Fatalf("unable to connect: %v", err)
}
defer client.Close()
}
If you want to write a more generic connection client which supports certificates and non-certificates, you would obviously require additional logic to handle other types of public key. As written, I would expect the type assertion pk.(*ssh.Certificate)
to fail for non-certificate public key files! (Indeed, for non-certificate connections, you probably don't need to read the public key at all.)