はじめに

みなさん、Google Cloud(旧GCP)のCompute Engine(GCE)インスタンスにSSH接続する時、どんなアクセス制御をしていますか?専用の踏み台(Bastion)サーバーを経由して対象のインスタンスへアクセスする」といった運用をしている現場も、きっと多いのではないでしょうか。

でもこのやり方だと、ちょっとログを確認したいだけなのに毎回踏み台を経由する手間がかかりますし、何より「踏み台サーバー自体の維持費や、OSの脆弱性管理(パッチ当て)」といったインフラの保守コストが重荷になりますよね。

そこで今回は、Google Cloudが強く推奨しているゼロトラストモデルのベストプラクティス、「IAP(Identity-Aware Proxy)+ OS Login」の構成で、セキュアなSSH接続環境の構築を実践してみました!
この後はIAPと略称で説明します。

■ そもそも「IAP + OS Login」とは? 簡単に言うと、以下のような2つの強力な仕組みを組み合わせた接続方法です。

IAPとは

特定のIPアドレスからの通信を許可するのではなく、Googleの強力な認証を通った通信だけを「トンネル」としてサーバーに到達させる仕組みです。

外部(公開)IPを持たないプライベートなインスタンスにも安全に接続できます。

OS Loginとは

面倒な手動でのSSH鍵管理をGoogle CloudのIAMで一元管理し、接続するたびに「一時的な鍵」を自動で生成・同期してくれる非常に便利な仕組みです。

この記事では、「なぜこの構成が優れているのか?」というさらに詳しいメリットの解説から、Terraformで構築しようとして直面したGoogle Cloud特有の「権限管理の壁」、そしてそれをどう解決したのかという実践記録をお届けします。

第1章: 従来の手法の課題と、IAP + OS Login導入のメリット

従来の「特定の固定IP制限」から、Google Cloudが推奨する「IAP経由 + OS Login」の構成へ移行することには、セキュリティと運用効率の両面ですごく大きなメリットがあります。

■ IAP (Identity-Aware Proxy) による攻撃対象領域の最小化

従来の踏み台サーバーを用いた構成では、踏み台自体には外部(公開)IPを持たせる必要があり、そこが攻撃の入り口として狙われるリスクがありました。
これに対してIAPを利用する場合は踏み台サーバーそのものが不要となり、すべてのインスタンスから外部IPを排除できるため、インターネットからの直接的な攻撃(ブルートフォース攻撃など)のリスクを大幅に低減できます。

インターネット全体にポート22を公開するのではなく、ファイアウォールでGoogle IAPのプロキシサーバ群の固定IPレンジ(35.235.240.0/20)からのTCP通信のみを許可することで、それ以外の経路からのアクセスを完全にシャットアウトできます。

さらに、「誰が」「どの端末から」アクセスしているかをGoogleの強力な認証基盤(IAM)がチェックしてくれるため、認証されたユーザーのみ安全にアクセス可能になります。

■ Google Cloud OS Login:IAMによるLinuxユーザー管理の統合

通常、LinuxインスタンスへのSSHアクセスは、各サーバーへの直接的な鍵配置や、統合認証基盤などによって管理されます。しかしこれらは、鍵の管理漏れリスクや、認証基盤自体の運用・保守コストを伴います。

OS Loginは、Linuxインスタンスのユーザー管理を Google Cloud IAM に統合し、ローカルでのユーザー管理の手間を大幅に簡素化する仕組みです。

1. 鍵管理の集中化と自動連携

SSH公開鍵は各サーバーではなく、Google Cloud上のOS Loginプロファイルで一元管理されます。SSH接続時には、サーバーがGoogleのAPIを通じてユーザーの権限や公開鍵情報を取得し、ログイン可否を判断します。

2. グループ管理によるセキュアな運用

実運用では、Cloud Identity や Google Workspace のグループで権限管理を行います。これにより、メンバーの異動や退職時には、グループからユーザーを削除するだけで、対象サーバーへのアクセス権を一括で剥奪できます。

3. 監査ログ(Cloud Audit Logs)への対応

OS Loginを利用すると、SSHのログイン履歴が Cloud Audit Logs に記録されます。

「誰が・いつ・どのインスタンスにアクセスしたか」をGoogleアカウントベースで中央集権的に追跡できるようになり、セキュリティ監査の信頼性が向上します。

■ 【補足】接続ツールの使い分け(コンソール SSH vs gcloud コマンド)

IAPを経由した接続ツールには、主に2つの選択肢があります。

  • コンソール SSH: ブラウザだけで完結するため、事前準備なしで簡単に接続できます。
  • gcloud compute ssh (CLI): ローカルのファイル転送(scp)や、手元のエディタ等と連携した作業が可能です。

第2章: Terraformでのインフラ構築と、IAM権限の落とし穴

このセキュアな環境をIaCとして管理するため、Terraformを使って構築をスタートしました。IAPとOS Loginを有効化するためのネットワークやインスタンス側の設定自体は、実はとてもシンプルです。

【実装したTerraformコード(インフラリソース部分)】

# 1. OS Loginの有効化(インスタンスのメタデータで設定)
metadata = {
  enable-oslogin = "TRUE"
}

# 2. IAP用ファイアウォールルールの追加
# Google IAP の固定 IP レンジ (35.235.240.0/20) からの TCP:22 通信を許可
resource "google_compute_firewall" "allow_iap_ssh" {
  name    = "allow-iap-ssh-rule-${var.environment}"
  project = var.project_id
  network = var.vpc_network_id

  allow {
    protocol = "tcp"
    ports    = ["22"]
  }

  source_ranges = ["35.235.240.0/20"]

  # 対象のインスタンスだけに適用する
  target_tags = ["instance-tag-name"] 
}

VPCネットワークやインスタンスといったインフラ基盤のデプロイは順調に進みました。しかし、構築をさらに進めようとした段階で、403 Permission DeniedPolicy update access denied といったエラーが多発してしまいました。

【ここで学んだ教訓:インフラリソースとアクセス権限(IAM)の分離】
調査の結果、Google Cloudの権限管理における明確なルールが原因であることが分かりました。

  • 編集者(Editor)権限: VMインスタンスやVPC、ストレージバケットなどの「リソース本体」を作成することはできる。
  • IAM管理者権限: そのリソースに対して「誰がアクセスできるか」という「IAMポリシー(権限)」を付与する操作(setIamPolicy)を行うことができる。

私は「編集者」権限でTerraformを実行していたため、リソースの作成は成功するものの、その直後に走る「IAM権限の付与」に関する処理でシステムに拒絶されていたわけです。Google Cloudでは「リソースを作成する権限」と「アクセス権を付与する権限」が明確に分離されており、Terraformで自動化する際にもこの権限モデルがそのまま適用されます。
今回はエラーを回避するため、TerraformからIAM付与のコードを一旦外し、インフラリソースの構築のみを先行させる対応をとりました。

第3章: 接続検証で発生した「4033エラー」とIAM権限の適切な設定

インフラリソース自体の構築は完了したので、IAM権限が付与されていない現状で接続を試みるとどうなるのか、以下のコマンドでターミナルから接続検証を行ってみました。(--tunnel-through-iap フラグをつけることで、IAP経由でのセキュアな接続を強制します)

gcloud compute ssh [インスタンス名] \
--zone=asia-northeast1-a \
--tunnel-through-iap

しかし、無情にも次のようなエラーが表示されました。

ERROR: Error during local connection to [stdin]: Error while connecting [4033: 'not authorized'].

【エラーの原因と、Terraformでの正しいIAM設定】
この 4033 というエラーは、IAPのトンネルを通過するための許可(IAM権限)をユーザー自身が持っていないことを意味するIAP特有のエラーです。IAP経由で接続するためには、インスタンス側にサービスアカウントとして権限をつけるのではなく、「接続を実行するユーザー本人(Googleアカウント)」に対して、以下の2つのIAMロールが必要になります。

本来であれば、Terraformで以下のように記述して権限を付与します。

【実装すべきTerraformコード(IAM権限部分)】

# 1. IAP経由でのアクセス経路を許可するロール
resource "google_project_iam_member" "iap_tunnel_user" {
  project = var.project_id
  role    = "roles/iap.tunnelResourceAccessor"
  member  = "user:[実行ユーザーのメールアドレス]" # 実務では group:[グループアドレス] が推奨
}

# 2. OS LoginによるLinuxログインを許可するロール
resource "google_project_iam_member" "os_login_user" {
  project = var.project_id
  role    = "roles/compute.osLogin" # sudo権限が必要な場合は roles/compute.osAdminLogin
  member  = "user:[実行ユーザーのメールアドレス]" # 実務では group:[グループアドレス] が推奨
}

※補足: 実務上のベストプラクティスとしては、個別のユーザーアカウント(user:)を直接指定するのではなく、運用チーム用のGoogleグループ(group:)を作成し、そのグループに対してIAMロールを一括付与する運用が推奨されています。これにより、異動などによる権限管理がとても楽になります。

これらの適切なIAM権限を付与したことで、4033 エラーの原因となっていた権限の壁をクリアし、セキュアに接続するための準備が整いました。

第4章: セキュアなSSH接続の確立と、自動生成される一時鍵の裏側

再度 gcloud compute ssh コマンドを実行したところ、以下のプロセスを経て無事にログインに成功しました!

WARNING: The private SSH key file for gcloud does not exist.
WARNING: SSH keygen will be executed to generate a key.
Generating public/private rsa key pair.
# (中略)
Linux [インスタンス名] 6.1.0-43-cloud-amd64 ...
Creating directory '/home/[ユーザー名]'.
[ユーザー名]@[インスタンス名]:~$

【自動処理される認証の裏側】

初回接続時、ローカル環境にSSH鍵が存在しない場合、gcloud コマンドが自動的に鍵ペアを生成し、ローカルPCなどに保存します。

生成された公開鍵はGoogle Cloud上のOS Loginプロファイルに登録され、SSH接続時にインスタンス側から動的に参照されます。これにより、従来のように公開鍵をサーバーへ手動で配置する必要がなくなります。

また、この際に作成されるLinuxユーザーは、Googleアカウント情報をもとにした名前として自動的に作成されます。

【運用面でのリカバリーのしやすさ】

2回目以降の接続では、ローカルに保持された鍵が利用されるため、コマンドを実行するだけでシームレスにログインが完了します。

仮にローカルPCを移行したり、誤って秘密鍵を削除してしまった場合でも、再度コマンドを実行すれば新しい鍵ペアが自動生成され、公開鍵がGoogle Cloud側に再登録されます。そのため、従来のようにサーバーごとに鍵を再配置するといった作業は不要です。

従来のクラウド環境でありがちな「鍵ファイル(.pemなど)を紛失してインスタンスにアクセスできなくなる」といった状況と比較すると、運用上のリカバリーは格段に容易になっているのが素晴らしいポイントです。

まとめ

IAPとOS Loginを導入することで、外部にポートを公開することなく、安全にアクセスできる非常にセキュアで利便性の高い環境を構築することができました。

一方で、この構成をTerraformで自動化する過程において「IAM付与の権限」という制約に直面し、「インフラリソースの構築」と「アクセス権限(IAM)の管理」をシステムとして明確に分けて考えることの重要性を深く学びました。また、権限エラーに直面した際の的確なステータス把握は、クラウドインフラ構築において欠かせないスキルだと実感しました。

セキュリティ要件の強化と運用効率の向上を同時に実現できるベストプラクティスですので、ぜひみなさんのプロジェクトでも導入を検討してみてください!