Best way to reference secrets as strings / variables?
What's the best way to reference secrets as variables so they can be used as values or substitutions in other Nix code (in my case, home-manager)?
Here's an example of what I'm trying to do:
home = {
programs = {
onepassword-secrets = {
enable = true;
# This doesn't work for home-manager opnix
# pathTemplate = "${config.home.homeDirectory}/.local/state/secrets/{service}/{name}";
secrets = {
cachixToken = {
reference = "op://Services/Cachix/token";
path = ".local/state/secrets/cachix.token";
mode = "0600";
};
};
};
};
};
# Or home.file?
xdg = {
configFile = {
"cachix/cachix.dhall".text = lib.generators.toDhall {} {
authToken = config.programs.onepassword-secrets.secretPaths.cachixToken;
hostname = "https://cachix.org";
binaryCaches = []; # : List { name : Text, secretKey : Text };
};
};
mime.enable = false;
};
}
That just puts the path to the file into the cachix config. If I try to use builtins.readFile - the build fails as that file doesn't exist at evaluation time.
Thanks!
Interested in figuring this out, too. I attempted to figure this out in May but was unsuccessful.
It seems, for home-manager at least, that there are issues at evaluation time like you say. I wonder if this is more doable in nixos or nix-darwin?
It appears as though reading through the code, there is no way to reference them as strings. You can only reference a path to the file. I would definitely like to see string references as a feature however.
There is no way of referencing to the secrets' values because every file written during nix evaluation will be written to the Nix store which is world readable and so defeats the purpose of using opnix (or sops-nix or agenix).
You can probably use something like scalpel to replace placeholders on activation. I've also done some hacks using systemd's ExecPreStart (on nixos) or launchd (on macOS).
Similar to what @CarlosZ suggested, I succeeded using activation scripts. I simply use a placeholder for the secrets and then in the activation script I read the secret from the file and replace the placeholder with a sed command.
Here an example:
Secret definition
security = {
opnix = {
secrets = {
apiKey = {
path = "secrets/api_key";
reference = "op://Development/woe3hj5uqm3cog2efpl33h65e4/credential";
group = "staff";
};
};
};
};
Activation
home.activation.generateConfig = home-manager.lib.hm.dag.entryAfter [ "writeBoundary" ] ''
# Substitute secrets
sed -i \
-e "s|__API_KEY__|$(<${config.programs.onepassword-secrets.secretPaths.apiKey})|" \
"$HOME/config.yaml"
'';
Is this the best way to do that? I am not sure, but at least it works It would be great if it would be possible to simply reference the secret, but I have no enough experience with Nix to know if this is possible without having the reference ending in the nix store.
As another alternative, I opted to use the 1Password CLI to inject secrets into my configs.
I wrote a small utility: https://github.com/itzTheMeow/dotfiles-nix/blob/master/lib/opinject.nix which enables me to simply do:
home.file.".config/immich/auth.yml" = {
text = ''
url: https://immich.example.com/api
key: {{op://Personal/Immich/API Keys/CLI}}
'';
force = true;
opinject = true;
};
to inject a secret into the file
i had to add a workaround to use the /usr/local/bin/op binary if it is installed, since the desktop app won't integrate with the CLI if installed via nix
i published it as a home-manager module: itzTheMeow/opinject