[OCI] PL/SQL SDKを使ってAutonomous DatabaseにAuditサービスの監査ログを取得してみた

# はじめに

OCIのAuditサービスでは、デフォルトで監査ログは90日間保存され、ログの保持期間は最長365日間まで設定できます。

366日以上前の監査ログを保管するのにデータベースを使えば管理が楽になると考え、PL/SQL SDKを用いて、PL/SQLからAuditサービスの監査ログにアクセスしてみました。

# 1.事前準備
あらかじめ、以下の情報を確認しておきます。
・rootコンパートメントのOCID
・リージョン識別子（今回はap-tokyo-1）
また、SDKを使用するためのクレデンシャル（資格証明）を作成しておきます。
（今回はMY_SDK_CRED）

# 2.PL/SQLプロシージャの作成
早速、監査ログにアクセスするPL/SQLプロシージャを作成します。
今回作成するプロシージャは、引数として日本時間の日付をYYYY-MM-DD形式で受け取り、対象日の監査ログを取得して表示します。

```
CREATE OR REPLACE PROCEDURE audit_check(check_date IN varchar2)
IS
  loop_flg    NUMBER := 1;
  num         NUMBER := 0;
  page        VARCHAR2(1000) :='';
  start_time  TIMESTAMP WITH TIME ZONE;
  end_time    TIMESTAMP WITH TIME ZONE;
  response          dbms_cloud_oci_ad_audit_list_events_response_t;
  response_body     dbms_cloud_oci_ad_audit_audit_event_tbl;
  json_obj          json_object_t;
  l_keys            json_key_list;

BEGIN
  -- セッションのNLS_TIMESTAMP_TZ_FORMATをRFC 3339に合わせて設定
  EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT=''YYYY-MM-DD"T"HH24:MI:SS"Z"''';
  -- 対象期間の開始時間をUTCで設定
  SELECT TO_TIMESTAMP_TZ(check_date||' 00:00:00.00','YYYY-MM-DD HH24:MI:SS.FF') -9/24 INTO start_time FROM dual;
  -- 対象期間の終了時間をUTCで設定
  SELECT TO_TIMESTAMP_TZ(check_date||' 23:59:59.99','YYYY-MM-DD HH24:MI:SS.FF') -9/24 INTO end_time FROM dual;
  
  -- 複数ページがある場合のループ処理（デフォルトでは1ページあたり100レコード）
  WHILE loop_flg = 1 LOOP
    loop_flg := 0;
    -- Audit情報を取得
    response := DBMS_CLOUD_OCI_AD_AUDIT.list_events (
      compartment_id => 'ocid1.tenancy.oc1..XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
      start_time => start_time,
      end_time => end_time,
      page => page,
      region => 'ap-tokyo-1',
      credential_name => 'MY_SDK_CRED'
    );
    -- responseからresponse_bodyを取得
    response_body := response.response_body;

    -- レスポンスヘッダの出力
    dbms_output.put_line('Headers: ' || CHR(10) ||'------------');
    json_obj := response.headers;
    l_keys := json_obj.get_keys;
    
    FOR i IN 1..l_keys.count LOOP
      dbms_output.put_line(l_keys(i)||':'||json_obj.get(l_keys(i)).to_string);
         -- レスポンスヘッダ内にopc-next-pageがあるか確認
         -- もしある場合は次のページがあるのでpageをセットして再度list_eventsを実行  
      IF l_keys(i) = 'opc-next-page' THEN 
        loop_flg := 1;
        page := RTRIM(SUBSTR(json_obj.get(l_keys(i)).to_string,3),'"');
      END IF;
    END LOOP;

    -- ステータスコードの出力
    dbms_output.put_line('Status Code: ' || CHR(10) || '------------' || CHR(10) || response.status_code);
    dbms_output.put_line(CHR(10));

    -- 取得した監査情報の出力  
    FOR i IN response_body.first .. response_body.last LOOP
      num := num + 1;
      dbms_output.put_line('No.:'||num);
      dbms_output.put_line('Compartment Name:'||response_body(i).data.compartment_name);
      dbms_output.put_line('Event Time:'||TO_CHAR(response_body(i).event_time at time zone 'Asia/Tokyo','YYYY-MM-DD HH24:MI:SS TZD'));
      dbms_output.put_line('Principal Name:'||response_body(i).data.identity.principal_name);
      dbms_output.put_line('Source:'||response_body(i).source);
      dbms_output.put_line('Event Name:'||response_body(i).data.event_name);
      dbms_output.put_line('Resource Name:'||response_body(i).data.resource_name);
      dbms_output.put_line('AD:'||response_body(i).data.availability_domain);
      dbms_output.put_line('Request Action:'||response_body(i).data.request.action);
      dbms_output.put_line('Response Status:'||response_body(i).data.response.status);
      dbms_output.put_line('---------------------------------------');
    END LOOP;
  END LOOP;
END;
/
```
# 3.PL/SQLプロシージャの実行
PL/SQLプロシージャが完成したので、監査ログを取得する日付をYYYY-MM-DD形式で指定して実行してみます。

```
exec audit_check('2020-11-01');
```
実行結果は以下のようになりました。

```
Headers: 
------------
Connection:"close"
Date:"Mon, 02 Nov 2020 13:04:52 GMT"
opc-request-id:"3MPAK484F0/48B8AE98BCFE1B0828CC299876D98B0E/7DE7BB5059FE4A63FD8E
CCD1D0925E5B"
opc-next-page:"v2:AP36_Pj-_fT-9Pn--_zpsK25qKW4sP78_vzh_fzh__2Y_f72_Pz2_PyWsP78_v
zh_f3h_P2Y_Pz2_Pz2_PyWsPyw_Iz9-f789fr8__7__fz__vX_-Pj8jP36_Pj99Pv69Pj5__mMorjh_e
mwrbmopbiw_vz-_OH9_eH8_Zj8_Pb8_Pb8_Jaw_vz-_OH9_eH8_Zj9_vb8_Pb8_Jaw_bD8jP35_Pv1_v
799P38-_r0_f37-_qM_fr8-P799P70-f77_IyiuOH96Q=="
Vary:"Accept-Encoding"
Content-Type:"application/json"
X-Content-Type-Options:"nosniff"
Transfer-Encoding:"chunked"
Status Code: 
------------
200


No.:1
Compartment Name:zeon
Event Time:2020-11-01 00:35:19 JST
Principal Name:oci-optimizer
Source:Compartments
Event Name:ListRegionSubscriptions
Resource Name:
AD:AD1
Request Action:GET
Response Status:200
---------------------------------------
No.:2
Compartment Name:
Event Time:2020-11-01 23:51:17 JST
Principal Name:XXX@XXXXXX.com
Source:Compartments
Event Name:GetTenancy
Resource Name:XXX
AD:AD1
Request Action:GET
Response Status:200
---------------------------------------
                 :
                 :
Headers: 
------------
Connection:"close"
Date:"Mon, 02 Nov 2020 13:05:11 GMT"
opc-request-id:"3UEPV8CA12/6E630A5875E90E4FC0DAB6295D56E431/F60307B986E32726F91B
4EAADDD8886E"
Vary:"Accept-Encoding"
Content-Type:"application/json"
X-Content-Type-Options:"nosniff"
Transfer-Encoding:"chunked"
Status Code: 
------------
200


No.:801
Compartment Name:zeon
Event Time:2020-11-01 23:52:31 JST
Principal Name:XXXX@XXXXXX.com
Source:ObjectStorage
Event Name:GetNamespace
Resource
Name:/n/?compartmentId=ocid1.tenancy.oc1..XXXXXXXXXXXXXXXXXXXXXXXXXXX
AD:NRT-AD-1
Request Action:GET
Response Status:200
---------------------------------------
No.:802
Compartment Name:zeon
Event Time:2020-11-01 23:54:01 JST
Principal Name:oci-optimizer
Source:Compartments
Event Name:ListRegionSubscriptions
Resource Name:
AD:AD1
Request Action:GET
Response Status:200
---------------------------------------


PL/SQL procedure successfully completed.

Elapsed: 00:00:20.943
````
無事、11月1日の監査ログを取得することができました。

# まとめ
今回はPL/SQL SDKを用いて、Autonomous DatabaseからAuditサービスの監査情報にアクセスしてみました。

PL/SQL内で監査情報を取得できるので、監査情報をデータベースに保存しておくことも可能になります。

日次でこちらのプロシージャを自動を実行して前日分の監査ログをデータベースに保存することで、366日以上前の監査ログも保管しておくことが可能になりますね。