如何从 Google 计算引擎实例安全地检索 ssh 主机密钥?

如何从 Google 计算引擎实例安全地检索 ssh 主机密钥?

我想~/.ssh/known_hosts用新创建的 GCE 实例的主机密钥信息来更新我的。但我不确定如何安全地检索该信息。

我觉得

gcloud compute ssh <GCEUSER>@<GCEHOST> --command='ssh-keyscan 127.0.0.1'

可能会奏效。但(根据gcloud compute ssh 文档) 似乎只是一个包装器ssh(并且,根据StrictHostKeyChecking=no在 $HOME/.config/gcloud/logs/ 下的关联日志文件中列出的参数,显然没有对主机的身份进行任何检查)。

似乎有一种方法可以使用 Web 控制台启动基于浏览器的 ssh 会话(并以交互方式/手动运行ssh-keyscan),但是 1)我看不到内部结构以知道它是否真的像它应该的那样安全以及 2)不是用于脚本集成的有效 API。

是否有一个 API/gcloud机制可以安全地检索 GCE 实例的主机密钥?

答案1

OP 链接至错误 35907612(最初由 Rahi 提及)标记为“已修复”,并附有文档链接存储主机密钥

要获取主机密钥,您需要启用“来宾属性” 或者期间创建 GCE 实例。我在整个项目范围内启用了它(不确定为什么默认情况下禁用它)。或者,您可以在创建实例时启用它。

根据文档,gcloud需要填充来宾属性,但我似乎可以跳至步骤“确认主机密钥存储为访客属性”实例启动后立即。

笔记

  1. 在步骤 2(确认密钥)中,我选择了选项 2,因为我在上一步中没有要求gcloud填充~/.ssh/google_compute_known_hosts,所以我想看到密钥,而不仅仅是验证它们是否在“某处”。

  2. 当你跑步时get-guest-attributes子命令--project--zone标志都是可选的,它们默认为使用gcloud config

  3. 要直接从 获得主机密钥的 SHA-256 哈希值get-guest-attributes,借助jq

    gcloud compute instances get-guest-attributes my-instance --format json | jq -r '.[] | "\(.key) \(.value) my-server-name"' | ssh-keygen -l -f -
    

    解释:-r表示--raw-outputjq-l表示列出带有哈希值的键。-f -表示来自的文件stdinmy-server-name是输出中出现的可选注释(否则您会看到no comment)。

答案2

“gcloud 命令”或“API”将仅检索与特定项目或 Google 产品相关的信息,例如来自 GCE 实例的实例元数据。无法通过 gcloud 命令或 API 检索实例的配置(例如主机密钥)。

更新

SSH 主机密钥对,私钥和公钥保存在 SSH 服务器上。公钥在每个连接上与 SSH 客户端共享。这用于向客户端验证 SSH 服务器。第一次连接后,“已知主机”密钥通常存储在 SSH 客户端(例如您的计算机)上的 ~/.ssh/known_hosts 中,但 gcloud 将其存储在不同的地方。

当您使用 gcloud compute ssh 时,您的 SSH 客户端的行为略有不同:第一次连接时,服务器的公共主机密钥会被自动接受,并将其写入 ~/.ssh/google_compute_known_hosts。

每次连接到 SSH 服务器时,它都会发送其公共主机密钥。在大多数情况下,服务器的公共主机密钥不会改变。连接一次并保存第一次连接的公钥后,SSH 客户端可以检查服务器的公共主机密钥是否不同。如果不同,您将看到一条消息:

主机密钥验证失败。

当您看到此消息时,表示存在以下情况之一:

  • 您正在连接到其他服务器。这不一定是恶意的。例如,如果您用新服务器替换旧服务器,就会看到此消息。
  • 您正在连接到同一台服务器,但其 SSH 主机密钥已重新生成。同样,这并不一定表示存在恶意行为。例如,管理员可能已使用 ssh-keygen 创建新的主机密钥对。

默认情况下,gcloud compute ssh 不执行严格主机密钥检查。这样做有原因;但是,您可以通过向 gcloud compute ssh 添加 --oStrictHostKeyChecking=yes 选项来启用严格主机密钥检查。以下是示例:

gcloud compute ssh [INSTANCE_NAME] --zone [ZONE] -- -oStrictHostKeyChecking=yes

这是如何向 gcloud compute ssh 添加任意 SSH 参数的一个很好的例子。

跳过主机密钥检查不是问题。假设您创建了一个从未连接过的新实例。您如何知道您正在连接到该实例而不是其他地方?

请考虑以下示例。假设您没有 SSH 用户密钥对供 gcloud 使用。(如果您将 ~/.ssh/google_compute_engine 和 ~/.ssh/google_compute_engine.pub 都移开,则可以对此进行测试。)假设您尚未登录 gcloud。您可以使用 gcloud auth revoke 注销。

现在尝试使用 gcloud compute ssh 通过 SSH 连接到实例。您必须先向 gcloud 进行身份验证。因此,运行 gcloud auth login 并登录。现在您的系统有一个存储在 ~/.config/gcloud/ 中的访问令牌。每次您使用 gcloud 进行 API 调用时,此令牌都会用于身份验证。

现在尝试再次使用 gcloud compute ssh 通过 SSH 连接到同一个实例。如果没有 SSH 用户密钥对,它将代表您调用 ssh-keygen 来创建新的 SSH 用户密钥对。私钥(用户)将存储在 ~/.ssh/google_compute_engine 中,公钥(用户)将存储在 ~/.ssh/google_compute_engine.pub 中。私钥保留在您的计算机本地,您负责保护它。公钥的内容通过 API 调用发送到 projects.setCommonInstanceMetadata 或 instance.setMetadata。默认情况下,gcloud 会将您的公钥添加到项目元数据(针对所有实例),除非您连接的实例已配置为阻止项目范围的 SSH 密钥;在这种情况下,它会将公钥添加到实例的元数据中。这两个 API 调用都使用您在执行 gcloud auth login 时收到的令牌进行身份验证。公钥在传输过程中被加密,因为 gcloud 命令是通过 HTTPS 上的 API 调用执行的。

此时,您有一个全新的 SSH 用户私钥。除非您已将其复制到其他地方,否则它位于您的计算机本地。其对应的公钥也是如此,只是您已通过 HTTPS 上的经过身份验证的 gcloud 命令将其发送至 Google。因此,唯一应该有权访问公钥的系统是您在 GCP 项目中运行的系统。

只有 SSH 用户私钥的持有者才能成功向 GCP 中运行的 SSH 服务器进行身份验证。只有在 GCP 中运行的 SSH 服务器才拥有您的 SSH 用户公钥的副本。因此,自动接受主机公钥是安全的。如果您连接到的是流氓服务器而不是 GCP SSH 服务器,身份验证过程将失败,因为该服务器没有您的 SSH 用户公钥。

在某些情况下,GCP 会自动重新创建 SSH 服务器的主机密钥对。例如,如果您使用一个实例创建一个模板,该模板将生成组中的其他实例。在每种情况下,都需要更新 SSH 主机密钥。因此,执行严格的 SSH 主机密钥检查可能会生成误导性的错误消息。

更新于 2018 年 9 月 5 日

无需“glcoud”即可连接到实例,GCP 团队已意识到这一点并正在努力解决。已提交公开错误这里. 任何进展都可以在那里找到。

答案3

不,没有直接的方法可以做到这一点(有一个解决此安全风险的公开问题)。

答案4

主机密钥已为您检索到您正在运行的框中gcloud,位于 ~/.ssh/google_compute_known_hosts。

根据--help,主机密钥检查的行为如下:

 --strict-host-key-checking=STRICT_HOST_KEY_CHECKING
    Override the default behavior of StrictHostKeyChecking for the
    connection. By default, StrictHostKeyChecking is set to 'no' the first
    time you connect to an instance, and will be set to 'yes' for all
    subsequent connections. STRICT_HOST_KEY_CHECKING must be one of: yes,
    no, ask.

如果您查看后续连接,日志文件中的行为就是如此。

2018-08-04 14:48:29,222 DEBUG    root            Running command [/usr/bin/ssh -t -i /home/john/.ssh/google_compute_engine -o CheckHostIP=no -o HostKeyAlias=compute.1520573357386694112 -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/home/john/.ssh/google_compute_known_hosts [email protected]].

2018-08-04 14:51:06,249 DEBUG    root            Executing command: [u'/usr/bin/ssh', u'-t', u'-i', u'/home/john/.ssh/google_compute_engine', u'-o', u'CheckHostIP=no', u'-o', u'HostKeyAlias=compute.1520573357386694112', u'-o', u'IdentitiesOnly=yes', u'-o', u'StrictHostKeyChecking=yes', u'-o', u'UserKnownHostsFile=/home/john/.ssh/google_compute_known_hosts', u'[email protected]']

这是为了方便,因此第一次连接将不会询问主机,但以后的连接将检查。

实际上,这与用户从其客户端看到未知主机密钥并在未读取密钥的情况下接受该密钥是一样的。如果对连接的主机名有一定信心,那么在大多数情况下这样做是合理的。

在这种情况下,您对系统的信任应该是您对 GCE 元数据服务将您路由到正确主机并将您的 ssh 密钥推送给它的信任程度。

相关内容