diff --git a/server/configuration.nix b/server/configuration.nix index 312c75a..42b3407 100644 --- a/server/configuration.nix +++ b/server/configuration.nix @@ -1,6 +1,7 @@ { config, lib, pkgs, ... }: { - imports = [ ../common.nix ./hardware-configuration.nix ]; + imports = [ ../common.nix ./hardware-configuration.nix + ./mods/web.nix ./mods/mail.nix ./mods/sysd.nix ]; boot.kernelParams = [ "consoleblank=60" #TPM fix @@ -49,218 +50,4 @@ }; }; }; - security.acme = { - acceptTerms = true; - defaults.email = "letsencrypt@bogaledev.ru"; - certs."bogaledev.ru" = { - validMinDays = 3; - dnsProvider = "cloudflare"; - extraDomainNames = [ "*.bogaledev.ru" ]; - credentialFiles = { "CF_DNS_API_TOKEN_FILE" = "/secrets/cf-token"; }; - reloadServices = [ "nginx.service" "dovecot.service" "postfix.service" ]; - }; - }; - systemd = { - tmpfiles.rules = [ - "d /var/spool/postfix 0755 postfix postfix -" - "d /var/spool/postfix/private 0755 postfix postfix -" - ]; - timers = { - tgbot-send = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnCalendar = "*-*-01 16:00:00"; - Persistent = true; - }; - }; - network-watchdog = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = 30; - AccuracySec = 1; - OnUnitActiveSec = 10; - }; - }; - }; - services = { - tgbot-send = { - serviceConfig.Type = "oneshot"; - script = '' - . /secrets/tgbot.env - ${pkgs.curl}/bin/curl "https://api.telegram.org/$BOT/sendMessage" \ - -X POST -d "chat_id=$CHAT_ID" -d "text=$TEXT" 2> /dev/null | \ - ${pkgs.jq}/bin/jq '.result.date |= strftime("%Y-%m-%d %H:%M:%S")' - ''; - }; - network-watchdog = { - serviceConfig = { - Type = "oneshot"; - LogLevelMax = "notice"; - }; - script = '' - if [ ! -e /run/network.failures ] || \ - ${pkgs.iputils}/bin/ping -c 1 -W 3 1.1.1.1 &> /dev/null; then - failures=0 - else - failures=$((1+$(cat /run/network.failures))) - fi - if [ $failures -gt 0 ]; then - echo "<5>failures = $failures" - fi - if [ $failures -ge 3 ]; then - echo "<4>restarting NetworkManager" - systemctl restart NetworkManager.service - failures=0 - fi - echo $failures > /run/network.failures - ''; - }; - }; - }; - services = let - mailBase = "/var/spool/mail/vmail"; - saslSocket = "/var/spool/postfix/private/auth"; - sslCertDir = config.security.acme.certs."bogaledev.ru".directory; - in { - openssh = { - enable = true; - settings.PasswordAuthentication = false; - }; - postgresql.enable = true; - forgejo = { - enable = true; - database.type = "postgres"; - settings = { - service.DISABLE_REGISTRATION = true; - server = { - HTTP_PORT = 8039; - ROOT_URL = "https://bogaledev.ru/git/"; - }; - }; - }; - phpfpm.pools.default = { - user = "php"; - group = "php"; - settings = { - "pm" = "ondemand"; - "pm.max_children" = 8; - "listen.owner" = "nginx"; - "listen.group" = "nginx"; - }; - }; - vaultwarden = { - enable = true; - dbBackend = "postgresql"; - configurePostgres = true; - package = pkgs.vaultwarden-postgresql; - environmentFile = "/secrets/vw-token.env"; - config = { - ROCKET_PORT = 8032; - SIGNUPS_ALLOWED = false; - TRASH_AUTO_DELETE_DAYS = 90; - PASSWORD_HINTS_ALLOWED = false; - EMERGENCY_ACCESS_ALLOWED = false; - DOMAIN = "https://bogaledev.ru/vw/"; - }; - }; - nginx.virtualHosts."bogaledev.ru" = let - phpPool = pool: '' - location ~ \.php$ { - fastcgi_pass unix:${config.services.phpfpm.pools.${pool}.socket}; - } - ''; - localNetworks = '' - allow fc00::/64; - allow fc01::/64; - allow fc02::/64; - allow 10.0.0.0/16; - allow 10.1.0.0/16; - allow 10.2.0.0/16; - deny all; - ''; - in { - quic = true; - default = true; - forceSSL = true; - root = "/srv/http"; - useACMEHost = "bogaledev.ru"; - extraConfig = ''add_header Alt-Svc 'h3=":443"; ma=86400' always;''; - locations = { - "/app/".extraConfig = phpPool "default"; - "/git/".proxyPass = "http://127.0.0.1:8039/"; - "/priv/".extraConfig = ''${localNetworks} ${phpPool "default"}''; - "/vw/" = { - proxyWebsockets = true; - extraConfig = localNetworks; - proxyPass = "http://127.0.0.1:8032"; - }; - }; - }; - redis.servers.rspamd = { - enable = true; - user = "rspamd"; - }; - rspamd = { - enable = true; - postfix.enable = true; - locals = { - "classifier-bayes.conf".text = "autolearn = true;"; - "redis.conf".text = '' - servers = "${config.services.redis.servers.rspamd.unixSocket}"; - ''; - }; - }; - dovecot2 = { - enable = true; - settings = { - ssl = "required"; - mail_gid = "vmail"; - mail_uid = "vmail"; - mail_path = mailBase; - protocols.imap = true; - mail_driver = "maildir"; - auth_mechanisms = [ "plain" ]; - dovecot_config_version = "2.4.5"; - dovecot_storage_version = "2.4.5"; - ssl_server_key_file = "${sslCertDir}/key.pem"; - ssl_server_cert_file = "${sslCertDir}/fullchain.pem"; - "passdb passwd-file".passwd_file_path = "/secrets/dovecot-pw"; - "service auth"."unix_listener ${saslSocket}" = { - mode = "0660"; - user = "postfix"; - group = "postfix"; - }; - }; - }; - postfix = { - enable = true; - enableSubmission = true; - virtualMapType = "regexp"; - virtual = "/.*@bogaledev.ru/ mail@bogaledev.ru"; - mapFiles = { - smtp_passwd = "/secrets/smtp_passwd"; - mailbox = pkgs.writeText "mailbox" "mail@bogaledev.ru /"; - }; - settings.main = { - smtpd_sasl_type = "dovecot"; - smtpd_sasl_path = saslSocket; - smtp_sasl_auth_enable = "yes"; - smtpd_sasl_auth_enable = "yes"; - virtual_mailbox_base = mailBase; - virtual_uid_maps = "static:1819"; - virtual_gid_maps = "static:1819"; - smtp_tls_security_level = "encrypt"; - smtpd_tls_security_level = "encrypt"; - relayhost = [ "smtp.resend.com:2587" ]; - virtual_mailbox_domains = "bogaledev.ru"; - smtp_sasl_tls_security_options = "noanonymous"; - virtual_mailbox_maps = "hash:/etc/postfix/mailbox"; - smtp_sasl_password_maps = "hash:/etc/postfix/smtp_passwd"; - smtpd_tls_chain_files = [ - "${sslCertDir}/key.pem" - "${sslCertDir}/fullchain.pem" - ]; - }; - }; - }; } diff --git a/server/mods/mail.nix b/server/mods/mail.nix new file mode 100644 index 0000000..67b93a1 --- /dev/null +++ b/server/mods/mail.nix @@ -0,0 +1,75 @@ +{ config, lib, pkgs, ... }: +{ + services = let + mailBase = "/var/spool/mail/vmail"; + saslSocket = "/var/spool/postfix/private/auth"; + sslCertDir = config.security.acme.certs."bogaledev.ru".directory; + in { + redis.servers.rspamd = { + enable = true; + user = "rspamd"; + }; + rspamd = { + enable = true; + postfix.enable = true; + locals = { + "classifier-bayes.conf".text = "autolearn = true;"; + "redis.conf".text = '' + servers = "${config.services.redis.servers.rspamd.unixSocket}"; + ''; + }; + }; + dovecot2 = { + enable = true; + settings = { + ssl = "required"; + mail_gid = "vmail"; + mail_uid = "vmail"; + mail_path = mailBase; + protocols.imap = true; + mail_driver = "maildir"; + auth_mechanisms = [ "plain" ]; + dovecot_config_version = "2.4.5"; + dovecot_storage_version = "2.4.5"; + ssl_server_key_file = "${sslCertDir}/key.pem"; + ssl_server_cert_file = "${sslCertDir}/fullchain.pem"; + "passdb passwd-file".passwd_file_path = "/secrets/dovecot-pw"; + "service auth"."unix_listener ${saslSocket}" = { + mode = "0660"; + user = "postfix"; + group = "postfix"; + }; + }; + }; + postfix = { + enable = true; + enableSubmission = true; + virtualMapType = "regexp"; + virtual = "/.*@bogaledev.ru/ mail@bogaledev.ru"; + mapFiles = { + smtp_passwd = "/secrets/smtp_passwd"; + mailbox = pkgs.writeText "mailbox" "mail@bogaledev.ru /"; + }; + settings.main = { + smtpd_sasl_type = "dovecot"; + smtpd_sasl_path = saslSocket; + smtp_sasl_auth_enable = "yes"; + smtpd_sasl_auth_enable = "yes"; + virtual_mailbox_base = mailBase; + virtual_uid_maps = "static:1819"; + virtual_gid_maps = "static:1819"; + smtp_tls_security_level = "encrypt"; + smtpd_tls_security_level = "encrypt"; + relayhost = [ "smtp.resend.com:2587" ]; + virtual_mailbox_domains = "bogaledev.ru"; + smtp_sasl_tls_security_options = "noanonymous"; + virtual_mailbox_maps = "hash:/etc/postfix/mailbox"; + smtp_sasl_password_maps = "hash:/etc/postfix/smtp_passwd"; + smtpd_tls_chain_files = [ + "${sslCertDir}/key.pem" + "${sslCertDir}/fullchain.pem" + ]; + }; + }; + }; +} diff --git a/server/mods/sysd.nix b/server/mods/sysd.nix new file mode 100644 index 0000000..21b9f7a --- /dev/null +++ b/server/mods/sysd.nix @@ -0,0 +1,64 @@ +{ config, lib, pkgs, ... }: +{ + services.openssh = { + enable = true; + settings.PasswordAuthentication = false; + }; + systemd = { + tmpfiles.rules = [ + "d /var/spool/postfix 0755 postfix postfix -" + "d /var/spool/postfix/private 0755 postfix postfix -" + ]; + timers = { + tgbot-send = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "*-*-01 16:00:00"; + Persistent = true; + }; + }; + network-watchdog = { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnBootSec = 30; + AccuracySec = 1; + OnUnitActiveSec = 10; + }; + }; + }; + services = { + tgbot-send = { + serviceConfig.Type = "oneshot"; + script = '' + . /secrets/tgbot.env + ${pkgs.curl}/bin/curl "https://api.telegram.org/$BOT/sendMessage" \ + -X POST -d "chat_id=$CHAT_ID" -d "text=$TEXT" 2> /dev/null | \ + ${pkgs.jq}/bin/jq '.result.date |= strftime("%Y-%m-%d %H:%M:%S")' + ''; + }; + network-watchdog = { + serviceConfig = { + Type = "oneshot"; + LogLevelMax = "notice"; + }; + script = '' + if [ ! -e /run/network.failures ] || \ + ${pkgs.iputils}/bin/ping -c 1 -W 3 1.1.1.1 &> /dev/null; then + failures=0 + else + failures=$((1+$(cat /run/network.failures))) + fi + if [ $failures -gt 0 ]; then + echo "<5>failures = $failures" + fi + if [ $failures -ge 3 ]; then + echo "<4>restarting NetworkManager" + systemctl restart NetworkManager.service + failures=0 + fi + echo $failures > /run/network.failures + ''; + }; + }; + }; +} diff --git a/server/mods/web.nix b/server/mods/web.nix new file mode 100644 index 0000000..e0ceb10 --- /dev/null +++ b/server/mods/web.nix @@ -0,0 +1,88 @@ +{ config, lib, pkgs, ... }: +{ + security.acme = { + acceptTerms = true; + defaults.email = "letsencrypt@bogaledev.ru"; + certs."bogaledev.ru" = { + validMinDays = 3; + dnsProvider = "cloudflare"; + extraDomainNames = [ "*.bogaledev.ru" ]; + credentialFiles = { "CF_DNS_API_TOKEN_FILE" = "/secrets/cf-token"; }; + reloadServices = [ "nginx.service" "dovecot.service" "postfix.service" ]; + }; + }; + services = let + sslCertDir = config.security.acme.certs."bogaledev.ru".directory; + in { + postgresql.enable = true; + phpfpm.pools.default = { + user = "php"; + group = "php"; + settings = { + "pm" = "ondemand"; + "pm.max_children" = 8; + "listen.owner" = "nginx"; + "listen.group" = "nginx"; + }; + }; + forgejo = { + enable = true; + database.type = "postgres"; + settings = { + service.DISABLE_REGISTRATION = true; + server = { + HTTP_PORT = 8039; + ROOT_URL = "https://bogaledev.ru/git/"; + }; + }; + }; + vaultwarden = { + enable = true; + dbBackend = "postgresql"; + configurePostgres = true; + package = pkgs.vaultwarden-postgresql; + environmentFile = "/secrets/vw-token.env"; + config = { + ROCKET_PORT = 8032; + SIGNUPS_ALLOWED = false; + TRASH_AUTO_DELETE_DAYS = 90; + PASSWORD_HINTS_ALLOWED = false; + EMERGENCY_ACCESS_ALLOWED = false; + DOMAIN = "https://bogaledev.ru/vw/"; + }; + }; + nginx.virtualHosts."bogaledev.ru" = let + phpPool = pool: '' + location ~ \.php$ { + fastcgi_pass unix:${config.services.phpfpm.pools.${pool}.socket}; + } + ''; + localNetworks = '' + allow fc00::/64; + allow fc01::/64; + allow fc02::/64; + allow 10.0.0.0/16; + allow 10.1.0.0/16; + allow 10.2.0.0/16; + deny all; + ''; + in { + quic = true; + default = true; + forceSSL = true; + root = "/srv/http"; + useACMEHost = "bogaledev.ru"; + extraConfig = ''add_header Alt-Svc 'h3=":443"; ma=86400' always;''; + locations = { + "/app/".extraConfig = phpPool "default"; + "/git/".proxyPass = "http://127.0.0.1:8039/"; + "/priv/".extraConfig = ''${localNetworks} ${phpPool "default"}''; + "/vw/" = { + proxyWebsockets = true; + extraConfig = localNetworks; + proxyPass = "http://127.0.0.1:8032"; + }; + }; + }; + }; +}