at master 14 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 buildEnv, 6 ... 7}: 8 9let 10 cfg = config.services.peering-manager; 11 12 pythonFmt = pkgs.formats.pythonVars { }; 13 settingsFile = pythonFmt.generate "peering-manager-settings.py" cfg.settings; 14 extraConfigFile = pkgs.writeTextFile { 15 name = "peering-manager-extraConfig.py"; 16 text = cfg.extraConfig; 17 }; 18 configFile = pkgs.concatText "configuration.py" [ 19 settingsFile 20 extraConfigFile 21 ]; 22 finalConfigFile = 23 if (cfg.environmentFile != null) then "/var/lib/peering-manager/configuration.py" else configFile; 24 25 pkg = 26 (pkgs.peering-manager.overrideAttrs (old: { 27 postInstall = '' 28 ln -s ${finalConfigFile} $out/opt/peering-manager/peering_manager/configuration.py 29 '' 30 + lib.optionalString cfg.enableLdap '' 31 ln -s ${cfg.ldapConfigPath} $out/opt/peering-manager/peering_manager/ldap_config.py 32 ''; 33 })).override 34 { 35 inherit (cfg) plugins; 36 }; 37 peeringManagerManageScript = pkgs.writeScriptBin "peering-manager-manage" '' 38 #!${pkgs.stdenv.shell} 39 export PYTHONPATH=${pkg.pythonPath} 40 sudo -u peering-manager ${pkg}/bin/peering-manager "$@" 41 ''; 42 43in 44{ 45 options.services.peering-manager = with lib; { 46 enable = mkOption { 47 type = types.bool; 48 default = false; 49 description = '' 50 Enable Peering Manager. 51 52 This module requires a reverse proxy that serves `/static` separately. 53 See this [example](https://github.com/peering-manager/contrib/blob/main/nginx.conf) on how to configure this. 54 ''; 55 }; 56 57 environmentFile = mkOption { 58 type = with types; nullOr path; 59 default = null; 60 example = "/run/secrets/peering-manager.env"; 61 description = '' 62 Environment file as defined in {manpage}`systemd.exec(5)`. 63 64 Secrets may be passed to the service without adding them to the world-readable 65 Nix store, by specifying placeholder variables as the option value in Nix and 66 setting these variables accordingly in the environment file. 67 68 ``` 69 # snippet of peering-manager-related config 70 services.peering-manager.settings.SOCIAL_AUTH_OIDC_SECRET = "$PM_OIDC_SECRET"; 71 ``` 72 73 ``` 74 # content of the environment file 75 PM_OIDC_SECRET=topsecret 76 ``` 77 78 Note that this file needs to be available on the host on which 79 `peering-manager` is running. 80 ''; 81 }; 82 83 enableScheduledTasks = mkOption { 84 type = types.bool; 85 default = true; 86 description = '' 87 Set up [scheduled tasks](https://peering-manager.readthedocs.io/en/stable/setup/8-scheduled-tasks/) 88 ''; 89 }; 90 91 listenAddress = mkOption { 92 type = types.str; 93 default = "[::1]"; 94 description = '' 95 Address the server will listen on. 96 ''; 97 }; 98 99 port = mkOption { 100 type = types.port; 101 default = 8001; 102 description = '' 103 Port the server will listen on. 104 ''; 105 }; 106 107 plugins = mkOption { 108 type = types.functionTo (types.listOf types.package); 109 default = _: [ ]; 110 defaultText = literalExpression '' 111 python3Packages: with python3Packages; []; 112 ''; 113 description = '' 114 List of plugin packages to install. 115 ''; 116 }; 117 118 secretKeyFile = mkOption { 119 type = types.path; 120 description = '' 121 Path to a file containing the secret key. 122 ''; 123 }; 124 125 peeringdbApiKeyFile = mkOption { 126 type = with types; nullOr path; 127 default = null; 128 description = '' 129 Path to a file containing the PeeringDB API key. 130 ''; 131 }; 132 133 settings = lib.mkOption { 134 description = '' 135 Configuration options to set in `configuration.py`. 136 See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options. 137 ''; 138 139 default = { }; 140 141 type = lib.types.submodule { 142 freeformType = pythonFmt.type; 143 144 options = { 145 ALLOWED_HOSTS = lib.mkOption { 146 type = with lib.types; listOf str; 147 default = [ "*" ]; 148 description = '' 149 A list of valid fully-qualified domain names (FQDNs) and/or IP 150 addresses that can be used to reach the peering manager service. 151 ''; 152 }; 153 }; 154 }; 155 }; 156 157 extraConfig = mkOption { 158 type = types.lines; 159 default = ""; 160 description = '' 161 Additional lines of configuration appended to the `configuration.py`. 162 See the [documentation](https://peering-manager.readthedocs.io/en/stable/configuration/optional-settings/) for more possible options. 163 ''; 164 }; 165 166 enableLdap = mkOption { 167 type = types.bool; 168 default = false; 169 description = '' 170 Enable LDAP-Authentication for Peering Manager. 171 172 This requires a configuration file being pass through `ldapConfigPath`. 173 ''; 174 }; 175 176 ldapConfigPath = mkOption { 177 type = types.path; 178 description = '' 179 Path to the Configuration-File for LDAP-Authentication, will be loaded as `ldap_config.py`. 180 See the [documentation](https://peering-manager.readthedocs.io/en/stable/setup/6-ldap/#configuration) for possible options. 181 ''; 182 }; 183 }; 184 185 imports = [ 186 (lib.mkRemovedOptionModule [ "services" "peering-manager" "enableOidc" ] '' 187 The enableOidc option has been removed, since peering-manager has OIDC support builtin since version >= 1.9.0. 188 189 Make sure to update your OIDC configuration according to the documentation: 190 https://peering-manager.readthedocs.io/en/v1.9.3/administration/authentication/oidc/ 191 '') 192 (lib.mkRemovedOptionModule [ "services" "peering-manager" "oidcConfigPath" ] '' 193 The oidcConfigPath option has been removed, since peering-manager has OIDC support builtin since version >= 1.9.0. 194 195 The new config settings for OIDC are explained in the documentation: 196 https://peering-manager.readthedocs.io/en/v1.9.3/administration/authentication/oidc/ 197 '') 198 ]; 199 200 config = lib.mkIf cfg.enable { 201 services.peering-manager = { 202 settings = { 203 DATABASE = { 204 NAME = "peering-manager"; 205 USER = "peering-manager"; 206 HOST = "/run/postgresql"; 207 }; 208 209 # Redis database settings. Redis is used for caching and for queuing background tasks such as webhook events. A separate 210 # configuration exists for each. Full connection details are required in both sections, and it is strongly recommended 211 # to use two separate database IDs. 212 REDIS = { 213 tasks = { 214 UNIX_SOCKET_PATH = config.services.redis.servers.peering-manager.unixSocket; 215 DATABASE = 0; 216 }; 217 caching = { 218 UNIX_SOCKET_PATH = config.services.redis.servers.peering-manager.unixSocket; 219 DATABASE = 1; 220 }; 221 }; 222 }; 223 224 extraConfig = '' 225 with open("${cfg.secretKeyFile}", "r") as file: 226 SECRET_KEY = file.readline() 227 '' 228 + lib.optionalString (cfg.peeringdbApiKeyFile != null) '' 229 with open("${cfg.peeringdbApiKeyFile}", "r") as file: 230 PEERINGDB_API_KEY = file.readline() 231 ''; 232 233 plugins = (ps: (lib.optionals cfg.enableLdap [ ps.django-auth-ldap ])); 234 }; 235 236 system.build.peeringManagerPkg = pkg; 237 238 services.redis.servers.peering-manager.enable = true; 239 240 services.postgresql = { 241 enable = true; 242 ensureDatabases = [ "peering-manager" ]; 243 ensureUsers = [ 244 { 245 name = "peering-manager"; 246 ensureDBOwnership = true; 247 } 248 ]; 249 }; 250 251 environment.systemPackages = [ peeringManagerManageScript ]; 252 253 systemd.targets.peering-manager = { 254 description = "Target for all Peering Manager services"; 255 wantedBy = [ "multi-user.target" ]; 256 wants = [ "network-online.target" ]; 257 after = [ 258 "network-online.target" 259 "redis-peering-manager.service" 260 ]; 261 }; 262 263 systemd.services = 264 let 265 defaults = { 266 environment = { 267 PYTHONPATH = pkg.pythonPath; 268 }; 269 serviceConfig = { 270 WorkingDirectory = "/var/lib/peering-manager"; 271 User = "peering-manager"; 272 Group = "peering-manager"; 273 StateDirectory = "peering-manager"; 274 StateDirectoryMode = "0750"; 275 Restart = "on-failure"; 276 }; 277 }; 278 in 279 { 280 peering-manager-config = lib.mkIf (cfg.environmentFile != null) ( 281 lib.recursiveUpdate defaults { 282 description = "Peering Manager config file setup"; 283 wantedBy = [ "peering-manager.target" ]; 284 serviceConfig = { 285 Type = "oneshot"; 286 EnvironmentFile = [ cfg.environmentFile ]; 287 ExecStart = "${lib.getExe pkgs.envsubst} -i ${configFile} -o ${finalConfigFile}"; 288 }; 289 } 290 ); 291 292 peering-manager-migration = lib.recursiveUpdate defaults { 293 description = "Peering Manager migrations"; 294 wantedBy = [ "peering-manager.target" ]; 295 after = lib.mkIf (cfg.environmentFile != null) [ "peering-manager-config.service" ]; 296 serviceConfig = { 297 Type = "oneshot"; 298 ExecStart = "${pkg}/bin/peering-manager migrate"; 299 }; 300 }; 301 302 peering-manager = lib.recursiveUpdate defaults { 303 description = "Peering Manager WSGI Service"; 304 wantedBy = [ "peering-manager.target" ]; 305 after = [ 306 "peering-manager-migration.service" 307 ] 308 ++ lib.optionals (cfg.environmentFile != null) [ "peering-manager-config.service" ]; 309 310 preStart = '' 311 ${pkg}/bin/peering-manager remove_stale_contenttypes --no-input 312 ''; 313 314 serviceConfig = { 315 ExecStart = '' 316 ${pkg.python.pkgs.gunicorn}/bin/gunicorn peering_manager.wsgi \ 317 --bind ${cfg.listenAddress}:${toString cfg.port} \ 318 --pythonpath ${pkg}/opt/peering-manager 319 ''; 320 }; 321 }; 322 323 peering-manager-rq = lib.recursiveUpdate defaults { 324 description = "Peering Manager Request Queue Worker"; 325 wantedBy = [ "peering-manager.target" ]; 326 after = [ "peering-manager.service" ]; 327 serviceConfig.ExecStart = "${pkg}/bin/peering-manager rqworker high default low"; 328 }; 329 330 peering-manager-housekeeping = lib.recursiveUpdate defaults { 331 description = "Peering Manager housekeeping job"; 332 after = [ "peering-manager.service" ]; 333 serviceConfig = { 334 Type = "oneshot"; 335 ExecStart = "${pkg}/bin/peering-manager housekeeping"; 336 }; 337 }; 338 339 peering-manager-peeringdb-sync = lib.recursiveUpdate defaults { 340 description = "PeeringDB sync"; 341 after = [ "peering-manager.service" ]; 342 serviceConfig = { 343 Type = "oneshot"; 344 ExecStart = "${pkg}/bin/peering-manager peeringdb_sync"; 345 }; 346 }; 347 348 peering-manager-prefix-fetch = lib.recursiveUpdate defaults { 349 description = "Fetch IRR AS-SET prefixes"; 350 after = [ "peering-manager.service" ]; 351 serviceConfig = { 352 Type = "oneshot"; 353 ExecStart = "${pkg}/bin/peering-manager grab_prefixes"; 354 }; 355 }; 356 357 peering-manager-configuration-deployment = lib.recursiveUpdate defaults { 358 description = "Push configuration to routers"; 359 after = [ "peering-manager.service" ]; 360 serviceConfig = { 361 Type = "oneshot"; 362 ExecStart = "${pkg}/bin/peering-manager configure_routers"; 363 }; 364 }; 365 366 peering-manager-session-poll = lib.recursiveUpdate defaults { 367 description = "Poll peering sessions from routers"; 368 after = [ "peering-manager.service" ]; 369 serviceConfig = { 370 Type = "oneshot"; 371 ExecStart = "${pkg}/bin/peering-manager poll_bgp_sessions"; 372 }; 373 }; 374 }; 375 376 systemd.timers = { 377 peering-manager-housekeeping = { 378 description = "Run Peering Manager housekeeping job"; 379 wantedBy = [ "timers.target" ]; 380 timerConfig.OnCalendar = "daily"; 381 }; 382 383 peering-manager-peeringdb-sync = { 384 enable = lib.mkDefault cfg.enableScheduledTasks; 385 description = "Sync PeeringDB at 2:30"; 386 wantedBy = [ "timers.target" ]; 387 timerConfig.OnCalendar = "02:30:00"; 388 }; 389 390 peering-manager-prefix-fetch = { 391 enable = lib.mkDefault cfg.enableScheduledTasks; 392 description = "Fetch IRR AS-SET prefixes at 4:30"; 393 wantedBy = [ "timers.target" ]; 394 timerConfig.OnCalendar = "04:30:00"; 395 }; 396 397 peering-manager-configuration-deployment = { 398 enable = lib.mkDefault cfg.enableScheduledTasks; 399 description = "Push router configuration every hour 5 minutes before full hour"; 400 wantedBy = [ "timers.target" ]; 401 timerConfig.OnCalendar = "*:55:00"; 402 }; 403 404 peering-manager-session-poll = { 405 enable = lib.mkDefault cfg.enableScheduledTasks; 406 description = "Poll peering sessions from routers every hour"; 407 wantedBy = [ "timers.target" ]; 408 timerConfig.OnCalendar = "*:00:00"; 409 }; 410 }; 411 412 users.users.peering-manager = { 413 home = "/var/lib/peering-manager"; 414 isSystemUser = true; 415 group = "peering-manager"; 416 }; 417 users.groups.peering-manager = { }; 418 users.groups."${config.services.redis.servers.peering-manager.user}".members = [ 419 "peering-manager" 420 ]; 421 }; 422 423 meta.maintainers = with lib.maintainers; [ yuka ]; 424}