SSH:无法使用由Hashicorp Vault签名的密钥进行身份验证,使用Go语言。

huangapple go评论91阅读模式
英文:

SSH: Unable to authenticate with key signed by Hashicorp Vault using Go

问题

我已经使用Hashicorp的Vault签署了我的公钥,并将生成的证书保存在~/.ssh/id_rsa-cert.pub中。它工作得很好,我可以使用ssh -i ~/.ssh/id_rsa-cert.pub user@hostname命令成功登录。

但是,当我尝试在Go中实现这个功能时,问题就出现了。

这是我的代码:

package main

import (
	"encoding/base64"
	"fmt"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
	"net"
	"os"
)

func main() {
	pubKey := "AAAAB3NzaC1yc2EAAAADAQABA..."
	signedKey := "AAAAHHNzaC1yc2EtY2VydC..."
	pubKeyBytes, err := base64.StdEncoding.DecodeString(pubKey)
	if err != nil {
		panic(err)
	}
	pk, err := ssh.ParsePublicKey(pubKeyBytes)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%T\n", pk)

	signedKeyBytes, err := base64.StdEncoding.DecodeString(signedKey)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%T\n", pk)
	sk, err := ssh.ParsePublicKey(signedKeyBytes)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%T\n", sk)

	conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
	if err != nil {
		panic(err)
	}
	sshAgent := agent.NewClient(conn)
	signers, err := sshAgent.Signers()
	if err != nil {
		panic(err)
	}

	c := &ssh.Certificate{
		Key:          pk,
		SignatureKey: sk,
	}

	signer, err := ssh.NewCertSigner(c, signers[0])
	if err != nil {
		panic(err)
	}

	auths := []ssh.AuthMethod{ssh.PublicKeys(signer)}
	sshClient, err := ssh.Dial("tcp", "10.0.0.150:22", &ssh.ClientConfig{
		User: "user1",
		/*The signed key is signed against user2's public key, and should allow him to log in.
		It works via command line; user2@localhost: ssh -i id_rsa-cert.pub user1@10.0.150*/
		Auth:            auths,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	})
	fmt.Println(sshClient, err) /*This does not work*/

	sshClient2, err := ssh.Dial("tcp", "10.0.0.150:22", &ssh.ClientConfig{
		User: "user2",
		/*User2 is allowed to connect with his basic keypair
		user2@localhost: ssh user2@10.0.0.150*/
		Auth: []ssh.AuthMethod{
			ssh.PublicKeysCallback(sshAgent.Signers),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	})
	fmt.Println(sshClient2, err) /*This works fine*/
}

我遇到的错误是:

ssh: handshake failed: ssh: unable to authenticate, attempted methods [publickey none],
no supported methods remain

以及服务器上的sshd日志:

sshd[7149]: error: Unknown certificate type 0 sshd[7149]: error:
key_from_blob: can't parse cert data sshd[7149]: error:
userauth_pubkey: cannot decode key: ssh-rsa-cert-v01@openssh.com

请问我需要做什么才能使其工作?

英文:

I have signed my public key using Hashicorp's Vault, and saved the generated certificiate in ~/.ssh/id_rsa-cert.pub
It works just fine, I can do ssh -i ~/.ssh/id_rsa-cert.pub user@hostname and it lets me in.

My problems start when I try to implement this in Go.

This is my code:

package main
import (
"encoding/base64"
"fmt"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"net"
"os"
)
func main() {
pubKey := "AAAAB3NzaC1yc2EAAAADAQABA..."
signedKey := "AAAAHHNzaC1yc2EtY2VydC..."
pubKeyBytes, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil {
panic(err)
}
pk, err := ssh.ParsePublicKey(pubKeyBytes)
if err != nil {
panic(err)
}
fmt.Printf("%T\n", pk)
signedKeyBytes, err := base64.StdEncoding.DecodeString(signedKey)
if err != nil {
panic(err)
}
fmt.Printf("%T\n", pk)
sk, err := ssh.ParsePublicKey(signedKeyBytes)
if err != nil {
panic(err)
}
fmt.Printf("%T\n", sk)
conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
if err != nil {
panic(err)
}
sshAgent := agent.NewClient(conn)
signers, err := sshAgent.Signers()
if err != nil {
panic(err)
}
c := &ssh.Certificate{
Key:          pk,
SignatureKey: sk,
}
signer, err := ssh.NewCertSigner(c, signers[0])
if err != nil {
panic(err)
}
auths := []ssh.AuthMethod{ssh.PublicKeys(signer)}
sshClient, err := ssh.Dial("tcp", "10.0.0.150:22", &ssh.ClientConfig{
User: "user1",
/*The signed key is signed against user2's public key, and should allow him to log in.
It works via command line; user2@localhost: ssh -i id_rsa-cert.pub user1@10.0.150*/
Auth:            auths,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
fmt.Println(sshClient, err) /*This does not work*/
sshClient2, err := ssh.Dial("tcp", "10.0.0.150:22", &ssh.ClientConfig{
User: "user2",
/*User2 is allowed to connect with his basic keypair
user2@localhost: ssh user2@10.0.0.150*/
Auth: []ssh.AuthMethod{
ssh.PublicKeysCallback(sshAgent.Signers),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
fmt.Println(sshClient2, err) /*This works fine*/
}

The errors I get are:

ssh: handshake failed: ssh: unable to authenticate, attempted methods [publickey none],
no supported methods remain

And the sshd logs from the server in question:

 sshd[7149]: error: Unknown certificate type 0 sshd[7149]: error:
key_from_blob: can't parse cert data sshd[7149]: error:
userauth_pubkey: cannot decode key: ssh-rsa-cert-v01@openssh.com

What do I need to do here to get this to work?

答案1

得分: 3

我花了几个小时来解决这个问题。

解决方案是首先使用您的私钥和证书创建一个签名者,然后创建一个证书签名者并将两者合并。下面是预期工作的代码。

package main

import (
	"bytes"
	"golang.org/x/crypto/ssh"
	"io/ioutil"
	"log"
)

func main() {
	authorizedKeysBytes, _ := ioutil.ReadFile("/home/wooh/.ssh/signed-cert.pub")
	pcert, _, _, _, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)

	privkeyBytes, _ := ioutil.ReadFile("/home/wooh/.ssh/id_rsa")
	upkey, err := ssh.ParseRawPrivateKey(privkeyBytes)

	if err != nil {
		log.Printf("Failed to load authorized_keys, err: %v", err)
	}

	usigner, err := ssh.NewSignerFromKey(upkey)
	if err != nil {
		log.Printf("Failed to create new signer, err: %v", err)
	}
	log.Printf("signer: %v", usigner)

	ucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)

	if err != nil {
		log.Printf("Failed to create new signer, err: %v", err)
	}

	sshConfig := &ssh.ClientConfig{
		User:            "wooh",
		Auth:            []ssh.AuthMethod{ssh.PublicKeys(ucertSigner)},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, err := ssh.Dial("tcp", "127.0.0.1:22", sshConfig)

	if err != nil {
		log.Fatalf("Failed to dial, err: %v", err)
	}

	session, err := client.NewSession()
	if err != nil {
		log.Fatal("Failed to create session: ", err)
	}
	defer session.Close()

	var b bytes.Buffer
	session.Stdout = &b
	if err := session.Run("/usr/bin/whoami"); err != nil {
		log.Fatal("Failed to run: " + err.Error())
	}
	log.Println(b.String())
}

希望对您有帮助!

英文:

I spent a few hours to figure this out.

The solution is to use your private key and your cert and make a signer first, then a certsigner and combine the two. Below is the working code which works as expected.

package main
import (
"bytes"
"golang.org/x/crypto/ssh"
"io/ioutil"
"log"
)
func main() {
authorizedKeysBytes, _ := ioutil.ReadFile("/home/wooh/.ssh/signed-cert.pub")
pcert, _, _, _, err := ssh.ParseAuthorizedKey(authorizedKeysBytes)
privkeyBytes, _ := ioutil.ReadFile("/home/wooh/.ssh/id_rsa")
upkey, err := ssh.ParseRawPrivateKey(privkeyBytes)
if err != nil {
log.Printf("Failed to load authorized_keys, err: %v", err)
}
usigner, err := ssh.NewSignerFromKey(upkey)
if err != nil {
log.Printf("Failed to create new signer, err: %v", err)
}
log.Printf("signer: %v", usigner)
ucertSigner, err := ssh.NewCertSigner(pcert.(*ssh.Certificate), usigner)
if err != nil {
log.Printf("Failed to create new signer, err: %v", err)
}
sshConfig := &ssh.ClientConfig{
User:            "wooh",
Auth:            []ssh.AuthMethod{ssh.PublicKeys(ucertSigner)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", "127.0.0.1:22", sshConfig)
if err != nil {
log.Fatalf("Failed to dial, err: %v", err)
}
session, err := client.NewSession()
if err != nil {
log.Fatal("Failed to create session: ", err)
}
defer session.Close()
var b bytes.Buffer
session.Stdout = &b
if err := session.Run("/usr/bin/whoami"); err != nil {
log.Fatal("Failed to run: " + err.Error())
}
log.Println(b.String())
}

huangapple
  • 本文由 发表于 2017年6月2日 02:46:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/44314924.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定