英文:
SSH Handshake complains about missing host key
问题
我正在尝试连接到远程主机并检查文件是否存在。目前我只是尝试连接,但是我遇到了一个错误:
2017/08/01 18:16:39 无法连接:ssh:握手失败:ssh:所需的主机密钥为nil
我尝试查找是否有其他人遇到了类似的问题,但是我找不到。
我理解在这个过程中需要检查known_hosts文件,但是我无法弄清楚如何做到...
var hostKey ssh.PublicKey
// 可以使用公钥通过使用未加密的PEM编码的私钥文件进行对远程服务器进行身份验证。
//
// 如果您有一个加密的私钥,可以使用crypto/x509包来解密它。
key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
if err != nil {
log.Fatalf("无法读取私钥:%v", err)
}
// 为此私钥创建签名者。
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
log.Fatalf("无法解析私钥:%v", err)
}
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
// 使用PublicKeys方法进行远程身份验证。
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
// 连接到远程服务器并执行SSH握手。
client, err := ssh.Dial("tcp", "host.com:22", config)
if err != nil {
log.Fatalf("无法连接:%v", err)
}
defer client.Close()
英文:
I'm trying to connect to a remote host and check if a file exist
At this stage I'm trying just to connect but I'm getting an error:
2017/08/01 18:16:39 unable to connect: ssh: handshake failed: ssh: required host key was nil
I've tried to find out if others had issues as mine but I just couldn't find.
I understand that I need to check the knowns_hosts somehow in the process but I just can't figure out how...
var hostKey ssh.PublicKey
// A public key may be used to authenticate against the remote
// server by using an unencrypted PEM-encoded private key file.
//
// If you have an encrypted private key, the crypto/x509 package
// can be used to decrypt it.
key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
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),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
}
// 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()
}
答案1
得分: 12
我建议使用knownhosts子包。
import knownhosts "golang.org/x/crypto/ssh/knownhosts"
...
hostKeyCallback, err := knownhosts.New("/Users/user/.ssh/known_hosts")
if err != nil {
log.Fatal(err)
}
...
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
// Use the PublicKeys method for remote authentication.
ssh.PublicKeys(signer),
},
HostKeyCallback: hostKeyCallback,
}
这样你就可以避免自己解析known_hosts文件了。
希望对你有帮助!
英文:
I would suggest to use knownhosts subpackage
import knownhosts "golang.org/x/crypto/ssh/knownhosts"
...
hostKeyCallback, err := knownhosts.New("/Users/user/.ssh/known_hosts")
if err != nil {
log.Fatal(err)
}
...
config := &ssh.ClientConfig{
User: "user",
Auth: []ssh.AuthMethod{
// Use the PublicKeys method for remote authentication.
ssh.PublicKeys(signer),
},
HostKeyCallback: hostKeyCallback,
}
So that you avoid parsing known_hosts yourself...
hth,
答案2
得分: 9
这是你要找的内容:
func getHostKey(host string) (ssh.PublicKey, error) {
file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
var hostKey ssh.PublicKey
for scanner.Scan() {
fields := strings.Split(scanner.Text(), " ")
if len(fields) != 3 {
continue
}
if strings.Contains(fields[0], host) {
var err error
hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
if err != nil {
return nil, errors.New(fmt.Sprintf("error parsing %q: %v", fields[2], err))
}
break
}
}
if hostKey == nil {
return nil, errors.New(fmt.Sprintf("no hostkey for %s", host))
}
return hostKey, nil
}
Then replace your hostKey definition line with
hostKey, err := getHostKey("host.com")
if err != nil {
log.Fatal(err)
}
For more information on the subject:
- [official sample](https://github.com/golang/crypto/blob/master/ssh/example_test.go) where I took parts of the code from
- [why a hostKey is necessary now](https://github.com/golang/go/issues/19767)
EDIT:
Also check out `Anton`'s answer below about the `golang.org/x/crypto/ssh/knownhosts` package.
然后将你的hostKey定义行替换为
hostKey, err := getHostKey("host.com")
if err != nil {
log.Fatal(err)
}
有关此主题的更多信息:
- 官方示例,我从中提取了部分代码
- 为什么现在需要hostKey
编辑:
还请查看下面Anton
关于golang.org/x/crypto/ssh/knownhosts
包的回答。
英文:
Here what you are looking for:
func getHostKey(host string) (ssh.PublicKey, error) {
file, err := os.Open(filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"))
if err != nil {
return nil, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
var hostKey ssh.PublicKey
for scanner.Scan() {
fields := strings.Split(scanner.Text(), " ")
if len(fields) != 3 {
continue
}
if strings.Contains(fields[0], host) {
var err error
hostKey, _, _, _, err = ssh.ParseAuthorizedKey(scanner.Bytes())
if err != nil {
return nil, errors.New(fmt.Sprintf("error parsing %q: %v", fields[2], err))
}
break
}
}
if hostKey == nil {
return nil, errors.New(fmt.Sprintf("no hostkey for %s", host))
}
return hostKey, nil
}
Then replace your hostKey definition line with
hostKey, err := getHostKey("host.com")
if err != nil {
log.Fatal(err)
}
For more information on the subject:
- official sample where I took parts of the code from
- why a hostKey is necessary now
EDIT:
Also check out Anton
's answer below about the golang.org/x/crypto/ssh/knownhosts
package.
答案3
得分: 0
我建议使用knownhosts子包。
请注意,从Go 1.12到1.20(2023年第一季度),你可能会遇到以下错误:
unable to connect: ssh: handshake failed: ssh: no authorities for hostname
请参考x/crypto/ssh/knownhosts
: can't verify host key if host certificate is sent #33366。
我认为这是由于https://github.com/golang/crypto/blob/dab2b10/ssh/certs.go#L304未对cert.Key
与c.HostKeyFallback
进行检查所导致的。
可能的修补方法:
if !c.IsHostAuthority(cert.SignatureKey, addr) {
+ if c.HostKeyFallback != nil {
+ err := c.HostKeyFallback(addr, remote, cert.Key)
+ if err != nil {
+ return fmt.Errorf("ssh: no authorities for hostname %v. retry with plain key but failed: %v", addr, err)
+ } else {
+ return nil
+ }
+ }
return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
}
英文:
> I would suggest to use knownhosts subpackage
Note that, from Go 1.12 to 1.20 (Q1 2023), you can get:
unable to connect: ssh: handshake failed: ssh: no authorities for hostname
See x/crypto/ssh/knownhosts
: can't verify host key if host certificate is sent #33366
> I think this happens due to https://github.com/golang/crypto/blob/dab2b10/ssh/certs.go#L304 not checking cert.Key
against c.HostKeyFallback
.
Possible patch:
if !c.IsHostAuthority(cert.SignatureKey, addr) {
+ if c.HostKeyFallback != nil {
+ err := c.HostKeyFallback(addr, remote, cert.Key)
+ if err != nil {
+ return fmt.Errorf("ssh: no authorities for hostname %v. retry with plain key but failed: %v", addr, err)
+ } else {
+ return nil
+ }
+ }
return fmt.Errorf("ssh: no authorities for hostname: %v", addr)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论