Kieran's opinionated (and probably slightly dumb) nix config

feat: add battleship

dunkirk.sh 6be37ef7 9923b828

verified
Changed files
+176
machines
terebithia
modules
nixos
secrets
+22
flake.lock
···
"type": "github"
}
},
+
"battleship-arena": {
+
"inputs": {
+
"nixpkgs": [
+
"nixpkgs"
+
]
+
},
+
"locked": {
+
"lastModified": 1764790763,
+
"narHash": "sha256-zeitWozGRMpBXpv0w2JchhvijPceJ1ZBE18Y3j19je8=",
+
"owner": "taciturnaxolotl",
+
"repo": "battleship-arena",
+
"rev": "ea9ed8d601369a4fb29b92ea6f408f1610ac4280",
+
"type": "github"
+
},
+
"original": {
+
"owner": "taciturnaxolotl",
+
"repo": "battleship-arena",
+
"rev": "ea9ed8d601369a4fb29b92ea6f408f1610ac4280",
+
"type": "github"
+
}
+
},
"catppuccin": {
"inputs": {
"nixpkgs": [
···
"root": {
"inputs": {
"agenix": "agenix",
+
"battleship-arena": "battleship-arena",
"catppuccin": "catppuccin",
"catppuccin-vsc": "catppuccin-vsc",
"cedarlogic": "cedarlogic",
+5
flake.nix
···
url = "git+https://tangled.org/tangled.org/core";
inputs.nixpkgs.follows = "nixpkgs";
};
+
+
battleship-arena = {
+
url = "github:taciturnaxolotl/battleship-arena/ea9ed8d601369a4fb29b92ea6f408f1610ac4280";
+
inputs.nixpkgs.follows = "nixpkgs";
+
};
};
outputs =
+26
machines/terebithia/default.nix
···
file = ../../secrets/github-knot-sync.age;
owner = "git";
};
+
battleship-arena = {
+
file = ../../secrets/battleship-arena.age;
+
owner = "battleship-arena";
+
};
};
environment.sessionVariables = {
···
}
'';
};
+
virtualHosts."battleship.dunkirk.sh" = {
+
extraConfig = ''
+
tls {
+
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
+
}
+
header {
+
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
+
}
+
reverse_proxy localhost:8081 {
+
header_up X-Forwarded-Proto {scheme}
+
header_up X-Forwarded-For {remote}
+
}
+
'';
+
};
extraConfig = ''
# Default response for unhandled domains
:80 {
···
enable = true;
domain = "emojibot.dunkirk.sh";
secretsFile = config.age.secrets.emojibot.path;
+
};
+
+
atelier.services.battleship-arena = {
+
enable = true;
+
domain = "battleship.dunkirk.sh";
+
sshPort = 2222;
+
package = inputs.battleship-arena.packages.aarch64-linux.default;
+
secretsFile = config.age.secrets.battleship-arena.path;
};
services.tangled.knot = {
+120
modules/nixos/services/battleship-arena.nix
···
+
{ config, lib, pkgs, ... }:
+
+
with lib;
+
+
let
+
cfg = config.atelier.services.battleship-arena;
+
in
+
{
+
options.atelier.services.battleship-arena = {
+
enable = mkEnableOption "battleship-arena service";
+
+
domain = mkOption {
+
type = types.str;
+
default = "battleship.dunkirk.sh";
+
description = "Domain name for the web interface";
+
};
+
+
sshPort = mkOption {
+
type = types.port;
+
default = 2222;
+
description = "SSH port for battleship arena";
+
};
+
+
webPort = mkOption {
+
type = types.port;
+
default = 8081;
+
description = "Web interface port";
+
};
+
+
uploadDir = mkOption {
+
type = types.str;
+
default = "/var/lib/battleship-arena/submissions";
+
description = "Directory for uploaded submissions";
+
};
+
+
resultsDb = mkOption {
+
type = types.str;
+
default = "/var/lib/battleship-arena/results.db";
+
description = "Path to results database";
+
};
+
+
adminPasscode = mkOption {
+
type = types.str;
+
default = "battleship-admin-override";
+
description = "Admin passcode for batch uploads";
+
};
+
+
secretsFile = mkOption {
+
type = types.nullOr types.path;
+
default = null;
+
description = "Path to agenix secrets file containing BATTLESHIP_ADMIN_PASSCODE";
+
};
+
+
package = mkOption {
+
type = types.package;
+
description = "The battleship-arena package to use";
+
};
+
};
+
+
config = mkIf cfg.enable {
+
users.users.battleship-arena = {
+
isSystemUser = true;
+
group = "battleship-arena";
+
home = "/var/lib/battleship-arena";
+
createHome = true;
+
};
+
+
users.groups.battleship-arena = {};
+
+
systemd.services.battleship-arena = {
+
description = "Battleship Arena SSH/Web Service";
+
after = [ "network.target" ];
+
wantedBy = [ "multi-user.target" ];
+
+
environment = {
+
BATTLESHIP_HOST = "0.0.0.0";
+
BATTLESHIP_SSH_PORT = toString cfg.sshPort;
+
BATTLESHIP_WEB_PORT = toString cfg.webPort;
+
BATTLESHIP_UPLOAD_DIR = cfg.uploadDir;
+
BATTLESHIP_RESULTS_DB = cfg.resultsDb;
+
BATTLESHIP_ADMIN_PASSCODE = cfg.adminPasscode;
+
BATTLESHIP_EXTERNAL_URL = "https://${cfg.domain}";
+
};
+
+
serviceConfig = {
+
Type = "simple";
+
User = "battleship-arena";
+
Group = "battleship-arena";
+
WorkingDirectory = "/var/lib/battleship-arena";
+
ExecStart = "${cfg.package}/bin/battleship-arena";
+
Restart = "always";
+
RestartSec = "10s";
+
+
# Load secrets if provided
+
EnvironmentFile = mkIf (cfg.secretsFile != null) cfg.secretsFile;
+
+
# Security hardening
+
NoNewPrivileges = true;
+
PrivateTmp = true;
+
ProtectSystem = "strict";
+
ProtectHome = true;
+
ReadWritePaths = [ "/var/lib/battleship-arena" ];
+
};
+
+
preStart = ''
+
mkdir -p ${cfg.uploadDir}
+
mkdir -p $(dirname ${cfg.resultsDb})
+
+
# Generate SSH host key if it doesn't exist
+
if [ ! -f /var/lib/battleship-arena/.ssh/battleship_arena ]; then
+
mkdir -p /var/lib/battleship-arena/.ssh
+
${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f /var/lib/battleship-arena/.ssh/battleship_arena -N ""
+
chown -R battleship-arena:battleship-arena /var/lib/battleship-arena/.ssh
+
fi
+
'';
+
};
+
+
networking.firewall.allowedTCPPorts = [ cfg.sshPort ];
+
};
+
}
secrets/battleship-arena.age

This is a binary file and will not be displayed.

+3
secrets/secrets.nix
···
"emojibot.age".publicKeys = [
kierank
];
+
"battleship-arena.age".publicKeys = [
+
kierank
+
];
}