diff --git a/flake.nix b/flake.nix index 0a29d99..eeae09c 100644 --- a/flake.nix +++ b/flake.nix @@ -79,15 +79,36 @@ hypnos = mkNixosSystem { system = "x86_64-linux"; name = "hypnos"; }; odyssey = mkNixosSystem { system = "x86_64-linux"; name = "odyssey"; }; pi = mkNixosSystem { system = "aarch64-linux"; name = "pi"; extraModules = [ nixos-hardware.nixosModules.raspberry-pi-4 ]; }; + vps1 = mkNixosSystem { system = "x86_64-linux"; name = "vps1"; }; }; - deploy.nodes.pi = { - hostname = "10.0.1.191"; - sshUser = "root"; + devShells.x86_64-linux.default = nixpkgs.legacyPackages.x86_64-linux.mkShell { + buildInputs = [ + deploy-rs.packages.x86_64-linux.deploy-rs + ]; + }; - profiles.system = { - user = "root"; - path = deploy-rs.lib.aarch64-linux.activate.nixos self.nixosConfigurations.pi; + deploy = { + magicRollback = true; + autoRollback = true; + sshUser = "root"; + nodes = { + vps1 = { + hostname = "vps1.mesh.vimium.net"; + + profiles.system = { + user = "root"; + path = deploy-rs.lib.x86_64-linux.activate.nixos self.nixosConfigurations.vps1; + }; + }; + # pi = { + # hostname = "10.0.1.191"; + # + # profiles.system = { + # user = "root"; + # path = deploy-rs.lib.aarch64-linux.activate.nixos self.nixosConfigurations.pi; + # }; + # }; }; }; diff --git a/hosts/vps1/README.md b/hosts/vps1/README.md new file mode 100644 index 0000000..f6f9f30 --- /dev/null +++ b/hosts/vps1/README.md @@ -0,0 +1,18 @@ +# vps1 + +## Overview +VPS hosted in OVH. + +## Specs +* CPU - ?? +* Memory - ?? + +### Disks +Device | Partitions _(filesystem, usage)_ +--- | --- +NVMe | `/dev/sda1` (ext4, NixOS Root) + +### Networks +- DHCP on `10.0.1.0/24` subnet. +- Tailscale on `100.64.0.0/10` subnet. FQDN: `vps1.mesh.vimium.net`. + diff --git a/hosts/vps1/default.nix b/hosts/vps1/default.nix new file mode 100644 index 0000000..5396f42 --- /dev/null +++ b/hosts/vps1/default.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, inputs, ... }: + +{ + imports = [ + ./hardware-configuration.nix + ../server.nix + ]; + + networking = { + hostId = "08bf6db3"; + domain = "mesh.vimium.net"; + firewall = { + enable = true; + allowedTCPPorts = [ + 22 # SSH + ]; + }; + }; + + users = { + users = { + jellyfin = { + isSystemUser = true; + group = "jellyfin"; + shell = "/bin/sh"; + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOaaS+KMAEAymZhIJGC4LK8aMhUzhpmloUgvP2cxeBH4 jellyfin" + ]; + }; + root = { + openssh.authorizedKeys.keys = [ + "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILVHTjsyMIV4THNw6yz0OxAxGnC+41gX72UrPqTzR+OS jordan@vimium.com" + ]; + }; + }; + groups = { + jellyfin = { }; + }; + }; + + security.acme.defaults = { + email = "hostmaster@vimium.com"; + group = "nginx"; + webroot = "/var/lib/acme/acme-challenge"; + }; + + modules = { + services = { + borgmatic = { + enable = true; + directories = [ + "/home" + "/var/lib" + "/var/www" + ]; + repoPath = "ssh://p91y8oh7@p91y8oh7.repo.borgbase.com/./repo"; + }; + coturn.enable = true; + gitea.enable = true; + headscale.enable = true; + matrix-synapse.enable = true; + nginx.enable = true; + }; + }; + + system.stateVersion = "22.11"; +} diff --git a/hosts/vps1/hardware-configuration.nix b/hosts/vps1/hardware-configuration.nix new file mode 100644 index 0000000..1f79513 --- /dev/null +++ b/hosts/vps1/hardware-configuration.nix @@ -0,0 +1,26 @@ +{ config, lib, pkgs, modulesPath, ... }: + +{ + imports = [ + (modulesPath + "/profiles/qemu-guest.nix") + ]; + + boot = { + initrd = { + availableKernelModules = [ "ata_piix" "uhci_hcd" "xen_blkfront" "vmw_pvscsi" ]; + kernelModules = [ "nvme" ]; + }; + loader.grub.device = "/dev/sda"; + tmp.cleanOnBoot = true; + }; + + zramSwap.enable = true; + + fileSystems = { + "/" = { + device = "/dev/sda1"; + fsType = "ext4"; + }; + }; +} + diff --git a/modules/default.nix b/modules/default.nix index 3c1fa02..37cbd2b 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -31,6 +31,11 @@ ./security/gpg.nix ./security/pass.nix ./services/borgmatic + ./services/coturn + ./services/gitea + ./services/headscale + ./services/matrix-synapse + ./services/nginx ./shell/git ./shell/zsh ]; diff --git a/modules/services/coturn/default.nix b/modules/services/coturn/default.nix new file mode 100644 index 0000000..8ddbe4c --- /dev/null +++ b/modules/services/coturn/default.nix @@ -0,0 +1,60 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +let + cfg = config.modules.services.coturn; +in { + options.modules.services.coturn = { + enable = mkOption { + default = false; + example = true; + }; + }; + + config = mkIf cfg.enable { + networking.firewall = { + allowedTCPPorts = [ + 5349 # STUN TLS + 5350 # STUN TLS alt + ]; + allowedUDPPortRanges = [ + { from = 49152; to = 49999; } # TURN relay + ]; + }; + + security.acme.certs = { + "turn.vimium.com" = { + reloadServices = [ "coturn" ]; + }; + }; + + age.secrets."passwords/services/coturn/shared-secret" = { + file = "${inputs.secrets}/passwords/services/coturn/shared-secret.age"; + owner = "turnserver"; + group = "turnserver"; + }; + + services.coturn = { + enable = true; + lt-cred-mech = true; + use-auth-secret = true; + static-auth-secret-file = config.age.secrets."passwords/services/coturn/shared-secret"; + realm = "turn.vimium.com"; + relay-ips = [ + "198.244.190.160" + ]; + no-tcp-relay = true; + extraConfig = '' + cipher-list="HIGH" + no-loopback-peers + no-multicast-peers + ''; + secure-stun = true; + cert = "/var/lib/acme/turn.vimium.com/fullchain.pem"; + pkey = "/var/lib/acme/turn.vimium.com/key.pem"; + min-port = 49152; + max-port = 49999; + }; + }; +} diff --git a/modules/services/gitea/default.nix b/modules/services/gitea/default.nix new file mode 100644 index 0000000..1fdfa73 --- /dev/null +++ b/modules/services/gitea/default.nix @@ -0,0 +1,83 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +let + cfg = config.modules.services.gitea; +in { + options.modules.services.gitea = { + enable = mkOption { + default = false; + example = true; + }; + }; + + config = mkIf cfg.enable { + users = { + users.git = { + isSystemUser = true; + useDefaultShell = true; + group = "git"; + extraGroups = [ "gitea" ]; + home = config.services.gitea.stateDir; + }; + groups.git = { }; + }; + + services.nginx = { + upstreams.gitea = { + servers = { + "unix:${config.services.gitea.settings.server.HTTP_ADDR}" = { }; + }; + }; + virtualHosts = { + "git.vimium.com" = { + forceSSL = true; + enableACME = true; + locations."/".proxyPass = "http://gitea"; + }; + }; + }; + + services.gitea = rec { + package = pkgs.unstable.gitea; + enable = true; + user = "git"; + appName = "Vimium Git"; + stateDir = "/var/lib/gitea"; + repositoryRoot = "${stateDir}/repositories"; + database = { + type = "sqlite3"; + inherit user; + path = "${stateDir}/gitea.db"; + }; + lfs = { + enable = true; + contentDir = "${stateDir}/lfs"; + }; + settings = { + server = { + SSH_USER = "git"; + SSH_DOMAIN = "git.vimium.com"; + SSH_PORT = lib.head config.services.openssh.ports; + OFFLINE_MODE = true; + PROTOCOL = "http+unix"; + DOMAIN = config.networking.domain; + ROOT_URL = "https://git.vimium.com/"; + }; + service.DISABLE_REGISTRATION = true; + session.COOKIE_SECURE = true; + log.ROOT_PATH = "${stateDir}/log"; + ui = { + THEMES = "gitea,arc-green,github-dark,bthree-dark"; + DEFAULT_THEME = "github-dark"; + }; + actions.ENABLED = true; + indexer = { + REPO_INDEXER_ENABLED = true; + }; + packages.CHUNKED_UPLOAD_PATH = lib.mkForce "${stateDir}/data/tmp/package-upload"; + }; + }; + }; +} diff --git a/modules/services/headscale/default.nix b/modules/services/headscale/default.nix new file mode 100644 index 0000000..3dc608e --- /dev/null +++ b/modules/services/headscale/default.nix @@ -0,0 +1,43 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +let + cfg = config.modules.services.headscale; +in { + options.modules.services.headscale = { + enable = mkOption { + default = false; + example = true; + }; + }; + + config = mkIf cfg.enable { + services.nginx.virtualHosts = { + "headscale.vimium.net" = { + forceSSL = true; + enableACME = true; + locations."/" = { + proxyPass = "http://localhost:${toString config.services.headscale.port}"; + proxyWebsockets = true; + }; + }; + }; + + services.headscale = { + enable = true; + port = 8080; + settings = { + server_url = "https://headscale.vimium.net"; + dns_config = { + base_domain = "vimium.net"; + }; + logtail.enabled = false; + }; + }; + + environment.systemPackages = with pkgs; [ + config.services.headscale.package + ]; + }; +} diff --git a/modules/services/matrix-synapse/default.nix b/modules/services/matrix-synapse/default.nix new file mode 100644 index 0000000..9b39a9c --- /dev/null +++ b/modules/services/matrix-synapse/default.nix @@ -0,0 +1,128 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +let + cfg = config.modules.services.matrix-synapse; + matrixClientConfig = { + "m.homeserver" = { + base_url = "https://matrix.vimium.com"; + server_name = "vimium.com"; + }; + "m.identity_server" = {}; + }; + matrixServerConfig."m.server" = "matrix.vimium.com:443"; + mkWellKnown = data: '' + more_set_headers 'Content-Type: application/json'; + return 200 '${builtins.toJSON data}'; + ''; +in { + options.modules.services.matrix-synapse = { + enable = mkOption { + default = false; + example = true; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ + 8448 # Matrix federation + ]; + + security.acme.certs = { + "matrix.vimium.com" = { + reloadServices = [ "matrix-synapse" ]; + }; + }; + + services.nginx.virtualHosts = { + "chat.vimium.com" = { + forceSSL = true; + enableACME = true; + root = pkgs.unstable.element-web.override { + conf = { + default_server_config = matrixClientConfig; + brand = "Vimium Chat"; + branding = { + auth_header_logo_url = "https://vimium.com/images/logo.svg"; + auth_footer_links = [ + { "text" = "Vimium.com"; "url" = "https://vimium.com"; } + ]; + }; + }; + }; + }; + "matrix.vimium.com" = { + forceSSL = true; + enableACME = true; + listen = [ + { + addr = "0.0.0.0"; + port = 443; + ssl = true; + } + { + addr = "0.0.0.0"; + port = 80; + } + { + addr = "0.0.0.0"; + port = 8448; + ssl = true; + } + { + addr = "[::1]"; + port = 443; + ssl = true; + } + { + addr = "[::1]"; + port = 80; + } + { + addr = "[::1]"; + port = 8448; + ssl = true; + } + ]; + locations = { + "/" = { + proxyPass = "http://localhost:8008"; + extraConfig = '' + proxy_set_header X-Forwarded-For $remote_addr; + ''; + }; + "/_matrix" = { + proxyPass = "http://localhost:8008"; + extraConfig = '' + proxy_set_header X-Forwarded-For $remote_addr; + client_max_body_size 50M; + ''; + }; + "/_synapse/client".proxyPass = "http://localhost:8008"; + }; + }; + "vimium.com" = { + locations."= /.well-known/matrix/server".extraConfig = (mkWellKnown matrixServerConfig); + locations."= /.well-known/matrix/client".extraConfig = (mkWellKnown matrixClientConfig); + }; + }; + + services.matrix-synapse = { + package = pkgs.unstable.matrix-synapse; + enable = true; + settings = { + database.name = "sqlite3"; + enable_registration = false; + server_name = "vimium.com"; + # turn_shared_secret = "???"; + # turn_uris = [ + # "turn:turn.vimium.com:5349?transport=udp" + # "turn:turn.vimium.com:5350?transport=udp" + # "turn:turn.vimium.com:5349?transport=tcp" + # "turn:turn.vimium.com:5350?transport=tcp" + # ]; + }; + }; + }; +} diff --git a/modules/services/nginx/default.nix b/modules/services/nginx/default.nix new file mode 100644 index 0000000..fafad00 --- /dev/null +++ b/modules/services/nginx/default.nix @@ -0,0 +1,159 @@ +{ config, lib, pkgs, inputs, ... }: + +with lib; + +let + cfg = config.modules.services.nginx; + nginxErrorPages = '' + location @error_pages { + rewrite ^.*$ /''${status}.html break; + + root "/var/www/html/errors"; + } + ''; + nginxEdgeHeaders = '' + more_set_headers 'Server: Vimium'; + more_set_headers 'Access-Control-Allow-Origin: *'; + add_header Expect-CT max-age=30 always; + add_header Referrer-Policy strict-origin-when-cross-origin always; + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; + add_header Vimium-Responding-Instance $hostname; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options nosniff always; + ''; + nginxStrictHeaders = '' + add_header X-Frame-Options SAMEORIGIN always; + add_header Permissions-Policy "fullscreen=(self), sync-xhr=(self)" always; + ''; + mkRedirect = from: to: { + "${from}" = { + forceSSL = true; + enableACME = true; + serverAliases = [ "www.${from}" ]; + locations."/".return = "301 https://${to}$request_uri"; + extraConfig = nginxErrorPages + nginxEdgeHeaders + nginxStrictHeaders; + }; + }; +in { + options.modules.services.nginx = { + enable = mkOption { + default = false; + example = true; + }; + }; + + config = mkIf cfg.enable { + networking.firewall.allowedTCPPorts = [ + 80 # HTTP + 443 # HTTPS + ]; + + services.nginx = { + enable = true; + package = pkgs.openresty; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedTlsSettings = true; + clientMaxBodySize = "2G"; + sslProtocols = "TLSv1.2 TLSv1.3"; + sslCiphers = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; + appendHttpConfig = '' + error_page 400 @error_pages; + error_page 401 @error_pages; + error_page 403 @error_pages; + error_page 404 @error_pages; + error_page 405 @error_pages; + error_page 429 @error_pages; + error_page 500 @error_pages; + error_page 501 @error_pages; + error_page 502 @error_pages; + error_page 503 @error_pages; + error_page 504 @error_pages; + + client_body_buffer_size 16k; + client_header_buffer_size 8k; + ''; + appendConfig = '' + worker_processes auto; + worker_cpu_affinity auto; + worker_rlimit_nofile 50000; + ''; + eventsConfig = '' + worker_connections 20000; + multi_accept off; + ''; + virtualHosts = { + ## Static sites + "jellyfin.vimium.com" = { + forceSSL = true; + enableACME = true; + extraConfig = nginxErrorPages + nginxEdgeHeaders; + locations."/" = { + proxyPass = "http://localhost:8000"; + extraConfig = '' + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header Host $host; + + proxy_set_header Range $http_range; + proxy_set_header If-Range $http_if_range; + + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + ''; + }; + }; + "pki.vimium.com" = { + addSSL = true; + forceSSL = false; + enableACME = true; + extraConfig = '' + ${nginxErrorPages} + more_set_headers 'Server: Vimium'; + ''; + locations."/" = { + root = "/var/www/pki.vimium.com"; + }; + }; + "suhailhussain.com" = { + forceSSL = true; + enableACME = true; + serverAliases = [ "www.suhailhussain.com" ]; + extraConfig = nginxErrorPages + nginxEdgeHeaders + nginxStrictHeaders; + locations."/" = { + root = "/var/www/suhailhussain.com"; + }; + }; + "vimium.com" = { + default = true; + forceSSL = true; + enableACME = true; + serverAliases = [ "www.vimium.com" ]; + extraConfig = nginxErrorPages + nginxEdgeHeaders + nginxStrictHeaders + '' + add_header Content-Security-Policy "default-src 'self' https://vimium.com https://www.vimium.com; style-src 'unsafe-inline'; object-src 'none'; upgrade-insecure-requests" always; + ''; + locations."= /.well-known/matrix/server".extraConfig = (mkWellKnown matrixServerConfig); + locations."= /.well-known/matrix/client".extraConfig = (mkWellKnown matrixClientConfig); + locations."/" = { + root = "/var/www/vimium.com"; + }; + }; + } + ## Redirects + // (mkRedirect "h0lt.com" "jdholt.com") + // (mkRedirect "jordanholt.xyz" "jdholt.com") + // (mkRedirect "jdholt.com" "vimium.com") + // (mkRedirect "omnimagic.com" "vimium.com") + // (mkRedirect "omnimagic.net" "vimium.com") + // (mkRedirect "thelostlegend.com" "suhailhussain.com") + // (mkRedirect "vimium.co" "vimium.com") + // (mkRedirect "vimium.co.uk" "vimium.com") + // (mkRedirect "vimium.info" "vimium.com") + // (mkRedirect "vimium.net" "vimium.com") + // (mkRedirect "vimium.org" "vimium.com") + // (mkRedirect "vimium.xyz" "vimium.com"); + }; + }; +}