|
| 1 | +{ |
| 2 | + config, |
| 3 | + pkgs, |
| 4 | + lib, |
| 5 | + ... |
| 6 | +}: |
| 7 | +with lib; |
| 8 | +let |
| 9 | + cfg = config.services.veilid; |
| 10 | + dataDir = "/var/db/veilid-server"; |
| 11 | + |
| 12 | + settingsFormat = pkgs.formats.yaml { }; |
| 13 | + configFile = settingsFormat.generate "veilid-server.conf" cfg.settings; |
| 14 | +in |
| 15 | +{ |
| 16 | + config = mkIf cfg.enable { |
| 17 | + networking.firewall = mkIf cfg.openFirewall { |
| 18 | + allowedTCPPorts = [ 5150 ]; |
| 19 | + allowedUDPPorts = [ 5150 ]; |
| 20 | + }; |
| 21 | + |
| 22 | + # Based on https://gitlab.com/veilid/veilid/-/blob/main/package/systemd/veilid-server.service?ref_type=heads |
| 23 | + systemd.services.veilid = { |
| 24 | + enable = true; |
| 25 | + description = "Veilid Headless Node"; |
| 26 | + wants = [ "network-online.target" ]; |
| 27 | + before = [ "network-online.target" ]; |
| 28 | + wantedBy = [ "multi-user.target" ]; |
| 29 | + restartTriggers = [ configFile ]; |
| 30 | + environment = { |
| 31 | + RUST_BACKTRACE = "1"; |
| 32 | + }; |
| 33 | + serviceConfig = { |
| 34 | + ExecStart = "${pkgs.veilid}/bin/veilid-server -c ${configFile}"; |
| 35 | + ExecReload = "${pkgs.coreutils}/bin/kill -s HUP $MAINPID"; |
| 36 | + KillSignal = "SIGQUIT"; |
| 37 | + TimeoutStopSec = 5; |
| 38 | + WorkingDirectory = "/"; |
| 39 | + User = "veilid"; |
| 40 | + Group = "veilid"; |
| 41 | + UMask = "0002"; |
| 42 | + |
| 43 | + CapabilityBoundingSet = ""; |
| 44 | + SystemCallFilter = [ "@system-service" ]; |
| 45 | + MemoryDenyWriteExecute = true; |
| 46 | + NoNewPrivileges = true; |
| 47 | + PrivateDevices = true; |
| 48 | + PrivateTmp = true; |
| 49 | + PrivateUsers = true; |
| 50 | + ProtectHome = true; |
| 51 | + ProtectClock = true; |
| 52 | + ProtectControlGroups = true; |
| 53 | + ProtectKernelLogs = true; |
| 54 | + ProtectKernelModules = true; |
| 55 | + ProtectKernelTunables = true; |
| 56 | + ProtectProc = "invisible"; |
| 57 | + ProtectSystem = "strict"; |
| 58 | + ReadWritePaths = dataDir; |
| 59 | + |
| 60 | + RestrictRealtime = true; |
| 61 | + SystemCallArchitectures = "native"; |
| 62 | + LockPersonality = true; |
| 63 | + RestrictSUIDSGID = true; |
| 64 | + }; |
| 65 | + }; |
| 66 | + users.users.veilid = { |
| 67 | + isSystemUser = true; |
| 68 | + group = "veilid"; |
| 69 | + home = dataDir; |
| 70 | + createHome = true; |
| 71 | + }; |
| 72 | + users.groups.veilid = { }; |
| 73 | + |
| 74 | + environment = { |
| 75 | + systemPackages = [ pkgs.veilid ]; |
| 76 | + }; |
| 77 | + services.veilid.settings = { }; |
| 78 | + }; |
| 79 | + |
| 80 | + options.services.veilid = { |
| 81 | + enable = mkEnableOption "Veilid Headless Node"; |
| 82 | + openFirewall = mkOption { |
| 83 | + default = false; |
| 84 | + type = types.bool; |
| 85 | + description = "Whether to open firewall on ports 5150/tcp, 5150/udp"; |
| 86 | + }; |
| 87 | + settings = mkOption { |
| 88 | + description = '' |
| 89 | + Build veilid-server.conf with nix expression. |
| 90 | + Check <link xlink:href="https://veilid.gitlab.io/developer-book/admin/config.html#configuration-keys">Configuration Keys</link>. |
| 91 | + ''; |
| 92 | + type = types.submodule { |
| 93 | + freeformType = settingsFormat.type; |
| 94 | + |
| 95 | + options = { |
| 96 | + client_api = { |
| 97 | + ipc_enabled = mkOption { |
| 98 | + type = types.bool; |
| 99 | + default = true; |
| 100 | + description = "veilid-server will respond to Python and other JSON client requests."; |
| 101 | + }; |
| 102 | + ipc_directory = mkOption { |
| 103 | + type = types.str; |
| 104 | + default = "${dataDir}/ipc"; |
| 105 | + description = "IPC directory where file sockets are stored."; |
| 106 | + }; |
| 107 | + }; |
| 108 | + logging = { |
| 109 | + system = { |
| 110 | + enabled = mkOption { |
| 111 | + type = types.bool; |
| 112 | + default = true; |
| 113 | + description = "Events of type 'system' will be logged."; |
| 114 | + }; |
| 115 | + level = mkOption { |
| 116 | + type = types.str; |
| 117 | + default = "info"; |
| 118 | + example = "debug"; |
| 119 | + description = "The minimum priority of system events to be logged."; |
| 120 | + }; |
| 121 | + }; |
| 122 | + terminal = { |
| 123 | + enabled = mkOption { |
| 124 | + type = types.bool; |
| 125 | + default = false; |
| 126 | + description = "Events of type 'terminal' will be logged."; |
| 127 | + }; |
| 128 | + level = mkOption { |
| 129 | + type = types.str; |
| 130 | + default = "info"; |
| 131 | + example = "debug"; |
| 132 | + description = "The minimum priority of terminal events to be logged."; |
| 133 | + }; |
| 134 | + }; |
| 135 | + api = { |
| 136 | + enabled = mkOption { |
| 137 | + type = types.bool; |
| 138 | + default = false; |
| 139 | + description = "Events of type 'api' will be logged."; |
| 140 | + }; |
| 141 | + level = mkOption { |
| 142 | + type = types.str; |
| 143 | + default = "info"; |
| 144 | + example = "debug"; |
| 145 | + description = "The minimum priority of api events to be logged."; |
| 146 | + }; |
| 147 | + }; |
| 148 | + }; |
| 149 | + core = { |
| 150 | + capabilities = { |
| 151 | + disable = mkOption { |
| 152 | + type = types.listOf types.str; |
| 153 | + default = [ ]; |
| 154 | + example = [ "APPM" ]; |
| 155 | + description = "A list of capabilities to disable (for example, DHTV to say you cannot store DHT information)."; |
| 156 | + }; |
| 157 | + }; |
| 158 | + protected_store = { |
| 159 | + allow_insecure_fallback = mkOption { |
| 160 | + type = types.bool; |
| 161 | + default = true; |
| 162 | + description = "If we can't use system-provided secure storage, should we proceed anyway?"; |
| 163 | + }; |
| 164 | + always_use_insecure_storage = mkOption { |
| 165 | + type = types.bool; |
| 166 | + default = true; |
| 167 | + description = "Should we bypass any attempt to use system-provided secure storage?"; |
| 168 | + }; |
| 169 | + directory = mkOption { |
| 170 | + type = types.str; |
| 171 | + default = "${dataDir}/protected_store"; |
| 172 | + description = "The filesystem directory to store your protected store in."; |
| 173 | + }; |
| 174 | + }; |
| 175 | + table_store = { |
| 176 | + directory = mkOption { |
| 177 | + type = types.str; |
| 178 | + default = "${dataDir}/table_store"; |
| 179 | + description = "The filesystem directory to store your table store within."; |
| 180 | + }; |
| 181 | + }; |
| 182 | + block_store = { |
| 183 | + directory = mkOption { |
| 184 | + type = types.nullOr types.str; |
| 185 | + default = "${dataDir}/block_store"; |
| 186 | + description = "The filesystem directory to store blocks for the block store."; |
| 187 | + }; |
| 188 | + }; |
| 189 | + network = { |
| 190 | + routing_table = { |
| 191 | + bootstrap = mkOption { |
| 192 | + type = types.listOf types.str; |
| 193 | + default = [ "bootstrap.veilid.net" ]; |
| 194 | + description = "Host name of existing well-known Veilid bootstrap servers for the network to connect to."; |
| 195 | + }; |
| 196 | + node_id = lib.mkOption { |
| 197 | + type = lib.types.nullOr lib.types.str; |
| 198 | + default = null; |
| 199 | + description = "Base64-encoded public key for the node, used as the node's ID."; |
| 200 | + }; |
| 201 | + }; |
| 202 | + dht = { |
| 203 | + min_peer_count = mkOption { |
| 204 | + type = types.number; |
| 205 | + default = 20; |
| 206 | + description = "Minimum number of nodes to keep in the peer table."; |
| 207 | + }; |
| 208 | + }; |
| 209 | + upnp = mkOption { |
| 210 | + type = types.bool; |
| 211 | + default = true; |
| 212 | + description = "Should the app try to improve its incoming network connectivity using UPnP?"; |
| 213 | + }; |
| 214 | + detect_address_changes = mkOption { |
| 215 | + type = types.bool; |
| 216 | + default = true; |
| 217 | + description = "Should veilid-core detect and notify on network address changes?"; |
| 218 | + }; |
| 219 | + }; |
| 220 | + }; |
| 221 | + }; |
| 222 | + }; |
| 223 | + }; |
| 224 | + }; |
| 225 | + |
| 226 | + meta.maintainers = with maintainers; [ figboy9 ]; |
| 227 | +} |
0 commit comments