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