at 21.11-pre 7.2 kB view raw
1{ config, lib, pkgs, ... }: 2with lib; 3let 4 keysPath = "/var/lib/yggdrasil/keys.json"; 5 6 cfg = config.services.yggdrasil; 7 configProvided = cfg.config != { }; 8 configFileProvided = cfg.configFile != null; 9 10in { 11 options = with types; { 12 services.yggdrasil = { 13 enable = mkEnableOption "the yggdrasil system service"; 14 15 config = mkOption { 16 type = attrs; 17 default = {}; 18 example = { 19 Peers = [ 20 "tcp://aa.bb.cc.dd:eeeee" 21 "tcp://[aaaa:bbbb:cccc:dddd::eeee]:fffff" 22 ]; 23 Listen = [ 24 "tcp://0.0.0.0:xxxxx" 25 ]; 26 }; 27 description = '' 28 Configuration for yggdrasil, as a Nix attribute set. 29 30 Warning: this is stored in the WORLD-READABLE Nix store! 31 Therefore, it is not appropriate for private keys. If you 32 wish to specify the keys, use <option>configFile</option>. 33 34 If the <option>persistentKeys</option> is enabled then the 35 keys that are generated during activation will override 36 those in <option>config</option> or 37 <option>configFile</option>. 38 39 If no keys are specified then ephemeral keys are generated 40 and the Yggdrasil interface will have a random IPv6 address 41 each time the service is started, this is the default. 42 43 If both <option>configFile</option> and <option>config</option> 44 are supplied, they will be combined, with values from 45 <option>configFile</option> taking precedence. 46 47 You can use the command <code>nix-shell -p yggdrasil --run 48 "yggdrasil -genconf"</code> to generate default 49 configuration values with documentation. 50 ''; 51 }; 52 53 configFile = mkOption { 54 type = nullOr path; 55 default = null; 56 example = "/run/keys/yggdrasil.conf"; 57 description = '' 58 A file which contains JSON configuration for yggdrasil. 59 See the <option>config</option> option for more information. 60 ''; 61 }; 62 63 group = mkOption { 64 type = types.str; 65 default = "root"; 66 example = "wheel"; 67 description = "Group to grant access to the Yggdrasil control socket."; 68 }; 69 70 openMulticastPort = mkOption { 71 type = bool; 72 default = false; 73 description = '' 74 Whether to open the UDP port used for multicast peer 75 discovery. The NixOS firewall blocks link-local 76 communication, so in order to make local peering work you 77 will also need to set <code>LinkLocalTCPPort</code> in your 78 yggdrasil configuration (<option>config</option> or 79 <option>configFile</option>) to a port number other than 0, 80 and then add that port to 81 <option>networking.firewall.allowedTCPPorts</option>. 82 ''; 83 }; 84 85 denyDhcpcdInterfaces = mkOption { 86 type = listOf str; 87 default = []; 88 example = [ "tap*" ]; 89 description = '' 90 Disable the DHCP client for any interface whose name matches 91 any of the shell glob patterns in this list. Use this 92 option to prevent the DHCP client from broadcasting requests 93 on the yggdrasil network. It is only necessary to do so 94 when yggdrasil is running in TAP mode, because TUN 95 interfaces do not support broadcasting. 96 ''; 97 }; 98 99 package = mkOption { 100 type = package; 101 default = pkgs.yggdrasil; 102 defaultText = "pkgs.yggdrasil"; 103 description = "Yggdrasil package to use."; 104 }; 105 106 persistentKeys = mkEnableOption '' 107 If enabled then keys will be generated once and Yggdrasil 108 will retain the same IPv6 address when the service is 109 restarted. Keys are stored at ${keysPath}. 110 ''; 111 112 }; 113 }; 114 115 config = mkIf cfg.enable (let binYggdrasil = cfg.package + "/bin/yggdrasil"; 116 in { 117 assertions = [{ 118 assertion = config.networking.enableIPv6; 119 message = "networking.enableIPv6 must be true for yggdrasil to work"; 120 }]; 121 122 system.activationScripts.yggdrasil = mkIf cfg.persistentKeys '' 123 if [ ! -e ${keysPath} ] 124 then 125 mkdir --mode=700 -p ${builtins.dirOf keysPath} 126 ${binYggdrasil} -genconf -json \ 127 | ${pkgs.jq}/bin/jq \ 128 'to_entries|map(select(.key|endswith("Key")))|from_entries' \ 129 > ${keysPath} 130 fi 131 ''; 132 133 systemd.services.yggdrasil = { 134 description = "Yggdrasil Network Service"; 135 bindsTo = [ "network-online.target" ]; 136 after = [ "network-online.target" ]; 137 wantedBy = [ "multi-user.target" ]; 138 139 preStart = 140 (if configProvided || configFileProvided || cfg.persistentKeys then 141 "echo " 142 143 + (lib.optionalString configProvided 144 "'${builtins.toJSON cfg.config}'") 145 + (lib.optionalString configFileProvided "$(cat ${cfg.configFile})") 146 + (lib.optionalString cfg.persistentKeys "$(cat ${keysPath})") 147 + " | ${pkgs.jq}/bin/jq -s add | ${binYggdrasil} -normaliseconf -useconf" 148 else 149 "${binYggdrasil} -genconf") + " > /run/yggdrasil/yggdrasil.conf"; 150 151 serviceConfig = { 152 ExecStart = 153 "${binYggdrasil} -useconffile /run/yggdrasil/yggdrasil.conf"; 154 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; 155 Restart = "always"; 156 157 Group = cfg.group; 158 RuntimeDirectory = "yggdrasil"; 159 RuntimeDirectoryMode = "0750"; 160 BindReadOnlyPaths = lib.optional configFileProvided cfg.configFile 161 ++ lib.optional cfg.persistentKeys keysPath; 162 163 # TODO: as of yggdrasil 0.3.8 and systemd 243, yggdrasil fails 164 # to set up the network adapter when DynamicUser is set. See 165 # github.com/yggdrasil-network/yggdrasil-go/issues/557. The 166 # following options are implied by DynamicUser according to 167 # the systemd.exec documentation, and can be removed if the 168 # upstream issue is fixed and DynamicUser is set to true: 169 PrivateTmp = true; 170 RemoveIPC = true; 171 NoNewPrivileges = true; 172 ProtectSystem = "strict"; 173 RestrictSUIDSGID = true; 174 # End of list of options implied by DynamicUser. 175 176 AmbientCapabilities = "CAP_NET_ADMIN"; 177 CapabilityBoundingSet = "CAP_NET_ADMIN"; 178 MemoryDenyWriteExecute = true; 179 ProtectControlGroups = true; 180 ProtectHome = "tmpfs"; 181 ProtectKernelModules = true; 182 ProtectKernelTunables = true; 183 RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6 AF_NETLINK"; 184 RestrictNamespaces = true; 185 RestrictRealtime = true; 186 SystemCallArchitectures = "native"; 187 SystemCallFilter = "~@clock @cpu-emulation @debug @keyring @module @mount @obsolete @raw-io @resources"; 188 }; 189 }; 190 191 networking.dhcpcd.denyInterfaces = cfg.denyDhcpcdInterfaces; 192 networking.firewall.allowedUDPPorts = mkIf cfg.openMulticastPort [ 9001 ]; 193 194 # Make yggdrasilctl available on the command line. 195 environment.systemPackages = [ cfg.package ]; 196 }); 197 meta = { 198 doc = ./yggdrasil.xml; 199 maintainers = with lib.maintainers; [ gazally ehmry ]; 200 }; 201}