From 4fb1eef0c0858a09868384d381ff6ca926c7d812 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Tue, 22 Apr 2025 06:16:55 +0700 Subject: [PATCH 1/4] add native support for ssh keys for age --- modules/home-manager/sops.nix | 29 ++++++++++++++++++++++++++++- modules/nix-darwin/default.nix | 28 +++++++++++++++++++++++++++- modules/nix-darwin/manifest-for.nix | 1 + modules/sops/default.nix | 28 +++++++++++++++++++++++++++- modules/sops/manifest-for.nix | 1 + pkgs/sops-install-secrets/main.go | 7 ++++++- 6 files changed, 90 insertions(+), 4 deletions(-) diff --git a/modules/home-manager/sops.nix b/modules/home-manager/sops.nix index ad6fe64..892eeb6 100644 --- a/modules/home-manager/sops.nix +++ b/modules/home-manager/sops.nix @@ -98,6 +98,7 @@ let gnupgHome = cfg.gnupg.home; sshKeyPaths = cfg.gnupg.sshKeyPaths; ageKeyFile = cfg.age.keyFile; + ageSshKeyFile = cfg.age.sshKeyFile; ageSshKeyPaths = cfg.age.sshKeyPaths; placeholderBySecretName = cfg.placeholder; userMode = true; @@ -250,11 +251,23 @@ in ''; }; + sshKeyFile = lib.mkOption { + type = lib.types.nullOr pathNotInStore; + default = null; + example = "/home/someuser/.ssh/id_ed25519"; + description = '' + Path to ssh key file that will be used by age for sops decryption. + ''; + }; + sshKeyPaths = lib.mkOption { type = lib.types.listOf lib.types.path; default = [ ]; description = '' - Paths to ssh keys added as age keys during sops description. + Paths to ssh keys added as age keys during sops description. The ssh + keys will be converted into age keys manually using ssh-to-age. + + This option is deprecated and will be removed in the future. Use sops.age.sshKeyFile instead. ''; }; }; @@ -301,6 +314,7 @@ in || cfg.gnupg.sshKeyPaths != [ ] || cfg.gnupg.qubes-split-gpg.enable == true || cfg.age.keyFile != null + || cfg.age.sshKeyFile != null || cfg.age.sshKeyPaths != [ ]; message = "No key source configured for sops. Either set services.openssh.enable or set sops.age.keyFile or sops.gnupg.home or sops.gnupg.qubes-split-gpg.enable"; } @@ -323,6 +337,19 @@ in } ]; + warnings = [ + (lib.mkIf + ( + cfg.age.sshKeyPaths != [ ] + && cfg.gnupg.sshKeyPaths == [ ] + && cfg.gnupg.home == null + && cfg.age.keyFile == null + && cfg.age.sshKeyFile == null + ) + "The option sops.age.sshKeyPaths has been deprecated, since age now has native SSH support. Use option sops.age.sshKeyFile instead." + ) + ]; + home.sessionVariables = lib.mkIf cfg.gnupg.qubes-split-gpg.enable { # TODO: Add this package to nixpkgs and use it from the store # https://github.com/QubesOS/qubes-app-linux-split-gpg diff --git a/modules/nix-darwin/default.nix b/modules/nix-darwin/default.nix index f169a30..15fc918 100644 --- a/modules/nix-darwin/default.nix +++ b/modules/nix-darwin/default.nix @@ -300,12 +300,24 @@ in ''; }; + sshKeyFile = lib.mkOption { + type = lib.types.nullOr pathNotInStore; + default = null; + example = "/etc/ssh/ssh_host_ed25519_key"; + description = '' + Path to ssh key file that will be used by age for sops decryption. + ''; + }; + sshKeyPaths = lib.mkOption { type = lib.types.listOf lib.types.path; default = defaultImportKeys "ed25519"; defaultText = lib.literalMD "The ed25519 keys from {option}`config.services.openssh.hostKeys`"; description = '' - Paths to ssh keys added as age keys during sops description. + Paths to ssh keys added as age keys during sops description. The ssh + keys will be converted into age keys manually using ssh-to-age. + + This option is deprecated and will be removed in the future. Use sops.age.sshKeyFile instead. ''; }; }; @@ -345,6 +357,7 @@ in cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [ ] || cfg.age.keyFile != null + || cfg.age.sshKeyFile != null || cfg.age.sshKeyPaths != [ ]; message = "No key source configured for sops. Either set services.openssh.enable or set sops.age.keyFile or sops.gnupg.home"; } @@ -383,6 +396,19 @@ in }) { + warnings = [ + (lib.mkIf + ( + cfg.age.sshKeyPaths != [ ] + && cfg.gnupg.sshKeyPaths == [ ] + && cfg.gnupg.home == null + && cfg.age.keyFile == null + && cfg.age.sshKeyFile == null + ) + "The option sops.age.sshKeyPaths has been deprecated, since age now has native SSH support. Use option sops.age.sshKeyFile instead." + ) + ]; + sops.environment.SOPS_GPG_EXEC = lib.mkIf (cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [ ]) ( lib.mkDefault "${pkgs.gnupg}/bin/gpg" ); diff --git a/modules/nix-darwin/manifest-for.nix b/modules/nix-darwin/manifest-for.nix index 5015659..edc6b3e 100644 --- a/modules/nix-darwin/manifest-for.nix +++ b/modules/nix-darwin/manifest-for.nix @@ -15,6 +15,7 @@ writeTextFile { gnupgHome = cfg.gnupg.home; sshKeyPaths = cfg.gnupg.sshKeyPaths; ageKeyFile = cfg.age.keyFile; + ageSshKeyFile = cfg.age.sshKeyFile; ageSshKeyPaths = cfg.age.sshKeyPaths; useTmpfs = false; placeholderBySecretName = cfg.placeholder; diff --git a/modules/sops/default.nix b/modules/sops/default.nix index b990e3e..6f38838 100644 --- a/modules/sops/default.nix +++ b/modules/sops/default.nix @@ -339,12 +339,24 @@ in ''; }; + sshKeyFile = lib.mkOption { + type = lib.types.nullOr pathNotInStore; + default = null; + example = "/etc/ssh/ssh_host_ed25519_key"; + description = '' + Path to ssh key file that will be used by age for sops decryption. + ''; + }; + sshKeyPaths = lib.mkOption { type = lib.types.listOf lib.types.path; default = defaultImportKeys "ed25519"; defaultText = lib.literalMD "The ed25519 keys from {option}`config.services.openssh.hostKeys`"; description = '' - Paths to ssh keys added as age keys during sops description. + Paths to ssh keys added as age keys during sops description. The ssh + keys will be converted into age keys manually using ssh-to-age. + + This option is deprecated and will be removed in the future. Use sops.age.sshKeyFile instead. ''; }; }; @@ -405,6 +417,7 @@ in cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [ ] || cfg.age.keyFile != null + || cfg.age.sshKeyFile != null || cfg.age.sshKeyPaths != [ ]; message = "No key source configured for sops. Either set services.openssh.enable or set sops.age.keyFile or sops.gnupg.home"; } @@ -483,6 +496,19 @@ in }; }) { + warnings = [ + (lib.mkIf + ( + cfg.age.sshKeyPaths != [ ] + && cfg.gnupg.sshKeyPaths == [ ] + && cfg.gnupg.home == null + && cfg.age.keyFile == null + && cfg.age.sshKeyFile == null + ) + "The option sops.age.sshKeyPaths has been deprecated, since age now has native SSH support. Use option sops.age.sshKeyFile instead." + ) + ]; + system.build.sops-nix-manifest = manifest; } ]; diff --git a/modules/sops/manifest-for.nix b/modules/sops/manifest-for.nix index 1824668..dc40065 100644 --- a/modules/sops/manifest-for.nix +++ b/modules/sops/manifest-for.nix @@ -40,6 +40,7 @@ else gnupgHome = cfg.gnupg.home; sshKeyPaths = cfg.gnupg.sshKeyPaths; ageKeyFile = cfg.age.keyFile; + ageSshKeyFile = cfg.age.sshKeyFile; ageSshKeyPaths = cfg.age.sshKeyPaths; useTmpfs = cfg.useTmpfs; placeholderBySecretName = cfg.placeholder; diff --git a/pkgs/sops-install-secrets/main.go b/pkgs/sops-install-secrets/main.go index 3934039..6e3ea93 100644 --- a/pkgs/sops-install-secrets/main.go +++ b/pkgs/sops-install-secrets/main.go @@ -79,6 +79,7 @@ type manifest struct { SSHKeyPaths []string `json:"sshKeyPaths"` GnupgHome string `json:"gnupgHome"` AgeKeyFile string `json:"ageKeyFile"` + AgeSSHKeyFile string `json:"ageSshKeyFile"` AgeSSHKeyPaths []string `json:"ageSshKeyPaths"` UseTmpfs bool `json:"useTmpfs"` UserMode bool `json:"userMode"` @@ -1325,7 +1326,7 @@ func installSecrets(args []string) error { } // Import age keys - if len(manifest.AgeSSHKeyPaths) != 0 || manifest.AgeKeyFile != "" { + if (len(manifest.AgeSSHKeyPaths) != 0 || manifest.AgeKeyFile != "") && manifest.AgeSSHKeyFile == "" { keyfile := filepath.Join(manifest.SecretsMountPoint, "age-keys.txt") os.Setenv("SOPS_AGE_KEY_FILE", keyfile) // Create the keyfile @@ -1360,6 +1361,10 @@ func installSecrets(args []string) error { } } + if manifest.AgeSSHKeyFile != "" { + os.Setenv("SOPS_AGE_SSH_PRIVATE_KEY_FILE", manifest.AgeSSHKeyFile) + } + if err := decryptSecrets(manifest.Secrets); err != nil { return err } From e148dc2c6871ee1b0bd248d14e48bd78a91d4772 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:36:08 +0700 Subject: [PATCH 2/4] remove sops.age.sshKeyPaths deprecation --- modules/home-manager/sops.nix | 13 ------------- modules/nix-darwin/default.nix | 13 ------------- modules/sops/default.nix | 13 ------------- 3 files changed, 39 deletions(-) diff --git a/modules/home-manager/sops.nix b/modules/home-manager/sops.nix index 892eeb6..d792c4c 100644 --- a/modules/home-manager/sops.nix +++ b/modules/home-manager/sops.nix @@ -337,19 +337,6 @@ in } ]; - warnings = [ - (lib.mkIf - ( - cfg.age.sshKeyPaths != [ ] - && cfg.gnupg.sshKeyPaths == [ ] - && cfg.gnupg.home == null - && cfg.age.keyFile == null - && cfg.age.sshKeyFile == null - ) - "The option sops.age.sshKeyPaths has been deprecated, since age now has native SSH support. Use option sops.age.sshKeyFile instead." - ) - ]; - home.sessionVariables = lib.mkIf cfg.gnupg.qubes-split-gpg.enable { # TODO: Add this package to nixpkgs and use it from the store # https://github.com/QubesOS/qubes-app-linux-split-gpg diff --git a/modules/nix-darwin/default.nix b/modules/nix-darwin/default.nix index 15fc918..3c975b2 100644 --- a/modules/nix-darwin/default.nix +++ b/modules/nix-darwin/default.nix @@ -396,19 +396,6 @@ in }) { - warnings = [ - (lib.mkIf - ( - cfg.age.sshKeyPaths != [ ] - && cfg.gnupg.sshKeyPaths == [ ] - && cfg.gnupg.home == null - && cfg.age.keyFile == null - && cfg.age.sshKeyFile == null - ) - "The option sops.age.sshKeyPaths has been deprecated, since age now has native SSH support. Use option sops.age.sshKeyFile instead." - ) - ]; - sops.environment.SOPS_GPG_EXEC = lib.mkIf (cfg.gnupg.home != null || cfg.gnupg.sshKeyPaths != [ ]) ( lib.mkDefault "${pkgs.gnupg}/bin/gpg" ); diff --git a/modules/sops/default.nix b/modules/sops/default.nix index 6f38838..67c166f 100644 --- a/modules/sops/default.nix +++ b/modules/sops/default.nix @@ -496,19 +496,6 @@ in }; }) { - warnings = [ - (lib.mkIf - ( - cfg.age.sshKeyPaths != [ ] - && cfg.gnupg.sshKeyPaths == [ ] - && cfg.gnupg.home == null - && cfg.age.keyFile == null - && cfg.age.sshKeyFile == null - ) - "The option sops.age.sshKeyPaths has been deprecated, since age now has native SSH support. Use option sops.age.sshKeyFile instead." - ) - ]; - system.build.sops-nix-manifest = manifest; } ]; From 7369f32be424e8462f35aecdd1d139f368e52679 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:37:17 +0700 Subject: [PATCH 3/4] clarify the differences between sshKeyPaths and sshKeyFile --- modules/home-manager/sops.nix | 10 ++++++---- modules/nix-darwin/default.nix | 11 +++++++---- modules/sops/default.nix | 10 ++++++---- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/modules/home-manager/sops.nix b/modules/home-manager/sops.nix index d792c4c..17a6cd6 100644 --- a/modules/home-manager/sops.nix +++ b/modules/home-manager/sops.nix @@ -257,6 +257,8 @@ in example = "/home/someuser/.ssh/id_ed25519"; description = '' Path to ssh key file that will be used by age for sops decryption. + + Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of the native ssh key support in age and requires no conversion. ''; }; @@ -264,10 +266,10 @@ in type = lib.types.listOf lib.types.path; default = [ ]; description = '' - Paths to ssh keys added as age keys during sops description. The ssh - keys will be converted into age keys manually using ssh-to-age. - - This option is deprecated and will be removed in the future. Use sops.age.sshKeyFile instead. + Paths to ssh keys added as age keys during sops description. + + These ssh keys will be converted into age keys automatically using + ssh-to-age before they are fed to age. ''; }; }; diff --git a/modules/nix-darwin/default.nix b/modules/nix-darwin/default.nix index 3c975b2..88e1ffe 100644 --- a/modules/nix-darwin/default.nix +++ b/modules/nix-darwin/default.nix @@ -306,6 +306,9 @@ in example = "/etc/ssh/ssh_host_ed25519_key"; description = '' Path to ssh key file that will be used by age for sops decryption. + + Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of + the native ssh key support in age and requires no conversion. ''; }; @@ -314,10 +317,10 @@ in default = defaultImportKeys "ed25519"; defaultText = lib.literalMD "The ed25519 keys from {option}`config.services.openssh.hostKeys`"; description = '' - Paths to ssh keys added as age keys during sops description. The ssh - keys will be converted into age keys manually using ssh-to-age. - - This option is deprecated and will be removed in the future. Use sops.age.sshKeyFile instead. + Paths to ssh keys added as age keys during sops description. + + These ssh keys will be converted into age keys automatically using + ssh-to-age before they are fed to age. ''; }; }; diff --git a/modules/sops/default.nix b/modules/sops/default.nix index 67c166f..6dde8f3 100644 --- a/modules/sops/default.nix +++ b/modules/sops/default.nix @@ -345,6 +345,8 @@ in example = "/etc/ssh/ssh_host_ed25519_key"; description = '' Path to ssh key file that will be used by age for sops decryption. + + Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of the native ssh key support in age and requires no conversion. ''; }; @@ -353,10 +355,10 @@ in default = defaultImportKeys "ed25519"; defaultText = lib.literalMD "The ed25519 keys from {option}`config.services.openssh.hostKeys`"; description = '' - Paths to ssh keys added as age keys during sops description. The ssh - keys will be converted into age keys manually using ssh-to-age. - - This option is deprecated and will be removed in the future. Use sops.age.sshKeyFile instead. + Paths to ssh keys added as age keys during sops description. + + These ssh keys will be converted into age keys automatically using + ssh-to-age before they are fed to age. ''; }; }; From df977b7f76e2858bf14b7190392c78b705013fd4 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:41:45 +0700 Subject: [PATCH 4/4] fix line length in documentation --- modules/home-manager/sops.nix | 3 ++- modules/sops/default.nix | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/home-manager/sops.nix b/modules/home-manager/sops.nix index 17a6cd6..844d014 100644 --- a/modules/home-manager/sops.nix +++ b/modules/home-manager/sops.nix @@ -258,7 +258,8 @@ in description = '' Path to ssh key file that will be used by age for sops decryption. - Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of the native ssh key support in age and requires no conversion. + Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of + the native ssh key support in age and requires no conversion. ''; }; diff --git a/modules/sops/default.nix b/modules/sops/default.nix index 6dde8f3..218e9b8 100644 --- a/modules/sops/default.nix +++ b/modules/sops/default.nix @@ -346,7 +346,8 @@ in description = '' Path to ssh key file that will be used by age for sops decryption. - Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of the native ssh key support in age and requires no conversion. + Unlike {option}`config.sops.age.sshKeyPaths`, this option makes use of + the native ssh key support in age and requires no conversion. ''; };