• Nixで秘密鍵のお漏らしを阻止する


    これをやった。

    構成

    • git-hooks
    • git-secrets

    git-hooksはpre-commitをNixで扱いやすくしたもの。なので実質pre-commitと考えてOK。

    git-secretsはAWS LabsっていうAWS向けのサービスを公開してるところから公開されてるもの。 クレデンシャル検知ツールはsecertlintがデファクトなのだけど(多分)、シェルクスリプトで書かれていてNixでも扱いやすいと考え、今回はこれを採用した。

    pre-commitについて

    GitにはGit Hooksっていう.git/hooks配下のスクリプトを実行する機能があるのだけど、それを補助するのがpre-commitになる。 これらのスクリプトはコミットの前に実行されるためこのような名前になっている。(だと思う)

    これだけ聞くと便利なツールだと思うのだけど、pre-commitはPythonで書かれている。 これだけでウッとなる人も多いんじゃなかろうか。

    環境依存のトラブルが多いPythonをcommit時のチェックのためだけに導入するのは結構勇気がいる。 そこでNixを使う。

    Nixを使うとプロジェクト毎にPythonの環境を隔離するのなんて訳ないし、なんなら他のツールもまるっと隔離できる。

    実際のコード

    このブログのflakeには既に導入してある。 残念ながらgit-hooksにはまだgit-secretsが対応してないので、任意のhooksとして設定する。

    pre-commit = {
      check.enable = true;
      settings = {
        hooks = {
          # フォーマットされてるかチェック
          nixfmt-rfc-style.enable = true; 
          # カスタムHookを設定する
          git-secrets = {
            enable = true;
            name = "git-secrets";
            entry = "${git-secrets}/bin/git-secrets";
            language = "system";
            types = [ "text" ];
          };
        };
      };
    };

    entryには実行可能なスクリプトを指定できるので、writeShellApplicationで実行可能なshell scriptを作成してそのパスを指定している。

    git-secrets = pkgs.writeShellApplication {
      name = "git-secrets";
      runtimeInputs = [ pkgs.git-secrets ];
      text = ''
    
    	'^[a-z]{4}-[a-z]{4}-[a-z]{4}-[a-z0-9]{4}$'
        git secrets --scan
      '';
    };

    writeShellScriptBinとかでも良いのだけど、実行時の依存とかを考えてこう書いていた。 結局使わなかったし、いざ使う時はstore pathを指定すれば良いから必要なかったなと思っている。

    任意のpatternを追加する

    git-secretsはAWS Labsが作っているので当然AWSのサポートはされている。 ただ多くの場合はそれ以外パターンにも対応して欲しいだろう。

    そういう場合はgit secrets --add hogehogeで正規表現を追加できる。 これを毎回やるのは面倒なのでshellHookに書いてしまうのがオススメ。

    以下はBlueskyのアプリケーションパスワードにマッチするpatternをdevShellに入った時に追加する設定。

    shellHook = ''
        ${pkgs.git-secrets}/bin/git-secrets --add '''^[a-z]{4}-[a-z]{4}-[a-z]{4}-[a-z0-9]{4}$'
    '';

    また、これらのパターンをまとめたものとしてproviderというものがあり、これもカスタムできる。 実態はただのpatternを出力する実行ファイルなのでmkDerivationなり前述したwriteShellApplicationなりが使える。

    これについては後日使ってみたらその時また記事を書いてみようと思う。

    まとめ

    • pre-commitは普通に便利
    • Nixを使うと「Pythonで書かれている」だけでツールの採用を諦めなくて良くなる
    • Nixはビルドに使えなくても十分便利なのでドシドシ使おう

    参考

    pre-commitとgit-secretsが干渉することに思い当たらなかったので参考になった。

    追記

    2024/11/11

    以下をmkShellに追加しないとgit-hooksが有効にならないらしい。

    inputsFrom = [ config.pre-commit.devShell ];

    全体像としてはこうなる。

    pre-commit = {
      check.enable = true;
      settings = {
        hooks = {
          git-secrets = {
            enable = true;
            name = "git-secrets";
            entry = "${git-secrets'}/bin/git-secrets";
            language = "system";
            types = [ "text" ];
          };
        };
      };
    };
    
    devShells.default = pkgs.mkShell {
      inputsFrom = [ config.pre-commit.devShell ];
      packages = with pkgs; [ ... ]
    
    shellHook = ''
      ln -s ${textlintrc} .textlintrc 
      ${pkgs.git-secrets}/bin/git-secrets --add '''^[a-z]{4}-[a-z]{4}-[a-z]{4}-[a-z0-9]{4}$'
    '';