#はじめに
Google Apps Script
、GoogleFormと連携して、アンケートへの自動返信メールを構築できたりといろいろ便利ですよね。
今回は Google Apps Script
を活用して、Firebaseの監査ログ収集とアラート通知を構築する手順について纏めています。
方針
Firebase Management にて監査ログが取得可能です。
また、ログは自動的に Cloud Loggingへ出力されています。
上記の前提のもと、
- BigQueryへCloud Loggingのエクスポート設定を追加し、監査ログを永続化する
- Google Apps Scriptから不正アクセスなどのイベントをBigQueryへクエリを実行して検知し、Slackなどに通知する
でログの保存、監視を構築していきます。
設計
1. ログの種類
リファレンス:
監査ログの種類:
- ログエントリ自体。LogEntry 型のオブジェクト
- logName: プロジェクト ID と監査ログのタイプを保持
- resource: 監査対象オペレーションのターゲットを保持
- timeStamp: 監査対象オペレーションの時刻を保持
- protoPayload: 監査対象情報を保持
- 監査ログデータ。ログエントリの protoPayload フィールドに保持される AuditLog
- サービス固有の監査情報(オプション)。AuditLog オブジェクトの serviceData フィールドに保持されるサービス固有のオブジェクト
料金:
- Stackdriver Logging の無効にできない監査ログ(すべての管理アクティビティ監査ログを含む)は無料
ログのエクスポート:
Stackdriver Loggingから、ログを次の宛先にエクスポート
- BigQuery → 永続化
データの無料枠:
- Stackdriver : 30日間保存されるログサイズ50GB
- BigQuery : クエリ使用量1TB, ログ保存サイズ10GB
2. ログの永続化
下記リファレンスを参照し、Stackdriver Loggingから、ログをBigQueryにエクスポートする設定を追加します。
Google Cloud Console でのログのエクスポート
3.ログの通知
BigQueryへエクスポートしたログを、1時間毎に確認し、Slackへアラート通知する仕組みをGoogleAppsScriptを用いて構築していきます。
構築
1. ログの永続化
ログシンク
環境ごとにログシンクを作成し、ログをBigQueryに出力する設定します。
BigQuery:
環境ごとにデータセットを作成し、ログを永続化していきます。
テーブル構造:
永続化ログのテーブル概要と、アラート条件は下記の通りです。
テーブル名 | 概要 | アラート条件 |
---|---|---|
cloudbuild | CI/CDサービスの実行ログ | 対象外 |
bigquerydatatransfer_googleapis_com_transfer_config | LogginなどからBigqueryへのデータ転送ログ | 対象外 |
cloudaudit_googleapis_com_activity | GCPリソースへのアクティビティログ | 対象外 |
cloudaudit_googleapis_com_data_access | GCPリソースへのアクセスログ | 社外からのアクセスが監視間隔内に1回以上 |
cloudfunctions_googleapis_com_cloud_functions | CloudFunctionsの実行ログ | 監視間隔内の実行エラーが規定回数以上 |
2.ログの通知
設計
2件の監視対象に対し、アラート通知の仕組みを構築していきます。
1. GCPリソースへのアクセスログ
GCP上のデータアクセスのaudit_logの異常検知 して、Slack通知
対象テーブル:cloudaudit_googleapis_com_data_access(GCP上のデータアクセスのaudit_log)
エラーの条件:protopayload_auditlog.authenticationInfo.principalEmailが社外
1時間以内に社外メンバがリソースにアクセスした回数をカウントするクエリ
SELECT
count(*) as result
FROM
`[プロジェクト名].[データセット名].cloudaudit_googleapis_com_data_access`
WHERE
TIMESTAMP(timestamp) >= TIMESTAMP_ADD(timestamp, INTERVAL 1 HOUR)
AND ENDS_WITH(protopayload_auditlog.authenticationInfo.principalEmail, '[ドメイン]') IS FALSE
2.CloudFunctionsのエラーレートが上がっていることを検知して、Slack通知
CloudFunctions、通常はエラーが発生起きないので、エラーカウントが1時間以内に10以上発生している場合、
何らかの障害が起きていると判定する
対象テーブル:cloudfunctions_googleapis_com_cloud_functions(CloudFunctionsの実行ログ)
エラーの条件:textPayload の 「failureCount: 1,」のカウントが10以上
■1時間以内に、textPayload の 「failureCount: 1,」が発生した回数をカウントするクエリ
SELECT
count(*) as result
FROM
`[プロジェクト名].[データセット名].cloudfunctions_googleapis_com_cloud_functions`
WHERE
TIMESTAMP(timestamp) >= TIMESTAMP_ADD(timestamp, INTERVAL 1 HOUR)
AND ENDS_WITH(textPayload, 'failureCount: 1,') IS TRUE
実装
Googleスプレッドシートでマスタを用意
下記のようにGoogleスプレッドシートで監視用マスタを設定していきます。
No | project | category | limit | query |
---|---|---|---|---|
1 | DEV | AccessCheck | 0 | SELECT count(*) as result FROM... |
2 | STG | ErrorRateCheck | 10 | SELECT count(*) as result FROM... |
監視用マスタを設定したGoogleスプレッドシートからスクリプトエディタを開き、Google Apps Scriptの編集画面を開きます。
事前にGoogle Apps ScriptからBigQueryを操作するためのサービスを追加しておきます。
Googleスプレッドシートの値を読み取ってBigQueryへクエリを発行し、閾値を超えていたらSlackへアラート通知する処理をGoogle Apps Script で構築します。
/**
* GCP Alert main function
*/
function main(){
const spreadsheet = SpreadsheetApp.getActive();
const data = spreadsheet
.getSheetByName('master')
.getDataRange()
.getValues();
readMaster(data);
}
/**
* Read Master Spreadsheet and call runQuery()
*/
function readMaster(data) {
try {
if (data.length > 1) {
for (var i = 0; i < data.length; i++) {
if(i == 0){
continue; // 1行目はスキップ
}
runQuery(data[i][1], data[i][2], data[i][3], data[i][4]);
}
}
}
catch(e) {
Logger.log(e);
}
}
/**
* Runs a BigQuery query and check logs
*/
function runQuery(project, category, limit, query) {
const projectId = 'synq-production';
const request = {
query: query,
useLegacySql: false
};
const queryResults = BigQuery.Jobs.query(request, projectId);
const jobId = queryResults.jobReference.jobId;
// Check on status of the Query Job.
const sleepTimeMs = 500;
while (!queryResults.jobComplete) {
Utilities.sleep(sleepTimeMs);
sleepTimeMs *= 2;
queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId);
}
// Get all the rows of results.
const rows = queryResults.rows;
while (queryResults.pageToken) {
queryResults = BigQuery.Jobs.getQueryResults(projectId, jobId, {
pageToken: queryResults.pageToken
});
rows = rows.concat(queryResults.rows);
}
if (rows) {
// Append the results.
const data = new Array(rows.length);
for (var i = 0; i < rows.length; i++) {
const cols = rows[i].f;
data[i] = new Array(cols.length);
for (var j = 0; j < cols.length; j++) {
data[i][j] = cols[j].v;
}
}
if(parseInt(data[0][0], 10) > parseInt(limit, 10)){
Logger.log("gcp alert check comp ->(: " + "プロジェクト名: " + project + ", カテゴリ: " + category + ":) check gcp log!!");
postSlack("GCPログを確認してください", project, category);
}else{
Logger.log("gcp alert check comp ->(: " + "プロジェクト名: " + project + ", カテゴリ: " + category + ":) no alert!!");
}
} else {
Logger.log('No rows returned.');
}
}
/**
* Send Notification to Sack WebHook
*/
function postSlack(message, project, category) {
//payload
const payload = {
'username' : "Notification",
'text' : 'gcp alert check comp ->: ' + 'プロジェクト名: '+ project + ', カテゴリ: ' + category + ', メッセージ: ' + message,
'channel' : "#alerts",
'icon_emoji': ":chart_with_upwards_trend:"
};
const options = {
'method' : 'post' ,
'contentType' : 'application/json' ,
'payload' : JSON.stringify(payload),
};
// Webhook URL
const url = '[SlackのWebhook URLを設定]';
UrlFetchApp.fetch(url, options);
}
作成した処理をタイマーで実行できるように設定しておきます。
#最後に
以上、Google Apps Scriptを用いてFirebaseの監査ログ収集とアラート通知を構築する手順でした。
処理を分岐したり、データ加工などが必要になってくると、Pub/Sub
+ CloudFunctison
で構築したほうが良いですが、簡単な監視 → アラート通知ならGoogle Apps Scriptだけで構築できてしまいます。
うまく使い分けて行きたいところです。
私が所属している株式会社クアンドではエンジニア募集中です。
地域産業のアップデートをミッションに、製造業や建設業など「現場」の課題を解決するためのアプリケーションを開発しています!
私もフルフレックス&フルリモートで働いています。
興味のある方はWantedlyの募集ページをご覧ください!