安全地连接到虚拟机实例

本文档介绍了安全连接到 Compute Engine 虚拟机实例的最佳做法,包括通过启用客机特性存储主机密钥,以及阻止虚拟机通过公共互联网访问

准备工作

通过启用客机特性来存储主机密钥

主机密钥是标识特定主机或机器的密钥对。连接到远程主机时,主机密钥用于验证您是否正在连接到目标机器。

如果您使用 gcloud compute ssh 连接到 Linux 虚拟机,则可以通过将主机密钥存储为客机特性来增加一层安全性。

将 SSH 主机密钥存储为客机特性有助于防范中间人 (MITM) 攻击等漏洞,从而提高连接的安全性。虚拟机初次启动时,如果客机特性已启用,则 Compute Engine 会将生成的主机密钥存储为客机特性。然后,Compute Engine 会使用这些存储的主机密钥来验证与该虚拟机的所有后续连接。

主机密钥可以存储为以下公共操作系统映像上的客机特性:

  • Debian
  • Ubuntu
  • Red Hat Enterprise Linux (RHEL)
  • CentOS
  • SUSE Linux Enterprise Server (SLES)

如需将主机密钥写入到客机特性,您必须在第一次启动虚拟机之前启用客机属性。您可以在虚拟机创建期间选定的虚拟机或在整个项目上启用客机特性。

为项目或虚拟机启用客机特性后,客机操作系统代理会自动将主机密钥发布为客机特性。如果您使用的是 gcloud compute ssh 而不是普通 SSH 客户端,则 gcloud 工具会自动读取这些特性,并在您下次连接时更新 known_hosts 文件。

如需将主机密钥存储为客机特性,请完成以下步骤:

  1. 第一次启动虚拟机之前,请执行以下操作:在虚拟机创建期间选定的虚拟机或在整个项目上启用客机特性

  2. 使用 gcloud compute ssh 连接到您的虚拟机。

    1. 确保您拥有最新版本的 gcloud 命令行工具:

      gcloud components update
      
    2. 连接到虚拟机:

      gcloud compute ssh --project=PROJECT_ID \
       --zone=ZONE \
       VM_NAME
      

      请替换以下内容:

      • PROJECT_ID:包含虚拟机的项目 ID。
      • ZONE:虚拟机所在区域的名称
      • VM_NAME:虚拟机的名称

      如果您已为 gcloud 命令行工具设置默认属性,则可以省略此命令中的 --project--zone 标志。例如:

      gcloud compute ssh VM_NAME
      
    3. 查看启动消息。例如,Debian 操作系统可能会显示以下消息:

      Writing 3 keys to YOUR_HOME_DIRECTORY/.ssh/google_compute_known_hosts
      Linux host-key-2 4.9.0-9-amd64 #1 SMP Debian 4.9.168-1+deb9u3 (2019-06-16) x86_64
      

如需确认主机密钥已存储为此虚拟机的客机特性,请查看主机密钥值以验证 SSH 密钥已写入虚拟机的客机特性(方式 1),或查看串行端口中是否存在主机密钥(方式 2):

方式 1:查看主机密钥值

您可以使用 gcloud 命令行工具验证 SSH 密钥已写入到客机特性:

gcloud compute instances get-guest-attributes VM_NAME \
  --query-path="hostkeys/" \
  --zone=ZONE

请替换以下内容:

  • VM_NAME:虚拟机的名称
  • ZONE:虚拟机所在区域的名称

输出内容类似如下:

NAMESPACE  KEY                  VALUE
hostkeys   ecdsa-sha2-nistp256  AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBJAGpTm
                                V3mFxBTHK1NIu9a7kVQWaHsZVaFUsqF8cLxQRQ+N96/Djiiuz1tucHQ8vBTJI=
hostkeys   ssh-ed25519          AAAAC3NzaC1lZDI1NTE5AAAAIM/WYBn3jIEW5t3BZumx0X/Htm61J6S9FcU8L
hostkeys   ssh-rsa              AAAAB3NzaC1yc2EAAAADAQABAAABAQDU3jReR/MoSttlWYfauW6qEqS2dhe5
                                Zdd3guYk2H7ZyxblNuP56nOl/IMuniVmsFa9v8W6MExViu6G5Cy4iIesot09
                                1hsgkG0U7sbWrXM10PQ8pnpI3B5arplCiEMhRtXy64rlW3Nx156bLdcxv5l+
                                7Unu4IviKlY43uqqwSyTv+V8q4ThpQ9dNbk1Gg838+KzazljzHahtbIaE1rm
                                I0L1lUqKiKLSLKuBgrI2Y/WSuqvqGEz+bMH7Ri4ht+7sAwykph6FbOgKqoBI
                                hVWBo38/Na/gEuvtmgULUwK+xy9zWg9k8k/Qtihc6El9GD9y

方式 2:查看串行端口

  1. 查看串行端口输出
  2. 选择串行端口 1
  3. 搜索以下消息:

    INFO Wrote ssh-rsa host key to guest attributes

    如果您的映像使用受支持的操作系统,但您在虚拟机第一次启动之前未启用客机特性设置,则您可能会看到以下消息:

    Unable to write ssh-rsa host key to guest attributes

    这意味着主机密钥未存储为此虚拟机的客机特性。如果要为计划创建的其他虚拟机存储主机密钥,请在虚拟机首次启动之前启用客机特性。

阻止用户通过公共互联网访问虚拟机

在 Compute Engine 上开发项目时,您希望在各种场景中阻止通过公共互联网访问虚拟机:

  • 网络服务尚在开发阶段,尚未准备好向外部用户公开,理由是它们的功能不完整或尚未配置 HTTPS。
  • 虚拟机提供的服务可能仅供项目中的其他虚拟机使用。
  • 虚拟机只能通过公司办公室或数据中心的专用互连选项访问。

即使服务的设计意图是面向互联网的,也有必要将与服务的通信限制为面向用户组且通过安全渠道(如 SSH 或 HTTPS)发生,以保护敏感信息。

本文演示了几种可确保与具有外部 IP 地址的虚拟机没有外部 IP 地址的虚拟机之间的通信安全无虞的方法。

在使用外部 IP 地址的机器上保护服务

在虚拟机具有公共 IP 地址时,务必保证仅有您希望公开的服务和流量可供访问,同时也要保证在已公开的服务和流量中,敏感信息均在传输过程中得到保护。您可以采用几种方法来保护具有本文档中所述外部 IP 地址的虚拟机上的服务,包括防火墙HTTPS 和 SSL通过 SSH 进行端口转发基于 SSH 的 SOCKS 代理

防火墙

第一道防线是使用防火墙限制哪些用户可以访问虚拟机。通过创建防火墙规则,您可对给定的一组端口上传送到特定网络或目标机器的所有流量进行限制,仅允许来自指定源 IP 地址的流量。

防火墙并非独立解决方案,仅允许来自特定源 IP 的流量这一做法本身并不能保护敏感信息(例如登录凭据,创建或销毁资源或文件的命令,或者日志)。在可公开访问的机器上运行网络服务(例如具有外部 IP 的 Compute Engine 虚拟机)时,您必须加密您的主机与已部署虚拟机之间的所有通信,确保具有适当安全性。

此外,防火墙并非总是合适的解决方案。例如,防火墙不适用于没有静态 IP 地址的开发环境,例如漫游的笔记本电脑。

HTTPS 和 SSL

对于生产网络系统,建议配置 HTTPS/SSL。您可以通过将某个虚拟机设置为 HTTPS 终结点或配置 HTTPS 负载平衡来设置 HTTPS/SSL。HTTPS/SSL 的初始阶段确实较为复杂,您需要执行以下任务:

通过 SSH 进行端口转发

您可以使用 gcloud 命令行工具在给定本地端口上启动服务器,以通过 SSH 连接将所有流量转发到远程主机。

首先,请记下提供安全连接服务的虚拟机和端口。然后运行以下命令:

gcloud compute ssh VM_NAME \
    --project PROJECT_ID \
    --zone ZONE \
    -- -NL LOCAL_PORT:localhost:REMOTE_PORT

请替换以下内容:

  • VM_NAME 是要连接到的虚拟机的名称。
  • PROJECT_ID 是您的 Google Cloud 项目 ID
  • ZONE:运行您的虚拟机的地区,例如 us-central1-a
  • LOCAL_PORT:您正在侦听的本地端口,例如 2222
  • REMOTE_PORT:您要连接的远程端口,例如 8888

例如,如果您指定本地端口为“2222”和远程端口“8888”,并在浏览器中打开 http://localhost:2222/,HTTP 连接使用您创建到远程主机的 SSH 隧道,以便通过 SSH 连接到指定的虚拟机。然后,HTTP 连接通过 SSH 隧道连接到同一台机器上的端口 8888,但采用的是已加密的安全 SSH 连接。

在 SSH 会话处于活动状态时,gcloud 命令创建并维护 SSH 连接。只要您退出 SSH 会话,使用 http://VM_NAME:LOCAL_PORT 端口转发就会停止工作。

若要创建多个端口转发规则,您可以通过重复使用标志在单个命令行中指定多条规则:

gcloud compute ssh VM_NAME \
    --project PROJECT_ID \
    --zone ZONE \
    -- -NL LOCAL_PORT:localhost:REMOTE_PORT \
    -- -NL LOCAL_PORT:localhost:REMOTE_PORT

您还可以每次运行一个新的 gcloud 命令来创建单独的隧道。请注意,如果不退出并从头开始重新建立连接,则无法在现有连接中添加或移除端口转发。

基于 SSH 的 SOCKS 代理

如果要在您的云部署中连接到大量不同主机,最简单的方法是对浏览器进行更改,直接通过您的网络执行查找。这样,您就可以使用主机的简称,而不必按每个主机的 IP 地址进行查找、为每项服务打开端口,也不必为每个主机/端口对创建一个 SSH 隧道。

在这里,您可以使用如下方法:

  1. 设置到网络中一台主机的单一 SSH 隧道,并在该主机上创建 SOCKS 代理。
  2. 更改浏览器配置,以使用该 SOCKS 代理主机执行所有查找。

请注意,您使用该主机通过隧道传输所有流量,因此请勿使用该浏览器或特定配置文件来浏览网页,理由是该带宽需要专门用于您的云服务。通常,我们建议您使用单独的浏览器配置文件,并在必要情况下切换到该配置文件。

启动 SOCKS 代理

要启动 SOCKS 代理,请运行以下命令:

gcloud compute ssh VM_NAME \
    --project PROJECT_ID \
    --zone ZONE
    --ssh-flag="-D" \
    --ssh-flag="LOCAL_PORT" \
    --ssh-flag="-N"

请替换以下内容:

  • VM_NAME:您要连接的虚拟机的名称。
  • PROJECT_ID:您的 Google Cloud 项目 ID
  • ZONE:运行您的虚拟机的地区,例如 us-central1-a
  • LOCAL_PORT:您正在侦听的本地端口,例如 1080

请注意,在本例中,您不需要指定远程端口。由于 SOCKS 代理不绑定到任何特定的远程端口,因此您使用 SOCKS 代理建立的任何连接都将相对于您连接到的主机进行解析。

通过 SOCKS 代理,您可以使用代理的简称连接到与代理虚拟机共用 Compute Engine 网络的任何虚拟机。此外,您还可以连接到给定虚拟机上的任何端口。

这种方法比简单的端口转发方法要灵活得多,但要使用代理,也需要您更改网络浏览器中的设置。

接下来,您需要将 Chrome 或 Firefox 配置为使用该代理。

Chrome

Chrome 默认使用系统级代理设置,因此您需要使用命令行标志指定不同的代理。默认情况下,启动 Chrome 会为已运行的配置文件创建虚拟机,因此,为了让您能够同时运行多个 Chrome 副本,一个副本使用代理,而其他副本不使用代理,您需要一个新的配置文件。

使用新配置文件启动 Chrome。如果该配置文件不存在,则系统会自动创建一个。

Linux

/usr/bin/google-chrome \
    --user-data-dir="$HOME/chrome-proxy-profile" \
    --proxy-server="socks5://localhost:1080"

macOS

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
    --user-data-dir="$HOME/chrome-proxy-profile" \
    --proxy-server="socks5://localhost:1080"

Windows

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" ^
    --user-data-dir="%USERPROFILE%\chrome-proxy-profile" ^
    --proxy-server="socks5://localhost:1080"

请务必将 localhost 端口设置为您之前在 gcloud 命令中使用的值(在我们的示例中为 1080)。

Firefox

在更改这些设置之前,您可能需要创建新的 Firefox 配置文件。否则,这会影响 Firefox 的所有虚拟机,导致其都将该主机用作代理,而这很可能并不是您想要的结果。

使用单独的配置文件运行 Firefox 后,您可以设置 SOCKS 代理:

  1. 打开偏好设置
  2. 点击高级 > 网络 > 设置,打开连接设置对话框。
  3. 选择手动代理配置 (Manual proxy configuration) 选项。
    1. SOCKS 主机 (SOCKS Host) 部分中,填写 localhost 作为主机,并填写您先前运行 gcloud 命令时选择的端口。
    2. 选择 SOCKS v5
    3. 勾选远程 DNS (Remote DNS) 框。
    4. 将其他所有条目留空。
  4. 点击确定并关闭偏好设置对话框。

连接至没有外部 IP 地址的虚拟机

如果虚拟机没有外部 IP 地址(包括作为 HTTPS 和 SSL 代理负载平衡器的虚拟机),则只有网络上的其他虚拟机可以访问它们,Identity-Aware Proxy 的 TCP 转发功能,或使用托管 VPN 网关。您可以在网络中预配虚拟机,以充当入站连接(也称为堡垒主机)的可信中继。此外,您还可以为网络出站流量配置 Cloud NAT,或设置交互式串行控制台,以维护没有外部 IP 地址的虚拟机或为其排查问题地址。

堡垒主机

堡垒主机提供接入包含专用网络实例的网络的外部入口点,如下图所示。

充当专用实例的网络的外部入口点的堡垒主机架构。

这种主机可以提供单一防御或审核点,而且可启动或停止来启用或停用入站 SSH。通过使用堡垒主机,您可以连接到没有外部 IP 地址的虚拟机。通过这种方法,您可以连接到开发环境或者为外部应用管理数据库实例,例如在不配置额外防火墙规则的情况下。

堡垒主机的完整安全强化介绍超出了本文讨论范围,但您可以采取一些初始步骤,包括:

  • 限制可与堡垒主机通信的源 IP 的 CIDR 范围。
  • 配置防火墙规则,仅允许来自堡垒主机的 SSH 流量传输到专用虚拟机。

默认情况下,虚拟机上的 SSH 会配置为使用私钥执行身份验证。使用堡垒主机时,您应该先登录堡垒主机,然后再登录目标专用虚拟机。由于登录分两步进行,人们有时也将堡垒主机称为“跳转服务器”,您应使用 ssh 转发,而不是通过将目标机器的私钥存储在堡垒主机上的方式访问目标机器。即使堡垒主机和目标虚拟机使用相同的密钥对,您也需要这样做,因为堡垒主机只能直接访问密钥对的公钥部分。

如需了解如何使用堡垒主机实例连接到 Google Cloud 网络上的其他虚拟机,请参阅通过堡垒主机进行连接

如需了解如何使用 ssh 转发和其他方法连接到没有外部 IP 地址的虚拟机,请参阅连接到没有外部 IP 地址的虚拟机

使用 IAP 进行 TCP 转发

搭配 SSH 使用 IAP 的 TCP 转发功能可将 SSH 连接封装在 HTTPS 内部。这样一来,IAP 的 TCP 转发功能就能将该连接发送至远程虚拟机。

如需了解如何使用 IAP 连接到远程虚拟机,请参阅使用 IAP 进行 TCP 转发

VPN

借助 Cloud VPN,您可以通过与 VPN 网关设备的 IPsec 连接将现有网络连接到 Google Cloud 网络。这样,就能将来自您本地环境的流量直接路由到 Compute Engine 虚拟机的专用 IP 接口。流量在通过公开链接传输到 Google 时会被加密。

要详细了解如何使用 Compute Engine 设置、配置和使用 VPN,请参阅 Cloud VPN 文档

要了解如何通过现有 VPN 而非虚拟机的外部 IP 地址连接到 Google Cloud 网络中的虚拟机,请阅读连接到没有外部 IP 地址的虚拟机

使用 Cloud NAT 的出站流量

如果没有为虚拟机分配外部 IP 地址,则虚拟机无法与外部服务(包括其他 Google Cloud 服务)建立直接连接。要允许这些虚拟机访问公共互联网上的服务,您可以设置和配置 Cloud NAT,让其代表网络中的任何虚拟机路由流量。不要将单个虚拟机视为高可用性配置,也无法支持多个虚拟机的高流量吞吐量。

交互式串行控制台访问

如果虚拟机没有外部 IP 地址,您仍可能需要与虚拟机交互才能进行问题排查或维护。如前所述,您可以选择设置堡垒主机,但所需设置工作量可能超过您的需求。如果要对没有外部 IP 地址的虚拟机进行问题排查,请考虑在串行控制台上启用交互式访问,这让您可以使用 SSH 与虚拟机的串行控制台进行交互然后针对串行控制台运行命令。

要了解详情,请参阅与串行控制台进行交互

HTTPS 和 SSL 代理负载平衡器

作为 HTTPS 和 SSL 代理负载平衡器后端的虚拟机无需通过负载平衡器访问外部 IP 地址。如需直接访问这些资源,您需要使用连接到没有外部 IP 地址的虚拟机部分中所列的方法。

如需了解详情,请参阅针对这些负载平衡器的负载平衡文档