diff options
Diffstat (limited to 'nixos')
28 files changed, 594 insertions, 167 deletions
diff --git a/nixos/doc/manual/man-nixos-install.xml b/nixos/doc/manual/man-nixos-install.xml index 0752c397182..9255ce763ef 100644 --- a/nixos/doc/manual/man-nixos-install.xml +++ b/nixos/doc/manual/man-nixos-install.xml @@ -210,7 +210,7 @@ The closure must be an appropriately configured NixOS system, with boot loader and partition configuration that fits the target host. Such a closure is typically obtained with a command such as <command>nix-build - -I nixos-config=./configuration.nix '<nixos>' -A system + -I nixos-config=./configuration.nix '<nixpkgs/nixos>' -A system --no-out-link</command> </para> </listitem> diff --git a/nixos/modules/installer/tools/nixos-rebuild.sh b/nixos/modules/installer/tools/nixos-rebuild.sh index c53dc1000c4..61b4af11027 100644 --- a/nixos/modules/installer/tools/nixos-rebuild.sh +++ b/nixos/modules/installer/tools/nixos-rebuild.sh @@ -22,7 +22,7 @@ repair= profile=/nix/var/nix/profiles/system buildHost= targetHost= -maybeSudo= +maybeSudo=() while [ "$#" -gt 0 ]; do i="$1"; shift 1 @@ -92,7 +92,7 @@ while [ "$#" -gt 0 ]; do ;; --use-remote-sudo) # note the trailing space - maybeSudo="sudo " + maybeSudo=(sudo --) shift 1 ;; *) @@ -102,6 +102,10 @@ while [ "$#" -gt 0 ]; do esac done +if [ -n "$SUDO_USER" ]; then + maybeSudo=(sudo --) +fi + if [ -z "$buildHost" -a -n "$targetHost" ]; then buildHost="$targetHost" fi @@ -116,17 +120,17 @@ buildHostCmd() { if [ -z "$buildHost" ]; then "$@" elif [ -n "$remoteNix" ]; then - ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "$maybeSudo$@" + ssh $SSHOPTS "$buildHost" env PATH="$remoteNix:$PATH" "${maybeSudo[@]}" "$@" else - ssh $SSHOPTS "$buildHost" "$maybeSudo$@" + ssh $SSHOPTS "$buildHost" "${maybeSudo[@]}" "$@" fi } targetHostCmd() { if [ -z "$targetHost" ]; then - "$@" + "${maybeSudo[@]}" "$@" else - ssh $SSHOPTS "$targetHost" "$maybeSudo$@" + ssh $SSHOPTS "$targetHost" "${maybeSudo[@]}" "$@" fi } diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index ddbd3963cc5..8a85035ceb7 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -6,6 +6,7 @@ let cfg = config.system.nixos; gitRepo = "${toString pkgs.path}/.git"; + gitRepoValid = lib.pathIsGitRepo gitRepo; gitCommitId = lib.substring 0 7 (commitIdFromGitRepo gitRepo); in @@ -91,8 +92,8 @@ in # These defaults are set here rather than up there so that # changing them would not rebuild the manual version = mkDefault (cfg.release + cfg.versionSuffix); - revision = mkIf (pathExists gitRepo) (mkDefault gitCommitId); - versionSuffix = mkIf (pathExists gitRepo) (mkDefault (".git." + gitCommitId)); + revision = mkIf gitRepoValid (mkDefault gitCommitId); + versionSuffix = mkIf gitRepoValid (mkDefault (".git." + gitCommitId)); }; # Generate /etc/os-release. See diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index aaff8dcb330..176b62f6cb5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -153,6 +153,7 @@ ./programs/system-config-printer.nix ./programs/thefuck.nix ./programs/tmux.nix + ./programs/traceroute.nix ./programs/tsm-client.nix ./programs/udevil.nix ./programs/usbtop.nix @@ -804,6 +805,7 @@ ./services/web-apps/codimd.nix ./services/web-apps/cryptpad.nix ./services/web-apps/documize.nix + ./services/web-apps/dokuwiki.nix ./services/web-apps/frab.nix ./services/web-apps/gotify-server.nix ./services/web-apps/icingaweb2/icingaweb2.nix @@ -871,7 +873,6 @@ ./services/x11/display-managers/xpra.nix ./services/x11/fractalart.nix ./services/x11/hardware/libinput.nix - ./services/x11/hardware/multitouch.nix ./services/x11/hardware/synaptics.nix ./services/x11/hardware/wacom.nix ./services/x11/hardware/digimend.nix diff --git a/nixos/modules/programs/gnupg.nix b/nixos/modules/programs/gnupg.nix index 2d262d90657..7a3cb588ee7 100644 --- a/nixos/modules/programs/gnupg.nix +++ b/nixos/modules/programs/gnupg.nix @@ -96,7 +96,7 @@ in # This overrides the systemd user unit shipped with the gnupg package systemd.user.services.gpg-agent = mkIf (cfg.agent.pinentryFlavor != null) { serviceConfig.ExecStart = [ "" '' - ${pkgs.gnupg}/bin/gpg-agent --supervised \ + ${cfg.package}/bin/gpg-agent --supervised \ --pinentry-program ${pkgs.pinentry.${cfg.agent.pinentryFlavor}}/bin/pinentry '' ]; }; diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix index 33e252be45f..7e646f8737d 100644 --- a/nixos/modules/programs/sway.nix +++ b/nixos/modules/programs/sway.nix @@ -87,7 +87,8 @@ in { type = with types; listOf package; default = with pkgs; [ swaylock swayidle - xwayland rxvt_unicode dmenu + xwayland alacritty dmenu + rxvt_unicode # For backward compatibility (old default terminal) ]; defaultText = literalExample '' with pkgs; [ swaylock swayidle xwayland rxvt_unicode dmenu ]; diff --git a/nixos/modules/programs/traceroute.nix b/nixos/modules/programs/traceroute.nix new file mode 100644 index 00000000000..4eb0be3f0e0 --- /dev/null +++ b/nixos/modules/programs/traceroute.nix @@ -0,0 +1,26 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.programs.traceroute; +in { + options = { + programs.traceroute = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to configure a setcap wrapper for traceroute. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + security.wrappers.traceroute = { + source = "${pkgs.traceroute}/bin/traceroute"; + capabilities = "cap_net_raw+p"; + }; + }; +} diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix index 94b5530192e..26de8a18d92 100644 --- a/nixos/modules/rename.nix +++ b/nixos/modules/rename.nix @@ -29,6 +29,11 @@ with lib; (mkRemovedOptionModule [ "services.fourStoreEndpoint" ] "The fourStoreEndpoint module has been removed") (mkRemovedOptionModule [ "programs" "way-cooler" ] ("way-cooler is abandoned by its author: " + "https://way-cooler.org/blog/2020/01/09/way-cooler-post-mortem.html")) + (mkRemovedOptionModule [ "services" "xserver" "multitouch" ] '' + services.xserver.multitouch (which uses xf86_input_mtrack) has been removed + as the underlying package isn't being maintained. Working alternatives are + libinput and synaptics. + '') # Do NOT add any option renames here, see top of the file ]; diff --git a/nixos/modules/services/continuous-integration/buildkite-agent.nix b/nixos/modules/services/continuous-integration/buildkite-agent.nix index 3c9c92bf052..58bce654941 100644 --- a/nixos/modules/services/continuous-integration/buildkite-agent.nix +++ b/nixos/modules/services/continuous-integration/buildkite-agent.nix @@ -50,8 +50,8 @@ in }; runtimePackages = mkOption { - default = [ pkgs.bash pkgs.nix ]; - defaultText = "[ pkgs.bash pkgs.nix ]"; + default = [ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]; + defaultText = "[ pkgs.bash pkgs.gnutar pkgs.gzip pkgs.git pkgs.nix ]"; description = "Add programs to the buildkite-agent environment"; type = types.listOf types.package; }; @@ -93,7 +93,8 @@ in }; privateSshKeyPath = mkOption { - type = types.path; + type = types.nullOr types.path; + default = null; ## maximum care is taken so that secrets (ssh keys and the CI token) ## don't end up in the Nix store. apply = final: if final == null then null else toString final; @@ -185,14 +186,14 @@ in }; config = mkIf config.services.buildkite-agent.enable { - users.users.buildkite-agent = - { name = "buildkite-agent"; - home = cfg.dataDir; - createHome = true; - description = "Buildkite agent user"; - extraGroups = [ "keys" ]; - isSystemUser = true; - }; + users.users.buildkite-agent = { + name = "buildkite-agent"; + home = cfg.dataDir; + createHome = true; + description = "Buildkite agent user"; + extraGroups = [ "keys" ]; + isSystemUser = true; + }; environment.systemPackages = [ cfg.package ]; @@ -212,11 +213,11 @@ in sshDir = "${cfg.dataDir}/.ssh"; tagStr = lib.concatStringsSep "," (lib.mapAttrsToList (name: value: "${name}=${value}") cfg.tags); in - '' + optionalString (cfg.privateSshKeyPath != null) '' mkdir -m 0700 -p "${sshDir}" - cp -f "${toString cfg.openssh.privateKeyPath}" "${sshDir}/id_rsa" - chmod 600 "${sshDir}"/id_rsa* - + cp -f "${toString cfg.privateSshKeyPath}" "${sshDir}/id_rsa" + chmod 600 "${sshDir}"/id_rsa + '' + '' cat > "${cfg.dataDir}/buildkite-agent.cfg" <<EOF token="$(cat ${toString cfg.tokenPath})" name="${cfg.name}" diff --git a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix index cca98c43dc7..8fa108c4f9d 100644 --- a/nixos/modules/services/desktops/gnome3/at-spi2-core.nix +++ b/nixos/modules/services/desktops/gnome3/at-spi2-core.nix @@ -18,6 +18,9 @@ with lib; description = '' Whether to enable at-spi2-core, a service for the Assistive Technologies available on the GNOME platform. + + Enable this if you get the error or warning + <literal>The name org.a11y.Bus was not provided by any .service files</literal>. ''; }; diff --git a/nixos/modules/services/mail/roundcube.nix b/nixos/modules/services/mail/roundcube.nix index 36dda619ad0..0bb0eaedad5 100644 --- a/nixos/modules/services/mail/roundcube.nix +++ b/nixos/modules/services/mail/roundcube.nix @@ -5,6 +5,8 @@ with lib; let cfg = config.services.roundcube; fpm = config.services.phpfpm.pools.roundcube; + localDB = cfg.database.host == "localhost"; + user = cfg.database.username; in { options.services.roundcube = { @@ -44,7 +46,10 @@ in username = mkOption { type = types.str; default = "roundcube"; - description = "Username for the postgresql connection"; + description = '' + Username for the postgresql connection. + If <literal>database.host</literal> is set to <literal>localhost</literal>, a unix user and group of the same name will be created as well. + ''; }; host = mkOption { type = types.str; @@ -58,7 +63,12 @@ in }; password = mkOption { type = types.str; - description = "Password for the postgresql connection"; + description = "Password for the postgresql connection. Do not use: the password will be stored world readable in the store; use <literal>passwordFile</literal> instead."; + default = ""; + }; + passwordFile = mkOption { + type = types.str; + description = "Password file for the postgresql connection. Must be readable by user <literal>nginx</literal>. Ignored if <literal>database.host</literal> is set to <literal>localhost</literal>, as peer authentication will be used."; }; dbname = mkOption { type = types.str; @@ -83,14 +93,22 @@ in }; config = mkIf cfg.enable { + # backward compatibility: if password is set but not passwordFile, make one. + services.roundcube.database.passwordFile = mkIf (!localDB && cfg.database.password != "") (mkDefault ("${pkgs.writeText "roundcube-password" cfg.database.password}")); + warnings = lib.optional (!localDB && cfg.database.password != "") "services.roundcube.database.password is deprecated and insecure; use services.roundcube.database.passwordFile instead"; + environment.etc."roundcube/config.inc.php".text = '' <?php + ${lib.optionalString (!localDB) "$password = file_get_contents('${cfg.database.passwordFile}');"} + $config = array(); - $config['db_dsnw'] = 'pgsql://${cfg.database.username}:${cfg.database.password}@${cfg.database.host}/${cfg.database.dbname}'; + $config['db_dsnw'] = 'pgsql://${cfg.database.username}${lib.optionalString (!localDB) ":' . $password . '"}@${if localDB then "unix(/run/postgresql)" else cfg.database.host}/${cfg.database.dbname}'; $config['log_driver'] = 'syslog'; $config['max_message_size'] = '25M'; $config['plugins'] = [${concatMapStringsSep "," (p: "'${p}'") cfg.plugins}]; + $config['des_key'] = file_get_contents('/var/lib/roundcube/des_key'); + $config['mime_types'] = '${pkgs.nginx}/conf/mime.types'; ${cfg.extraConfig} ''; @@ -116,12 +134,26 @@ in }; }; - services.postgresql = mkIf (cfg.database.host == "localhost") { + services.postgresql = mkIf localDB { enable = true; + ensureDatabases = [ cfg.database.dbname ]; + ensureUsers = [ { + name = cfg.database.username; + ensurePermissions = { + "DATABASE ${cfg.database.username}" = "ALL PRIVILEGES"; + }; + } ]; + }; + + users.users.${user} = mkIf localDB { + group = user; + isSystemUser = true; + createHome = false; }; + users.groups.${user} = mkIf localDB {}; services.phpfpm.pools.roundcube = { - user = "nginx"; + user = if localDB then user else "nginx"; phpOptions = '' error_log = 'stderr' log_errors = on @@ -143,9 +175,7 @@ in }; systemd.services.phpfpm-roundcube.after = [ "roundcube-setup.service" ]; - systemd.services.roundcube-setup = let - pgSuperUser = config.services.postgresql.superUser; - in mkMerge [ + systemd.services.roundcube-setup = mkMerge [ (mkIf (cfg.database.host == "localhost") { requires = [ "postgresql.service" ]; after = [ "postgresql.service" ]; @@ -153,22 +183,31 @@ in }) { wantedBy = [ "multi-user.target" ]; - script = '' - mkdir -p /var/lib/roundcube - if [ ! -f /var/lib/roundcube/db-created ]; then - if [ "${cfg.database.host}" = "localhost" ]; then - ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "create role ${cfg.database.username} with login password '${cfg.database.password}'"; - ${pkgs.sudo}/bin/sudo -u ${pgSuperUser} psql postgres -c "create database ${cfg.database.dbname} with owner ${cfg.database.username}"; - fi - PGPASSWORD="${cfg.database.password}" ${pkgs.postgresql}/bin/psql -U ${cfg.database.username} \ - -f ${cfg.package}/SQL/postgres.initial.sql \ - -h ${cfg.database.host} ${cfg.database.dbname} - touch /var/lib/roundcube/db-created + script = let + psql = "${lib.optionalString (!localDB) "PGPASSFILE=${cfg.database.passwordFile}"} ${pkgs.postgresql}/bin/psql ${lib.optionalString (!localDB) "-h ${cfg.database.host} -U ${cfg.database.username} "} ${cfg.database.dbname}"; + in + '' + version="$(${psql} -t <<< "select value from system where name = 'roundcube-version';" || true)" + if ! (grep -E '[a-zA-Z0-9]' <<< "$version"); then + ${psql} -f ${cfg.package}/SQL/postgres.initial.sql + fi + + if [ ! -f /var/lib/roundcube/des_key ]; then + base64 /dev/urandom | head -c 24 > /var/lib/roundcube/des_key; + # we need to log out everyone in case change the des_key + # from the default when upgrading from nixos 19.09 + ${psql} <<< 'TRUNCATE TABLE session;' fi ${pkgs.php}/bin/php ${cfg.package}/bin/update.sh ''; - serviceConfig.Type = "oneshot"; + serviceConfig = { + Type = "oneshot"; + StateDirectory = "roundcube"; + User = if localDB then user else "nginx"; + # so that the des_key is not world readable + StateDirectoryMode = "0700"; + }; } ]; }; diff --git a/nixos/modules/services/monitoring/prometheus/alertmanager.nix b/nixos/modules/services/monitoring/prometheus/alertmanager.nix index 9af6b1d94f3..2e8433fbc88 100644 --- a/nixos/modules/services/monitoring/prometheus/alertmanager.nix +++ b/nixos/modules/services/monitoring/prometheus/alertmanager.nix @@ -18,7 +18,7 @@ let in checkedConfig yml; cmdlineArgs = cfg.extraFlags ++ [ - "--config.file ${alertmanagerYml}" + "--config.file /tmp/alert-manager-substituted.yaml" "--web.listen-address ${cfg.listenAddress}:${toString cfg.port}" "--log.level ${cfg.logLevel}" ] ++ (optional (cfg.webExternalUrl != null) @@ -127,6 +127,18 @@ in { Extra commandline options when launching the Alertmanager. ''; }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/root/alertmanager.env"; + description = '' + File to load as environment file. Environment variables + from this file will be interpolated into the config file + using envsubst with this syntax: + <literal>$ENVIRONMENT ''${VARIABLE}</literal> + ''; + }; }; }; @@ -144,9 +156,14 @@ in { systemd.services.alertmanager = { wantedBy = [ "multi-user.target" ]; after = [ "network.target" ]; + preStart = '' + ${lib.getBin pkgs.envsubst}/bin/envsubst -o /tmp/alert-manager-substituted.yaml" \ + -i ${alertmanagerYml}" + ''; serviceConfig = { Restart = "always"; - DynamicUser = true; + DynamicUser = true; # implies PrivateTmp + EnvironmentFile = lib.mkIf (cfg.environmentFile != null) cfg.environmentFile; WorkingDirectory = "/tmp"; ExecStart = "${cfg.package}/bin/alertmanager" + optionalString (length cmdlineArgs != 0) (" \\\n " + diff --git a/nixos/modules/services/networking/knot.nix b/nixos/modules/services/networking/knot.nix index 1cc1dd3f2f6..47364ecb846 100644 --- a/nixos/modules/services/networking/knot.nix +++ b/nixos/modules/services/networking/knot.nix @@ -56,6 +56,7 @@ in { package = mkOption { type = types.package; default = pkgs.knot-dns; + defaultText = "pkgs.knot-dns"; description = '' Which Knot DNS package to use ''; @@ -92,4 +93,3 @@ in { environment.systemPackages = [ knot-cli-wrappers ]; }; } - diff --git a/nixos/modules/services/networking/kresd.nix b/nixos/modules/services/networking/kresd.nix index 5eb50a13ca9..bb941e93e15 100644 --- a/nixos/modules/services/networking/kresd.nix +++ b/nixos/modules/services/networking/kresd.nix @@ -5,12 +5,15 @@ with lib; let cfg = config.services.kresd; - package = pkgs.knot-resolver; + configFile = pkgs.writeText "kresd.conf" '' + ${optionalString (cfg.listenDoH != []) "modules.load('http')"} + ${cfg.extraConfig}; + ''; - configFile = pkgs.writeText "kresd.conf" cfg.extraConfig; -in - -{ + package = pkgs.knot-resolver.override { + extraFeatures = cfg.listenDoH != []; + }; +in { meta.maintainers = [ maintainers.vcunat /* upstream developer */ ]; imports = [ @@ -67,6 +70,15 @@ in For detailed syntax see ListenStream in man systemd.socket. ''; }; + listenDoH = mkOption { + type = with types; listOf str; + default = []; + example = [ "198.51.100.1:443" "[2001:db8::1]:443" "443" ]; + description = '' + Addresses and ports on which kresd should provide DNS over HTTPS (see RFC 7858). + For detailed syntax see ListenStream in man systemd.socket. + ''; + }; # TODO: perhaps options for more common stuff like cache size or forwarding }; @@ -104,6 +116,18 @@ in }; }; + systemd.sockets.kresd-doh = mkIf (cfg.listenDoH != []) rec { + wantedBy = [ "sockets.target" ]; + before = wantedBy; + partOf = [ "kresd.socket" ]; + listenStreams = cfg.listenDoH; + socketConfig = { + FileDescriptorName = "doh"; + FreeBind = true; + Service = "kresd.service"; + }; + }; + systemd.sockets.kresd-control = rec { wantedBy = [ "sockets.target" ]; before = wantedBy; diff --git a/nixos/modules/services/networking/matterbridge.nix b/nixos/modules/services/networking/matterbridge.nix index bad35133459..b8b4f37c84a 100644 --- a/nixos/modules/services/networking/matterbridge.nix +++ b/nixos/modules/services/networking/matterbridge.nix @@ -111,7 +111,7 @@ in serviceConfig = { User = cfg.user; Group = cfg.group; - ExecStart = "${pkgs.matterbridge.bin}/bin/matterbridge -conf ${matterbridgeConfToml}"; + ExecStart = "${pkgs.matterbridge}/bin/matterbridge -conf ${matterbridgeConfToml}"; Restart = "always"; RestartSec = "10"; }; diff --git a/nixos/modules/services/networking/syncthing.nix b/nixos/modules/services/networking/syncthing.nix index 47b10e408c0..5b3eb6f04b4 100644 --- a/nixos/modules/services/networking/syncthing.nix +++ b/nixos/modules/services/networking/syncthing.nix @@ -484,6 +484,24 @@ in { -gui-address=${cfg.guiAddress} \ -home=${cfg.configDir} ''; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateTmp = true; + PrivateUsers = true; + ProtectControlGroups = true; + ProtectHostname = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + CapabilityBoundingSet = [ + "~CAP_SYS_PTRACE" "~CAP_SYS_ADMIN" + "~CAP_SETGID" "~CAP_SETUID" "~CAP_SETPCAP" + "~CAP_SYS_TIME" "~CAP_KILL" + ]; }; }; syncthing-init = mkIf ( diff --git a/nixos/modules/services/networking/zerotierone.nix b/nixos/modules/services/networking/zerotierone.nix index 764af3846fe..069e15a909b 100644 --- a/nixos/modules/services/networking/zerotierone.nix +++ b/nixos/modules/services/networking/zerotierone.nix @@ -38,10 +38,13 @@ in config = mkIf cfg.enable { systemd.services.zerotierone = { description = "ZeroTierOne"; - path = [ cfg.package ]; - bindsTo = [ "network-online.target" ]; - after = [ "network-online.target" ]; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + wants = [ "network-online.target" ]; + + path = [ cfg.package ]; + preStart = '' mkdir -p /var/lib/zerotier-one/networks.d chmod 700 /var/lib/zerotier-one @@ -53,6 +56,7 @@ in ExecStart = "${cfg.package}/bin/zerotier-one -p${toString cfg.port}"; Restart = "always"; KillMode = "process"; + TimeoutStopSec = 5; }; }; diff --git a/nixos/modules/services/security/vault.nix b/nixos/modules/services/security/vault.nix index b0ab8fadcbe..6a8a3a93327 100644 --- a/nixos/modules/services/security/vault.nix +++ b/nixos/modules/services/security/vault.nix @@ -135,6 +135,7 @@ in User = "vault"; Group = "vault"; ExecStart = "${cfg.package}/bin/vault server -config ${configFile}"; + ExecReload = "${pkgs.coreutils}/bin/kill -SIGHUP $MAINPID"; PrivateDevices = true; PrivateTmp = true; ProtectSystem = "full"; diff --git a/nixos/modules/services/web-apps/dokuwiki.nix b/nixos/modules/services/web-apps/dokuwiki.nix new file mode 100644 index 00000000000..07af7aa0dfe --- /dev/null +++ b/nixos/modules/services/web-apps/dokuwiki.nix @@ -0,0 +1,272 @@ +{ config, lib, pkgs, ... }: + +let + + inherit (lib) mkEnableOption mkForce mkIf mkMerge mkOption optionalAttrs recursiveUpdate types; + + cfg = config.services.dokuwiki; + + user = config.services.nginx.user; + group = config.services.nginx.group; + + dokuwikiAclAuthConfig = pkgs.writeText "acl.auth.php" '' + # acl.auth.php + # <?php exit()?> + # + # Access Control Lists + # + ${toString cfg.acl} + ''; + + dokuwikiLocalConfig = pkgs.writeText "local.php" '' + <?php + $conf['savedir'] = '${cfg.stateDir}'; + $conf['superuser'] = '${toString cfg.superUser}'; + $conf['useacl'] = '${toString cfg.aclUse}'; + ${toString cfg.extraConfig} + ''; + + dokuwikiPluginsLocalConfig = pkgs.writeText "plugins.local.php" '' + <?php + ${cfg.pluginsConfig} + ''; + +in +{ + options.services.dokuwiki = { + enable = mkEnableOption "DokuWiki web application."; + + hostName = mkOption { + type = types.str; + default = "localhost"; + description = "FQDN for the instance."; + }; + + stateDir = mkOption { + type = types.path; + default = "/var/lib/dokuwiki/data"; + description = "Location of the dokuwiki state directory."; + }; + + acl = mkOption { + type = types.nullOr types.lines; + default = null; + example = "* @ALL 8"; + description = '' + Access Control Lists: see <link xlink:href="https://www.dokuwiki.org/acl"/> + Mutually exclusive with services.dokuwiki.aclFile + Set this to a value other than null to take precedence over aclFile option. + ''; + }; + + aclFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Location of the dokuwiki acl rules. Mutually exclusive with services.dokuwiki.acl + Mutually exclusive with services.dokuwiki.acl which is preferred. + Consult documentation <link xlink:href="https://www.dokuwiki.org/acl"/> for further instructions. + Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/acl.auth.php.dist"/> + ''; + }; + + aclUse = mkOption { + type = types.bool; + default = true; + description = '' + Necessary for users to log in into the system. + Also limits anonymous users. When disabled, + everyone is able to create and edit content. + ''; + }; + + pluginsConfig = mkOption { + type = types.lines; + default = '' + $plugins['authad'] = 0; + $plugins['authldap'] = 0; + $plugins['authmysql'] = 0; + $plugins['authpgsql'] = 0; + ''; + description = '' + List of the dokuwiki (un)loaded plugins. + ''; + }; + + superUser = mkOption { + type = types.nullOr types.str; + default = "@admin"; + description = '' + You can set either a username, a list of usernames (“admin1,admin2”), + or the name of a group by prepending an @ char to the groupname + Consult documentation <link xlink:href="https://www.dokuwiki.org/config:superuser"/> for further instructions. + ''; + }; + + usersFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Location of the dokuwiki users file. List of users. Format: + login:passwordhash:Real Name:email:groups,comma,separated + Create passwordHash easily by using:$ mkpasswd -5 password `pwgen 8 1` + Example: <link xlink:href="https://github.com/splitbrain/dokuwiki/blob/master/conf/users.auth.php.dist"/> + ''; + }; + + extraConfig = mkOption { + type = types.nullOr types.lines; + default = null; + example = '' + $conf['title'] = 'My Wiki'; + $conf['userewrite'] = 1; + ''; + description = '' + DokuWiki configuration. Refer to + <link xlink:href="https://www.dokuwiki.org/config"/> + for details on supported values. + ''; + }; + + poolConfig = mkOption { + type = with types; attrsOf (oneOf [ str int bool ]); + default = { + "pm" = "dynamic"; + "pm.max_children" = 32; + "pm.start_servers" = 2; + "pm.min_spare_servers" = 2; + "pm.max_spare_servers" = 4; + "pm.max_requests" = 500; + }; + description = '' + Options for the dokuwiki PHP pool. See the documentation on <literal>php-fpm.conf</literal> + for details on configuration directives. + ''; + }; + + nginx = mkOption { + type = types.submodule ( + recursiveUpdate + (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }) + { + # Enable encryption by default, + options.forceSSL.default = true; + options.enableACME.default = true; + } + ); + default = {forceSSL = true; enableACME = true;}; + example = { + serverAliases = [ + "wiki.\${config.networking.domain}" + ]; + enableACME = false; + }; + description = '' + With this option, you can customize the nginx virtualHost which already has sensible defaults for DokuWiki. + ''; + }; + }; + + # implementation + + config = mkIf cfg.enable { + + warnings = mkIf (cfg.superUser == null) ["Not setting services.dokuwiki.superUser will impair your ability to administer DokuWiki"]; + + assertions = [ + { + assertion = cfg.aclUse -> (cfg.acl != null || cfg.aclFile != null); + message = "Either services.dokuwiki.acl or services.dokuwiki.aclFile is mandatory when aclUse is true"; + } + { + assertion = cfg.usersFile != null -> cfg.aclUse != false; + message = "services.dokuwiki.aclUse must be true when usersFile is not null"; + } + ]; + + services.phpfpm.pools.dokuwiki = { + inherit user; + inherit group; + phpEnv = { + DOKUWIKI_LOCAL_CONFIG = "${dokuwikiLocalConfig}"; + DOKUWIKI_PLUGINS_LOCAL_CONFIG = "${dokuwikiPluginsLocalConfig}"; + } //optionalAttrs (cfg.usersFile != null) { + DOKUWIKI_USERS_AUTH_CONFIG = "${cfg.usersFile}"; + } //optionalAttrs (cfg.aclUse) { + DOKUWIKI_ACL_AUTH_CONFIG = if (cfg.acl != null) then "${dokuwikiAclAuthConfig}" else "${toString cfg.aclFile}"; + }; + + settings = { + "listen.mode" = "0660"; + "listen.owner" = user; + "listen.group" = group; + } // cfg.poolConfig; + }; + + services.nginx = { + enable = true; + + virtualHosts = { + ${cfg.hostName} = mkMerge [ cfg.nginx { + root = mkForce "${pkgs.dokuwiki}/share/dokuwiki/"; + extraConfig = "fastcgi_param HTTPS on;"; + + locations."~ /(conf/|bin/|inc/|install.php)" = { + extraConfig = "deny all;"; + }; + + locations."~ ^/data/" = { + root = "${cfg.stateDir}"; + extraConfig = "internal;"; + }; + + locations."~ ^/lib.*\.(js|css|gif|png|ico|jpg|jpeg)$" = { + extraConfig = "expires 365d;"; + }; + + locations."/" = { + priority = 1; + index = "doku.php"; + extraConfig = ''try_files $uri $uri/ @dokuwiki;''; + }; + + locations."@dokuwiki" = { + extraConfig = '' + # rewrites "doku.php/" out of the URLs if you set the userwrite setting to .htaccess in dokuwiki config page + rewrite ^/_media/(.*) /lib/exe/fetch.php?media=$1 last; + rewrite ^/_detail/(.*) /lib/exe/detail.php?media=$1 last; + rewrite ^/_export/([^/]+)/(.*) /doku.php?do=export_$1&id=$2 last; + rewrite ^/(.*) /doku.php?id=$1&$args last; + ''; + }; + + locations."~ \.php$" = { + extraConfig = '' + try_files $uri $uri/ /doku.php; + include ${pkgs.nginx}/conf/fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param REDIRECT_STATUS 200; + fastcgi_pass unix:${config.services.phpfpm.pools.dokuwiki.socket}; + fastcgi_param HTTPS on; + ''; + }; + }]; + }; + + }; + + systemd.tmpfiles.rules = [ + "d ${cfg.stateDir}/attic 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/cache 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/index 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/locks 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/media 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/media_attic 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/media_meta 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/meta 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/pages 0750 ${user} ${group} - -" + "d ${cfg.stateDir}/tmp 0750 ${user} ${group} - -" + ]; + + }; +} diff --git a/nixos/modules/services/web-servers/unit/default.nix b/nixos/modules/services/web-servers/unit/default.nix index 2303dfa9540..f8a18954fc9 100644 --- a/nixos/modules/services/web-servers/unit/default.nix +++ b/nixos/modules/services/web-servers/unit/default.nix @@ -111,7 +111,7 @@ in { AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SETGID" "CAP_SETUID" ]; # Security NoNewPrivileges = true; - # Sanboxing + # Sandboxing ProtectSystem = "full"; ProtectHome = true; RuntimeDirectory = "unit"; @@ -130,8 +130,10 @@ in { }; users.users = optionalAttrs (cfg.user == "unit") { - unit.group = cfg.group; - isSystemUser = true; + unit = { + group = cfg.group; + isSystemUser = true; + }; }; users.groups = optionalAttrs (cfg.group == "unit") { diff --git a/nixos/modules/services/x11/hardware/multitouch.nix b/nixos/modules/services/x11/hardware/multitouch.nix deleted file mode 100644 index c03bb3b494f..00000000000 --- a/nixos/modules/services/x11/hardware/multitouch.nix +++ /dev/null @@ -1,94 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let cfg = config.services.xserver.multitouch; - disabledTapConfig = '' - Option "MaxTapTime" "0" - Option "MaxTapMove" "0" - Option "TapButton1" "0" - Option "TapButton2" "0" - Option "TapButton3" "0" - ''; -in { - - options = { - - services.xserver.multitouch = { - - enable = mkOption { - default = false; - description = "Whether to enable multitouch touchpad support."; - }; - - invertScroll = mkOption { - default = false; - type = types.bool; - description = "Whether to invert scrolling direction à la OSX Lion"; - }; - - ignorePalm = mkOption { - default = false; - type = types.bool; - description = "Whether to ignore touches detected as being the palm (i.e when typing)"; - }; - - tapButtons = mkOption { - type = types.bool; - default = true; - description = "Whether to enable tap buttons."; - }; - - buttonsMap = mkOption { - type = types.listOf types.int; - default = [3 2 0]; - example = [1 3 2]; - description = "Remap touchpad buttons."; - apply = map toString; - }; - - additionalOptions = mkOption { - type = types.str; - default = ""; - example = '' - Option "ScaleDistance" "50" - Option "RotateDistance" "60" - ''; - description = '' - Additional options for mtrack touchpad driver. - ''; - }; - - }; - - }; - - config = mkIf cfg.enable { - - services.xserver.modules = [ pkgs.xf86_input_mtrack ]; - - services.xserver.config = - '' - # Automatically enable the multitouch driver - Section "InputClass" - MatchIsTouchpad "on" - Identifier "Touchpads" - Driver "mtrack" - Option "IgnorePalm" "${boolToString cfg.ignorePalm}" - Option "ClickFinger1" "${builtins.elemAt cfg.buttonsMap 0}" - Option "ClickFinger2" "${builtins.elemAt cfg.buttonsMap 1}" - Option "ClickFinger3" "${builtins.elemAt cfg.buttonsMap 2}" - ${optionalString (!cfg.tapButtons) disabledTapConfig} - ${optionalString cfg.invertScroll '' - Option "ScrollUpButton" "5" - Option "ScrollDownButton" "4" - Option "ScrollLeftButton" "7" - Option "ScrollRightButton" "6" - ''} - ${cfg.additionalOptions} - EndSection - ''; - - }; - -} diff --git a/nixos/modules/system/boot/networkd.nix b/nixos/modules/system/boot/networkd.nix index 3e289a63139..56a9d6b1138 100644 --- a/nixos/modules/system/boot/networkd.nix +++ b/nixos/modules/system/boot/networkd.nix @@ -49,7 +49,7 @@ let (assertValueOneOf "Kind" [ "bond" "bridge" "dummy" "gre" "gretap" "ip6gre" "ip6tnl" "ip6gretap" "ipip" "ipvlan" "macvlan" "macvtap" "sit" "tap" "tun" "veth" "vlan" "vti" "vti6" - "vxlan" "geneve" "vrf" "vcan" "vxcan" "wireguard" "netdevsim" + "vxlan" "geneve" "vrf" "vcan" "vxcan" "wireguard" "netdevsim" "xfrm" ]) (assertByteFormat "MTUBytes") (assertMacAddress "MACAddress") @@ -172,6 +172,14 @@ let (assertValueOneOf "AllSlavesActive" boolValues) ]; + checkXfrm = checkUnitConfig "Xfrm" [ + (assertOnlyFields [ + "InterfaceId" "Independent" + ]) + (assertRange "InterfaceId" 1 4294967295) + (assertValueOneOf "Independent" boolValues) + ]; + checkNetwork = checkUnitConfig "Network" [ (assertOnlyFields [ "Description" "DHCP" "DHCPServer" "LinkLocalAddressing" "IPv4LLRoute" @@ -182,7 +190,7 @@ let "IPv6HopLimit" "IPv4ProxyARP" "IPv6ProxyNDP" "IPv6ProxyNDPAddress" "IPv6PrefixDelegation" "IPv6MTUBytes" "Bridge" "Bond" "VRF" "VLAN" "IPVLAN" "MACVLAN" "VXLAN" "Tunnel" "ActiveSlave" "PrimarySlave" - "ConfigureWithoutCarrier" + "ConfigureWithoutCarrier" "Xfrm" ]) # Note: For DHCP the values both, none, v4, v6 are deprecated (assertValueOneOf "DHCP" ["yes" "no" "ipv4" "ipv6" "both" "none" "v4" "v6"]) @@ -477,6 +485,18 @@ let ''; }; + xfrmConfig = mkOption { + default = {}; + example = { InterfaceId = 1; }; + type = types.addCheck (types.attrsOf unitOption) checkXfrm; + description = '' + Each attribute in this set specifies an option in the + <literal>[Xfrm]</literal> section of the unit. See + <citerefentry><refentrytitle>systemd.netdev</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> for details. + ''; + }; + }; addressOptions = { @@ -712,6 +732,16 @@ let ''; }; + xfrm = mkOption { + default = [ ]; + type = types.listOf types.str; + description = '' + A list of xfrm interfaces to be added to the network section of the + unit. See <citerefentry><refentrytitle>systemd.network</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> for details. + ''; + }; + addresses = mkOption { default = [ ]; type = with types; listOf (submodule addressOptions); @@ -810,6 +840,11 @@ let ${attrsToSection def.bondConfig} ''} + ${optionalString (def.xfrmConfig != { }) '' + [Xfrm] + ${attrsToSection def.xfrmConfig} + + ''} ${optionalString (def.wireguardConfig != { }) '' [WireGuard] ${attrsToSection def.wireguardConfig} @@ -847,6 +882,7 @@ let ${concatStringsSep "\n" (map (s: "MACVLAN=${s}") def.macvlan)} ${concatStringsSep "\n" (map (s: "VXLAN=${s}") def.vxlan)} ${concatStringsSep "\n" (map (s: "Tunnel=${s}") def.tunnel)} + ${concatStringsSep "\n" (map (s: "Xfrm=${s}") def.xfrm)} ${optionalString (def.dhcpConfig != { }) '' [DHCP] diff --git a/nixos/modules/virtualisation/amazon-init.nix b/nixos/modules/virtualisation/amazon-init.nix index 8032b2c6d7c..8c12e0e49bf 100644 --- a/nixos/modules/virtualisation/amazon-init.nix +++ b/nixos/modules/virtualisation/amazon-init.nix @@ -7,8 +7,8 @@ let echo "attempting to fetch configuration from EC2 user data..." export HOME=/root - export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.gnused config.system.build.nixos-rebuild]}:$PATH - export NIX_PATH=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels + export PATH=${pkgs.lib.makeBinPath [ config.nix.package pkgs.systemd pkgs.gnugrep pkgs.git pkgs.gnutar pkgs.gzip pkgs.gnused config.system.build.nixos-rebuild]}:$PATH + export NIX_PATH=nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels userData=/etc/ec2-metadata/user-data @@ -18,9 +18,9 @@ let # that as the channel. if sed '/^\(#\|SSH_HOST_.*\)/d' < "$userData" | grep -q '\S'; then channels="$(grep '^###' "$userData" | sed 's|###\s*||')" - printf "%s" "$channels" | while read channel; do + while IFS= read -r channel; do echo "writing channel: $channel" - done + done < <(printf "%s\n" "$channels") if [[ -n "$channels" ]]; then printf "%s" "$channels" > /root/.nix-channels @@ -48,7 +48,7 @@ in { wantedBy = [ "multi-user.target" ]; after = [ "multi-user.target" ]; requires = [ "network-online.target" ]; - + restartIfChanged = false; unitConfig.X-StopOnRemoval = false; @@ -58,4 +58,3 @@ in { }; }; } - diff --git a/nixos/release.nix b/nixos/release.nix index f40b5fa9bd7..9109f5751eb 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -209,7 +209,8 @@ in rec { hydraJob ((import lib/eval-config.nix { inherit system; modules = - [ versionModule + [ configuration + versionModule ./maintainers/scripts/ec2/amazon-image.nix ]; }).config.system.build.amazonImage) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index ceeab2c21d9..8c11464f9d6 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -33,6 +33,7 @@ in bind = handleTest ./bind.nix {}; bittorrent = handleTest ./bittorrent.nix {}; #blivet = handleTest ./blivet.nix {}; # broken since 2017-07024 + buildkite-agent = handleTest ./buildkite-agent.nix {}; boot = handleTestOn ["x86_64-linux"] ./boot.nix {}; # syslinux is unsupported on aarch64 boot-stage1 = handleTest ./boot-stage1.nix {}; borgbackup = handleTest ./borgbackup.nix {}; @@ -74,6 +75,7 @@ in docker-tools = handleTestOn ["x86_64-linux"] ./docker-tools.nix {}; docker-tools-overlay = handleTestOn ["x86_64-linux"] ./docker-tools-overlay.nix {}; documize = handleTest ./documize.nix {}; + dokuwiki = handleTest ./dokuwiki.nix {}; dovecot = handleTest ./dovecot.nix {}; # ec2-config doesn't work in a sandbox as the simulated ec2 instance needs network access #ec2-config = (handleTestOn ["x86_64-linux"] ./ec2.nix {}).boot-ec2-config or {}; diff --git a/nixos/tests/buildkite-agent.nix b/nixos/tests/buildkite-agent.nix new file mode 100644 index 00000000000..3c824c9aedf --- /dev/null +++ b/nixos/tests/buildkite-agent.nix @@ -0,0 +1,36 @@ +import ./make-test-python.nix ({ pkgs, ... }: + +{ + name = "buildkite-agent"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ flokli ]; + }; + + nodes = { + node1 = { pkgs, ... }: { + services.buildkite-agent = { + enable = true; + privateSshKeyPath = (import ./ssh-keys.nix pkgs).snakeOilPrivateKey; + tokenPath = (pkgs.writeText "my-token" "5678"); + }; + }; + # don't configure ssh key, run as a separate user + node2 = { pkgs, ...}: { + services.buildkite-agent = { + enable = true; + tokenPath = (pkgs.writeText "my-token" "1234"); + }; + }; + }; + + testScript = '' + start_all() + # we can't wait on the unit to start up, as we obviously can't connect to buildkite, + # but we can look whether files are set up correctly + + node1.wait_for_file("/var/lib/buildkite-agent/buildkite-agent.cfg") + node1.wait_for_file("/var/lib/buildkite-agent/.ssh/id_rsa") + + node2.wait_for_file("/var/lib/buildkite-agent/buildkite-agent.cfg") + ''; +}) diff --git a/nixos/tests/corerad.nix b/nixos/tests/corerad.nix index 68b698857b4..950c9abc899 100644 --- a/nixos/tests/corerad.nix +++ b/nixos/tests/corerad.nix @@ -18,8 +18,7 @@ import ./make-test-python.nix ( [[interfaces]] name = "eth1" send_advertisements = true - [[interfaces.plugins]] - name = "prefix" + [[interfaces.prefix]] prefix = "::/64" ''; }; diff --git a/nixos/tests/dokuwiki.nix b/nixos/tests/dokuwiki.nix new file mode 100644 index 00000000000..38bde10f47e --- /dev/null +++ b/nixos/tests/dokuwiki.nix @@ -0,0 +1,29 @@ +import ./make-test-python.nix ({ lib, ... }: + +with lib; + +{ + name = "dokuwiki"; + meta.maintainers = with maintainers; [ maintainers."1000101" ]; + + nodes.machine = + { pkgs, ... }: + { services.dokuwiki = { + enable = true; + acl = " "; + superUser = null; + nginx = { + forceSSL = false; + enableACME = false; + }; + }; + }; + + testScript = '' + machine.start() + machine.wait_for_unit("phpfpm-dokuwiki.service") + machine.wait_for_unit("nginx.service") + machine.wait_for_open_port(80) + machine.succeed("curl -sSfL http://localhost/ | grep 'DokuWiki'") + ''; +}) |