Git - 服务器主机密钥未缓存

116

我试图将本地仓库的更改推送到远程仓库。 当我键入:

git push origin

我遇到了以下错误:

The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
Connection abandoned.
fatal: The remote end hung up unexpectedly

我该如何解决这个问题?我正在使用Windows 7命令行中的git。

编辑

当我尝试简单地进行ssh时

ssh user@hostname

我遇到了以下错误:

Could not create directory '/c//%HOMEDRIVE%%HOMEPATH%/.ssh'.
percent_expand: unknown key %H

由于路径无效,某种情况下它无法创建目录。如何解决?

@eckes:编辑2

我的主目录设置为%HOMEDRIVE%%HOMEPATH%,这正确吗?


2
似乎$HOME没有正确设置。尝试在Windows上使用“我的电脑”->右键单击->“属性”->“高级”选项卡->“环境变量”按钮来设置HOME环境变量。 - eckes
1
我不是Windows专家,但是在/c//(可能是驱动器号)之后仍然有%HOMEDRIVE%确实让我感到奇怪...你可以通过调整值并回显来节省一些时间。 - Cascabel
1
扩展HOMEDRIVEHOMEPATH并将HOME设置为结果值... - eckes
21个回答

180

如果您正在使用标准命令提示符通过PuTTY在Windows上设置MSYS Git,那么向PuTTY的缓存中添加主机的方法是运行以下命令:

> plink.exe <host>
例如:
> plink.exe codebasehq.com

The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 2e:db:b6:22:f7:bd:48:f6:da:72:bf:59:d7:75:d7:4e
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)

只需回答y,然后 Ctrl+C 其余部分。

不过要检查指纹。这个警告是有充分理由的。一些 Git 服务的指纹(请编辑添加更多):


16
这应该是被接受的答案。它恰好解释了错误信息的含义。在我的情况下,当我克隆时使用了一个完全限定域名(FQDN),但在我的新机器上,我只使用了短本地域名登录。我必须通过putty或plink作为FQDN登录以缓存源主机名的密钥。使用 "git remote -v" 可以帮助交叉检查远程使用的主机名。 - peabody
3
使用交互式的PuTTY到你想使用的主机也可以起作用。例如,如果你正在尝试在新的Windows计算机上首次克隆Github存储库,请使用PuTTY打开到主机“github.com”的会话,接受有关服务器信任的提示,然后在命令行上进行克隆应该可以工作。 - Jeremy McGee
1
你可以通过运行$ set | grep GIT_SSH并检查 GIT_SSH='C:\Program Files (x86)\PuTTY\plink.exe' 来确定MSYS git是否正在尝试使用 plink. - shuckc
2
我最终通过将我的密钥添加到Pageant中,并直接使用Putty访问主机来解决了这个问题。这会要求您将主机添加到缓存中。做同样的事情。 - Knossos
2
如果您的git存储库在自定义SSH端口上提供服务,请使用“-P”选择端口,例如:plink.exe example.com -P 2222。我能够从github克隆,但无法从我的个人服务器克隆,这让我非常困惑。 - Hay

81

在 Git Bash 提示符下尝试执行 "set | grep -i ssh" 命令

如果你的设置和我的一样,你可能会看到以下内容:

GIT_SSH='C:\Program Files (x86)\PuTTY\plink.exe'
PLINK_PROTOCOL=ssh
SVN_SSH='"C:\\Program Files (x86)\\PuTTY\\plink.exe"'
我做了一个
unset GIT_SSH
unset PLINK_PROTOCOL
unset GIT_SVN

之后它就可以用了,我猜putty把它的密钥存储在$HOME/.ssh或其他地方(我也遇到过一个问题,其中$HOME被设置为“C:\Users\usrnam”而不是“/C/Users/usrnam/”)。

无论如何,你的情况可能有所不同,但这对我很有效。:-)

(可能只执行unset GIT_SSH就足够了,但我继续了下去)

注意:如果unset对您没有用,请尝试这个:

set GIT_SSH=

1
"unset GIT_SSH" 对我有用。我之前为另一个服务器设置了 Pageant/putty,但当我使用 Git Bash 提示符构建新密钥时,我需要返回。感谢您的帮助。 - supermitch
在按照你的步骤后,我取得了进一步的进展,但现在出现了“输入的Mac损坏”错误...你见过这种情况吗? - CD Smith
2
安装git时,您可以选择不设置这些变量。这甚至是默认的变体。虽然我也选择了plink集成,这就是我在这里的原因) 谢谢。 - Antony Hatchkins
1
这对我在Win7上也起作用了。显然,git bash与plink的设置在我的情况下引起了问题。 - nhylated
2
unset GIT_SSH 对我也起作用,但我每次启动 git bash 都必须这样做,这相当无聊。有什么自动化的想法吗? - Loïc
老兄,我太爱你了,你不知道!!!我连续战斗了过去4个小时才解决这个问题,而这正是让我成功的关键——谢谢你!!! - Hrisimir Dakov

55
这条消息的意思是你的受信任主机文件中没有记录origin的主机密钥。
为了解决这个问题,可以通过Git控制台打开一个普通的SSH连接到origin,SSH会询问你是否信任远程主机:
$ ssh 127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
RSA key fingerprint is <FINGERPRINT>.
Are you sure you want to continue connecting (yes/no)?

如果你信任远程主机(即输入yes),SSH 将把其密钥添加到已知主机列表中。
之后,你应该能够执行git push origin命令。
或者,你也可以手动将origin的密钥添加到.ssh/known_hosts文件中,但这要求你遵循sshd(第AUTHORIZED_KEYS FILE FORMAT部分)手册中描述的known_hosts文件格式。

4
当我向 GitHub 推送代码时,我收到了相同的信息,但我可以通过 SSH 连接到 GitHub,而且我的 known_hosts 文件中确实有 github.com。 - Magnus Lindhe
1
请参见下面的答案。 - Nikita Koksharov
3
您可以在Windows上使用PuTTY代替命令行SSH客户端,以实现相同的目的。 - brianmearns
1
确保主机名完全相同。例如,如果您在本地安装了git并使用名称“home.mydomain.com”作为远程名称,但使用putty存储密钥连接到“localhost”,那是行不通的。您需要精确地连接到远程URL中的主机名。 - Jason Goemaat
对我来说,尝试使用putty连接服务器是固定的。假设git url是ssh://git@example.ex.com:222/something/shop.git,因此我在putty主机名字段中输入了example.ex.com和端口222。然后连接失败,但我猜它添加了需要的指纹。我只是不明白它添加了哪里,因为当我删除旧密钥时,我的主目录known_hosts文件没有受到影响。 - Dariux

20
我怀疑你的GIT_SSH环境变量被设置为%ProgramFiles(x86)%\putty\plink.exe。由于某种原因,PLink不会使用你用户目录下的.ssh/known_hosts文件来存储远程主机的密钥。
如果这确实是你的情况,而且如果你想使用pageant的话,你需要先使用PLink连接到主机。
"$GIT_SSH" user@hostname

你应该会收到类似的信息。

The server's host key is not cached in the registry. You
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 86:7b:1b:12:85:35:8a:b7:98:b6:d2:97:5e:96:58:1d
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)

一旦您已回答问题并成功连接到远程主机,那么您就准备好了。继续尝试您的推送操作。


这就是我在Windows上使用Git Bash与PLink/Pageant的全部内容。非常感谢! - amsross
1
使用Stash(现在是Bitbucket)存储库,我必须使用"$GIT_SSH" -P 7999 git@stash.domain.local - Julien

4

仅仅通过SSH连接到主机是不够的,至少在Windows上是这样。这会将主机密钥添加到ssh/known_hosts,但错误仍然存在。

您需要关闭Git Bash窗口并打开一个新窗口。然后注册表缓存被清除,推送/拉取就可以正常工作了。


ssh/known_hosts 相对于什么?我在 Win 7 上遇到了这个问题,但没有解决方案... - Frank N

2

Plink的解决方案

此Python脚本保存为known_hosts.py:

#! /usr/bin/env python

# $Id$
# Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY
# host keys.
#   usage:
#     kh2reg.py [ --win ] known_hosts1 2 3 4 ... > hosts.reg
#       Creates a Windows .REG file (double-click to install).
#     kh2reg.py --unix    known_hosts1 2 3 4 ... > sshhostkeys
#       Creates data suitable for storing in ~/.putty/sshhostkeys (Unix).
# Line endings are someone else's problem as is traditional.
# Developed for Python 1.5.2.

import fileinput
import base64
import struct
import string
import re
import sys
import getopt

def winmungestr(s):
    "Duplicate of PuTTY's mungestr() in winstore.c:1.10 for Registry keys"
    candot = 0
    r = ""
    for c in s:
        if c in ' \*?%~' or ord(c)<ord(' ') or (c == '.' and not candot):
            r = r + ("%%%02X" % ord(c))
        else:
            r = r + c
        candot = 1
    return r

def strtolong(s):
    "Convert arbitrary-length big-endian binary data to a Python long"
    bytes = struct.unpack(">%luB" % len(s), s)
    return reduce ((lambda a, b: (long(a) << 8) + long(b)), bytes)

def longtohex(n):
    """Convert long int to lower-case hex.

    Ick, Python (at least in 1.5.2) doesn't appear to have a way to
    turn a long int into an unadorned hex string -- % gets upset if the
    number is too big, and raw hex() uses uppercase (sometimes), and
    adds unwanted "0x...L" around it."""

    plain=string.lower(re.match(r"0x([0-9A-Fa-f]*)l?$", hex(n), re.I).group(1))
    return "0x" + plain

output_type = 'windows'

try:
    optlist, args = getopt.getopt(sys.argv[1:], '', [ 'win', 'unix' ])
    if filter(lambda x: x[0] == '--unix', optlist):
        output_type = 'unix'
except getopt.error, e:
    sys.stderr.write(str(e) + "\n")
    sys.exit(1)

if output_type == 'windows':
    # Output REG file header.
    sys.stdout.write("""REGEDIT4

[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys]
""")

# Now process all known_hosts input.
for line in fileinput.input(args):

    try:
        # Remove leading/trailing whitespace (should zap CR and LF)
        line = string.strip (line)

        # Skip blanks and comments
        if line == '' or line[0] == '#':
            raise "Skipping input line"

        # Split line on spaces.
        fields = string.split (line, ' ')

        # Common fields
        hostpat = fields[0]
        magicnumbers = []   # placeholder
        keytype = ""        # placeholder

        # Grotty heuristic to distinguish known_hosts from known_hosts2:
        # is second field entirely decimal digits?
        if re.match (r"\d*$", fields[1]):

            # Treat as SSH-1-type host key.
            # Format: hostpat bits10 exp10 mod10 comment...
            # (PuTTY doesn't store the number of bits.)
            magicnumbers = map (long, fields[2:4])
            keytype = "rsa"

        else:

            # Treat as SSH-2-type host key.
            # Format: hostpat keytype keyblob64 comment...
            sshkeytype, blob = fields[1], base64.decodestring (fields[2])

            # 'blob' consists of a number of
            #   uint32    N (big-endian)
            #   uint8[N]  field_data
            subfields = []
            while blob:
                sizefmt = ">L"
                (size,) = struct.unpack (sizefmt, blob[0:4])
                size = int(size)   # req'd for slicage
                (data,) = struct.unpack (">%lus" % size, blob[4:size+4])
                subfields.append(data)
                blob = blob [struct.calcsize(sizefmt) + size : ]

            # The first field is keytype again, and the rest we can treat as
            # an opaque list of bignums (same numbers and order as stored
            # by PuTTY). (currently embedded keytype is ignored entirely)
            magicnumbers = map (strtolong, subfields[1:])

            # Translate key type into something PuTTY can use.
            if   sshkeytype == "ssh-rsa":   keytype = "rsa2"
            elif sshkeytype == "ssh-dss":   keytype = "dss"
            else:
                raise "Unknown SSH key type", sshkeytype

        # Now print out one line per host pattern, discarding wildcards.
        for host in string.split (hostpat, ','):
            if re.search (r"[*?!]", host):
                sys.stderr.write("Skipping wildcard host pattern '%s'\n"
                                 % host)
                continue
            elif re.match (r"\|", host):
                sys.stderr.write("Skipping hashed hostname '%s'\n" % host)
                continue
            else:
                m = re.match (r"\[([^]]*)\]:(\d*)$", host)
                if m:
                    (host, port) = m.group(1,2)
                    port = int(port)
                else:
                    port = 22
                # Slightly bizarre output key format: 'type@port:hostname'
                # XXX: does PuTTY do anything useful with literal IP[v4]s?
                key = keytype + ("@%d:%s" % (port, host))
                value = string.join (map (longtohex, magicnumbers), ',')
                if output_type == 'unix':
                    # Unix format.
                    sys.stdout.write('%s %s\n' % (key, value))
                else:
                    # Windows format.
                    # XXX: worry about double quotes?
                    sys.stdout.write("\"%s\"=\"%s\"\n"
                                     % (winmungestr(key), value))

    except "Unknown SSH key type", k:
        sys.stderr.write("Unknown SSH key type '%s', skipping\n" % k)
    except "Skipping input line":
        pass

测试环境为Win7x64和Python 2.7

然后运行以下命令:

ssh-keyscan -t rsa bitbucket.org >>~/.ssh/known_hosts
python --win known_hosts.py >known_hosts.reg
start known_hosts.reg

选择导入到注册表中。使用keyscan检索域的公钥(我在使用bitbucket时遇到了问题),然后Python脚本将其转换为Plink格式。


2

工作环境:

  • Windows 10
  • git
  • putty

第一步:按照Regedit中的步骤删除putty known_hosts。
第二步:在Windows命令提示符中执行命令%GIT_SSH% user@hostname即可解决问题。

希望这对大家有所帮助。


2
Rene,你的HOME变量设置不正确。要么将其更改为c:\Users\(你的用户名),要么只更改为%USERNAME%

2

我曾经遇到过同样的问题,忘记连接到实际存储库所在的SSH端口,而不仅仅是一般的SSH端口,这时主机密钥就会不同!


同样使用相同的主机指定方式,例如对于ssh和git都不要使用gitserver.example.com和gitserver。 - Matthijs P

2

只需打开Putty,尝试连接到您想要推送代码的远程服务器。当对话框出现时,请按“是”(您信任远程服务器),然后一切都将正常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接