要点
- テーブル中に記録された個人情報アクセスの監視。
- 開発者並びに特定ユーザが、テーブル中の特定カラムを参照した場合にemail通知を送信する。
AWSを用いて構成した。システム構成図は以下。
構築手順
準備.pgAuditについて
1.PostgreSQLの設定
2.CloudWatch Logsのサブスクリプションフィルター設定
3.SNSトピック作成
4.Lambda関数でメッセージ構築
ex.TerraformによるIaC化
pgAuditについて
PostgreSQL向け監査用ロギングツール。
公式サイトはこちら
本ツールを用いると例えば「○○が○○という操作をしました」「○○カラムを○○ロールが参照しました」というログを証拠として残すことができる。
pgAuditによる取得可能なログの種類
2種類存在する。
- セッション監査ログ
- オブジェクト監査ログ
セッション監査ログ
PostgreSQLのデータベース(CREATE DATABASEで作成したデータベースのこと)を全て監査する場合、ロール(ユーザ)による操作を全て監査する場合はセッション監査ログを用いる。
pgaudit.logに値を設定し、設定した値に従った形式の記録をする。
(例:対象データベースの監査、対象ロールの監査)
例:ロール中の操作を全てログ出力する場合
ALTER ROLE <role_name> SET pgaudit.log='all';
例えば、上記の設定を行うと対象ロール上での操作を全て監査できる。
オブジェクト監査ログ
あるテーブルの特定カラムを参照した場合に使うことが主。
pgaudit.roleにロールを設定し、そのロールに与えられた権限(監視権限)に関する操作を全て記録する。(例:対象テーブルの監査、対象テーブル中のカラムの監査)
例:テーブル中の特定カラムアクセスの監査
オブジェクト監査の場合、pgaudit.role用の監査ロールが必要。
例として監査ロールをrds_pgauditという名前で作成する。他の記事でもこの名前で作成している場合が多いが、名前は自由で良い。
CREATE ROLE rds_pgaudit; -- どんなロール名でも良い
その後、監査対象カラムへのアクセス権限を監査ロールへ与え、監査対象とするロールのpgaudit.roleに監査ロールを設定する。監査対象が複数であってもそれをロールに設定できる。
GRANT SELECT (column_name) ON <table_name> to rds_pgaudit; # table_name.column_nameのselect操作のアクセス権を監査ロールへ付与。
ALTER ROLE <role_name1> set pgaudit.role=rds_pgaudit; # 監査対象のロールに割り当て
ALTER ROLE <role_name2> set pgaudit.role=rds_pgaudit; # 監査対象が複数でも可能
...
設定反映のために、RDS or Auroraを再起動。
上記設定でpgaudit.role=rds_pgauditが設定されたロールについて、対象カラムへの参照したログを取得できる。
CloudWatch Logsのサブスクリプションフィルタ設定
AWS RDS, Auroraの場合、通常CloudWatch Logsがログ出力先となる。
次に、CloudWatch Logsからサブスクリプションフィルタを設定し、宛先とログの条件を設定する。
サブスクリプションフィルタのフィルタパターンは、pgAuditからやってくるログがどのようなログになるかを知っている必要がある。よしなにやって頂く他無いが、一応記載。
(CFnの例)
FilterPattern: '"LOG: AUDIT: OBJECT" SELECT'
としておくと、例えば以下のログがCWLogsに流れた時にフィルターに引っかかる
2023-12-12 10:00:00 UTC:192.168.1.123(50123) postgres@postgres:[12345]:LOG AUDIT: OBJECT,1,1,READ,SELECT,TABLE,public.user,select * from user;,<none>
フィルターに引っかかったログは、lambdaへ流れる。
Lambdaでメッセージ構築
今回はnode_modulesとセットでzip化してデプロイする。
サブスクリプションフィルターによってイベントがlambdaに流れ、eventからログを参照することができる。
const AWS = require('aws-sdk');
const sns = new AWS.SNS();
exports.handler = async (event) => {
const snsParams = {
Message: 'Access!',// 本文
Subject: 'specific column access',// メールタイトル
TopicArn: 'your_sns_topic_arn',
};
try {
await sns.publish(snsParams).promise();
return { statusCode: 200, body: 'Message sent successfully!' };
} catch (error) {
console.error('Error publishing message to SNS:', error);
return { statusCode: 500, body: 'Error publishing message to SNS' };
}
};
SNS設定
トピックを作成して、宛先emailを設定する。
お疲れさまでした。
Terraformによるサンプル
参考まで。