Commit f6cefc0
Changed files (2)
modules
core
hosts
modules/core/openssh.nix
@@ -26,9 +26,18 @@
})
];
+ settings = {
+ port = lib.mkOption {
+ description = "Port to open for SSH access.";
+ type = lib.types.port;
+ default = 22;
+ };
+ };
+
nixos = {host, ...}: {
services.openssh = {
enable = true;
+ ports = [host.settings.core.openssh.access.port];
settings = {
PermitRootLogin = "prohibit-password";
PasswordAuthentication = false;
modules/hosts/schema.nix
@@ -5,6 +5,85 @@
}: {
den.schema.host = {
options = let
+ # NOTE: Copied from https://github.com/sini/nix-config/blob/1a9f6a8f6ec5f6c324f9f4ce01630703dd6c1eaa/modules/den/schema/host.nix#L121
+ # TODO: This feature may be merge into den in furture. Check https://github.com/denful/den/pull/563
+ # Dynamic settings type — recursively discovers aspects that declare .settings.
+ # Mirrors the aspect tree: den.aspects.disk.zfs-disk-single.settings →
+ # host.settings.disk.zfs-disk-single.*
+ settingsType = let
+ inherit (den.lib.aspects.fx.keyClassification) structuralKeysSet;
+ classKeys = den.classes or {};
+ quirkKeys = den.quirks or {};
+ skipKey = k: k == "settings" || structuralKeysSet ? ${k} || classKeys ? ${k} || quirkKeys ? ${k};
+
+ # Settings declarations may be plain option attrsets
+ # (`{ foo = mkOption {...}; }`) or module-shaped with explicit
+ # imports/config. Default the module keys so plain attrsets work.
+ #
+ # imports'/config' are bound under distinct names on purpose: writing
+ # `imports = raw.imports or [ ]` here gets rewritten by statix (W04) to
+ # `inherit (raw) imports`, which DROPS the `or` default and throws when
+ # raw is a plain options attrset with no imports/config key.
+ reshapeSettings = raw: let
+ imports' = raw.imports or [];
+ config' = raw.config or {};
+ in {
+ imports = imports';
+ config = config';
+ options = removeAttrs raw [
+ "imports"
+ "config"
+ ];
+ };
+
+ # True if a node has .settings anywhere in its aspect subtree.
+ hasSettingsDeep = node:
+ builtins.isAttrs node
+ && (
+ (node ? settings)
+ || lib.any (k: !(skipKey k) && hasSettingsDeep (node.${k} or null)) (builtins.attrNames node)
+ );
+
+ # Build the submodule for one aspect-tree node, mirroring the tree.
+ # A node may be BOTH an aspect with .settings AND a parent of child
+ # aspects that have settings (e.g. services.bgp has localAsn settings and
+ # also parents services.bgp.cilium-bgp). Merge the node's own settings
+ # options with recursion into its settings-bearing children.
+ nodeModule = node: let
+ ownSettings =
+ if node ? settings
+ then reshapeSettings node.settings
+ else {
+ imports = [];
+ config = {};
+ options = {};
+ };
+ settingChildren =
+ lib.filterAttrs (
+ k: v: !(skipKey k) && builtins.isAttrs v && hasSettingsDeep v
+ )
+ node;
+ childOptions =
+ lib.mapAttrs (
+ name: child:
+ lib.mkOption {
+ type = lib.types.submodule (nodeModule child);
+ default = {};
+ description = "Settings under ${name}";
+ }
+ )
+ settingChildren;
+ # Distinct names so statix (W04) can't rewrite to
+ # `inherit (ownSettings) imports`, which would drop the `or` default.
+ ownImports = ownSettings.imports or [];
+ ownConfig = ownSettings.config or {};
+ in {
+ imports = ownImports;
+ config = ownConfig;
+ options = (ownSettings.options or {}) // childOptions;
+ };
+ in
+ lib.types.submodule (nodeModule (den.aspects or {}));
in {
description = lib.mkOption {
description = "Description of the host.";
@@ -12,6 +91,12 @@
default = "";
};
+ settings = lib.mkOption {
+ description = "Per-aspect typed settings";
+ type = settingsType;
+ default = {};
+ };
+
pubKey = lib.mkOption {
description = "Public SSH key for the host.";
type = lib.types.nullOr lib.types.str;