diff --git a/host/fermi.nix b/host/fermi.nix index 4b120a8..e387f58 100644 --- a/host/fermi.nix +++ b/host/fermi.nix @@ -27,6 +27,9 @@ in { aftgraphs = true; blog = true; searx.enable = true; + + ip = "170.130.165.174"; + ipv6 = "2a0b:7140:8:1:5054:ff:fe84:ed8c"; }; znc.enable = true; diff --git a/host/opt/www/blog.nix b/host/opt/www/blog.nix index 87931d7..b1a4ccc 100644 --- a/host/opt/www/blog.nix +++ b/host/opt/www/blog.nix @@ -29,34 +29,31 @@ in { forceSSL = true; useACMEHost = cfg.hostname; - locations = - { - "/".tryFiles = "$uri $uri/ =404"; + locations = { + "/".tryFiles = "$uri $uri/ =404"; - "/searx/${cfg.searx.subdomain}".return = mkIf cfg.searx.enable "https://${cfg.searx.subdomain}.${cfg.hostname}/static/?$args"; - "/searx".return = mkIf cfg.searx.enable "https://${cfg.searx.subdomain}.${cfg.hostname}/?$args"; + "/searx/".return = mkIf cfg.searx.enable "https://${cfg.searx.subdomain}.${cfg.hostname}/?$args"; - "/advent2023/" = mkIf cfg.adventofcode { - alias = "${cfg.root}/advent2023/"; - extraConfig = '' - fancyindex on; - fancyindex_exact_size off; - fancyindex_localtime on; - ''; - }; + "/advent2023/" = mkIf cfg.adventofcode { + alias = "${cfg.root}/advent2023/"; + extraConfig = '' + fancyindex on; + fancyindex_exact_size off; + fancyindex_localtime on; + ''; + }; - "/aftgraphs/" = mkIf cfg.aftgraphs { - alias = "${cfg.root}/simulations/"; - extraConfig = '' - fancyindex on; - fancyindex_exact_size off; - fancyindex_localtime on; - add_header "Cross-Origin-Opener-Policy" "same-origin"; - add_header "Cross-Origin-Embedder-Policy" "require-corp"; - ''; - }; - } - // cfg.acme-location-block; + "/aftgraphs/" = mkIf cfg.aftgraphs { + alias = "${cfg.root}/simulations/"; + extraConfig = '' + fancyindex on; + fancyindex_exact_size off; + fancyindex_localtime on; + add_header "Cross-Origin-Opener-Policy" "same-origin"; + add_header "Cross-Origin-Embedder-Policy" "require-corp"; + ''; + }; + }; }; }; } diff --git a/host/opt/www/default.nix b/host/opt/www/default.nix index d974162..0e06192 100644 --- a/host/opt/www/default.nix +++ b/host/opt/www/default.nix @@ -19,6 +19,15 @@ in { type = lib.types.str; }; + ip = mkOption { + default = ""; + type = lib.types.str; + }; + ipv6 = mkOption { + default = ""; + type = lib.types.str; + }; + root = mkOption { default = "/srv"; type = lib.types.str; @@ -34,46 +43,70 @@ in { type = lib.types.str; }; - acme-location-block = mkOption { - default = { - "^~ /.well-known/acme-challenge".extraConfig = '' - location ^~ /.well-known/acme-challenge/ { - default_type "text/plain"; - root ${cfg.root}/acme; - } - ''; - }; - readOnly = true; + keys = mkOption { + default = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMmFgG1EuQDoJb8pQcxnhjqbncrpJGZ3iNon/gu0bXiE aftix@aftix.xyz" + ]; }; }; config = { + sops.secrets = { + porkbun_api_key = { + inherit (cfg) group; + owner = cfg.user; + }; + porkbun_secret_api_key = { + inherit (cfg) group; + owner = cfg.user; + }; + }; + users = { users.${cfg.user} = { inherit (cfg) group; password = ""; shell = "/run/current-system/sw/bin/nologin"; isSystemUser = true; + home = cfg.root; + openssh.authorizedKeys.keys = cfg.keys; }; groups.${cfg.group} = {}; }; networking.firewall = { - allowedTCPPorts = [80 443]; - allowedUDPPorts = [80 443]; + allowedTCPPorts = [53 80 443]; + allowedUDPPorts = [53 80 443]; }; - services.nginx = { - inherit (cfg) user group; - enable = true; - enableReload = true; + services = { + nginx = { + inherit (cfg) user group; + enable = true; + enableReload = true; + + additionalModules = with pkgs.nginxModules; [fancyindex]; - additionalModules = with pkgs.nginxModules; [fancyindex]; + virtualHosts."auth.${cfg.hostname}" = { + serverName = "auth.${cfg.hostname} www.auth.${cfg.hostname}"; + kTLS = true; + forceSSL = true; + useACMEHost = cfg.hostname; - appendHttpConfig = '' - limit_req_zone $binary_remote_addr zone=put_request_by_addr:20m rate=100r/s; - ''; + locations."/" = { + proxyPass = "http://[::1]:4443/"; + extraConfig = '' + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + ''; + }; + }; + + appendHttpConfig = '' + limit_req_zone $binary_remote_addr zone=put_request_by_addr:20m rate=100r/s; + ''; + }; }; systemd.tmpfiles.rules = [ @@ -83,14 +116,23 @@ in { security.acme = { acceptTerms = true; + defaults = { email = "aftix@aftix.xyz"; - webroot = cfg.root + "/acme"; + dnsProvider = "porkbun"; + credentialFiles = { + PORKBUN_SECRET_API_KEY_FILE = config.sops.secrets.porkbun_secret_api_key.path; + PORKBUN_API_KEY_FILE = config.sops.secrets.porkbun_api_key.path; + }; }; certs.${cfg.hostname} = { inherit (cfg) group; - extraDomainNames = ["www.${cfg.hostname}"]; + extraDomainNames = [ + "www.${cfg.hostname}" + "auth.${cfg.hostname}" + "www.auth.${cfg.hostname}" + ]; }; }; }; diff --git a/host/opt/www/searx.nix b/host/opt/www/searx.nix index a594c4d..89dd4e2 100644 --- a/host/opt/www/searx.nix +++ b/host/opt/www/searx.nix @@ -7,7 +7,7 @@ inherit (lib.options) mkOption mkEnableOption; wwwCfg = config.my.www; cfg = wwwCfg.searx; - socket = "/run/uwsgi/app/searx/socket"; + socket = "/run/uwsgi/socket"; in { options.my.www.searx = { enable = mkEnableOption "searx"; @@ -35,9 +35,7 @@ in { systemd.tmpfiles.rules = let inherit (config.services.searx.uwsgiConfig) immediate-uid immediate-gid; in [ - "d /run/uwsgi 0775 ${immediate-uid} ${immediate-gid} -" - "d /run/uwsgi/app 0775 ${immediate-uid} ${immediate-gid} -" - "d /run/uwsgi/app/searx 0775 ${immediate-uid} ${immediate-gid} -" + "d ${builtins.baseNameOf socket} 0775 ${immediate-uid} ${immediate-gid} -" ]; environment.etc."nginx/uwsgi_params".text = '' @@ -63,22 +61,19 @@ in { services = { nginx.virtualHosts."${cfg.subdomain}.${wwwCfg.hostname}" = { - root = "${config.services.searx.package}/share"; serverName = "${cfg.subdomain}.${wwwCfg.hostname} www.${cfg.subdomain}.${wwwCfg.hostname}"; kTLS = true; forceSSL = true; useACMEHost = wwwCfg.hostname; - locations = - { - "/".extraConfig = '' - include uwsgi_params; - uwsgi_pass unix:${socket}; - ''; + locations = { + "/".extraConfig = '' + include uwsgi_params; + uwsgi_pass unix:${socket}; + ''; - "/static/".alias = "${config.services.searx.package}/share/static"; - } - // wwwCfg.acme-location-block; + "/static/".alias = "${config.services.searx.package}/share/static/"; + }; }; redis.servers.searx.user = mkForce config.services.searx.uwsgiConfig.immediate-uid; @@ -96,12 +91,11 @@ in { environmentFile = config.sops.templates."searx.env".path; settings = { - use_default_settings = mkForce false; instance_name = "searx"; contact_url = "mailto:aftix@aftix.xyz"; server = { secret_key = "@SEARX_SECRET_KEY@"; - base_url = "${cfg.subdomain}.${wwwCfg.hostname}"; + base_url = "/"; image_proxy = true; }; }; diff --git a/host/opt/www/znc.nix b/host/opt/www/znc.nix index 48ea154..1c4805c 100644 --- a/host/opt/www/znc.nix +++ b/host/opt/www/znc.nix @@ -34,56 +34,60 @@ in { }; }; + security.acme.certs.${hostname}.extraDomainNames = [ + "${subdomain}.${hostname}" + "www.${subdomain}.${hostname}" + ]; + systemd = { - services = { - znc-init = { - description = "Initialize znc settings"; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - User = cfg.user; - Group = cfg.group; - RuntimeDirectory = cfg.dataDir; - RuntimeDirectoryMode = "750"; - NoNewPrivileges = true; - ProtectSystem = "strict"; - ReadWritePaths = cfg.dataDir; - ProtectHome = true; - StateDirectory = cfg.dataDir; - StateDirectoryMode = "755"; - PrivateTmp = true; - ProtectHostname = true; - ProtectClock = true; - ProtectKernelTunables = true; - ProtectKernelModules = true; - ProtectKernelLogs = true; - ProtectControlGroups = true; - RestrictNamespaces = true; - RestrictRealtime = true; - RestrictSUIDSGID = true; - RemoveIPC = true; - PrivateNetwork = true; - }; - script = let - passwordSecretPath = escapeShellArg config.sops.secrets.znc_password.path; - passwordSaltSecretPath = escapeShellArg config.sops.secrets.znc_passwordsalt.path; - twitchOauthSecretPath = escapeShellArg config.sops.secrets.znc_twitchoauth.path; - in '' - cd ${cfg.dataDir}/configs || exit 1 - [[ -f ${passwordSecretPath} ]] || exit 1 - [[ -f ${passwordSaltSecretPath} ]] || exit 1 - [[ -f ${twitchOauthSecretPath} ]] || exit 1 - ${pkgs.gnused}/bin/sed -i"" -e "s/PASSWORD/$(cat ${passwordSecretPath})/g" znc.conf - ${pkgs.gnused}/bin/sed -i"" -e "s/PASSWORD/$(cat ${passwordSaltSecretPath})/g" znc.conf - ${pkgs.gnused}/bin/sed -i"" -e "s/PASSWORD/$(cat ${twitchOauthSecretPath})/g" znc.conf - ''; - }; + tmpfiles.rules = [ + "d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} -" + "d ${cfg.dataDir}/configs 0750 ${cfg.user} ${cfg.group} -" + ]; - znc = { - requires = ["znc-init.service"]; - after = ["znc-init.service"]; + services.znc.preStart = let + inherit (lib.strings) optionalString; + modules = pkgs.buildEnv { + name = "znc-modules"; + paths = config.services.znc.modulePackages; }; - }; + passwordSecretPath = escapeShellArg config.sops.secrets.znc_password.path; + passwordSaltSecretPath = escapeShellArg config.sops.secrets.znc_passwordsalt.path; + twitchOauthSecretPath = escapeShellArg config.sops.secrets.znc_twitchoauth.path; + in + lib.mkForce '' + mkdir -p ${cfg.dataDir}/configs + + # If mutable, regenerate conf file every time. + ${optionalString (!cfg.mutable) '' + echo "znc is set to be system-managed. Now deleting old znc.conf file to be regenerated." + rm -f ${cfg.dataDir}/configs/znc.conf + ''} + + # Ensure essential files exist. + if [[ ! -f ${cfg.dataDir}/configs/znc.conf ]]; then + echo "No znc.conf file found in ${cfg.dataDir}. Creating one now." + cp --no-preserve=ownership --no-clobber ${cfg.configFile} ${cfg.dataDir}/configs/znc.conf + chmod u+rw ${cfg.dataDir}/configs/znc.conf + fi + + if [[ ! -f ${cfg.dataDir}/znc.pem ]]; then + echo "No znc.pem file found in ${cfg.dataDir}. Creating one now." + ${pkgs.znc}/bin/znc --makepem --datadir ${cfg.dataDir} + fi + + # Symlink modules + rm ${cfg.dataDir}/modules || true + ln -fs ${modules}/lib/znc ${cfg.dataDir}/modules + + # Insert secrets + [[ -f ${passwordSecretPath} ]] || exit 1 + [[ -f ${passwordSaltSecretPath} ]] || exit 1 + [[ -f ${twitchOauthSecretPath} ]] || exit 1 + mv ${cfg.dataDir}/configs/znc.conf ${cfg.dataDir}/configs/znc.conf.old + ${pkgs.gnused}/bin/sed -e "s/__PASSWORD__/$(cat ${passwordSecretPath})/g" -e "s/__SALT__/$(cat ${passwordSaltSecretPath})/g" -e "s/__TWITCHOAUTH__/$(cat ${twitchOauthSecretPath})/g" ${cfg.dataDir}/configs/znc.conf.old > ${cfg.dataDir}/configs/znc.conf + rm ${cfg.dataDir}/configs/znc.conf.old + ''; }; services = { @@ -94,32 +98,34 @@ in { forceSSL = true; useACMEHost = hostname; - locations = - { - "/" = { - proxyPass = "http://[[::1]]:7001/"; - extraConfig = '' - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - ''; - }; - } - // config.my.www.acme-location-block; + locations."/" = { + proxyPass = "http://[::1]:7001/"; + extraConfig = '' + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + ''; + }; }; streamConfig = '' upstream znc { - server [::1]::7000; + server [::1]:7000; } server { - listen 0.0.0.0:6697 http2 ssl; - listen [::0]:6697 http2 ssl; + listen 0.0.0.0:6697 ssl; + listen [::0]:6697 ssl; ssl_certificate ${config.security.acme.certs.${hostname}.directory}/fullchain.pem; ssl_certificate_key ${config.security.acme.certs.${hostname}.directory}/key.pem; ssl_trusted_certificate ${config.security.acme.certs.${hostname}.directory}/chain.pem; ssl_conf_command Options KTLS; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256; + ssl_ecdh_curve secp384r1; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:https:15m; + ssl_session_timeout 15m; proxy_pass znc; } @@ -172,6 +178,8 @@ in { Port = 7001; URIPrefix = "/"; }; + + l = lib.mkForce null; }; User.aftix = { @@ -191,7 +199,7 @@ in { JoinTries = 10; MaxJoins = 0; MaxNetworks = 1; - LoadModul = [ + LoadModule = [ "chansaver" "controlpanel" "dcc" @@ -216,7 +224,6 @@ in { "keepnick" "savebuff" "clientbuffer" - "autoadd" "route_replies" ]; FloodBurst = 9; @@ -254,15 +261,15 @@ in { twitch = { - Server = "irc.chat.twitch.tv 6667 TWITCHOAUTH"; + Server = "irc.chat.twitch.tv 6667 __TWITCHOAUTH__"; } // network; }; Pass.password = { - Hash = "PASSWORD"; + Hash = "__PASSWORD__"; Method = "SHA256"; - Salt = "PASSWORDSALT"; + Salt = "__SALT__"; }; }; }; diff --git a/host/srv_secrets.yaml b/host/srv_secrets.yaml index 7c5198a..da9de6d 100644 --- a/host/srv_secrets.yaml +++ b/host/srv_secrets.yaml @@ -1,7 +1,9 @@ searx_key: ENC[AES256_GCM,data:wfStkyrrZ7/0hPfjUab1K7xaSkH1FMSWp/OLBa/WjNs=,iv:GRoBObX9LRM0Ub/BaIQZftSMojMLOyE2GqOmQzE0uQg=,tag:PwZtesniRnXIQmOHY6tw1w==,type:str] -znc_password: ENC[AES256_GCM,data:lv7zKC8wssxtRFOiKEVahygLAwKvr4YAjGNivtaXRPEBMncrjRLxyN3O4X7i9Oqkc/635e+T4zvlPiDBUguCGw==,iv:NjWi9PwO7c15HDkwIGC5b9vcSuXMTGBj/O6IU4BLrno=,tag:usEVfvjEmtyaoo+oZDZCsg==,type:str] -znc_passwordsalt: ENC[AES256_GCM,data:9GfoPVTmvwkkGBK1DvgILZUbuYE=,iv:80W1BY6yQrWaqD7NUlGDpX5goPIlbqLueuHaY2CurA8=,tag:SikIr2iCBNrPdwkxTofg2A==,type:str] +znc_password: ENC[AES256_GCM,data:aCfUc5Xsx4OBOePvqgV+eT9meEqMB70QiIIWArcI46tWMjQzcbAWdIy3vZsnRgOYMR/Pm41HXR7X5TtwBqPUAg==,iv:vP29x2yfLwd5TJCCQbQcN3OwFQQ1/fPhwoaq4F3P7vc=,tag:E6RqH1R8w1aelJ4h2UQ1Wg==,type:str] +znc_passwordsalt: ENC[AES256_GCM,data:3adQTS7tM1d51sJ2hHlYt+htpDI=,iv:eZNIXLe5vJ4XNhZD1ltsJR/2kr83kwnm/N7GLYCzRmk=,tag:h/toK5JpoFUndP5kDWbZiA==,type:str] znc_twitchoauth: ENC[AES256_GCM,data:FZdP1WHzscMrWoCZyD+Dn3N+LPdvOL1022hkHsbpXH86QB4y,iv:7s3CDR2IFQJsAQKz1ofNPP6YyReJZs6oGTpe+TfLLOY=,tag:uI/ZDqmdDJ96VtUUWCUiMw==,type:str] +porkbun_api_key: ENC[AES256_GCM,data:pQanyA+CJuO7sScUHcVOz1IQ8ceTlsDpoHoNcqbEqB9MlVLXaXoB3Bk0VErGosqxveD02TWU2QE9NZ+ywRsgLh8MLfY=,iv:i8DBZ2GiFVyanGN6R7eBXmiYFCYNSKzgotgl568zBwk=,tag:d20mSYPIsAuDugW/PvOprQ==,type:str] +porkbun_secret_api_key: ENC[AES256_GCM,data:qH4oHIWpJp4UbKC35/LjbPrKs7SfugOIabOObmjG2jM53hZ63Qgnjo526aHdJCHZ9/vp2VwryOAB+PGFD+vXyxeNXgs=,iv:VtX6PookE++m9OqeArJEEgJc2fVJguWiTErGXIooLps=,tag:R2AONGLn6pgrp8zLTB/9aA==,type:str] sops: kms: [] gcp_kms: [] @@ -35,8 +37,8 @@ sops: Y1A3cVp1ZjlQTFlNYXpCNU0vdFo2K2cKW6HKJa8xsl+dPiU/pn/e4OzdYl1dAd2E I80O0jS0TQ/8Ifjr/a4V1f911/ShSWiAis8mHEa4wf3pVJ26CI9D9Q== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-05-12T18:44:09Z" - mac: ENC[AES256_GCM,data:GwLhZj83Pbz6dk1mXxiAgKWHf8hJ7YBZZrro2yqxOgxbdi6hIwMTAWoBmw4SEKGKhFER7YSY4jCSS4oyZUhP9HIdvCaqMc5u8rC+2UO3nlLsTTLB21vp5QBm0x5/CGH8K4r1S8Xi9bHzdb39+goI9sIARlNkD0fcS02jgb92ErA=,iv:emRl+fQ+CpcpaCcwZKGEBd0ckW6XyDUmrl9XBa+n+zc=,tag:SXcmB4SKp9hy31Yo6KcV9Q==,type:str] + lastmodified: "2024-05-17T23:33:14Z" + mac: ENC[AES256_GCM,data:WzQn940UZHTG6J/rc2eiVzUW5joa7OA5oG6Ld1ka533/ke42G+wlGsj3MAOv1LNAqWrBM/cjYoG70TpkZjuH3B/NhOBD5ddOYHwQeuqFYtOCXjOHMeDozNhLQUCx9xJtWJh4lPc2ldsrm8bfVRv/e3dyCu1+WJILfa4Bb5YgcX8=,iv:sl4fofj6ZOAEf4SSg4nffV7mErmKpkIoxW3Mb7W/NSw=,tag:ZowJeuJmY53JSyK83oQNPw==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1