How to hide private info in a flake-based config?

So I have all my configs open-sourced on a public Git hoster, including my NixOS configuration. That’s all nice and dandy but now I’m faced with having to use the GLPI inventory system agent to automatically inventory my device and the installed packages for my employer.

I thought about having a public inventory.nix on the repo providing the module and a private inventory_entry.nix (not tracked by git) where I instanciate it with all the (partially confidential) data necessary, looking somewhat like this:

{ config, pkgs, ... }:

{ 
  services.work.inventory = {
    enable = true;
    url = "https://example.org/.../";
    inventoryTag = "xxxxxxxx";
    assetTag = "yyyyyyyyy";
    user = "foo";
  };
}

However, when this file is not in git, nixos-rebuild complains about the file not existing (because it only copies all tracked files, I know):

felix@entropy /e/nixos (main) > sudo nixos-rebuild switch --flake '.#'
[...]
error: getting status of '/nix/store/fhycm265qb2hm6kml9jycvqqqxlyl0q4-source/entropy/nixos/modules/inventory_entry.nix': No such file or directory
(use '--show-trace' to show detailed location information)

Is there any way I could solve this problem without leaking confidential data to the general public?

I realize now that one could maybe use sops as a suitable tool for the job, however I would have to readFile all the fields, probably like this:

  services.work.inventory = {
    enable = true;
    url = builtin.readFile /run/secrets/glpi_url;
    inventoryTag = builtin.readFile /run/secrets/glpi_inventory;
    assetTag = builtin.readFile /run/secrets/glpi_asset;
    user = builtin.readFile /run/secrets/glpi_user;
  };

Is this the best option?

Probably not, it’s impure. You’re depending on files that are only available at runtime at build time. This makes it impossible to deploy your configuration from scratch, because sops-nix won’t have deployed your secrets yet before you evaluate your configuration.

I was actually under the impression that flakes don’t allow resolving absolute paths like that. The whole copying tracked files is specifically designed to prevent you from shooting yourself in the foot like this. Do you use the --impure flag? Or is builtins.readFile an exception somehow?

Anyway, the better way of doing this is to think hard about what actually is confidential and why, and once you’ve identified an exact list, change the modules you’re using to read the sops-created files at run time, i.e. with systemd’s LoadCredential, or a little shell script or something. See any of the passwordFile options for how others implement this.

This also prevents the files from going into the publicly readable nix store, which may also break your confidentiality requirements, especially once remote build hosts and caches are involved.

With non-password data, you’ll often find that people don’t provide good mechanisms for reading them from files, so this may be a fair bit of effort. Hence I suggest thinking really hard about what needs to be confidential before attempting it, maybe check with your employer for specific guidelines.

You can also consider something like git-crypt, but note that this doesn’t prevent the data going to the nix store.

1 Like

I would look at defining a public repository with all the non-public parts empty, then import it from another repository that only applies non-public config changes (e.g. fills the inventory)

1 Like

I was writing that up before actually testing it, lo and behold it doesn’t work. And especially considering that I want a hydra to build my system configuration, this is not the right way to move forward.

Thank you! This is probably what I’m going to try. Seems to be a much better option in the long run, even though there’s some overhead attached to get it to work now.

That was the other option I saw though I would like to try to avoid having multiple repositories.

Well, flakes → git → ACL is by-repo; and I think having a private repo is better than having to manage backups of something that lives inside a git repo but is not actually a part of the repo.

Run-time reading is of course a very reasonable option as then the private things can be stored in a separate place with a separate backup logic.

1 Like