infrastructure

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 93721815ecbc5db950c107cd1aeb78bbb735f8f8
parent 705e8c575acea8b494365ff2ea5db41ed5ab4bcb
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sun, 12 Apr 2026 16:28:56 +0200

chore: refactor into hosts and modules, use proper host names

Diffstat:
M.forgejo/workflows/nixos.yaml | 7++++---
M.sops.yaml | 8++++----
MMakefile | 14++++++++------
Mflake.nix | 10+++++-----
Rnixos/gaia/configuration.nix -> hosts/gaia/configuration.nix | 0
Rnixos/gaia/hardware-configuration.nix -> hosts/gaia/hardware-configuration.nix | 0
Rnixos/gaia/home.nix -> hosts/gaia/home.nix | 0
Rnixos/gaia/hyprland.nix -> hosts/gaia/hyprland.nix | 0
Rnixos/gaia/vpn.nix -> hosts/gaia/vpn.nix | 0
Ahosts/helios.nix | 366+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahosts/poseidon.nix | 199+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rnixos/simple-web-app.nix -> modules/simple-web-app.nix | 0
Dnixos/debian-2gb-hel1-1.nix | 366-------------------------------------------------------------------------------
Dnixos/nixos-s-1vcpu-512mb-10gb-ams3-01.nix | 199-------------------------------------------------------------------------------
14 files changed, 586 insertions(+), 583 deletions(-)

diff --git a/.forgejo/workflows/nixos.yaml b/.forgejo/workflows/nixos.yaml @@ -14,8 +14,8 @@ jobs: - name: Build run: | - if [[ "$(nix eval .#nixosConfigurations.debian-2gb-hel1-1.pkgs.system)" = '"x86_64-linux"' ]]; then - nix build .#nixosConfigurations.debian-2gb-hel1-1.config.system.build.toplevel + if [[ "$(nix eval .#nixosConfigurations.helios.pkgs.system)" = '"x86_64-linux"' ]]; then + nix build .#nixosConfigurations.helios.config.system.build.toplevel fi - name: Check @@ -26,4 +26,5 @@ jobs: env: NIX_SSHOPTS: "-i /var/lib/gitea-runner/default/.ssh/id_ed25519 -o UserKnownHostsFile=/var/lib/gitea-runner/default/.ssh/known_hosts" run: | - nix run nixpkgs#nixos-rebuild -- --fast --flake .#debian-2gb-hel1-1 --target-host root@46.62.150.48 test + nix run nixpkgs#nixos-rebuild -- --fast --flake .#poseidon --target-host root@188.166.127.72 test + nix run nixpkgs#nixos-rebuild -- --fast --flake .#helios --target-host root@46.62.150.48 test diff --git a/.sops.yaml b/.sops.yaml @@ -2,8 +2,8 @@ keys: - &users: - &desktop age1s29qgfupgx7mqn9dk2rndp6xh63f5qan8vqa77ve603wd5m9a9rqkpq7re - &hosts: - - &server age1e52v0cr69sycuh59hksx9nk5327vgkemkxqwngd25gtztxhkpups87yd5g - - &hetzner age1damatvmepexkts3ayg2rfac0nal24tghxnrrvvmpu76wa4gvd58qyn2nwc + - &poseidon age1e52v0cr69sycuh59hksx9nk5327vgkemkxqwngd25gtztxhkpups87yd5g + - &helios age1damatvmepexkts3ayg2rfac0nal24tghxnrrvvmpu76wa4gvd58qyn2nwc - &gaia age173q4ncu9rux9ufx54vp0r9369qqkaz4z7nwsyqsyp0whkvv2rynqnnt93w creation_rules: - path_regex: secrets.yaml @@ -11,5 +11,5 @@ creation_rules: - age: - *desktop - *gaia - - *server - - *hetzner + - *poseidon + - *helios diff --git a/Makefile b/Makefile @@ -1,11 +1,13 @@ -.PHONY: deploy-digitalocean deploy-hetzner deploy-gaia +.PHONY: deploy deploy-poseidon deploy-helios deploy-gaia -## Deploy configuration to production server -deploy-digitalocean: - nixos-rebuild test --show-trace --no-reexec --flake .#nixos-s-1vcpu-512mb-10gb-ams3-01 --target-host root@188.166.127.72 +## Deploy configuration to production servers +deploy: deploy-poseidon deploy-helios -deploy-hetzner: - nixos-rebuild test --show-trace --no-reexec --flake .#debian-2gb-hel1-1 --target-host root@46.62.150.48 +deploy-poseidon: + nixos-rebuild test --show-trace --no-reexec --flake .#poseidon --target-host root@188.166.127.72 + +deploy-helios: + nixos-rebuild test --show-trace --no-reexec --flake .#helios --target-host root@46.62.150.48 deploy-gaia: sudo nixos-rebuild switch --flake .#gaia diff --git a/flake.nix b/flake.nix @@ -34,14 +34,14 @@ }; in { - nixosConfigurations.nixos-s-1vcpu-512mb-10gb-ams3-01 = nixpkgs.lib.nixosSystem { + nixosConfigurations.poseidon = nixpkgs.lib.nixosSystem { inherit system; modules = [ sops-nix.nixosModules.sops - (import ./nixos/nixos-s-1vcpu-512mb-10gb-ams3-01.nix) + (import ./hosts/poseidon.nix) ]; }; - nixosConfigurations.debian-2gb-hel1-1 = nixpkgs.lib.nixosSystem { + nixosConfigurations.helios = nixpkgs.lib.nixosSystem { inherit system; modules = [ sops-nix.nixosModules.sops @@ -50,7 +50,7 @@ pkgs, ... }: - import ./nixos/debian-2gb-hel1-1.nix (inputs // { inherit simple-web-app mkv website; }) + import ./hosts/helios.nix (inputs // { inherit simple-web-app mkv website; }) ) ]; }; @@ -59,7 +59,7 @@ specialArgs = { inherit pkgs-unstable; }; modules = [ sops-nix.nixosModules.sops - ./nixos/gaia/configuration.nix + ./hosts/gaia/configuration.nix home-manager.nixosModules.home-manager ]; }; diff --git a/nixos/gaia/configuration.nix b/hosts/gaia/configuration.nix diff --git a/nixos/gaia/hardware-configuration.nix b/hosts/gaia/hardware-configuration.nix diff --git a/nixos/gaia/home.nix b/hosts/gaia/home.nix diff --git a/nixos/gaia/hyprland.nix b/hosts/gaia/hyprland.nix diff --git a/nixos/gaia/vpn.nix b/hosts/gaia/vpn.nix diff --git a/hosts/helios.nix b/hosts/helios.nix @@ -0,0 +1,366 @@ +{ + pkgs, + config, + lib, + simple-web-app, + mkv, + website, + ... +}: +{ + # Generic + system.stateVersion = "25.05"; # Do not change lightly! + environment.systemPackages = with pkgs; [ + vim + wget + curl + rsync + rclone + zip + unzip + ]; + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + services.openssh.enable = true; + networking.firewall = { + allowedTCPPorts = [ + 80 + 443 + ]; + # allowedUDPPorts = [ + # 10232 + # ]; + }; + + boot.loader.grub = { + enable = false; + }; + fileSystems."/" = { + device = "/dev/sda1"; + fsType = "ext4"; + }; + fileSystems."/mnt/volume-hel1-1" = { + device = "/dev/sdb"; + fsType = "ext4"; + }; + nix.gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + nix.settings.auto-optimise-store = true; + + # Specific + networking.hostName = "helios"; + time.timeZone = "Europe/Helsinki"; + + imports = [ + # ../modules/simple-web-app.nix + + # (builtins.fetchTarball { + # # Pick a release version you are interested in and set its hash, e.g. + # url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-25.05/nixos-mailserver-nixos-25.05.tar.gz"; + # # To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command: + # # release="nixos-25.05"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack + # sha256 = "0la8v8d9vzhwrnxmmyz3xnb6vm76kihccjyidhfg6qfi3143fiwq"; + # }) + ]; + + sops.defaultSopsFile = ../secrets.yaml; + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + sops.secrets.hetzner_wireguard_key = { + restartUnits = [ "wg-quick-wg0.service" ]; + }; + sops.secrets.mediawiki_admin_password = { + owner = "mediawiki"; + mode = "0400"; + }; + + networking.wg-quick.interfaces = { + wg0 = { + address = [ "10.100.0.7/24" ]; + dns = [ "10.100.0.1" ]; + listenPort = 10232; + privateKeyFile = "/run/secrets/hetzner_wireguard_key"; + peers = [ + { + publicKey = "2jWfr5UmACvuS+0HOfSNgEUYqoqVNnfVDoaatmgEykw="; + allowedIPs = [ "10.100.0.0/24" ]; + endpoint = "188.166.127.72:51820"; + persistentKeepalive = 25; + } + ]; + }; + }; + + users.users = { + root.openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPGZOv4PvL/NvBiDFnCh8V6xm03k/C3b8O82z1+tmboI gitea-runner" + ]; + silas = { + isNormalUser = true; + description = "Silas Brack"; + home = "/home/silas"; + extraGroups = [ + "networkmanager" + "wheel" + ]; # "keys" + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZ0gNM0od3EE/LL27kYQ/s5MOU7LBNc0pWRWROeINij silas@Lorenzas-MacBook-Pro.local" # Lolo's Macbook Pro + ]; + }; + }; + + security.acme = { + acceptTerms = true; + defaults.email = "silasbrack@gmail.com"; + }; + services.nginx.enable = true; + services.nginx.recommendedGzipSettings = true; + services.nginx.recommendedBrotliSettings = true; # Better compression than gzip for modern browsers + services.nginx.recommendedOptimisation = true; # sendfile, tcp_nopush, tcp_nodelay, keepalive + services.nginx.recommendedTlsSettings = true; # Strong TLS defaults + services.nginx.recommendedProxySettings = true; # Good defaults for proxied services (Forgejo) + + # Rate limiting zone (10 requests/second per IP, 10MB zone) + services.nginx.commonHttpConfig = '' + limit_req_zone $binary_remote_addr zone=ratelimit:10m rate=10r/s; + ''; + services.nginx.appendHttpConfig = '' + limit_req_status 429; + ''; + + # Catch-all for unknown hostnames + services.nginx.virtualHosts."_" = { + default = true; + rejectSSL = true; + locations."/" = { + return = "444"; + }; + }; + + # MediaWiki + services.mediawiki = { + enable = true; + name = "Silas' Wiki"; + webserver = "nginx"; + passwordFile = config.sops.secrets.mediawiki_admin_password.path; + database = { + type = "postgres"; + createLocally = true; + }; + nginx.hostName = "wiki.fnarglebeast.com"; + uploadsDir = "/var/lib/mediawiki/uploads"; + extensions = { + ParserFunctions = null; + }; + extraConfig = '' + $wgEnableUploads = true; + $wgUseImageMagick = true; + + # Security: disable reading by anonymous users + $wgGroupPermissions['*']['read'] = false; + + # Security: disable anonymous editing (require login) + $wgGroupPermissions['*']['edit'] = false; + $wgGroupPermissions['*']['createpage'] = false; + $wgGroupPermissions['*']['createtalk'] = false; + + # Security: disable account creation by anonymous users + $wgGroupPermissions['*']['createaccount'] = false; + + # Security: restrict upload file types + $wgFileExtensions = ['png', 'gif', 'jpg', 'jpeg', 'webp', 'pdf']; + ''; + }; + services.nginx.virtualHosts."wiki.fnarglebeast.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + limit_req zone=ratelimit burst=20 nodelay; + ''; + }; + + # Forgejo + services.forgejo = { + enable = true; + database.type = "postgres"; + lfs.enable = true; + settings = { + server = { + DOMAIN = "git.fnarglebeast.com"; + ROOT_URL = "https://git.fnarglebeast.com/"; + HTTP_PORT = 3001; + }; + service = { + DISABLE_REGISTRATION = true; + }; + session = { + COOKIE_SECURE = true; + }; + }; + }; + services.nginx.virtualHosts."git.fnarglebeast.com" = { + enableACME = true; + forceSSL = true; + extraConfig = '' + limit_req zone=ratelimit burst=20 nodelay; + ''; + locations."/" = { + proxyPass = "http://127.0.0.1:3001"; + proxyWebsockets = true; + }; + }; + + # Personal website + services.nginx.virtualHosts."silasbrack.com" = let + staticSite = pkgs.runCommand "website" {} '' + mkdir -p $out + cp ${website}/index.html $out/ + cp ${website}/style.css $out/ + cp ${website}/style.min.css $out/ + cp -r ${website}/posts $out/ + cp -r ${website}/assets $out/ + ''; + in { + enableACME = true; + forceSSL = true; + root = staticSite; + extraConfig = '' + limit_req zone=ratelimit burst=20 nodelay; + ''; + locations."/" = { + index = "index.html"; + extraConfig = '' + try_files $uri $uri.html $uri/ /index.html; + ''; + }; + }; + # services.simple-web-app = { + # enable = true; + # package = simple-web-app.packages."x86_64-linux".default; + # port = 8032; + # }; + + # services.karakeep = { + # enable = true; + # extraEnvironment = { + # PORT = "8360"; + # DISABLE_SIGNUPS = "true"; + # }; + # }; + # services.nginx.virtualHosts."karakeep.fnarglebeast.com" = { + # forceSSL = true; + # enableACME = true; + # locations."/" = { + # proxyPass = "http://127.0.0.1:8360"; + # }; + # }; + + # services.nginx.virtualHosts."mail.silasbrack.com" = { + # forceSSL = true; + # enableACME = true; + # locations."/" = { + # proxyPass = ""; + # }; + # }; + # mailserver = { + # enable = true; + # fqdn = "mail.silasbrack.com"; + # domains = [ "silasbrack.com" ]; + # + # # Use Let's Encrypt certificates. Note that this needs to set up a stripped + # # down nginx and opens port 80. + # certificateScheme = "acme-nginx"; + # + # # A list of all login accounts. To create the password hashes, use + # # nix-shell -p mkpasswd --run 'mkpasswd -s' + # loginAccounts = { + # "test@silasbrack.com" = { + # hashedPasswordFile = "/home/silas/.test.txt"; + # # aliases = [ "postmaster@example.com" ]; + # }; + # }; + # }; + + # Create mkv volume directory + systemd.tmpfiles.rules = [ + "d /mnt/volume-hel1-1/mkv 0755 nginx nginx -" + ]; + + # mkv key-value store + systemd.services.mkv = { + description = "mkv distributed key-value store"; + wantedBy = [ "multi-user.target" ]; + after = [ + "network-online.target" + "wg-quick-wg0.service" + "nginx.service" + ]; + wants = [ "network-online.target" ]; + requires = [ "wg-quick-wg0.service" ]; + environment = { + MKV_DB = "/var/lib/mkv/index.db"; + MKV_VOLUMES = "http://10.100.0.7:8081,http://10.100.0.4:8081"; + MKV_REPLICAS = "2"; + MKV_PORT = "3000"; + }; + serviceConfig = { + Type = "simple"; + DynamicUser = true; + StateDirectory = "mkv"; + ExecStart = "${mkv.packages.${pkgs.system}.default}/bin/mkv serve"; + Restart = "on-failure"; + RestartSec = 5; + }; + }; + + # nginx WebDAV volume server for mkv (VPN only) + services.nginx.virtualHosts."mkv-volume" = { + listen = [ + { + addr = "10.100.0.7"; + port = 8081; + } + ]; + root = "/mnt/volume-hel1-1/mkv"; + extraConfig = '' + autoindex on; + autoindex_format json; + dav_methods PUT DELETE; + create_full_put_path on; + client_max_body_size 256M; + ''; + }; + + systemd.services.nginx.serviceConfig = { + ReadWritePaths = [ "/mnt/volume-hel1-1/mkv" ]; + ProtectSystem = lib.mkForce "full"; # downgrade from strict + }; + + # Allow mkv and volume ports on VPN interface only + networking.firewall.interfaces.wg0.allowedTCPPorts = [ + 3000 + 8081 + ]; + + # Gitea runner SSH known hosts setup + systemd.services.gitea-runner-known-hosts = { + description = "Populate Gitea runner SSH known_hosts"; + wantedBy = [ "multi-user.target" ]; + before = [ "gitea-runner-default.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + mkdir -p /var/lib/gitea-runner/default/.ssh + ${pkgs.openssh}/bin/ssh-keyscan -H 46.62.150.48 >> /var/lib/gitea-runner/default/.ssh/known_hosts 2>/dev/null || true + chmod 644 /var/lib/gitea-runner/default/.ssh/known_hosts + ''; + }; +} diff --git a/hosts/poseidon.nix b/hosts/poseidon.nix @@ -0,0 +1,199 @@ +{ + pkgs, + modulesPath, + ... +}: +{ + # Generic + system.stateVersion = "24.05"; # Do not change lightly! + environment.systemPackages = with pkgs; [ + vim + ]; + nix.settings.experimental-features = [ + "nix-command" + "flakes" + ]; + services.openssh.enable = true; + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; + + boot.loader.grub = { + enable = false; + }; + fileSystems."/" = { + device = "/dev/sda1"; + fsType = "ext4"; + }; + nix.gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + nix.settings.auto-optimise-store = true; + + # Specific + networking.hostName = "poseidon"; + time.timeZone = "Europe/Amsterdam"; + + imports = [ + (modulesPath + "/virtualisation/digital-ocean-image.nix") + ]; + + sops.defaultSopsFile = ../secrets.yaml; + sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; + + sops.secrets.server_wireguard_key = { + restartUnits = [ "wg-quick-wg0.service" ]; + }; + + # VPN + networking.nat = { + enable = true; + externalInterface = "eth0"; + internalInterfaces = [ "wg0" ]; + }; + # Port 53 for DNS, port 51820 for WireGuard + networking.firewall = { + allowedTCPPorts = [ 53 ]; + allowedUDPPorts = [ + 53 + 51820 + ]; + }; + services.dnsmasq = { + enable = true; + settings = { + interface = "wg0"; + }; + }; + networking.wg-quick.interfaces = { + wg0 = { + address = [ "10.100.0.1/24" ]; + listenPort = 51820; + privateKeyFile = "/run/secrets/server_wireguard_key"; + + postUp = '' + ${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -A FORWARD -o wg0 -i eth0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.1/24 -j MASQUERADE + ''; + preDown = '' + ${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -D FORWARD -o wg0 -i eth0 -j ACCEPT + ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.1/24 -j MASQUERADE + ''; + + peers = [ + { + # Thinkpad + publicKey = "3owbBawNk7IEI8sHmJjr+Wna7QH5WxYuRGjU+OP4i2M="; + allowedIPs = [ "10.100.0.2/32" ]; + } + { + # Pixel + publicKey = "pTuUtRphmXYKhwInaCIQY7nXRgftNzgKJxleACutrFc="; + allowedIPs = [ "10.100.0.3/32" ]; + } + { + # Desktop + publicKey = "F/UODKJ2SQaRUOb7my2LtvRaN4p/khfW8bstO+09/zo="; + allowedIPs = [ "10.100.0.4/32" ]; + } + { + # iPad + publicKey = "KwQo8pMz0JYHaEV3sbezmpD8jwgbXjaumIKeqWYaeiE="; + allowedIPs = [ "10.100.0.5/32" ]; + } + { + # Lolo's Macbook Pro + publicKey = "qmch+NjlcVOxBcOsfwk2SJIcpN58i54131OS9J2yvmM="; + allowedIPs = [ "10.100.0.6/32" ]; + } + { + # Hetzner server + publicKey = "QE4UoW2vSQMX8o1liA4MSwe4fE5sUB0+Ari5a/CqkSc="; + allowedIPs = [ "10.100.0.7/32" ]; + } + { + # XPS 13 + publicKey = "ZkRCc3EpJYl0EmRBEAjf9WXCW0JQvlzeq5h7fUn5aEs="; + allowedIPs = [ "10.100.0.8/32" ]; + } + { + # Work Macbook pro + publicKey = "j+5aLHMTUGQyyjmU55ibA+JXC0pmEvQgQIOOfdMEQUA="; + allowedIPs = [ "10.100.0.9/32" ]; + } + ]; + }; + }; + + users.users = { + silas = { + isNormalUser = true; + description = "Silas Brack"; + home = "/home/silas"; + extraGroups = [ + "networkmanager" + "wheel" + ]; # "keys" + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZ0gNM0od3EE/LL27kYQ/s5MOU7LBNc0pWRWROeINij silas@Lorenzas-MacBook-Pro.local" # Lolo's Macbook Pro + ]; + }; + }; + + security.acme = { + acceptTerms = true; + defaults.email = "silasbrack@gmail.com"; + }; + services.nginx = { + enable = true; + virtualHosts."freshrss.fnarglebeast.com" = { + forceSSL = true; + enableACME = true; + }; + # virtualHosts."livebook.fnarglebeast.com" = { + # forceSSL = true; + # enableACME = true; + # locations."/" = { + # proxyPass = "http://127.0.0.1:20123"; + # proxyWebsockets = true; # needed if you need to use WebSocket + # extraConfig = + # # required when the target is also TLS server with multiple hosts + # "proxy_ssl_server_name on;" + # + + # # required when the server wants to use HTTP Authentication + # "proxy_pass_header Authorization;"; + # }; + # }; + }; + + # sops.secrets.livebook_password_env_file = { }; + # services.livebook = { + # enableUserService = true; + # environment = { + # LIVEBOOK_PORT = 20123; + # LIVEBOOK_IFRAME_PORT = 20124; + # }; + # environmentFile = "/run/secrets/livebook_password_env_file"; + # extraPackages = with pkgs; [ + # gcc + # gnumake + # ]; + # }; + + sops.secrets.freshrss_password = { + owner = "freshrss"; + }; + services.freshrss = { + enable = true; + defaultUser = "silas"; + passwordFile = "/run/secrets/freshrss_password"; + baseUrl = "http://localhost:8080"; + virtualHost = "freshrss.fnarglebeast.com"; + }; +} diff --git a/nixos/simple-web-app.nix b/modules/simple-web-app.nix diff --git a/nixos/debian-2gb-hel1-1.nix b/nixos/debian-2gb-hel1-1.nix @@ -1,366 +0,0 @@ -{ - pkgs, - config, - lib, - simple-web-app, - mkv, - website, - ... -}: -{ - # Generic - system.stateVersion = "25.05"; # Do not change lightly! - environment.systemPackages = with pkgs; [ - vim - wget - curl - rsync - rclone - zip - unzip - ]; - nix.settings.experimental-features = [ - "nix-command" - "flakes" - ]; - services.openssh.enable = true; - networking.firewall = { - allowedTCPPorts = [ - 80 - 443 - ]; - # allowedUDPPorts = [ - # 10232 - # ]; - }; - - boot.loader.grub = { - enable = false; - }; - fileSystems."/" = { - device = "/dev/sda1"; - fsType = "ext4"; - }; - fileSystems."/mnt/volume-hel1-1" = { - device = "/dev/sdb"; - fsType = "ext4"; - }; - nix.gc = { - automatic = true; - dates = "weekly"; - options = "--delete-older-than 30d"; - }; - nix.settings.auto-optimise-store = true; - - # Specific - networking.hostName = "debian-2gb-hel1-1"; - time.timeZone = "Europe/Helsinki"; - - imports = [ - # ./simple-web-app.nix - - # (builtins.fetchTarball { - # # Pick a release version you are interested in and set its hash, e.g. - # url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-25.05/nixos-mailserver-nixos-25.05.tar.gz"; - # # To get the sha256 of the nixos-mailserver tarball, we can use the nix-prefetch-url command: - # # release="nixos-25.05"; nix-prefetch-url "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/${release}/nixos-mailserver-${release}.tar.gz" --unpack - # sha256 = "0la8v8d9vzhwrnxmmyz3xnb6vm76kihccjyidhfg6qfi3143fiwq"; - # }) - ]; - - sops.defaultSopsFile = ../secrets.yaml; - sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; - sops.secrets.hetzner_wireguard_key = { - restartUnits = [ "wg-quick-wg0.service" ]; - }; - sops.secrets.mediawiki_admin_password = { - owner = "mediawiki"; - mode = "0400"; - }; - - networking.wg-quick.interfaces = { - wg0 = { - address = [ "10.100.0.7/24" ]; - dns = [ "10.100.0.1" ]; - listenPort = 10232; - privateKeyFile = "/run/secrets/hetzner_wireguard_key"; - peers = [ - { - publicKey = "2jWfr5UmACvuS+0HOfSNgEUYqoqVNnfVDoaatmgEykw="; - allowedIPs = [ "10.100.0.0/24" ]; - endpoint = "188.166.127.72:51820"; - persistentKeepalive = 25; - } - ]; - }; - }; - - users.users = { - root.openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPGZOv4PvL/NvBiDFnCh8V6xm03k/C3b8O82z1+tmboI gitea-runner" - ]; - silas = { - isNormalUser = true; - description = "Silas Brack"; - home = "/home/silas"; - extraGroups = [ - "networkmanager" - "wheel" - ]; # "keys" - openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZ0gNM0od3EE/LL27kYQ/s5MOU7LBNc0pWRWROeINij silas@Lorenzas-MacBook-Pro.local" # Lolo's Macbook Pro - ]; - }; - }; - - security.acme = { - acceptTerms = true; - defaults.email = "silasbrack@gmail.com"; - }; - services.nginx.enable = true; - services.nginx.recommendedGzipSettings = true; - services.nginx.recommendedBrotliSettings = true; # Better compression than gzip for modern browsers - services.nginx.recommendedOptimisation = true; # sendfile, tcp_nopush, tcp_nodelay, keepalive - services.nginx.recommendedTlsSettings = true; # Strong TLS defaults - services.nginx.recommendedProxySettings = true; # Good defaults for proxied services (Forgejo) - - # Rate limiting zone (10 requests/second per IP, 10MB zone) - services.nginx.commonHttpConfig = '' - limit_req_zone $binary_remote_addr zone=ratelimit:10m rate=10r/s; - ''; - services.nginx.appendHttpConfig = '' - limit_req_status 429; - ''; - - # Catch-all for unknown hostnames - services.nginx.virtualHosts."_" = { - default = true; - rejectSSL = true; - locations."/" = { - return = "444"; - }; - }; - - # MediaWiki - services.mediawiki = { - enable = true; - name = "Silas' Wiki"; - webserver = "nginx"; - passwordFile = config.sops.secrets.mediawiki_admin_password.path; - database = { - type = "postgres"; - createLocally = true; - }; - nginx.hostName = "wiki.fnarglebeast.com"; - uploadsDir = "/var/lib/mediawiki/uploads"; - extensions = { - ParserFunctions = null; - }; - extraConfig = '' - $wgEnableUploads = true; - $wgUseImageMagick = true; - - # Security: disable reading by anonymous users - $wgGroupPermissions['*']['read'] = false; - - # Security: disable anonymous editing (require login) - $wgGroupPermissions['*']['edit'] = false; - $wgGroupPermissions['*']['createpage'] = false; - $wgGroupPermissions['*']['createtalk'] = false; - - # Security: disable account creation by anonymous users - $wgGroupPermissions['*']['createaccount'] = false; - - # Security: restrict upload file types - $wgFileExtensions = ['png', 'gif', 'jpg', 'jpeg', 'webp', 'pdf']; - ''; - }; - services.nginx.virtualHosts."wiki.fnarglebeast.com" = { - enableACME = true; - forceSSL = true; - extraConfig = '' - limit_req zone=ratelimit burst=20 nodelay; - ''; - }; - - # Forgejo - services.forgejo = { - enable = true; - database.type = "postgres"; - lfs.enable = true; - settings = { - server = { - DOMAIN = "git.fnarglebeast.com"; - ROOT_URL = "https://git.fnarglebeast.com/"; - HTTP_PORT = 3001; - }; - service = { - DISABLE_REGISTRATION = true; - }; - session = { - COOKIE_SECURE = true; - }; - }; - }; - services.nginx.virtualHosts."git.fnarglebeast.com" = { - enableACME = true; - forceSSL = true; - extraConfig = '' - limit_req zone=ratelimit burst=20 nodelay; - ''; - locations."/" = { - proxyPass = "http://127.0.0.1:3001"; - proxyWebsockets = true; - }; - }; - - # Personal website - services.nginx.virtualHosts."silasbrack.com" = let - staticSite = pkgs.runCommand "website" {} '' - mkdir -p $out - cp ${website}/index.html $out/ - cp ${website}/style.css $out/ - cp ${website}/style.min.css $out/ - cp -r ${website}/posts $out/ - cp -r ${website}/assets $out/ - ''; - in { - enableACME = true; - forceSSL = true; - root = staticSite; - extraConfig = '' - limit_req zone=ratelimit burst=20 nodelay; - ''; - locations."/" = { - index = "index.html"; - extraConfig = '' - try_files $uri $uri.html $uri/ /index.html; - ''; - }; - }; - # services.simple-web-app = { - # enable = true; - # package = simple-web-app.packages."x86_64-linux".default; - # port = 8032; - # }; - - # services.karakeep = { - # enable = true; - # extraEnvironment = { - # PORT = "8360"; - # DISABLE_SIGNUPS = "true"; - # }; - # }; - # services.nginx.virtualHosts."karakeep.fnarglebeast.com" = { - # forceSSL = true; - # enableACME = true; - # locations."/" = { - # proxyPass = "http://127.0.0.1:8360"; - # }; - # }; - - # services.nginx.virtualHosts."mail.silasbrack.com" = { - # forceSSL = true; - # enableACME = true; - # locations."/" = { - # proxyPass = ""; - # }; - # }; - # mailserver = { - # enable = true; - # fqdn = "mail.silasbrack.com"; - # domains = [ "silasbrack.com" ]; - # - # # Use Let's Encrypt certificates. Note that this needs to set up a stripped - # # down nginx and opens port 80. - # certificateScheme = "acme-nginx"; - # - # # A list of all login accounts. To create the password hashes, use - # # nix-shell -p mkpasswd --run 'mkpasswd -s' - # loginAccounts = { - # "test@silasbrack.com" = { - # hashedPasswordFile = "/home/silas/.test.txt"; - # # aliases = [ "postmaster@example.com" ]; - # }; - # }; - # }; - - # Create mkv volume directory - systemd.tmpfiles.rules = [ - "d /mnt/volume-hel1-1/mkv 0755 nginx nginx -" - ]; - - # mkv key-value store - systemd.services.mkv = { - description = "mkv distributed key-value store"; - wantedBy = [ "multi-user.target" ]; - after = [ - "network-online.target" - "wg-quick-wg0.service" - "nginx.service" - ]; - wants = [ "network-online.target" ]; - requires = [ "wg-quick-wg0.service" ]; - environment = { - MKV_DB = "/var/lib/mkv/index.db"; - MKV_VOLUMES = "http://10.100.0.7:8081,http://10.100.0.4:8081"; - MKV_REPLICAS = "2"; - MKV_PORT = "3000"; - }; - serviceConfig = { - Type = "simple"; - DynamicUser = true; - StateDirectory = "mkv"; - ExecStart = "${mkv.packages.${pkgs.system}.default}/bin/mkv serve"; - Restart = "on-failure"; - RestartSec = 5; - }; - }; - - # nginx WebDAV volume server for mkv (VPN only) - services.nginx.virtualHosts."mkv-volume" = { - listen = [ - { - addr = "10.100.0.7"; - port = 8081; - } - ]; - root = "/mnt/volume-hel1-1/mkv"; - extraConfig = '' - autoindex on; - autoindex_format json; - dav_methods PUT DELETE; - create_full_put_path on; - client_max_body_size 256M; - ''; - }; - - systemd.services.nginx.serviceConfig = { - ReadWritePaths = [ "/mnt/volume-hel1-1/mkv" ]; - ProtectSystem = lib.mkForce "full"; # downgrade from strict - }; - - # Allow mkv and volume ports on VPN interface only - networking.firewall.interfaces.wg0.allowedTCPPorts = [ - 3000 - 8081 - ]; - - # Gitea runner SSH known hosts setup - systemd.services.gitea-runner-known-hosts = { - description = "Populate Gitea runner SSH known_hosts"; - wantedBy = [ "multi-user.target" ]; - before = [ "gitea-runner-default.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - script = '' - mkdir -p /var/lib/gitea-runner/default/.ssh - ${pkgs.openssh}/bin/ssh-keyscan -H 46.62.150.48 >> /var/lib/gitea-runner/default/.ssh/known_hosts 2>/dev/null || true - chmod 644 /var/lib/gitea-runner/default/.ssh/known_hosts - ''; - }; -} diff --git a/nixos/nixos-s-1vcpu-512mb-10gb-ams3-01.nix b/nixos/nixos-s-1vcpu-512mb-10gb-ams3-01.nix @@ -1,199 +0,0 @@ -{ - pkgs, - modulesPath, - ... -}: -{ - # Generic - system.stateVersion = "24.05"; # Do not change lightly! - environment.systemPackages = with pkgs; [ - vim - ]; - nix.settings.experimental-features = [ - "nix-command" - "flakes" - ]; - services.openssh.enable = true; - networking.firewall.allowedTCPPorts = [ - 80 - 443 - ]; - - boot.loader.grub = { - enable = false; - }; - fileSystems."/" = { - device = "/dev/sda1"; - fsType = "ext4"; - }; - nix.gc = { - automatic = true; - dates = "weekly"; - options = "--delete-older-than 30d"; - }; - nix.settings.auto-optimise-store = true; - - # Specific - networking.hostName = "nixos-s-1vcpu-512mb-10gb-ams3-01"; - time.timeZone = "Europe/Amsterdam"; - - imports = [ - (modulesPath + "/virtualisation/digital-ocean-image.nix") - ]; - - sops.defaultSopsFile = ../secrets.yaml; - sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ]; - - sops.secrets.server_wireguard_key = { - restartUnits = [ "wg-quick-wg0.service" ]; - }; - - # VPN - networking.nat = { - enable = true; - externalInterface = "eth0"; - internalInterfaces = [ "wg0" ]; - }; - # Port 53 for DNS, port 51820 for WireGuard - networking.firewall = { - allowedTCPPorts = [ 53 ]; - allowedUDPPorts = [ - 53 - 51820 - ]; - }; - services.dnsmasq = { - enable = true; - settings = { - interface = "wg0"; - }; - }; - networking.wg-quick.interfaces = { - wg0 = { - address = [ "10.100.0.1/24" ]; - listenPort = 51820; - privateKeyFile = "/run/secrets/server_wireguard_key"; - - postUp = '' - ${pkgs.iptables}/bin/iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT - ${pkgs.iptables}/bin/iptables -A FORWARD -o wg0 -i eth0 -j ACCEPT - ${pkgs.iptables}/bin/iptables -t nat -A POSTROUTING -s 10.100.0.1/24 -j MASQUERADE - ''; - preDown = '' - ${pkgs.iptables}/bin/iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT - ${pkgs.iptables}/bin/iptables -D FORWARD -o wg0 -i eth0 -j ACCEPT - ${pkgs.iptables}/bin/iptables -t nat -D POSTROUTING -s 10.100.0.1/24 -j MASQUERADE - ''; - - peers = [ - { - # Thinkpad - publicKey = "3owbBawNk7IEI8sHmJjr+Wna7QH5WxYuRGjU+OP4i2M="; - allowedIPs = [ "10.100.0.2/32" ]; - } - { - # Pixel - publicKey = "pTuUtRphmXYKhwInaCIQY7nXRgftNzgKJxleACutrFc="; - allowedIPs = [ "10.100.0.3/32" ]; - } - { - # Desktop - publicKey = "F/UODKJ2SQaRUOb7my2LtvRaN4p/khfW8bstO+09/zo="; - allowedIPs = [ "10.100.0.4/32" ]; - } - { - # iPad - publicKey = "KwQo8pMz0JYHaEV3sbezmpD8jwgbXjaumIKeqWYaeiE="; - allowedIPs = [ "10.100.0.5/32" ]; - } - { - # Lolo's Macbook Pro - publicKey = "qmch+NjlcVOxBcOsfwk2SJIcpN58i54131OS9J2yvmM="; - allowedIPs = [ "10.100.0.6/32" ]; - } - { - # Hetzner server - publicKey = "QE4UoW2vSQMX8o1liA4MSwe4fE5sUB0+Ari5a/CqkSc="; - allowedIPs = [ "10.100.0.7/32" ]; - } - { - # XPS 13 - publicKey = "ZkRCc3EpJYl0EmRBEAjf9WXCW0JQvlzeq5h7fUn5aEs="; - allowedIPs = [ "10.100.0.8/32" ]; - } - { - # Work Macbook pro - publicKey = "j+5aLHMTUGQyyjmU55ibA+JXC0pmEvQgQIOOfdMEQUA="; - allowedIPs = [ "10.100.0.9/32" ]; - } - ]; - }; - }; - - users.users = { - silas = { - isNormalUser = true; - description = "Silas Brack"; - home = "/home/silas"; - extraGroups = [ - "networkmanager" - "wheel" - ]; # "keys" - openssh.authorizedKeys.keys = [ - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGqHJMc2SCzL50SO9Flfnhn012p1dmp4Iph8lPQ2aoe/ silasbrack@gmail.com" # Desktop - "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMZ0gNM0od3EE/LL27kYQ/s5MOU7LBNc0pWRWROeINij silas@Lorenzas-MacBook-Pro.local" # Lolo's Macbook Pro - ]; - }; - }; - - security.acme = { - acceptTerms = true; - defaults.email = "silasbrack@gmail.com"; - }; - services.nginx = { - enable = true; - virtualHosts."freshrss.fnarglebeast.com" = { - forceSSL = true; - enableACME = true; - }; - # virtualHosts."livebook.fnarglebeast.com" = { - # forceSSL = true; - # enableACME = true; - # locations."/" = { - # proxyPass = "http://127.0.0.1:20123"; - # proxyWebsockets = true; # needed if you need to use WebSocket - # extraConfig = - # # required when the target is also TLS server with multiple hosts - # "proxy_ssl_server_name on;" - # + - # # required when the server wants to use HTTP Authentication - # "proxy_pass_header Authorization;"; - # }; - # }; - }; - - # sops.secrets.livebook_password_env_file = { }; - # services.livebook = { - # enableUserService = true; - # environment = { - # LIVEBOOK_PORT = 20123; - # LIVEBOOK_IFRAME_PORT = 20124; - # }; - # environmentFile = "/run/secrets/livebook_password_env_file"; - # extraPackages = with pkgs; [ - # gcc - # gnumake - # ]; - # }; - - sops.secrets.freshrss_password = { - owner = "freshrss"; - }; - services.freshrss = { - enable = true; - defaultUser = "silas"; - passwordFile = "/run/secrets/freshrss_password"; - baseUrl = "http://localhost:8080"; - virtualHost = "freshrss.fnarglebeast.com"; - }; -}