🔐

sops-nix入門

に公開

初投稿なので至らぬ点があるかもしれませんが、もし分かりにくい所があればご指摘ください。

sops-nixへの誘い

まず、sops-nixは名前からしてNix上で使用するものである。Nixを使用することで、Linux上で使用するパッケージを宣言的に定義でき、ファイルや設定が散らかりにくくなる。といったことはこの記事を見たい人なら分かりきったことと思う。

筆者はNixに入門したのが3ヶ月前、NixOSのInstallに踏み切ったのが2ヶ月前の素人である。もともとWindowsメインで、UbuntuをWSL2上やWinとデュアルブートして使用していた。PCを弄る問題として、どこをどう弄ったか覚えていられない、というのがある(と思っている)。大昔に自分で作った設定の残骸が残っていたり、コマンドでした設定によって裏で生成されたファイルなどが貯まっていたりして、どこでどう影響しているのか把握しづらい。

そのため、Linux環境を壊して新たに作り直そうと思っていたので、某所で活気ありそうなNixに手を出した。そこで手応えを感じて、少し古い、今はあまり使わないPCにNixOSをInstallした。

ここで問題が発生した。バージョン管理のためにgitとFlakeを使用している場合、Nix関係の全てのファイルはgit管理下である必要がある。しかし、このgitリポジトリをgithub上で管理したいのだ。パスワードのような機密情報を含めたいのにである。そこで、sops-nixなどを使用する。

https://github.com/Mic92/sops-nix

できること

  • 機密情報を含むファイルの暗号化
  • 公開鍵による暗号化
  • git上で暗号化したファイルをNixで配置
  • 複数公開鍵の使用
  • gitはできるがssh先にできない新環境へ暗号化状態を保ったまま引っ越し

できないこと

  • 暗号化したデータのNix式での使用(おそらく)

agenixでもsops-nixと同じようなことはできる。

Install

sops-nixをグローバルにInstallする。おそらくconfigure.nixをFlake化していると思われるので、以下のようになる。

Flake.nix
{
    inputs = {
        nixpkgs.url = "github:nixos/nixpkgs?ref=release-25.05";  # お好みのバージョンで
        sops-nix = {
            url = "github:Mic92/sops-nix";
            inputs.nixpkgs.follows = "nixpkgs";
        };
    };
    outputs = {self, nixpkgs, sops-nix, ...}: {
        nixConfigurations.yourname = nixpkgs.lib.nixosSystem {
            modules = [
                ./configure.nix
                sops-nix.nixosModules.sops
            ];
        };
    };
}

公開鍵の生成

GPGかageを使用する。ここではageにする。

bash
nix-shell -p age
age-keygen -o filename

指定したfilenameの中に公開鍵と秘密鍵のペアが生成される。ssh-keygenが2つのファイルに分けて生成するのとは対称的である。

次に、生成した公開鍵をsopsに認識させる。鍵情報は.sops.yamlというファイルを生成し、記入する。このファイルの置き場所だが、以降でsopsコマンドを使用した際に親ディレクトリに遡って検索され、無ければ$HOME/.sops.yaml、それも無ければ$XDG_CONFIG_HOME/sops/config.yamlが使用される。

.sops.yaml
keys:
  - &hostname age1234...  # 公開鍵を記入
creation_rules:
  - path_regex: secrets/[^/]+\.(yaml|json|env|ini)$  # 暗号化したファイルとして認識するpathの正規表現
    key_groups:
    - age:
      - *hostname

ここで、hostnameは任意の名前を使用できる。また、異なる名前で複数並べることにより、複数の鍵を使用することができる。

公開鍵を登録したら、秘密鍵も登録する。ageで生成した、公開鍵と秘密鍵の両方を含むファイルを設置し、configure.nixに場所を記述する。このファイルはgit管理外にするので、鍵ファイルをPath型ではなく文字列で指定していることに注意。なお、ここではグローバルで鍵を使用したかったのでこの場所に置いたが、sopsからはデフォルトで$XDG_CONFIG_HOME/sops/age/keys.txtが設置場所として認識される。そのため、environment.variablesなどで$SOPS_AGE_KEY_FILEに鍵の場所を指定しておく。これを忘れると、暗号化したファイルを復号できなくなる。

./configure.nix
{config, lib, pkgs, ...}:
let
    ageKeyFile = "/var/lib/sops-nix/key.txt";  # 鍵ファイルの場所(文字列)
in
{
    sops = {
        age.keyFile = ageKeyFile;  # 鍵ファイルの場所を指定
        age.generateKey = true;  # もし鍵ファイルが無ければ自動生成する
    };
    environment.variables = {
        SOPS_AGE_KEY_FILE = ageKeyFile;  # 鍵ファイルの場所を環境変数に
    };
}

暗号化ファイルの生成

yamlやjsonなどが使用できる。これらのファイル内に暗号化したいデータを記入し、暗号化させると、復号した際に中の値ごとにファイルとしてアクセスできるようになる。

sopsを使用してファイルを開くことで、保存した際に自動で暗号化される。このとき、$EDITORで指定したエディタを使用する。後から追記したい場合も同様だが、鍵ファイルが見当らないと復号して編集できないので注意。

bash
nix-shell -p sops
sops secrets/filename.yaml

初めて開く際は中身が自動生成されるので、何となく雰囲気が分かるはず。

暗号化したファイルはgit管理する。このファイルもNixで登録する。

configure.nix
{
    sops = {
        age.keyFile = "/var/lib/sops-nix/key.txt";
        age.generateKey = true;
        defaultSopsFile = ./secrets/secret.yaml;  # デフォルトで使用する暗号化したファイル
        defaultSopsFormat = "yaml";  # デフォルトで使用するファイル形式
        secrets."${attrName}".neededForUsers = true;  # このattrNameはログインしている状態で使用可能にする?
    };
}

暗号化したデータをNixで使用したい場合、config.sops.secrets."${attrName}".pathで属性attrNameの値が記入されたファイルを指定できる。

configure.nix
{
    users.users.${username} = {
        hashedPasswordFileName = config.sops.secrets.myHashedPassword.path;  # 事前にハッシュ化パスワードを記入していた
    };
}

ハッシュ化パスワードを公開するのはNG(ブルートフォースで簡単に破られる)なので公開鍵暗号化した。ただ、ローカルに平文パスワードを置くのも良くないのでハッシュ化パスワードを使用している。

これで、暗号化したデータをgitで管理しつつ、ファイルの場所をNixから参照できるようになった。

新環境への追加

ここで、以下のケースを考える。

  • 事前にsops-nixで暗号化したファイルを含むNix環境を用意した。これを新しい環境に持ち込みたい。
  • しかし、ネットワーク的に離れた場所にあり、ローカルから新環境へsshできない。新環境は遠隔ではなく直接触る。
  • 新環境はgitを使用できる。githubへのアクセスはあらかじめisoにssh秘密鍵を入れていた、などで可能であるとする。
  • sopsの鍵は共有できていない。忘れたとか後からsopsを導入したからとか理由は色々。

この場合、以下の方法で共有することができる。

  1. 新環境で鍵を生成。sops.age.generateKey = true;による場合、ユーザーがrootなので適宜chownする。
  2. 生成された公開鍵を.sops.yamlに追記する。keyscreation_rules.key_groupsの両方にすること。
  3. gitで元の環境に送る。
  4. nix-shell -p sops --run "sops updatekeys secrets/example.yaml"でファイルを再暗号化する。
  5. 新環境へ送り返す。

環境ごとに鍵を分け、使用しなくなったら.sops.yamlから削除する、という運用もありかもしれない。

まとめ

  • 公開鍵は.sops.yamlに記述する。
  • 秘密鍵はsops.age.keyFileに指定した場所に置き、$SOPS_AGE_KEY_FILEでも指定する。
  • 暗号化ファイルの中身はファイルとして取り出せる。そのpathはconfig.sops.secrets."${attrName}".pathに格納される。

おわりに

複数ファイルに分けて暗号化したり、暗号化するファイルの形式に縛られず丸ごと暗号化したりすることもできるが、試せていない。機会があればやってみたいが、どういうシチュで使えばいいのか?ssh秘密鍵とか?

あと、設定した後で調べてみたらagenixの方がよく使用されている気が……。ただ、agenixは機密情報をgitリポジトリから直接読み取っている?ローカルで暗号化ファイルを置いておかずに、ということか?わからん……

Discussion