こんにちは、インサイトテクノロジーの松尾です!
本投稿は Japan AWS Top Engineers Advent Calendar 2025 の15日目の記事です!!
本投稿では、Amazon RDS for Oracle運用における監査ログ保存運用において、 統合監査(Unified Auditing)ログを S3 へスケジュール転送する方法 を紹介します。
1. はじめに
Amazon RDS for Oracle を利用する際、セキュリティとコンプライアンスの確保に不可欠なのが 監査ログの管理 です。特に、機密データを扱うシステムにおいては、誰が、いつ、どのような操作を行ったかを詳細に記録し、長期間にわたって保持することが求められます。
現在、RDS for Oracle の標準的な監査ログの出力方法としては、主に以下の 二つのオプション が提供されています。
- 標準監査ログの CloudWatch Logs への出力
- Database Activity Streams (DAS) の利用
これらの既存ソリューションは、それぞれ強力な機能を提供しますが、特に長期保存とコスト効率の観点からの課題も存在します。
1.1. 標準監査ログ (Standard Audit) を CloudWatch Logs へ出力
RDS for Oracle のパラメーターグループ設定を通じて有効化される標準監査ログは、最も基本的な監査情報を提供します。これらのログは、AWS コンソールでチェックをつけるだけで Amazon CloudWatch Logs に自動的にエクスポートされ、一元管理が可能です。
-
特徴:
- セットアップが容易で、標準的な監査要件を満たします。
- CloudWatch Logs の堅牢な機能(フィルタリング、メトリクス化、アラートなど)と連携できます。
- ログファイルを RDS API を使用してダウンロード可能です。
-
課題:
- コスト: CloudWatch Logs は、データ取り込み量と保存期間に応じて課金されます。監査ログはトランザクション量に比例して大量に出力されるため、 長期間の保存や大容量のログの場合、コストが増大しやすい 傾向にあります。
-
ログ形式: 従来の標準監査(
AUDIT_TRAIL=DBなど)のログは、統一性が低い場合や、後述の Unified Auditing に比べて提供する情報が限定的になることがあり、また、今後の Oracle バージョンでは標準監査の利用が非推奨または廃止になることがアナウンスされています。
1.2. Database Activity Streams (DAS) の利用
Database Activity Streams (DAS) は、データベースの活動をほぼリアルタイムでキャプチャし、Amazon Kinesis Data Streams を経由して、Amazon S3 や SIEM ツール(Splunk, Sentinel など)へストリーミング配信する高度なソリューションです。
-
特徴:
- 高セキュリティ: 監査ストリームが暗号化され、データベース管理者であっても改ざんが困難な構造を持っています。
- リアルタイム性: ほぼリアルタイムでアクティビティを監視できます。
- 詳細な情報: 取得できる情報が豊富で、セキュリティ要件が非常に厳しい環境に適しています。
- Unified Auditing: 監査ログの出力は Oracle の 統合監査 (Unified Auditing) の機能がベースです。
-
課題:
- コスト: DAS は、使用時間に応じた料金が発生し、特に大規模な環境や24時間365日の運用では、高額な費用がかかることがあります。
- 複雑性: Kinesis Data Streams や KMS など、複数の AWS サービスを組み合わせるため、セットアップと運用が複雑になりがちです。
1.3. その他の方法もあるけれど
その他の方法として上記の応用パターンとして以下のような手段もあります。
- RDS 内にファイル出力させた標準監査ログを API 経由で取得して S3 等に保存する。
- DB 内に出力させた監査ログ情報を外部からクエリで取得して S3 等にファイル保存する。
ただし、いずれも RDS 外のリソースを起動して処理させる必要がありますので、若干面倒ですよね?
2. 新たな選択肢としての Unified Auditing と S3 統合
上記の課題を背景に、特にコストや管理の容易さを意識しつつ、コンプライアンス要件を満たす 長期的な監査ログの保管 を目的とした場合、RDS for Oracle の Unified Auditing(統合監査)機能をベースに出力される情報をベースとしつつも、Kinesis などを使わずに、もっとお手軽に長期保存用ストレージへ退避できないか、の一つの解決策が今回のソリューションです。
2.1. 処理の流れ
処理自体はさほど複雑ではなく、Unified Auditing 機能と RDS の S3 統合の組み合わせで、おおよそ以下の流れで行います。
- RDS for Oracle で Unified Auditing の設定を行い、DB 内に監査ログを出力する。
- 定期的にプロシージャーを実行し、DB 内の監査ログをファイル出力し、出力したファイルを S3 に保存する。
この処理例を簡単に見つけることができなかったため、今回、ステップバイステップで紹介してきたいと思います。
2.2. Unified Auditing のメリット
Oracle Database 12c 以降で導入された Unified Auditing は、従来の標準監査と比較して多くの利点があります。
-
情報の一元化: 監査情報を単一の場所(
UNIFIED_AUDIT_TRAIL)に集約し、ログ形式を統一します。 - 詳細な監査: より詳細なコンテキスト情報(クライアント情報、ホスト情報など)を提供し、きめ細やかな監査ポリシーの設定が可能です。
- パフォーマンス: 従来の標準監査と比べ、オーバーヘッドが少ない設計になっています。
2.3. S3 への統合によるコスト効果
Unified Auditing のログを定期的に S3 へエクスポート・保存するアプローチは、CloudWatch Logs や DAS と比較して多くの利点があります。
- 格段に低い保存コスト: S3 は、AWS のストレージサービスの中でも非常に安価なストレージを提供します。特に、Glacier や Infrequent Access (IA) などのストレージクラスを利用すれば、長期保存のコストを劇的に抑えることができます。これは、DAS や CloudWatch Logs のデータ保存料金と比較して、大きなコストメリットとなります。
- 長期コンプライアンス対応: 日本の金融庁ガイドラインやその他の規制要件では、数年間の監査ログ保持が求められることがあります。S3 は耐久性と可用性が高く、ライフサイクルポリシーを利用して自動的に低コストのストレージクラスへ移行できるため、この長期保存の要件を低コストで満たすのに最適です。
- 柔軟なデータ分析: S3 に格納された監査ログデータは、AWS Glue、Amazon Athena、Amazon Redshift Spectrum などのサービスと連携することで、高度な分析や検索をオンデマンドで実行できます。これにより、ログの取り込みコストをかけずに、必要なときだけ検索・分析を行う「低コストでのデータ活用基盤」を構築できます。
3. やってみよう!
おおまかには以下が必要となります。
- 本投稿での事前条件:
- RDS for Oracle インスタンス ※19c SE, non-cdbで確認しています (
rds-oracle-ua-s3-integration-testとして用意しました)- テスト用の一般ユーザーとして
SCOTTユーザーとテスト用テーブルを作成 - インスタンスへ接続可能な sqlplus 環境
- テスト用の一般ユーザーとして
- ファイル出力先のS3バケット (
rds-oracle-audit-logs-bucketとして用意しました)
- RDS for Oracle インスタンス ※19c SE, non-cdbで確認しています (
- S3 統合の設定
- AWS: IAM ロール、RDS のオプショングループ
- Unified Auditing 利用の設定
- AWS: RDS のパラメーターグループ
- Oracle: 監査ポリシーの設定
- 監査ログの転送
- Oracle: プロシージャーの作成、スケジュールの設定
では早速やってみましょう!
3.1. AWS側の設定:S3 統合の有効化と疎通確認 (と監査のためのパラメーターグループ設定)
3.1.1. IAMロールとポリシーの設定(必須)
RDS が S3 バケットに書き込みを行うための IAM ロールを作成します。
ステップ 1: IAMポリシーの作成
rds-s3-upload-policy などの名前で以下のポリシーを作成します。YOUR-S3-BUCKET-NAME は実際のバケット名に置き換えてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3AccessForRDS",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::YOUR-S3-BUCKET-NAME",
"arn:aws:s3:::YOUR-S3-BUCKET-NAME/*"
]
}
]
}
ステップ 2: IAM ロールの作成
rds-s3-upload-role などの名前でロールを作成し、信頼されたエンティティとして rds.amazonaws.com を指定し、作成したポリシーをアタッチします。
ステップ 3: オプショングループの作成
RDS コンソールで、rds-s3-integration-option-group などの名前で「S3_INTEGRATION」オプションを追加したオプショングループを作成します。
ステップ 4: IAM ロールとオプショングループの適用
DB インスタンスに、作成したオプショングループと IAM ロールを設定します。
IAM ロールは対象 DB の Connectivity & security タブの一番下で設定します。
オプショングループは、DB インスタンスの Modify を選択し、作成したオプショングループを設定します。
3.1.2. パラメーターグループ設定 (監査ログ出力のため)
以下の設定のパラメーターグループを rds-oracle-audit-db-extended などの名前で作成して設定後、DB インスタンスを再起動します。
| パラメーター名 | 変更後の値 | 説明 |
|---|---|---|
audit_trail |
DB,EXTENDED |
監査機能の安定稼働(混合モード)を保証します。 |
audit_sys_operations |
TRUE |
SYS ユーザー操作の監査を有効化します。 |
パラメーターグループは、DB インスタンスの Modify を選択し、作成したオプショングループを設定します。
3.1.3. S3 統合の動作確認 (疎通確認)
S3 統合の設定が正しく完了したことを確認するため、テストファイルを S3 にアップロードするプロシージャを実行します。
プロシージャコード:
CREATE OR REPLACE PROCEDURE verify_s3_upload
AUTHID CURRENT_USER
AS
v_s3_bucket_name VARCHAR2(100) := 'rds-oracle-audit-logs-bucket'; -- 実際のバケット名に置換
v_directory_name VARCHAR2(30) := 'DATA_PUMP_DIR';
v_file_handle UTL_FILE.FILE_TYPE;
v_file_name VARCHAR2(100);
v_task_id VARCHAR2(100);
v_sql VARCHAR2(500);
BEGIN
v_file_name := 's3_test_' || TO_CHAR(SYSDATE, 'YYYYMMDDHH24MISS') || '.txt';
v_file_handle := UTL_FILE.FOPEN(v_directory_name, v_file_name, 'W', 32767);
UTL_FILE.PUT_LINE(v_file_handle, 'This is a test file for S3 integration verification.');
UTL_FILE.PUT_LINE(v_file_handle, 'Timestamp: ' || TO_CHAR(SYSTIMESTAMP, 'YYYY-MM-DD HH24:MI:SS'));
UTL_FILE.FCLOSE(v_file_handle);
DBMS_OUTPUT.PUT_LINE('Uploading file ' || v_file_name || ' to S3...');
v_sql := 'SELECT rdsadmin.rdsadmin_s3_tasks.upload_to_s3(' ||
'p_bucket_name => :b_bucket, p_prefix => :b_file_name, ' ||
'p_s3_prefix => ''test_verification/'', p_directory_name => :b_dir' ||
') FROM DUAL';
EXECUTE IMMEDIATE v_sql INTO v_task_id
USING v_s3_bucket_name, v_file_name, v_directory_name;
DBMS_OUTPUT.PUT_LINE('S3 Upload Task ID: ' || v_task_id);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
IF UTL_FILE.IS_OPEN(v_file_handle) THEN UTL_FILE.FCLOSE(v_file_handle); END IF;
RAISE;
END;
/
実行コマンド:
SQL> SET SERVEROUTPUT ON
SQL> EXEC verify_s3_upload;
実行結果
プロシージャを作成して実行すると、以下のように S3 にファイルが作成されていることが確認できましたね!
3.2. 統合監査の設定と確認
続いて Unified Auditing の監査ログを出力するための監査ポリシーを設定します。
なお、RDS for Oracle の Unified Auditing では、混合監査のみをサポートしているようです。
3.2.1. 統合監査ポリシーの適用
ここでは、ユーザー SCOTT が実行する SQL をすべて監査ログとして保存する監査ポリシーを設定しています。
-- 統合監査ポリシーの作成と適用
CREATE AUDIT POLICY scott_unified_policy ACTIONS ALL;
AUDIT POLICY scott_unified_policy BY SCOTT;
COMMIT;
以下のクエリを実行すると設定状態を確認できます。
SELECT
POLICY_NAME,
ENTITY_NAME,
ENTITY_TYPE,
SUCCESS,
FAILURE
FROM
AUDIT_UNIFIED_ENABLED_POLICIES
WHERE
ENTITY_NAME = 'SCOTT';
3.2.2. 監査ログの存在確認
では SCOTT ユーザーで SQL を実行し、監査ログが期待通りに記録されているかを確認します。
SQL 実行後に念のため監査ログを強制フラッシュし、UNIFIED_AUDIT_TRAIL を確認します。
実行コマンド:
SCOTT ユーザーにて以下を実行しましょう。
SELECT count(*) FROM EMP;
INSERT INTO DEPT (DEPTNO, DNAME, LOC) VALUES (99, 'TEST', 'TOKYO');
COMMIT;
続いて、管理者ユーザーで監査ログの出力を確認します。
-- 監査ログの強制フラッシュ (必須)
SQL> EXEC DBMS_AUDIT_MGMT.FLUSH_UNIFIED_AUDIT_TRAIL;
-- ログデータの存在確認 (DBセッションのローカルタイムを基準に過去1時間のログを抽出)
SQL> SELECT COUNT(*)
FROM UNIFIED_AUDIT_TRAIL
WHERE EVENT_TIMESTAMP >= LOCALTIMESTAMP - INTERVAL '1' HOUR
AND DBUSERNAME = 'SCOTT';
何件かログが出ていますね。試しに1件見てみましょう。
SELECT
TO_CHAR(EVENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') AS log_time,
UNIFIED_AUDIT_POLICIES AS POLICY_NAME,
DBUSERNAME, ACTION_NAME, SQL_TEXT
FROM UNIFIED_AUDIT_TRAIL
WHERE ROWNUM <= 1
AND DBUSERNAME = 'SCOTT'
AND UNIFIED_AUDIT_POLICIES = 'SCOTT_UNIFIED_POLICY'
ORDER BY EVENT_TIMESTAMP;
SCOTT ユーザーのログオンが記録されていました。
3.3. ファイルの作成、S3 への転送、スケジュール実行
続いて UNIFIED_AUDIT_TRAIL の情報をファイ出力し、S3 へファイルを保存するプロシージャを作成します。
3.3.1. S3転送プロシージャの作成
本ロジックでは直前の完全な1時間 (例: 10:00:00 to 11:00:00) の範囲のログを対象に処理を行います。
プロシージャコード:
CREATE OR REPLACE PROCEDURE EXTRACT_AND_UPLOAD_AUDIT_LOG
AUTHID CURRENT_USER
AS
v_s3_bucket_name VARCHAR2(100) := 'rds-oracle-audit-logs-bucket'; -- 実際のバケット名に置換
v_directory_name VARCHAR2(30) := 'DATA_PUMP_DIR';
v_s3_prefix VARCHAR2(100) := 'audit_logs/';
v_file_handle UTL_FILE.FILE_TYPE;
v_file_name VARCHAR2(100);
v_task_id VARCHAR2(100);
v_sql VARCHAR2(500);
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
v_row_count NUMBER;
-- 統合監査のUNIFIED_AUDIT_TRAILに対応したカーソル定義
CURSOR c_audit_logs IS
SELECT
TO_CHAR(EVENT_TIMESTAMP, 'YYYY-MM-DD HH24:MI:SS') AS log_time,
UNIFIED_AUDIT_POLICIES AS POLICY_NAME,
DBUSERNAME, ACTION_NAME, RETURN_CODE,
OS_USERNAME, USERHOST, SQL_TEXT
FROM UNIFIED_AUDIT_TRAIL
WHERE EVENT_TIMESTAMP >= v_start_time AND EVENT_TIMESTAMP < v_end_time
ORDER BY EVENT_TIMESTAMP;
BEGIN
-- ★固定時間ウィンドウロジック:直前の完全な1時間 (例: 10:00:00 to 11:00:00) を抽出
v_end_time := TRUNC(LOCALTIMESTAMP, 'HH');
v_start_time := v_end_time - INTERVAL '60' MINUTE;
v_file_name := 'audit_' || TO_CHAR(v_start_time, 'YYYYMMDD_HH24') || '.csv'; -- ファイル名は開始時刻ベース
-- デバッグ用: 行数カウント (行がなければファイル作成をスキップし、S3転送を防止)
v_sql := 'SELECT COUNT(*) FROM UNIFIED_AUDIT_TRAIL WHERE EVENT_TIMESTAMP >= :start_time AND EVENT_TIMESTAMP < :end_time';
EXECUTE IMMEDIATE v_sql INTO v_row_count USING v_start_time, v_end_time;
IF v_row_count = 0 THEN
RETURN;
END IF;
-- UTL_FILEによるファイル作成と書き込み
v_file_handle := UTL_FILE.FOPEN(v_directory_name, v_file_name, 'W', 32767);
UTL_FILE.PUT_LINE(v_file_handle, 'EVENT_TIMESTAMP,POLICY_NAME,DBUSERNAME,ACTION_NAME,RETURN_CODE,OS_USERNAME,USERHOST,SQL_TEXT');
FOR rec IN c_audit_logs LOOP
UTL_FILE.PUT_LINE(v_file_handle, rec.log_time || ',' || rec.policy_name || ',' ||
rec.dbusername || ',' || rec.action_name || ',' || rec.return_code || ',' ||
rec.os_username || ',' || rec.userhost || ',' ||
REPLACE(rec.sql_text, CHR(10), ' ')
);
END LOOP;
UTL_FILE.FCLOSE(v_file_handle);
-- S3へのアップロード実行
v_sql := 'SELECT rdsadmin.rdsadmin_s3_tasks.upload_to_s3(' ||
'p_bucket_name => :b_bucket, p_prefix => :b_file_name, ' ||
'p_s3_prefix => :b_s3_prefix, p_directory_name => :b_dir' ||
') FROM DUAL';
EXECUTE IMMEDIATE v_sql INTO v_task_id
USING v_s3_bucket_name, v_file_name, v_s3_prefix, v_directory_name;
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(v_file_handle) THEN UTL_FILE.FCLOSE(v_file_handle); END IF;
RAISE;
END;
/
実行コマンド:
プロシージャを作成したら以下でまずは手動で実行してみましょう。
SQL> SET SERVEROUTPUT ON
SQL> EXEC EXTRACT_AND_UPLOAD_AUDIT_LOG;
すると S3 へ audit_logs フォルダが作成され、ファイルが出力されていました。
ファイルの中身は以下のようなものでした。期待通り出力されていそうですね!
3.3.2. 1時間ごとのスケジュール設定
続いて、このプロシージャーを1時間ごとに実行するように設定します。
毎時、固定時間枠の処理が完了するよう、開始時刻から毎時実行するジョブを作成します。
実行コマンド:
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'AUDIT_S3_UPLOAD_JOB',
job_type => 'STORED_PROCEDURE',
job_action => 'EXTRACT_AND_UPLOAD_AUDIT_LOG',
start_date => SYSTIMESTAMP,
repeat_interval => 'FREQ=HOURLY; BYMINUTE=5', -- 毎時5分に実行開始 (直前の完全な1時間枠のログを処理)
enabled => TRUE,
comments => 'Hourly Unified Audit Transfer to S3'
);
END;
/
これが正しく動作するかは、xx:05 の時刻になってみないとわからないですね。その前に SCOTT ユーザーで適当な SQL を実行しておきましょう。
そして、時間になったら、、、、
来ました!!
3.3.3. ハウスキーピング(パージ)ジョブ
最後に、ログ転送が完了した後の UNIFIED_AUDIT_TRAIL の領域肥大化を防ぐためのパージジョブを設定しておきます。これが最後のプロシージャです。
実行コマンド:
BEGIN
DBMS_AUDIT_MGMT.SET_LAST_ARCHIVE_TIMESTAMP(
audit_trail_type => DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED,
last_archive_time => SYSDATE - 3
);
DBMS_AUDIT_MGMT.CREATE_PURGE_JOB (
audit_trail_type => DBMS_AUDIT_MGMT.AUDIT_TRAIL_UNIFIED,
audit_trail_purge_interval => 24, -- 24時間 (1日ごと)
audit_trail_purge_name => 'DAILY_AUDIT_PURGE_JOB',
use_last_arch_timestamp => TRUE
);
END;
/
CREATE_PURGE_JOB を実行すると、実際にはジョブが作成されているようです。
SQL> SELECT job_name, repeat_interval from dba_scheduler_jobs where job_name='DAILY_AUDIT_PURGE_JOB';
何やら登録されていましたね!
これで、24時間おきにジョブが起動し、前回処理した時間(24時間)を経過したログは自動的に削除されると期待されます。(※動作未確認)
4. スケジュール運用の課題:ロストデータ問題(★未対応★)
Hourly Job の設定で、ログ転送の自動化は完了しました。しかし、本番運用において必ず考慮しなければならない課題が残っています。それが「ロストデータ問題」です。
課題:スケジューラが起動に失敗した場合のログ欠損
DBインスタンスの再起動、RDSメンテナンス、あるいは一時的なリソース枯渇など、何らかの理由で 毎時のスケジュールジョブ (AUDIT_S3_UPLOAD_JOB) が実行に失敗した場合、その時間帯(固定された1時間)のログは DB 内に残されたままになり、永久に S3 に転送されないリスクが発生します。
今回の実装は「直前の固定された1時間」を抽出対象としているため、時間が過ぎるとその窓は閉じてしまい、残されたデータは回収されません。
最新のジョブ実行タイミングから最新の転送されたログ期間が判断できるはずなので、それを考慮して再実装やログの保持期間を検討する必要がありそうです。
5. 終わりに
本投稿では、高額になりがちな CloudWatch Logs や管理が面倒そうな DAS ではなく、耐久性の高い、そしてリーズナブルな S3 へ Unified Auditing で取得した監査ログを保存する方法を紹介しました。本番で運用するには、紹介したようなロストデータ問題の解決や実行時の負荷など検討/検証が必要な部分も残っていますが、本投稿でご紹介した方法が選択肢の一つになるケースもあるかもしれません。本投稿が何かのお役に立てればと思います。


























