株式会社ネットワールドのエンジニアがお届けする技術情報ブログです。
各製品のエキスパートたちが旬なトピックをご紹介します。

GitとCI/CDに関する知識ゼロのSEが、GitLab.comの監査イベントをCloudWatch Logsに出力する話

皆様こんにちは。SEの小池と申します。

会社で新しいサービスやソフトウェアを導入する際、

あ、この製品 "監査ログ" 取得できるんだ。とりあえずSIEMかsyslogに保存しておくか・・・。

と思われたこと、ございませんか?

実はGitLabにも Audit Events という監査イベントを記録・閲覧する機能があります。
今回は GitLabの Audit Events 機能で出力した監査イベントをAmazon CloudWatch Logsに出力する方法 をご紹介いたします。

本記事の対象の方

  • GitLab.comの監査イベントをAmazon CloudWatch Logsに保存したいと思っていらっしゃる方。
  • GitLabの監査イベントに関する情報を収集中の方。

今回のブログのゴール

このブログのゴールはこちらです。


今回のゴール
  • GitLabの監査イベントの概要を把握する。
  • GitLab.comの監査イベントをAmazon CloudWatch Logsに出力する。

このブログをお読みいただくにあたっての事前ご連絡事項

  • 本記事はSaaS版 GitLab.com (Enterprise Edition 15.8.0-pre) における仕様をベースに記載しております。それ以外のエディションやバージョンではこの記事に記載の通りではない可能性がございます。
  • 本記事のGitLab.comのGUIは日本語にローカライズした状態で掲載しております。それ以外の言語をご利用の方は適宜読み替えてください。
  • 本記事では3rdパーティ製のローコード統合プラットフォームであるPipeDreamを使用しています。PipeDreamは2023/01/06時点の仕様で記載しております。
  • 本記事の後半ではAudit Event Streaming機能を使ってGitLab.comの監査イベント取得の実装手順を記載しておりますが、この実装方法はSaaS版 GitLab.comのみが対象であり、Self-Managed版 (オンプレミス版) は対象外です。

GitLabのAudit Events (監査イベント) の概要

GitLabのAudit Events (監査イベント) では、グループやプロジェクトの削除や公開範囲 (visibility) の変更等、重要なアクションを追跡・閲覧することが可能です。

本機能はティア Premium 以上のSaaS版 (GitLab.com) 又は ティア Premium 以上のSelf-Managed版 (オンプレミス版) で利用可能です。
参考:Audit events | GitLab

GitLabのAudit Events (監査イベント) はインスタンスレベル、グループレベル、プロジェクトレベルが存在し、それぞれ出力される内容が異なります。
参考:Audit events - Available audit events | GitLab

インスタンスレベルのAudit Eventsにはグループレベル 及び プロジェクトレベルのAudit Eventsは含まれず、グループレベルのAudit EventsにはプロジェクトレベルのAudit Eventsは含まれません。
また、インスタンスレベルのAudit Eventsは、Self-Managed版 (オンプレミス版) でのみ利用可能です。
Audit Eventsの保存を検討なさっている場合はこれらの仕様に留意いただき、必要なAudit Eventsを漏れなく取得できる方法をご検討ください。

Audit Events (監査イベント) は、SaaS版かSelf-Managed版かを問わず、ティア Premium 以上を利用している場合は自動的にON (利用可能な状態) になっている機能です。

Audit Events (監査イベント) はGUIもしくはAPIで閲覧することが可能です。
GUIの場合、グループ 及び プロジェクトレベルのAudit Eventsは [セキュリティとコンプライアンス] > [監査イベント] から閲覧できます。
(下図はグループレベルのAudit Eventsの閲覧画面ですが、プロジェクトレベルのAudit Eventsも[セキュリティとコンプライアンス] > [監査イベント] から閲覧できます。)

GitLabのAudit Event Streamingの概要

Audit Event Streamingは、最上位 (ルート) グループのストリーミング先を設定することで、最上位 (ルート) グループ、サブグループ、およびプロジェクトに関するすべてのAudit Events (監査イベント) をJSON形式で受信できるAPIストリーミング機能です。

本機能はティア Ultimate 以上のSaaS版 (GitLab.com) 又は ティア Ultimate 以上のSelf-Managed版 (オンプレミス版) で利用可能です。
参考:Audit event streaming | GitLab

前章で述べた通り、通常のAudit Eventsはインスタンスレベル、グループレベル、プロジェクトレベルでそれぞれ内容が異なりましたが、Audit Event Streaming機能であれば最上位 (ルート) グループのストリーミング先を設定するだけで、そのグループと傘下のサブグループとプロジェクトのAudit Eventsを全て取得することが可能です。便利!

このブログで実装する処理の概要

GitLab.comの Audit Event Streaming 機能を使って、監査イベントをAmazon CloudWatch Logsに取得していきます。

本ブログで実装する処理の流れは以下の図の通りです。

このブログで紹介する方法を実装するには以下を全て満たす必要があります。

本ブログで紹介する実装方法の前提
  • GitLab.comを利用している。
  • ティアUltimateが適用された最上位 (ルート) グループが存在する。

また、このブログで紹介する方法では、GitLab.com以外に以下の外部サービスを利用します。

GitLab.com以外で利用する外部サービス
  • Amazon Web Service - CloudWatch Logs
  • PipeDream

このブログに記載した設定を実装することで外部サービスで課金が発生する可能性が有ります。
2022/12/26現在、本ブログの内容を実装するにあたりPipeDreamはFreeプランで問題ありません。
ただし、PipeDreamのFreeプランは月10,000回の呼び出ししかできません。
監査イベントが多数発生する環境での実装は、あらかじめ一か月に発生する監査イベントの数を確認してから実装することをお勧めいたします。

Step1: Amazon CloudWatch Logsの準備

PipeDreamでワークフローを作成する前に、先にAmazon CloudWatch Logsでロググループとログストリームを作成します。
作成するロググループ と ログストリームの各種設定は全て任意です。
このブログでは以下の表の通り作成致します。

表1. CloudWathc Logsのロググループとログストリームの設定
設定項目 設定値
リージョン アジアパシフィック (東京) ap-northeast-1
ロググループ名 Gitlab-AuditEvents-BLOG
ロググループ名 sample-gitlab-auditevents-blog

Step2: AWS IAMユーザーの準備

Amazon CloudWatch LogsのログストリームにイベントをPUTする際に、アクセスキーIDシークレットアクセスキーを使用します。
このアクションに必要な権限を保有するIAMユーザーのアクセスキーIDとシークレットアクセスキーをご準備ください。

このブログではAWS管理ポリシーである CloudWatchLogsFullAccess をアタッチしたユーザーを用意しています。
このポリシーは必要最小限の権限ではないため、実際に本ブログで紹介する方法を実装する場合は、必要最小限の権限をご確認いただけますようお願い申し上げます。

Step3: PipeDreamのワークフロー作成

PipeDreamでワークフローを作成します。
PipeDreamのアカウントを以下のURLであらかじめ作成したうえで、続きの手順を実施してください。
参考:Connect APIs, Remarkably Fast - Pipedream

Step3-1: GitLab.comとAWSの認証設定

最初にGitLab.comのAudit Eventsを取得するステップを追加します。

PipeDreamのTOP画面で、[Workflows] > [New +] をクリックします。

ステップの検索ボックスで gitlab 等と入力すると [GitLab] が候補に表示されるので、それをクリックします。

再び検索ボックスで audit 等と入力すると New Audit Event (Instant) というステップが表示されるので、それをクリックします。

Gitlab Account欄の [Connect a Gitlab account] をクリックします。

別ウィンドウでGitLab.comのサインイン画面が表示されるので、Audit Eventsを取得する予定のグループに権限 Owner を所有するアカウントでサインインします。

PipeDreamを承認して、アカウントを使用することへの確認メッセージが表示されます。
[Authorize] をクリックします。

再度、Gitlab Account欄の [Connect a Gitlab account] をクリックします。

表示が [Select a Gitlab account...] に変わるので、それをクリックします。
すると、プルダウンに先ほど GitLab.com にサインインしたアカウントが表示されるので、それを選択します。

Group ID欄の [Select an option...] をクリックすると、先ほどのGitLab.comのアカウントが所属する最上位 (ルート) グループの一覧が表示されます。
Audit Eventsを取得したい最上位 (ルート) のグループ名をクリックします。
なお、この際指定した最上位 (ルート) グループにUltimateライセンスが適用されていない場合は、後続の手順に進むことができません。

[Create source] をクリックします。

そのまましばらく待つと、テストイベントが生成されます。
[Select event...] をクリックし、生成されたテスト用イベントをクリックします。

[Exports] タブでテスト用のイベントの詳細が表示されることを確認します。

この画面のまま次の手順を実施します。

Step3-2: イベント作成日時をUNIX時間に変換処理

Amazon CloudWatch Logsの仕様にあわせて、GitLab.comから取得したイベント作成時刻 created_at をUNIX時間 (ミリ秒) に変換します。

前の手順で作成したGitLab.comのAudit Eventsを取得するステップの下に表示されている [Continue] をクリックします。

[Python] をクリックします。

[Run Python code] をクリックします。

[Code] セクションを開き、デフォルトで入力されているサンプルコードを全て削除します。

[Code] セクションに、GitLab.comから取得したAudit Eventsの作成時刻を、UNIX時間 (ミリ秒) に変換するコードを記載します。
コードのサンプルは以下の通りです。

def handler(pd: "pipedream"):
  # イベント作成時刻を時刻フォーマットに変換 
  create_time = pd.steps["trigger"]["event"]["created_at"]

  import pandas as pd
  return(pd.to_datetime(create_time).timestamp() * 1000)

[Test] をクリックします。

[Exports] タブに表示されている内容に、13桁の数字 (ミリ秒単位のUNIX時間) が表示されていることを確認します。

最後に、このステップに任意の名前を付けておきます。
ここでは例として python_Get_Epoch_Time とつけておきます。

この画面のまま次の手順を実施します。

Step3-3: Amazon CloudWatch Logsへの出力処理

Amazon CloudWatch Logsにイベントを出力するためのステップを作成します。

前の手順で作成したUNIX時間 (ミリ秒) を取得するステップの下に表示されている [+] をクリックします。

検索ボックスに aws 等と入力すると候補に [AWS] と表示されるので、それをクリックします。

続けて検索ボックスに cloudwatch 等と入力すると候補に [CloudWatch Logs - Put Log Event] と表示されるので、それをクリックします。

AWS Account欄の [Connect an AWS account] をクリックします。

事前に作成していたAWS IAMユーザーの、アクセスキーID、シークレットアクセスキーを設定します。
ニックネームは任意で指定します。
[SAVE] をクリックします。

事前に作成していたAmazon CloudWatch Logsのリージョン、ロググループ名、ログストリーム名を以下の表に通りに設定します。

表2. データストアに保存したNextTokenを取得するステップの設定
設定項目 設定値
AWS Region 事前にCloudWathc Logsのロググループを作成したリージョン。
このブログの場合はAsia Pacific (Tokyo) - `ap-northeast-1`
CloudWatch Log Groups 事前にCloudWathc Logsのロググループ名。
このブログの場合はGitlab-AuditEvents-BLOG
CloudWatch Log Streams 事前にCloudWathc Logsのログストリーム名。
このブログの場合はsample-gitlab-auditevents-blog

Message欄は実際にAmazon CloudWatch LogsのログストリームにPUTする文字列です。
このブログでは例として、可読性も踏まえて改行ありのJSON形式で指定します。
ワークフローの一番最初のステップ名がデフォルトである場合の例です。ステップ名を変更している場合は以下の通りではありません。

{ 
   "cloudwatch_logs_put_log_event": {}, 
   "trigger": { 
       "event": { 
            "id": {{steps.trigger.event.id}}, 
            "author_id": {{steps.trigger.event.author_id}}, 
            "entity_id": {{steps.trigger.event.entity_id}}, 
            "entity_type": "{{steps.trigger.event.entity_type}}", 
            "details": { 
                "author_name": "{{steps.trigger.event.details.author_name}}", 
                "author_class": "{{steps.trigger.event.details.author_class}}", 
                "target_id": {{steps.trigger.event.details.target_id}}, 
                "target_type": "{{steps.trigger.event.details.target_type}}", 
                "target_details": "{{steps.trigger.event.details.target_details}}", 
                "custom_message": "{{steps.trigger.event.details.custom_message}}", 
                "ip_address": "{{steps.trigger.event.details.ip_address}}",
                "entity_path": "{{steps.trigger.event.details.entity_path}}"
            }, 
            "ip_address": "{{steps.trigger.event.ip_address}}", 
            "author_name": "{{steps.trigger.event.author_name}}" ,
            "entity_path": "{{steps.trigger.event.entity_path}}", 
            "target_details": "{{steps.trigger.event.target_details}}", 
            "created_at": "{{steps.trigger.event.created_at}}", 
            "target_type": "{{steps.trigger.event.target_type}}", 
            "target_id": {{steps.trigger.event.target_id}}, 
            "event_type": "{{steps.trigger.event.event_type}}"
        }, 
        "context": { 
           "id": {{steps.trigger.context.id}}, 
           "ts": "{{steps.trigger.context.ts}}", 
           "pipeline_id": {{steps.trigger.context.pipeline_id}}, 
           "workflow_id": "{{steps.trigger.context.workflow_id}}", 
           "deployment_id": "{{steps.trigger.context.deployment_id}}", 
           "source_type": "{{steps.trigger.context.source_type}}", 
           "verified": {{steps.trigger.context.verified}}, 
           "hops": {{steps.trigger.context.hops}}, 
           "test": {{steps.trigger.context.test}}, 
           "replay": "{{steps.trigger.context.replay}}", 
           "owner_id": "{{steps.trigger.context.owner_id}}", 
           "platform_version": "{{steps.trigger.context.platform_version}}", 
           "workflow_name": {{steps.trigger.context.workflow_name}}, 
           "resume": {{steps.trigger.context.resume}}, 
           "trace_id": "{{steps.trigger.context.trace_id}}"
        } 
    }
}

Timestamp欄に、前のステップで生成したAudit Eventsが作成されたUNIX時間 (ミリ秒) をJSON形式のパスで指定します。
このブログのままにステップを作成している場合、本項目は{{steps.python_Get_Epoch_Time.$return_value}}になります。

[Test] をクリックします。

下図のように Successfully sotred log と表示されることを確認します。

AWSのコンソールでCloudWatch LogsのログストリームにメッセージをPUTできたか確認します。
AWSのコンソールから [CloudWatch] > [ログ] > [ロググループ] > [事前に作成しておいたロググループ名] > [事前に作成しておいたログストリーム名] を開き、JSON形式のメッセージ (GitLab.comのAudit Events) が存在することを確認します。

PipeDreamのワークフロー編集画面は閉じずに、次の手順を実施します。

Step3-4: NextTokenをデータストアに記録する処理

Amazon CloudWatch Logsの仕様で、同じログストリームにメッセージをPUTする場合は、前のメッセージをPUTした際のレスポンスにある NextToken (表記はnextToken) という値を次のメッセージをPUTする際に付与する必要があります。
このブログではこの NextToken をPipeDreamのデータストアに格納し、次のイベントをPUTする際にそれを参照できるようにします。

前の手順で作成したステップの下に表示されている [+] をクリックします。

[Data Stores] をクリックします。

検索ボックスに add 等と入力すると [Add or update single record] と表示されるので、それをクリックします。

Data store欄の [Create a store] をクリックします。
Name欄に任意のデータストアの名前を入力し、[Save] をクリックします。
(Amazon CloudWatch LogsのNext Tokenを格納していることが分かるような名前が良いかと存じます。)

別ウィンドウ もしくは 別タブでPipeDreamのメニューを開きます。
[Data stores] をクリックすると先ほど作成したデータストアがあるので、その名前をクリックします。

[ADD] をクリックします。

Amazon CLoudWatch LogsのNext Tokenを格納するキーを作成します。
任意のキー名を入力し、値 (Value) には何も指定せず、緑色のチェックマークをクリックします。

ステップを作成していたタブ (もしくはウィンドウ) に戻ります。
key欄で先ほど作成したキー名を指定します。

Value欄に、前のステップで取得した nextSequenceToken を指定します。

Value欄に指定した値を、ダブルクオーテーションで囲います。
(これをしないとNextTokenが数値扱いになり、データストアに浮動小数点表記で格納され、結果的に桁落ちが発生します。)

[Test] をクリックします。

結果が Success となることを確認します。
また、return_value の value に表示されている数値が浮動小数点表記になっていないことを確認します。

別ウィンドウ もしくは 別タブでPipeDreamのメニューを開きます。
[Data stores] > [先ほど作成したデータストア名] をクリックし、キーの値 (value) に nextToken が入っていることを確認します。

PipeDreamのワークフロー編集画面に戻り、次の手順を実施します。

Step3-5: データストアに格納したNextTokenの読み取り処理

PipeDreamのデータストアに格納されたNextTokenを読み取るステップを作成します。

UNIX時間 (ミリ秒) を取得するステップと、Amazon CloudWathc LogsにメッセージをPUTするステップの間にある [+] をクリックします。

[Data Stores] をクリックします。

検索ボックスに get 等と入力すると候補に [Get record (or create one if not found) ] と表示されるので、それをクリックします。

以下の通り設定します。

表3. データストアに保存したNextTokenを取得するステップの設定
設定項目 設定値
Data Store 前の手順で作成したデータストア名。
このブログの場合はAmazonCloudWatchLogs-NextToken
Key 前の手順で作成したNextTokenを格納するキー名。
このブログの場合はnexttoken
Create a new record if the key is not found? YES or NO を任意に指定。
このブログでは例としてNoを指定。

[Test] をクリックします。

Success と表示されること、及び、Exports タブの $return_value でデータストアに格納しているNextTokenを取得できていることを確認します。

この画面のまま次の手順を実施します。
もうちょっとで完成です!

Step3-6: Amazon CloudWatch LogsへのリクエストにNextTokenを追加する

最後に、Step3-3で作成したAmazon CloudWatch LogsへのPUTリクエストのステップに、データストアから取得したNextTokenを追加します。

Step3-3で作成したステップ (Amazon CloudWathc LogsにイベントをPUTする処理) の [CONFIGURE] を展開します。

一番下にある [Optional Fields] > [ + Sequence token] をクリックします。

Step3-5 で作成したステップのレスポンスに含まれるNextTokenを指定します。

ワークフローはこれで完成です!
ワークフロー編集画面の右上にある [Deploy] をクリックしてワークフローをデプロイします。

Step4: 動作確認

実際にGitLab.comで監査イベントが発生するアクションを実施して、その監査イベントがAmazon CloudWatch Logsに出力されることを確認します。

前述の通りGitLabの監査イベントはインスタンスレベル・グループレベル・プロジェクトレベルが存在しますが、このパイプラインで取得できる監査イベントは、対象の最上位 (ルート) グループ 及び その配下に含まれるすべてのサブグループ 及び プロジェクトで発生した監査イベントです。
したがって動作確認では、グループレベルの監査イベントとプロジェクトレベルの監査イベントの両方を試験的に発生させ、それらの両方がAmazon CloudWatch Logsに出力されることを確認しましょう。

監査イベントで出力されるイベントはGitLab Docsに一覧があります。
グループレベルの監査イベントはこちら、プロジェクトレベルの監査イベントはこちらをご参照ください。

例として、こちらは対象の最上位 (ルート) グループ配下にあるプロジェクトからzip形式でリポジトリをダウンロードした際に発生する監査イベントです。

今回のブログに記載したまま実装すると、Amazon CloudWatch Logsではこんな感じに出力されます。

最後に

この度はGitもCI/CDもよくわかっていないど素人SEによるGitLab検証ブログをお読みいただき、誠にありがとうございます。
このブログの目標は以下のとおりでしたが、皆さまはいかがでしたでしょうか。


今回のゴール
  • GitLabの監査イベントの概要を把握する。
  • GitLab.comの監査イベントをAmazon CloudWatch Logsに出力する。

監査イベントはPremium以上、Audit Event StreamingはUltimateでご利用いただける機能です。
今回は出力処理までPipeDreamで簡単に完結させたい気持ちがあったため、AWSのCloudWatch Logsのログストリームを出力先に選びましたが、他にも様々な出力先を指定することができます。
需要がありそうであれば、AWS S3やSIEM等に出力するブログも書こうかなと存じます。予定は未定ってやつですが…。
最後にどうしても一言・・・。筆者はPythonもAPIもJSON形式も苦手なので、この記事を書くのはとてつもなく大変でしたっ!!!

この記事がGitLabを触り始めた方の一助となれば幸いにございます。


GitLabに関するお問い合わせは、以下のフォームからお願い致します。
GitLab製品 お問い合わせ

今までのGitLabの検証ブログはこちらです。
GitLab カテゴリーの記事一覧 - ネットワールド らぼ

GitLab操作デモ動画 (基本編) を作ってみました。(音声の録音は自宅でiPhoneのボイスメモ使うという超低クオリティですが…。)
つたない内容ではありますが、ご興味がおありでしたら是非ご視聴いただければと存じます。

www.youtube.com