at 25.11-pre 6.6 kB view raw
1# NixOS module for iodine, ip over dns daemon 2{ 3 config, 4 lib, 5 pkgs, 6 ... 7}: 8let 9 cfg = config.services.iodine; 10 11 iodinedUser = "iodined"; 12 13 # is this path made unreadable by ProtectHome = true ? 14 isProtected = x: lib.hasPrefix "/root" x || lib.hasPrefix "/home" x; 15in 16{ 17 imports = [ 18 (lib.mkRenamedOptionModule 19 [ "services" "iodined" "enable" ] 20 [ "services" "iodine" "server" "enable" ] 21 ) 22 (lib.mkRenamedOptionModule 23 [ "services" "iodined" "domain" ] 24 [ "services" "iodine" "server" "domain" ] 25 ) 26 (lib.mkRenamedOptionModule [ "services" "iodined" "ip" ] [ "services" "iodine" "server" "ip" ]) 27 (lib.mkRenamedOptionModule 28 [ "services" "iodined" "extraConfig" ] 29 [ "services" "iodine" "server" "extraConfig" ] 30 ) 31 (lib.mkRemovedOptionModule [ "services" "iodined" "client" ] "") 32 ]; 33 34 ### configuration 35 36 options = { 37 38 services.iodine = { 39 clients = lib.mkOption { 40 default = { }; 41 description = '' 42 Each attribute of this option defines a systemd service that 43 runs iodine. Many or none may be defined. 44 The name of each service is 45 `iodine-«name»` 46 where «name» is the name of the 47 corresponding attribute name. 48 ''; 49 example = lib.literalExpression '' 50 { 51 foo = { 52 server = "tunnel.mdomain.com"; 53 relay = "8.8.8.8"; 54 extraConfig = "-v"; 55 } 56 } 57 ''; 58 type = lib.types.attrsOf ( 59 lib.types.submodule ({ 60 options = { 61 server = lib.mkOption { 62 type = lib.types.str; 63 default = ""; 64 description = "Hostname of server running iodined"; 65 example = "tunnel.mydomain.com"; 66 }; 67 68 relay = lib.mkOption { 69 type = lib.types.str; 70 default = ""; 71 description = "DNS server to use as an intermediate relay to the iodined server"; 72 example = "8.8.8.8"; 73 }; 74 75 extraConfig = lib.mkOption { 76 type = lib.types.str; 77 default = ""; 78 description = "Additional command line parameters"; 79 example = "-l 192.168.1.10 -p 23"; 80 }; 81 82 passwordFile = lib.mkOption { 83 type = lib.types.str; 84 default = ""; 85 description = "Path to a file containing the password."; 86 }; 87 }; 88 }) 89 ); 90 }; 91 92 server = { 93 enable = lib.mkOption { 94 type = lib.types.bool; 95 default = false; 96 description = "enable iodined server"; 97 }; 98 99 ip = lib.mkOption { 100 type = lib.types.str; 101 default = ""; 102 description = "The assigned ip address or ip range"; 103 example = "172.16.10.1/24"; 104 }; 105 106 domain = lib.mkOption { 107 type = lib.types.str; 108 default = ""; 109 description = "Domain or subdomain of which nameservers point to us"; 110 example = "tunnel.mydomain.com"; 111 }; 112 113 extraConfig = lib.mkOption { 114 type = lib.types.str; 115 default = ""; 116 description = "Additional command line parameters"; 117 example = "-l 192.168.1.10 -p 23"; 118 }; 119 120 passwordFile = lib.mkOption { 121 type = lib.types.str; 122 default = ""; 123 description = "File that contains password"; 124 }; 125 }; 126 127 }; 128 }; 129 130 ### implementation 131 132 config = lib.mkIf (cfg.server.enable || cfg.clients != { }) { 133 environment.systemPackages = [ pkgs.iodine ]; 134 boot.kernelModules = [ "tun" ]; 135 136 systemd.services = 137 let 138 createIodineClientService = name: cfg: { 139 description = "iodine client - ${name}"; 140 after = [ "network.target" ]; 141 wantedBy = [ "multi-user.target" ]; 142 script = "exec ${pkgs.iodine}/bin/iodine -f -u ${iodinedUser} ${cfg.extraConfig} ${ 143 lib.optionalString (cfg.passwordFile != "") "< \"${builtins.toString cfg.passwordFile}\"" 144 } ${cfg.relay} ${cfg.server}"; 145 serviceConfig = { 146 RestartSec = "30s"; 147 Restart = "always"; 148 149 # hardening : 150 # Filesystem access 151 ProtectSystem = "strict"; 152 ProtectHome = if isProtected cfg.passwordFile then "read-only" else "true"; 153 PrivateTmp = true; 154 ReadWritePaths = "/dev/net/tun"; 155 PrivateDevices = false; 156 ProtectKernelTunables = true; 157 ProtectKernelModules = true; 158 ProtectControlGroups = true; 159 # Caps 160 NoNewPrivileges = true; 161 # Misc. 162 LockPersonality = true; 163 RestrictRealtime = true; 164 PrivateMounts = true; 165 MemoryDenyWriteExecute = true; 166 }; 167 }; 168 in 169 lib.listToAttrs ( 170 lib.mapAttrsToList ( 171 name: value: lib.nameValuePair "iodine-${name}" (createIodineClientService name value) 172 ) cfg.clients 173 ) 174 // { 175 iodined = lib.mkIf (cfg.server.enable) { 176 description = "iodine, ip over dns server daemon"; 177 after = [ "network.target" ]; 178 wantedBy = [ "multi-user.target" ]; 179 script = "exec ${pkgs.iodine}/bin/iodined -f -u ${iodinedUser} ${cfg.server.extraConfig} ${ 180 lib.optionalString ( 181 cfg.server.passwordFile != "" 182 ) "< \"${builtins.toString cfg.server.passwordFile}\"" 183 } ${cfg.server.ip} ${cfg.server.domain}"; 184 serviceConfig = { 185 # Filesystem access 186 ProtectSystem = "strict"; 187 ProtectHome = if isProtected cfg.server.passwordFile then "read-only" else "true"; 188 PrivateTmp = true; 189 ReadWritePaths = "/dev/net/tun"; 190 PrivateDevices = false; 191 ProtectKernelTunables = true; 192 ProtectKernelModules = true; 193 ProtectControlGroups = true; 194 # Caps 195 NoNewPrivileges = true; 196 # Misc. 197 LockPersonality = true; 198 RestrictRealtime = true; 199 PrivateMounts = true; 200 MemoryDenyWriteExecute = true; 201 }; 202 }; 203 }; 204 205 users.users.${iodinedUser} = { 206 uid = config.ids.uids.iodined; 207 group = "iodined"; 208 description = "Iodine daemon user"; 209 }; 210 users.groups.iodined.gid = config.ids.gids.iodined; 211 }; 212}