kore / nixos / features / kore.nix119 lines · 3495B · raw
1{ config, lib, kore, ... }:
2
3let
4 cfg = config.kore;
5 hostname = config.networking.hostName;
6 hasHome = lib.elem "home" cfg.currentHost.roles;
7 hostKey = name: ../../keys/hosts + "/${name}.pub";
8
9 identityOptions = { name, ... }:
10 let
11 dir = ../../keys/identities + "/${name}";
12 sshDir = dir + "/ssh";
13 gpgFile = dir + "/gpg.asc";
14
15 sshFiles = lib.optionals (builtins.pathExists sshDir)
16 (lib.filter (lib.hasSuffix ".pub") (lib.attrNames (builtins.readDir sshDir)));
17 in {
18 options = with lib; {
19 email = mkOption {
20 type = types.nullOr types.str;
21 default = null;
22 };
23 gpg = mkOption {
24 type = types.nullOr types.str;
25 default = null;
26 };
27 sshKeys = mkOption {
28 type = types.listOf types.str;
29 default = map (file: lib.fileContents (sshDir + "/${file}")) sshFiles;
30 };
31 gpgKey = mkOption {
32 type = types.nullOr types.path;
33 default = if builtins.pathExists gpgFile then gpgFile else null;
34 };
35 };
36 };
37
38 hostOptions = with lib; {
39 system = mkOption {
40 type = types.str;
41 };
42 stateVersion = mkOption {
43 type = types.str;
44 };
45 roles = mkOption {
46 type = types.listOf types.str;
47 default = [];
48 };
49 users = mkOption {
50 type = types.listOf types.str;
51 default = [];
52 };
53 ipv4 = mkOption {
54 type = types.nullOr types.str;
55 default = null;
56 };
57 };
58in {
59 options.kore = with lib; {
60 identities = mkOption {
61 type = types.attrsOf (types.submodule identityOptions);
62 default = {};
63 };
64 hosts = mkOption {
65 type = types.attrsOf (types.submodule [{ options = hostOptions; }]);
66 default = {};
67 };
68 currentHost = mkOption {
69 type = types.submodule [{ options = hostOptions; }];
70 };
71
72 impermanence.enable = mkOption {
73 type = types.bool;
74 default = true;
75 };
76 };
77
78 config = {
79 kore = {
80 identities = kore.identities or {};
81 hosts = kore.hosts;
82 currentHost = kore.hosts.${hostname};
83 };
84
85 assertions = map (user: {
86 assertion = cfg.identities ? ${user};
87 message = "kore: ${hostname} has user ${user}, but kore.json has no identity for it";
88 }) cfg.currentHost.users;
89
90 nixpkgs.hostPlatform = cfg.currentHost.system;
91 system.stateVersion = cfg.currentHost.stateVersion;
92
93 users.users = lib.listToAttrs (lib.imap0 (i: user: lib.nameValuePair user {
94 isNormalUser = true;
95 password = "tmp";
96 extraGroups = [ "wheel" ];
97
98 createHome = hasHome;
99 home = if hasHome then "/home/${user}" else "/var/empty";
100
101 subUidRanges = [{ startUid = 100000 + i * 65536; count = 65536; }];
102 subGidRanges = [{ startGid = 100000 + i * 65536; count = 65536; }];
103
104 openssh.authorizedKeys.keys = cfg.identities.${user}.sshKeys or [];
105 }) cfg.currentHost.users);
106
107 # /etc/hosts so machines resolve each other by name.
108 networking.hosts = lib.mkMerge (lib.mapAttrsToList (name: host:
109 lib.optionalAttrs (host.ipv4 != null) {
110 "${host.ipv4}" = [ "${name}.${config.networking.domain}" name ];
111 }) cfg.hosts);
112
113 programs.ssh.knownHosts = lib.mapAttrs (name: host: {
114 hostNames = [ name "${name}.${config.networking.domain}" ]
115 ++ lib.optional (host.ipv4 != null) host.ipv4;
116 publicKeyFile = hostKey name;
117 }) (lib.filterAttrs (name: _: builtins.pathExists (hostKey name)) cfg.hosts);
118 };
119}