numtide / system-manager

Manage system config using nix on any distro
MIT License
659 stars 16 forks source link

Unable to use nix-sops #81

Open snylonue opened 5 months ago

snylonue commented 5 months ago

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
    };
    }
  2. 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.

r-vdp commented 5 months ago

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.

snylonue commented 5 months ago

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 commented 5 months ago
{
  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? ```
r-vdp commented 5 months ago

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

snylonue commented 5 months ago
  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 commented 4 months ago

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

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

r-vdp commented 4 months ago

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.

aanderse commented 4 months ago

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

snylonue commented 4 months ago

relevant: Mic92/sops-nix#39

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