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