OCI Bastionセッションを作成してターミナルソフト(TeraTerm)でSSH接続するまでを PowerShell でスクリプト化する。(Oracle Cloud Infrastructure)

前回からの続きです。OCI Bastion(踏み台)経由での接続のスクリプト化について
ガチャガチャと検証していたので、まとめておきますやで彡(ﾟ)(ﾟ)

> OCI Bastionサービス で Private Compute に SSH接続してみる。(Oracle Cloud Infrastructure)
> https://qiita.com/ora_gonsuke777/items/9a4d7e4a933e6d850ccd

# 1. 目的(やりたい事はスクリプト化)

OCI Bastion はいわゆる踏み台のサービスで OCI の Subnet に存在するリソースへの
アクセスを提供します。AWS で云うと Session Manager に相当するサービスになります。

* (1). 専用の踏み台サーバーを作成する必要が無くコスト安(Bastionサービスは無料)
* (2). Bastionセッションの作成権限を付与/剥奪することで、IAM によるアクセス制御が可能  
* (3). Bastionセッション は時間制限があるものを作成する必要が有り、セキュリティ向上に寄与

ところが (3). の Bastionセッション をコンソールで都度作成するのがぼちぼち面倒です。
Private Subnet にあるリソースに接続する度に OCI Console にアクセスして、
Bastionセッションをいちいち作成するのは辛い……彡(-)(-)

という訳で、一連の作業をスクリプト化して一発実行で接続できるようにしてみます。

# 2. 前提条件／事前準備

* 端末(PC) に OCI CLI をインストールしておいて下さい。
* OCI CLI をセットアップして 端末(PC) で実行可能な状態にしておいて下さい。
* OCI CLI のセットアップ時に指定した IAMユーザー に Bastionセッション の作成権限を付与して下さい。
* Bastionサービス および Compute は 予め作成して OCID を控えておいて下さい。
* Bastionセッション作成時 および アクセス時に必要な SSHキーペア(公開鍵/秘密鍵) は予め作成しておいて下さい。
* ~~Compute の Bastion Plug-in は有効化しておいて下さい。前回の記事を参照~~ Port forwarding の場合は不要でした。

以下の環境で検証しています。

* Windows 10
* PowerShell (PSVersion 5.1.19041.1237)
* OCI Compute(VM.StandardE2)
* Oracle Linux 7.9 on Compute
* OCI Bastion Service

# 3. OCI CLI による Bastionセッション作成

OCI CLI の oci bastion session create-port-forwarding でポートフォワードの Bastionセッション を作成します。

```shell
oci bastion session create-port-forwarding 
--bastion-id <bastion_ocid> 
--ssh-public-key-file <bastion_public_key> 
--target-port <target_port> 
--target-resource-id <target_ocid> 
--session-ttl <session_ttl_sec> 
--wait-for-state SUCCEEDED
```

パラメータには以下の値を設定します。

| パラメータ名 | 設定する値 |
|---|---|
| bastion-id | Bastionサービス の OCID を指定します。 |
| ssh-public-key-file | Bastionセッションで使用する SSHキーペア の公開鍵をフルパスで指定します。 |
| target-port | 接続先リソースのポートを指定します。今回は SSH接続 するので 22番(SSH) を指定します。 |
| target-resource-ID | 接続先リソース(今回は Compute) の OCID を指定します。 |
| session-ttl | Bastionセッションの生存時間を秒で指定します。 |
| wait-for-state | 作成するBastionセッションが指定したステータスに遷移するまで待機します。 |

実行サンプルは以下の通りです。複数行で記述していますが実際は一行で実行します。

```shell
oci bastion session create-port-forwarding 
--bastion-id ocid1.bastion.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
--ssh-public-key-file C:\xxxxxxxxxxxxxxxxxxxx\xxxxxxxxxxxxxxxxxxxxxx.pub 
--target-port 22
--target-resource-id ocid1.instance.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 
--session-ttl 10800
--wait-for-state SUCCEEDED

{
  "data": {
   ：
    "resources": [
      {
        ：
        "identifier": "ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    ],
    "status": "SUCCEEDED",
        ：
  }
}
```

identifier は作成された Bastionセッション の OCID ですが、直後に使うので控えておきます。

# 4. OCI CLI で Bastionセッション の SSH接続文字列 を 取得

OCI CLI の oci bastion session get で Bastionセッション の詳細情報を出力します。

パラメータには以下の値を設定します。

```shell
oci bastion session get --session-id <session_ocid>
```

| パラメータ名 | 設定する値 |
|---|---|
| session_id | 上記3. で作成した Bastionセッション の OCID を指定します。 |

実行サンプルは以下の通りです。ssh-metadata にポートフォワード用SSH接続のコマンドが格納されています。

```shell
oci bastion session get --session-id ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxx
{
  "data": {
   ：
    "session-ttl-in-seconds": 10800,
    "ssh-metadata": {
      "command": "ssh -i <privateKey> -N -L <localPort>:xx.xx.xx.xx:22 -p 22 ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxx@host.bastion.us-phoenix-1.oci.oraclecloud.com"
    },
   ：
}

```

# 5. SSHコマンドを編集してポートフォワード用のSSH接続を確立

ssh-metadata を編集してポートフォワード用のSSH接続のコマンドを作成します。
ローカルポートは何でも良いのですが、今回は 2222 を指定します。

```shell
ssh -i C:\xxxxxxxxxxxxxxxxxxxxxxx\bastion_private.key 
-N -L 2222:xx.xx.xx.xx:22 
-p 22 ocid1.bastionsession.oc1.phx.xxxxxxxxxxxxxx@host.bastion.us-phoenix-1.oci.oraclecloud.com
```

編集したコマンドを PowerShell で実行すると、ポートフォワード用の SSH接続 が確立した状態になります。

# 6. TeraTerm等のターミナルソフトでSSH接続

Compute に接続します。SSHポートフォワードが確立した状態なので、ホスト名は localhost、
ポート番号は上記 5. で指定したポート番号(今回は 2222)を指定します。

秘密鍵は Compute作成時 の SSHキーペア の秘密鍵の方を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110066/da58b2d8-ac14-a590-ac3d-4bb38ec72b2c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110066/c7cbff6e-593d-c594-74f6-575ca400f799.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110066/0f86ec3d-d485-f4f6-b36f-df867105aacf.png)

無事接続できました。

# 7. 上記 3. ～ 6. を PowerShell でスクリプト化する。

上記 3. ～ 6. をスクリプト化します。今回は Windows PC なので
PowerShell と OCI CLI でスクリプト化してみました。

```powershell
############################################################
#
# Usage: Edit the Init Parameters and exec on PowerShell.
#
############################################################

# Init Parameters
$env:bastion_ocid = "ocid1.bastion.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$env:bastion_ocid
$env:target_ocid = "ocid1.instance.oc1.phx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
$env:target_ocid
$env:session_ttl_sec = "10800"
$env:session_ttl_sec
$env:bastion_public_key = "C:\xxxxxxxxxxxxxxxxxxxxxxx\bastion_public_key.pub"
$env:bastion_public_key
$env:bastion_private_key = "C:\xxxxxxxxxxxxxxxxxxxxxxx\bastion_private.key"
$env:bastion_private_key
$env:compute_private_key = "C:\xxxxxxxxxxxxxxxxxxxxxxx\compute_private_key.pem"
$env:compute_private_key
$env:target_port = "22"
$env:target_port
$env:local_port = "2222"
$env:local_port

# Create Bastion Session
$create_session_json = Invoke-Expression "oci bastion session create-port-forwarding --bastion-id $env:bastion_ocid --ssh-public-key-file $env:bastion_public_key --target-port $env:target_port --target-resource-id $env:target_ocid --session-ttl $env:session_ttl_sec --wait-for-state SUCCEEDED"
$create_session_json
$formatted_create_session = $create_session_json | ConvertFrom-Json
$formatted_create_session
$env:session_ocid = $formatted_create_session.data.resources.identifier
$env:session_ocid

# Get Port-forward SSH Command
$get_session_json = Invoke-Expression "oci bastion session get --session-id $env:session_ocid"
$get_session_json
$formatted_get_session = $get_session_json | ConvertFrom-Json
$formatted_get_session
$env:ssh_pre_command = $formatted_get_session.data.'ssh-metadata'.command
$env:ssh_command = Invoke-Expression '$env:ssh_pre_command -replace "<privateKey>", $env:bastion_private_key -replace "<localPort>", $env:local_port'
$env:ssh_command

# Connect Port-forward SSH 
# Invoke-Expression $env:ssh_command
$job = Start-Job {Invoke-Expression $env:ssh_command}
$job

# Connect Private-Compute by TeraTerm via Bastion Port-forward.
$env:Terminal_Parameter = "opc@localhost:$env:local_port /ssh /auth=publickey /keyfile=$env:compute_private_key /noproxy"
$env:Terminal_Parameter
$env:Terminal_Command = "`"C:\Program Files (x86)\teraterm\ttermpro.exe`" opc@localhost:$env:local_port /ssh /auth=publickey /keyfile=$env:compute_private_key /noproxy"
$env:Terminal_Command
Invoke-Expression "&$env:Terminal_Command"

# Wait-Job for Port-forward SSH Session
Wait-Job $job
```

OCID や SSHキーペアのパスを編集して PowerShell として実行すると……

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110066/41966c3a-047e-843a-16db-4ca2e68e8158.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110066/9b7cad1e-13f6-8929-55aa-cc4e6a060b55.png)

上手く行きました。スクリプト一発実行で接続できるようになったで！彡(^)(^)

# 8. 改善ポイント

oci bastion session create-port-forwarding と oci bastion session get で
少ーし待たされるので、ローカルで Bastionセッション のSSHコマンドと有効時間を保持しておいて、
Bastionセッションが生きている間はそちらを使い廻した方が利便性が高まると思いますた。

難易度もそれほど高くないし、今後の改善ポイントでしょうか。
create～ の 返り値JSON に SSHコマンドも含まれてると本当はエエんですけど(ﾟεﾟ )

# 9. まとめ

OCI Bastionセッション経由の SSH接続をスクリプト化できたで！
ちなみに今回 PowerShell のスクリプト化がほぼ初挑戦だったのですが、
ちょっとクセが有りますけど色々できますのね……。彡(ﾟ)(ﾟ)