GCE への gcloud compute ssh 接続

## はじめに
gcloud でインスタンスへの ssh を実施するには、インスタンスの Public IP 有無だったり、SSH 設定の違いだったりがある
VPN で接続した環境から Private IP を使用した直接続で、詰まったり、動作がよくわからなかったので、Network 環境などの違いでの SSH 接続の違いなど調べた内容をまとめて記載する

## パターンによる違い
下図は、インスタンスの Public IP 有無や、クライアントによる Private IP への直接疎通可能な環境によっての違いをまとめてみた

![スクリーンショット 2021-05-30 22.18.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/dd05e166-b6e3-2fa8-53ce-eff1088da486.png)

SSH の鍵については、「OS Login」と「メタデータでの管理」がある

![スクリーンショット 2021-05-30 23.04.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/dadbda90-2ce4-2e72-dd2c-9ff1f945fa12.png)


- OS Login
    - ユーザ名
        - google アカウント名に基づいたもので、詳細は profile の username (loginProfile:posixAccounts:username)
        - (アカウントによる。「@」と「.」を「_」 に置き換えたものなど)
        - (Service Account なら sa_[Service Accout の Unique ID]
    - SSH Keys
        - 設定不要
        - 3rd Party ツールを使用したい場合は、設定可能
        - gcloud compute os-login ssh-keys add --key-file=KEY_FILE_PATH --ttl=EXPIRE_TIME
        - これをすれば ssh -i [Google アカウント名]@[IP] で接続可能
    - 権限
        - IAM で管理される
            - roles/compute.osLogin : sudo 不可
            - roles/compute.osAdminLogin : sudo 可
- メタデータでの鍵管理
    - ユーザ名
        - SSH Key のユーザ名
            - id_rsa.pub の format: <protocol> <key-blob> <username@example.com> などで書かれているユーザ名
            - gcloud compute ssh 時に[username]@[INSTANCE_NAME] で username を指定も可能
            - ユーザ名が任意に決められる(決められてしまう. なりすませる)
        - SSH Keys
            - メタデータに登録されたものを使用
            - gcloud compute ssh 初回接続時に作成され公開鍵をメタデータに登録もされる
            - 設定した SSH Key を使用して 3rd Party の SSH を利用可能
        - 権限
            - sudo 可

以降、`--internal-ip`オプションと、OS Login 設定を実施したので、試した内容を記載する

## 実施
実際にやった内容を記載する

### Private IP to Private IP での接続(VPN or Interconnect で Private IP 間で直接疎通可能な場合)

**--internal-ip**オプションを付ける [[参考](https://cloud.google.com/compute/docs/instances/connecting-advanced#vpn)]

```
gcloud compute ssh INTERNAL_INSTANCE_NAME \
    --zone=ZONE \
    --internal-ip \
    --projects PROJECT_NAME \
```

初回は鍵を生成してメタデータへの登録を実施してくれ、インスタンスの authorized_key へ設定をしてくれる (今回対象インスタンスは OS Login 有効化してない)
実施後は、作成した鍵(`~/.ssh/`)を使用して gcloud 無しでログイン可能


```txt:実施例
% gcloud compute instances list --project suzuyu-service1-dev
NAME                     ZONE        MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP   STATUS
internal-bastion-sv      us-west1-b  f1-micro                   172.18.0.10                RUNNING
% gcloud compute ssh internal-bastion-sv --zone us-west1-b --project suzuyu-service1-dev --internal-ip
WARNING: The private SSH key file for gcloud does not exist.
WARNING: The public SSH key file for gcloud does not exist.
WARNING: You do not have an SSH key for gcloud.
WARNING: SSH keygen will be executed to generate a key.
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/suzuyu/.ssh/google_compute_engine.
Your public key has been saved in /home/suzuyu/.ssh/google_compute_engine.pub.
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx suzuyu@onpresv01
The key's randomart image is:
+---[RSA 3072]----+
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
|                 |
+----[SHA256]-----+
Updating project ssh metadata...⠏Updated [https://www.googleapis.com/compute/v1/projects/suzuyu-service1-dev].                                                                        
Updating project ssh metadata...done.                                                                                                                                                   
Waiting for SSH key to propagate.
Warning: Permanently added 'compute.XXXXXXXXXXXXXXXXXXX' (ECDSA) to the list of known hosts.
[suzuyu@internal-bastion-sv ~]$
```

自動で、`~/.ssh/`配下に GCE 向けの鍵が作成されていることを確認できる

```diff_shell
% ls -1 .ssh/
authorized_keys
+ google_compute_engine
+ google_compute_engine.pub
+ google_compute_known_hosts
id_rsa
id_rsa.pub
known_hosts
```

作成された鍵を使用して直接 SSH が可能になったことを確認できる

```
ssh -i ~/.ssh/google_compute_engine 172.18.0.10
```

#### エラー例 (--internal-ip付けない・IAP Tunnel が利用できない環境/設定)
下記のようなエラーが出る。デフォルトは IAM Tunnel を使用しようとするため。

```sh:internal-ipをつけないとエラー
$ gcloud compute ssh compute ssh INTERNAL_INSTANCE_NAME --zone=ZONE 
~ 中略 ~
External IP address was not found; defaulting to using IAP tunneling.
~ 中略 ~
Waiting for SSH key to propagate.
ERROR: (gcloud.compute.start-iap-tunnel) Error while connecting [4003: 'failed to connect to backend']. (Failed to connect to port 22)
kex_exchange_identification: Connection closed by remote host
ERROR: (gcloud.compute.ssh) [/usr/bin/ssh] exited with return code [255].
```

### SSH 鍵管理方法
「パターンによる違い」に書いたとおり、SSH の鍵については、「OS Login」と「メタデータでの管理」がある
メタデータでの管理についてはデフォルトで、GCE を作成したことがあれば通る道でもあると思われるし、「Private IP to Private IP」でも軽く動作確認してるので、
ここでは OS Login について動作を確認する

**OS Login とメタデータでの管理は両立しない**ので、既存のインスタンスへ設定したり、プロジェクトへの有効化をする際は要注意

#### OS Login 設定
詳細は[ドキュメント](https://cloud.google.com/compute/docs/instances/managing-instance-access#gcloud)を参照

ここでは、3ステップで実施している


1. OS Login API の有効化
2. インスタンスで OS Login 機能を有効 (メタデータ `enable-oslogin = "TRUE"` を追加)
3. 必要な IAM のロールを付与


##### 1. OS Login API の有効化

OS Login の API を有効化する

```sh:gcloudで実施の場合
gcloud services enable oslogin.googleapis.com
```

```diff_terraform:terraform例(抜粋)
resource "google_project_service" "oslogin_api_enable" {
~中略~
+  service = "oslogin.googleapis.com"
}
```

##### 2. インスタンスで OS Login 機能を有効 (メタデータ `enable-oslogin = "TRUE"` を追加)
OS Login はプロジェクト・インスタンス単位で設定可能 [詳細参照[ドキュメント](https://cloud.google.com/compute/docs/instances/managing-instance-access#enable_oslogin)]
ここではインスタンス単位で設定する

```sh:gcloudで実施の場合
gcloud compute instances add-metadata VM_NAME \
    --metadata enable-oslogin=TRUE
```

```diff_terraform:terraform例(抜粋)
resource "google_compute_instance" "internal-bastion-sv" {
~中略~
+  metadata = {
+    enable-oslogin = "TRUE"
+  }
}

```

##### 3. 必要な IAM のロールを付与
[ドキュメント](https://cloud.google.com/compute/docs/instances/managing-instance-access#grant-iam-roles)

必要に応じて下記ロールを付与する

- roles/compute.osLogin（管理者の権限は付与されない）
- roles/compute.osAdminLogin（管理者の権限は付与される）

ただし、ユーザーが基本の roles/owner ロールまたはroles/editor ロールを持っている場合は、管理者の権限を持った状態なので不要

```terraform:プロジェクトservice1で権限付与する場合(role以外の変数などは環境によって適宜読み替えてください)
resource "google_project_iam_binding" "osAdminLogin" {
  project = google_project.service1.id
  role = roles/compute.osAdminLogin
  members = [
    "user:${var.service1_pj_admin_user_email}"
  ]
}
```

```sh:gcloudでの設定例
gcloud projects add-iam-policy-binding [PROJECT_ID] \
  --member user:[USER_EMAIL] \
  --role roles/compute.osAdminLogin
```

以上で、OS Login 有効化と権限付与完了
環境によって必要な設定があるので、詳細は最初に述べたドキュメント参照

#### OS Login での SSH 鍵登録
OS Login は gcloud で ssh する場合は鍵登録は不要
3rd Party ツール (sshコマンド) などで実施したい場合は登録をする
実際の登録設定は下記の通り

##### terraform での登録
[ドキュメント](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/os_login_ssh_public_key)

```terraform
resource "google_os_login_ssh_public_key" "cache" {
  user =  data.google_client_openid_userinfo.me.email
  key = file("path/to/id_rsa.pub")
}
```

##### gcloud での登録
gcloud 登録では実施出力例と合わせて下記に記載する
実際に ssh コマンドでログインするには出力される loginProfile の **username:** が使用するユーザ名になる

```txt:gcloudでの設定例(一部マスク.省略.usernameがログインに使用するユーザ名)
% gcloud compute os-login ssh-keys add --key-file=/home/suzuyu/.ssh/id_rsa.pub --ttl 1d
loginProfile:
  name: 'xxxxxxxxxxxxxxxxxxxxx'
  posixAccounts:
  - accountId: suzuyu-service1-dev
    gid: 'xxxxxxxxxx'
    homeDirectory: /home/sa_xxxxxxxxxxxxxxxxxxxxx
    name: users/[serviceAccountName]@[project_id].iam.gserviceaccount.com/projects/[project_name]
    operatingSystemType: LINUX
    primary: true
    uid: 'xxxxxxxxxx'
    username: sa_xxxxxxxxxxxxxxxxxxxxx
  sshPublicKeys:
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:
      expirationTimeUsec: '1622462917423494'
      fingerprint: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
      key: |
        ssh-rsa AAAABxxxxxxxxxxx xxxxxx on xxxxx
~中略~
% ssh sa_xxxxxxxxxxxxxxxxxxxxx@172.18.0.10
Last login: Sun May 30 12:23:44 2021 from 192.168.129.13
[sa_xxxxxxxxxxxxxxxxxxxxx@internal-bastion-sv ~]$
```

上記の通り、OS Login での鍵登録でもログインできることを確認できた

また、メタデータで登録していた鍵が利用できないことも確認できる

```sh:メタデータでのssh鍵はログインできなくなる(OS_Login有効化時)
% ssh -i ~/.ssh/google_compute_engine 172.18.0.10
suzuyu@172.18.0.10: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
```

## まとめ
今回は gcloud compute ssh で実施する際のパターン別の動作概要と、Private IP のみでの接続での `--internal-ip` での動作、OS Login について軽く実践してみた
SSH 鍵管理については OS Login は Google アカウントでユーザと権限の管理が可能で、推奨されているので利用できる環境であれば良いと感じた
IAP Tunnel については試せてないので、別途試していきたい


## 参考

https://cloud.google.com/sdk/gcloud/reference/compute/ssh#--internal-ip

https://cloud.google.com/compute/docs/instances/connecting-advanced#sshbetweeninstances

https://cloud.google.com/compute/docs/instances/access-overview

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/os_login_ssh_public_key

https://cloud.google.com/compute/docs/instances/managing-instance-access#gcloud

https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys