dotfiles/system/hosts/server/default.nix

377 lines
12 KiB
Nix
Raw Normal View History

2023-05-11 05:33:08 +07:00
{ lib
, pkgs
, config
, ... }:
let
cfg = config.server;
hosted-domains =
builtins.concatLists
(builtins.attrValues
(builtins.mapAttrs
(k: v: [ k ] ++ v.serverAliases)
config.services.nginx.virtualHosts));
2023-05-11 05:33:08 +07:00
in {
imports = [
./options.nix
./matrix.nix
./fdroid.nix
./mumble.nix
2023-05-17 06:29:03 +07:00
./mailserver.nix
./home.nix
2023-08-28 00:46:51 +07:00
./keycloak.nix
2023-05-11 05:33:08 +07:00
];
system.stateVersion = "22.11";
impermanence.directories = [
{ directory = /var/www; }
{ directory = /secrets; mode = "0755"; }
];
2023-05-11 05:33:08 +07:00
networking.useDHCP = true;
networking.firewall = {
enable = true;
allowedTCPPorts = lib.mkMerge [
[
# ssh
22
# http/s
80 443
]
(lib.mkIf config.services.unbound.enable [
# dns
53 853
])
2023-05-11 05:33:08 +07:00
];
allowedUDPPorts = lib.mkIf config.services.unbound.enable [
2023-05-11 05:33:08 +07:00
# dns
53 853
# quic
443
2023-05-11 05:33:08 +07:00
];
};
# UNBOUND
users.users.${config.common.mainUsername}.extraGroups = lib.mkIf config.services.unbound.enable [ config.services.unbound.group ];
2023-05-17 06:29:03 +07:00
networking.resolvconf.extraConfig = lib.mkIf config.services.unbound.enable ''
name_servers="127.0.0.1 ::1"
'';
2023-05-11 05:33:08 +07:00
services.unbound = {
enable = false;
2023-05-11 05:33:08 +07:00
package = pkgs.unbound-with-systemd.override {
2023-06-24 07:12:11 +07:00
stdenv = pkgs.ccacheStdenv;
2023-05-11 05:33:08 +07:00
withPythonModule = true;
python = pkgs.python3;
2023-05-11 05:33:08 +07:00
};
localControlSocketPath = "/run/unbound/unbound.ctl";
resolveLocalQueries = false;
settings = {
server = {
interface = [ "0.0.0.0" "::" ];
access-control = [ "0.0.0.0/0 allow" "::/0 allow" ];
2023-05-11 05:33:08 +07:00
aggressive-nsec = true;
do-ip6 = true;
};
remote-control.control-enable = true;
};
};
# just in case
networking.hosts."127.0.0.1" = hosted-domains;
networking.hosts."::1" = hosted-domains;
2023-05-11 05:33:08 +07:00
services.postgresql.enable = true;
services.postgresql.package = pkgs.postgresql_13;
services.postgresql.extraPlugins = with pkgs.postgresql13Packages; [ tsja ];
2023-05-11 05:33:08 +07:00
# SSH
services.openssh.enable = true;
2023-05-13 20:32:35 +07:00
services.fail2ban = {
enable = true;
ignoreIP = lib.optionals (cfg.lanCidrV4 != "0.0.0.0/0") [ cfg.lanCidrV4 ]
++ (lib.optionals (cfg.lanCidrV6 != "::/0") [ cfg.lanCidrV6 ]);
maxretry = 10;
2023-05-13 20:32:35 +07:00
jails.dovecot = ''
enabled = true
filter = dovecot
'';
2023-05-11 05:33:08 +07:00
};
# SEARXNG
services.searx.enable = true;
services.searx.package = pkgs.searxng;
2023-05-11 05:33:08 +07:00
services.searx.runInUwsgi = true;
services.searx.uwsgiConfig = let inherit (config.services.searx) settings; in {
2023-05-17 06:29:03 +07:00
socket = "${lib.quoteListenAddr settings.server.bind_address}:${toString settings.server.port}";
2023-05-11 05:33:08 +07:00
};
2023-05-13 20:32:35 +07:00
services.searx.environmentFile = /var/lib/searx/searx.env;
2023-05-11 05:33:08 +07:00
services.searx.settings = {
use_default_settings = true;
search = {
2023-05-13 20:32:35 +07:00
safe_search = 0;
autocomplete = "duckduckgo"; # dbpedia, duckduckgo, google, startpage, swisscows, qwant, wikipedia - leave blank to turn off
default_lang = ""; # leave blank to detect from browser info or use codes from languages.py
2023-05-11 05:33:08 +07:00
};
server = {
port = 8888;
bind_address = "::1";
secret_key = "@SEARX_SECRET_KEY@";
base_url = "https://search.${cfg.domainName}/";
image_proxy = true;
default_http_headers = {
X-Content-Type-Options = "nosniff";
X-XSS-Protection = "1; mode=block";
X-Download-Options = "noopen";
X-Robots-Tag = "noindex, nofollow";
Referrer-Policy = "no-referrer";
};
};
outgoing = {
request_timeout = 5.0; # default timeout in seconds, can be override by engine
max_request_timeout = 15.0; # the maximum timeout in seconds
pool_connections = 100; # Maximum number of allowable connections, or null
pool_maxsize = 10; # Number of allowable keep-alive connections, or null
enable_http2 = true; # See https://www.python-httpx.org/http2/
};
};
2023-05-11 05:33:08 +07:00
services.nginx.virtualHosts."search.${cfg.domainName}" = let inherit (config.services.searx) settings; in {
quic = true;
2023-05-11 05:33:08 +07:00
enableACME = true;
forceSSL = true;
2023-05-17 06:29:03 +07:00
# locations."/".proxyPass = "http://${lib.quoteListenAddr settings.server.bind_address}:${toString settings.server.port}";
2023-05-11 05:33:08 +07:00
locations."/".extraConfig = ''
2023-05-17 06:29:03 +07:00
uwsgi_pass "${lib.quoteListenAddr settings.server.bind_address}:${toString settings.server.port}";
2023-05-11 05:33:08 +07:00
include ${config.services.nginx.package}/conf/uwsgi_params;
'';
};
# NGINX
services.nginx.enable = true;
services.nginx.enableReload = true;
services.nginx.package = pkgs.nginxQuic;
/* DNS over TLS
2023-05-11 05:33:08 +07:00
services.nginx.streamConfig =
let
inherit (config.security.acme.certs."${cfg.domainName}") directory;
in ''
upstream dns {
zone dns 64k;
server 127.0.0.1:53;
}
server {
listen 853 ssl;
ssl_certificate ${directory}/fullchain.pem;
ssl_certificate_key ${directory}/key.pem;
ssl_trusted_certificate ${directory}/chain.pem;
proxy_pass dns;
}
'';*/
services.nginx.commonHttpConfig = ''
log_format postdata '{\"ip\":\"$remote_addr\",\"time\":\"$time_iso8601\",\"referer\":\"$http_referer\",\"body\":\"$request_body\",\"ua\":\"$http_user_agent\"}';
${lib.concatMapStringsSep "\n" (x: "set_real_ip_from ${x};") (lib.splitString "\n" ''
${builtins.readFile (builtins.fetchurl {
url = "https://www.cloudflare.com/ips-v4";
sha256 = "0ywy9sg7spafi3gm9q5wb59lbiq0swvf0q3iazl0maq1pj1nsb7h";
})}
${builtins.readFile (builtins.fetchurl {
url = "https://www.cloudflare.com/ips-v6";
sha256 = "1ad09hijignj6zlqvdjxv7rjj8567z357zfavv201b9vx3ikk7cy";
})}'')}
real_ip_header CF-Connecting-IP;
'';
# brotli and zstd requires recompilation so I don't enable it
# services.nginx.recommendedBrotliSettings = true;
# services.nginx.recommendedZstdSettings = true;
2023-05-11 05:33:08 +07:00
services.nginx.recommendedGzipSettings = true;
services.nginx.recommendedOptimisation = true;
2023-05-11 05:33:08 +07:00
services.nginx.recommendedProxySettings = true;
services.nginx.recommendedTlsSettings = true;
2023-05-11 05:33:08 +07:00
# BLOG
services.nginx.virtualHosts.${cfg.domainName} = {
quic = true;
2023-05-11 05:33:08 +07:00
enableACME = true;
serverAliases = [ "www.${cfg.domainName}" ];
2023-05-11 05:33:08 +07:00
forceSSL = true;
extraConfig = "autoindex on;";
locations."/".root = "/var/www/${cfg.domainName}/";
locations."/src".root = "/var/www/${cfg.domainName}/";
locations."/src".extraConfig = "index force_dirlisting;";
locations."/submit_comment".extraConfig = ''
access_log /var/log/nginx/comments.log postdata;
proxy_pass https://${cfg.domainName}/submit.htm;
break;
'';
locations."/submit.htm" = {
extraConfig = ''
return 200 '<!doctype html><html><head><base href="/"/><link rel="preload" href="style.css" as="style"><title>Success!</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><link rel="icon" type="image/jpeg" href="pfp.jpg"><link rel="alternate" type="application/rss+xml" title="RSS" href="https://${cfg.domainName}/blog/index.xml"><link href="style.css" rel="stylesheet" /><script src="main.js"></script><meta http-equiv="refresh" content="10; url=$http_referer" /></head><body onload="documentLoaded()"><hr/><div class="main-body"><p>Success! It may take a while for your comment to get moderated.</p><p>Please wait for 10 seconds until you get redirected back...</p><p>Or just go there <a href="$http_referer">manually</a>.</p></div><hr/></body></html>';
'';
};
};
2023-05-13 20:32:35 +07:00
2023-05-11 05:33:08 +07:00
# GITEA
services.nginx.virtualHosts."git.${cfg.domainName}" = let inherit (config.services.gitea) settings; in {
quic = true;
2023-05-11 05:33:08 +07:00
enableACME = true;
forceSSL = true;
2023-05-17 06:29:03 +07:00
locations."/".proxyPass = "http://${lib.quoteListenAddr settings.server.HTTP_ADDR}:${toString settings.server.HTTP_PORT}";
2023-05-11 05:33:08 +07:00
};
services.gitea = {
enable = true;
database = {
createDatabase = false;
passwordFile = "/var/lib/gitea/db_password";
type = "postgres";
};
settings = {
mailer = {
ENABLED = true;
FROM = "Gitea <noreply@${cfg.domainName}>";
MAILER_TYPE = "smtp";
HOST = "mail.${cfg.domainName}:587";
USER = "noreply@${cfg.domainName}";
PASSWD = cfg.unhashedNoreplyPassword;
SKIP_VERIFY = true;
};
session = {
COOKIE_SECURE = true;
};
server = {
ROOT_URL = "https://git.${cfg.domainName}";
HTTP_ADDR = "::1";
HTTP_PORT = 3310;
DOMAIN = "git.${cfg.domainName}";
# START_SSH_SERVER = true;
# SSH_PORT = 2222;
};
service = {
DISABLE_REGISTRATION = true;
REGISTER_EMAIL_CONFIRM = true;
};
};
};
# NEXTCLOUD
services.nginx.virtualHosts."cloud.${cfg.domainName}" = {
quic = true;
2023-05-11 05:33:08 +07:00
enableACME = true;
forceSSL = true;
};
services.nextcloud = {
enable = true;
enableBrokenCiphersForSSE = false;
package = pkgs.nextcloud27;
2023-05-11 05:33:08 +07:00
autoUpdateApps.enable = true;
# TODO: use socket auth and remove the next line
database.createLocally = false;
config = {
adminpassFile = "/var/lib/nextcloud/admin_password";
dbpassFile = "/var/lib/nextcloud/db_password";
dbtype = "pgsql";
dbhost = "/run/postgresql";
overwriteProtocol = "https";
};
hostName = "cloud.${cfg.domainName}";
https = true;
};
# AKKOMA
# TODO: remove this in 2024
services.nginx.virtualHosts."pleroma.${cfg.domainName}" = {
quic = true;
enableACME = true;
addSSL = true;
serverAliases = [ "akkoma.${cfg.domainName}" ];
locations."/".return = "301 https://fedi.${cfg.domainName}$request_uri";
};
services.akkoma = {
2023-05-11 17:28:47 +07:00
enable = true;
dist.extraFlags = [
"+sbwt" "none"
"+sbwtdcpu" "none"
"+sbwtdio" "none"
];
config.":pleroma"."Pleroma.Web.Endpoint" = {
url = {
scheme = "https";
host = "fedi.${cfg.domainName}";
port = 443;
};
secret_key_base._secret = "/secrets/akkoma/secret_key_base";
signing_salt._secret = "/secrets/akkoma/signing_salt";
live_view.signing_salt._secret = "/secrets/akkoma/live_view_signing_salt";
};
extraStatic."static/terms-of-service.html" = pkgs.writeText "terms-of-service.html" ''
no bigotry kthx
'';
initDb = {
enable = false;
username = "akkoma";
password._secret = "/secrets/akkoma/postgres_password";
};
config.":pleroma".":instance" = {
name = cfg.domainName;
description = "Insert instance description here";
email = "webmaster-akkoma@${cfg.domainName}";
notify_email = "noreply@${cfg.domainName}";
limit = 5000;
registrations_open = true;
};
config.":pleroma"."Pleroma.Repo" = {
adapter = (pkgs.formats.elixirConf { }).lib.mkRaw "Ecto.Adapters.Postgres";
username = "akkoma";
password._secret = "/secrets/akkoma/postgres_password";
database = "akkoma";
hostname = "localhost";
};
config.":web_push_encryption".":vapid_details" = {
subject = "mailto:webmaster-akkoma@${cfg.domainName}";
public_key._secret = "/secrets/akkoma/push_public_key";
private_key._secret = "/secrets/akkoma/push_private_key";
};
config.":joken".":default_signer"._secret = "/secrets/akkoma/joken_signer";
nginx = {
quic = true;
enableACME = true;
forceSSL = true;
};
2023-05-11 17:28:47 +07:00
};
systemd.services.akkoma = {
path = [ pkgs.exiftool pkgs.gawk ];
serviceConfig.Restart = "on-failure";
unitConfig = {
StartLimitIntervalSec = 60;
StartLimitBurst = 3;
};
};
2023-05-11 05:33:08 +07:00
2023-10-24 00:19:12 +07:00
users.users.certspotter.extraGroups = [ "acme" ];
services.certspotter = {
enable = true;
watchlist = [ ".pavluk.org" ];
hooks = let
openssl = "${pkgs.openssl.bin}/bin/openssl";
in lib.toList (pkgs.writeShellScript "certspotter-hook" ''
if [[ "$EVENT" == discovered_cert ]]; then
mkdir -p /var/lib/certspotter/allowed_tbs
for cert in $(find /var/lib/acme -regex ".*/fullchain.pem"); do
hash="$(${openssl} x509 -in "$cert" -pubkey -noout | ${openssl} pkey -pubin -outform DER | ${openssl} sha256 | cut -d" " -f2)"
touch "/var/lib/certspotter/allowed_tbs/$hash"
done
[[ -f "/var/lib/certspotter/allowed_tbs/$TBS_SHA256" ]] && exit 0
fi
(echo "Subject: $SUMMARY" && echo && cat "$TEXT_FILENAME") | /run/wrappers/bin/sendmail -i webmaster-certspotter@${cfg.domainName}
'');
};
2023-05-11 05:33:08 +07:00
/*locations."/dns-query".extraConfig = ''
grpc_pass grpc://127.0.0.1:53453;
'';*/
# TODO: firefox sync?
}