at 25.11-pre 9.6 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 9 pkg = pkgs.cjdns; 10 11 cfg = config.services.cjdns; 12 13 connectToSubmodule = 14 { ... }: 15 { 16 options = { 17 password = lib.mkOption { 18 type = lib.types.str; 19 description = "Authorized password to the opposite end of the tunnel."; 20 }; 21 login = lib.mkOption { 22 default = ""; 23 type = lib.types.str; 24 description = "(optional) name your peer has for you"; 25 }; 26 peerName = lib.mkOption { 27 default = ""; 28 type = lib.types.str; 29 description = "(optional) human-readable name for peer"; 30 }; 31 publicKey = lib.mkOption { 32 type = lib.types.str; 33 description = "Public key at the opposite end of the tunnel."; 34 }; 35 hostname = lib.mkOption { 36 default = ""; 37 example = "foobar.hype"; 38 type = lib.types.str; 39 description = "Optional hostname to add to /etc/hosts; prevents reverse lookup failures."; 40 }; 41 }; 42 }; 43 44 # Additional /etc/hosts entries for peers with an associated hostname 45 cjdnsExtraHosts = pkgs.runCommand "cjdns-hosts" { } '' 46 exec >$out 47 ${lib.concatStringsSep "\n" ( 48 lib.mapAttrsToList ( 49 k: v: 50 lib.optionalString ( 51 v.hostname != "" 52 ) "echo $(${pkgs.cjdns}/bin/publictoip6 ${v.publicKey}) ${v.hostname}" 53 ) (cfg.ETHInterface.connectTo // cfg.UDPInterface.connectTo) 54 )} 55 ''; 56 57 parseModules = 58 x: 59 x 60 // { 61 connectTo = lib.mapAttrs (name: value: { inherit (value) password publicKey; }) x.connectTo; 62 }; 63 64 cjdrouteConf = builtins.toJSON ( 65 lib.recursiveUpdate { 66 admin = { 67 bind = cfg.admin.bind; 68 password = "@CJDNS_ADMIN_PASSWORD@"; 69 }; 70 authorizedPasswords = map (p: { password = p; }) cfg.authorizedPasswords; 71 interfaces = { 72 ETHInterface = if (cfg.ETHInterface.bind != "") then [ (parseModules cfg.ETHInterface) ] else [ ]; 73 UDPInterface = if (cfg.UDPInterface.bind != "") then [ (parseModules cfg.UDPInterface) ] else [ ]; 74 }; 75 76 privateKey = "@CJDNS_PRIVATE_KEY@"; 77 78 resetAfterInactivitySeconds = 100; 79 80 router = { 81 interface = { 82 type = "TUNInterface"; 83 }; 84 ipTunnel = { 85 allowedConnections = [ ]; 86 outgoingConnections = [ ]; 87 }; 88 }; 89 90 security = [ 91 { 92 exemptAngel = 1; 93 setuser = "nobody"; 94 } 95 ]; 96 97 } cfg.extraConfig 98 ); 99 100in 101 102{ 103 options = { 104 105 services.cjdns = { 106 107 enable = lib.mkOption { 108 type = lib.types.bool; 109 default = false; 110 description = '' 111 Whether to enable the cjdns network encryption 112 and routing engine. A file at /etc/cjdns.keys will 113 be created if it does not exist to contain a random 114 secret key that your IPv6 address will be derived from. 115 ''; 116 }; 117 118 extraConfig = lib.mkOption { 119 type = lib.types.attrs; 120 default = { }; 121 example = { 122 router.interface.tunDevice = "tun10"; 123 }; 124 description = '' 125 Extra configuration, given as attrs, that will be merged recursively 126 with the rest of the JSON generated by this module, at the root node. 127 ''; 128 }; 129 130 confFile = lib.mkOption { 131 type = lib.types.nullOr lib.types.path; 132 default = null; 133 example = "/etc/cjdroute.conf"; 134 description = '' 135 Ignore all other cjdns options and load configuration from this file. 136 ''; 137 }; 138 139 authorizedPasswords = lib.mkOption { 140 type = lib.types.listOf lib.types.str; 141 default = [ ]; 142 example = [ 143 "snyrfgkqsc98qh1y4s5hbu0j57xw5s0" 144 "z9md3t4p45mfrjzdjurxn4wuj0d8swv" 145 "49275fut6tmzu354pq70sr5b95qq0vj" 146 ]; 147 description = '' 148 Any remote cjdns nodes that offer these passwords on 149 connection will be allowed to route through this node. 150 ''; 151 }; 152 153 admin = { 154 bind = lib.mkOption { 155 type = lib.types.str; 156 default = "127.0.0.1:11234"; 157 description = '' 158 Bind the administration port to this address and port. 159 ''; 160 }; 161 }; 162 163 UDPInterface = { 164 bind = lib.mkOption { 165 type = lib.types.str; 166 default = ""; 167 example = "192.168.1.32:43211"; 168 description = '' 169 Address and port to bind UDP tunnels to. 170 ''; 171 }; 172 connectTo = lib.mkOption { 173 type = lib.types.attrsOf (lib.types.submodule (connectToSubmodule)); 174 default = { }; 175 example = lib.literalExpression '' 176 { 177 "192.168.1.1:27313" = { 178 hostname = "homer.hype"; 179 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM"; 180 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k"; 181 }; 182 } 183 ''; 184 description = '' 185 Credentials for making UDP tunnels. 186 ''; 187 }; 188 }; 189 190 ETHInterface = { 191 bind = lib.mkOption { 192 type = lib.types.str; 193 default = ""; 194 example = "eth0"; 195 description = '' 196 Bind to this device for native ethernet operation. 197 `all` is a pseudo-name which will try to connect to all devices. 198 ''; 199 }; 200 201 beacon = lib.mkOption { 202 type = lib.types.int; 203 default = 2; 204 description = '' 205 Auto-connect to other cjdns nodes on the same network. 206 Options: 207 0: Disabled. 208 1: Accept beacons, this will cause cjdns to accept incoming 209 beacon messages and try connecting to the sender. 210 2: Accept and send beacons, this will cause cjdns to broadcast 211 messages on the local network which contain a randomly 212 generated per-session password, other nodes which have this 213 set to 1 or 2 will hear the beacon messages and connect 214 automatically. 215 ''; 216 }; 217 218 connectTo = lib.mkOption { 219 type = lib.types.attrsOf (lib.types.submodule (connectToSubmodule)); 220 default = { }; 221 example = lib.literalExpression '' 222 { 223 "01:02:03:04:05:06" = { 224 hostname = "homer.hype"; 225 password = "5kG15EfpdcKNX3f2GSQ0H1HC7yIfxoCoImnO5FHM"; 226 publicKey = "371zpkgs8ss387tmr81q04mp0hg1skb51hw34vk1cq644mjqhup0.k"; 227 }; 228 } 229 ''; 230 description = '' 231 Credentials for connecting look similar to UDP credientials 232 except they begin with the mac address. 233 ''; 234 }; 235 }; 236 237 addExtraHosts = lib.mkOption { 238 type = lib.types.bool; 239 default = false; 240 description = '' 241 Whether to add cjdns peers with an associated hostname to 242 {file}`/etc/hosts`. Beware that enabling this 243 incurs heavy eval-time costs. 244 ''; 245 }; 246 247 }; 248 249 }; 250 251 config = lib.mkIf cfg.enable { 252 253 boot.kernelModules = [ "tun" ]; 254 255 # networking.firewall.allowedUDPPorts = ... 256 257 systemd.services.cjdns = { 258 description = "cjdns: routing engine designed for security, scalability, speed and ease of use"; 259 wantedBy = [ 260 "multi-user.target" 261 "sleep.target" 262 ]; 263 after = [ "network-online.target" ]; 264 bindsTo = [ "network-online.target" ]; 265 266 preStart = lib.optionalString (cfg.confFile == null) '' 267 [ -e /etc/cjdns.keys ] && source /etc/cjdns.keys 268 269 if [ -z "$CJDNS_PRIVATE_KEY" ]; then 270 shopt -s lastpipe 271 ${pkg}/bin/makekeys | { read private ipv6 public; } 272 273 install -m 600 <(echo "CJDNS_PRIVATE_KEY=$private") /etc/cjdns.keys 274 install -m 444 <(echo -e "CJDNS_IPV6=$ipv6\nCJDNS_PUBLIC_KEY=$public") /etc/cjdns.public 275 fi 276 277 if [ -z "$CJDNS_ADMIN_PASSWORD" ]; then 278 echo "CJDNS_ADMIN_PASSWORD=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)" \ 279 >> /etc/cjdns.keys 280 fi 281 ''; 282 283 script = ( 284 if cfg.confFile != null then 285 "${pkg}/bin/cjdroute < ${cfg.confFile}" 286 else 287 '' 288 source /etc/cjdns.keys 289 (cat <<'EOF' 290 ${cjdrouteConf} 291 EOF 292 ) | sed \ 293 -e "s/@CJDNS_ADMIN_PASSWORD@/$CJDNS_ADMIN_PASSWORD/g" \ 294 -e "s/@CJDNS_PRIVATE_KEY@/$CJDNS_PRIVATE_KEY/g" \ 295 | ${pkg}/bin/cjdroute 296 '' 297 ); 298 299 startLimitIntervalSec = 0; 300 serviceConfig = { 301 Type = "forking"; 302 Restart = "always"; 303 RestartSec = 1; 304 CapabilityBoundingSet = "CAP_NET_ADMIN CAP_NET_RAW CAP_SETUID"; 305 ProtectSystem = true; 306 # Doesn't work on i686, causing service to fail 307 MemoryDenyWriteExecute = !pkgs.stdenv.hostPlatform.isi686; 308 ProtectHome = true; 309 PrivateTmp = true; 310 }; 311 }; 312 313 networking.hostFiles = lib.mkIf cfg.addExtraHosts [ cjdnsExtraHosts ]; 314 315 assertions = [ 316 { 317 assertion = (cfg.ETHInterface.bind != "" || cfg.UDPInterface.bind != "" || cfg.confFile != null); 318 message = "Neither cjdns.ETHInterface.bind nor cjdns.UDPInterface.bind defined."; 319 } 320 { 321 assertion = config.networking.enableIPv6; 322 message = "networking.enableIPv6 must be enabled for CJDNS to work"; 323 } 324 ]; 325 326 }; 327 328}