ryantm / agenix

age-encrypted secrets for NixOS and Home manager
https://matrix.to/#/#agenix:nixos.org
Creative Commons Zero v1.0 Universal
1.57k stars 119 forks source link

home-manager integration? #50

Open Radvendii opened 3 years ago

Radvendii commented 3 years ago

Is it possible to use agenix from inside a home-manager module? I don't see anything talking about this, so I'm guessing it's not possible, but I figured I'd check.

I realize I can use an agenix secret from inside a home-manager module by just accessing the /run/secrets/blah file, but I'd like to keep the file generation (i.e. age.secrets.blah.file = "${self}/secrets/blah";) close to the point of use, rather than off in it's own NixOS module.

ryantm commented 3 years ago

I don't think it will work by default. We'll need to update the module to work with it. This is definitely in scope for this project.

Pacman99 commented 3 years ago

A few of us came to a similar conclusion a while ago: https://github.com/divnix/devos/discussions/319. Thanks for actually opening the issue here.

Radvendii commented 3 years ago

This seems to have been attempted: https://github.com/jordanisaacs/homeage

I wonder if it would be possible / desireable to merge that into agenix, or better to leave it separate.

Gerschtli commented 3 years ago

Because both project try to achieve the same result except different build environments, these should at some point be merged. Ping @jordanisaacs for your opinion on that.

jordanisaacs commented 3 years ago

The reason I wrote mine separately as I see my project being primarily a module for home-manager. My vision for homeage doesn't involve a CLI (interested only in the module) and its on pace to become more intimately tied to home-manager. So while both are similar, I see them as fitting different use cases: agenix for the system and CLI, homeage for declarative use with home-manager

Gerschtli commented 3 years ago

I think it would create a more consistent experience if the modules for NixOS and home-manager work the same (from a UX point of view). I agree, that agenix currently only supports system configuration, but a home-manager integration is possible. Also I think there are many common logic parts or scripts (like module options, the shell script for linking/copying, the cleanup logic, etc.) that could be shared between NixOS and home-manager implementation. That would improve the quality of both project because the actual logic only needs to be written once.

And the CLI seems to be replaced by agenix-cli in the future anyway.

Opinions @jordanisaacs @ryantm?

Nebucatnetzer commented 2 years ago

Would be nice if agenix would work with home-manager as well. Just for the sake of uniformity in the config.

aouerf commented 1 year ago

This can probably be closed because of #180 ?

Nebucatnetzer commented 1 year ago

I have seen that as well but how do you use it?On 25 Jun 2023, at 18:45, Ahmad Ouerfelli @.***> wrote: This can probably be closed because of #180 ?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

sellout commented 1 year ago

Yeah, I think it's reasonable to keep this open pending docs. I've used it already, so I can probably write it up today or tomorrow.

Nebucatnetzer commented 1 year ago

That would be great!On 25 Jun 2023, at 21:09, Greg Pfeil @.***> wrote: Yeah, I think it's reasonable to keep this open pending docs. I've used it already, so I can probably write it up today or tomorrow.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

Brawl345 commented 1 year ago

any updates on this? Struggling to get it to work, tried adding agenix.homeManagerModules.age in my home-manager config to ìmports` but it keeps erroring out with "infinite recursion encountered" :/

sellout commented 1 year ago

Sorry, I apologize for not getting to this yet. I’ve only used it via flakes, where you do

    {
      homeConfigurations = {
        "user@host" = inputs.home-manager.lib.homeManagerConfiguration {
          modules = [
            inputs.agenix.homeManagerModules.age # ← the important bit
          ];
        };
      };
    };

for a non-flake config (like it sounds like you have, @Brawl345), I think you should instead do something like

{
  imports = [ <agenix/modules/age-home.nix> ];
}

that assumes you’re using nix channels, but the instructions here should translate from NixOS to home-manager with a few substitutions:

@Nebucatnetzer @Brawl345 – let me know if this short explanation solved your issues, so I know if my writeup will be correct.

Brawl345 commented 1 year ago

I use flakes with nix-darwin and home-manager as a nix-darwin module. I had some mistake in my (spaghetti) config and now everything works! Here is a minimal flake:

{
  description = "sample";

  inputs = {
    nixpkgs = {
      url = "github:NixOS/nixpkgs/nixpkgs-unstable";
    };

    darwin = {
      url = "github:lnl7/nix-darwin/master";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    agenix = {
      url = "github:ryantm/agenix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { self, nixpkgs, darwin, home-manager, agenix, ... } @ inputs:
    {
      darwinConfigurations = {
        someuser = darwin.lib.darwinSystem {
          system = "aarch64-darwin";
          specialArgs = { inherit inputs; };
          modules = [
            agenix.darwinModules.default
            home-manager.darwinModules.home-manager
            {
              home-manager.useGlobalPkgs = true;
              home-manager.useUserPackages = true;
              home-manager.users.someuser = { config, pkgs, ... }: {
                programs.home-manager.enable = true;
                home.stateVersion = "22.11";
                manual.manpages.enable = false;
                manual.html.enable = false;
                manual.json.enable = false;
                programs.bash.enable = true;

                imports = [
                  agenix.homeManagerModules.default
                ];

                age.identityPaths = [ "/Users/someuser/.ssh/id_default_ed25519" ];
                age.secrets.secret1.file = ./secrets/secret1.age;
                home.sessionVariables = {
                  SECRET_VALUE = ''
                    $(${pkgs.coreutils}/bin/cat ${config.age.secrets.secret1.path})
                  '';
                };
              };
            }
          ];
        };
      };
    };
}

The age.identityPaths is very important or else nothing gets decrypted and you won't get an error. On NixOS it uses config.services.openssh.hostKeys.

Nebucatnetzer commented 1 year ago

I reckon with the examples by @sellout and @Brawl345 I should be able to integrate it.

Nebucatnetzer commented 1 year ago

Hm I'm a bit confused. When I do offlineimap -o I get cat: '$XDG_RUNTIME_DIR/agenix/personalEmailKey': No such file or directory but when I run the command manually I see the password. The config is basically the same just with the new module. Here is a diff of the new config: https://git.2li.ch/Nebucatnetzer/nixos/pulls/3/files

ldicarlo commented 1 year ago

Hi, and thank you for this awesome package. So I see the same error as @Nebucatnetzer. It seems that age.secrets.github_token.file = ./secrets/github_token.age; results in ${config.age.secrets.github_token.path} being evaluated to /agenix/github_token

Since it results in the log /agenix/github_token: No such file or directory during switch.

(I use a flake).

/agenix/ folder does not exists on my computer. What is the folder supposed to be when using Home Manager Agenix Module? Thank you

[EDIT] Also when I update the secret I don't see any new generation in /run/agenix.d. I though I would see a new generation, but I am no expert in this package.

ldicarlo commented 1 year ago

I managed to do it. I think there are 2 problems:

Currently on a non-darwin system, this is what I have (and work):

imports = [ inputs.agenix.homeManagerModules.default ]; age.secrets.secret1.file = ./secrets/sec1.age;

home.packages = [ (pkgs.writeShellScriptBin "echo-secret" '' ${pkgs.coreutils}/bin/cat ${config.age.secrets.secret1.path} '') ];



secrets.nix with basic info like in the tutorial.

[EDIT]
Just to confirm, each time I run `agenix -e ...` and then `nixos-rebuild switch ... ` I still need to run `systemctl --user start agenix.service` after. 
Nebucatnetzer commented 1 year ago

I can’t remember it from the top of my head but I think there is a way to force a restart of a service after a rebuild. Might be a useful workaround for this. I hadn’t had time yet to try a second time but I hope to be able to test it soon.

shymega commented 1 year ago

I'm having a similar issue with Home Manager+Agenix, and the $XDG_RUNTIME_DIR.

I'm basically storing my Taskwarrior sync CA/certificate/key, and credentials in Age-encrypted files.

My home manager configuration is fairly simple. The secrets can be decrypted, but the produced file for Taskwarrior keeps the $XDG_RUNTIME_DIR path in the config. This is invalid for Taskwarrior, and I'm a little unsure of how to proceed. I think this is something that could be extended in Agenix to have an output with the full, expanded path to the decrypted secret.

I really like Agenix, and I'm happy with it thus far. I just don't want to use solutions involving cat or builtin.readFile for the security risks involved there.

My configuration extract for Taskwarrior is listed below, as well as the produced configuration by Home Manager:

    taskwarrior = {
      enable = true;
      config = {
        confirmation = false;
        report = {
          minimal.filter = "status:pending";
          active.columns = [ "id" "start" "entry.age" "priority" "project" "due" "description" ];
          active.labels = [ "ID" "Started" "Age" "Priority" "Project" "Due" "Description" ];
        };
        taskd = {
          certificate = config.age.secrets.taskwarrior_sync_cert.path;
          key = config.age.secrets.taskwarrior_sync_key.path;
          ca = config.age.secrets.taskwarrior_sync_ca.path;
          server = "inthe.am:53589";
          credentials = config.age.secrets.taskwarrior_sync_cred.path;
        };
      };
    };
  };
data.location=/home/dzrodriguez/.local/share/task

confirmation=false
report.active.columns=id,start,entry.age,priority,project,due,description
report.active.labels=ID,Started,Age,Priority,Project,Due,Description
report.minimal.filter=status:pending
taskd.ca=$XDG_RUNTIME_DIR/agenix/taskwarrior_sync_ca
taskd.certificate=$XDG_RUNTIME_DIR/agenix/taskwarrior_sync_cert
taskd.credentials=$XDG_RUNTIME_DIR/agenix/taskwarrior_sync_cred
taskd.key=$XDG_RUNTIME_DIR/agenix/taskwarrior_sync_key
taskd.server=inthe.am:53589
shymega commented 1 year ago

I can’t remember it from the top of my head but I think there is a way to force a restart of a service after a rebuild. Might be a useful workaround for this. I hadn’t had time yet to try a second time but I hope to be able to test it soon.

That would be really useful to know. If you do remember, maybe a PR to the documentation would be great!

Nebucatnetzer commented 1 year ago

Maybe extending home.activation helps. https://nix-community.github.io/home-manager/options.html#opt-home.activation IIRC what I was using was the userActivationScript from NixOS. Doesn't sound like the most optimal solution however.

Deadbyrd12 commented 1 year ago

I solved the issue of restarting the service on my system by adding this line: systemd.user.startServices = "sd-switch"; to my home-manager config.

This changes the general behavior when it comes to starting/stopping services after switching generations. That means it might cause some weird side effects. The default is "suggest" which prints some suggestions as to which services should be restarted manually. "sd-switch" uses a 3rd-party app to start/stop the required services (requires a user dbus session). It seems this is going to become the default at some point. See the systemd home-manager module.

jackwilsdon commented 9 months ago

I'm currently using the following workaround for machines which do not have a dbus session (and therefore can't use systemd user units):

# In home-manager.users.your-name-here
home.activation.agenix = lib.hm.dag.entryAnywhere config.systemd.user.services.agenix.Service.ExecStart;

It's a bit of a hack as agenix doesn't expose the user activation script anywhere, but it appears to work well enough. It seems to me like agenix might be better off using an activation script with home-manager too (like it already does with the system-wide version, as suggested by @Nebucatnetzer), instead of systemd/launchd as it currently uses.

jackwilsdon commented 9 months ago

The above only seems to work at boot if you change the secretsDir and secretsMountPoint to be outside of $XDG_RUNTIME_DIR, as this isn't set (and the directory likely doesn't even exist) at boot:

# In home-manager.users.your-name-here
age = {
  secretsDir = "${config.home.homeDirectory}/.agenix/agenix";
  secretsMountPoint = "${config.home.homeDirectory}/.agenix/agenix.d";
};
kukovecz commented 7 months ago

Hello,

I am really new in learning nix and using some base ubuntu (linux mint precisely) with nix as package manager and home manager to setup my config.

It was a little bit painful to setup agenix in this way, but it works, thanks for all the help in the documentation and people responses in this issue.

I want to leave the working configuration here if it might help anyone.

I am using flakes and I've put this to my flake.nix:

homeConfigurations."username" = home-manager.lib.homeManagerConfiguration {
  inherit pkgs;

  modules = [
    # <other modules>
    agenix.homeManagerModules.age
    {
      age = {
        secrets = {
          "mysecret1" = {
            file = path/to/mysecret1.age;
            # owner = "username";  <-- This is not working
          };
        };
        secretsDir = "<home of username>/.agenix/agenix";
        secretsMountPoint = "<home of username>/.agenix/agenix.d";
        identityPaths = [ "<path/to/private/key/for/mysecret1.age>" ];
      };
    }
    # <other modules>
  ];
};

The owner setting is not working for the secret, despite stating in the documentation.

By not working, I mean

error: The option `age.secrets."mysecret1".owner' does not exist.

I assume this is intentional (that option does not exist in age-home.nix), because as this being a home manager module, the owner will always be username.

Yeshey commented 6 months ago

I was trying to write a user service that would put my secrets where I wanted, apparently you need systemd.user.services.<my_service>.Unit.After = [ "agenix.service" ]; for it to work, figured out by reading the sops-nix documentation, some similar documentation on agenix would be cool. Im not using systemd.user.startServices = "sd-switch";. With help fom @ldicarlo answer this is the service I got:

let
  inherit (pkgs.stdenv.hostPlatform) system; # for agenix pkg
  mystuff = pkgs.writeShellScriptBin "echo-secret" ''
        ${pkgs.coreutils}/bin/cat ${config.age.secrets.my_identity.path} > /home/yeshey/Downloads/ImOkay.txt
      '';
in
{
    home.packages = [
      inputs.agenix.packages.${system}.agenix
      mystuff # so now in the terminal running `echo-secret` runs the above command
    ];

    systemd.user.services."test" = {
      Unit = {
        Description = "test";
        After = [ "agenix.service" ];
      };
      Service = {
        Type = "oneshot";
        ExecStart = "${mystuff}/bin/echo-secret";
      };
      Install.WantedBy = [ "default.target" ];
    };
}