diff options
Diffstat (limited to 'modules/server')
38 files changed, 1437 insertions, 0 deletions
diff --git a/modules/server/acme/default.nix b/modules/server/acme/default.nix new file mode 100644 index 000000000000..38f92c865ea2 --- /dev/null +++ b/modules/server/acme/default.nix @@ -0,0 +1,8 @@ +{ ... }: + +{ + security.acme.acceptTerms = true; + + # TODO: email to root? + security.acme.defaults.email = "hi@alyssa.is"; +} diff --git a/modules/server/bitfolk/default.nix b/modules/server/bitfolk/default.nix new file mode 100644 index 000000000000..d5b54109fa05 --- /dev/null +++ b/modules/server/bitfolk/default.nix @@ -0,0 +1,15 @@ +{ ... }: + +{ + boot.initrd.availableKernelModules = [ "xen_blkfront" ]; + + systemd.enableEmergencyMode = false; + + # Bitfolk provides its own GRUB, so we need to give it a grub.cfg + # but don't need to actually install GRUB anywhere. + boot.loader.grub.enable = true; + boot.loader.grub.device = "nodev"; + + networking.dhcpcd.enable = false; +} + diff --git a/modules/server/cgit/default.nix b/modules/server/cgit/default.nix new file mode 100644 index 000000000000..aba0d1b54c5d --- /dev/null +++ b/modules/server/cgit/default.nix @@ -0,0 +1,133 @@ +{ lib, pkgs, config, ... }: + +let + inherit (builtins) split; + inherit (lib) flip foldr groupBy head literalExpression mapAttrs + mapAttrs' mapAttrsToList mdDoc mkOption nameValuePair optionalAttrs types; + + cfg = config.services.cgit-qyliss; + + instancesByVhost = groupBy ({ value, ... }: value.vhost) + (mapAttrsToList nameValuePair cfg.instances); + + vhostConfigs = mapAttrs (vhost: instances: + foldr (l: r: l // r) {} (map ({ name, value }: let + unslashedPath = head (split "/+$" value.path); + # We'll be dealing almost exclusively with paths ending in /, + # since otherwise Nginx likes to do simple prefix matching. + path = "${unslashedPath}/"; + in { + locations = { + ${path} = { + alias = "${value.package}/cgit/"; + tryFiles = "$uri @${name}-cgit"; + }; + "@${name}-cgit" = { + proxyPass = "http://unix:/run/cgit/${name}.sock"; + }; + } // optionalAttrs (unslashedPath != "") { + ${unslashedPath} = { + return = "301 ${path}"; + }; + }; + + extraConfig = '' + if ($http_user_agent = "my-tiny-bot") { + return 429; + } + if ($http_user_agent = "thesis-research-bot") { + return 429; + } + ''; + }) instances) + ) instancesByVhost; +in + +{ + options.services.cgit-qyliss = { + instances = mkOption { + type = types.attrsOf (types.submodule { + options = { + vhost = mkOption { + type = types.str; + example = "spectrum-os.org"; + description = mdDoc "Nginx vhost for the cgit"; + }; + + path = mkOption { + type = types.strMatching "/(.*[^/])?"; + default = "/"; + example = "/git"; + description = mdDoc '' + Path to be appended to all cgit URLs. + + Leading slashes are mandatory; trailing slashes are forbidden. + ''; + }; + + package = mkOption { + type = types.package; + default = pkgs.cgit; + defaultText = literalExpression "pkgs.cgit"; + description = mdDoc "cgit package to use"; + }; + + config = mkOption { + type = types.package; + description = mdDoc '' + Configuration file for cgit. See + <citerefentry><refentrytitle>cgitrc</refentrytitle> + <manvolnum>5</manvolnum></citerefentry>. + ''; + }; + }; + }); + default = {}; + description = mdDoc "List of cgit instances to run"; + }; + }; + + config = { + services.nginx.virtualHosts = vhostConfigs; + + systemd.services = flip mapAttrs' cfg.instances (name: instance: { + name = "lighttpd-${name}@"; + value = { + unitConfig.CollectMode = "inactive-or-failed"; + serviceConfig.StandardInput = "socket"; + serviceConfig.StandardOutput = "socket"; + serviceConfig.StandardError = "journal"; + serviceConfig.DynamicUser = true; + serviceConfig.Type = "oneshot"; + serviceConfig.TimeoutSec = "30"; + serviceConfig.ExecStart = "${lib.getExe pkgs.lighttpd} -1 -f ${pkgs.writeText "lighttpd-${name}.conf" '' + server.modules = ( "mod_alias", "mod_setenv", "mod_cgi" ) + + server.document-root = "/var/empty" + + alias.url = ( + "${if instance.path == "/" then "" else instance.path}" => + "${instance.package}/cgit/cgit.cgi" + ) + + cgi.assign = ( + "cgit.cgi" => "${instance.package}/cgit/cgit.cgi" + ) + + setenv.add-environment = ( + "CGIT_CONFIG" => "${instance.config}" + ) + ''}"; + }; + }); + + systemd.sockets = flip mapAttrs' cfg.instances (name: instance: { + name = "lighttpd-${name}"; + value = { + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = "/run/cgit/${name}.sock"; + socketConfig.Accept = "yes"; + }; + }); + }; +} diff --git a/modules/server/default.nix b/modules/server/default.nix new file mode 100644 index 000000000000..f59ea9662667 --- /dev/null +++ b/modules/server/default.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: + +{ + imports = [ ../nix ../ssh ../users ]; + + security.sudo.wheelNeedsPassword = false; + + networking.firewall.logRefusedConnections = false; + + i18n.defaultLocale = "C.UTF-8"; + + environment.systemPackages = with pkgs; [ htop ncdu ]; +} diff --git a/modules/server/dns/default.nix b/modules/server/dns/default.nix new file mode 100644 index 000000000000..ae36f06a2f80 --- /dev/null +++ b/modules/server/dns/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + networking.nameservers = [ "127.0.0.1" ]; + + services.unbound.enable = true; +} diff --git a/modules/server/ftp/default.nix b/modules/server/ftp/default.nix new file mode 100644 index 000000000000..5fbf3f82877c --- /dev/null +++ b/modules/server/ftp/default.nix @@ -0,0 +1,49 @@ +{ pkgs, lib, config, ... }: + +with lib; + +let + cfg = config.ftp; +in + +{ + options = { + ftp = { + files = mkOption { + default = {}; + type = with types; attrsOf path; + description = mdDoc '' + Files to serve on https://ftp.qyliss.net/ + ''; + example = literalExample '' + { + "foo/bar.txt" = pkgs.writeText "bar.txt" '''' + Hello, world! + ''''; + } + ''; + }; + }; + }; + + config = { + services.nginx.virtualHosts."ftp.qyliss.net" = { + forceSSL = true; + useACMEHost = "qyliss.net"; + + root = pkgs.runCommand "ftp.qyliss.net" {} '' + mkdir $out + ${concatStrings (mapAttrsToList (httpPath: diskPath: '' + mkdir -p "$out/$(dirname ${escapeShellArg httpPath})" + ln -s ${escapeShellArg diskPath} $out/${escapeShellArg httpPath} + '') cfg.files)} + ''; + + extraConfig = '' + autoindex on; + ''; + }; + + security.acme.certs."qyliss.net".extraDomainNames = [ "ftp.qyliss.net" ]; + }; +} diff --git a/modules/server/git-http-backend/default.nix b/modules/server/git-http-backend/default.nix new file mode 100644 index 000000000000..32e20e603e61 --- /dev/null +++ b/modules/server/git-http-backend/default.nix @@ -0,0 +1,106 @@ +{ lib, pkgs, config, ... }: + +let + inherit (builtins) split; + inherit (lib) flip foldr groupBy head literalExpression mapAttrs mapAttrs' + mapAttrsToList mdDoc mkOption nameValuePair optionalAttrs types; + + cfg = config.services.git-http-backend; + + instancesByVhost = groupBy ({ value, ... }: value.vhost) + (mapAttrsToList nameValuePair cfg.instances); + + vhostConfigs = mapAttrs (vhost: instances: + foldr (l: r: l // r) {} (map ({ name, value }: let + path = head (split "/+$" value.path); + pathRegex = + "^${path}/.*?(\.git)?/(HEAD|info/refs|git-(upload|receive)-pack)$"; + in { + locations = { + "~ ${pathRegex}" = { + proxyPass = "http://unix:/run/cgiserver/git-http-backend/${name}.sock"; + + extraConfig = '' + client_max_body_size 0; + proxy_read_timeout 3600; + proxy_send_timeout 3600; + ''; + }; + }; + }) instances) + ) instancesByVhost; +in + +{ + options.services.git-http-backend = { + package = mkOption { + type = types.package; + default = pkgs.gitMinimal; + description = mdDoc "git package to use"; + }; + + instances = mkOption { + type = types.attrsOf (types.submodule { + options = { + vhost = mkOption { + type = types.str; + example = "spectrum-os.org"; + description = mdDoc "Nginx vhost for the git server"; + }; + + path = mkOption { + type = types.strMatching "/(.*[^/])?"; + default = "/"; + example = "/git"; + description = mdDoc '' + Path to be prepended to all clone URLs. + + Leading slashes are mandatory; trailing slashes are forbidden. + ''; + }; + + cgiserver = mkOption { + type = types.package; + default = pkgs.cgiserver; + defaultText = literalExpression "pkgs.cgiserver"; + description = mdDoc "cgiserver package to use"; + }; + + projectRoot = mkOption { + type = types.strMatching "/(.*[^/])?"; + example = "/var/www/git"; + description = mdDoc '' + Directory in which to look for git repositories. + + Leading slashes are mandatory; trailing slashes are forbidden. + ''; + }; + }; + }); + default = {}; + description = mdDoc "List of git-http-backend instances to run"; + }; + }; + + config = { + services.nginx.virtualHosts = vhostConfigs; + + systemd.services = flip mapAttrs' cfg.instances (name: instance: { + name = "git-http-backend-${name}"; + value = { + environment.GIT_HTTP_EXPORT_ALL = ""; + environment.GIT_PROJECT_ROOT = instance.projectRoot; + serviceConfig.DynamicUser = true; + serviceConfig.ExecStart = "${instance.cgiserver}/bin/cgiserver -r ${instance.path} ${cfg.package}/bin/git-http-backend"; + }; + }); + + systemd.sockets = flip mapAttrs' cfg.instances (name: instance: { + name = "git-http-backend-${name}"; + value = { + wantedBy = [ "sockets.target" ]; + socketConfig.ListenStream = "/run/cgiserver/git-http-backend/${name}.sock"; + }; + }); + }; +} diff --git a/modules/server/git/default.nix b/modules/server/git/default.nix new file mode 100644 index 000000000000..523715a363d9 --- /dev/null +++ b/modules/server/git/default.nix @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: V <v@unfathomable.blue> +# SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is> +# SPDX-License-Identifier: OSL-3.0 + +# Adapted from https://src.unfathomable.blue/nixos-config/tree/modules/declarative-git.nix + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.declarative-git; + + repoOpts = { config, ... }: { + options = { + branch = mkOption { + default = "main"; + description = mdDoc "Branch to be the repository's HEAD"; + type = types.str; + }; + + description = mkOption { + description = mdDoc "Description of the repository."; + type = types.str; + }; + + config = mkOption { + description = mdDoc "Git configuration for the repository."; + type = types.attrs; + default = {}; + }; + + hooks = mkOption { + description = mdDoc "Git hooks for the repository."; + type = with types; attrsOf (listOf path); + default = {}; + }; + + owner = mkOption { + description = mdDoc "Name of the user to own the git repository."; + type = types.str; + default = "-"; + }; + + group = mkOption { + description = mdDoc "Name of the group for the git repository."; + type = types.str; + default = "-"; + }; + }; + }; +in { + options.declarative-git = { + repositories = mkOption { + description = mdDoc "Repositories to manage declaratively."; + type = types.attrsOf (types.submodule repoOpts); + default = {}; + }; + + hooks = mkOption { + description = mdDoc "Git hooks to apply to all declarative repositories."; + type = with types; attrsOf (listOf path); + default = {}; + }; + }; + + config.systemd.tmpfiles.packages = mapAttrsToList (path: config: + pkgs.declarative-git-repository { + inherit path; + inherit (config) branch config description owner group; + hooks = zipAttrsWith (_: concatLists) [ cfg.hooks config.hooks ]; + }) cfg.repositories; +} diff --git a/modules/server/git/nixpkgs/default.nix b/modules/server/git/nixpkgs/default.nix new file mode 100644 index 000000000000..95788318a334 --- /dev/null +++ b/modules/server/git/nixpkgs/default.nix @@ -0,0 +1,58 @@ +{ lib, pkgs, ... }: + +let + inherit (pkgs) writeText; + toGitConfig = lib.generators.toINI { listsAsDuplicateKeys = true; }; +in + +{ + users.groups.nixpkgs = {}; + + environment.etc.gitconfig.text = '' + [safe] + directory = /var/lib/git/nixpkgs.git + ''; + + systemd.tmpfiles.rules = [ + "L+ /var/lib/git/nixpkgs.git/HEAD - - - - refs/heads/master" + "L+ /var/lib/git/nixpkgs.git/config - - - - ${writeText "config" (toGitConfig { + core.repositoryformatversion = 0; + core.filemode = true; + core.bare = true; + core.sharedRepository = "world"; + "remote \"origin\"" = { + url = "https://github.com/NixOS/nixpkgs"; + fetch = [ + "+refs/heads/master:refs/remotes/origin/master" + "+refs/heads/staging:refs/remotes/origin/staging" + "+refs/heads/staging-*:refs/remotes/origin/staging-*" + "+refs/heads/nixos-*:refs/remotes/origin/nixos-*" + "+refs/heads/nixpkgs-unstable:refs/remotes/origin/nixpkgs-unstable" + "+refs/heads/nixpkgs-*-darwin:refs/remotes/origin/nixpkgs-*-darwin" + "+refs/heads/release-*:refs/remotes/origin/release-*" + ]; + }; + })}" + "d /var/lib/git/nixpkgs.git 2775 - nixpkgs" + "d /var/lib/git/nixpkgs.git/refs 2775 - nixpkgs" + "d /var/lib/git/nixpkgs.git/objects 2775 - nixpkgs" + "d /var/lib/git/nixpkgs.git/objects/pack 2775 - nixpkgs" + ]; + + systemd.services.git-fetch-nixpkgs = { + after = [ "network-online.target" ]; + requires = [ "network-online.target" ]; + serviceConfig.DynamicUser = true; + serviceConfig.Group = "nixpkgs"; + serviceConfig.ReadWritePaths = "/var/lib/git/nixpkgs.git"; + serviceConfig.ExecStart = "${pkgs.gitMinimal}/bin/git --git-dir /var/lib/git/nixpkgs.git fetch"; + serviceConfig.Type = "oneshot"; + serviceConfig.UMask = "0002"; + }; + + systemd.timers.git-fetch-nixpkgs = { + wantedBy = [ "timers.target" ]; + timerConfig.OnActiveSec = 0; + timerConfig.OnUnitActiveSec = 300; + }; +} diff --git a/modules/server/irc/default.nix b/modules/server/irc/default.nix new file mode 100644 index 000000000000..81a039ae420b --- /dev/null +++ b/modules/server/irc/default.nix @@ -0,0 +1,5 @@ +{ ... }: + +{ + imports = [ ./soju ./znc ]; +} diff --git a/modules/server/irc/soju/default.nix b/modules/server/irc/soju/default.nix new file mode 100644 index 000000000000..8e8a1dce502b --- /dev/null +++ b/modules/server/irc/soju/default.nix @@ -0,0 +1,47 @@ +{ config, lib, ... }: + +{ + networking.firewall.allowedTCPPorts = [ 6698 ]; + + services.postgresql.enable = true; + services.postgresql.ensureDatabases = [ "soju" ]; + services.postgresql.ensureUsers = [ + { + name = "soju"; + ensureDBOwnership = true; + } + ]; + + services.soju.enable = true; + services.soju.hostName = "${config.networking.hostName}.${config.networking.domain}"; + services.soju.extraConfig = '' + db postgres "dbname=soju host=/run/postgresql sslmode=disable" + message-store db + ''; + services.soju.listen = [ + "unix:///run/soju/soju.sock" + "unix+admin://" + ]; + + services.nginx.streamConfig = '' + server { + listen [::]:6698 ssl ipv6only=off; + ssl_certificate /var/lib/acme/${config.networking.domain}/fullchain.pem; + ssl_certificate_key /var/lib/acme/${config.networking.domain}/key.pem; + proxy_pass unix:/run/soju/soju.sock; + } + ''; + + systemd.services.soju.serviceConfig.DynamicUser = lib.mkForce false; + systemd.services.soju.serviceConfig.Group = "soju"; + systemd.services.soju.serviceConfig.RuntimeDirectory = "soju"; + systemd.services.soju.serviceConfig.UMask = "0007"; + systemd.services.soju.serviceConfig.User = "soju"; + + users.users.nginx.extraGroups = [ "soju" ]; + users.users.soju = { + isNormalUser = true; + group = "soju"; + }; + users.groups.soju = {}; +} diff --git a/modules/server/irc/znc/default.nix b/modules/server/irc/znc/default.nix new file mode 100644 index 000000000000..65ba8d087d83 --- /dev/null +++ b/modules/server/irc/znc/default.nix @@ -0,0 +1,31 @@ +{ config, pkgs, ... }: + +{ + services.znc.enable = true; + services.znc.useLegacyConfig = false; + services.znc.modulePackages = with pkgs; [ zncModules.playback ]; + + services.nginx.virtualHosts."znc.${config.networking.domain}" = { + forceSSL = true; + useACMEHost = "qyliss.net"; + + locations = { + "/" = { + proxyPass = "http://127.0.0.1:6667/"; + }; + }; + }; + + services.nginx.streamConfig = '' + server { + listen [::]:6697 ssl ipv6only=off; + ssl_certificate /var/lib/acme/${config.networking.domain}/fullchain.pem; + ssl_certificate_key /var/lib/acme/${config.networking.domain}/key.pem; + proxy_pass 127.0.0.1:6667; + } + ''; + + security.acme.certs."qyliss.net".extraDomainNames = [ "znc.qyliss.net" ]; + + networking.firewall.allowedTCPPorts = [ 6697 ]; +} diff --git a/modules/server/mail/default.nix b/modules/server/mail/default.nix new file mode 100644 index 000000000000..cebc16dfabe1 --- /dev/null +++ b/modules/server/mail/default.nix @@ -0,0 +1,79 @@ +{ lib, pkgs, config, ... }: + +let + inherit (pkgs) runCommand; + + mailmanCfg = config.services.mailman; +in + +{ + services.postgresql.enable = true; + services.postgresql.ensureDatabases = [ "mailman" ]; + services.postgresql.ensureUsers = [ + { + name = "mailman"; + ensureDBOwnership = true; + } + ]; + + services.mailman.enable = true; + + services.mailman.siteOwner = "postmaster@spectrum-os.org"; + services.mailman.webHosts = [ "spectrum-os.org" ]; + services.mailman.hyperkitty.enable = true; + services.mailman.hyperkitty.baseUrl = "http://localhost:18507/lists/hyperkitty/"; + services.mailman.settings.database.class = "mailman.database.postgresql.PostgreSQLDatabase"; + services.mailman.settings.database.url = "postgresql:///mailman"; + services.mailman.extraConfig = '' + + [antispam] + header_checks: + X-Spam-Flag: YES + + [logging.template] + level: debug + ''; + + services.mailman.webSettings.ADMINS = [ [ "Alyssa Ross" "hi@alyssa.is" ] ]; + services.mailman.webSettings.ALLOWED_HOSTS = [ "localhost" "127.0.0.1" "spectrum-os.org" ]; + services.mailman.webSettings.INSTALLED_APPS = [ + "hyperkitty" + "postorius" + "django_mailman3" + "django.contrib.admin" + "django.contrib.auth" + "django.contrib.contenttypes" + "django.contrib.sessions" + "django.contrib.sites" + "django.contrib.messages" + "django.contrib.staticfiles" + "rest_framework" + "django_gravatar" + "compressor" + "haystack" + "django_extensions" + "django_q" + "allauth" + "allauth.account" + "allauth.socialaccount" + ]; + services.mailman.webSettings.USE_X_FORWARDED_HOST = true; + services.mailman.webSettings.SECURE_PROXY_SSL_HEADER = [ "HTTP_X_FORWARDED_SCHEME" "https" ]; + services.mailman.webSettings.SESSION_COOKIE_SECURE = true; + services.mailman.webSettings.SECURE_CONTENT_TYPE_NOSNIFF = true; + services.mailman.webSettings.SECURE_BROWSER_XSS_FILTER = true; + services.mailman.webSettings.CSRF_COOKIE_SECURE = true; + services.mailman.webSettings.CSRF_COOKIE_HTTPONLY = true; + services.mailman.webSettings.LANGUAGE_CODE = "en-gb"; + services.mailman.webSettings.STATIC_URL = "/lists/static/"; + services.mailman.webSettings.DEFAULT_FROM_EMAIL = "postmaster@spectrum-os.org"; + services.mailman.webSettings.SERVER_EMAIL = "postmaster@spectrum-os.org"; + services.mailman.webSettings.SOCIALACCOUNT_PROVIDERS = {}; + services.mailman.webSettings.COMPRESS_CSS_HASHING_METHOD = "content"; + services.mailman.webSettings.FILTER_VHOST = true; + + systemd.services.mailman.after = [ "postgresql.service" ]; + + services.mailman.serve.enable = true; + services.mailman.serve.virtualRoot = "/lists"; +} diff --git a/modules/server/mail/public-inbox/default.nix b/modules/server/mail/public-inbox/default.nix new file mode 100644 index 000000000000..d533f98d03b9 --- /dev/null +++ b/modules/server/mail/public-inbox/default.nix @@ -0,0 +1,62 @@ +{ config, pkgs, lib, ... }: + +let + public-inbox = config.services.public-inbox.package; + + public-inbox-src = pkgs.stdenv.mkDerivation { + name = "public-inbox-${public-inbox.version}-qyliss.tar.gz"; + + inherit (public-inbox) src patches; + + doBuild = false; + + installPhase = '' + cd $NIX_BUILD_TOP + mv $sourceRoot public-inbox-${public-inbox.version}-qyliss + tar cf $out public-inbox-${public-inbox.version}-qyliss + ''; + }; + + hash = with lib; + # Safe because we're just using the hash as a file name, and don't + # need the file name itself to have a dependency on the src. + builtins.unsafeDiscardStringContext + (head (splitString "-" + (last (splitString "/" public-inbox-src.outPath)))); + + tarballName = "public-inbox-${public-inbox.version}-qyliss-${hash}.tar.gz"; +in + +{ + services.public-inbox.enable = true; + services.public-inbox.mda.enable = true; + services.public-inbox.http.enable = true; + services.public-inbox.nntp.enable = true; + + services.public-inbox.mda.args = [ "--no-precheck" ]; + services.public-inbox.http.port = "/run/public-inbox-httpd.sock"; + services.public-inbox.postfix.enable = true; + services.public-inbox.settings.publicinbox.wwwlisting = "match=domain"; + + services.public-inbox.nntp.port = null; + systemd.sockets.public-inbox-nntpd.listenStreams = [ "0.0.0.0:119" "0.0.0.0:563" ]; + systemd.services.public-inbox-nntpd.serviceConfig.SupplementaryGroups = [ "public-inbox" "acme" ]; + + services.public-inbox.settings.publicinbox.css = + [ "href=https://spectrum-os.org/lists/archives/public-inbox.css" ]; + + services.public-inbox.settings.publicinbox.sourceinfo = + let + url = "https://ftp.qyliss.net/public-inbox/${tarballName}"; + in (pkgs.writeText "public-inbox-source-info.html" '' + <a href="${url}" download>${url}</a> + '').outPath; + + ftp.files."public-inbox/${tarballName}" = public-inbox-src; + + services.spamassassin.enable = true; + environment.etc."mail/spamassassin/public-inbox.pre".source = + "${public-inbox.sa_config}/root/etc/spamassassin/public-inbox.pre"; + + networking.firewall.allowedTCPPorts = [ 119 563 ]; +} diff --git a/modules/server/nginx/default.nix b/modules/server/nginx/default.nix new file mode 100644 index 000000000000..d60b1aa30c56 --- /dev/null +++ b/modules/server/nginx/default.nix @@ -0,0 +1,22 @@ +{ pkgs, ... }: + +{ + services.nginx.enable = true; + services.nginx.package = pkgs.nginxMainline; + + services.nginx.recommendedOptimisation = true; + services.nginx.recommendedTlsSettings = true; + services.nginx.recommendedGzipSettings = true; + services.nginx.recommendedProxySettings = true; + + services.nginx.commonHttpConfig = '' + log_format privacy '[$time_local] $request_method ' + '$scheme://$host$request_uri $status $body_bytes_sent ' + '($upstream_response_time seconds)'; + + # systemd catches syslog, and access_log doesn't support stdout/stderr. + access_log syslog:server=unix:/dev/log privacy; + ''; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; +} diff --git a/modules/server/nixpk.gs/acme/default.nix b/modules/server/nixpk.gs/acme/default.nix new file mode 100644 index 000000000000..4c3c8f446602 --- /dev/null +++ b/modules/server/nixpk.gs/acme/default.nix @@ -0,0 +1,7 @@ +{ config, lib, ... }: + +{ + security.acme.certs."nixpk.gs" = { + webroot = "/var/lib/acme/acme-challenge"; + }; +} diff --git a/modules/server/nixpk.gs/default.nix b/modules/server/nixpk.gs/default.nix new file mode 100644 index 000000000000..7ed0e4b4f7d4 --- /dev/null +++ b/modules/server/nixpk.gs/default.nix @@ -0,0 +1,5 @@ +{ ... }: + +{ + imports = [ ./acme ./nginx ./pr-tracker ]; +} diff --git a/modules/server/nixpk.gs/nginx/default.nix b/modules/server/nixpk.gs/nginx/default.nix new file mode 100644 index 000000000000..cd02a70be0c7 --- /dev/null +++ b/modules/server/nixpk.gs/nginx/default.nix @@ -0,0 +1,13 @@ +{ pkgs, ... }: + +{ + services.nginx.virtualHosts."nixpk.gs" = { + forceSSL = true; + useACMEHost = "nixpk.gs"; + + locations."/".root = pkgs.runCommand "index.html" {} '' + mkdir -p $out + cp ${./index.html} $out/index.html + ''; + }; +} diff --git a/modules/server/nixpk.gs/nginx/index.html b/modules/server/nixpk.gs/nginx/index.html new file mode 100644 index 000000000000..0c4e94022447 --- /dev/null +++ b/modules/server/nixpk.gs/nginx/index.html @@ -0,0 +1,12 @@ +<!doctype html> +<html lang="en"> + <title>nixpk.gs</title> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + + <h1>nixpk.gs</h1> + + <ul> + <li><a href="/pr-tracker.html">Pull request tracker</a> + </ul> +</html> diff --git a/modules/server/nixpk.gs/pr-tracker/default.nix b/modules/server/nixpk.gs/pr-tracker/default.nix new file mode 100644 index 000000000000..e3b00c433455 --- /dev/null +++ b/modules/server/nixpk.gs/pr-tracker/default.nix @@ -0,0 +1,28 @@ +{ pkgs, ... }: + +{ + imports = [ ../../git/nixpkgs ]; + + services.nginx.virtualHosts."nixpk.gs".locations."/pr-tracker.html" = { + proxyPass = "http://unix:/run/pr-tracker.sock:/pr-tracker.html"; + extraConfig = '' + proxy_http_version 1.1; + ''; + }; + + systemd.services.pr-tracker = { + requires = [ "pr-tracker.socket" ]; + serviceConfig.ExecStart = "${pkgs.pr-tracker}/bin/pr-tracker --path /var/lib/git/nixpkgs.git --remote origin --user-agent 'pr-tracker by alyssais' --source-url https://git.qyliss.net/pr-tracker --mount pr-tracker.html"; + serviceConfig.StandardInput = "file:/etc/pr-tracker/token"; + serviceConfig.DynamicUser = true; + serviceConfig.SupplementaryGroups = "nixpkgs"; + serviceConfig.UMask = "0002"; + serviceConfig.ReadWritePaths = "/var/lib/git/nixpkgs.git"; + }; + + systemd.sockets.pr-tracker = { + wantedBy = [ "sockets.target" ]; + before = [ "nginx.service" ]; + socketConfig.ListenStream = "/run/pr-tracker.sock"; + }; +} diff --git a/modules/server/spectrum/acme/default.nix b/modules/server/spectrum/acme/default.nix new file mode 100644 index 000000000000..6a60f52d2456 --- /dev/null +++ b/modules/server/spectrum/acme/default.nix @@ -0,0 +1,7 @@ +{ ... }: + +{ + security.acme.certs."spectrum-os.org" = { + webroot = "/var/lib/acme/acme-challenge"; + }; +} diff --git a/modules/server/spectrum/cgit/default.nix b/modules/server/spectrum/cgit/default.nix new file mode 100644 index 000000000000..e691d438a840 --- /dev/null +++ b/modules/server/spectrum/cgit/default.nix @@ -0,0 +1,57 @@ +{ pkgs, ... }: + +let + + spectrumReadme = pkgs.writeText "about.html" '' + <article> + + <h1>Contributing to Spectrum</h1> + + <p> + Want to contribute to Spectrum? We'd love to have you. + Have a look at the <a href="/contributing.html">online + documentation</a>. + + </article> + ''; + + sourceFilter = pkgs.runCommand "source-filter" { + nativeBuildInputs = with pkgs; with python3.pkgs; [ wrapPython ]; + } '' + mkdir -p $out/bin + sed s/pastie/friendly/g >$out/bin/syntax-highlighting.py \ + <${pkgs.cgit-pink}/lib/cgit/filters/.syntax-highlighting.py-wrapped + chmod +x $out/bin/syntax-highlighting.py + wrapPythonPrograms + ''; +in + +{ + imports = [ ../../cgit ]; + + services.cgit-qyliss.instances.spectrum = { + package = pkgs.cgit-pink; + vhost = "spectrum-os.org"; + path = "/git"; + config = pkgs.writeText "cgit.conf" '' + clone-prefix=https://spectrum-os.org/git + css=/git/cgit.css + enable-blame=1 + enable-commit-graph=1 + enable-follow-links=1 + enable-git-config=1 + enable-index-owner=0 + favicon=https://spectrum-os.org/logo/logo_html.svg + logo= + remove-suffix=1 + root-desc=Web interface for Spectrum source code + root-readme=${spectrumReadme} + root-title=Spectrum Git Repository Browser + snapshots=all + about-filter=${pkgs.cgit-pink}/lib/cgit/filters/about-formatting.sh + source-filter=${sourceFilter}/bin/syntax-highlighting.py + + scan-path=/home/spectrum/git + ''; + }; +} diff --git a/modules/server/spectrum/default.nix b/modules/server/spectrum/default.nix new file mode 100644 index 000000000000..d6c2eaa57d0e --- /dev/null +++ b/modules/server/spectrum/default.nix @@ -0,0 +1,14 @@ +{ ... }: + +{ + imports = [ + ./acme ./cgit ./git ./git-http-backend ./nginx ./patch-refs ./postfix + ./public-inbox ./spectrumbot ./vultr-mon + ]; + + nix.settings.substituters = [ "https://cache.dataaturservice.se/spectrum/" ]; + nix.settings.trusted-public-keys = [ + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + "spectrum-os.org-1:rnnSumz3+Dbs5uewPlwZSTP0k3g/5SRG4hD7Wbr9YuQ=" + ]; +} diff --git a/modules/server/spectrum/git-http-backend/default.nix b/modules/server/spectrum/git-http-backend/default.nix new file mode 100644 index 000000000000..e7a3b003f190 --- /dev/null +++ b/modules/server/spectrum/git-http-backend/default.nix @@ -0,0 +1,11 @@ +{ ... }: + +{ + imports = [ ../../git-http-backend ]; + + services.git-http-backend.instances.spectrum = { + vhost = "spectrum-os.org"; + path = "/git"; + projectRoot = "/home/spectrum/git"; + }; +} diff --git a/modules/server/spectrum/git/default.nix b/modules/server/spectrum/git/default.nix new file mode 100644 index 000000000000..beb61b78dd89 --- /dev/null +++ b/modules/server/spectrum/git/default.nix @@ -0,0 +1,110 @@ +{ pkgs, ... }: + +{ + imports = [ ../../git ]; + + declarative-git.repositories."/home/spectrum/git/crosvm.git" = { + branch = "master"; + description = "Downstream crosvm tree for Spectrum"; + group = "spectrum"; + config.cgit.section = "obsolete"; + }; + + declarative-git.repositories."/home/spectrum/git/doc.git" = { + branch = "master"; + description = "Old manuals for Spectrum"; + hooks.post-update = [ + (pkgs.writeShellScript "post-update.sh" '' + nix-build --tarball-ttl 0 --out-link built --cores 1 -j1 -E " + let src = builtins.fetchGit ./.; + in (import src).overrideAttrs ({ ... }: { inherit src; }) + " + '') + ]; + group = "spectrum"; + config.cgit.section = "obsolete"; + }; + + declarative-git.repositories."/home/spectrum/git/nixpkgs.git" = { + branch = "rootfs"; + description = "Downstream nixpkgs tree for Spectrum"; + group = "spectrum"; + config.cgit.defBranch = "rootfs"; + config.cgit.section = "obsolete"; + config.core.sharedrepository = "0644"; + }; + + declarative-git.repositories."/home/spectrum/git/mktuntap.git" = { + branch = "master"; + description = "Utility program for creating TUN and TAP devices on file descriptors"; + group = "spectrum"; + config.cgit.readme = ":README"; + config.core.sharedrepository = "0644"; + config.receive.denyNonFastforwards = true; + }; + + declarative-git.repositories."/home/spectrum/git/spectrum.git" = { + description = "A compartmentalized operating system"; + group = "spectrum"; + config.cgit.defBranch = "main"; + hooks.post-receive = with pkgs; [ + (writeShellScript "send-email.sh" '' + set -ueo pipefail + export PATH=${lib.makeBinPath [ coreutils curl gitMinimal gnused mailutils ]} + + repo_url=https://spectrum-os.org/git/spectrum + inbox_url=https://spectrum-os.org/lists/archives/spectrum-devel + + while read oldrev newrev refname; do + [ "$refname" = "refs/heads/main" ] || continue + + git log --reverse --format=%H "$oldrev..$newrev" | while read commit; do + message_id="$(git log -1 --format=%B "$commit" | + git interpret-trailers --parse | + sed -n 's/^Message-Id: <\(.*\)>$/\1/Ip' | head -n 1)" + + [ -n "$message_id" ] || continue + + url="$inbox_url/$message_id/raw" + path="$(mktemp)" + curl -LSfso "$path" "$url" + mail -E "file $path" -E "reply" -E "quit" <<EOF + This patch has been committed as $commit, + which can be viewed online at + $repo_url/commit/?id=$commit. + + This is an automated message. Send comments/questions/requests to: + Alyssa Ross <hi@alyssa.is> + EOF + rm "$path" + done + done + '') + (writeShellScript "build-documentation.sh" '' + nix-build --tarball-ttl 0 --out-link /home/spectrum/Documentation -E ' + import "''${builtins.fetchGit { url = ./.; ref = "main"; }}/Documentation" {} + ' + '') + ]; + }; + + declarative-git.repositories."/home/spectrum/git/ucspi-vsock.git" = { + branch = "master"; + description = "UCSPI-1996 implementation for Linux AF_VSOCK sockets"; + group = "spectrum"; + config.cgit.section = "obsolete"; + }; + + declarative-git.repositories."/home/spectrum/git/www.git" = { + branch = "master"; + description = "Static source files for the Spectrum website"; + group = "spectrum"; + config.cgit.readme = ":README"; + config.core.bare = false; + config.core.logallrefupdates = true; + config.core.sharedrepository = 1; + config.core.worktree = "../../www"; + config.receive.denyCurrentBranch = "updateInstead"; + config.receive.denyNonFastforwards = true; + }; +} diff --git a/modules/server/spectrum/nginx/default.nix b/modules/server/spectrum/nginx/default.nix new file mode 100644 index 000000000000..edb4a8f855f1 --- /dev/null +++ b/modules/server/spectrum/nginx/default.nix @@ -0,0 +1,41 @@ +{ lib, ... }: + +let + inherit (lib) head tail; + + redirectDomains = [ + "spectrum-os.com" + "spectrumos.org" + "www.spectrum-os.com" + "www.spectrum-os.org" + "www.spectrumos.org" + ]; +in + +{ + services.nginx.virtualHosts."spectrum-redirects" = { + serverName = head redirectDomains; + serverAliases = tail redirectDomains; + addSSL = true; + useACMEHost = "spectrum-os.org"; + globalRedirect = "spectrum-os.org"; + }; + + services.nginx.virtualHosts."spectrum-os.org".locations."= /participating.html".return = + "301 /doc/contributing/communication.html"; + + services.nginx.virtualHosts."spectrum-os.org".locations."= /doc".return = + "301 /doc/"; + services.nginx.virtualHosts."spectrum-os.org".locations."/doc/".alias = + "/home/spectrum/Documentation/"; + + # TODO: some sort of robots.txt generation module might be nice. + services.nginx.virtualHosts."spectrum-os.org".locations."= /robots.txt" = { + alias = ./robots.txt; + }; + + security.acme.certs."spectrum-os.org".extraDomainNames = redirectDomains; + + # The Spectrum website lives in /home/spectrum/www + systemd.services.nginx.serviceConfig.ProtectHome = false; +} diff --git a/modules/server/spectrum/nginx/robots.txt b/modules/server/spectrum/nginx/robots.txt new file mode 100644 index 000000000000..b2d1429a929b --- /dev/null +++ b/modules/server/spectrum/nginx/robots.txt @@ -0,0 +1,5 @@ +User-agent: * +# Wildcards are not supported in the robots.txt spec, +# but let's hope they work anyway. +Disallow: /git/*/snapshot/ +Crawl-delay: 5 diff --git a/modules/server/spectrum/patch-refs/default.nix b/modules/server/spectrum/patch-refs/default.nix new file mode 100644 index 000000000000..8e608c5201d2 --- /dev/null +++ b/modules/server/spectrum/patch-refs/default.nix @@ -0,0 +1,46 @@ +{ lib, pkgs, ... }: + +{ + users.users.patch-refs = { + description = "spectrum-devel patch monitor"; + group = "spectrum"; + isSystemUser = true; + }; + + services.postfix.virtual = '' + patch-refs@spectrum-os.org patch-refs@spectrum-os.org + ''; + + services.postfix.transport = '' + patch-refs@spectrum-os.org patch-refs: + ''; + + services.postfix.masterConfig.patch-refs = { + type = "unix"; + command = "pipe"; + privileged = true; + args = [ + "flags=X" + "user=patch-refs" + "argv=${with pkgs; toString [ + "${execline}/bin/export" "PATH" + (lib.makeBinPath [ + b4 coreutils findutils gitMinimal strace + + (mblaze.overrideAttrs ({ patches ? [], ... }: { + patches = patches ++ [ + (fetchpatch { + url = "https://inbox.vuxu.org/mblaze/20220523170921.2623516-1-hi@alyssa.is/raw"; + sha256 = "1fwnr6277fjdrv0lvjrzyxjd1p94c6jg2nl6cd4lh9aizmfbjiq0"; + }) + ]; + })) + ]) + "${execline}/bin/execlineb" + "-S1" + (copyPathToStore ./mda.elb) + "$client_address" + ]}" + ]; + }; +} diff --git a/modules/server/spectrum/patch-refs/mda.elb b/modules/server/spectrum/patch-refs/mda.elb new file mode 100644 index 000000000000..c613d0529f88 --- /dev/null +++ b/modules/server/spectrum/patch-refs/mda.elb @@ -0,0 +1,36 @@ +foreground { echo "Mail from " $1 } +if -x 77 { test $1 = IPv6:::1 } + +backtick message_id { mhdr -h Message-Id - } +backtick dir { mktemp -d } + +multisubstitute { + importas -i message_id message_id + importas -i dir dir + define origin /home/spectrum/git/spectrum.git +} + +foreground { + if { mkdir ${dir}/git } + cd ${dir}/git + export GIT_CONFIG_COUNT 2 + export GIT_CONFIG_KEY_0 am.messageid + export GIT_CONFIG_VALUE_0 true + export GIT_CONFIG_KEY_1 b4.midmask + export GIT_CONFIG_VALUE_1 https://spectrum-os.org/lists/archives/spectrum-test/%s + export XDG_CACHE_HOME ${dir}/cache + export XDG_DATA_HOME ${dir}/data + if { git clone -n --single-branch --reference $origin $origin . } + if -x 75 { b4 shazam -CH $message_id } + pipeline { + git log -z --format=%H:refs/patches/%(trailers:key=Message-Id,valueonly) + HEAD..FETCH_HEAD + } + pipeline { tr -d <> } + redirfd -w 2 /tmp/err + xargs -tr0 + git push origin --dry-run +} +importas -iu exit ? +if { rm -rf $dir } +exit $exit diff --git a/modules/server/spectrum/postfix/default.nix b/modules/server/spectrum/postfix/default.nix new file mode 100644 index 000000000000..978cb47726e6 --- /dev/null +++ b/modules/server/spectrum/postfix/default.nix @@ -0,0 +1,71 @@ +{ pkgs, ... }: + +{ + services.postfix.enable = true; + services.postfix.enableSubmission = true; + services.postfix.hostname = "atuin.qyliss.net"; + services.postfix.config.smtp_tls_loglevel = "1"; + services.postfix.config.smtpd_forbid_bare_newline = true; + services.postfix.config.disable_mime_output_conversion = true; + services.postfix.sslCert = "/var/lib/acme/spectrum-os.org/fullchain.pem"; + services.postfix.sslKey = "/var/lib/acme/spectrum-os.org/key.pem"; + services.postfix.rootAlias = "hi@alyssa.is"; + services.postfix.relayDomains = [ "hash:/var/lib/mailman/data/postfix_domains" ]; + services.postfix.config.transport_maps = [ "hash:/var/lib/mailman/data/postfix_lmtp" ]; + services.postfix.localRecipients = []; # empty array causes NixOS to add $alias_maps + services.postfix.config.mailbox_command = "${pkgs.coreutils}/bin/false"; + services.postfix.config.local_recipient_maps = + [ "proxy:unix:passwd.byname" "hash:/var/lib/mailman/data/postfix_lmtp" ]; + + services.postfix.destination = + [ "atuin.qyliss.net" "qyliss.net" "spectrumos.org" "spectrum-os.org" ]; + services.postfix.extraAliases = '' + abuse: root + noc: root + security: root + hostmaster: root + usenet: root + news: root + webmaster: root + www: root + uucp: root + ftp: root + ''; + + services.postfix.enableHeaderChecks = true; + + # Local mail can be submitted without being filtered through SpamAssassin. + services.postfix.masterConfig."::1:smtp" = { + type = "inet"; + private = false; + command = "smtpd"; + }; + + services.postfix.masterConfig.smtp_inet.args = + [ "-o" "content_filter=spamassassin" ]; + + services.postfix.masterConfig.spamassassin = { + privileged = true; + chroot = false; + command = "pipe"; + args = [ + "user=postfix-spamc" + "argv=${pkgs.spamassassin}/bin/spamc" + "-f" + "-e" + "/run/wrappers/bin/sendmail" + "-oi" + "-f" + "\${sender}" + "\${recipient}" + ]; + }; + + networking.firewall.allowedTCPPorts = [ 25 ]; + + users.groups.postfix-spamc = {}; + users.users.postfix-spamc = { + group = "postfix-spamc"; + isSystemUser = true; + }; +} diff --git a/modules/server/spectrum/public-inbox/default.nix b/modules/server/spectrum/public-inbox/default.nix new file mode 100644 index 000000000000..2c5aed09631b --- /dev/null +++ b/modules/server/spectrum/public-inbox/default.nix @@ -0,0 +1,70 @@ +{ config, lib, ... }: + +let + repos = [ "crosvm" "doc" "mktuntap" "nixpkgs" "spectrum" "ucspi-vsock" "www" ]; +in + +{ + imports = [ ../../mail/public-inbox ]; + + services.public-inbox.http.mounts = + [ "https://spectrum-os.org/lists/archives" ]; + services.public-inbox.nntp.cert = + "/var/lib/acme/spectrum-os.org/fullchain.pem"; + services.public-inbox.nntp.key = "/var/lib/acme/spectrum-os.org/key.pem"; + services.public-inbox.settings.publicinbox.nntpserver = + [ "nntps://spectrum-os.org" "nntp://spectrum-os.org" ]; + + systemd.services.public-inbox-httpd.serviceConfig.ProtectHome = "tmpfs"; + systemd.services.public-inbox-httpd.serviceConfig.BindReadOnlyPaths = + map (c: c.dir) (lib.attrValues config.services.public-inbox.settings.coderepo); + + services.public-inbox.settings.coderepo = lib.genAttrs repos (name: { + dir = "/home/spectrum/git/${name}.git"; + cgitUrl = "https://spectrum-os.org/git/${name}"; + }); + + services.public-inbox.inboxes.spectrum-announce = { + address = [ + "public-inbox+spectrum-announce@spectrum-os.org" + "announce@spectrum-os.org" + ]; + description = "announcements from the spectrum developers"; + url = "https://spectrum-os.org/lists/archives/spectrum-announce"; + newsgroup = "inbox.comp.spectrum.announce"; + }; + + services.public-inbox.inboxes.spectrum-discuss = { + address = [ + "public-inbox+spectrum-discuss@spectrum-os.org" + "discuss@spectrum-os.org" + ]; + description = "general high-level discussion about spectrum"; + filter = "PublicInbox::Filter::Mirror"; + url = "https://spectrum-os.org/lists/archives/spectrum-discuss"; + newsgroup = "inbox.comp.spectrum.discuss"; + }; + + services.public-inbox.inboxes.spectrum-devel = { + address = [ + "public-inbox+spectrum-devel@spectrum-os.org" + "devel@spectrum-os.org" + ]; + description = "patches and low-level development discussion"; + filter = "PublicInbox::Filter::Mirror"; + url = "https://spectrum-os.org/lists/archives/spectrum-devel"; + newsgroup = "inbox.comp.spectrum.devel"; + coderepo = repos; + }; + + services.public-inbox.inboxes.spectrum-test = { + address = [ + "public-inbox+spectrum-test@spectrum-os.org" + "test@spectrum-os.org" + ]; + description = "test list for spectrum infrastructure"; + url = "https://spectrum-os.org/lists/archives/spectrum-test"; + newsgroup = "inbox.comp.spectrum.test"; + hide = [ "www" ]; + }; +} diff --git a/modules/server/spectrum/spectrumbot/default.nix b/modules/server/spectrum/spectrumbot/default.nix new file mode 100644 index 000000000000..bef02077a3af --- /dev/null +++ b/modules/server/spectrum/spectrumbot/default.nix @@ -0,0 +1,5 @@ +{ config, lib, pkgs, ... }: + +{ + imports = [ ./irccat ./postfix ]; +} diff --git a/modules/server/spectrum/spectrumbot/irccat/default.nix b/modules/server/spectrum/spectrumbot/irccat/default.nix new file mode 100644 index 000000000000..f4efd3828703 --- /dev/null +++ b/modules/server/spectrum/spectrumbot/irccat/default.nix @@ -0,0 +1,53 @@ +{ config, pkgs, ... }: + +{ + environment.etc."irccat.json".text = builtins.toJSON { + tcp.listen = "[::1]:18770"; + + irc.server = "irc.libera.chat:6697"; + irc.tls = true; + irc.nick = "spectrumbot"; + irc.realname = "#spectrum bot"; + irc.channels = [ "#spectrum" ]; + irc.keys = {}; + + irc.sasl_external = true; + irc.tls_client_cert = "/var/lib/irccat/tls.pem"; + + commands = {}; + }; + + systemd.services.irccat = { + after = [ "network-online.target" ]; + requires = [ "network-online.target" ]; + restartTriggers = [ config.environment.etc."irccat.json".source ]; + serviceConfig.StateDirectory = "irccat"; + serviceConfig.StateDirectoryMode = "0700"; + serviceConfig.ExecStart = "${pkgs.irccat}/bin/irccat"; + serviceConfig.Restart = "always"; + serviceConfig.RestartSec = 60; + wantedBy = [ "multi-user.target" ]; + + serviceConfig.CapabilityBoundingSet = ""; + serviceConfig.DynamicUser = true; + serviceConfig.LockPersonality = true; + serviceConfig.MemoryDenyWriteExecute = true; + serviceConfig.PrivateDevices = true; + serviceConfig.PrivateUsers = true; + serviceConfig.ProcSubset = "pid"; + serviceConfig.ProtectClock = true; + serviceConfig.ProtectControlGroups = true; + serviceConfig.ProtectHome = true; + serviceConfig.ProtectHostname = true; + serviceConfig.ProtectKernelLogs = true; + serviceConfig.ProtectKernelModules = true; + serviceConfig.ProtectKernelTunables = true; + serviceConfig.ProtectProc = "invisible"; + serviceConfig.RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + serviceConfig.RestrictNamespaces = true; + serviceConfig.RestrictRealtime = true; + serviceConfig.SystemCallArchitectures = "native"; + serviceConfig.SystemCallFilter = [ "@system-service" "~@privileged" ]; + serviceConfig.UMask = "0077"; + }; +} diff --git a/modules/server/spectrum/spectrumbot/postfix/default.nix b/modules/server/spectrum/spectrumbot/postfix/default.nix new file mode 100644 index 000000000000..3430107a59be --- /dev/null +++ b/modules/server/spectrum/spectrumbot/postfix/default.nix @@ -0,0 +1,39 @@ +{ lib, pkgs, ... }: + +{ + users.groups.irccat-mail = {}; + users.users.irccat-mail = { + isSystemUser = true; + group = "irccat-mail"; + }; + + services.postfix.virtual = '' + irccat@spectrum-os.org irccat@spectrum-os.org + ''; + + services.postfix.transport = '' + irccat@spectrum-os.org irccat: + ''; + + services.postfix.masterConfig.irccat = { + type = "unix"; + maxproc = 1; + command = "pipe"; + privileged = true; + args = [ + "flags=X" + "user=irccat-mail" + "argv=${with pkgs; toString [ + "${execline}/bin/export" "PATH" + (lib.makeBinPath [ coreutils gnused libressl.nc mblaze ]) + "${execline}/bin/execlineb" + "-S1" + (copyPathToStore ./mda.elb) + "$client_address" + ]}" + ]; + }; + + systemd.services.postfix.wants = [ "irccat.service" ]; +} + diff --git a/modules/server/spectrum/spectrumbot/postfix/mda.elb b/modules/server/spectrum/spectrumbot/postfix/mda.elb new file mode 100644 index 000000000000..05b111cf59a5 --- /dev/null +++ b/modules/server/spectrum/spectrumbot/postfix/mda.elb @@ -0,0 +1,26 @@ +backtick -E path { mktemp } +if { redirfd -w 1 $path cat } +foreground { echo "Mail from " $1 } +if -x 77 { test $1 = IPv6:::1 } + +foreground { + pipeline -w { nc -N ::1 18770 } + pipeline -w { if { tr -d "\n" } echo } + backtick list { + pipeline { mhdr -h List-Id $path } + sed "s/.*<\\([^.>]*\\)[.>].*/\\1/" + } + if { printf "📨 #ORANGE" } + if { printenv list } + if { printf "@ #GREEN" } + if { maddr -dh from $path } + if { printf " #NORMAL#BOLD" } + if { mhdr -h Subject $path } + if { printf " #NORMAL#BLUE#UNDERLINEhttps://spectrum-os.org/lists/archives/spectrum-" } + if { printenv list } + pipeline { mhdr -h Message-Id $path } + sed "s,.*<\\([^>]*\\)>.*,/\\1/," +} +importas -iu exit ? +if { rm $path } +exit $exit diff --git a/modules/server/spectrum/vultr-mon/default.nix b/modules/server/spectrum/vultr-mon/default.nix new file mode 100644 index 000000000000..50890d68ccbb --- /dev/null +++ b/modules/server/spectrum/vultr-mon/default.nix @@ -0,0 +1,24 @@ +{ pkgs, ... }: + +{ + systemd.services.vultr-mon = { + after = [ "network-online.target" ]; + requires = [ "network-online.target" ]; + path = with pkgs; [ coreutils curl findutils jq ]; + script = '' + api_base=https://api.vultr.com/v2 + curl -fsLSH @/var/lib/vultr-mon/key $api_base/instances | + jq -r '.instances[] | select(.date_created < $date) | .id' --arg date "$(date -uIseconds -d '24 hours ago')" | + xargs -rtd '\n' -I% curl -fsLSX DELETE -H @/var/lib/vultr-mon/key $api_base/instances/% + ''; + serviceConfig.DynamicUser = true; + serviceConfig.StateDirectory = "vultr-mon"; + serviceConfig.Type = "oneshot"; + }; + + systemd.timers.vultr-mon = { + wantedBy = [ "timers.target" ]; + timerConfig.OnActiveSec = 0; + timerConfig.OnUnitActiveSec = 3600; + }; +} diff --git a/modules/server/tor/default.nix b/modules/server/tor/default.nix new file mode 100644 index 000000000000..3e93309835b6 --- /dev/null +++ b/modules/server/tor/default.nix @@ -0,0 +1,19 @@ +{ lib, config, ... }: + +{ + networking.firewall.allowedTCPPorts = [ 143 ]; + + services.tor.enable = true; + + services.tor.settings.AccountingMax = + lib.mkDefault (throw "Set tor accountingMax!!"); + + services.tor.settings.AccountingStart = + lib.mkDefault (throw "Set tor accountingStart!!"); + + services.tor.relay.enable = true; + services.tor.relay.role = "relay"; + services.tor.settings.ContactInfo = "hi@alyssa.is"; + services.tor.settings.Nickname = lib.mkDefault config.networking.hostName; + services.tor.settings.ORPort = [ 143 ]; +} diff --git a/modules/server/xmpp/default.nix b/modules/server/xmpp/default.nix new file mode 100644 index 000000000000..dcd3dba8000f --- /dev/null +++ b/modules/server/xmpp/default.nix @@ -0,0 +1,30 @@ +{ pkgs, ... }: + +{ + networking.firewall.allowedTCPPorts = [ 5222 5269 5281 ]; + + security.acme.certs."qyliss.net".extraDomainNames = + [ "muc.qyliss.net" "upload.qyliss.net" ]; + + services.prosody.enable = true; + services.prosody.modules.http_files = true; + services.prosody.modules.mam = true; + services.prosody.s2sSecureAuth = true; + services.prosody.muc = [ + { domain = "muc.qyliss.net"; } + ]; + services.prosody.package = pkgs.prosody.override { + withCommunityModules = [ "smacks" "csi" ]; + }; + services.prosody.ssl.key = "/var/lib/acme/qyliss.net/key.pem"; + services.prosody.ssl.cert = "/var/lib/acme/qyliss.net/fullchain.pem"; + services.prosody.uploadHttp.domain = "upload.qyliss.net"; + services.prosody.virtualHosts."qyliss.net" = { + domain = "qyliss.net"; + enabled = true; + ssl.key = "/var/lib/acme/qyliss.net/key.pem"; + ssl.cert = "/var/lib/acme/qyliss.net/fullchain.pem"; + }; + + users.users.prosody.extraGroups = [ "acme" ]; +} |