OCI IAMとAutonomous Databaseのトークン・ベースの認証連携

Autonomous Databaseは、BaseDBとは異なりWalletによるTLSが設定済になっているので、事前準備は必要ありません。ADBと接続するためのVMインスタンスを新規作成してからの手順を説明します。

## 1.クライアントのADBへの接続設定 
```shell
#opcで接続
#19cの最新のInstance Clientをインストールする
$ wget https://download.oracle.com/otn_software/linux/instantclient/1920000/oracle-instantclient19.20-sqlplus-19.20.0.0.0-1.x86_64.rpm
$ wget https://download.oracle.com/otn_software/linux/instantclient/1920000/oracle-instantclient19.20-basic-19.20.0.0.0-1.x86_64.rpm
$ sudo yum install -y libnsl
$ sudo rpm -ivh oracle-instantclient19.20-basic-19.20.0.0.0-1.x86_64.rpm 
$ sudo rpm -ivh oracle-instantclient19.20-sqlplus-19.20.0.0.0-1.x86_64.rpm

#ADBから接続用のOracle Walletをダウンロードし、/home/opc/walletに解凍
[opc@ Wallet]$ ls
cwallet.sso  ewallet.pem   ojdbc.properties  sqlnet.ora    truststore.jks
ewallet.p12  keystore.jks  README            tnsnames.ora

#sqlnet.oraのWalletのパスを/home/opc/Walletに変更し、トレースの設定
$ vi /home/opc/Wallet/sqlnet.ora
WALLET_LOCATION = (SOURCE = (METHOD = file) (METHOD_DATA = (DIRECTORY="/home/opc/Wallet")))　
SSL_SERVER_DN_MATCH=yes

#トークン認証が失敗した際に調査できるようにトレースログを設定しておく
DIAG_ADR_ENABLED=on
TRACE_LEVEL_CLIENT=16
TRACE_TIMESTAMP_CLIENT=on


#TNS_ADMINを環境変数に設定
$ export TNS_ADMIN=/home/opc/Wallet

#5.ADBへの接続確認
$ sqlplus admin/xxxxxx##@uwbwp6wr5inades3_high
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.22.0.1.0
SQL> 
```

## 2.IDENTITY_PROVIDER_TYPEの設定
OCI IAMとの認証連携機能を有効化には、Autonomous DatabaseのIDENTITY_PROVIDER_TYPEのパラメータを設定する必要がある。 

```sql
-- DBMS_CLOUD_ADMIN.プロシージャでIdentity ProviderをIAMに設定
BEGIN
  DBMS_CLOUD_ADMIN.ENABLE_EXTERNAL_AUTHENTICATION(
    type => 'OCI_IAM'
    force => TRUE
    );
END;
/
SQL> SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME='identity_provider_type';
NAME                      VALUE
------------------------  -------------
identity_provider_type    OCI_IAM
```

## 3. IAMグループとポリシーの作成

IAMトークンを使用してデータベースに接続するためには、そもそもIAMユーザーがBaseDBサービスを使用するための権限をIAMポリシーとして許可されていなければならない。

ここでは、そのサービスを使用できるポリシーを持つ**dbtoken_grp**というグループを作成し、**emp1**と**mgr1**というユーザーをそのグループに所属させる。

### IAMユーザーの作成
- IAMに**emp1**と**mgr1**のユーザーを二つ作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/5f187839-9ad9-a590-c31d-c8ec4e961143.png)




### グループとポリシーの作成
- グループを作成 -> グループ名: **dbtoken_grp**
- データベースの権限をグループに付与したポリシーを作成 -> ポリシー名: **dbtoken_pol**
<font color="red">allow group dbtoken_grp to use database-connections in tenancy </font>
<font color="red">allow group dbusers to use autonomous-database-family in tenancy</font>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/9bb9882e-f8a4-21ff-ccc9-b50de9bae4c7.png)

- 作成したグループにemp1とmgr1ユーザーを割り当てる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/8a685ac1-a860-0ac8-26fe-4af11d401d68.png)

## 3. クライアントの設定
今回のテストでは、IAMの二つのユーザー(emp1とmgr1)をOCIのコンフィグで切り替えてDBアクセストークンを取得し、それぞれのIAMユーザーのトークンを用いてDBに接続する。そのための必要なOCI Cliの実行環境の設定を行う。

### OCI Cliでアクセス・トークンを取得する設定

```shell
#clientにopcで接続
#oci cliのインストール
$ sudo pip3 install oci-cli

#oci cliの接続設定
$ oci setup config
Enter a location for your config [/home/opc/.oci/config]: --> Enter
Enter a user OCID: ocid1.user.oc1..xxxxxxxx　--> emp1のOCID
Enter a tenancy OCID: ocid1.tenancy.oc1..xxxxx
Enter a region by index or name  
Do you want to generate a new API Signing RSA key pair? (If you decline you will be asked to supply the path to an existing key.) [Y/n]: Y
Enter a directory for your keys to be created [/home/opc/.oci]: --> Enter
Enter a name for your key [oci_api_key]: --> Enter
Enter a passphrase for your private key ("N/A" for no passphrase): -->N/Aと入力

#oci configのPublic Keyをコピー
$ cat /home/opc/.oci/oci_api_key_public.pem 
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxuxnwAZVRFVVi4yVu9BZ
vFifL/KVtMQWoY5pjomLxMgyoYeSydjThxui6EHEbdxGhvo0BgmiWcNFQI603jL1
oXkkUO8+IcNETslW4bffoCwiGrcAlZ/u*****
-----END PUBLIC KEY-----

```

 - 作成した**emp1**ユーザーのAPIキーとして追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/562423/b80e3a85-8496-6b54-1bb2-0810ade2f286.png)


```shell
#ociコマンドを実行し、結果が表示されればOK
$ oci iam region list --output table
+-----+-------------------+
| key | name              |
+-----+-------------------+
| AMS | eu-amsterdam-1    |
| ARN | eu-stockholm-1    |
```
IAMユーザーを切り替えてアクセストークンを取得するテストを行うので、emp1とmgr1でそれぞれconfigを切り替えて接続できるようにする

```shell
#emp1の接続キーをemp1フォルダにまとめ、configを変更
$ mkdir /home/opc/.oci/emp1
$ mv /home/opc/.oci/oci_api_key* /home/opc/.oci/emp1
$ vi /home/opc/.oci/config
[emp1] <--名前変更
user=ocid1.user.oc1..xxxxxxxx
fingerprint=e1:dd:32:10:0c:b5:4d:8e:93:63:05:27:be:64:0e:38 
key_file=/home/opc/.oci/emp1/oci_api_key.pem　<--ディレクトリ変更
tenancy=ocid1.tenancy.oc1..xxxxxxxx
region=ap-tokyo-1

#今度はmgr1でconfigの設定を行う
$ oci setup config
Config file: /home/opc/.oci/config already exists. Do you want add a profile here? (If no, you will be prompted to overwrite the file) [Y/n]: --> Enter
Enter the name of the profile you would like to create: mgr1
以降は前回と同じ。作成後は同様にPublic KeyをAPIキーとしてmgr1に登録する

#mgr1の接続キーをmgr1フォルダにまとめ、configを変更
$ mkdir /home/opc/.oci/mgr1
$ mv /home/opc/.oci/oci_api_key* /home/opc/.oci/mgr1
$ vi /home/opc/.oci/config
[mgr1]
user=ocid1.user.oc1..xxxxxxxx
fingerprint=f1:37:19:5a:58:48:55:63:42:ee:74:61:95:5d:c0:10
key_file=/home/opc/.oci/mgr1/oci_api_key.pem <--ディレクトリ変更
tenancy=ocid1.tenancy.oc1..xxxxxxxx
region=ap-tokyo-1

#IAMユーザーの切り替えはOCI_CLI_PROFILEの変数で行う。それぞれのユーザーでDBアクセストークンを取得するociのコマンドが実行できるかどうかを確認しておく
$ export OCI_CLI_PROFILE=emp1
$ oci iam db-token get
Private key written at /home/opc/.oci/db-token/oci_db_key.pem
db-token written at: /home/opc/.oci/db-token/token　　　<-- トークンが格納されているディレクトリ
db-token is valid until 2024-01-28 05:46:42

$ export OCI_CLI_PROFILE=mgr1
$ oci iam db-token get
iam db-token get
Private key written at /home/opc/.oci/db-token/oci_db_key.pem
db-token written at: /home/opc/.oci/db-token/token
db-token is valid until 2024-01-28 05:50:48
```

### Oracle Database Clientのトークン認証の設定
クライアント側のtnsnames.oraにトークン認証の接続文字列を追加する


```sql
$ vi /home/opc/Wallet/tnsnames.ora

接続文字列 =
   (description= (retry_count=20)(retry_delay=3)
      (address=(protocol=tcps)(port=1522)(host=ホスト名))
      (connect_data=(service_name=サービス名))
      (security=(ssl_server_dn_match=yes)(TOKEN_AUTH=OCI_TOKEN))
   )
   
<例>
iam =
   (description= (retry_count=20)(retry_delay=3)
      (address=(protocol=tcps)(port=1522)(host=adb.ap-tokyo-1.oraclecloud.com))
      (connect_data=(service_name=gd6d249ba18dd16_uwbwp6wr5inades3_tpurgent.adb.oraclecloud.com))
      (security=(ssl_server_dn_match=yes)(TOKEN_AUTH=OCI_TOKEN))
   )
   
````

## 4. IAMユーザーとのDBスキーマのマッピング
IAMユーザーがトークンでDB認証されると、Databaseは最終的には何らかのDBスキーマにマッピングしなければならない。マッピングの方式は、排他的スキーマと共有スキーマの２種類あるが、ここでは一番簡単な排他的マッピングでトークン認証の設定が正しくできているかどうかの基本的な動作確認を行う。
排他的マッピングとは、IAMユーザとDBスキーマを1:1でマッピングする方法

```sql
-- 排他的マッピングユーザーの作成
CREATE USER emp1 IDENTIFIED GLOBALLY AS 'IAM_PRINCIPAL_NAME=emp1'; -- DBスキーマ:emp1 = IAMユーザ:emp1
grant connect to emp1; -- connect権限を付与
```


## 5. 接続テスト 
トークンを使用してDBに接続できることを確認する

```shell
#emp1ユーザーのOCIプロファイルに変更
$ export OCI_CLI_PROFILE=emp1

#DBトークンを取得
$ oci iam db-token get
Private key written at /home/opc/.oci/db-token/oci_db_key.pem
db-token written at: /home/opc/.oci/db-token/token
db-token is valid until 2024-01-31 14:12:43

#トークン・ベースで接続
$ sqlplus /@iam
Connected to:
Oracle Database 19c EE High Perf Release 19.0.0.0.0 - Production
Version 19.21.0.0.0
SQL> show user
USER is "EMP1"  #DBスキーマ EMP1にマッピングされた
```
データベースへの接続は、下記のようなフローで動作する
① DB Clientは、**oci iam db-token get**でemp1ユーザーのDBトークンを取得する。トークンは.oci/db-token/tokenに出力される
② データベースに**sqlplus /@接続文字列**で接続することでトークンをTLSでDatabaseに送信する
③ DatabaseはトークンをValidationし、トークンに含まれるユーザーIDのグループをチェックし、最終的にDBスキーマにマッピングする


## 6.トラブルシューティング
手順通りに実施しても一発で接続成功することは難しいかもしれないので、どの部分の問題なのか段階的に調査するのが良い
 - クライアント側のSQL Traceのログからアクセストークンは正しく取得できているか？
 - Databaseのアラートログにエラーは出力されていないか？
 - IAMのグループやユーザーは適切に設定されているか？
 - DatabaseとIAMのマッピングユーザーは適切か？
 - OS側、OCI側のFirewallで必要なポートをブロックしていないか？
 - サポートされているバージョンのDatabase Clientを使用しているか？

共有マッピングの設定については、[こちら](https://qiita.com/western24/items/9ed6581f5742fcba28db)の手順を参照ください。