通过 OS Login 启用安全密钥


本文档介绍如何使用您的 Google 账号中注册的物理安全密钥连接到使用 OS Login 的虚拟机实例。

实体安全密钥用于生成用于连接到虚拟机的 SSH 私钥文件。通过 Google Cloud 控制台的“浏览器中的 SSH”工具或 Google Cloud CLI 使用安全密钥连接到虚拟机时,OS Login 会检索与您的安全密钥关联的 SSH 私钥文件,并为您配置 SSH 密钥文件。使用第三方工具进行连接时,必须使用 OS Login API 检索 SSH 密钥信息并自行配置 SSH 密钥文件。

准备工作

  • 向 Google 账户添加安全密钥
  • 设置 OS Login
  • 如果您尚未设置身份验证,请进行设置。身份验证是通过其进行身份验证以访问 Google Cloud 服务和 API 的过程。如需从本地开发环境运行代码或示例,您可以按如下方式向 Compute Engine 进行身份验证。

    Select the tab for how you plan to use the samples on this page:

    Console

    When you use the Google Cloud console to access Google Cloud services and APIs, you don't need to set up authentication.

    gcloud

    1. Install the Google Cloud CLI, then initialize it by running the following command:

      gcloud init
    2. Set a default region and zone.
    3. REST

      如需在本地开发环境中使用本页面上的 REST API 示例,请使用您提供给 gcloud CLI 的凭据。

        Install the Google Cloud CLI, then initialize it by running the following command:

        gcloud init

      如需了解详情,请参阅 Google Cloud 身份验证文档中的使用 REST 时进行身份验证

限制

  • 启用了安全密钥的虚拟机仅接受来自已附加到 Google 账号中注册的实体安全密钥的 SSH 密钥的连接。
  • 您无法使用 Cloud Shell 连接到启用了安全密钥的虚拟机。
  • 您要连接的虚拟机和从中连接的工作站都必须使用支持安全密钥 SSH 类型的 OpenSSH 8.2 或更高版本。以下 Compute Engine 虚拟机操作系统支持安全密钥:

    • Debian 11(或更高版本)
    • SUSE Linux Enterprise Server (SLES) 15(或更高版本)
    • Ubuntu 20.04 LTS(或更高版本)
    • Container-Optimized OS 93 LTS(或更高版本)
    • Rocky Linux 9(或更高版本)

    如需检查您的环境是否支持安全密钥,请运行以下命令:

    ssh -Q key | grep ^sk-
    

    如果该命令未返回任何输出,则您的环境不支持安全密钥。

  • 您要从中连接的工作站上的 SSH 客户端必须支持安全密钥,并包含所需的库(例如 libfido2)。

通过 OS Login 启用安全密钥

您可以为项目中使用 OS Login 的所有虚拟机或单个虚拟机启用安全密钥。

为项目中所有启用了 OS Login 的虚拟机启用安全密钥

如需在项目中使用 OS Login 的所有虚拟机上启用安全密钥,请使用 Google Cloud 控制台或 gcloud CLI。

控制台

如需为所有启用了 OS Login 的虚拟机启用安全密钥,请使用 Google Cloud 控制台将项目元数据中的 enable-osloginenable-oslogin-sk 设置为 TRUE

  1. 转到元数据页面。

    转到元数据

  2. 点击修改

  3. 点击添加项

    1. 密钥字段中,输入 enable-oslogin
    2. 字段中,输入 TRUE
  4. 点击添加项

    1. 密钥字段中,输入 enable-oslogin-sk
    2. 字段中,输入 TRUE
  5. 点击保存

gcloud

如需为所有已启用 OS Login 的虚拟机启用安全密钥,请使用 gcloud compute project-info add-metadata 命令在项目元数据中设置 enable-oslogin=TRUEenable-oslogin-sk=TRUE

gcloud compute project-info add-metadata \
    --metadata enable-oslogin=TRUE,enable-oslogin-sk=TRUE

在已启用 OS Login 的单个虚拟机上启用安全密钥

如需在使用 OS Login 的虚拟机上启用安全密钥,请使用 Google Cloud 控制台或 gcloud CLI。

控制台

如需在单个虚拟机上启用安全密钥,请使用 Google Cloud 控制台将实例元数据中的 enable-osloginenable-oslogin-sk 设置为 TRUE

  1. 转到虚拟机实例页面。

    转到虚拟机实例

  2. 点击要为其启用安全密钥的虚拟机的名称。

  3. 点击修改

  4. 元数据部分,点击添加项

    1. 密钥字段中,输入 enable-oslogin
    2. 字段中,输入 TRUE
  5. 点击添加项

    1. 密钥字段中,输入 enable-oslogin-sk
    2. 字段中,输入 TRUE
  6. 点击保存

gcloud

如需在单个虚拟机上启用安全密钥,请使用 gcloud compute instances add-metadata 命令在实例元数据中设置 enable-oslogin=TRUEenable-oslogin-sk=TRUE

gcloud compute instances add-metadata VM_NAME \
    --metadata enable-oslogin=TRUE,enable-oslogin-sk=TRUE

VM_NAME 替换为虚拟机名称。

使用安全密钥连接到虚拟机

您可以使用 Google Cloud 控制台、gcloud CLI 或第三方工具连接到使用安全密钥的虚拟机。如果您使用 Google Cloud 控制台或 gcloud CLI 连接到虚拟机,Compute Engine 会为您配置 SSH 密钥。如果使用第三方工具连接到虚拟机,则您必须自行执行此配置。

控制台

使用 Google Cloud 控制台的浏览器中的 SSH 工具连接到虚拟机时,浏览器中的 SSH 会检索与您的安全密钥关联的私钥。

如需连接到启用了安全密钥的虚拟机,请执行以下操作:

  1. 在 Google Cloud 控制台中,转到虚拟机实例页面。

  2. 在虚拟机列表中,点击要连接的虚拟机所在行中的 SSH

  3. 当系统提示时,轻触您的安全密钥。

gcloud

使用 gcloud CLI 连接到虚拟机时,gcloud CLI 会检索与您的安全密钥关联的私钥,并配置私钥文件。此配置是永久性的,适用于使用安全密钥的所有虚拟机。

使用 gcloud beta compute ssh 命令连接到启用了安全密钥的虚拟机:

gcloud beta compute ssh VM_NAME

第三方工具

在连接到启用了安全密钥的虚拟机之前,必须先检索与您的安全密钥关联的私钥并配置私钥文件。本示例使用 Python 客户端库执行配置。

您仅需在首次连接到虚拟机时执行此配置。该配置是永久性的,适用于您的项目中使用安全密钥的所有虚拟机。

从工作站上的终端中,执行以下操作:

  1. 安装 Python 版 Google 客户端库(如果尚未安装):

    pip3 install google-api-python-client
    
  2. 保存以下示例 Python 脚本,该脚本用于检索与您的安全密钥关联的私钥、配置私钥文件并连接到虚拟机。

    import argparse
    import os
    import subprocess
    from typing import Optional
    
    import googleapiclient.discovery
    
    
    def write_ssh_key_files(security_keys: list[dict], directory: str) -> list[str]:
        """
        Store the SSH key files.
    
        Saves the SSH keys into files inside specified directory. Using the naming
        template of `google_sk_{i}`.
    
        Args:
            security_keys: list of dictionaries representing security keys retrieved
                from the OSLogin API.
            directory: path to directory in which the security keys will be stored.
    
        Returns:
            List of paths to the saved keys.
        """
        key_files = []
        for index, key in enumerate(security_keys):
            key_file = os.path.join(directory, f"google_sk_{index}")
            with open(key_file, "w") as f:
                f.write(key.get("privateKey"))
                os.chmod(key_file, 0o600)
                key_files.append(key_file)
        return key_files
    
    
    def ssh_command(key_files: list[str], username: str, ip_address: str) -> list[str]:
        """
        Construct the SSH command for a given IP address and key files.
    
        Args:
            key_files: SSH keys to be used for authentication.
            username: username used to authenticate.
            ip_address: the IP address or hostname of the remote system.
    
        Returns:
            SSH command as a list of strings.
        """
        command = ["ssh"]
        for key_file in key_files:
            command.extend(["-i", key_file])
        command.append(f"{username}@{ip_address}")
        return command
    
    
    def main(
        user_key: str, ip_address: str, dryrun: bool, directory: Optional[str] = None
    ) -> None:
        """
        Configure SSH key files and print SSH command.
    
        Args:
            user_key: name of the user you want to authenticate as. Usually an email address.
            ip_address: the IP address of the machine you want to connect to.
            dryrun: bool flag to do dry run, without connecting to the remote machine.
            directory: the directory to store SSH private keys.
        """
        directory = directory or os.path.join(os.path.expanduser("~"), ".ssh")
    
        # Create the OS Login API object.
        oslogin = googleapiclient.discovery.build("oslogin", "v1beta")
    
        # Retrieve security keys and OS Login username from a user's Google account.
        profile = (
            oslogin.users()
            .getLoginProfile(name=f"users/{user_key}", view="SECURITY_KEY")
            .execute()
        )
    
        if "posixAccounts" not in profile:
            print("You don't have a POSIX account configured.")
            print("Please make sure that you have enabled OS Login for your VM.")
            return
    
        username = profile.get("posixAccounts")[0].get("username")
    
        # Write the SSH private key files.
        security_keys = profile.get("securityKeys")
    
        if security_keys is None:
            print(
                "The account you are using to authenticate does not have any security keys assigned to it."
            )
            print(
                "Please check your Application Default Credentials "
                "(https://cloud.google.com/docs/authentication/application-default-credentials)."
            )
            print(
                "More info about using security keys: https://cloud.google.com/compute/docs/oslogin/security-keys"
            )
            return
    
        key_files = write_ssh_key_files(security_keys, directory)
    
        # Compose the SSH command.
        command = ssh_command(key_files, username, ip_address)
    
        if dryrun:
            # Print the SSH command.
            print(" ".join(command))
        else:
            # Connect to the IP address over SSH.
            subprocess.call(command)
    
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser(
            description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
        )
        parser.add_argument("--user_key", help="Your primary email address.")
        parser.add_argument(
            "--ip_address", help="The external IP address of the VM you want to connect to."
        )
        parser.add_argument("--directory", help="The directory to store SSH private keys.")
        parser.add_argument(
            "--dryrun",
            dest="dryrun",
            default=False,
            action="store_true",
            help="Turn off dryrun mode to execute the SSH command",
        )
        args = parser.parse_args()
    
        main(args.user_key, args.ip_address, args.dryrun, args.directory)
  3. 运行脚本以配置您的密钥,还可选择连接到虚拟机。

    python3 SCRIPT_NAME.py --user_key=USER_KEY --ip_address=IP_ADDRESS [--dryrun]
    

    替换以下内容:

    • SCRIPT_NAME:配置脚本的名称。
    • USER_KEY:您的主要电子邮件地址。
    • IP_ADDRESS:要连接的虚拟机的外部 IP 地址。
    • [--dryrun]:(可选)添加 --dryrun 标志以输出连接命令,而不连接到虚拟机。如果未指定此标志,则脚本会运行连接命令。

后续步骤