I’ve setup sops-nix in my flakes NixOS configuration. I can access secrets from my NixOS configuration files. How do I access a secret from Home Manager without having to pass in the --impure option?
Accessing SOPS Secrets via Home Manager
The Home Manager file that needs access to a secret:
This works when I rebuild my system with sudo nixos-rebuild switch --flake ./ --impure, however, I’d like to pass in the value(s) in sops.secrets... into Home Manager via my flake.nix file so Nix knows that it’s an external dependency (and presumably will not require the use of --impure).
Stripped Down Version of My flake.nix
{
inputs = {
...
sops-nix.url = "github:Mic92/sops-nix";
};
outputs = inputs@{ self, home-manager, nixpkgs, sops-nix, ... }: {
nixosConfigurations.<hostName> = inputs.nixpkgs.lib.nixosSystem {
specialArgs = { inherit inputs pkgs; };
modules = [
home-manager.nixosModules.home-manager
<pathToNixOsConfig> <----- This would be the NixOS file that
{ \---- configures sops-nix and currently
home-manager = { \--- accessed secrets w/o the --impure option.
extraSpecialArgs = { inherit inputs pkgs; };
users.<userName>.imports = [
<pathToUserHomeManagerConfig> <----- This would be the Home
]; \---- Manager file that is
}; \--- attempting to access
} \-- the sops secret.
];
};
};
}
It imported sops-nix and config.sops.secrets.<bla> exist.
The Bad
The path that’s returned is incorrect. Specificly, it’s %r/secrets/app/element-desktop/message-key when it should be /run/user/1000/secrets/app/element-desktop/message-key (because it’s a Home Manager modules rather than a NixOS one).
The Ugly
There exists no /run/user/1000/secrets/ directory, so the path is wrong, but even if it was correct, it doesn’t exist. It also doesn’t exist in the location it would be at if it was configured as a NixOS module (/run/secrets/app/element-desktop/message-key).
When I change .source to .text it writes this %r/secrets/app/element-desktop/message-key to the file and /run/user/1000/secrets/ still don’t exist.
Error Output
error:
… while calling the 'head' builtin
at /nix/store/5hwz775f3grzikafj1sbwx4lqkjwqswb-source/lib/attrsets.nix:922:11:
921| || pred here (elemAt values 1) (head values) then
922| head values
| ^
923| else
… while evaluating the attribute 'value'
at /nix/store/5hwz775f3grzikafj1sbwx4lqkjwqswb-source/lib/modules.nix:807:9:
806| in warnDeprecation opt //
807| { value = builtins.addErrorContext "while evaluating the option `${showOption loc}':" value;
| ^
808| inherit (res.defsFinal') highestPrio;
(stack trace truncated; use '--show-trace' to show the full trace)
error: A definition for option `home-manager.users.reedclanton.home.file."/home/reedclanton/Documents/Secrets/Element/security-key.txt".source' is not of type `path'. Definition values:
- In `/nix/store/z1ggqi8k3xhgcwc32702m2glhkr4k7qm-source/users/reedclanton/home/modules/applications/gui/element-desktop.nix': "%r/secrets/app/element-desktop/message-key"
Unfortunately, no luck. The value changes, but only to mach the new name (i.e. %r/secrets/app-element-desktop-message-key rather than %s/secrets/app/element-desktop/message-key).
Also, the same value I had works when I use --impure (as described in the original post), so I don’t think it’s an issue associated with how the secrets key is formatted. Plus, that secrets file has multiple other secrets in it that are currently functioning in the NixOS portion of the configuration.
Perhaps the issue relate to how sops-nix is imported. Maybe importing both NixOS and Home Manager modules system wide (i.e. from the flake) causes a conflict.
Hey, I actually was able to work around it. There’s a github issue for this from a while back, and it seems the solution is to append the following options to your sops config:
sops = {
# ... all the other stuff ...
defaultSymlinkPath = "/run/user/1000/secrets";
defaultSecretsMountPoint = "/run/user/1000/secrets.d";
};
Reason being that sops-nix apparently has no way of knowing your UUID at build time. (I’m not really happy with this solution because I would rather not hardcode the UUID, if you happen to know a way around that, please do tell!)
{ config, pkgs, ... }:
let
sops-path = builtins.toString inputs.nix-secrets;
uid = let
uidScript = pkgs.writeScript "get-uid" ''
#!${pkgs.runtimeShell}
id -u
'';
in pkgs.lib.strings.toIntBase10 (builtins.readFile
(pkgs.runCommand "get-uid-result" { } ''
${uidScript} >$out
'')
);
in
{
sops = {
defaultSymlinkPath = "/run/user/${builtins.toString uid}/secrets";
defaultSecretsMountPoint = "/run/user/${builtins.toString uid}/secrets.d";
secrets = {
# ...
};
};
# ... other config
}
There really should be a way to get your user id from the home manager configuration, but this was the best I could figure out so far. Perhaps there is an issue filed within the home-manager repository or this could become a feature request? If anyone finds an issue, it would be great to link it here!
And this of course evaluates in pure mode, so no need for an --impure flag either!
You know, I’m not sure if its getting the UID of the builder or the home manager. I would assume its getting the UID of the home manager user since I build as root and its returning 1000 in my derivation. A simple two user test should reveal what it’s doing. It all depends on who pkgs.runCommand is run as.
Also I would like to note that from this thread: Home Manager Symlinking Directories - #3 by user2358, you shouldn’t readFile your secret since it would put it in the nix store visible to everyone. Instead you could symlink the file so that permission are retained.
I am even more confused. I ran a simple nix repl and issued the same command, commands below if someone wants to try it. I ran the nix repl both as myself and as root using sudo su root. Both returned 1000, which is not correct, but also not what I was expecting. So even though I’m issuing the command as sudo nixos-rebuild switch --flake /etc/nixos, it returns 1000 as my uid. I would also assume that this does not work for two users, as it will return 1000 no matter what.
nix-repl> pkgs = import <nixpkgs> {}
nix-repl> let
uid = let
uidScript = pkgs.writeScript "get-uid" ''
#!${pkgs.runtimeShell}
id -u
'';
in pkgs.lib.strings.toIntBase10 (builtins.readFile
(pkgs.runCommand "get-uid-result" { } ''
${uidScript} >$out
'')
);
in {
uid = uid;
}
{ uid = 1000; }
I think a better way to not hard-code the UID’s is to somehow use arguments passed to the home manager through where you declare the home.nix file. From Home Manager Manual, near the bottom of the section:
Home Manager will pass osConfig as a module argument to any modules you create. This contains the system’s NixOS configuration.