0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Oracle Autonomous AI Database に OCI Functions から IAM トークン + TLS (Walletなし) で接続する

0
Last updated at Posted at 2026-03-17

はじめに

この記事では、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() ではなく、DataplaneClientgenerate_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"}]

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?