Access NixOS SOPS Secret via Home Manager

I wanted to use sops for other things too, this is just basic example I wanted to do, so I could understand how it works and use it later.

Final version of this particular file

{
  lib,
  config,
  pkgs,
  HOSTNAME ? "unknown-host",
  ...
}:
let
  secretsFile = ../../secrets/secrets.yaml;
  privatekey = "ssh_${HOSTNAME}_privkey";

  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
        ''
      )
    );

  fromYAML = import (
    builtins.fetchTarball {
      url = "https://github.com/milahu/nix-yaml/archive/4b0f53d4e95e007f474d5ba7b2548d25f11c2afc.tar.gz";
      sha256 = "1bb7fywnpa8qvk5n5sd8bd5a84mlbjlh64pqbrwb02qqwds6i36w";
    }
    + "/from-yaml.nix"
  ) { inherit lib; };

  allSecrets = fromYAML (builtins.readFile secretsFile);

  sshPubkeys = lib.filterAttrs (
    name: _: lib.hasPrefix "ssh_" name && lib.hasSuffix "_pubkey_unencrypted" name
  ) allSecrets;

  authorizedKeysText = lib.concatStringsSep "\n" (lib.attrValues sshPubkeys);
in
{
  options.ssh-keys.enable = lib.mkOption {
    type = lib.types.bool;
    default = true;
    description = "enables ssh-keys module";
  };

  config = lib.mkIf (lib.hasAttr "ssh-keys" config && config.ssh-keys.enable) {
    sops = {
      defaultSopsFile = secretsFile;
      secrets.${privatekey} = {
      };
      defaultSymlinkPath = "/run/user/${builtins.toString uid}/secrets";
      defaultSecretsMountPoint = "/run/user/${builtins.toString uid}/secrets.d";
      age.keyFile = "/run/user/${builtins.toString uid}/sops-age.txt";
    };

    programs.ssh = {
      enable = true;
      enableDefaultConfig = false;
      matchBlocks."*".extraOptions = {
        IdentityFile = "/run/user/${toString uid}/secrets/${privatekey}";
      };
    };

    home.file.".ssh/.authorized_keys_nix" = {
      text = authorizedKeysText + "\n";
      recursive = true;
      onChange = ''
        systemctl --user restart fix-authorized-keys.service
      '';
    };

    home.activation.fixAuthorizedKeys = ''
      ${pkgs.coreutils}/bin/mkdir -p ${config.home.homeDirectory}/.ssh
      ${pkgs.coreutils}/bin/rm -f ${config.home.homeDirectory}/.ssh/authorized_keys_nix
      ${pkgs.coreutils}/bin/cp ${config.home.homeDirectory}/.ssh/.authorized_keys_nix \
         ${config.home.homeDirectory}/.ssh/authorized_keys_nix
      ${pkgs.coreutils}/bin/chmod 600 ${config.home.homeDirectory}/.ssh/authorized_keys_nix
    '';
  };
}

For the authorized_keys you could just created a template, tell sops nix where to put it and you would be done. I wouldn’t do that as I don’t think public keys need to be secrets but that is up to you. With templates you can create nearly any file type/format you might need if you want to store things in your sops secrets file.

sops.templates."" = {
  content = ''
    ${config.sops.placeholder."pubkey1"}
    ${config.sops.placeholder."pubkey2"}
  '';
  owner = "username";
  group = "group";
  path = "/home/username/.ssh/.authorized_keys_nix";
};

For normal secrets you can specify which owner and group they should be created as and where they should be stored. No hassle with ids or something alike.

  sops.secrets."private_keys" = {
    owner = "username";
    group = "username";
    path = "/home/username/.ssh/id_ed25519";
  };
  programs.ssh = {
    enable = true;
    matchBlocks."*".extraOptions = {
      IdentityFile = "/home/username/.ssh/id_ed25519";
    };
  };

Or you could do:

  sops.secrets."private_key" = {
    owner = "username";
    group = "username";
  };
  programs.ssh = {
    enable = true;
    matchBlocks."*".extraOptions = {
      IdentityFile = sops.secrets."private_key".path;
    };
  };

But maybe I still don’t get why you really need to do things more complicated.

1 Like

Thanks for the info. I also don’t think pubkeys should be treated as secrets — I just wanted to try it out as a test. Once things got complicated, I started digging for literally any way to make it work. It ended up like this simply because I don’t have a deep understanding of NixOS yet, so I just do things in whatever way I can to achieve my goals. It wasn’t about “I need to do it exactly this way,” but rather “I want to do something and get it working.”

1 Like