Aurora のアクティビティストリームについて調べてみた。

記事タイトルとURLをコピーする

こんにちは🐱
カスタマーサクセス部の山本です。

以前からある機能なものの、Aurora のアクティビティストリームについて初めて調べたので記事にします。
アクティビティストリームはデータベース操作の監査を行う機能です。
監査ログ機能は他にもあるので、使い分けについて主に調べて記述します。

2019 年の 5 月に Aurora Postgres 向けに出来た機能です。
Amazon Aurora (PostgreSQL 互換) でのデータベースアクティビティストリームのサポートによりリアルタイムのモニタリングが可能に

2020 年の 6 月に、Aurora MySQL にも対応しています。
データベースアクティビティストリームが Aurora (MySQL 互換) で利用可能に

公式ドキュメント:
データベースアクティビティストリーミングの概要 - Amazon Aurora

はじめに

調査内容を順番に長々と書いているので、時間のない方は【まとめ】アクティビティストリームを使うタイミングをみてください。

目次

Aurora のアクティビティストリームの目的

まず、Aurora のアクティビティストリームとは

Aurora Postgres または Aurora MySQL 上のデータベースに向けて実施した操作を、一つ一つイベントとして Json 形式で記録します。データベース操作の監査に利用可能です。
イベントの記録対象はSQL、プリペアドステートメント、組み込み関数、PL/pgSQLの関数、ストアド・プロシージャの実行です。ストアド・プロシージャや、関数内で発行したSQLも記録します。( ドキュメントに "PL/SQL" とあるものの、"PL/pgSQL" の誤記のようです。AWS サポートに確認させていただきました。)

イベントは Kinesis Data Streams に自動的に 24 時間保管されます。
Kinesis Data Streams からイベントを取り出して、必要な場合には管理者に通知することができます。
公式ドキュメント「データベースアクティビティストリーミングの概要 - Amazon Aurora」には、IBM 社の 「Security Guardium」、Imperva 社の 「SecureSphere Database Audit and Protection」 などのコンプライアンス管理アプリケーションと連携して監視・通知できる、とあります。
Kinesis Data Streams は AWS サービスでは、Lambda や、Kinesis Data Firehose と連携できます。それらのサービスを中継して通知を行ったり、イベントを S3 に保管することもできます。
構成図をドキュメントより抜粋します。

参考:

Aurora のアクティビティストリームの目的

上述した特定のコンプライアンス管理アプリケーションと連携して監視・通知するために、Kinesis Data Streams にストリームとして、監査イベントをニアリアルタイムで配信する ことが主たる目的のようです。コンプライアンス管理アプリケーションを使用しない場合は、ユーザー側で Lambda をコーディングして作成するなど、何らかの作り込みが必要です。

Aurora の他の監査ログ機能との比較

アクティビティストリームとは異なる機能で、Aurora クラスターのパラメータグループに監査ログの出力を設定することもできます。 これは、DBエンジンの機能で出力する監査ログ(pgaudit など)を、AWS 上に保管する機能です。

MySQL / Postgres それぞれ設定の仕方が異なります。
DBエンジンの機能で出力する監査ログであるため、出力内容や表示形式がエンジン毎に様々です。形式は MySQL では CSV、Postgres では CSV または テキストから選べます。

その点、アクティビティストリームはエンジンに関わらずほとんど同じ形式で Json フォーマットになるので、複数のデータベースエンジンを使用している際には、監査ログの管理がしやすいでしょう。ただし、アクティビティストリームの場合は、Kinesis Data Streams に保管する際に KMS キーを用いた暗号化を行いますそのため、ログの参照時には復号をしないといけません。

図による比較

Aurora のアクティビティストリームと他の監査ログ機能、それぞれの監視・通知・保管の方法について、図を描きつつ説明します。

DBエンジンの機能で出力する監査ログ(CSV / テキスト)
まず、DBエンジンの機能で出力する監査ログは CloudWatch Logs に出力することができます。暗号化はされないため、CloudWatch Logs ですぐに見ることが出来ます。ログ出力には通常ほとんど遅延はありません。通知をするには CloudWatch Logs をフィルターして、CloudWatch メトリックスとして記録します。メトリックス の値を元に CloudWatch Alarm を使用してメール・チャットなどで通知が可能です。CloudWatch Logs をフィルターする際には正規表現も利用可能です(※1)。ただし、メトリックスフィルター数の上限が一つのロググループに対して 5 となっているため、多数の条件がある場合の通知は実装不可になります(※2)。そのような場合、CloudWatch Logs を検索する Lambda 関数を自前実装することになります。長期保管をする場合は、Kinesis Data Firehose を使って、 S3 にエクスポート出来ます。CloudWatch Logs には監査ログが他のログと混在して出力されているため、 CloudWatch Logs をフィルターして、Firehose に出力し、S3 に保存する必要があります。

アクティビティストリーム( Json )
次に、アクティビティストリームに関しては、上述したように、KMS キーを使って暗号化したうえで Kinesis Data Streams に出力します。 ログ出力は通常ほとんど遅延はありません。ニアリアルタイムにログ出力をします。特定のコンプライアンス管理アプリケーションと連携して監視・通知することができます。また、Kinesis Data Streams から Kinesis Data Firehose 経由で S3 に保管することもできます。ただし、S3 に保管したログを確認するには KMS キーで復号が必要です。特定のコンプライアンス管理アプリケーションで管理できない部分では、KMS キーを用いた復号処理を自前で実装することになるため、Lambda 等を用いたコーディングがどこかで発生します。実装コストが高くなります。ログイベント が json 形式であるため、自前実装する場合にフィルターする処理は、テキストや CSV と比較して容易ではあるでしょう。

※1 メトリクスフィルター、サブスクリプションフィルター、フィルターログイベント、およびライブテールのフィルターパターン構文 - Amazon CloudWatch Logs (パターンマッチは使えないなど制約あり。)
※2 CloudWatch ログクォータ - Amazon CloudWatch Logs

Aurora のアクティビティストリームと他の監査ログ機能との比較表

比較表を作ってみました。

比較項目 DBエンジンの機能で出力する監査ログ アクティビティストリーム
ログ出力先 CloudWatch Logs Kinesis Data Streams
ログ出力の際の暗号化有無 有。Kinesis Data Streams からのログを取得する際に復号が必須。
ログ出力形式 DBエンジンごとに異なる。MySQL は CSV 形式。Postgres はテキストまたは CSV 形式で出力。 Json 形式
監視・通知の手段 CloudWatch Logs をフィルターして、CloudWatch メトリックスとして記録する。メトリックス の値を元に CloudWatch Alarm を呼び出すことで、通知が可能。CloudWatch Logs をフィルターする際には正規表現も利用可能。メトリックスフィルター数の上限が 5 となっているため、多くの条件での通知は実装不可。そのような場合、CloudWatch Logs を検索する Lambda 関数を自前実装することになる。 IBM 社の 「Security Guardium」、Imperva 社の 「SecureSphere Database Audit and Protection」 などのコンプライアンス管理アプリケーションと連携して監視・通知できる。またはユーザーによる作り込み(実装コスト高)
ログの保管 CloudWatch Logs から Kinesis Data Firehose を経由して S3 に保存可能。監査ログが他のログと混ざって出力されるため、 CloudWatch Logs をフィルターして Firehose に出力する必要がある。 Kinesis Data Streams から Kinesis Data Firehose を経由して S3 に保存可能。S3 に保管したログを確認するには KMS キーで復号が必要。(実装コスト高)
パフォーマンス影響 ログの出力が増えることでCPU使用率やディスクI/Oが増加する。パフォーマンスへの影響とコストを考慮し、AUTOVACUUM と AUTOANALYZE がログに記録されないことに留意する。 パフォーマンス影響あり。同期モードと非同期モードがあり、非同期モードの方がパフォーマンス影響は少ない。ログ出力を重視する場合は同期モードを選択する。同期モードは Postgres の場合のみ選択可能。
料金 CloudWatch Logs での保管料金 ほか Kinesis Data Streams での保存料 (最大 24 時間)ほか

公式の比較資料

アクティビティストリームと pgaudit の比較をした AWS の記事もあるので、よければご参照ください。

パート 1 では、アクティビティストリームと pgaudit の比較をしています。理解するために重要な部分を抜粋します。

データベース監査の概要
ここ数年、企業が遵守を求められる規制の数と複雑さは著しく増加しています。業界特有のコンプライアンスには、GDPR、SOX、PCI DSS、HIPAA などがあります。また、情報漏えいに伴う賠償金の金額も増加しており、監査の重要性はこれまで以上に高まっています。

IT 部門は、ニアリアルタイムのビジネス・インテリジェンス・レポートを確実に受けとれるようにして、ビジネス関係者を支援することが重要です。これは、レポートを自動化するシステムを導入し、細心の注意を必要とする重要なイベントが発生したときにアラートされるように設定することを意味します。

ほとんどの場合、データベース監査を推進するのはコンプライアンスです。組織では、ユーザーアクセス、セキュリティアクセス、およびその他の監査可能なデータを監査 することがあります。以下の機能を実現するための仕組みやシステムが必要です:
〜割愛〜
このソリューションには次の利点があります。

ニアリアルタイムのストリームを提供する
ネイティブの AWS サービスおよびサードパーティの監視ツールと統合
データベースアクティビティを詳細にキャプチャする
DBA とセキュリティ担当者の職務を分離する
AWS Key Management Service (AWS KMS) カスタマーマネージドキーを使用して、保存時の監査データを暗号化する
監査データを統一された JSON 形式で提供する

パート 2 では、CloudWatch Logs や Kinesis Data Streams から Lambda を使って復号や通知を行う方法やサンプルコードも紹介しています。理解するために重要な部分を抜粋します。

DBエンジンの機能で出力する監査ログ(CSV / テキスト)

アクティビティストリーム

補足:DBエンジンの機能で出力する監査ログを CloudWatch Logs にも出さない場合の保存期間

DBエンジンの機能で出力する監査ログを CloudWatch Logs にも出さない場合の保存期間は以下です。
CloudWatch Logs に出さない場合、RDS のコンソールからログファイルをダウンロードして参照できます。

Aurora MySQL

参考: Aurora MySQL データベースログの概要 - Amazon Aurora

Aurora MySQL 監査ログは、ファイルサイズが 100 MB に達するとローテーションされ、24 時間後に削除されます。

Aurora Postgres

参考: RDS for PostgreSQL データベースログファイル - Amazon Relational Database Service

デフォルトの設定は 3 日 (4,320 分) ですが、この値を 1 日 (1,440 分) から 7 日 (10,080 分) までの任意の時間に設定できます。

補足:Aurora のアクティビティストリームのイベント例

イベントの例をドキュメント:データベースアクティビティストリーミングのモニタリング - Amazon Aurora より、抜粋します。

  • 例 Aurora PostgreSQL CONNECT SQL ステートメント のアクティビティイベントレコード
{
  "type":"DatabaseActivityMonitoringRecords",
  "version":"1.1",
  "databaseActivityEvents": 
    {
      "type":"DatabaseActivityMonitoringRecord",
      "clusterId":"cluster-4HNY5V4RRNPKKYB7ICFKE5JBQQ",
      "instanceId":"db-FZJTMYKCXQBUUZ6VLU7NW3ITCM",
      "databaseActivityEventList":[
        {
          "startTime": "2019-10-30 00:39:49.940668+00",
          "logTime": "2019-10-30 00:39:49.990579+00",
          "statementId": 1,
          "substatementId": 1,
          "objectType": null,
          "command": "CONNECT",
          "objectName": null,
          "databaseName": "postgres",
          "dbUserName": "rdsadmin",
          "remoteHost": "172.31.3.195",
          "remotePort": "49804",
          "sessionId": "5ce5f7f0.474b",
          "rowCount": null,
          "commandText": null,
          "paramList": [],
          "pid": 18251,
          "clientApplication": "psql",
          "exitCode": null,
          "class": "MISC",
          "serverVersion": "2.3.1",
          "serverType": "PostgreSQL",
          "serviceName": "Amazon Aurora PostgreSQL-Compatible edition",
          "serverHost": "172.31.3.192",
          "netProtocol": "TCP",
          "dbProtocol": "Postgres 3.0",
          "type": "record",
          "errorMessage": null
        }
      ]
    },
   "key":"decryption-key"
}  
  • 例 Aurora PostgreSQL CREATE TABLE ステートメントのアクティビティイベントレコード
{
  "type":"DatabaseActivityMonitoringRecords",
  "version":"1.1",
  "databaseActivityEvents": 
    {
      "type":"DatabaseActivityMonitoringRecord",
      "clusterId":"cluster-4HNY5V4RRNPKKYB7ICFKE5JBQQ",
      "instanceId":"db-FZJTMYKCXQBUUZ6VLU7NW3ITCM",
      "databaseActivityEventList":[
        {
          "startTime": "2019-05-24 00:36:54.403455+00",
          "logTime": "2019-05-24 00:36:54.494235+00",
          "statementId": 2,
          "substatementId": 1,
          "objectType": null,
          "command": "CREATE TABLE",
          "objectName": null,
          "databaseName": "postgres",
          "dbUserName": "rdsadmin",
          "remoteHost": "172.31.3.195",
          "remotePort": "34534",
          "sessionId": "5ce73c6f.7e64",
          "rowCount": null,
          "commandText": "create table my_table (id serial primary key, name varchar(32));",
          "paramList": [],
          "pid": 32356,
          "clientApplication": "psql",
          "exitCode": null,
          "class": "DDL",
          "serverVersion": "2.3.1",
          "serverType": "PostgreSQL",
          "serviceName": "Amazon Aurora PostgreSQL-Compatible edition",
          "serverHost": "172.31.3.192",
          "netProtocol": "TCP",
          "dbProtocol": "Postgres 3.0",
          "type": "record",
          "errorMessage": null
        }
      ]
    },
   "key":"decryption-key"
} 
  • 例 Aurora MySQL CONNECT SQL ステートメントのアクティビティイベントレコード
{
  "type":"DatabaseActivityMonitoringRecord",
  "clusterId":"cluster-some_id",
  "instanceId":"db-some_id",
  "databaseActivityEventList":[
    {
      "logTime":"2020-05-22 18:07:13.267214+00",
      "type":"record",
      "clientApplication":null,
      "pid":2830,
      "dbUserName":"rdsadmin",
      "databaseName":"",
      "remoteHost":"localhost",
      "remotePort":"11053",
      "command":"CONNECT",
      "commandText":"",
      "paramList":null,
      "objectType":"TABLE",
      "objectName":"",
      "statementId":0,
      "substatementId":1,
      "exitCode":"0",
      "sessionId":"725121",
      "rowCount":0,
      "serverHost":"master",
      "serverType":"MySQL",
      "serviceName":"Amazon Aurora MySQL",
      "serverVersion":"MySQL 5.7.12",
      "startTime":"2020-05-22 18:07:13.267207+00",
      "endTime":"2020-05-22 18:07:13.267213+00",
      "transactionId":"0",
      "dbProtocol":"MySQL",
      "netProtocol":"TCP",
      "errorMessage":"",
      "class":"MAIN"
    }
  ]
}
  • 例 Aurora MySQL CREATE TABLE ステートメントのアクティビティイベントレコード
{
  "type":"DatabaseActivityMonitoringRecord",
  "clusterId":"cluster-some_id",
  "instanceId":"db-some_id",
  "databaseActivityEventList":[
    {
      "logTime":"2020-05-22 18:07:12.250221+00",
      "type":"record",
      "clientApplication":null,
      "pid":2830,
      "dbUserName":"master",
      "databaseName":"test",
      "remoteHost":"localhost",
      "remotePort":"11054",
      "command":"QUERY",
      "commandText":"CREATE TABLE test1 (id INT)",
      "paramList":null,
      "objectType":"TABLE",
      "objectName":"test1",
      "statementId":65459278,
      "substatementId":1,
      "exitCode":"0",
      "sessionId":"725118",
      "rowCount":0,
      "serverHost":"master",
      "serverType":"MySQL",
      "serviceName":"Amazon Aurora MySQL",
      "serverVersion":"MySQL 5.7.12",
      "startTime":"2020-05-22 18:07:12.226384+00",
      "endTime":"2020-05-22 18:07:12.250222+00",
      "transactionId":"0",
      "dbProtocol":"MySQL",
      "netProtocol":"TCP",
      "errorMessage":"",
      "class":"MAIN"
    }
  ]
}

アクティビティストリームの制約

アクティビティストリームを使用するには以下の制約をクリアする必要があります。

インスタンスタイプ

インスタンスタイプに Overview of Database Activity Streams - Amazon Aurora に記載の通りの制約があります。

また、Aurora のみで提供している機能で、アクティビティストリーム機能はRDS にはありません。

KMS のサービスエンドポイントとの通信

Aurora のデータベースが KMS のエンドポイントに到達できる必要があります。
Aurora のある VPC 内に、KMS の VPC エンドポイントを作成するのが一番簡単です。
ほかは NAT Gateway を作成して、インターネットへの経路をサブネットに定義する方法、Aurora をパブリック公開する方法があります。

【まとめ】アクティビティストリームを使うタイミング

調査の結果を踏まえて、アクティビティストリームを使うタイミングを列挙します。

  • データベースのコンプライアンスに関するレポートの自動化が必要。IBM 社の 「Security Guardium」、Imperva 社の 「SecureSphere Database Audit and Protection」 などのコンプライアンス管理アプリケーションと連携して監視・通知するとき
  • コンプライアンス担当者とデータベース管理者が異なり、明確な境界線が必要なとき(監査者には Kinesis Data Streams 以降の権限を付与できる)
  • 監査ログを暗号化する必要があるとき
  • Aurora MySQL と Aurora Postgres を使用していて、コンプライアンス担当者が確認するために、統一された JSON 形式の監査ログが必要なとき

感想・まとめ

データベースのコンプライアンスに関するレポートの自動化が必要で、サードパーティのコンプライアンス管理アプリケーションを使うための機能なのかな、という印象を受けました。 ちょっと古いものの、公式さんの比較資料には、Python のサンプルコードもあるので、監視・通知を自前実装する際には試してみると良いかも知れません。

山本 哲也 (記事一覧)

カスタマーサクセス部のエンジニア(一応)

好きなサービス:ECS、ALB

趣味:トレラン、登山(たまに)