透過 OS 登入功能啟用安全金鑰


本文說明如何使用 Google 帳戶中註冊的實體安全金鑰,連線至使用 OS 登入的虛擬機器 (VM) 執行個體。

實體安全金鑰可用於產生私密安全殼層金鑰檔案,以便連線至 VM。使用 Google Cloud 控制台的瀏覽器內建 SSH 工具或 Google Cloud CLI,透過安全金鑰連線至 VM 時,OS Login 會擷取與安全金鑰相關聯的私密 SSH 金鑰檔案,並為您設定 SSH 金鑰檔案。使用第三方工具連線時,您必須使用 OS Login API 擷取 SSH 金鑰資訊,並自行設定 SSH 金鑰檔案。

事前準備

  • 為 Google 帳戶新增安全金鑰
  • 設定 OS 登入
  • 如果尚未設定驗證,請先完成設定。 「驗證」是指驗證身分的程序,確認您有權存取 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. 安裝 Google Cloud CLI。 安裝完成後,執行下列指令初始化 Google Cloud CLI:

      gcloud init

      如果您使用外部識別資訊提供者 (IdP),請先 使用聯合身分登入 gcloud CLI

    2. Set a default region and zone.

    REST

    如要在本機開發環境中使用本頁的 REST API 範例,請使用您提供給 gcloud CLI 的憑證。

      安裝 Google Cloud CLI。 安裝完成後,執行下列指令初始化 Google Cloud CLI:

      gcloud init

      如果您使用外部識別資訊提供者 (IdP),請先 使用聯合身分登入 gcloud CLI

    詳情請參閱 Google Cloud 驗證說明文件中的「Authenticate for using REST」。

限制

  • 啟用安全金鑰的 VM 只會接受來自 SSH 金鑰的連線,而這些金鑰會附加至您 Google 帳戶中註冊的實體安全金鑰。
  • 您無法使用 Cloud Shell 連線至已啟用安全金鑰的 VM。
  • 連線的 VM 和連線來源的工作站都必須使用 OpenSSH 8.2 以上版本,才能支援安全金鑰 SSH 類型。下列 Compute Engine VM 作業系統支援安全金鑰:

    • 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-
    

    如果該指令未傳回任何輸出內容,表示您的環境不支援安全金鑰。

  • 您用來連線的工作站必須支援安全金鑰,並包含必要的程式庫,例如 libfido2

透過 OS 登入啟用安全金鑰

您可以為專案中所有使用 OS 登入功能的 VM,或為單一 VM 啟用安全金鑰。

為專案中所有啟用 OS 登入功能的 VM 啟用安全金鑰

如要在專案中,為所有使用 OS 登入功能的 VM 啟用安全金鑰,請使用Google Cloud 控制台或 gcloud CLI。

主控台

如要為所有已啟用 OS 登入功能的 VM 啟用安全金鑰,請使用Google Cloud 控制台,將專案中繼資料的 enable-osloginenable-oslogin-sk 設為 TRUE

  1. 前往「中繼資料」頁面。

    前往「中繼資料」

  2. 按一下 [編輯]

  3. 按一下 [新增項目]。

    1. 在「Key」(金鑰) 欄位中輸入 enable-oslogin
    2. 在「Value」(值) 欄位中,輸入 TRUE
  4. 按一下 [新增項目]。

    1. 在「Key」(金鑰) 欄位中輸入 enable-oslogin-sk
    2. 在「Value」(值) 欄位中,輸入 TRUE
  5. 按一下 [儲存]

gcloud

如要為所有已啟用 OS 登入功能的 VM 啟用安全金鑰,請使用 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 登入功能的單一 VM 上啟用安全金鑰

如要在使用 OS 登入功能的 VM 上啟用安全金鑰,請使用Google Cloud 控制台或 gcloud CLI。

主控台

如要在單一 VM 上啟用安全金鑰,請使用 Google Cloud 控制台,將執行個體中繼資料的 enable-osloginenable-oslogin-sk 設為 TRUE

  1. 前往「VM instances」(VM 執行個體) 頁面。

    前往 VM 執行個體

  2. 按一下要啟用安全金鑰的 VM 名稱。

  3. 按一下 [編輯]

  4. 在「Metadata」(中繼資料) 區段中,按一下「Add item」(新增項目)

    1. 在「Key」(金鑰) 欄位中輸入 enable-oslogin
    2. 在「Value」(值) 欄位中,輸入 TRUE
  5. 按一下 [新增項目]。

    1. 在「Key」(金鑰) 欄位中輸入 enable-oslogin-sk
    2. 在「Value」(值) 欄位中,輸入 TRUE
  6. 按一下 [儲存]

gcloud

如要在單一 VM 上啟用安全金鑰,請使用 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 名稱替換為 VM_NAME

使用安全金鑰連線至 VM

您可以使用 Google Cloud 控制台、gcloud CLI 或第三方工具,連線至使用安全金鑰的 VM。如果您使用 Google Cloud 控制台或 gcloud CLI 連線至 VM,Compute Engine 會為您設定 SSH 金鑰。如果您使用第三方工具連線至 VM,則必須自行完成設定。

主控台

使用 Google Cloud 控制台 瀏覽器中的 SSH 工具連線至 VM 時,瀏覽器中的 SSH 會擷取與安全金鑰相關聯的私密金鑰。

如要連線至已啟用安全金鑰的 VM,請按照下列步驟操作:

  1. 前往 Google Cloud 控制台的「VM instances」(VM 執行個體) 頁面

  2. 在 VM 清單中,找到要連線的 VM,然後按一下該列中的「SSH」SSH

  3. 在系統提示時輕觸安全金鑰。

gcloud

使用 gcloud CLI 連線至 VM 時,gcloud CLI 會擷取與安全金鑰相關聯的私密金鑰,並設定私密金鑰檔案。這項設定會持續生效,並套用至使用安全金鑰的所有 VM。

使用 gcloud beta compute ssh 指令連線至已啟用安全金鑰的 VM:

gcloud beta compute ssh VM_NAME

第三方工具

連線至已啟用安全金鑰的 VM 前,您必須先擷取與安全金鑰相關聯的私密金鑰,並設定私密金鑰檔案。本範例使用 Python 用戶端程式庫執行設定。

您只需要在首次連線至 VM 時執行這項設定。這項設定會持續生效,並套用至專案中所有使用安全金鑰的 VM。

在工作站的終端機中,執行下列操作:

  1. 如果尚未安裝 Python 適用的 Google 用戶端程式庫,請執行下列指令:

    pip3 install google-api-python-client
    
  2. 儲存下列 Python 指令碼範例,這個指令碼會擷取與安全金鑰相關聯的私密金鑰、設定私密金鑰檔案,並連線至 VM。

    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. 執行指令碼來設定金鑰,並視需要連線至 VM。

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

    更改下列內容:

    • SCRIPT_NAME:設定指令碼的名稱。
    • USER_KEY:您的主要電子郵件地址。
    • IP_ADDRESS:您要連線的 VM 外部 IP 位址。
    • [--dryrun]:(選用) 新增 --dryrun 旗標,即可列印連線指令,但不會連線至 VM。如未指定此旗標,指令碼會執行連線指令。

後續步驟