nixos/freeciv: init
This commit is contained in:
parent
dbbdc2eb3e
commit
39a5e2c76b
@ -349,6 +349,7 @@
|
||||
./services/editors/emacs.nix
|
||||
./services/editors/infinoted.nix
|
||||
./services/games/factorio.nix
|
||||
./services/games/freeciv.nix
|
||||
./services/games/minecraft-server.nix
|
||||
./services/games/minetest-server.nix
|
||||
./services/games/openarena.nix
|
||||
|
187
nixos/modules/services/games/freeciv.nix
Normal file
187
nixos/modules/services/games/freeciv.nix
Normal file
@ -0,0 +1,187 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
with lib;
|
||||
let
|
||||
cfg = config.services.freeciv;
|
||||
inherit (config.users) groups;
|
||||
rootDir = "/run/freeciv";
|
||||
argsFormat = {
|
||||
type = with lib.types; let
|
||||
valueType = nullOr (oneOf [
|
||||
bool int float str
|
||||
(listOf valueType)
|
||||
]) // {
|
||||
description = "freeciv-server params";
|
||||
};
|
||||
in valueType;
|
||||
generate = name: value:
|
||||
let mkParam = k: v:
|
||||
if v == null then []
|
||||
else if isBool v then if v then [("--"+k)] else []
|
||||
else [("--"+k) v];
|
||||
mkParams = k: v: map (mkParam k) (if isList v then v else [v]);
|
||||
in escapeShellArgs (concatLists (concatLists (mapAttrsToList mkParams value)));
|
||||
};
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.freeciv = {
|
||||
enable = mkEnableOption ''freeciv'';
|
||||
settings = mkOption {
|
||||
description = ''
|
||||
Parameters of freeciv-server.
|
||||
'';
|
||||
default = {};
|
||||
type = types.submodule {
|
||||
freeformType = argsFormat.type;
|
||||
options.Announce = mkOption {
|
||||
type = types.enum ["IPv4" "IPv6" "none"];
|
||||
default = "none";
|
||||
description = "Announce game in LAN using given protocol.";
|
||||
};
|
||||
options.auth = mkEnableOption "server authentication";
|
||||
options.Database = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
apply = pkgs.writeText "auth.conf";
|
||||
default = ''
|
||||
[fcdb]
|
||||
backend="sqlite"
|
||||
database="/var/lib/freeciv/auth.sqlite"
|
||||
'';
|
||||
description = "Enable database connection with given configuration.";
|
||||
};
|
||||
options.debug = mkOption {
|
||||
type = types.ints.between 0 3;
|
||||
default = 0;
|
||||
description = "Set debug log level.";
|
||||
};
|
||||
options.exit-on-end = mkEnableOption "exit instead of restarting when a game ends.";
|
||||
options.Guests = mkEnableOption "guests to login if auth is enabled";
|
||||
options.Newusers = mkEnableOption "new users to login if auth is enabled";
|
||||
options.port = mkOption {
|
||||
type = types.port;
|
||||
default = 5556;
|
||||
description = "Listen for clients on given port";
|
||||
};
|
||||
options.quitidle = mkOption {
|
||||
type = types.nullOr types.int;
|
||||
default = null;
|
||||
description = "Quit if no players for given time in seconds.";
|
||||
};
|
||||
options.read = mkOption {
|
||||
type = types.lines;
|
||||
apply = v: pkgs.writeTextDir "read.serv" v + "/read";
|
||||
default = ''
|
||||
/fcdb lua sqlite_createdb()
|
||||
'';
|
||||
description = "Startup script.";
|
||||
};
|
||||
options.saves = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = "/var/lib/freeciv/saves/";
|
||||
description = ''
|
||||
Save games to given directory,
|
||||
a sub-directory named after the starting date of the service
|
||||
will me inserted to preserve older saves.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
openFirewall = mkEnableOption "opening the firewall for the port listening for clients";
|
||||
};
|
||||
};
|
||||
config = mkIf cfg.enable {
|
||||
users.groups.freeciv = {};
|
||||
# Use with:
|
||||
# journalctl -u freeciv.service -f -o cat &
|
||||
# cat >/run/freeciv.stdin
|
||||
# load saves/2020-11-14_05-22-27/freeciv-T0005-Y-3750-interrupted.sav.bz2
|
||||
systemd.sockets.freeciv = {
|
||||
wantedBy = [ "sockets.target" ];
|
||||
socketConfig = {
|
||||
ListenFIFO = "/run/freeciv.stdin";
|
||||
SocketGroup = groups.freeciv.name;
|
||||
SocketMode = "660";
|
||||
RemoveOnStop = true;
|
||||
};
|
||||
};
|
||||
systemd.services.freeciv = {
|
||||
description = "Freeciv Service";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment.HOME = "/var/lib/freeciv";
|
||||
serviceConfig = {
|
||||
Restart = "on-failure";
|
||||
RestartSec = "5s";
|
||||
StandardInput = "fd:freeciv.socket";
|
||||
StandardOutput = "journal";
|
||||
StandardError = "journal";
|
||||
ExecStart = pkgs.writeShellScript "freeciv-server" (''
|
||||
set -eux
|
||||
savedir=$(date +%Y-%m-%d_%H-%M-%S)
|
||||
'' + "${pkgs.freeciv}/bin/freeciv-server"
|
||||
+ " " + optionalString (cfg.settings.saves != null)
|
||||
(concatStringsSep " " [ "--saves" "${escapeShellArg cfg.settings.saves}/$savedir" ])
|
||||
+ " " + argsFormat.generate "freeciv-server" (cfg.settings // { saves = null; }));
|
||||
DynamicUser = true;
|
||||
# Create rootDir in the host's mount namespace.
|
||||
RuntimeDirectory = [(baseNameOf rootDir)];
|
||||
RuntimeDirectoryMode = "755";
|
||||
StateDirectory = [ "freeciv" ];
|
||||
WorkingDirectory = "/var/lib/freeciv";
|
||||
# Avoid mounting rootDir in the own rootDir of ExecStart='s mount namespace.
|
||||
InaccessiblePaths = ["-+${rootDir}"];
|
||||
# This is for BindPaths= and BindReadOnlyPaths=
|
||||
# to allow traversal of directories they create in RootDirectory=.
|
||||
UMask = "0066";
|
||||
RootDirectory = rootDir;
|
||||
RootDirectoryStartOnly = true;
|
||||
MountAPIVFS = true;
|
||||
BindReadOnlyPaths = [
|
||||
builtins.storeDir
|
||||
"/etc"
|
||||
"/run"
|
||||
];
|
||||
# The following options are only for optimizing:
|
||||
# systemd-analyze security freeciv
|
||||
AmbientCapabilities = "";
|
||||
CapabilityBoundingSet = "";
|
||||
# ProtectClock= adds DeviceAllow=char-rtc r
|
||||
DeviceAllow = "";
|
||||
LockPersonality = true;
|
||||
MemoryDenyWriteExecute = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
PrivateNetwork = mkDefault false;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
# Groups in @system-service which do not contain a syscall listed by:
|
||||
# perf stat -x, 2>perf.log -e 'syscalls:sys_enter_*' freeciv-server
|
||||
# in tests, and seem likely not necessary for freeciv-server.
|
||||
"~@aio" "~@chown" "~@ipc" "~@keyring" "~@memlock"
|
||||
"~@resources" "~@setuid" "~@sync" "~@timer"
|
||||
];
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallErrorNumber = "EPERM";
|
||||
};
|
||||
};
|
||||
networking.firewall = mkIf cfg.openFirewall
|
||||
{ allowedTCPPorts = [ cfg.settings.port ]; };
|
||||
};
|
||||
meta.maintainers = with lib.maintainers; [ julm ];
|
||||
}
|
Loading…
Reference in New Issue
Block a user