Kieran's opinionated (and probably slightly dumb) nix config
at main 5.2 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.atelier.services.battleship-arena; 7in 8{ 9 options.atelier.services.battleship-arena = { 10 enable = mkEnableOption "battleship-arena service"; 11 12 domain = mkOption { 13 type = types.str; 14 default = "battleship.dunkirk.sh"; 15 description = "Domain name for the web interface"; 16 }; 17 18 sshPort = mkOption { 19 type = types.port; 20 default = 2222; 21 description = "SSH port for battleship arena"; 22 }; 23 24 webPort = mkOption { 25 type = types.port; 26 default = 8081; 27 description = "Web interface port"; 28 }; 29 30 uploadDir = mkOption { 31 type = types.str; 32 default = "/var/lib/battleship-arena/submissions"; 33 description = "Directory for uploaded submissions"; 34 }; 35 36 resultsDb = mkOption { 37 type = types.str; 38 default = "/var/lib/battleship-arena/results.db"; 39 description = "Path to results database"; 40 }; 41 42 adminPasscode = mkOption { 43 type = types.str; 44 default = "battleship-admin-override"; 45 description = "Admin passcode for batch uploads"; 46 }; 47 48 secretsFile = mkOption { 49 type = types.nullOr types.path; 50 default = null; 51 description = "Path to agenix secrets file containing BATTLESHIP_ADMIN_PASSCODE"; 52 }; 53 54 package = mkOption { 55 type = types.package; 56 description = "The battleship-arena package to use"; 57 }; 58 }; 59 60 config = mkIf cfg.enable { 61 users.users.battleship-arena = { 62 isSystemUser = true; 63 group = "battleship-arena"; 64 home = "/var/lib/battleship-arena"; 65 createHome = true; 66 }; 67 68 users.groups.battleship-arena = {}; 69 70 systemd.services.battleship-arena = { 71 description = "Battleship Arena SSH/Web Service"; 72 after = [ "network.target" ]; 73 wantedBy = [ "multi-user.target" ]; 74 75 environment = { 76 BATTLESHIP_HOST = "0.0.0.0"; 77 BATTLESHIP_SSH_PORT = toString cfg.sshPort; 78 BATTLESHIP_WEB_PORT = toString cfg.webPort; 79 BATTLESHIP_UPLOAD_DIR = cfg.uploadDir; 80 BATTLESHIP_RESULTS_DB = cfg.resultsDb; 81 BATTLESHIP_ADMIN_PASSCODE = cfg.adminPasscode; 82 BATTLESHIP_EXTERNAL_URL = "https://${cfg.domain}"; 83 BATTLESHIP_ENGINE_PATH = "/var/lib/battleship-arena/battleship-engine"; 84 CPLUS_INCLUDE_PATH = "/var/lib/battleship-arena/battleship-engine/include"; 85 }; 86 87 path = [ pkgs.gcc pkgs.coreutils ]; 88 89 serviceConfig = { 90 Type = "simple"; 91 User = "battleship-arena"; 92 Group = "battleship-arena"; 93 WorkingDirectory = "/var/lib/battleship-arena"; 94 ExecStart = "${cfg.package}/bin/battleship-arena"; 95 Restart = "always"; 96 RestartSec = "10s"; 97 98 # Load secrets if provided 99 EnvironmentFile = mkIf (cfg.secretsFile != null) cfg.secretsFile; 100 101 # Security hardening 102 NoNewPrivileges = true; 103 PrivateTmp = true; 104 ProtectSystem = "strict"; 105 ProtectHome = true; 106 ReadWritePaths = [ "/var/lib/battleship-arena" ]; 107 }; 108 109 preStart = '' 110 mkdir -p ${cfg.uploadDir} 111 mkdir -p $(dirname ${cfg.resultsDb}) 112 chown -R battleship-arena:battleship-arena ${cfg.uploadDir} 113 chmod -R u+rwX ${cfg.uploadDir} 114 115 # Generate SSH host key if it doesn't exist 116 if [ ! -f /var/lib/battleship-arena/.ssh/battleship_arena ]; then 117 mkdir -p /var/lib/battleship-arena/.ssh 118 ${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /var/lib/battleship-arena/.ssh/battleship_arena -N "" 119 chown -R battleship-arena:battleship-arena /var/lib/battleship-arena/.ssh 120 fi 121 122 # Copy battleship-engine to writable directory 123 chmod -R u+w /var/lib/battleship-arena/battleship-engine 2>/dev/null || true 124 rm -rf /var/lib/battleship-arena/battleship-engine 125 cp -r ${cfg.package}/share/battleship-arena/battleship-engine /var/lib/battleship-arena/ 126 chown -R battleship-arena:battleship-arena /var/lib/battleship-arena/battleship-engine 127 chmod -R u+rwX /var/lib/battleship-arena/battleship-engine 128 ''; 129 }; 130 131 # Service to recalculate Glicko-2 ratings (manual trigger only) 132 # Ratings automatically recalculate after each round-robin 133 # Use: sudo systemctl start battleship-arena-recalculate 134 systemd.services.battleship-arena-recalculate = { 135 description = "Recalculate Battleship Arena Glicko-2 Ratings"; 136 137 environment = { 138 BATTLESHIP_RESULTS_DB = cfg.resultsDb; 139 }; 140 141 serviceConfig = { 142 Type = "oneshot"; 143 User = "battleship-arena"; 144 Group = "battleship-arena"; 145 WorkingDirectory = "/var/lib/battleship-arena"; 146 ExecStart = "${cfg.package}/bin/battleship-arena recalculate-ratings"; 147 }; 148 }; 149 150 # Allow battleship-arena user to create transient systemd units for sandboxing 151 security.polkit.extraConfig = '' 152 polkit.addRule(function(action, subject) { 153 if (action.id == "org.freedesktop.systemd1.manage-units" && 154 subject.user == "battleship-arena") { 155 return polkit.Result.YES; 156 } 157 }); 158 ''; 159 160 networking.firewall.allowedTCPPorts = [ cfg.sshPort ]; 161 }; 162}