diff --git a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml index 54f0b0bf0fc..c9fae970ce5 100644 --- a/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml +++ b/nixos/doc/manual/from_md/release-notes/rl-2205.section.xml @@ -104,6 +104,14 @@ services.tetrd. + + + ArchiSteamFarm, + a C# application with primary purpose of idling Steam cards + from multiple accounts simultaneously. Available as + services.archisteamfarm. + +
diff --git a/nixos/doc/manual/release-notes/rl-2205.section.md b/nixos/doc/manual/release-notes/rl-2205.section.md index 81bac061572..5c18780ce36 100644 --- a/nixos/doc/manual/release-notes/rl-2205.section.md +++ b/nixos/doc/manual/release-notes/rl-2205.section.md @@ -33,6 +33,8 @@ In addition to numerous new and upgraded packages, this release has the followin - [tetrd](https://tetrd.app), share your internet connection from your device to your PC and vice versa through a USB cable. Available at [services.tetrd](#opt-services.tetrd.enable). +- [ArchiSteamFarm](https://github.com/JustArchiNET/ArchiSteamFarm), a C# application with primary purpose of idling Steam cards from multiple accounts simultaneously. Available as [services.archisteamfarm](options.html#opt-services.archisteamfarm.enable). + ## Backward Incompatibilities {#sec-release-22.05-incompatibilities} - `pkgs.ghc` now refers to `pkgs.targetPackages.haskellPackages.ghc`. diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 73a61c4ca0e..9e78f498646 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -396,6 +396,7 @@ ./services/editors/emacs.nix ./services/editors/infinoted.nix ./services/finance/odoo.nix + ./services/games/asf.nix ./services/games/crossfire-server.nix ./services/games/deliantra-server.nix ./services/games/factorio.nix diff --git a/nixos/modules/services/games/asf.nix b/nixos/modules/services/games/asf.nix new file mode 100644 index 00000000000..f204ac5a02a --- /dev/null +++ b/nixos/modules/services/games/asf.nix @@ -0,0 +1,206 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.archisteamfarm; + + format = pkgs.formats.json { }; + + asf-config = format.generate "ASF.json" (cfg.settings // { + # we disable it because ASF cannot update itself anyways + # and nixos takes care of restarting the service + # is in theory not needed as this is already the default for default builds + UpdateChannel = 0; + }); + + mkBot = n: c: + format.generate "${n}.json" (c.settings // { + SteamLogin = if c.username == "" then n else c.username; + SteamPassword = c.passwordFile; + # sets the password format to file (https://github.com/JustArchiNET/ArchiSteamFarm/wiki/Security#file) + PasswordFormat = 4; + Enabled = c.enabled; + }); +in +{ + options.services.archisteamfarm = { + enable = mkOption { + type = types.bool; + description = '' + If enabled, starts the ArchisSteamFarm service. + For configuring the SteamGuard token you will need to use the web-ui, which is enabled by default over on 127.0.0.1:1242. + You cannot configure ASF in any way outside of nix, since all the config files get wiped on restart and replaced with the programatically set ones by nix. + ''; + default = false; + }; + + web-ui = mkOption { + type = types.submodule { + options = { + enable = mkEnableOption + "Wheter to start the web-ui. This is the preffered way of configuring things such as the steam guard token"; + + package = mkOption { + type = types.package; + default = pkgs.ASF-ui; + description = + "Web ui package to use. Contents must be in lib/dist."; + }; + }; + }; + default = { + enable = true; + package = pkgs.ASF-ui; + }; + example = { + enable = false; + }; + description = "The Web-UI hosted on 127.0.0.1:1242."; + }; + + package = mkOption { + type = types.package; + default = pkgs.ArchiSteamFarm; + description = + "Package to use. Should always be the latest version, for security reasons, since this module uses very new features and to not get out of sync with the Steam API."; + }; + + dataDir = mkOption { + type = types.path; + default = "/var/lib/asf"; + description = '' + The ASF home directory used to store all data. + If left as the default value this directory will automatically be created before the ASF server starts, otherwise the sysadmin is responsible for ensuring the directory exists with appropriate ownership and permissions.''; + }; + + settings = mkOption { + type = let format = pkgs.formats.json { }; in format.type; + description = '' + The ASF.json file, all the options are documented here. + Do note that `AutoRestart` and `UpdateChannel` is always to `false` +respectively `0` because NixOS takes care of updating everything. + `Headless` is also always set to `true` because there is no way to provide inputs via a systemd service. + You should try to keep ASF up to date since upstream does not provide support for anything but the latest version and you're exposing yourself to all kinds of issues - as is outlined here. + ''; + example = { + Statistics = false; + }; + default = { }; + }; + bots = mkOption { + type = types.attrsOf (types.submodule { + options = { + username = mkOption { + type = types.str; + description = + "Name of the user to log in. Default is attribute name."; + default = ""; + }; + passwordFile = mkOption { + type = types.path; + description = + "Path to a file containig the password. The file must be readable by the asf user/group."; + }; + enabled = mkOption { + type = types.bool; + default = true; + description = "Whether to enable the bot on startup."; + }; + settings = mkOption { + type = types.attrs; + description = + "Additional settings that are documented here."; + default = { }; + }; + }; + }); + description = '' + Bots name and configuration. + ''; + example = { + exampleBot = { + username = "alice"; + passwordFile = "/var/lib/asf/secrets/password"; + settings = { SteamParentalCode = "1234"; }; + }; + }; + default = { }; + }; + }; + + config = mkIf cfg.enable { + + users = { + users.asf = { + home = cfg.dataDir; + isSystemUser = true; + group = "asf"; + description = "Archis-Steam-Farm service user"; + }; + groups.asf = { }; + }; + + systemd.services = { + asf = { + description = "Archis-Steam-Farm Service"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = lib.mkMerge [ + (lib.mkIf (cfg.dataDir == "/var/lib/asf") { StateDirectory = "asf"; }) + { + User = "asf"; + Group = "asf"; + WorkingDirectory = cfg.dataDir; + Type = "simple"; + ExecStart = + "${cfg.package}/bin/ArchiSteamFarm --path ${cfg.dataDir} --process-required --no-restart --service --no-config-migrate"; + + # mostly copied from the default systemd service + PrivateTmp = true; + LockPersonality = true; + PrivateDevices = true; + PrivateIPC = true; + PrivateMounts = true; + PrivateUsers = true; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "full"; + RemoveIPC = true; + RestrictAddressFamilies = "AF_INET AF_INET6"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + } + ]; + + preStart = '' + set -x + # we remove config to have no complexity when creating the required files/directories + mkdir -p config + + rm -rf config/*.json + rm -rf www + ln -s ${asf-config} config/ASF.json + echo -e '${ + lib.strings.concatStringsSep "\\n" + (attrsets.mapAttrsToList mkBot cfg.bots) + }' \ + | while read -r line; do + ln -s $line config/$(basename $line) + done + + ${lib.strings.optionalString cfg.web-ui.enable '' + ln -s ${cfg.web-ui.package}/lib/dist www + ''} + ''; + }; + }; + }; +}