diff --git a/modules/programs/ssh.nix b/modules/programs/ssh.nix index 6b0747dd9..95d4edc4b 100644 --- a/modules/programs/ssh.nix +++ b/modules/programs/ssh.nix @@ -56,7 +56,7 @@ let }; }; - matchBlockModule = types.submodule ({ name, ... }: { + matchBlockModule = types.submodule ({ dagName, ... }: { options = { host = mkOption { type = types.str; @@ -266,7 +266,7 @@ let }; }; - config.host = mkDefault name; + config.host = mkDefault dagName; }); matchBlockStr = cf: concatStringsSep "\n" ( @@ -392,7 +392,7 @@ in }; matchBlocks = mkOption { - type = types.loaOf matchBlockModule; + type = hm.types.listOrDagOf matchBlockModule; default = {}; example = literalExample '' { @@ -400,7 +400,7 @@ in hostname = "example.com"; user = "john"; }; - foo = { + foo = lib.hm.dag.entryBefore ["john.example.com"] { hostname = "example.com"; identityFile = "/home/john/.ssh/foo_rsa"; }; @@ -408,11 +408,15 @@ in ''; description = '' Specify per-host settings. Note, if the order of rules matter - then this must be a list. See + then use the DAG functions to express the dependencies as + shown in the example. + + See ssh_config 5 - . + + for more information. ''; }; }; @@ -432,18 +436,24 @@ in checkLocal = block: any' checkBindAndHost block.localForwards; checkRemote = block: any' checkBindAndHost block.remoteForwards; checkMatchBlock = block: all (fn: fn block) [ checkLocal checkRemote checkDynamic ]; - in any' checkMatchBlock (builtins.attrValues cfg.matchBlocks); + in any' checkMatchBlock (map (block: block.data) (builtins.attrValues cfg.matchBlocks)); message = "Forwarded paths cannot have ports."; } ]; - home.file.".ssh/config".text = '' + home.file.".ssh/config".text = + let + sortedMatchBlocks = hm.dag.topoSort cfg.matchBlocks; + sortedMatchBlocksStr = builtins.toJSON sortedMatchBlocks; + matchBlocks = + if sortedMatchBlocks ? result + then sortedMatchBlocks.result + else abort "Dependency cycle in SSH match blocks: ${sortedMatchBlocksStr}"; + in '' ${concatStringsSep "\n" ( mapAttrsToList (n: v: "${n} ${v}") cfg.extraOptionOverrides)} - ${concatStringsSep "\n\n" ( - map matchBlockStr ( - builtins.attrValues cfg.matchBlocks))} + ${concatStringsSep "\n\n" (map (block: matchBlockStr block.data) matchBlocks)} Host * ForwardAgent ${yn cfg.forwardAgent} diff --git a/tests/modules/programs/ssh/match-blocks-attrs-expected.conf b/tests/modules/programs/ssh/match-blocks-attrs-expected.conf index f0d768375..131918161 100644 --- a/tests/modules/programs/ssh/match-blocks-attrs-expected.conf +++ b/tests/modules/programs/ssh/match-blocks-attrs-expected.conf @@ -16,6 +16,9 @@ Host xyz RemoteForward /run/user/1000/gnupg/S.gpg-agent.extra /run/user/1000/gnupg/S.gpg-agent DynamicForward [localhost]:2839 +Host ordered + Port 1 + Host * ForwardAgent no Compression no diff --git a/tests/modules/programs/ssh/match-blocks-attrs.nix b/tests/modules/programs/ssh/match-blocks-attrs.nix index a84a703e8..1b6f0ae01 100644 --- a/tests/modules/programs/ssh/match-blocks-attrs.nix +++ b/tests/modules/programs/ssh/match-blocks-attrs.nix @@ -12,6 +12,8 @@ with lib; proxyJump = "jump-host"; }; + ordered = hm.dag.entryAfter [ "xyz" ] { port = 1; }; + xyz = { identityFile = "file"; serverAliveInterval = 60;