結論
OKE(Kubernetes)のWorker NodeからInstance Principal認証でAutonomous Database(ADB)にIAMトークン認証(パスワードレス)接続しようとしたところ、トークン発行API(GenerateScopedAccessToken)が一貫して404 NotAuthorizedOrNotFoundを返す事象にハマりました。
原因は Dynamic Groupのmatching_ruleの構文 でした。
# NG: 新しい推奨構文だとADBのトークン発行だけが404になる
matching_rule = "ALL {resource.type = 'instance', resource.compartment.id = '<compartment-ocid>'}"
# OK: 旧構文にするとADBのトークン発行が成功する
matching_rule = "ALL {instance.compartment.id = '<compartment-ocid>'}"
同じCompute InstanceのInstance Principalでも、他のOCIサービス(ブロックボリューム、Load Balancerなど)へのAPI呼び出しは新構文で問題なく動作していたため、ADBのIAMトークン発行だけが影響を受けるという点で気づきにくく、切り分けにかなり時間がかかったので記録として残します。
やりたかったこと・前提
- OKE上で動くSpring Bootアプリから、Autonomous Databaseにパスワードなしで接続したい
- DB側は
ENABLE_EXTERNAL_AUTHENTICATIONでIAM認証を有効化し、IDENTIFIED GLOBALLY AS 'IAM_GROUP_NAME=...'でOCI Dynamic Groupに紐づくユーザーを作成済み - アプリ(JDBC)側はojdbc-provider-ociを使い、
authenticationMethod=instance-principalでOKE Worker NodeのInstance Principalからアクセストークンを取得する構成
spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.accessToken=ojdbc-provider-oci-token
spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.accessToken.authenticationMethod=instance-principal
spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.accessToken.region=ap-tokyo-1
spring.datasource.hikari.data-source-properties.oracle.jdbc.provider.accessToken.scope=urn:oracle:db::id::<compartment-ocid>
IAM側は、以下のようなDynamic GroupとPolicyを用意していました。
resource "oci_identity_dynamic_group" "oke_service" {
compartment_id = var.tenancy_ocid
name = "oke-test-dynamic-group"
matching_rule = "ALL {resource.type = 'instance', resource.compartment.id = '${var.compartment_id}'}"
}
resource "oci_identity_policy" "adb_iam_authentication" {
compartment_id = var.tenancy_ocid
name = "oke-test-adb-iam-auth-policy"
statements = [
"Allow dynamic-group ${oci_identity_dynamic_group.oke_service.name} to use database-connections in compartment id ${var.compartment_id}",
]
}
構文としてはエラーとなっていない上、実際にこのDynamic Groupは他のAPI(仮想ネットワーク操作やLoad Balancer操作など)には正常に使われていました。
動作環境
本記事の内容は以下の環境で確認したものです。OCI側のAPI挙動に依存する内容のため、時期やリージョン、各種バージョンが異なると挙動が変わる可能性があります。
- 検証時期: 2026年7月、リージョン:
ap-tokyo-1 - OKE: Kubernetesバージョン
v1.35.2(Enhanced Cluster、Native Ingress Controller使用) - Autonomous Database:
db_version = "26ai"、compute_model = "ECPU"(プライベート・エンドポイント構成) - JDBCドライバ:
com.oracle.database.jdbc:ojdbc11:23.6.0.24.10 - IAMトークン取得ライブラリ:
com.oracle.database.jdbc:ojdbc-provider-oci:1.1.0(ojdbc-extensions) - Terraform:
v1.15.7、oracle/ociprovider:v8.22.0
事象
アプリのPodログには次のようなエラーが出力され続けました(実際の値を一部マスクしています)。
oracle.jdbc.provider.oci.token.OciTokenProviderException: Failed to generate access token
Caused by: com.oracle.bmc.model.BmcException: Error returned by GenerateScopedAccessToken operation in Dataplane service.
(404, NotAuthorizedOrNotFound, false)
Message: Authorization failed or requested resource not found.
Request Endpoint: https://auth.ap-tokyo-1.oraclecloud.com/v1/actions/generateScopedAccessToken
Operation Name: generateScopedAccessToken
opc-request-id: <request-id>
generateScopedAccessTokenは、Instance PrincipalなどのOCI Identity上のPrincipalに対して、Autonomous Database向けのスコープ付きアクセストークンを発行するAPI(Identity Data Plane、auth.<region>.oraclecloud.com)です。
厄介なのは、この404が「権限(Policy)が足りない」場合と「認証(Dynamic Groupのマッチング)に失敗している」場合の両方で返ってくることです。OCIのエラーメッセージからはどちらが原因か判別できません。
切り分けの過程
以下の観点で1つずつ要因を除外していきました。
1. JWT(Instance Principalの証明書)の中身を確認する
Instance Principal認証は、Compute Instanceのメタデータサービスから取得した証明書をもとにJWTを生成し、それをIdentity Data Planeに提示する仕組みです。まずこのJWTが正しい内容(正しいCompartment OCID、正しいTenancy OCIDなど)を含んでいるかを確認しました。
Worker Node上にOCI Python SDK(oci)を入れたデバッグPodを立て、InstancePrincipalsSecurityTokenSignerが内部でIdentity Data Planeから受け取るセキュリティトークン(JWT)を取り出し、ペイロード部分を手動でBase64urlデコードします。
import base64, json
from oci.auth.signers import InstancePrincipalsSecurityTokenSigner
signer = InstancePrincipalsSecurityTokenSigner()
token = signer._security_token # JWT形式(header.payload.signature)
payload = token.split(".")[1]
payload += "=" * (-len(payload) % 4) # base64urlのパディング調整
print(json.dumps(json.loads(base64.urlsafe_b64decode(payload)), indent=2))
出力されたJSONのcompartmentid(Instanceが属するCompartment OCID)やtenantidを確認し、Dynamic Groupのmatching_ruleで指定していた条件と一致していることを確認しました。
2. ネットワーク経路を疑い、hostNetwork Podで切り分ける
Podの仮想NIC経由の通信で何か問題が起きている可能性を考え、hostNetwork: trueを指定したデバッグPodを立て、Worker Nodeのネットワーク名前空間から直接同じAPIを呼び出してみましたが、同様に404が発生することを確認。
上記から、Podの仮想NIC経由による通信の問題ではないことを確認しました。
3. 権限を極端に広げても404が消えない
そもそも足りない権限があるのでは?という可能性も考え、一時的にPolicyの範囲をin tenancyまで広げても404が解消しないことを確認。ここから、権限の広さの問題ではなさそうであり、Dynamic Group側のマッチングそのものがうまくいっていない可能性が高そうだと思い始めました。
4. matching_ruleの構文をA/Bテストする
最後に、Policy側の内容は変えずに、Dynamic Groupのmatching_ruleだけを新旧2パターンで切り替えてデプロイし、動作確認を実施しました。
# パターンA(新構文)
matching_rule = "ALL {resource.type = 'instance', resource.compartment.id = '${var.compartment_id}'}"
# パターンB(旧構文)
matching_rule = "ALL {instance.compartment.id = '${var.compartment_id}'}"
その結果、パターンAでは404、パターンBでは成功という挙動が複数回のterraform apply/動作確認を通して再現しました。
原因
OCIのDynamic Groupのmatching_ruleには、Compute Instanceを対象にする記法が2種類あります。
- 旧構文:
instance.compartment.id = '<ocid>'/instance.id = '<ocid>' - 新構文(汎用リソース記法):
resource.type = 'instance', resource.compartment.id = '<ocid>'
公式ドキュメント上はどちらも「Compute Instanceにマッチする」記法として案内されていますが、少なくとも今回検証した環境では、Autonomous DatabaseへのIAMトークン発行(generateScopedAccessToken)のスコープ判定においては旧構文でないと一致しないという挙動を確認しました。同じDynamic Groupの新構文条件で、Block StorageやLoad BalancerなどのAPI呼び出しは問題なく成功していたため、これは「Instance Principal認証全般の問題」ではなく、「ADBのトークン発行APIにおけるDynamic Groupマッチング判定の実装上の差異」だと考えられます。
なお、影響を受けるのはあくまでADBのトークン発行時、instanceをマッチさせる条件に限られます。他のリソースタイプ・他のAPIでは新構文でも問題なく動作しているため、「新構文は使うべきではない」という話ではないので注意してください。
この挙動は公式ドキュメントで明文化されたものではなく、あくまで今回の検証環境で再現した実装依存の挙動です。OCI側の将来のアップデートで解消・変化する可能性がある点はご留意ください。
最終的な構成
最終的にDynamic GroupのMatching Ruleを以下のように、Worker Node(Instance)を旧構文でマッチさせる形にしました。
resource "oci_identity_dynamic_group" "oke_service" {
compartment_id = var.tenancy_ocid
name = "oke-test-dynamic-group"
# 旧構文(instance.compartment.id)でないと
# ADBのIAMトークン発行(generateScopedAccessToken)が404 NotAuthorizedOrNotFoundになる。
matching_rule = "ALL {instance.compartment.id = '${var.compartment_id}'}"
}
Policyの最小権限化
原因が判明した後、Policy側についても最小権限を意識して以下を検証しました。
-
autonomous-database-family(広め) vsdatabase-connections(狭め): 今回のIAMトークン発行用途ではdatabase-connectionsのみで十分と確認 -
target.id/target.database.id(ADB固有の条件変数):autonomous-database-familyに対しては機能するが、database-connectionsに対しては機能しないことを確認 - 代わりに、より汎用的な
target.compartment.idを使うことで、database-connectionsでもCompartment単位への絞り込みが機能することを確認
最終的なPolicyは以下の形に落ち着きました。
resource "oci_identity_policy" "adb_iam_authentication" {
compartment_id = var.tenancy_ocid
name = "oke-test-adb-iam-auth-policy"
statements = [
"Allow group ${oci_identity_group.db_admins.name} to use database-connections in compartment id ${var.compartment_id}",
"Allow dynamic-group ${oci_identity_dynamic_group.oke_service.name} to use database-connections in tenancy where target.compartment.id = '${var.compartment_id}'",
]
}
(database-connectionsを対象にする場合、Policy文自体はin tenancyスコープで書く必要があり、Compartment単位の絞り込みはwhere条件のtarget.compartment.idで行う形になります。)
まとめ
- OCIのInstance PrincipalでAutonomous DatabaseにIAMトークン認証する際、
generateScopedAccessTokenが404 NotAuthorizedOrNotFoundになる場合は、権限不足だけでなくDynamic Groupのmatching_ruleの構文(新旧どちらか)も疑うべき - 同じDynamic Groupが他のOCI API呼び出しで正常に機能していても、ADBのトークン発行だけがマッチング判定の影響を受けることがある
同じ事象で困っている方の参考になれば幸いです。
参考リンク
-
動的グループを定義するための一致ルールの記述(OCI公式ドキュメント) —
instance.compartment.id(旧構文)とresource.type = 'instance'(汎用リソース構文)の両方が案内されている - ojdbc-provider-oci(ojdbc-extensions) — 本記事で使用したIAMトークン取得用JDBC拡張ライブラリ
-
Prerequisites for Identity and Access Management (IAM) Authentication on Autonomous Database(OCI公式ドキュメント) —
database-connectionsなど、ADBのIAM認証用ポリシーの記述方法