system-manager icon indicating copy to clipboard operation
system-manager copied to clipboard

Unable to use nix-sops

Open snylonue opened this issue 2 years ago • 10 comments

Describe the bug

I'm trying to use nix-sops to handle some secrets, but I can't make it.

To Reproduce

  1. In flake.nix
systemConfigs =
      let makeSystemConfig = inputs.system-manager.lib.makeSystemConfig;
      in {
        "minami" = makeSystemConfig {
          modules = [ ./system/minami inputs.sops-nix.nixosModules.sops ];
          extraSpecialArgs = { inherit inputs; };
        };
      };

In system/minami/default.nix

{
  imports = [ ./other-modules ];

  sops = {
    age.sshKeyPaths = [ "/etc/ssh/nix-sops" ];
    secrets = {
      secret = {
        sopsFile = ./secrets/secret.json;
        format = "json";
      };
    };
  };
  config = {
    nixpkgs.hostPlatform = "x86_64-linux";
    # other config
  };
}

nix repl --extra-experimental-features 'flakes repl-flake' flake.nix --show-trace

nix-repl> systemConfigs.minami
error:
       … while evaluating the attribute 'minami'

         at /nix/store/n2xvix786b3v8sni332pikjq3j8r3ygw-source/flake.nix:89:9:

           88|           makeSystemConfig { modules = [ ./system/marushiru.nix ]; };
           89|         "minami" = makeSystemConfig {
             |         ^
           90|           modules = [ ./system/minami inputs.sops-nix.nixosModules.sops ];

       … from call site

         at /nix/store/n2xvix786b3v8sni332pikjq3j8r3ygw-source/flake.nix:89:20:

           88|           makeSystemConfig { modules = [ ./system/marushiru.nix ]; };
           89|         "minami" = makeSystemConfig {
             |                    ^
           90|           modules = [ ./system/minami inputs.sops-nix.nixosModules.sops ];

       … while calling 'makeSystemConfig'

         at /nix/store/ncibdc9d87f9ysfvjkaiqpmwxgiayl05-source/nix/lib.nix:17:5:

           16|   makeSystemConfig =
           17|     { modules
             |     ^
           18|     , extraSpecialArgs ? { }

       … from call site

         at /nix/store/ncibdc9d87f9ysfvjkaiqpmwxgiayl05-source/nix/lib.nix:92:5:

           91|     in
           92|     returnIfNoAssertions toplevel;
             |     ^
           93|

       … while calling 'returnIfNoAssertions'

         at /nix/store/ncibdc9d87f9ysfvjkaiqpmwxgiayl05-source/nix/lib.nix:46:30:

           45|
           46|       returnIfNoAssertions = drv:
             |                              ^
           47|         let

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:242:28:

          241|           # For definitions that have an associated option
          242|           declaredConfig = mapAttrsRecursiveCond (v: ! isOption v) (_: v: v.value) options;
             |                            ^
          243|

       … while calling 'mapAttrsRecursiveCond'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1167:5:

         1166|     f:
         1167|     set:
             |     ^
         1168|     let

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:234:33:

          233|           ({ inherit lib options config specialArgs; } // specialArgs);
          234|         in mergeModules prefix (reverseList collected);
             |                                 ^
          235|

       … while calling 'reverseList'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/lists.nix:1068:17:

         1067|   */
         1068|   reverseList = xs:
             |                 ^
         1069|     let l = length xs; in genList (n: elemAt xs (l - n - 1)) l;

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:229:25:

          228|       merged =
          229|         let collected = collectModules
             |                         ^
          230|           class

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:445:37:

          444|
          445|     in modulesPath: initialModules: args:
             |                                     ^
          446|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:446:7:

          445|     in modulesPath: initialModules: args:
          446|       filterModules modulesPath (collectStructuredModules unknownModule "" initialModules args);
             |       ^
          447|

       … while calling 'filterModules'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:413:36:

          412|       # modules recursively. It returns the final list of unique-by-key modules
          413|       filterModules = modulesPath: { disabled, modules }:
             |                                    ^
          414|         let

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:439:31:

          438|           disabledKeys = concatMap ({ file, disabled }: map (moduleKey file) disabled) disabled;
          439|           keyFilter = filter (attrs: ! elem attrs.key disabledKeys);
             |                               ^
          440|         in map (attrs: attrs.module) (builtins.genericClosure {

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:400:22:

          399|           let
          400|             module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x);
             |                      ^
          401|             collectedImports = collectStructuredModules module._file module.key module.imports args;

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:365:11:

          364|         else
          365|           m: m;
             |           ^
          366|

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:400:35:

          399|           let
          400|             module = checkModule (loadModule args parentFile "${parentKey}:anon-${toString n}" x);
             |                                   ^
          401|             collectedImports = collectStructuredModules module._file module.key module.imports args;

       … while calling 'loadModule'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:336:53:

          335|       # Like unifyModuleSyntax, but also imports paths and calls functions if necessary
          336|       loadModule = args: fallbackFile: fallbackKey: m:
             |                                                     ^
          337|         if isFunction m then

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:354:14:

          353|           throw "Module imports can't be nested lists. Perhaps you meant to remove one level of lists? Definitions: ${showDefs defs}"
          354|         else unifyModuleSyntax (toString m) (toString m) (applyModuleArgsIfFunction (toString m) (import m) args);
             |              ^
          355|

       … while calling 'unifyModuleSyntax'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:454:34:

          453|      of ‘options’, ‘config’ and ‘imports’ attributes. */
          454|   unifyModuleSyntax = file: key: m:
             |                                  ^
          455|     let

       error: Module `/nix/store/n2xvix786b3v8sni332pikjq3j8r3ygw-source/system/minami' has an unsupported attribute `sops'. This is caused by introducing a top-level `config' or `options' attribute. Add configuration attributes immediately on the top level instead, or move all of them (namely: sops) into the explicit `config' attribute.

Expected behavior

The configure can be built normally.

System information

latest system-manager and nix-sops

Ubuntu 22.04

Additional context

It seems that system-manager cannot use nixos modules. I'm wondering if the home-manager module can be used.

snylonue avatar Apr 10 '24 07:04 snylonue

No you should use the NixOS module, not the HM module.

From the error, I have a suspicion that you didn't share your full config, because that error is a generic one from the module system that you get when you put attributes in the top-level module scope together with an explicit options or config attribute.

Do you by any chance have something like this?

{
  sops = { ... };

  config = { ... };
}

In that case you'd need to move the sops part inside config. If you introduce an options attribute, you also need to put the actual config inside config.

That being said, I didn't test sops-nix with system-manager, so I'm not sure if it works.

r-vdp avatar Apr 10 '24 10:04 r-vdp

No you should use the NixOS module, not the HM module.

From the error, I have a suspicion that you didn't share your full config, because that error is a generic one from the module system that you get when you put attributes in the top-level module scope together with an explicit options or config attribute.

Do you by any chance have something like this?

{
  sops = { ... };

  config = { ... };
}

In that case you'd need to move the sops part inside config. If you introduce an options attribute, you also need to put the actual config inside config.

That being said, I didn't test sops-nix with system-manager, so I'm not sure if it works.

Yes, I didn't upload the full config. I would upload a minimum reproduction if needed.

However, there are still errors if I move sops options into config, or even I import sops nixos module only. I will upload such error messages later since they may contain some more helpful information.

snylonue avatar Apr 10 '24 11:04 snylonue

{
  imports = [ ./../../modules/system/xray.nix ];

  config = {
    nixpkgs.hostPlatform = "x86_64-linux";

    sops = {
      age.sshKeyPaths = [ "/etc/ssh/nix-sops" ];
      secrets = {
        secret = {
          sopsFile = ./secrets/secret.json;
          format = "json";
        };
      };
    };
  # other config

  };
}

After moving sops into config, I got:

error:
       … while evaluating the attribute 'minami'

         at /nix/store/jc7jknyri77nx3giz3rzzbxdflsrl1mj-source/flake.nix:89:9:

           88|           makeSystemConfig { modules = [ ./system/marushiru.nix ]; };
           89|         "minami" = makeSystemConfig {
             |         ^
           90|           modules = [ ./system/minami inputs.sops-nix.nixosModules.sops ];

       … from call site

         at /nix/store/jc7jknyri77nx3giz3rzzbxdflsrl1mj-source/flake.nix:89:20:

           88|           makeSystemConfig { modules = [ ./system/marushiru.nix ]; };
           89|         "minami" = makeSystemConfig {
             |                    ^
           90|           modules = [ ./system/minami inputs.sops-nix.nixosModules.sops ];

       … while calling 'makeSystemConfig'

         at /nix/store/ncibdc9d87f9ysfvjkaiqpmwxgiayl05-source/nix/lib.nix:17:5:

           16|   makeSystemConfig =
           17|     { modules
             |     ^
           18|     , extraSpecialArgs ? { }

       … from call site

         at /nix/store/ncibdc9d87f9ysfvjkaiqpmwxgiayl05-source/nix/lib.nix:92:5:

           91|     in
           92|     returnIfNoAssertions toplevel;
             |     ^
           93|

       … while calling 'returnIfNoAssertions'

         at /nix/store/ncibdc9d87f9ysfvjkaiqpmwxgiayl05-source/nix/lib.nix:46:30:

           45|
           46|       returnIfNoAssertions = drv:
             |                              ^
           47|         let

       … while evaluating the error message for definitions for `system', which is an option that does not exist

       … while evaluating a definition from `/nix/store/jslmnzssy01r4pighp0mnhp9dd5yk2w9-source/modules/sops/secrets-for-users'

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/modules.nix:272:25:

          271|                       "while evaluating a definition from `${firstDef.file}'"
          272|                       ( showDefs [ firstDef ])
             |                         ^
          273|                     );

       … while calling 'showDefs'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/options.nix:445:14:

          444|
          445|   showDefs = defs: concatMapStrings (def:
             |              ^
          446|     let

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/options.nix:445:20:

          444|
          445|   showDefs = defs: concatMapStrings (def:
             |                    ^
          446|     let

       … while calling 'concatMapStrings'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/strings.nix:60:25:

           59|   */
           60|   concatMapStrings = f: list: concatStrings (map f list);
             |                         ^
           61|

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/options.nix:445:38:

          444|
          445|   showDefs = defs: concatMapStrings (def:
             |                                      ^
          446|     let

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/options.nix:449:10:

          448|       prettyEval = builtins.tryEval
          449|         (lib.generators.toPretty { }
             |          ^
          450|           (lib.generators.withRecursion { depthLimit = 10; throwOnDepthLimit = false; } def.value));

       … while calling 'go'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:343:18:

          342|     let
          343|     go = indent: v:
             |                  ^
          344|     let     introSpace = if multiline then "\n${indent}  " else " ";

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1062:10:

         1061|     attrs:
         1062|     map (name: f name attrs.${name}) (attrNames attrs);
             |          ^
         1063|

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1062:16:

         1061|     attrs:
         1062|     map (name: f name attrs.${name}) (attrNames attrs);
             |                ^
         1063|

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:391:22:

          390|           + concatStringsSep introSpace (mapAttrsToList
          391|               (name: value:
             |                      ^
          392|                 "${escapeNixIdentifier name} = ${

       … while evaluating an attribute `activationScripts`

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:394:22:

          393|                   addErrorContext "while evaluating an attribute `${name}`"
          394|                     (go (indent + "  ") value)
             |                      ^
          395|                 };") v)

       … while calling 'go'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:343:18:

          342|     let
          343|     go = indent: v:
             |                  ^
          344|     let     introSpace = if multiline then "\n${indent}  " else " ";

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1062:10:

         1061|     attrs:
         1062|     map (name: f name attrs.${name}) (attrNames attrs);
             |          ^
         1063|

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1062:16:

         1061|     attrs:
         1062|     map (name: f name attrs.${name}) (attrNames attrs);
             |                ^
         1063|

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:391:22:

          390|           + concatStringsSep introSpace (mapAttrsToList
          391|               (name: value:
             |                      ^
          392|                 "${escapeNixIdentifier name} = ${

       … while evaluating an attribute `content`

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:394:22:

          393|                   addErrorContext "while evaluating an attribute `${name}`"
          394|                     (go (indent + "  ") value)
             |                      ^
          395|                 };") v)

       … while calling 'go'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:343:18:

          342|     let
          343|     go = indent: v:
             |                  ^
          344|     let     introSpace = if multiline then "\n${indent}  " else " ";

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1062:10:

         1061|     attrs:
         1062|     map (name: f name attrs.${name}) (attrNames attrs);
             |          ^
         1063|

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1062:16:

         1061|     attrs:
         1062|     map (name: f name attrs.${name}) (attrNames attrs);
             |                ^
         1063|

       … while calling anonymous lambda

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:391:22:

          390|           + concatStringsSep introSpace (mapAttrsToList
          391|               (name: value:
             |                      ^
          392|                 "${escapeNixIdentifier name} = ${

       … while evaluating an attribute `setupSecretsForUsers`

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:394:22:

          393|                   addErrorContext "while evaluating an attribute `${name}`"
          394|                     (go (indent + "  ") value)
             |                      ^
          395|                 };") v)

       … while calling 'go'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:343:18:

          342|     let
          343|     go = indent: v:
             |                  ^
          344|     let     introSpace = if multiline then "\n${indent}  " else " ";

       … while calling 'evalNext'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:318:24:

          317|           let
          318|             evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
             |                        ^
          319|           in

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:318:27:

          317|           let
          318|             evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
             |                           ^
          319|           in

       … while calling 'mapAny'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:316:25:

          315|           else id;
          316|         mapAny = depth: v:
             |                         ^
          317|           let

       … from call site

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/generators.nix:318:47:

          317|           let
          318|             evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
             |                                               ^
          319|           in

       … while calling 'id'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/trivial.nix:36:8:

           35|   */
           36|   id = x: x;
             |        ^
           37|

       … from call site

         at /nix/store/jslmnzssy01r4pighp0mnhp9dd5yk2w9-source/modules/sops/secrets-for-users/default.nix:36:11:

           35|       ${withEnvironment "${cfg.package}/bin/sops-install-secrets -ignore-passwd ${manifestForUsers}"}
           36|     '' // lib.optionalAttrs (config.system ? dryActivationScript) {
             |           ^
           37|       supportsDryActivation = true;

       … while calling 'optionalAttrs'

         at /nix/store/j10523yhkcc34478azkgcl70yzcx6j2j-source/lib/attrsets.nix:1318:5:

         1317|     cond:
         1318|     as:
             |     ^
         1319|     if cond then as else {};

       error: attribute 'system' missing

       at /nix/store/jslmnzssy01r4pighp0mnhp9dd5yk2w9-source/modules/sops/secrets-for-users/default.nix:36:30:

           35|       ${withEnvironment "${cfg.package}/bin/sops-install-secrets -ignore-passwd ${manifestForUsers}"}
           36|     '' // lib.optionalAttrs (config.system ? dryActivationScript) {
             |                              ^
           37|       supportsDryActivation = true;
       Did you mean systemd?

snylonue avatar Apr 10 '24 13:04 snylonue

Right, you'd need to add system as an option in https://github.com/numtide/system-manager/blob/55b5f097e79b97223a60785cf76ecc48caef4885/nix/modules/upstream/nixpkgs/default.nix#L18-L27

r-vdp avatar Apr 10 '24 14:04 r-vdp

  options = {
    system = lib.mkOption { type = lib.types.raw; };

    services.openssh = {
      enable = lib.mkOption {
        type = lib.types.bool;
        default = false;
      };
    };
  };

I managed to make it work after adding such options. However, the secret file couldn't be read at runtime (and there is no /run/secrets).

snylonue avatar Apr 10 '24 15:04 snylonue

It seems that system-manager needs to handle system.activationScripts

https://github.com/Mic92/sops-nix/blob/538c114cfdf1f0458f507087b1dcf018ce1c0c4c/modules/sops/default.nix#L331-L347

snylonue avatar Apr 13 '24 02:04 snylonue

It seems that system-manager needs to handle system.activationScripts

https://github.com/Mic92/sops-nix/blob/538c114cfdf1f0458f507087b1dcf018ce1c0c4c/modules/sops/default.nix#L331-L347

A better solution would be for sops to decrypt secrets through a systemd service instead of an activation script, which is the general direction in which things are going anyway, with systemd initrd and such.

r-vdp avatar Apr 13 '24 12:04 r-vdp

relevant: https://github.com/Mic92/sops-nix/pull/39

aanderse avatar Apr 13 '24 12:04 aanderse

relevant: Mic92/sops-nix#39

But the pr has been opened for 3 years. Could we do something now?

snylonue avatar Apr 14 '24 03:04 snylonue