From 572647d7c4608b269aebbf82f710cae624c00ac5 Mon Sep 17 00:00:00 2001 From: TheGeneralist <180094941+thegeneralist01@users.noreply.github.com> Date: Sun, 3 Aug 2025 14:48:21 +0200 Subject: [PATCH] services: add jellyfin, archivebox, custom dns - `internal.thegeneralist01.com` and `archive.thegeneralist01.com` are not public. I have Split DNS enabled on them (in Tailscale), with the IP of the DNS server set to a private Tailscale IP of my home server; - CoreDNS (also on my home server) is used to resolve the two private domains' IPs to the home server itself; - nginx only listens to its machine's (home server's) Tailscale IP; - Therefore, all of it is hermetic! --- hosts/thegeneralist-central/acme/default.nix | 16 +++-- .../thegeneralist-central/archive/default.nix | 41 +++++++++++++ hosts/thegeneralist-central/configuration.nix | 4 +- hosts/thegeneralist-central/dns.nix | 58 ++++++++++++++++++ hosts/thegeneralist-central/garage.nix | 18 ++++++ .../jellyfin/default.nix | 60 +++++++++++++++++++ hosts/thegeneralist-central/site.nix | 2 +- .../dotfiles/nvim/lua/thegeneralist/init.lua | 1 - modules/linux/dns.nix | 5 +- 9 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 hosts/thegeneralist-central/archive/default.nix create mode 100644 hosts/thegeneralist-central/dns.nix create mode 100644 hosts/thegeneralist-central/garage.nix create mode 100644 hosts/thegeneralist-central/jellyfin/default.nix diff --git a/hosts/thegeneralist-central/acme/default.nix b/hosts/thegeneralist-central/acme/default.nix index 9db64c5..10373ff 100644 --- a/hosts/thegeneralist-central/acme/default.nix +++ b/hosts/thegeneralist-central/acme/default.nix @@ -5,16 +5,24 @@ in { security.acme = { defaults = { - # Options: https://go-acme.github.io/lego/dns/ + # Options: https://go-acme.github.io/lego/dns/acme environmentFile = config.age.secrets.acmeEnvironment.path; email = "thegeneralist01@proton.me"; dnsResolver = "1.1.1.1"; dnsProvider = "cloudflare"; }; - certs.${domain} = { - extraDomainNames = [ "*.${domain}" ]; - group = "acme"; + certs = { + ${domain} = { + extraDomainNames = [ "*.${domain}" ]; + group = "acme"; + }; + "internal.${domain}" = { + group = "acme"; + }; + "archive.${domain}" = { + group = "acme"; + }; }; acceptTerms = true; diff --git a/hosts/thegeneralist-central/archive/default.nix b/hosts/thegeneralist-central/archive/default.nix new file mode 100644 index 0000000..3954c23 --- /dev/null +++ b/hosts/thegeneralist-central/archive/default.nix @@ -0,0 +1,41 @@ +let + acmeDomain = "thegeneralist01.com"; + domain = "archive.${acmeDomain}"; + + ssl = { + forceSSL = true; + quic = true; + useACMEHost = domain; + }; +in +{ + services.nginx.virtualHosts.${domain} = ssl // { + listen = [ + { + addr = "100.86.129.23"; + port = 443; + ssl = true; + } + { + addr = "100.86.129.23"; + port = 80; + } + ]; + + locations."/" = { + proxyPass = "http://127.0.0.1:8000"; + recommendedProxySettings = true; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # tell nginx not to buffer the response. send it as it comes. + proxy_buffering off; + + # give jellyfin plenty of time to transcode + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + ''; + }; + }; +} diff --git a/hosts/thegeneralist-central/configuration.nix b/hosts/thegeneralist-central/configuration.nix index 4c0ad22..54dd49e 100644 --- a/hosts/thegeneralist-central/configuration.nix +++ b/hosts/thegeneralist-central/configuration.nix @@ -5,14 +5,14 @@ { config, pkgs, inputs, ... }: { - imports = [ ./hardware-configuration.nix ./site.nix ./cache ]; + imports = [ ./hardware-configuration.nix ./site.nix ./cache ./garage.nix ./archive ]; age.secrets.password.file = ./password.age; users.users = { thegeneralist = { isNormalUser = true; description = "thegeneralist"; - extraGroups = [ "wheel" "audio" "video" "input" "scanner" ]; + extraGroups = [ "wheel" "audio" "video" "input" "scanner" "docker" ]; shell = pkgs.zsh; home = "/home/thegeneralist"; hashedPasswordFile = config.age.secrets.password.path; diff --git a/hosts/thegeneralist-central/dns.nix b/hosts/thegeneralist-central/dns.nix new file mode 100644 index 0000000..df7feaf --- /dev/null +++ b/hosts/thegeneralist-central/dns.nix @@ -0,0 +1,58 @@ +{ pkgs, ... }: +let + internalZoneFile = pkgs.writeText "internal.zone" '' + $ORIGIN internal.thegeneralist01.com. + @ IN SOA ns.internal.thegeneralist01.com. thegeneralist01.proton.me. ( + 2025071801 ; serial (yyyymmddXX) + 3600 ; refresh + 600 ; retry + 86400 ; expire + 3600 ; minimum + ) + IN NS ns.internal.thegeneralist01.com. + ns IN A 100.86.129.23 + @ IN A 100.86.129.23 + ''; + + archiveZoneFile = pkgs.writeText "archive.zone" '' + $ORIGIN archive.thegeneralist01.com. + @ IN SOA ns.archive.thegeneralist01.com. thegeneralist01.proton.me. ( + 2025073101 ; serial (yyyymmddXX) + 3600 ; refresh + 600 ; retry + 86400 ; expire + 3600 ; minimum + ) + IN NS ns.archive.thegeneralist01.com. + ns IN A 100.86.129.23 + @ IN A 100.86.129.23 + ''; +in +{ + services.coredns = { + enable = true; + config = '' + internal.thegeneralist01.com:53 { + file ${internalZoneFile} + log + errors + } + + archive.thegeneralist01.com:53 { + file ${archiveZoneFile} + log + errors + } + + .:53 { + forward . 100.100.100.100 45.90.28.181 45.90.30.181 + cache + log + errors + } + ''; + }; + + networking.firewall.allowedUDPPorts = [ 53 ]; + networking.firewall.allowedTCPPorts = [ 53 ]; +} diff --git a/hosts/thegeneralist-central/garage.nix b/hosts/thegeneralist-central/garage.nix new file mode 100644 index 0000000..ea38243 --- /dev/null +++ b/hosts/thegeneralist-central/garage.nix @@ -0,0 +1,18 @@ +{ pkgs, ... }: { + virtualisation.docker.enable = true; + virtualisation.oci-containers.containers.archivebox = { + image = "ghcr.io/archivebox/archivebox:main"; + ports = [ "127.0.0.1:8000:8000" ]; + volumes = [ + "/mnt/usb/services/archivebox/data:/data" + ]; + environment = { + ALLOWLIST_HOSTS = "localhost"; + CSRF_TRUSTED_ORIGINS = "https://archive.thegeneralist01.com,127.0.0.1:8000"; + REVERSE_PROXY_USER_HEADER = "X-Remote-User"; + REVERSE_PROXY_WHITELIST = "127.0.0.1/32,100.86.129.23/32"; + }; + }; + + environment.systemPackages = [ pkgs.docker ]; +} diff --git a/hosts/thegeneralist-central/jellyfin/default.nix b/hosts/thegeneralist-central/jellyfin/default.nix new file mode 100644 index 0000000..8192636 --- /dev/null +++ b/hosts/thegeneralist-central/jellyfin/default.nix @@ -0,0 +1,60 @@ +{ pkgs, ... }: +let + acmeDomain = "thegeneralist01.com"; + domain = "internal.${acmeDomain}"; + + ssl = { + forceSSL = true; + quic = true; + useACMEHost = domain; + }; +in +{ + environment.systemPackages = with pkgs; [ + jellyfin + jellyfin-web + jellyfin-ffmpeg + ]; + + services.jellyfin = { + enable = true; + package = pkgs.jellyfin; + group = "jellyfin"; + user = "jellyfin"; + + cacheDir = "/mnt/usb/jellyfin/cache"; + dataDir = "/mnt/usb/jellyfin/data"; + configDir = "/mnt/usb/jellyfin/data/config"; + logDir = "/mnt/usb/jellyfin/data/log"; + }; + + services.nginx.virtualHosts.${domain} = ssl // { + listen = [ + { + addr = "100.86.129.23"; + port = 443; + ssl = true; + } + { + addr = "100.86.129.23"; + port = 80; + } + ]; + + locations."/" = { + proxyPass = "http://127.0.0.1:8096"; + recommendedProxySettings = true; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + # tell nginx not to buffer the response. send it as it comes. + proxy_buffering off; + + # give jellyfin plenty of time to transcode + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + ''; + }; + }; +} diff --git a/hosts/thegeneralist-central/site.nix b/hosts/thegeneralist-central/site.nix index 7af1517..6d9ebd4 100644 --- a/hosts/thegeneralist-central/site.nix +++ b/hosts/thegeneralist-central/site.nix @@ -6,7 +6,7 @@ useACMEHost = domain; }; in { - imports = [ ./acme ]; + imports = [ ./acme ./dns.nix ./jellyfin ]; # Nginx services.nginx = { diff --git a/modules/dotfiles/nvim/lua/thegeneralist/init.lua b/modules/dotfiles/nvim/lua/thegeneralist/init.lua index 058fcd1..e16be77 100644 --- a/modules/dotfiles/nvim/lua/thegeneralist/init.lua +++ b/modules/dotfiles/nvim/lua/thegeneralist/init.lua @@ -46,7 +46,6 @@ autocmd('LspAttach', { vim.keymap.set("n", "vca", function() vim.lsp.buf.code_action() end, opts("View code actions")) vim.keymap.set("i", "", function() vim.lsp.buf.code_action() end, opts("View code actions")) vim.keymap.set("n", "va", function() - -- TODO: this local params = vim.lsp.util.make_range_params() params.context = { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } local result, err = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 1000) diff --git a/modules/linux/dns.nix b/modules/linux/dns.nix index fd51dec..d900e1f 100644 --- a/modules/linux/dns.nix +++ b/modules/linux/dns.nix @@ -1,8 +1,7 @@ { config, lib, ... }: let - inherit (lib) concatStringsSep; + inherit (lib) mkIf concatStringsSep; in { - # TODO: add fallback & check other options - services.resolved = { + services.resolved = mkIf (!config.isServer) { enable = true; extraConfig = config.dnsServers