【OCI】Bastionサービスの概要と使い方

この記事は，Oracle Cloud Infrastructure（以下OCI）の踏み台サービス，Bastionについての解説になります。

# 0. 概要

OCI Bastionは，プライベートIPのみを持つインスタンスへSSH接続を提供する無料のサービスになります。
指定されたIPアドレスからのみ，一定の制約と制限時間の下でターゲットリソース (コンピュートインスタンス，Base DB システム，Autonomous Databaseを含む) への SSH 接続が可能になります。
またSSH トンネリングにより，リモートデスクトップ (RDP) などの他のプロトコルも利用可能です。


OCIドキュメントでは「要塞」と訳されており，以下のドキュメントから詳細が見られます。

https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Concepts/bastionoverview.htm





# 1. Bastionの詳細

接続のためにコンソールで行う設定としては，Bastionを特定のサブネットに作成した後，対象インスタンスを指定してセッションを作成するという流れになります。


## 1.1. 必要となるIAMポリシー
OCIのIAMポリシーでは，対象を表すリソースタイプに個々のリソースタイプと，その集合ファミリ・リソースタイプがあります。詳しくは[コア・サービスの詳細](https://docs.oracle.com/ja-jp/iaas/Content/Identity/Reference/corepolicyreference.htm#resource-types)，[ポリシー・リファレンス](https://docs.oracle.com/ja-jp/iaas/Content/Identity/Reference/policyreference.htm#Resource)を御覧ください。

Bastionのリソースタイプは下のような構成になっています。
```
basion-family
├- bastion
└- bastion-session
```

そのため，次のように一括で許可してもいいですし，
```
Allow group <group_name> to manage bastion-family in tenancy
```
下のように個別で細かく許可してもいいでしょう。

```
Allow group <group_name> to <verb> bastion in tenancy
Allow group <group_name> to <verb> bastion-session in tenancy
```


リソースタイプbastion/sessionとverbの組み合わせでどこまで権限が許可できるかは，[要塞のIAMポリシー](https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Reference/bastionpolicyreference.htm#Details)を参照ください。


:::note info
上記はBastionについてのポリシーのみですが，Bastionすべての操作のためには以下の権限がまとめて必要となります。

- 要塞，セッションおよびネットワーク：manage権限
- コンピュート・インスタンス：read以上の権限
- コンピュート・インスタンス・エージェント(Oracle Cloud Agent)・プラグイン：read以上の権限
- 作業リクエスト：inspect以上の権限
:::



## 1.2. セッションタイプ

ターゲットとなるインスタンス，並びに接続の仕方によって，以下の3つのうちからセッションタイプを選ぶ必要があります。
1. **管理対象SSHセッション**
1. **SSHポート転送セッション**
1. **動的ポート転送(SOCKS5)セッション**

セッションを作成する際には上の2つを選択することになるため，ここでは1. 2.のセッションタイプについて言及します。


### 管理対象SSHセッション
対象となるインスタンスへのSSH接続を中継します。

使用するための前提条件として，以下を満たす必要があります。
- Linuxインスタンスである
- インスタンス上でOpenSSHサーバーが実行されている
- インスタンス上でOracle Cloud Agentが実行されている
- Oracle Cloud Agentで，要塞プラグインが有効化されている

特に最後の要塞プラグインはインスタンス作成時のデフォルトでは有効化されていないので注意しておきます。

Bastionセッション作成時に登録した鍵でSSH接続できますので，権限が足りていれば対象インスタンスの秘密鍵を忘れてしまった場合などに接続する手段としても使用できると思います。

また管理対象SSHセッションでは，Bastionに渡した公開鍵がAgentによって対象インスタンスの`.ssh/Authorized_key`に登録されます。

### SSHポート転送セッション
SSHポート転送（SSHトンネリング）を作成し，対象インスタンスと接続します。
トンネリングにより，RDPやOracle Net Services，MySQLなど他のプロトコルを使用することができます。SSH通信にて暗号化されるため，非暗号化プロトコルの通信も安全に行えます。

こちらは特に前提条件はなく，上記の「管理対象SSHセッション」の前提条件であったOpenSSHやOracle Cloud Agentの実行は不要となります。



## 1.3. 構成

[こちらのページ](https://docs.oracle.com/en/solutions/use-bastion-service/index.html#GUID-E30EE335-0EFD-4212-BD47-171203160723)により詳細な構成図が紹介されています。

![bastion_architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/caa78c18-57b6-a201-6c3e-2de391bcfd1d.png)

Bastionを作成する際に指定するサブネットがインスタンスと同じサブネットか独立したサブネットか，つまりプライベートエンドポイントをどこに置くかで2通りの利用が記載されています。

対象インスタンスとは別のサブネットにBasionを作成した場合，通信に関わる全セキュリティリストにて制御する必要があります。
一方，インスタンスと同じサブネットにBastionを作成した場合はセキュリティリストの設定は必要ありません。
この2つの違いはBastionの使用目的や運用方針によって変わってくるでしょう。Bastionが悪用された際の影響範囲にも注意したいところです。

さて，このアーキテクチャ図からわかる限り，Bastion接続に関わる登場人物は主に
- User (Operator)
- Bastion (Bastion Service, Bastion Service Backend)
- Session (Private Endpoint)
- VM
- Service Gateway

の5つです。（括弧の中は上図内の表記）

急にService Gateway（以下SGW）が出てきましたが，SGWはOracle Service Network（以下OSN）との通信をするためのものです。OSNとSGWについては下のリンクを参考にしてください。

https://docs.oracle.com/ja-jp/iaas/Content/Network/Tasks/servicegateway.htm

SGWがなければOSNと通信できない，というわけではありません。OSNはインターネットからもアクセスできますので，代わりにNAT GatewayまたはInternet GatewayがVCNにあれば問題ありません。

また少し細かい話ですが，BastionはOracleマネージドなサービスになるため，構成図は正しく書くと次のようではなく，
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/0d9b8a0f-b6d2-2c59-92a9-23e99423f3e6.png)

以下のようになります。（どこまでの粒度で作成するかにも依りますが）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/f1ff0a9f-bccc-f73f-a78b-d59e0edaefc3.png)


# 2. Bastionをつかって接続する
## 2.1. Bastionの作成
コンソール左上のメニューより，[アイデンティティとセキュリティ]>[要塞]を選択します。

[要塞の作成]を選択し，必要項目を入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/605caa1a-e493-443c-9d32-cfda44773de1.png)


Bastion名（要塞名）は英数字のみのようです。


この際設定するサブネットが，セッションが設置されるサブネットになります。
拡張オプションを展開すると現れる[最大セッション持続時間(TTL)]はセッションの制限時間になります。各セッションにおける制限時間はセッション作成時にて別に設定することができます。

なお，Bastionを作成した時点でIPアドレスが2つ確保されます。

```
{
  "data": [
    {
      "availability-domain": null,
      "compartment-id": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxx",
      "defined-tags": {},
      "display-name": "ReverseConnectionIp",
      "freeform-tags": {},
      "hostname-label": null,
      "id": "ocid1.privateip.oc1.iad.aaaaaaaaaaaaaa",
      "ip-address": "10.0.1.163",
      "ip-state": "ASSIGNED",
      "is-primary": false,
      "lifetime": "EPHEMERAL",
      "route-table-id": null,
      "subnet-id": "ocid1.subnet.oc1.iad.yyyyyyyyyyyyyyy",
      "time-created": "2025-03-24T02:17:07.455000+00:00",
      "vlan-id": null,
      "vnic-id": "ocid1.vnic.oc1.iad.zzzzzzzzzzzzzzzzzz"
    },
    {
      "availability-domain": "LPNJ:US-ASHBURN-AD-3",
      "compartment-id": "ocid1.compartment.oc1..xxxxxxxxxxxxxxxx",
      "defined-tags": {},
      "display-name": "ocid1.bastion.oc1.iad.vvvvvvvvvvvvvvv",
      "freeform-tags": {},
      "hostname-label": "host-10-0-1-165",
      "id": "ocid1.privateip.oc1.iad.bbbbbbbbbbbbbbbb",
      "ip-address": "10.0.1.165",
      "ip-state": "ASSIGNED",
      "is-primary": true,
      "lifetime": "EPHEMERAL",
      "route-table-id": null,
      "subnet-id": "ocid1.subnet.oc1.iad.yyyyyyyyyyyyyyy",
      "time-created": "2025-03-24T02:16:11.360000+00:00",
      "vlan-id": null,
      "vnic-id": "ocid1.vnic.oc1.iad.zzzzzzzzzzzzzzzzzz"
    }
  ]
}
```

同じVNICに対して2つのプライベートIPアドレスが割り当てられているのが確認できます。
`ocid1.bastion.oc1.iad.~`の方がコンソールにも表示されるBastion本体のプライマリIP。一方，リバースプロキシとして`ReverseConnectionIp`を確保しているのがわかります。

## 2.2. セッションの作成

次はセッションを作成します。
こちらでは前述の通り，セッションタイプ「管理対象SSHセッション」「SSHポート転送セッション」の2つについて記載します。


### 管理対象SSHセッション
[セッションの作成]を選択，セッションタイプを「管理対象SSHセッション」にした後，項目を入力します。SSHキーは対象インスタンスのものでも構いませんが，セキュリティ対策としてbastion用に別のキーを使用してもいいでしょう。

![createing_managed_session.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/9b6c6ced-daba-9cca-6378-d5018d3ac8ab.png)


セッションが作成されると右のケバブメニューより， [SSHコマンドのコピー]ができます。\<privateKey\>が2箇所ありますので，bastionに渡したSSHキーのパスに置き換えた後，コマンドラインにペーストしてSSH接続します。


![copy_managed_session_ssh_cmd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/dfee6dcb-6411-3a3d-217f-21ef7164e174.png)


（なおコマンドの内容からもわかることですが，前半の\<privateKey\>を対象インスタンスキーのパス，後半の<privateKey>をBastionキーのパスにしても問題なく接続できます。）

```
ssh -i <privateKey> -o ProxyCommand="ssh -i <privateKey> -W %h:%p -p 22 ocid1.bastionsession.oc1.iad.xxxxxxxxxx@host.bastion.us-ashburn-1.oci.oraclecloud.com" -p 22 opc@10.0.1.115
```

接続した後，`.ssh/authorized_keys`を確認すると，セッション作成時Bastionに渡したSSHキーが（Agentによって）登録されていることが確認できます。
（インスタンスの公開鍵を使用しても，再度同じ鍵が`authorized_keys`に登録されます）


```
[opc@test-instance ~]$ cat .ssh/authorized_keys
...

#ocid1.bastionsession.oc1.iad.xxxxxxxxxx
ssh-rsa
r8/SkoJhAoGBAOhkCOzqY+7gs50EUNrKI617uwksqmia/zN9eDeqn6G67B
M0FLDTYSC+hDBYh2UoUv33SUbXCdpJCIoA7e/6wBz1kHn4/sAbVrRBAGY7
MErzn+t85KtvO5oKocs3ClVnMGIFvv ssh-key-bastion
```

:::note info
登録されるタイミングはセッション作成時です。OSN経由でセッションに関するメタデータがやりとりされていると思われます。
そのため，セッション作成時はOSNにつながっている必要があるわけです。

<details>
    <summary>
    セッション作成後，SGWのルートルールを削除したとき
    </summary>
    
---

セッションを作成した後，SGWへのルートルールを削除すると（大体1分ほどで）authorized_keysに追加された鍵は削除されます。SSH接続は[1.3.](#13-構成)の構成図を用いると[Bastion Service]→[Private Endopoint]とアクセスされるため，すでに張られているSSH接続自体が切れることはありません。
しかしauthorized_keysの内容はリセットされるため，SSH接続の際，2つの\<privateKey\>にBastionの鍵パスを使用しての接続は不可能になります。プライベートエンドポイントにはBastionの鍵で接続できますので，インスタンスとbastionの鍵パスの2つを指定したコマンドでのSSH接続をする分には問題ありません。ルートルールを再び追加すると再び鍵は追加されますが，即時反映ではないようです。（10分ほどかかったのを確認しています）

もちろん再度セッションを作成する際は設定を元に戻す必要があります。

---
</details>

:::



### SSHポート転送セッション

[セッションの作成]を選択，セッションタイプを「SSHポート転送セッション」にした後，項目を入力します。SSHキーはBastion固有のもので問題ありません。

おそらく管理対象SSHセッションよりもこちらの方を多用されると思います。


![creating_port_forwarding_session.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761799/310f2413-4679-22a0-e388-5692c5c5131b.png)

右のケバブメニューより[SSHコマンドのコピー]ができます。`<privateKey>`を秘密鍵のパス，`<localPort>`にポート番号を入れ，コマンドラインに入力します。
`<localPort>`は使用可能なロカールポートであれば，どれでも問題ありません。
```
ssh -i <privateKey> -N -L <localPort>:10.0.1.115:22 -p 22 ocid1.bastionsession.oc1.iad.xxxxxxxxxx@host.bastion.us-ashburn-1.oci.oraclecloud.com
```


上記のコマンドに加え，正常に接続ができているかの確認として`-v`オプションをつけておきます。[ドキュメントにも記載がある通り](https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Tasks/connect-port-forwarding.htm#connect_to_rdp)，`-vv`，`-vvv`オプションはセキュリティ上推奨されていないようです。

また，
> 詳細出力(-v)を有効にした場合，接続に成功した後の最終メッセージは次のようになります:
>`debug1: pledge: network`

とありますが，こちらは`debug1: pledge: filesystem`でも問題ありません。

先程のコマンドにてSSHトンネルを作成した後，新たにターミナルを立ち上げ，以下のコマンドを入力します。`<privateKey>`は対象SSHインスタンスの鍵のパス，`<localPort>`は先程の指定したポート番号と同じものです。
```
ssh -i <privateKey> -p <localPort> <user>@localhost
```

あとは以上のコマンドで，対象インスタンスへのSSH接続が完了します。

<br>

今回はSSHサーバへの接続を行いましたが，ドキュメントの方には以下のシナリオの接続方法が記載されています。その他の接続方法についてはドキュメントを参照ください。

> - コンピュート・インスタンスのSSHサーバーへの接続
> - Remote Desktop Protocol (RDP)を使用した Windowsへの接続
> - RDPおよびPuTTYを使用したWindowsへの接続
> - Autonomous Database for Transaction Processingおよび混合ワークロード・データベースへの接続
> - MySQL DB Systemへの接続


https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Tasks/connect-port-forwarding.htm#connect_to_rdp

# 3. 想定されるトラブルシューティング
さて最後になりましたが，想定されるトラブルとその解決法を書いていきます。解決に決して漏れがないわけではないため，ご了承ください。
また，以下ドキュメントにも詳しく書いてありますのでそちらも参考にしてみてください。

https://docs.oracle.com/ja-jp/iaas/Content/Bastion/Tasks/troubleshooting.htm

## 3.1. Bastionが作成できない
以下のエラー文が出る場合です。
```
You have already reached max quota for number of bastions that can be created under the tenancy.
```
1リージョンに作成できるBastionは5つまでです。また，セッションは1つのBastionあたり20セッションまでです。（参考：[Service Limits](https://docs.oracle.com/en-us/iaas/Content/General/Concepts/servicelimits.htm#Bastion_Limits)）
複数リージョンを登録しており，検証環境に拘りがなければリージョンを変えるのも手でしょう。

## 3.2. Bastionセッションが作成できない
主に管理対象SSHセッションについてですが，以下のエラー文が出る場合を想定します。
```
To create a Managed SSH session, the Bastion plugin must be enabled on the target instance, but the plugin is disabled on ocid1.instance.oc1.<region>.xxxxxxxxxx.
Enable the Bastion plugin on the target instance before creating the session.
````
エラー文通り，Oracle Cloudエージェントの「要塞」プラグインが有効化されていることを確認してください。

有効が反映されるには数分ほどかかる場合もあります。また，有効・無効を何度か切り替えることで解決することがあるようです。

なお有効化しても，`Plugin Bastion not present for instance ocid1.instance.oc1.<region>.xxxxxxxxxx`と出る場合，または対象インスタンスのメトリックが取得できていない場合はOSNへ接続ができていないことが原因です。Service GatewayまたはNAT Gatewayが作成されているか，サブネットのルートルールが正しく設定されているかを確認してください。



# 4. 関連サイト

この記事にちょっと関連するサイトをならべます。

- 管理対象SSHセッションとSSHポート転送セッションにまとめているブログです。

https://www.ateam-oracle.com/post/managed-ssh-session-vs-ssh-port-forwarding-session



-  Bastionを含め，OCI Linuxインスタンスへのアクセスをまとめた記事になります。

https://docs.oracle.com/ja/learn/access-oci-linux-instance/index.html