ryantm/agenix

how to use `agenix.homeManagerModules.default` on Darwin to manage an SSH-key?

pimvh opened this issue · 2 comments

Hi there! Thanks for this great project! I have been happily using this for a while, and it works great, both for Linux and Darwin.

I have been fighting a while with using the home-manager module on darwin of this project to manage secret files (SSH key in my case) within my home folder, and cannot figure it out (system-wide using nix-darwin works great!).

I have looked at related issues, like

  • #50, which goes into the features/limitations of the home-manager module
  • I also found: #180 (comment), but I don't think I understand how to apply that solution to my use-case

Attempt 1: just using path as 'normal'

By doing the following in home-manager

    programs.ssh = {
      enable = true;
      extraOptionOverrides = { };

      matchBlocks = {
        "github.com" = {
          user = "git";
          identityFile = config.age.secrets.githubKey.path;
        };

and this with agenix:

{ config, ... }: {
  age = {
    secrets = {
      ...

      githubKey = {
        file = ./github.age;
      };
      
      ...

    };

    identityPaths = [ "..." ];
  };
}

I get the following result:

Host github.com
  User git
  IdentityFile $(getconf DARWIN_USER_TEMP_DIR)/agenix/githubStubSshKey

Usage of env variables is supported in ~/.ssh/config by using ${VAR} syntax, but DARWIN_USER_TEMP_DIR is not exposed as a normal env variable to the user (only via getconf), which is different from the linux behaviour

source: https://github.com/ryantm/agenix/blob/8d37c5bdeade12b6479c85acd133063ab53187a0/modules/age-home.nix#L148C15-L148C22

Attempt 2: workaround using DAGS

As i noticed the darwin part of home-manager relies on shell, I have tried using a DAG, but now getconf is not available as a command in the build context (this currently only works on home-manager unstable, run is only available there).

home.activation.linkSshKeys = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
  run ln -s $VERBOSE_ARG ${config.pimvh.homeDirectory}/.ssh/github "${config.age.secrets.githubStubSshKey.path}"
'';

I am unsure whether either I am using agenix incorrectly, or have an incorrect idea on how I am supposed to achieve this. I think not using getconf might also be able to solve this issue for me, but that might have a security impact. I have been unable to find docs on why this is different across Darwin/Linux.

Could you point me in the right direction? :)

I would be happy to contribute to docs, when I figure out the proper solution.

I managed to fix this by doing the following voodoo, which works, but might not be the greatest solution to ever exist:

...
    home.sessionVariables = lib.mkIf config.pimvh.isNixDarwin {
      # getconf wants to append \n to the config var, which is why we need this voodoo
      DARWIN_USER_TEMP_DIR = with pkgs; ''$(${pkgs.coreutils}/bin/printf '%s' "$(${lib.getExe getconf} DARWIN_USER_TEMP_DIR)" | ${pkgs.coreutils}/bin/tr -d "\n")'';
    };

    home.file.".ssh/github.pub".source = "${self}/dotfiles/.ssh/github_${config.pimvh.hostname}.pub";

    programs.ssh =
      let
        turnAgenixPathToSshCompatible = x:
          if config.pimvh.isNixDarwin then
          # turn $(getconf $DARWIN_USER_TEMP_DIR)/<< PATH >>
          # into ${ENV_VAR}/<< PATH >>
            "\${DARWIN_USER_TEMP_DIR}${lib.concatStrings (lib.strings.match ".*\)(.*)" (builtins.toString x))}"
          else
          # turn $XDG_RUNTIME_DIR/<< PATH >>
          # into ${ENV_VAR}/<< PATH >>
            "\${XDG_RUNTIME_DIR}${lib.concatStrings (lib.strings.match ".[A-Z_]+\(.*\)" (builtins.toString x))}"
        ;
      in
      {
        enable = true;
        addKeysToAgent = "yes";
        # UseKeychain does not work with non-system versions of ssh
        # Which we have to rely on due to yubikey not being supported, see modules/darwin/yubikey.nix

        # extraConfig = lib.mkIf config.pimvh.isNixDarwin ''
        #   UseKeychain yes
        # '';

        matchBlocks = {
          "github.com" = {
            user = "git";
            identityFile = turnAgenixPathToSshCompatible config.age.secrets.githubKey.path;
          };
...

I can generalize and contribute (part of) this to home-manager, but I am not sure whether it would be a good fit.

This getconf DARWIN_USER_TEMP_DIR business seems like a pain, and I'm not sure why we're doing it that way.

On my system it evaluates the same as $TMPDIR, though I imagine we don't have access to this variable either.

$ test $(getconf DARWIN_USER_TEMP_DIR) = $TMPDIR && echo yup
yup

I see zero references to DARWIN_USER_TEMP_DIR in all of nixpkgs or home-manager, so it doesn't seem that anybody else is doing anything this way.

I'm sure there has been prior discussion on this. Seems like it should ideally be a directory that does not persist across reboots and obviously needs to be one with user permissions for creating files (which seems to rule out /run on MacOS).

I don't think stuff in ~/Library/Caches is cleared on reboot.