顧客はログを外に送れないと言った
目標
顧客のサーバーで、顧客の担当者が自分で実行するサポートバンドルの収集ツールを作ります。許可リストで集め、秘密をマスクし、manifestとサイズの上限を設け、同じ入力なら同じバイト列になるtar.gzにまとめます。
なぜ重要なのか
ログを外に送れない顧客からバンドルを受け取るには、顧客のセキュリティ担当者の承認が必要です。何が入ったのか、何を何件マスクしたのか、受け取ったファイルが確認したそのファイルなのかを示せなければ、バンドルは外に出せません。毎回人がファイルを選ぶと、秘密が漏れたり、役に立たないバンドルができたりします。
用意するもの: /opt/lab/p1a-bundle/site(顧客サーバーのコピー。VERSION、etc、logs、run/environ、data)、/opt/lab/p1a-bundle/rules.tsv(顧客のセキュリティチームの追加のマスキングルール)。 作業ディレクトリは/root/bundleです。想定所要時間は60分で、セッションが終わると、/root/bundleは消えます。
収集のルール(このラボの約束):
- 入れるもの:
VERSION、etc/の下の通常ファイル(シンボリックリンクは除く)、logs/の直下の*.log、run/environ(stageではenv.txtで、NULを改行に変えて、名前順に並べ替えます)。 - マスクするもの: 秘密鍵のブロック全体 →
[REDACTED:private_key]、Bearerのあとの値 →[REDACTED:token]、キー名がpassword・passwd・secretで終わる値 →[REDACTED:password]、token・api_keyで終わる値 →[REDACTED:token]、メールアドレス →[REDACTED:email]。キーと値は、:または=で区切られます。キー名と区切り文字は残します。 - ログの上限: ファイルごとに65536バイト。マスクしたあとに、最近の側の、完全な行だけを残します。
ステップ
- 用意するもののsiteから、収集のルールに合うファイルの相対パスを、1行に1つずつ記録します(ファイル: /root/bundle/scope.txt)。
- 標準入力をマスクして標準出力へ出し、標準エラー出力に
redactions=Nを出力するスクリプトを作ります(ファイル: /root/bundle/redact.py)。 python3 collect.py SRC OUTが、OUT/stageに、許可リストのファイルをマスクして置くスクリプトを作ります(ファイル: /root/bundle/collect.py)。- collect.pyが、manifestを作るようにします(files配列: path・size・sha256・redactions・truncated、path順に並べ替え、total_redactions。ファイル: OUT/stage/manifest.json)。
- ログファイルに65536バイトの上限を適用し、manifestのtruncated・redactionsを実際に合わせます。
- collect.pyが、バンドルを作るようにします(ファイル: OUT/support-bundle.tar.gz)。項目は、
support-bundle/の下で名前順にし、同じ入力なら、いつどこでまとめても、sha256が同じでなければなりません。 --rules FILEを受け取って、kind<TAB>정규식のルール(プレースホルダーは正規表現です)を、デフォルトのルールのあとに適用し、[REDACTED:kind]でマスクするようにします。- 用意するもののsiteとrules.tsvから、受け渡し用のバンドル、SHA256SUMS、REDACTION-REPORT.tsvを作ります(保存先: /root/bundle/delivery/support-bundle.tar.gz、SHA256SUMS、REDACTION-REPORT.tsv)。
参考
- 採点ツールは、あなたのredact.py・collect.pyを、毎回秘密を新しく仕込んだサーバーのツリーで直接実行します。用意するものの値に合わせたルールやハードコードは不合格になります。
- 過剰なマスクも失敗です:
password_min_length: 12、token_ttl_sec: 3600、passwordless_login: false、orders-api@sha256:…のような行は、そのまま残る必要があります。 - ステップ3から、collect.pyの1つのファイルを育て続けます。前のステップの機能を消すと、前のステップの採点が、再び不合格になります。
- NUL区切りのファイルを見る:
tr '\0' '\n' < /opt/lab/p1a-bundle/site/run/environ - 決定性の確認: 同じコマンドを2回(間に
touchで元データのmtimeを変えて)動かし、sha256sumを比べます。 - よくある間違い:
tarfile.open(..., "w:gz")をそのまま使う、manifestに生成時刻を入れる、切ってからマスクする。
何を入れるかを、許可リストで決める
用意するもののsiteから、収集のルールに合うファイルの相対パスを、1行に1つずつ記録してください(ファイル: /root/bundle/scope.txt)。
siteの全体を、まずfindで見渡してみてください。ルールに名前で書かれているものだけを入れ、ローテーションされたログ、下位ディレクトリのログ、顧客データ、pidファイルは、ルールにないので外れます。パスは、siteを基準にした相対パスです。
狭く正確なマスクのフィルター
標準入力をマスクして標準出力へ出し、標準エラー出力にredactions=Nを出力するスクリプトを作ってください(ファイル: /root/bundle/redact.py)。
正規表現は、形で決めます。秘密鍵は複数行なので、行のルールより先に、re.Sでブロックごと置き換えてください。キー名は、passwordが「含まれる」ものではなく、「終わる」ものでなければ、password_min_lengthが生き残りません。値は、引用符で囲まれていることも、&や空白で終わることもあります。
許可リストだけを集めて、マスクしたstageを作る
python3 /root/bundle/collect.py SRC OUTが、OUT/stageに、許可リストのファイルをマスクして置くスクリプトを作ってください(ファイル: /root/bundle/collect.py)。
redact.pyを同じディレクトリでimportすれば、ルールを2回書かずに済みます。etcの下をたどるときは、os.path.islinkでリンクを除き、run/environはNULで分割してから並べ替えて、env.txtとして置きます。もう一度動かしたとき、前のstageのファイルが残らないようにしてください。
確認する人が照合するmanifest
collect.pyが、manifestを作るようにしてください(files: path・size・sha256・redactions・truncated、path順、total_redactions。ファイル: OUT/stage/manifest.json)。
sha256とsizeは、マスクしたあとで、stageに実際に書いたバイト列から計算します。redactionsは、そのファイルでマスクした件数です。このファイルに生成時刻を入れると、ステップ6で何が問題になるかを、先に考えてみてください。
大きなログは、マスクしてから最近の行だけ
logsのファイルごとに65536バイトの上限を適用し、manifestのtruncated・redactionsを、切り詰めた結果に合わせてください。
マスクした結果の末尾から65536バイトを切り取ったあと、その窓の最初の行が完全でなければ捨てます。窓の直前のバイトが改行かどうかを見れば、判断できます。件数は、切り落とした部分を除いて、残った部分で数え直す必要があります。
同じ入力なら同じバイト列
collect.pyが、バンドルを作るようにしてください(ファイル: OUT/support-bundle.tar.gz)。項目は、support-bundle/の下で名前順にし、別の時刻・別のOUTでまとめても、sha256が同じでなければなりません。
tarには、更新時刻・所有者・権限・順序が入り、gzipのヘッダーには、時刻がもう1回入ります。Pythonなら、TarInfoを自分で作って値を固定し、GzipFileのmtimeを決めてください。GNU tarでまとめるなら、reproducible-buildsのドキュメントのオプションを見てください。
顧客のセキュリティチームの追加ルールをファイルで受け取る
collect.pyが、--rules FILE(kind正規表現)を受け取って、デフォルトのルールのあとに適用し、件数をmanifestにまとめるようにしてください。
コメント(#)と空行は飛ばします。ルールは、コードに埋め込まず、ファイルから読んでください。採点ツールは、毎回異なる種類名と正規表現を渡し、ルールなしで動かしたとき、その値が残るかも見ます。
受け渡し用のバンドルと、確認用の報告書
用意するもののsiteとrules.tsvから、受け渡し用のバンドル、SHA256SUMS、REDACTION-REPORT.tsvを作ってください(保存先: /root/bundle/delivery/support-bundle.tar.gz、/root/bundle/delivery/SHA256SUMS、/root/bundle/delivery/REDACTION-REPORT.tsv)。
SHA256SUMSは、受け取る側が、sha256sum -cで確認できる形式です。報告書の件数は、manifestを写さずに、バンドルの中のファイルのマーカーを、種類ごとに数えて書いてください。採点ツールは、いまのcollect.pyで、用意するものからもう一度まとめて、受け渡し用のものとバイト列が同じかを見ます。