TT Lab
はじめる
学ぶ 学習パス コース

CI/CDパイプライン

消したはずのトークンが、レイヤーとコミット履歴からそのまま出てきた

TT Labで続きを見る

目標

秘密情報が残る4つの場所(ビルドログ・イメージのレイヤー・イメージ設定のEnvとhistory・コミット履歴)を、それぞれ手で再現して見せ、秘密情報をイメージから取り除いて実行時に注入する設計に変えたうえで、3つの場所を一度に走査するゲートを作ります。

なぜ重要なのか

秘密情報が漏れる事故は、たいてい悪意ではなく、便宜から生まれます。デバッグのためにトレースを有効にし、ビルドの途中でファイルとして書き込むのが最も簡単で、設定ファイルをそのままコミットしました。どれも数分を節約しようとしたことなのに、結果は、その値を読める人の範囲がまるごと広がることです。ここで人が最もよく頼るのがマスキングですが、マスキングが隠せるのは、知っている文字列とまったく同じものだけです。base64に変換したり、2つに分割したりすると、そのまま通り抜けます。そのため、順序は、出力しないようにすることが先で、マスキングは最後の安全網です。イメージ側は、さらに静かです。イメージは、ファイルシステムだけでなく、どのように作られたのかも一緒に含んでいるので、中間のレイヤーでファイルとして書き込み、あとのレイヤーで削除しても、前のレイヤーのブロブにはそのまま残り、設定のEnvとhistoryにも値が書かれます。コミット履歴も同じです。次のコミットで削除しても、ブロブは残ります。そして、最もよく間違える場所が最後にあります。一度漏れた秘密情報への対応は、消すことではなく、取り消して新しく発行することです。履歴の修正は、すでにクローンされたコピーやフォーク、キャッシュ、バックアップに何の影響も与えられず、その間に誰が読んだのかもわからないからです。

ステップ

  1. 作業ディレクトリは/root/secrets-labです。まず、/root/secrets-lab/secret/api.tokenを作って、最初の行にこのラボの偽のトークンsk-labhub-fake-9f3a2b1c8d7e6f5a4b3c2d1e0f9a8b7cを書いてください。ファイルの権限は600である必要があります。続いて、/root/secrets-lab/publish.sh <토큰파일> <산출물경로>(プレースホルダーは、トークンファイルとアーティファクトのパスです)を作ってください。このスクリプトは、(1)トークンファイルの最初の行を読み、(2)アーティファクトのパスの親ディレクトリがなければ作ったあと、そのファイルにpublished <산출물 파일 이름>(プレースホルダーはアーティファクトのファイル名です)を1行書き、(3)シェルのトレースを有効にした状態でAuthorization: Bearer <토큰>(プレースホルダーはトークンです)を出力します。実行権限を付けてください。続いて、bash /root/secrets-lab/publish.sh /root/secrets-lab/secret/api.token /root/secrets-lab/out/app-1.0.0.txtを実行して、標準出力と標準エラー出力を一緒に/root/secrets-lab/logs/publish.logに保存し、そのログでトークンが見える行数を、/root/secrets-lab/leak-count.txtに数字1つで書いてください。2行以上である必要があります。
  2. /root/secrets-lab/mask.sh <토큰파일>(プレースホルダーはトークンファイルです)を作ってください。標準入力を読んで、トークンとまったく同じ文字列をすべて***MASKED***に置き換えて標準出力に出力するフィルターです。トークンが空なら、最初の行がERRORで始まる行を出して、2で終了します。続いて、/root/secrets-lab/leaky-shapes.sh <토큰파일>を作ってください。同じ値を4つの形で、ちょうど4行出力します(順序もこのとおりです)。raw <토큰>(プレースホルダーはトークンです)、base64 <토큰을 줄바꿈 없이 base64 로 인코딩한 값>(プレースホルダーは、トークンを改行なしでbase64エンコードした値です)、split <앞 절반> <뒤 절반>(プレースホルダーは、前半と後半です)、hex <토큰 바이트를 16진수 소문자로 이어 붙인 값>(プレースホルダーは、トークンのバイトを16進数の小文字でつなげた値です)。両方に実行権限を付けて、bash /root/secrets-lab/leaky-shapes.sh /root/secrets-lab/secret/api.token | bash /root/secrets-lab/mask.sh /root/secrets-lab/secret/api.tokenの結果を/root/secrets-lab/logs/mask-probe.logに保存してください。最後に、/root/secrets-lab/mask-report.txtに4行を<이름> <masked|leaked>(プレースホルダーは名前です)の形で書いてください。その行に、隠された箇所が見えればmasked、値がそのまま見えればleakedです。
  3. コンテナを起動せずに、OCIイメージを手で作ります。/root/secrets-lab/build-image.sh <레이아웃디렉터리> <태그> <토큰파일>(プレースホルダーは、レイアウトのディレクトリ、タグ、トークンファイルです)を作ってください。レイヤーは2枚です。最初のレイヤーのルートには、app/main.py(内容はprint("labhub demo")の1行)とetc/build.env(内容はAPI_TOKEN=<토큰>の1行。プレースホルダーはトークンです)が入り、2枚目のレイヤーのルートには、そのファイルを削除するホワイトアウトのエントリetc/.wh.build.env(空のファイル)だけが入ります。設定JSONは、architectureをuname -mから取り出して(x86_64はamd64、aarch64はarm64)書き、osはlinux、config.EnvはPATH=/usr/local/bin:/usr/bin:/binとAPI_TOKEN=<토큰>の2項目、rootfs.diff_idsは圧縮前のtarのsha256をレイヤーの順序どおりに、historyは2項目で、1つ目のcreated_byにトークンが書かれたコマンドを、2つ目のcreated_byにそのファイルを削除するコマンドを書きます。マニフェストのconfig・layersのdigestとsizeは圧縮後の値で、すべてのブロブはblobs/sha256/<접두어 없는 다이제스트>(プレースホルダーはプレフィックスのないダイジェストです)に置きます。index.jsonにはマニフェストを1つ置き、アノテーションorg.opencontainers.image.ref.nameに2つ目の引数のタグを書き、oci-layoutは{"imageLayoutVersion":"1.0.0"}です。実行権限を付けたあと、bash /root/secrets-lab/build-image.sh /root/secrets-lab/oci/app v1 /root/secrets-lab/secret/api.tokenで作り、skopeo inspect oci:/root/secrets-lab/oci/app:v1の出力を/root/secrets-lab/inspect.jsonに保存してください。最後に、最初のレイヤーのブロブを展開して、そこから取り出したetc/build.envの行を、/root/secrets-lab/layer-leak.txtにそのまま書いてください。2枚目のレイヤーで削除したのに、出てきます。
  4. 規則をデータとして書きます。/root/secrets-lab/rules.txtに、1行が<이름> <확장정규식>(プレースホルダーは、名前と拡張正規表現です)である規則を3つ書いてください(名前のあとの空白から行末までが正規表現です)。名前は、正確にlabhub-fake-token、aws-access-key-id、private-key-headerで、それぞれ、このラボの偽のトークンの形(sk-labhub-fake-のあとに16進数32文字)、AWSアクセスキーIDの形(AKIAのあとに大文字と数字16文字)、PEM秘密鍵のヘッダー(-----BEGINとPRIVATE KEY-----の間に大文字と空白)を検出する必要があります。続いて、/root/secrets-lab/image-scan.sh <이미지참조> <규칙파일>(プレースホルダーは、イメージの参照と規則ファイルです)を作ってください。イメージの4つの場所を走査して、ちょうど4行をこの順序で出力します。env、history、layer、manifestです。各行は、検出されれば<자리> hit <걸린 규칙 이름들>(プレースホルダーは、場所と、検出された規則の名前です)、検出されなければ<자리> cleanです。1つでも検出されれば1で、1つも検出されなければ0で終了します。規則ファイルがないか、空であるか、イメージを読み取れなければ、最初の行がERRORで始まる行を出して、2で終了します。実行権限を付けたあと、bash /root/secrets-lab/image-scan.sh oci:/root/secrets-lab/oci/app:v1 /root/secrets-lab/rules.txtの出力を/root/secrets-lab/scan-v1.txtに保存してください。
  5. /root/secrets-lab/build-image-clean.sh <레이아웃디렉터리> <태그>(プレースホルダーは、レイアウトのディレクトリとタグです)を作ってください。トークンファイルを引数として受け取らないことが、設計の核心です。レイヤーは1枚で、ルートにapp/main.py(前のステップと同じ1行)とetc/app.conf(内容はapi_token_file=/run/secrets/api.tokenの1行)が入ります。設定のconfig.Envは、PATH=/usr/local/bin:/usr/bin:/binとAPI_TOKEN_FILE=/run/secrets/api.tokenの2項目で、historyは、秘密情報が書かれていない1項目です。残りの構造(ダイジェスト・サイズ・diff_ids・index.json・oci-layout)は、前のステップと同じです。そして、/root/secrets-lab/run.shを作ってください。環境変数API_TOKEN_FILE(なければ/run/secrets/api.token)が指すファイルを読んで、ready token_sha256=<그 값의 sha256 앞 12자리>(プレースホルダーは、その値のsha256の先頭12桁です)を1行出力します。値そのものは絶対に出力しません。ファイルがないか、空であれば、最初の行がERRORで始まる行を出して、2で終了します。両方に実行権限を付けたあと、bash /root/secrets-lab/build-image-clean.sh /root/secrets-lab/oci/app-clean v1でイメージを作り、bash /root/secrets-lab/image-scan.sh oci:/root/secrets-lab/oci/app-clean:v1 /root/secrets-lab/rules.txtの出力を/root/secrets-lab/scan-clean.txtに保存してください。4行すべてがcleanである必要があります。
  6. /root/secrets-lab/with-secret.sh <토큰파일> <명령> [인자...](プレースホルダーは、トークンファイルとコマンドと引数です)を作ってください。このラッパーは、(1)自分専用の一時ディレクトリを作って権限を700に絞り、(2)その中に、トークンファイルの最初の行を、権限600のファイルとしてコピーし、(3)最初の行にsecret_file <만든 파일 경로>(プレースホルダーは、作ったファイルのパスです)を出力し、(4)環境変数API_TOKEN_FILEにそのパスを入れて、渡されたコマンドを実行し、(5)コマンドの終了コードをそのまま自分の終了コードとし、(6)成功しても失敗しても、終わるときにそのディレクトリを削除します。秘密情報の値そのものは、画面に出力しません。実行するコマンドがないか、トークンファイルがないか、空であれば、最初の行がERRORで始まる行を出して、2で終了します。実行権限を付けたあと、bash /root/secrets-lab/with-secret.sh /root/secrets-lab/secret/api.token bash /root/secrets-lab/run.shの出力を/root/secrets-lab/logs/with-secret.logに保存してください。そして、/root/secrets-lab/secretディレクトリの権限を700に合わせたあと、/root/secrets-lab/perm-audit.txtに2行を<모드> <경로>(プレースホルダーは、モードとパスです)の形で書いてください。1行目は/root/secrets-lab/secret、2行目は/root/secrets-lab/secret/api.tokenです。
  7. 練習用のリポジトリ/root/secrets-lab/repoを作って、コミットを3つ積んでください。最初のコミットはconfig/app.conf(秘密情報なし)、2つ目のコミットはconfig/deploy.env(内容はDEPLOY_TOKEN=sk-labhub-fake-9f3a2b1c8d7e6f5a4b3c2d1e0f9a8b7cの1行)、3つ目のコミットは、そのconfig/deploy.envを削除するコミットです。続いて、削除された値を履歴から取り出してください。/root/secrets-lab/git-leak.txtにはそのブロブの内容を、/root/secrets-lab/git-leak-commit.txtにはそのファイルを最初に入れたコミットの40桁のハッシュを書きます。最後に、/root/secrets-lab/scan-repo.sh <저장소> <규칙파일>(プレースホルダーは、リポジトリと規則ファイルです)を作ってください。作業ツリーではなく、すべてのコミットに含まれるすべてのブロブを、前のステップの規則ファイルで走査して、検出されたものごとに<규칙 이름> <블롭 해시> <경로>(プレースホルダーは、規則の名前、ブロブのハッシュ、パスです)を1行出力し、1つでも検出されれば1、1つもなければ何も出力せずに0で終了します。リポジトリでないか、規則ファイルがないか、空であれば、最初の行がERRORで始まる行を出して、2で終了します。実行権限を付けたあと、bash /root/secrets-lab/scan-repo.sh /root/secrets-lab/repo /root/secrets-lab/rules.txtの出力を/root/secrets-lab/repo-scan.txtに保存してください。
  8. /root/secrets-lab/secret-gate.sh <로그디렉터리> <이미지참조> <저장소> <규칙파일> <보고서파일>(プレースホルダーは、ログディレクトリ、イメージの参照、リポジトリ、規則ファイル、レポートファイルです)を作ってください。ログディレクトリの下のすべてのファイル、イメージ、リポジトリの履歴という3つの場所を一度に走査します(イメージとリポジトリは、前のステップで作った2つの検査スクリプトをそのまま呼び出します)。検出されたものが1つでもあれば、画面とレポートの最初の行にSECRETS-FOUND <전체 건수>(プレースホルダーは全体の件数です)を出力して3で、1つもなければSECRETS-CLEANを出力して0で終了します。ログディレクトリがないか、規則ファイルがないか、リポジトリでなければ、最初の行がERRORで始まる行を出して、1で終了します。レポートには、最初の行のあとに3つの場所ごとの結果を続けて書きますが、ログで検出された行にはそのファイルのパスが、リポジトリで検出された行にはそのパスが、見えている必要があります。レポートのパスの親ディレクトリがなければ作ります。実行権限を付けたあと、bash /root/secrets-lab/secret-gate.sh /root/secrets-lab/logs oci:/root/secrets-lab/oci/app-clean:v1 /root/secrets-lab/repo /root/secrets-lab/rules.txt /root/secrets-lab/report/gate.txtで、1回実行してください。最後に、/root/secrets-lab/revoke-checklist.txtにちょうど5行を<순번> <열쇠> <한 줄 설명>(プレースホルダーは、順番、キー、1行の説明です)の形で書いてください。キーは、この順序です。revoke、reissue、rotate-consumers、audit-access、prevent-recurrence。説明は、それぞれ10文字以上で、自分の言葉で書きます。

参考

デバッグのためにトレースを有効にしたら、トークンがログにそのまま出力された

作業ディレクトリは/root/secrets-labです。まず、/root/secrets-lab/secret/api.tokenを作って、最初の行にこのラボの偽のトークンsk-labhub-fake-9f3a2b1c8d7e6f5a4b3c2d1e0f9a8b7cを書いてください。ファイルの権限は600である必要があります。続いて、/root/secrets-lab/publish.sh <토큰파일> <산출물경로>(プレースホルダーは、トークンファイルとアーティファクトのパスです)を作ってください。このスクリプトは、(1)トークンファイルの最初の行を読み、(2)アーティファクトのパスの親ディレクトリがなければ作ったあと、そのファイルにpublished <산출물 파일 이름>(プレースホルダーはアーティファクトのファイル名です)を1行書き、(3)シェルのトレースを有効にした状態でAuthorization: Bearer <토큰>(プレースホルダーはトークンです)を出力します。実行権限を付けてください。続いて、bash /root/secrets-lab/publish.sh /root/secrets-lab/secret/api.token /root/secrets-lab/out/app-1.0.0.txtを実行して、標準出力と標準エラー出力を一緒に/root/secrets-lab/logs/publish.logに保存し、そのログでトークンが見える行数を、/root/secrets-lab/leak-count.txtに数字1つで書いてください。2行以上である必要があります。

シェルのトレースはset -xで有効にし、set +xで無効にします。トレースは標準エラー出力に出るので、> 파일 2>&1(プレースホルダーはファイル名です)のように両方を一緒に受け取らないと見えません。値を出力する行が1つと、その行のトレースが1つなので、同じ値が2か所に残ります。権限を絞ってファイルを作るときは、umask 077をサブシェルに設定するか、作ったあとにchmodします。行数はgrep -cが数えます。

マスキングを付けたのに、base64に変換した値がそのまま通り抜けた

/root/secrets-lab/mask.sh <토큰파일>(プレースホルダーはトークンファイルです)を作ってください。標準入力を読んで、トークンとまったく同じ文字列をすべて***MASKED***に置き換えて標準出力に出力するフィルターです。トークンが空なら、最初の行がERRORで始まる行を出して、2で終了します。続いて、/root/secrets-lab/leaky-shapes.sh <토큰파일>を作ってください。同じ値を4つの形で、ちょうど4行出力します(順序もこのとおりです)。raw <토큰>(プレースホルダーはトークンです)、base64 <토큰을 줄바꿈 없이 base64 로 인코딩한 값>(プレースホルダーは、トークンを改行なしでbase64エンコードした値です)、split <앞 절반> <뒤 절반>(プレースホルダーは、前半と後半です)、hex <토큰 바이트를 16진수 소문자로 이어 붙인 값>(プレースホルダーは、トークンのバイトを16進数の小文字でつなげた値です)。両方に実行権限を付けて、bash /root/secrets-lab/leaky-shapes.sh /root/secrets-lab/secret/api.token | bash /root/secrets-lab/mask.sh /root/secrets-lab/secret/api.tokenの結果を/root/secrets-lab/logs/mask-probe.logに保存してください。最後に、/root/secrets-lab/mask-report.txtに4行を<이름> <masked|leaked>(プレースホルダーは名前です)の形で書いてください。その行に、隠された箇所が見えればmasked、値がそのまま見えればleakedです。

sedで置換すると、値の中の/・&・.を1つずつエスケープする必要があります。Pythonの1行で文字列をそのまま置き換えるほうが安全です。base64は、base64 -w0が改行なしで出力します。16進数は、od -An -tx1の出力から空白と改行を消せば済みます。半分は、bashの部分文字列${변수:시작:길이}で切り出します(プレースホルダーは、変数、開始位置、長さです)。マスキングが隠せるのは「知っている文字列」1つだけだということを、4行で確認するのが、このステップのすべてです。

次のレイヤーで削除したのに、前のレイヤーのブロブにそのまま残っていた

コンテナを起動せずに、OCIイメージを手で作ります。/root/secrets-lab/build-image.sh <레이아웃디렉터리> <태그> <토큰파일>(プレースホルダーは、レイアウトのディレクトリ、タグ、トークンファイルです)を作ってください。レイヤーは2枚です。最初のレイヤーのルートには、app/main.py(内容はprint("labhub demo")の1行)とetc/build.env(内容はAPI_TOKEN=<토큰>の1行。プレースホルダーはトークンです)が入り、2枚目のレイヤーのルートには、そのファイルを削除するホワイトアウトのエントリetc/.wh.build.env(空のファイル)だけが入ります。設定JSONは、architectureをuname -mから取り出して(x86_64はamd64、aarch64はarm64)書き、osはlinux、config.EnvはPATH=/usr/local/bin:/usr/bin:/binとAPI_TOKEN=<토큰>の2項目、rootfs.diff_idsは圧縮前のtarのsha256をレイヤーの順序どおりに、historyは2項目で、1つ目のcreated_byにトークンが書かれたコマンドを、2つ目のcreated_byにそのファイルを削除するコマンドを書きます。マニフェストのconfig・layersのdigestとsizeは圧縮後の値で、すべてのブロブはblobs/sha256/<접두어 없는 다이제스트>(プレースホルダーはプレフィックスのないダイジェストです)に置きます。index.jsonにはマニフェストを1つ置き、アノテーションorg.opencontainers.image.ref.nameに2つ目の引数のタグを書き、oci-layoutは{"imageLayoutVersion":"1.0.0"}です。実行権限を付けたあと、bash /root/secrets-lab/build-image.sh /root/secrets-lab/oci/app v1 /root/secrets-lab/secret/api.tokenで作り、skopeo inspect oci:/root/secrets-lab/oci/app:v1の出力を/root/secrets-lab/inspect.jsonに保存してください。最後に、最初のレイヤーのブロブを展開して、そこから取り出したetc/build.envの行を、/root/secrets-lab/layer-leak.txtにそのまま書いてください。2枚目のレイヤーで削除したのに、出てきます。

レイヤーは、ただのtarです。再現できる形でアーカイブするには、--sort=name --mtime=@1735689600 --owner=0 --group=0 --numeric-ownerを付けて、gzip -n -9で圧縮します。1つのレイヤーで、3つの値が必要です。圧縮前のtarのsha256(diff_id)、圧縮後のバイト列のsha256(ブロブの名前であり、マニフェストのdigest)、圧縮後のサイズ(size)です。3つのうち1つでも食い違うとskopeo copyが拒否するので、それがそのまま検算になります。JSONは、jq -n --argで作れば、引用符を手で合わせなくて済みます。マニフェストのブロブも、ほかのブロブと同じ場所に入り、index.jsonがそのダイジェストとサイズを指します。レイヤーのブロブからファイルを1つだけ取り出すには、gzip -dc <블롭> | tar -xO ./etc/build.envです(プレースホルダーはブロブです)。

レイヤーを調べるまでもなく、設定のEnvとhistoryに書かれていた

規則をデータとして書きます。/root/secrets-lab/rules.txtに、1行が<이름> <확장정규식>(プレースホルダーは、名前と拡張正規表現です)である規則を3つ書いてください(名前のあとの空白から行末までが正規表現です)。名前は、正確にlabhub-fake-token、aws-access-key-id、private-key-headerで、それぞれ、このラボの偽のトークンの形(sk-labhub-fake-のあとに16進数32文字)、AWSアクセスキーIDの形(AKIAのあとに大文字と数字16文字)、PEM秘密鍵のヘッダー(-----BEGINとPRIVATE KEY-----の間に大文字と空白)を検出する必要があります。続いて、/root/secrets-lab/image-scan.sh <이미지참조> <규칙파일>(プレースホルダーは、イメージの参照と規則ファイルです)を作ってください。イメージの4つの場所を走査して、ちょうど4行をこの順序で出力します。env、history、layer、manifestです。各行は、検出されれば<자리> hit <걸린 규칙 이름들>(プレースホルダーは、場所と、検出された規則の名前です)、検出されなければ<자리> cleanです。1つでも検出されれば1で、1つも検出されなければ0で終了します。規則ファイルがないか、空であるか、イメージを読み取れなければ、最初の行がERRORで始まる行を出して、2で終了します。実行権限を付けたあと、bash /root/secrets-lab/image-scan.sh oci:/root/secrets-lab/oci/app:v1 /root/secrets-lab/rules.txtの出力を/root/secrets-lab/scan-v1.txtに保存してください。

コンテナなしでイメージの中を見るには、skopeo copy --insecure-policy <참조> dir:<임시디렉터리>が最も簡単です(プレースホルダーは、参照と一時ディレクトリです)。そうすると、manifest.jsonとブロブが普通のファイルとして出力され、ブロブのファイル名が、そのブロブのダイジェストです。設定のブロブは、マニフェストの.config.digestが指します。レイヤーのブロブはgzipなので、展開して見る必要があり、tarの中にはNULバイトが混ざっているので、grepに-aを付けるか、NULを先に消します。正規表現が-で始まる場合は、grep -E -- "$re"のようにオプションの終わりを示す必要があります。規則ファイルが空なのに0で終了する検査スクリプトは、「問題がない」と「何も見えていない」を同じものとして報告してしまいます。そのため、終了コードを別に用意します。

秘密情報をイメージから取り除き、実行時に渡したら、同じ検査が静かになった

/root/secrets-lab/build-image-clean.sh <레이아웃디렉터리> <태그>(プレースホルダーは、レイアウトのディレクトリとタグです)を作ってください。トークンファイルを引数として受け取らないことが、設計の核心です。レイヤーは1枚で、ルートにapp/main.py(前のステップと同じ1行)とetc/app.conf(内容はapi_token_file=/run/secrets/api.tokenの1行)が入ります。設定のconfig.Envは、PATH=/usr/local/bin:/usr/bin:/binとAPI_TOKEN_FILE=/run/secrets/api.tokenの2項目で、historyは、秘密情報が書かれていない1項目です。残りの構造(ダイジェスト・サイズ・diff_ids・index.json・oci-layout)は、前のステップと同じです。そして、/root/secrets-lab/run.shを作ってください。環境変数API_TOKEN_FILE(なければ/run/secrets/api.token)が指すファイルを読んで、ready token_sha256=<그 값의 sha256 앞 12자리>(プレースホルダーは、その値のsha256の先頭12桁です)を1行出力します。値そのものは絶対に出力しません。ファイルがないか、空であれば、最初の行がERRORで始まる行を出して、2で終了します。両方に実行権限を付けたあと、bash /root/secrets-lab/build-image-clean.sh /root/secrets-lab/oci/app-clean v1でイメージを作り、bash /root/secrets-lab/image-scan.sh oci:/root/secrets-lab/oci/app-clean:v1 /root/secrets-lab/rules.txtの出力を/root/secrets-lab/scan-clean.txtに保存してください。4行すべてがcleanである必要があります。

前のステップのビルダーをコピーして、レイヤーを1枚に減らし、秘密情報が入っていた場所を「読み取るパス」に変えれば済みます。イメージが含むのは、値ではなく契約です。どこにあるはずだと約束するパスが1つです。値をその場所へ持ってくるのは実行環境の役割で、だから、同じイメージを開発と本番がそのまま共有できます。sha256sumは、標準入力も受け取れます。検査スクリプトが4行すべてにcleanを出力すると、終了コードが0であることも確認してください。

ファイルで渡した秘密情報が、作業が終わったあとも、その場所に残っていた

/root/secrets-lab/with-secret.sh <토큰파일> <명령> [인자...](プレースホルダーは、トークンファイルとコマンドと引数です)を作ってください。このラッパーは、(1)自分専用の一時ディレクトリを作って権限を700に絞り、(2)その中に、トークンファイルの最初の行を、権限600のファイルとしてコピーし、(3)最初の行にsecret_file <만든 파일 경로>(プレースホルダーは、作ったファイルのパスです)を出力し、(4)環境変数API_TOKEN_FILEにそのパスを入れて、渡されたコマンドを実行し、(5)コマンドの終了コードをそのまま自分の終了コードとし、(6)成功しても失敗しても、終わるときにそのディレクトリを削除します。秘密情報の値そのものは、画面に出力しません。実行するコマンドがないか、トークンファイルがないか、空であれば、最初の行がERRORで始まる行を出して、2で終了します。実行権限を付けたあと、bash /root/secrets-lab/with-secret.sh /root/secrets-lab/secret/api.token bash /root/secrets-lab/run.shの出力を/root/secrets-lab/logs/with-secret.logに保存してください。そして、/root/secrets-lab/secretディレクトリの権限を700に合わせたあと、/root/secrets-lab/perm-audit.txtに2行を<모드> <경로>(プレースホルダーは、モードとパスです)の形で書いてください。1行目は/root/secrets-lab/secret、2行目は/root/secrets-lab/secret/api.tokenです。

mktemp -dは、他の人が入れない場所にディレクトリを作ってくれますが、その中にファイルを作るときの権限は、umaskが決めます。( umask 077; ... )をサブシェルに設定するか、作ったあとにchmodします。削除はtrapに任せます。EXITだけを設定すると、シグナルで終了したときに残るので、INTとTERMも一緒に設定します。コマンドが失敗してもラッパーが先に終了してはいけないので、set -eをいったん外して終了コードを受け取っておき、最後にそのまま出力します。ファイルの権限だけを絞ってディレクトリを開けたままにしていると意味がないことも、忘れないでください。そして、こうしても終わりではありません。その値をすでに読み取ったプロセスが何をしたのかは、このラッパーにはわかりません。

あとのコミットで削除したファイルが、git cat-fileでそのまま出てきた

練習用のリポジトリ/root/secrets-lab/repoを作って、コミットを3つ積んでください。最初のコミットはconfig/app.conf(秘密情報なし)、2つ目のコミットはconfig/deploy.env(内容はDEPLOY_TOKEN=sk-labhub-fake-9f3a2b1c8d7e6f5a4b3c2d1e0f9a8b7cの1行)、3つ目のコミットは、そのconfig/deploy.envを削除するコミットです。続いて、削除された値を履歴から取り出してください。/root/secrets-lab/git-leak.txtにはそのブロブの内容を、/root/secrets-lab/git-leak-commit.txtにはそのファイルを最初に入れたコミットの40桁のハッシュを書きます。最後に、/root/secrets-lab/scan-repo.sh <저장소> <규칙파일>(プレースホルダーは、リポジトリと規則ファイルです)を作ってください。作業ツリーではなく、すべてのコミットに含まれるすべてのブロブを、前のステップの規則ファイルで走査して、検出されたものごとに<규칙 이름> <블롭 해시> <경로>(プレースホルダーは、規則の名前、ブロブのハッシュ、パスです)を1行出力し、1つでも検出されれば1、1つもなければ何も出力せずに0で終了します。リポジトリでないか、規則ファイルがないか、空であれば、最初の行がERRORで始まる行を出して、2で終了します。実行権限を付けたあと、bash /root/secrets-lab/scan-repo.sh /root/secrets-lab/repo /root/secrets-lab/rules.txtの出力を/root/secrets-lab/repo-scan.txtに保存してください。

リポジトリを作るときは、git config user.emailとuser.nameを、リポジトリの中で先に設定しないと、コミットできません。履歴にあるすべてのオブジェクトは、git rev-list --objects --allが<해시> <경로>(プレースホルダーは、ハッシュとパスです)の形で出力します。そのうち何がブロブなのかは、git cat-file --batch-checkにその一覧をそのまま流し込めば、一度にわかります(%(rest)がパスを返します)。内容はgit cat-file -p <해시>です(プレースホルダーはハッシュです)。ファイルを最初に入れたコミットは、git log --diff-filter=Aで探します。検査スクリプトを作るときに最もよくあるミスは、何も見つけられずに0で終了しておいて、「きれいだ」と報告することです。規則がない状態と、違反がない状態を、終了コードで分けておいてください。

3つの場所を一度に走査するゲートを作り、取り消しから書いた

/root/secrets-lab/secret-gate.sh <로그디렉터리> <이미지참조> <저장소> <규칙파일> <보고서파일>(プレースホルダーは、ログディレクトリ、イメージの参照、リポジトリ、規則ファイル、レポートファイルです)を作ってください。ログディレクトリの下のすべてのファイル、イメージ、リポジトリの履歴という3つの場所を一度に走査します(イメージとリポジトリは、前のステップで作った2つの検査スクリプトをそのまま呼び出します)。検出されたものが1つでもあれば、画面とレポートの最初の行にSECRETS-FOUND <전체 건수>(プレースホルダーは全体の件数です)を出力して3で、1つもなければSECRETS-CLEANを出力して0で終了します。ログディレクトリがないか、規則ファイルがないか、リポジトリでなければ、最初の行がERRORで始まる行を出して、1で終了します。レポートには、最初の行のあとに3つの場所ごとの結果を続けて書きますが、ログで検出された行にはそのファイルのパスが、リポジトリで検出された行にはそのパスが、見えている必要があります。レポートのパスの親ディレクトリがなければ作ります。実行権限を付けたあと、bash /root/secrets-lab/secret-gate.sh /root/secrets-lab/logs oci:/root/secrets-lab/oci/app-clean:v1 /root/secrets-lab/repo /root/secrets-lab/rules.txt /root/secrets-lab/report/gate.txtで、1回実行してください。最後に、/root/secrets-lab/revoke-checklist.txtにちょうど5行を<순번> <열쇠> <한 줄 설명>(プレースホルダーは、順番、キー、1行の説明です)の形で書いてください。キーは、この順序です。revoke、reissue、rotate-consumers、audit-access、prevent-recurrence。説明は、それぞれ10文字以上で、自分の言葉で書きます。

ゲートが隣にある検査スクリプトを呼び出すときは、自分の位置から探します。here=$(cd "$(dirname "$0")" && pwd)です。2つの検査スクリプトの終了コードは、0(きれい)・1(検出)・2(エラー)の3つなので、2が返ってきたら、ゲートも止まる必要があります。件数は、3つの場所から出た行を足した値です。イメージの検査スクリプトは、常に4行を出力するので、そのうち検出された行だけを数える必要があります。そして、このステップの本当の結論は、スクリプトではなくチェックリストにあります。履歴を書き換えて消す作業は、すでにクローンされたコピー・フォーク・キャッシュ・バックアップに何の影響も与えられず、その間に誰が読んだのかもわかりません。だから、最初の行が取り消しなのです。