2024-09-11 20:22:53 +02:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
let
|
|
|
|
cfg = config.kyouma.ooklaserver;
|
|
|
|
in {
|
|
|
|
options = {
|
2024-09-12 00:15:52 +02:00
|
|
|
kyouma.ooklaserver = let
|
|
|
|
inherit (lib) mkOption types;
|
|
|
|
in {
|
|
|
|
enable = lib.mkEnableOption "ookla speedtest server";
|
|
|
|
package = lib.mkPackageOption pkgs "ooklaserver" {};
|
2024-09-11 20:22:53 +02:00
|
|
|
domain = mkOption {
|
|
|
|
description = "Domain to use.";
|
|
|
|
default = null;
|
2024-09-12 00:15:52 +02:00
|
|
|
type = with types; nullOr nonEmptyStr;
|
2024-09-11 20:22:53 +02:00
|
|
|
};
|
|
|
|
openFirewall = mkOption {
|
|
|
|
description = "Whether to open the firewall for the specified ports.";
|
|
|
|
default = false;
|
|
|
|
type = types.bool;
|
|
|
|
};
|
|
|
|
tcpPorts = mkOption {
|
|
|
|
description = ''
|
|
|
|
The server listens on TCP port 5060 and 8080 by default. These ports are required for
|
|
|
|
speedtest.net servers, although more can be added.
|
|
|
|
'';
|
|
|
|
default = [ 5060 8080 ];
|
|
|
|
type = with types; listOf port;
|
|
|
|
};
|
|
|
|
udpPorts = mkOption {
|
|
|
|
description = ''
|
|
|
|
The server listens on UDP port 5060 and 8080 by default. These ports are required for
|
|
|
|
speedtest.net servers, although more can be added.
|
|
|
|
'';
|
|
|
|
default = [ 5060 8080 ];
|
|
|
|
type = with types; listOf port;
|
|
|
|
};
|
|
|
|
settings = mkOption {
|
|
|
|
description = ''
|
|
|
|
OoklaServer configuration written as Nix expression.
|
|
|
|
Comma seperated values should be written as list.
|
|
|
|
'';
|
|
|
|
default = {};
|
|
|
|
type = with lib.types; let
|
|
|
|
valueType = nullOr (oneOf [
|
|
|
|
bool
|
|
|
|
int
|
|
|
|
str
|
|
|
|
(attrsOf valueType)
|
|
|
|
(listOf (oneOf [ port str ]))
|
|
|
|
]);
|
|
|
|
in valueType;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
|
|
security.acme.certs.${cfg.domain} = {
|
|
|
|
reloadServices = [ "ooklaserver.service" ];
|
|
|
|
webroot = "/var/lib/acme/acme-challenge";
|
|
|
|
};
|
|
|
|
|
2024-09-12 00:15:52 +02:00
|
|
|
networking.firewall = lib.mkIf cfg.openFirewall {
|
|
|
|
allowedUDPPorts = cfg.udpPorts;
|
|
|
|
allowedTCPPorts = cfg.tcpPorts;
|
|
|
|
};
|
|
|
|
|
|
|
|
kyouma.ooklaserver.settings = let
|
|
|
|
inherit (lib) mkDefault;
|
|
|
|
in {
|
2024-09-11 20:22:53 +02:00
|
|
|
OoklaServer = {
|
|
|
|
inherit (cfg) tcpPorts udpPorts;
|
|
|
|
enableAutoUpdate = false;
|
|
|
|
ssl.useLetsEncrypt = false;
|
|
|
|
useIPv6 = mkDefault true;
|
|
|
|
allowedDomains = mkDefault [ "*.ookla.com" "*.speedtest.net" ];
|
|
|
|
userAgentFilterEnabled = mkDefault true;
|
|
|
|
workerThreadPool = {
|
|
|
|
capacity = mkDefault 30000;
|
|
|
|
stackSizeBytes = mkDefault 102400;
|
|
|
|
};
|
|
|
|
ipTracking = {
|
|
|
|
gcIntervalMinutes = mkDefault 5;
|
|
|
|
maxIdleAgeMinutes = mkDefault 35;
|
|
|
|
slidingWindowBucketLengthMinutes = mkDefault 5;
|
|
|
|
metricTopIpCount = mkDefault 5;
|
|
|
|
maxConnPerIp = mkDefault 500;
|
|
|
|
maxConnPerBucketPerIp = mkDefault 20000;
|
|
|
|
};
|
|
|
|
clientAuthToken.denyInvalid = mkDefault true;
|
|
|
|
websocket.frameSizeLimitBytes = mkDefault 5242880;
|
|
|
|
http.maxHeadersSize = mkDefault 65536;
|
|
|
|
};
|
|
|
|
openSSL.server = {
|
2024-09-12 00:15:52 +02:00
|
|
|
certificateFile = "/run/credentials/${config.systemd.services.ooklaserver.name}/cert.pem";
|
|
|
|
privateKeyFile = "/run/credentials/${config.systemd.services.ooklaserver.name}/key.pem";
|
2024-09-11 20:22:53 +02:00
|
|
|
minimumTLSProtocol = mkDefault "1.2";
|
|
|
|
};
|
|
|
|
logging.loggers.app = {
|
|
|
|
name = mkDefault "Application";
|
|
|
|
channel = {
|
|
|
|
class = mkDefault "ConsoleChannel";
|
|
|
|
pattern = mkDefault "[%p] %t";
|
|
|
|
};
|
|
|
|
level = mkDefault "information";
|
|
|
|
};
|
|
|
|
};
|
2024-09-12 00:15:52 +02:00
|
|
|
|
2024-09-11 20:22:53 +02:00
|
|
|
systemd.services.ooklaserver = let
|
|
|
|
configFile = let
|
|
|
|
anyToString = arg: if (lib.isBool arg) then
|
|
|
|
lib.boolToString arg
|
|
|
|
else if (lib.isList arg) then
|
|
|
|
lib.concatStringsSep "," (map (val: toString val) arg)
|
|
|
|
else toString arg;
|
2024-09-12 00:15:52 +02:00
|
|
|
in
|
|
|
|
with lib; lib.pipe cfg.settings [
|
2024-09-11 20:22:53 +02:00
|
|
|
(mapAttrsRecursive (path: val: "${concatStringsSep "." path} = ${anyToString val}"))
|
|
|
|
(collect isString)
|
|
|
|
(concatLines)
|
|
|
|
(pkgs.writeTextDir "bin/OoklaServer.properties")
|
|
|
|
];
|
|
|
|
packageWithCfg = pkgs.symlinkJoin {
|
|
|
|
name = "${cfg.package.name}-with-config";
|
|
|
|
paths = [ cfg.package configFile ];
|
|
|
|
};
|
|
|
|
in {
|
|
|
|
description = "Ookla speedtest server daemon";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
wants = [ "network-online.target" ];
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
Type = "simple";
|
|
|
|
Restart = "always";
|
2024-09-12 00:15:52 +02:00
|
|
|
|
2024-09-11 20:22:53 +02:00
|
|
|
User = "ooklaserver";
|
|
|
|
Group = "ooklaserver";
|
2024-09-12 00:15:52 +02:00
|
|
|
DynamicUser = true;
|
2024-09-11 20:22:53 +02:00
|
|
|
|
2024-09-12 00:15:52 +02:00
|
|
|
LoadCredential = [
|
|
|
|
"cert.pem:${config.security.acme.certs.${cfg.domain}.directory}/cert.pem"
|
|
|
|
"key.pem:${config.security.acme.certs.${cfg.domain}.directory}/key.pem"
|
|
|
|
];
|
2024-09-11 20:22:53 +02:00
|
|
|
ExecStart = "${packageWithCfg}/bin/OoklaServer";
|
|
|
|
WorkingDirectory = packageWithCfg;
|
|
|
|
SyslogIdentifier = "ooklaserver";
|
|
|
|
|
|
|
|
ReadOnlyPaths = [ packageWithCfg ];
|
|
|
|
RestrictSUIDSGID = true;
|
|
|
|
RestrictNamespaces = true;
|
|
|
|
PrivateTmp = true;
|
|
|
|
PrivateDevices = true;
|
|
|
|
PrivateUsers = true;
|
|
|
|
ProtectHostname = true;
|
|
|
|
ProtectClock = true;
|
|
|
|
ProtectKernelTunables = true;
|
|
|
|
ProtectKernelModules = true;
|
|
|
|
ProtectKernelLogs = true;
|
|
|
|
ProtectControlGroups = true;
|
|
|
|
ProtectSystem = "strict";
|
|
|
|
ProtectHome = true;
|
|
|
|
ProtectProc = "invisible";
|
|
|
|
SystemCallArchitectures = "native";
|
|
|
|
SystemCallFilter = "@system-service";
|
|
|
|
SystemCallErrorNumber = "EPERM";
|
|
|
|
LockPersonality = true;
|
|
|
|
NoNewPrivileges = true;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|