はじめに
この記事では、OCI Functions から Oracle Autonomous AI Database (ADB) に対して、以下の構成で接続する方法を解説します。
- 認証: OCI IAM トークン(Resource Principal 経由)
- 通信: TLS(mTLS オフ、Wallet 不要)
前提条件
- ADB インスタンスが作成済みであること
- OCI Functions 用の VCN・サブネットが作成済みであること
- OCI Functions を利用するためのポリシーが作成済みであること
IAM トークン認証の仕組み
通常の DB 認証との違い
通常の DB 接続ではユーザー名・パスワード(または Wallet)で認証しますが、今回は OCI IAM を認証基盤として使うため、パスワードを管理する必要がありません。
| 通常認証 | IAM トークン認証 | |
|---|---|---|
| 認証基盤 | DB 内部 | OCI IAM |
| 認証情報 | ユーザー名・パスワード | スコープ付き IAM トークン+秘密鍵 |
| なりすまし防止 | パスワード | Proof of Possession(鍵ペア) |
| ユーザー管理 | DB 内で管理 | IAM の動的グループと DB ユーザーのマッピング |
詳細フロー
① Function 起動
↓
② Resource Principal で自分自身を証明
(「私はこの Function です」)
↓
③ RSA 鍵ペアを生成(Proof of Possession 用)
↓
④ DataplaneClient が OCI IAM に問い合わせ
「この Function に、この ADB 用のトークンを発行してください」
(公開鍵も一緒に渡す)
↓
⑤ IAM がスコープ付きトークンを発行
(トークンの中に公開鍵・動的グループ情報・ADB の OCID が埋め込まれる)
↓
⑥ oracledb.connect(access_token=(token, 秘密鍵), externalauth=True)
↓
⑦ ADB が IAM にトークンを検証依頼
「このトークンは本物か?この ADB 宛か?」
↓
⑧ 動的グループ → DB ユーザー(dbusers)にマッピング → 接続 OK
ポイント解説
スコープ付きトークンとは
get_security_token() は Function 自身の汎用的な IAM トークンです。一方、generate_scoped_access_token は特定の ADB だけに使えるトークンを発行します。
scope = "urn:oracle:db::id::<compartment_ocid>::<adb_ocid>"
# ↑ このADB専用のトークンを発行してください、という指定
Proof of Possession とは
トークンを盗まれてもなりすましできないようにする仕組みです。公開鍵をIAMに渡してトークンを発行してもらい、DB接続時にトークン+対応する秘密鍵をセットで提示することで、「トークンの正当な所有者である」ことを証明します。
DB ユーザーのマッピング
IAM の動的グループと DB ユーザーは以下のように紐付けられます。
CREATE USER dbusers IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME=dg-function-adb';
接続後は dbusers として扱われ、このユーザーに付与された権限でクエリが実行されます。
1. ADB 側の設定
1-1. mTLS 必須をオフにする
OCI コンソール → Autonomous AI Database → 対象 ADB →「Autonomous AI Database情報」→「相互 TLS(mTLS) 認証を必須とする」を編集からオフ もしくは作成時にオフにしておきます。
1-2. TLS 接続文字列をコピー
「データベース接続」からTLSの接続文字列をコピーしておきます(ポートが 1521 になっているものです)。
1-3. IAM 認証を有効化
ADB の Database Actions に ADMIN でログインして実行します。
⚠️ ADB では
ALTER SYSTEMは使用できません。DBMS_CLOUD_ADMINを使います。
BEGIN
DBMS_CLOUD_ADMIN.ENABLE_EXTERNAL_AUTHENTICATION(
type => 'OCI_IAM',
force => TRUE
);
END;
/
-- 確認
SELECT NAME, VALUE FROM V$PARAMETER WHERE NAME = 'identity_provider_type';
-- VALUE が OCI_IAM になれば OK
1-4. Dynamic Group にマップする DB ユーザー作成
-- Dynamic Group 名と紐付けたグローバルユーザー作成
CREATE USER dbusers IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME=dg-function-adb';
GRANT CONNECT, RESOURCE, UNLIMITED TABLESPACE TO dbusers;
-- テスト用テーブルとデータ
CREATE TABLE dbusers.users (
id VARCHAR2(32) DEFAULT sys_guid() PRIMARY KEY,
first_name VARCHAR2(50) NOT NULL,
last_name VARCHAR2(50) NOT NULL
);
INSERT INTO dbusers.users (first_name, last_name) VALUES ('John', 'Doe');
INSERT INTO dbusers.users (first_name, last_name) VALUES ('Jane', 'Smith');
COMMIT;
2. OCI IAM 設定
2-1. Dynamic Group の作成
OCI コンソール →「アイデンティティ」→「動的グループ」→「作成」
-
名前:
dg-function-adb - ルール:
ALL {resource.type = 'fnfunc', resource.compartment.id = '<コンパートメント OCID>'}
2-2. ポリシーの作成
OCI コンソール →「アイデンティティ」→「ポリシー」→「作成」
Allow dynamic-group dg-function-adb to use autonomous-database-family in compartment <compartment_name>
3. Function の作成
3-1. ディレクトリ作成
Cloud Shell で以下を実行します。
fn init --runtime python access-adb-func
cd access-adb-func
3-2. requirements.txt
以下のように編集します。
fdk
oci
oracledb
cryptography
3-3. func.py
ポイントは get_security_token() ではなく、DataplaneClient の generate_scoped_access_token を使って DB 専用のスコープ付きトークンを生成することです。
以下のように編集します。
import io
import os
import oci
import json
import oracledb
from fdk import response
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
def handler(ctx, data: io.BytesIO = None):
try:
cfg = dict(ctx.Config())
dsn = cfg["DSN"]
adb_ocid = cfg["ADB_OCID"]
adb_compartment_ocid = cfg["ADB_COMPARTMENT_OCID"]
region = cfg["REGION"]
scope = "urn:oracle:db::id::{}::{}".format(adb_compartment_ocid, adb_ocid)
client = oci.identity_data_plane.DataplaneClient(
config={},
signer=oci.auth.signers.get_resource_principals_signer()
)
token_auth_config = {
"scope": scope,
"region": region
}
connection = oracledb.connect(
access_token=_generate_access_token(client, token_auth_config),
dsn=dsn,
externalauth=True
)
cursor = connection.cursor()
cursor.execute("SELECT first_name, last_name FROM dbusers.users")
columns = [col[0] for col in cursor.description]
rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
cursor.close()
connection.close()
return response.Response(
ctx,
response_data=json.dumps(rows),
headers={"Content-Type": "application/json"}
)
except Exception as e:
print(f"ERROR: {e}", flush=True)
return response.Response(
ctx,
response_data=json.dumps({"error": str(e)}),
headers={"Content-Type": "application/json"},
status_code=500
)
def _get_key_pair():
"""Proof of Possession 用の公開鍵・秘密鍵ペアを生成"""
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
)
private_key_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
).decode("utf-8")
public_key_pem = (
private_key.public_key()
.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
.decode("utf-8")
)
return {"private_key": private_key_pem, "public_key": public_key_pem}
def _generate_access_token(client, token_auth_config):
"""DB 接続用のスコープ付き IAM トークンを生成"""
key_pair = _get_key_pair()
scope = token_auth_config.get("scope")
details = oci.identity_data_plane.models.GenerateScopedAccessTokenDetails(
scope=scope, public_key=key_pair["public_key"]
)
resp = client.generate_scoped_access_token(
generate_scoped_access_token_details=details
)
return (resp.data.token, key_pair["private_key"])
4. Function のデプロイ
4-1. アプリケーション作成
OCI コンソール →「開発者サービス」→「Functions」→「アプリケーション」→「作成」
-
名前:
app-adb-iam-token - VCN・サブネット: ADB に到達できるもの
-
シェイプ:
GENERIC_X86
4-2. アプリケーション構成(環境変数)設定
OCI コンソール → app-adb-iam-token →「構成」で以下を追加します。
| キー | 値 |
|---|---|
DSN |
手順 1-2 でコピーした TLS 接続文字列(ポート 1521) |
ADB_OCID |
ADB の OCID |
ADB_COMPARTMENT_OCID |
ADB が存在するコンパートメントの OCID |
REGION |
ADB のリージョン(例: ap-tokyo-1) |
4-3. Cloud Shell でデプロイ
# コンテキスト設定
fn use context <your-region>
fn update context oracle.compartment-id <compartment-ocid>
fn update context registry <region>.ocir.io/<tenancy-namespace>/<repo-prefix>
# OCIR ログイン(パスワードは認証トークン)
docker login <region>.ocir.io -u '<tenancy-namespace>/<username>'
# Oracle Container Registry ログイン(ベースイメージ取得用)
docker login container-registry.oracle.com
# デプロイ
fn deploy --app app-adb-iam-token
4-4. セキュリティリスト設定
Function のサブネットのセキュリティリストにエグレスルールを追加します。
| 項目 | 値 |
|---|---|
| 方向 | エグレス(送信) |
| 宛先 |
0.0.0.0/0 または ADB のエンドポイント |
| プロトコル | TCP |
| 宛先ポート | 1521 |
4-5. ADB のアクセス制御リスト設定
ADB が「指定された IP および VCN からのセキュア・アクセスを許可」に設定されている場合、以下を許可リストに追加します。
- Function が存在する VCN の OCID
- Function のサブネット CIDR
5. 動作確認
fn invoke app-adb-iam-token access-adb-func
以下のような JSON が返れば成功です。
[{"first_name": "John", "last_name": "Doe"}, {"first_name": "Jane", "last_name": "Smith"}]