0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cloudflare Logpush を Syslog に変換して ALog Cloud に連携する

Last updated at Posted at 2024-08-30

参考:ALog Cloud とは

無料トライアルを活用させていただきました。

ALogはオンプレからクラウドまで、あらゆるシステムのログデータを収集・分析・保管する純国産のSIEM製品です。
“わかりやすさ”を追及したALogは、あらゆるITシステムのログを自動で収集、一元的に管理するSIEM製品です。
「セキュリティの難しいをカンタンに」を、コンセプトに専門知識やノウハウがなくとも、誰でもカンタンに高度なログ分析を実現。
企業がかかえる課題に沿って、幅広いシーンで活用できるのが特徴です。

TCP Workers を使って ALog Syslog Receiver に Syslog 送信

ALog Cloud のログ取り込み方式を考慮し、以下のような構成を実装します。

Syslog のプロトコル特性上、認証の機構が限られるため、 送信元 IP アドレスを制限する等、Syslog Receiver の公開には注意が必要です。また、通信を暗号化するには TLS オプションを使う必要があります。

4.1.8. TLSを使用してsyslogを受信したい — ALog Syslog Receiver User Guide 1.3.0 ドキュメント

参考: ALog Syslog Receiver とは

ALog Syslog Receiverは、各機器から送信されるSyslogを受信してファイル出力する製品です。 各機器のSyslogデータをSyslog形式のまま、もしくはCSV形式にしてファイル化します。
ALog Syslog Receiverによってファイル出力されたデータは、ALog EVAで取り込み、用意された専用の標準テンプレートやユーザー自身で作成したテンプレートによってマッピング処理を行います。

image.png

ALog Syslog Receiver 準備

以下のリンクから ALog Syslog Receiver をダウンロードしてインストールします。

image.png

タスクマネージャーやサービスでプロセスが稼働していることを確認します。

image.png

ALog Syslog Receiver の再起動が必要な場合には、以下の画面から実施できます。

image.png

TCP Workers 準備

Cloudflare Workers は Outbound TCP 接続をすることができます。

今回は TCP 514 ポートを宛先として Syslog メッセージを送ります。

後でマッピング設定をスムーズにおこなうために Syslog メッセージのフォーマットは RFC 3164(<%PRI%>%timegenerated% %HOSTNAME% %syslogtag%%msg)を使います。

ALog EVAで処理可能なSyslogはRFC3164に準拠したもので、「日時」、「ホスト名」、「メッセージ」の順に「スペース区切り」で記録されたフォーマットになっている

PRESHARED_AUTH_HEADER_VALUEsyslogEndpoint を適宜設定して、コードをデプロイします。

export URL='https://github.com/kyouheicf/logpush-to-syslog.git'
git clone $URL && cd $(basename $_ .git)
wrangler deploy
src/index.ts
// For decompressing
import { Base64 } from 'js-base64';
// For TCP socket
import { connect } from 'cloudflare:sockets';

export default {
  async fetch(req): Promise<Response> {
    // Check pre-shared key header
    const PRESHARED_AUTH_HEADER_KEY = 'X-Logpush-Auth';
    const PRESHARED_AUTH_HEADER_VALUE = 'mypresharedkey';
    const psk = req.headers.get(PRESHARED_AUTH_HEADER_KEY);
    const contentEncoding = req.headers.get('content-encoding')
    if (psk !== PRESHARED_AUTH_HEADER_VALUE) {
      return new Response('Sorry, you have submitted an invalid key.', {
        status: 403,
      });
    }
    // Decompress gzipped logpush body to json
    const buf = await req.arrayBuffer();
    const enc = new TextDecoder("utf-8");
    const blob = new Blob([buf])
    const ds = new DecompressionStream('gzip');
    const decompressedStream = blob.stream().pipeThrough(ds);
    const buffer2 = await new Response(decompressedStream).arrayBuffer();
    const decompressed = new Uint8Array(buffer2)
    const ndjson = enc.decode(decompressed)

    // Initial pre-flight Logpush Request to confirm the integration check
    if (ndjson === '{"content":"test"}') {
      return new Response('Initial pre-flight Logpush Request has been confirmed', {
        status: 200,
      })
    }
    // parse ndjson to JSON object
    var json = '[' + ndjson.trim().replace(/\n/g, ',') + ']';
    const jsonobj = JSON.parse(json);

    try {
      // Define Your Syslog Endpoint
      const syslogEndpoint = { hostname: "x.x.x.x", port: 514 };
      const socket = connect(syslogEndpoint /* , { secureTransport: "on" } */);
      let writer = socket.writable.getWriter()

      // Write Syslog RFC 3164 Format message
      //"<34>Oct 11 22:14:15 gateway_http AccountID=\"xxx\" Action=\"bypass\" Datetime=\"1724782330\" DestinationIP=\"x.x.x.x\" DeviceName=\"xxx\" Email=\"xxx@example.com\" HTTPHost=\"www.google.co.jp\" HTTPMethod=\"UNKNOWN\" HTTPStatusCode=\"0\" \n");
      jsonobj.map(jsonelem => {
        const jsonfmt = JSON.stringify(jsonelem, null, 2);
        let jstNow = new Date(Date.now() + ((new Date().getTimezoneOffset() + (9 * 60)) * 60 * 1000))//.toLocaleString({ timeZone: 'Asia/Tokyo' });
        let MMMddHHmmss = jstNow.toString().slice(4, 7) + ' ' + jstNow.getDate() + ' ' + jstNow.toTimeString().slice(0, 8)
        let message = '<34>' + MMMddHHmmss + ' gateway_http ' + Object.keys(jsonelem).map(function (key) { return key + '=' + JSON.stringify(jsonelem[key]) }).join(' ') + ' \n'
        writer.write(new TextEncoder().encode(message));
      })
      await writer.close();

      // Start reading from the socket.	  
      const decoder = new TextDecoder();
      let syslogResponse = "";
      for await (const chunk of socket.readable) {
        syslogResponse += decoder.decode(chunk, { stream: true });
      }
      syslogResponse += decoder.decode();
      console.log("Read ", syslogResponse.length, " from Syslog server");
      return new Response(syslogResponse, { headers: { "Content-Type": "text/plain" } });
    } catch (error) {
      return new Response("Socket connection failed: " + error, { status: 500 });
    }
  }
} satisfies ExportedHandler;

Windows Firewall 構成

TCP Workers は Cloudflare IP Ranges に記載されていない IP アドレスから接続をおこなうことが観測されたため、Windows Firewall の構成時に注意が必要です。

2024-08-27 01:33:32 DROP TCP 104.28.157.26 10.146.0.17 23169 514 60 S 1023346287 0 65535 - - - RECEIVE
2024-08-27 01:34:18 DROP TCP 104.28.157.52 10.146.0.17 42193 514 60 S 1289832775 0 65535 - - - RECEIVE
2024-08-27 01:35:40 DROP TCP 104.28.157.33 10.146.0.17 25009 514 60 S 2700424325 0 65535 - - - RECEIVE
2024-08-27 01:36:05 DROP TCP 104.28.157.24 10.146.0.17 58408 514 60 S 3116203926 0 65535 - - - RECEIVE
2024-08-27 01:37:31 DROP TCP 104.28.157.43 10.146.0.17 14550 514 60 S 1785922093 0 65535 - - - RECEIVE

観測された範囲では 104.28.0.0/16 を使っているように見受けられたため、以下の受信許可ルールを追加して有効にします。

cmd
netsh advfirewall firewall add rule ^
name="Cloudflare TCP Workers Syslog Connection" ^
dir=in action=allow enable=yes ^
remoteip=104.28.0.0/16 ^
protocol=tcp localport=514

Logpush ジョブ構成

以下のコマンドで gateway_http データセットの Logpush を作成します。

output_typetimestamp_formatunix にし、destination_conf で作成した TCP Workers エンドポイント https://syslog.example.workers.dev を指定します。

export EMAIL='YOUR_EMAIL'
export APIKEY='YOUR_APIKEY'
export ACCOUNT_ID='YOUR_ACCOUNT_ID'

# 全フィールドを指定
export FIELDS=$(curl -s \
-H "X-Auth-Email: $EMAIL" \
-H "X-Auth-Key: $APIKEY" \
"https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/logpush/datasets/gateway_http/fields" | jq -r '.result | keys | join("\",\"")')
echo $FIELDS

# Logpush ジョブを作成
curl -s "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/logpush/jobs" -X POST -d '
{                                              
  "name": "gateway-http-syslog-alog",
  "output_options": {
    "field_names": ["'$FIELDS'"],
    "timestamp_format": "unix"
  },
  "destination_conf": "https://syslog.example.workers.dev?header_X-Logpush-Auth=mypresharedkey&dataset:gateway_http",
  "max_upload_bytes": 5000000,
  "max_upload_records": 1000,
  "dataset": "gateway_http",
  "enabled": true
}' \
-H "X-Auth-Email: $EMAIL" \
-H "X-Auth-Key: $APIKEY"

TCP Workers と Logpush の構成がうまく動いていると、以下のフォルダに *.log ファイルが保存されます。

image.png

ALog Forwarder 設定

以下のガイドに従ってログ連携します。

対象ホストの登録は以下のように設定できます。

image.png

ALog Cloud マッピング設定

管理>対象ホストから以下のマッピング設定を行い、ログを見やすい形で取り込みます。

Syslog メッセージ内でログを key="value" のように変換したことで KeySearch 関数を使ったマッピングが可能です。

TimeStampunix シリアル値の DateTime フィールドを参照し、ConvSerialToDateTime 関数で取り込むことができます。

その他の詳細フィールドは適宜見やすい形に定義できます。

image.png

検索画面にて以下のようにログを取り込んで活用できる状態になったことが確認できます。

image.png

まとめ

Cloudflare からの Logpush で直接の宛先として対応していない SIEM に対しても、TCP Workers を使って柔軟にファイル形式を変換する処理を実現できました。

こうしたログ処理のパイプラインを一度構築してしまえば、後は自動で取り込みが実行されていくため、SIEM 側でのレポート・アラートを活用した運用に注力していくことができます。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?