diff options
author | Frederik Rietdijk <fridh@fridh.nl> | 2020-06-13 11:02:21 +0200 |
---|---|---|
committer | Frederik Rietdijk <fridh@fridh.nl> | 2020-06-13 11:02:21 +0200 |
commit | 1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02 (patch) | |
tree | bae982223d71e1d3760d022a26f2e06b7202e255 /nixos | |
parent | 98f95ef9c4559c96cf852e5c0dae1d5124145a78 (diff) | |
parent | 0905a5d99031c213c20c962e9501a2202602741b (diff) | |
download | nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.tar nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.tar.gz nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.tar.bz2 nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.tar.lz nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.tar.xz nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.tar.zst nixpkgs-1523382160220d7cd1fc6a7ff91b6eeb8ce5dc02.zip |
Merge master into staging-next
Diffstat (limited to 'nixos')
-rw-r--r-- | nixos/lib/make-disk-image.nix | 3 | ||||
-rw-r--r-- | nixos/modules/module-list.nix | 1 | ||||
-rw-r--r-- | nixos/modules/programs/ssmtp.nix | 77 | ||||
-rw-r--r-- | nixos/modules/services/games/teeworlds.nix | 119 | ||||
-rw-r--r-- | nixos/modules/services/network-filesystems/ipfs.nix | 153 | ||||
-rw-r--r-- | nixos/modules/tasks/network-interfaces-scripted.nix | 24 | ||||
-rw-r--r-- | nixos/tests/ipfs.nix | 7 | ||||
-rw-r--r-- | nixos/tests/teeworlds.nix | 55 |
8 files changed, 304 insertions, 135 deletions
diff --git a/nixos/lib/make-disk-image.nix b/nixos/lib/make-disk-image.nix index 5e86ea479d5..9364aab3277 100644 --- a/nixos/lib/make-disk-image.nix +++ b/nixos/lib/make-disk-image.nix @@ -39,7 +39,7 @@ , name ? "nixos-disk-image" -, # Disk image format, one of qcow2, qcow2-compressed, vpc, raw. +, # Disk image format, one of qcow2, qcow2-compressed, vdi, vpc, raw. format ? "raw" }: @@ -57,6 +57,7 @@ let format' = format; in let filename = "nixos." + { qcow2 = "qcow2"; + vdi = "vdi"; vpc = "vhd"; raw = "img"; }.${format}; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 978d33e7585..5c5281b730f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -334,6 +334,7 @@ ./services/games/minecraft-server.nix ./services/games/minetest-server.nix ./services/games/openarena.nix + ./services/games/teeworlds.nix ./services/games/terraria.nix ./services/hardware/acpid.nix ./services/hardware/actkbd.nix diff --git a/nixos/modules/programs/ssmtp.nix b/nixos/modules/programs/ssmtp.nix index c7a94739349..15d2750c193 100644 --- a/nixos/modules/programs/ssmtp.nix +++ b/nixos/modules/programs/ssmtp.nix @@ -21,9 +21,11 @@ in (mkRenamedOptionModule [ "networking" "defaultMailServer" "useTLS" ] [ "services" "ssmtp" "useTLS" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "useSTARTTLS" ] [ "services" "ssmtp" "useSTARTTLS" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "authUser" ] [ "services" "ssmtp" "authUser" ]) - (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPass" ] [ "services" "ssmtp" "authPass" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "authPassFile" ] [ "services" "ssmtp" "authPassFile" ]) (mkRenamedOptionModule [ "networking" "defaultMailServer" "setSendmail" ] [ "services" "ssmtp" "setSendmail" ]) + + (mkRemovedOptionModule [ "networking" "defaultMailServer" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path") + (mkRemovedOptionModule [ "services" "ssmtp" "authPass" ] "authPass has been removed since it leaks the clear-text password into the world-readable store. Use authPassFile instead and make sure it's not a store path") ]; options = { @@ -45,6 +47,21 @@ in ''; }; + settings = mkOption { + type = with types; attrsOf (oneOf [ bool str ]); + default = {}; + description = '' + <citerefentry><refentrytitle>ssmtp</refentrytitle><manvolnum>5</manvolnum></citerefentry> configuration. Refer + to <link xlink:href="https://linux.die.net/man/5/ssmtp.conf"/> for details on supported values. + ''; + example = literalExample '' + { + Debug = true; + FromLineOverride = false; + } + ''; + }; + hostName = mkOption { type = types.str; example = "mail.example.org"; @@ -101,18 +118,6 @@ in ''; }; - authPass = mkOption { - type = types.str; - default = ""; - example = "correctHorseBatteryStaple"; - description = '' - Password used for SMTP auth. (STORED PLAIN TEXT, WORLD-READABLE IN NIX STORE) - - It's recommended to use <option>authPassFile</option> - which takes precedence over <option>authPass</option>. - ''; - }; - authPassFile = mkOption { type = types.nullOr types.str; default = null; @@ -121,11 +126,6 @@ in Path to a file that contains the password used for SMTP auth. The file should not contain a trailing newline, if the password does not contain one. This file should be readable by the users that need to execute ssmtp. - - <option>authPassFile</option> takes precedence over <option>authPass</option>. - - Warning: when <option>authPass</option> is non-empty <option>authPassFile</option> - defaults to a file in the WORLD-READABLE Nix store containing that password. ''; }; @@ -142,25 +142,28 @@ in config = mkIf cfg.enable { - services.ssmtp.authPassFile = mkIf (cfg.authPass != "") - (mkDefault (toString (pkgs.writeTextFile { - name = "ssmtp-authpass"; - text = cfg.authPass; - }))); - - environment.etc."ssmtp/ssmtp.conf".text = - let yesNo = yes : if yes then "YES" else "NO"; in - '' - MailHub=${cfg.hostName} - FromLineOverride=YES - ${optionalString (cfg.root != "") "root=${cfg.root}"} - ${optionalString (cfg.domain != "") "rewriteDomain=${cfg.domain}"} - UseTLS=${yesNo cfg.useTLS} - UseSTARTTLS=${yesNo cfg.useSTARTTLS} - #Debug=YES - ${optionalString (cfg.authUser != "") "AuthUser=${cfg.authUser}"} - ${optionalString (cfg.authPassFile != null) "AuthPassFile=${cfg.authPassFile}"} - ''; + services.ssmtp.settings = mkMerge [ + ({ + MailHub = cfg.hostName; + FromLineOverride = mkDefault true; + UseTLS = cfg.useTLS; + UseSTARTTLS = cfg.useSTARTTLS; + }) + (mkIf (cfg.root != "") { root = cfg.root; }) + (mkIf (cfg.domain != "") { rewriteDomain = cfg.domain; }) + (mkIf (cfg.authUser != "") { AuthUser = cfg.authUser; }) + (mkIf (cfg.authPassFile != null) { AuthPassFile = cfg.authPassFile; }) + ]; + + environment.etc."ssmtp/ssmtp.conf".source = + let + toStr = value: + if value == true then "YES" + else if value == false then "NO" + else builtins.toString value + ; + in + pkgs.writeText "ssmtp.conf" (concatStringsSep "\n" (mapAttrsToList (key: value: "${key}=${toStr value}") cfg.settings)); environment.systemPackages = [pkgs.ssmtp]; diff --git a/nixos/modules/services/games/teeworlds.nix b/nixos/modules/services/games/teeworlds.nix new file mode 100644 index 00000000000..babf989c98c --- /dev/null +++ b/nixos/modules/services/games/teeworlds.nix @@ -0,0 +1,119 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.teeworlds; + register = cfg.register; + + teeworldsConf = pkgs.writeText "teeworlds.cfg" '' + sv_port ${toString cfg.port} + sv_register ${if cfg.register then "1" else "0"} + ${optionalString (cfg.name != null) "sv_name ${cfg.name}"} + ${optionalString (cfg.motd != null) "sv_motd ${cfg.motd}"} + ${optionalString (cfg.password != null) "password ${cfg.password}"} + ${optionalString (cfg.rconPassword != null) "sv_rcon_password ${cfg.rconPassword}"} + ${concatStringsSep "\n" cfg.extraOptions} + ''; + +in +{ + options = { + services.teeworlds = { + enable = mkEnableOption "Teeworlds Server"; + + openPorts = mkOption { + type = types.bool; + default = false; + description = "Whether to open firewall ports for Teeworlds"; + }; + + name = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Name of the server. Defaults to 'unnamed server'. + ''; + }; + + register = mkOption { + type = types.bool; + example = true; + default = false; + description = '' + Whether the server registers as public server in the global server list. This is disabled by default because of privacy. + ''; + }; + + motd = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Set the server message of the day text. + ''; + }; + + password = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password to connect to the server. + ''; + }; + + rconPassword = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Password to access the remote console. If not set, a randomly generated one is displayed in the server log. + ''; + }; + + port = mkOption { + type = types.int; + default = 8303; + description = '' + Port the server will listen on. + ''; + }; + + extraOptions = mkOption { + type = types.listOf types.str; + default = []; + description = '' + Extra configuration lines for the <filename>teeworlds.cfg</filename>. See <link xlink:href="https://www.teeworlds.com/?page=docs&wiki=server_settings">Teeworlds Documentation</link>. + ''; + example = [ "sv_map dm1" "sv_gametype dm" ]; + }; + }; + }; + + config = mkIf cfg.enable { + networking.firewall = mkIf cfg.openPorts { + allowedUDPPorts = [ cfg.port ]; + }; + + systemd.services.teeworlds = { + description = "Teeworlds Server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + DynamicUser = true; + ExecStart = "${pkgs.teeworlds}/bin/teeworlds_srv -f ${teeworldsConf}"; + + # Hardening + CapabilityBoundingSet = false; + PrivateDevices = true; + PrivateUsers = true; + ProtectHome = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + RestrictNamespaces = true; + SystemCallArchitectures = "native"; + }; + }; + }; +} diff --git a/nixos/modules/services/network-filesystems/ipfs.nix b/nixos/modules/services/network-filesystems/ipfs.nix index 1f5c14d777d..a3bd40135d1 100644 --- a/nixos/modules/services/network-filesystems/ipfs.nix +++ b/nixos/modules/services/network-filesystems/ipfs.nix @@ -1,69 +1,17 @@ -{ config, lib, pkgs, ... }: +{ config, lib, pkgs, options, ... }: with lib; let - inherit (pkgs) ipfs runCommand makeWrapper; - cfg = config.services.ipfs; + opt = options.services.ipfs; ipfsFlags = toString ([ (optionalString cfg.autoMount "--mount") - #(optionalString cfg.autoMigrate "--migrate") (optionalString cfg.enableGC "--enable-gc") (optionalString (cfg.serviceFdlimit != null) "--manage-fdlimit=false") (optionalString (cfg.defaultMode == "offline") "--offline") (optionalString (cfg.defaultMode == "norouting") "--routing=none") ] ++ cfg.extraFlags); - defaultDataDir = if versionAtLeast config.system.stateVersion "17.09" then - "/var/lib/ipfs" else - "/var/lib/ipfs/.ipfs"; - - # Wrapping the ipfs binary with the environment variable IPFS_PATH set to dataDir because we can't set it in the user environment - wrapped = runCommand "ipfs" { buildInputs = [ makeWrapper ]; preferLocalBuild = true; } '' - mkdir -p "$out/bin" - makeWrapper "${ipfs}/bin/ipfs" "$out/bin/ipfs" \ - --set IPFS_PATH ${cfg.dataDir} \ - --prefix PATH : /run/wrappers/bin - ''; - - - commonEnv = { - environment.IPFS_PATH = cfg.dataDir; - path = [ wrapped ]; - serviceConfig.User = cfg.user; - serviceConfig.Group = cfg.group; - }; - - baseService = recursiveUpdate commonEnv { - wants = [ "ipfs-init.service" ]; - # NB: migration must be performed prior to pre-start, else we get the failure message! - preStart = optionalString cfg.autoMount '' - ipfs --local config Mounts.FuseAllowOther --json true - ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir} - ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir} - '' + concatStringsSep "\n" (collect - isString - (mapAttrsRecursive - (path: value: - # Using heredoc below so that the value is never improperly quoted - '' - read value <<EOF - ${builtins.toJSON value} - EOF - ipfs --local config --json "${concatStringsSep "." path}" "$value" - '') - ({ Addresses.API = cfg.apiAddress; - Addresses.Gateway = cfg.gatewayAddress; - Addresses.Swarm = cfg.swarmAddress; - } // - cfg.extraConfig)) - ); - serviceConfig = { - ExecStart = "${wrapped}/bin/ipfs daemon ${ipfsFlags}"; - Restart = "on-failure"; - RestartSec = 1; - } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; }; - }; in { ###### interface @@ -88,7 +36,9 @@ in { dataDir = mkOption { type = types.str; - default = defaultDataDir; + default = if versionAtLeast config.system.stateVersion "17.09" + then "/var/lib/ipfs" + else "/var/lib/ipfs/.ipfs"; description = "The data dir for IPFS"; }; @@ -98,18 +48,6 @@ in { description = "systemd service that is enabled by default"; }; - /* - autoMigrate = mkOption { - type = types.bool; - default = false; - description = '' - Whether IPFS should try to migrate the file system automatically. - - The daemon will need to be able to download a binary from https://ipfs.io to perform the migration. - ''; - }; - */ - autoMount = mkOption { type = types.bool; default = false; @@ -199,13 +137,21 @@ in { example = 64*1024; }; + startWhenNeeded = mkOption { + type = types.bool; + default = false; + description = "Whether to use socket activation to start IPFS when needed."; + }; + }; }; ###### implementation config = mkIf cfg.enable { - environment.systemPackages = [ wrapped ]; + environment.systemPackages = [ pkgs.ipfs ]; + environment.variables.IPFS_PATH = cfg.dataDir; + programs.fuse = mkIf cfg.autoMount { userAllowOther = true; }; @@ -234,10 +180,14 @@ in { "d '${cfg.ipnsMountDir}' - ${cfg.user} ${cfg.group} - -" ]; - systemd.services.ipfs-init = recursiveUpdate commonEnv { + systemd.packages = [ pkgs.ipfs ]; + + systemd.services.ipfs-init = { description = "IPFS Initializer"; - before = [ "ipfs.service" "ipfs-offline.service" "ipfs-norouting.service" ]; + environment.IPFS_PATH = cfg.dataDir; + + path = [ pkgs.ipfs ]; script = '' if [[ ! -f ${cfg.dataDir}/config ]]; then @@ -251,34 +201,63 @@ in { fi ''; + wantedBy = [ "default.target" ]; + serviceConfig = { Type = "oneshot"; RemainAfterExit = true; + User = cfg.user; + Group = cfg.group; }; }; - # TODO These 3 definitions possibly be further abstracted through use of a function - # like: mutexServices "ipfs" [ "", "offline", "norouting" ] { ... shared conf here ... } + systemd.services.ipfs = { + path = [ "/run/wrappers" pkgs.ipfs ]; + environment.IPFS_PATH = cfg.dataDir; + + wants = [ "ipfs-init.service" ]; + after = [ "ipfs-init.service" ]; - systemd.services.ipfs = recursiveUpdate baseService { - description = "IPFS Daemon"; - wantedBy = mkIf (cfg.defaultMode == "online") [ "multi-user.target" ]; - after = [ "network.target" "ipfs-init.service" ]; - conflicts = [ "ipfs-offline.service" "ipfs-norouting.service"]; + preStart = optionalString cfg.autoMount '' + ipfs --local config Mounts.FuseAllowOther --json true + ipfs --local config Mounts.IPFS ${cfg.ipfsMountDir} + ipfs --local config Mounts.IPNS ${cfg.ipnsMountDir} + '' + concatStringsSep "\n" (collect + isString + (mapAttrsRecursive + (path: value: + # Using heredoc below so that the value is never improperly quoted + '' + read value <<EOF + ${builtins.toJSON value} + EOF + ipfs --local config --json "${concatStringsSep "." path}" "$value" + '') + ({ Addresses.API = cfg.apiAddress; + Addresses.Gateway = cfg.gatewayAddress; + Addresses.Swarm = cfg.swarmAddress; + } // + cfg.extraConfig)) + ); + serviceConfig = { + ExecStart = ["" "${pkgs.ipfs}/bin/ipfs daemon ${ipfsFlags}"]; + User = cfg.user; + Group = cfg.group; + } // optionalAttrs (cfg.serviceFdlimit != null) { LimitNOFILE = cfg.serviceFdlimit; }; + } // optionalAttrs (!cfg.startWhenNeeded) { + wantedBy = [ "default.target" ]; }; - systemd.services.ipfs-offline = recursiveUpdate baseService { - description = "IPFS Daemon (offline mode)"; - wantedBy = mkIf (cfg.defaultMode == "offline") [ "multi-user.target" ]; - after = [ "ipfs-init.service" ]; - conflicts = [ "ipfs.service" "ipfs-norouting.service"]; + systemd.sockets.ipfs-gateway = { + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = [ "" ] + ++ lib.optional (cfg.gatewayAddress == opt.gatewayAddress.default) [ "127.0.0.1:8080" "[::1]:8080" ]; }; - systemd.services.ipfs-norouting = recursiveUpdate baseService { - description = "IPFS Daemon (no routing mode)"; - wantedBy = mkIf (cfg.defaultMode == "norouting") [ "multi-user.target" ]; - after = [ "ipfs-init.service" ]; - conflicts = [ "ipfs.service" "ipfs-offline.service"]; + systemd.sockets.ipfs-api = { + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = [ "" "%t/ipfs.sock" ] + ++ lib.optional (cfg.apiAddress == opt.apiAddress.default) [ "127.0.0.1:5001" "[::1]:5001" ]; }; }; diff --git a/nixos/modules/tasks/network-interfaces-scripted.nix b/nixos/modules/tasks/network-interfaces-scripted.nix index d895c58bab0..2e87197176b 100644 --- a/nixos/modules/tasks/network-interfaces-scripted.nix +++ b/nixos/modules/tasks/network-interfaces-scripted.nix @@ -232,18 +232,22 @@ let ''; preStop = '' state="/run/nixos/network/routes/${i.name}" - while read cidr; do - echo -n "deleting route $cidr... " - ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" - done < "$state" - rm -f "$state" + if [ -e "$state" ]; then + while read cidr; do + echo -n "deleting route $cidr... " + ip route del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" + done < "$state" + rm -f "$state" + fi state="/run/nixos/network/addresses/${i.name}" - while read cidr; do - echo -n "deleting address $cidr... " - ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" - done < "$state" - rm -f "$state" + if [ -e "$state" ]; then + while read cidr; do + echo -n "deleting address $cidr... " + ip addr del "$cidr" dev "${i.name}" >/dev/null 2>&1 && echo "done" || echo "failed" + done < "$state" + rm -f "$state" + fi ''; }; diff --git a/nixos/tests/ipfs.nix b/nixos/tests/ipfs.nix index 4d721aec0c7..82234f96922 100644 --- a/nixos/tests/ipfs.nix +++ b/nixos/tests/ipfs.nix @@ -21,5 +21,12 @@ import ./make-test-python.nix ({ pkgs, ...} : { ) machine.succeed(f"ipfs cat /ipfs/{ipfs_hash.strip()} | grep fnord") + + ipfs_hash = machine.succeed( + "echo fnord2 | ipfs --api /unix/run/ipfs.sock add | awk '{ print $2 }'" + ) + machine.succeed( + f"ipfs --api /unix/run/ipfs.sock cat /ipfs/{ipfs_hash.strip()} | grep fnord2" + ) ''; }) diff --git a/nixos/tests/teeworlds.nix b/nixos/tests/teeworlds.nix new file mode 100644 index 00000000000..edf58896878 --- /dev/null +++ b/nixos/tests/teeworlds.nix @@ -0,0 +1,55 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +let + client = + { pkgs, ... }: + + { imports = [ ./common/x11.nix ]; + environment.systemPackages = [ pkgs.teeworlds ]; + }; + +in { + name = "teeworlds"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ hax404 ]; + }; + + nodes = + { server = + { services.teeworlds = { + enable = true; + openPorts = true; + }; + }; + + client1 = client; + client2 = client; + }; + + testScript = + '' + start_all() + + server.wait_for_unit("teeworlds.service") + server.wait_until_succeeds("ss --numeric --udp --listening | grep -q 8303") + + client1.wait_for_x() + client2.wait_for_x() + + client1.execute("teeworlds 'player_name Alice;connect server'&") + server.wait_until_succeeds( + 'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Alice"' + ) + + client2.execute("teeworlds 'player_name Bob;connect server'&") + server.wait_until_succeeds( + 'journalctl -u teeworlds -e | grep --extended-regexp -q "team_join player=\'[0-9]:Bob"' + ) + + server.sleep(10) # wait for a while to get a nice screenshot + + client1.screenshot("screen_client1") + client2.screenshot("screen_client2") + ''; + +}) |