From d1dd864df4f2b8acb0082b1198639cc5d7d9a7f9 Mon Sep 17 00:00:00 2001 From: Joe Mou Date: Thu, 15 Feb 2024 02:31:58 -0500 Subject: Port configs from weebnix --- hostnix/elmo/Makefile | 7 ++ hostnix/elmo/configuration.nix | 78 +++++++++++---------- hostnix/elmo/desktop.nix | 39 ----------- hostnix/elmo/dyndns.nix | 78 +++++++++++++++++++++ hostnix/elmo/home-assistant.nix | 116 +++++++++++++++++++++++++++++++ hostnix/elmo/oidc.nix | 54 +++++++++++++++ hostnix/elmo/privacy-frontends.nix | 15 ++++ hostnix/elmo/syncthing.nix | 136 +++++++++++++++++++++++++++++++++++++ 8 files changed, 448 insertions(+), 75 deletions(-) create mode 100644 hostnix/elmo/Makefile delete mode 100644 hostnix/elmo/desktop.nix create mode 100644 hostnix/elmo/dyndns.nix create mode 100644 hostnix/elmo/home-assistant.nix create mode 100644 hostnix/elmo/oidc.nix create mode 100644 hostnix/elmo/privacy-frontends.nix create mode 100644 hostnix/elmo/syncthing.nix (limited to 'hostnix/elmo') diff --git a/hostnix/elmo/Makefile b/hostnix/elmo/Makefile new file mode 100644 index 0000000..81c317d --- /dev/null +++ b/hostnix/elmo/Makefile @@ -0,0 +1,7 @@ +push: + rsync --rsync-path='sudo rsync' *.nix elmo.lan:/etc/nixos/ + +switch: push + ssh elmo.lan sudo nixos-rebuild switch + +.PHONY: push switch diff --git a/hostnix/elmo/configuration.nix b/hostnix/elmo/configuration.nix index 3ab9922..bc9b84a 100644 --- a/hostnix/elmo/configuration.nix +++ b/hostnix/elmo/configuration.nix @@ -1,68 +1,74 @@ -# Edit this configuration file to define what should be installed on -# your system. Help is available in the configuration.nix(5) man page -# and in the NixOS manual (accessible by running ‘nixos-help’). - { config, pkgs, ... }: { - imports = - [ # Include the results of the hardware scan. - ./desktop.nix - ./hardware-configuration.nix - ./system.nix - ]; + imports = [ + ./dyndns.nix + ./hardware-configuration.nix + ./home-assistant.nix + ./oidc.nix + ./privacy-frontends.nix + ./syncthing.nix + ./system.nix + ]; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + + security.acme.acceptTerms = true; + security.acme.defaults.email = "hostmaster@mou.fo"; + # TODO switch to production certs + security.acme.defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; security.sudo.wheelNeedsPassword = false; - # Define a user account. Don't forget to set a password with ‘passwd’. users.users.joe = { isNormalUser = true; description = "Joe Mou"; - extraGroups = [ "networkmanager" "wheel" ]; + extraGroups = [ "networkmanager" "wheel" "syncthing" ]; packages = with pkgs; [ - firefox - # thunderbird + jq + sqlite-interactive ]; openssh.authorizedKeys.keys = [ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDHcqQDnH0LcUWFV+cd9aABfM55+72UAn1ZY5x5bHwPL4IEgT1nagbi1X3FwCJkh2rEcDsHl4FflcNfh+IyU2VyuQfRIXl7CKP040ovXmPVzgU8JiEDrrRebpZ7aHCKk+4Q6cJ8dzAI1sRogVyYrV0hLB4yeMnVexkIcsQMt5pXQ1zodlzHfW1W7OWQDbKjIyh4pv1KJ/IIjiBIt7l+OCqi7wZK4ubmxyMhmZx1xpRC5mz2/29Mpt+1VgCmxC7Hv1SG7yu+U0s4eJ3kbzFKNcZ/bCYqFxK48dT66qhS3qZFg09omumG8IbEUcx+WqM1V7WhiJYig9x5ovmMToQXTJ5uxLtcovnxO/53IgwV7INvmr8+NhD+LlkQfSjjUAW09bhVj9sKIW2bbrIxlOT8z6CQY9zGXNXi8u1oxPt3Q7d3rMxoWhWnr2l3Ok7OIkWooOK3e0NdhgIAxZKlYJAmIYaX391sGTyiuwUy96mg5JcYsLYMfkEg/Sv+2vQp8kkvB0E= joe@sparky" ]; }; - # Allow unfree packages nixpkgs.config.allowUnfree = true; - # List packages installed in system profile. To search, run: - # $ nix search wget environment.systemPackages = with pkgs; [ - # vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. - # wget + dig + file + gitFull + tmux + tree ]; - # Some programs need SUID wrappers, can be configured further or are - # started in user sessions. - # programs.mtr.enable = true; - # programs.gnupg.agent = { - # enable = true; - # enableSSHSupport = true; - # }; - - # List services that you want to enable: + programs.vim.defaultEditor = true; + programs.nano.enable = false; - # Enable the OpenSSH daemon. services.openssh.enable = true; - # Open ports in the firewall. - # networking.firewall.allowedTCPPorts = [ ... ]; - # networking.firewall.allowedUDPPorts = [ ... ]; - # Or disable the firewall altogether. - # networking.firewall.enable = false; + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + }; + + # TODO remove upon switching to production certs + services.oauth2_proxy.extraConfig = { + "ssl-insecure-skip-verify" = true; + "ssl-upstream-insecure-skip-verify" = true; + }; + + networking.firewall.allowedTCPPorts = [ 80 443 ]; # This value determines the NixOS release from which the default # settings for stateful data, like file locations and database versions - # on your system were taken. It‘s perfectly fine and recommended to leave + # on your system were taken. It's perfectly fine and recommended to leave # this value at the release version of the first install of this system. # Before changing this value read the documentation for this option # (e.g. man configuration.nix or on https://nixos.org/nixos/options.html). system.stateVersion = "23.11"; # Did you read the comment? - } diff --git a/hostnix/elmo/desktop.nix b/hostnix/elmo/desktop.nix deleted file mode 100644 index 376cbbf..0000000 --- a/hostnix/elmo/desktop.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ ... }: - -{ - # Enable the X11 windowing system. - services.xserver.enable = true; - - # Enable the Pantheon Desktop Environment. - services.xserver.displayManager.lightdm.enable = true; - services.xserver.desktopManager.pantheon.enable = true; - - # Configure keymap in X11 - services.xserver = { - layout = "us"; - xkbVariant = ""; - }; - - # Enable CUPS to print documents. - services.printing.enable = true; - - # Enable sound with pipewire. - sound.enable = true; - hardware.pulseaudio.enable = false; - security.rtkit.enable = true; - services.pipewire = { - enable = true; - alsa.enable = true; - alsa.support32Bit = true; - pulse.enable = true; - # If you want to use JACK applications, uncomment this - #jack.enable = true; - - # use the example session manager (no others are packaged yet so this is enabled by default, - # no need to redefine it in your config for now) - #media-session.enable = true; - }; - - # Enable touchpad support (enabled default in most desktopManager). - # services.xserver.libinput.enable = true; -} diff --git a/hostnix/elmo/dyndns.nix b/hostnix/elmo/dyndns.nix new file mode 100644 index 0000000..a59c665 --- /dev/null +++ b/hostnix/elmo/dyndns.nix @@ -0,0 +1,78 @@ +{ config, pkgs, ... }: + +{ + # Needs to be started manually, and the key added to nameservers. + # Based on https://nixos.org/manual/nixos/stable/index.html#module-security-acme-config-dns + systemd.services.sig0-keygen = { + unitConfig = { + ConditionPathExists = "!/var/lib/secrets/${config.networking.fqdn}.id"; + }; + serviceConfig = { + Type = "oneshot"; + }; + path = [ pkgs.bind ]; + scriptArgs = config.networking.fqdn; + script = '' + mkdir -p /var/lib/secrets + chmod 755 /var/lib/secrets + cd /var/lib/secrets + dnssec-keygen -a RSASHA512 -b 2048 -n HOST -T KEY $1. > $1.id + ''; + }; + + systemd.services.dyndns = { + requires = [ "network-online.target" ]; + after = [ "network-online.target" ]; + unitConfig = { + AssertPathExists = "/var/lib/secrets/${config.networking.fqdn}.id"; + # Defer errors for ~45min, throttle e-mails to ~hourly. + StartLimitIntervalSec = "1hr"; + StartLimitBurst = "45"; + }; + serviceConfig = { + Type = "oneshot"; + Restart = "on-failure"; + RestartSec = "1min"; + }; + path = [ pkgs.dnsutils pkgs.gawk pkgs.iproute2 ]; + scriptArgs = config.networking.fqdn; + script = '' + RR=''${1%%.*}.dynamic.''${1#*.} + + IP4=`dig +short @1.1.1.1 ch txt whoami.cloudflare | tr -d \"` + if [ -z "$IP4" ]; then + echo "Missing IP: $IP4" >&2 + exit 100 + fi + + # Follow some RFC 6724 default address guidance, excluding ULA. + # It might be more robust to bind a public source socket (RFC 5014). + IP6=`ip -6 address show scope global -deprecated | awk -F'[ /]+' '$2 == "inet6" && $3 !~ /^f[cd]/ { print $3; exit }'` + + OLDIP4=`dig +short @popfresh.mou.fo $RR A 2> /dev/null` + OLDIP6=`dig +short @popfresh.mou.fo $RR AAAA 2> /dev/null` + # [ "x$IP" = "x$OLDIP4" ] && exit 0 # no update + if [ "x$IP4" = "x$OLDIP4" -a "x$IP6" = "x$OLDIP6" ]; then + exit 0 + fi + + nsupdate -v -k /var/lib/secrets/`< /var/lib/secrets/$1.id`.private <<. + update delete $RR. A + update add $RR. 300 A $IP4 + update delete $RR. AAAA + ''${IP6:+update add $RR. 300 AAAA $IP6} + update delete $RR. TXT + update add $RR. 300 TXT "v=spf1 include:_spf.mou.fo ~all" + send + . + ''; + }; + + systemd.timers.dyndns = { + wantedBy = [ "multi-user.target" ]; + timerConfig = { + OnStartupSec = "10"; + OnUnitActiveSec = "1min"; + }; + }; +} diff --git a/hostnix/elmo/home-assistant.nix b/hostnix/elmo/home-assistant.nix new file mode 100644 index 0000000..b580a57 --- /dev/null +++ b/hostnix/elmo/home-assistant.nix @@ -0,0 +1,116 @@ +{ pkgs, ... }: + +{ + services.postgresql = { + enable = true; + ensureDatabases = [ "hass" ]; + ensureUsers = [{ + name = "hass"; + ensureDBOwnership = true; + }]; + }; + + services.home-assistant = { + enable = true; + extraPackages = ps: with ps; [ psycopg2 ]; + extraComponents = [ + "androidtv_remote" + "apple_tv" + "cast" + "homekit_controller" + "hue" + "spotify" + "esphome" + "met" + "radio_browser" + ]; + customComponents = [ + ( + pkgs.buildHomeAssistantComponent rec { + owner = "BeryJu"; + domain = "auth_header"; + version = "1.10"; + src = pkgs.fetchFromGitHub { + inherit owner; + repo = "hass-auth-header"; + rev = "refs/tags/v${version}"; + hash = "sha256-dSmY3d8Kx0pXl+20dTGAYgjSH6OhNh53jPX7VLCZs7Y="; + }; + dontBuild = true; + } + ) + ( + pkgs.buildHomeAssistantComponent rec { + owner = "make-all"; + domain = "tuya_local"; + version = "2023.12.1"; + src = pkgs.fetchFromGitHub { + inherit owner; + repo = "tuya-local"; + rev = "refs/tags/${version}"; + hash = "sha256-vi5EmtXAyXaUbJl+yAT5EL0yYb3XFRaAj6fybQRCM4A="; + }; + propagatedBuildInputs = with pkgs.home-assistant.python.pkgs; [ + ( + buildPythonPackage rec { + pname = "tinytuya"; + version = "1.13.1"; + format = "wheel"; + src = pkgs.fetchPypi { + inherit pname version format; + hash = "sha256-j7t4P4U9iuVHyb6HASkf7LmBheHN32IjdKE60HUbjIE="; + }; + } + ) + colorama + ]; + dontBuild = true; + } + ) + ]; + config = { + default_config = { }; + http = { + server_host = "::1"; + trusted_proxies = [ "::1" ]; + use_x_forwarded_for = true; + }; + recorder.db_url = "postgresql://@/hass"; + auth_header = { }; + }; + }; + + services.nginx.virtualHosts."ha.elmo.mou.fo" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://[::1]:8123"; + proxyWebsockets = true; + extraConfig = '' + # This is frequently used in examples but without clear explanation. It + # might help with WebSockets. + proxy_buffering off; + # oauth2_proxy NixOS module sets some non-standard headers, but we need + # the preferred_username claim. + auth_request_set $preferred_username $upstream_http_x_auth_request_preferred_username; + proxy_set_header X-Forwarded-Preferred-Username $preferred_username; + ''; + }; + # Duplicate relevant parts of root route to skip oauth2-proxy module magic. + locations."/api/" = { + proxyPass = "http://[::1]:8123"; + proxyWebsockets = true; + extraConfig = '' + proxy_buffering off; + ''; + }; + # Disable service worker caching that works improperly with reverse proxy. + # https://github.com/home-assistant/frontend/issues/14836 + # https://community.home-assistant.io/t/disabling-service-worker-reverse-proxy-auth-causes-issues/167082 + locations."/service_worker.js" = { + return = ''410 "Service worker disabled: https://github.com/home-assistant/frontend/issues/14836"''; + }; + }; + + services.oauth2_proxy.nginx.virtualHosts = [ "ha.elmo.mou.fo" ]; +} diff --git a/hostnix/elmo/oidc.nix b/hostnix/elmo/oidc.nix new file mode 100644 index 0000000..b2c34e5 --- /dev/null +++ b/hostnix/elmo/oidc.nix @@ -0,0 +1,54 @@ +{ ... }: + +{ + services.keycloak = { + enable = true; + database.passwordFile = "/var/lib/secrets/keycloak.dbpass"; + settings = { + hostname = "kc.elmo.mou.fo"; + http-host = "127.0.0.1"; + http-port = 7567; + proxy = "edge"; + }; + }; + + services.nginx.virtualHosts."kc.elmo.mou.fo" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = "http://127.0.0.1:7567"; + # We can handle oauth2-proxy callbacks on any subdomain, but the Keycloak + # subdomain is the least arbitrary. + locations."/oauth2/".proxyPass = "http://127.0.0.1:4180"; + }; + + # Work around "upstream sent too big header" because of large tokens. + services.nginx.appendHttpConfig = '' + proxy_buffers 8 16k; + proxy_buffer_size 16k; + ''; + + # The oauth2_proxy module has a magic nginx.virtualHosts option that rewrites + # nginx configs. It's mostly unhelpful, but we use it for brevity. In + # particular, it configures Traefik-like ForwardAuth authentication with + # auth_request. Note if this resource is missing for whatever reason, the + # module magic will fail open (auth_request unset). + services.oauth2_proxy = { + enable = true; + cookie.domain = "elmo.mou.fo"; + setXauthrequest = true; # include claims + email.domains = [ "*" ]; # allow any authenticated user + # https://oauth2-proxy.github.io/oauth2-proxy/configuration/providers/keycloak_oidc + provider = "keycloak-oidc"; + clientID = "oauth2-proxy"; + # Sets OAUTH2_PROXY_COOKIE_SECRET and OAUTH2_PROXY_CLIENT_SECRET. + keyFile = "/var/lib/secrets/oauth2-proxy.env"; + redirectURL = "https://kc.elmo.mou.fo/oauth2/callback"; + extraConfig = { + "oidc-issuer-url" = "https://kc.elmo.mou.fo/realms/prod"; + "whitelist-domain" = ".elmo.mou.fo"; + # https://github.com/oauth2-proxy/oauth2-proxy/issues/1612#issuecomment-1099217761 + "insecure-oidc-allow-unverified-email" = true; + "oidc-email-claim" = "sub"; + }; + }; +} diff --git a/hostnix/elmo/privacy-frontends.nix b/hostnix/elmo/privacy-frontends.nix new file mode 100644 index 0000000..b410962 --- /dev/null +++ b/hostnix/elmo/privacy-frontends.nix @@ -0,0 +1,15 @@ +{ ... }: + +{ + services.libreddit = { + enable = true; + address = "[::1]"; + port = 7682; + }; + + services.nginx.virtualHosts."lr.elmo.mou.fo" = { + enableACME = true; + forceSSL = true; + locations."/".proxyPass = "http://[::1]:7682"; + }; +} diff --git a/hostnix/elmo/syncthing.nix b/hostnix/elmo/syncthing.nix new file mode 100644 index 0000000..a17950b --- /dev/null +++ b/hostnix/elmo/syncthing.nix @@ -0,0 +1,136 @@ +{ ... }: + +let + staggeredVersioning = { + type = "staggered"; + params = { + cleanInterval = "3600"; + maxAge = "31536000"; + }; + }; +in +{ + systemd.tmpfiles.rules = [ + "d /var/lib/syncthing 0775 syncthing syncthing" + ]; + + systemd.services.syncthing = { + serviceConfig.UMask = "0002"; + }; + + services.syncthing = { + enable = true; + openDefaultPorts = true; + # Syncthing supports named sockets but the NixOS module assumes network. + guiAddress = "[::1]:8384"; + settings = { + devices = { + "Asus Nexus 7" = { + id = "BVZARYC-2D56A6I-V6L2VQF-MU7IVCT-ITJTCGQ-IGMZG2W-RZRCTOT-K4GDHAY"; + }; + "DESKTOP-SFVBFBU" = { + id = "FQEK2MG-2AVMHEM-H6KASQ3-RTA3Z3F-A4R4MUY-YELZVRQ-6QUDSHA-DZZN7AJ"; + autoAcceptFolders = true; + }; + "Joes-iPhone-6" = { + id = "F5APH5K-XXO454B-6YU4BTT-YPHF4KT-OD7YF5Y-JTWJRSU-UNJ2KZK-IVJZNQT"; + autoAcceptFolders = true; + }; + "iPad" = { + id = "U7D7667-RFEFHXX-TUJGGII-CE62S6P-YC6MWNV-4LBJ4YJ-5ZUZVPT-GBC5PQH"; + autoAcceptFolders = true; + }; + "maxsettings-C6554E6AF22F" = { + id = "HO3QQZO-RDWYDB6-U74ZQRC-FP7YPB2-IPB2EBC-KIQ5JII-FD4KBXR-J3GCOQ5"; + autoAcceptFolders = true; + }; + "penguin" = { + id = "5BEB6SZ-YAPV3CC-54RZF3V-HQXXP3Y-TTVDWDU-SHHNVCH-IXKZ3O2-6RXG6QL"; + autoAcceptFolders = true; + }; + "sparky" = { + id = "FE43LDI-33WS467-LIGFWCC-5PPSKN6-GYODEWJ-KLQZLN7-H3GYGLG-IJ2JGQW"; + autoAcceptFolders = true; + }; + "steamdeck" = { + id = "SCUAABL-XU6AS5H-IZZE4PN-LY5J4LH-OYZMXTU-2UMTVUL-I7B7QKE-ZBPSAQ5"; + autoAcceptFolders = true; + }; + "weeber.mou.fo" = { + id = "PRE6XCX-7JDMJGJ-6TPMHOS-TP2AT3S-T6CAR3Z-URL5EEU-HVXUDJ4-C5UJ2AV"; + autoAcceptFolders = true; + }; + }; + folders = { + "Documents" = { + id = "bhemx-9nh3v"; + path = "~/Documents"; + versioning = staggeredVersioning; + devices = [ "sparky" "weeber.mou.fo" ]; + }; + "Downloads" = { + id = "kvq6q-axjhu"; + path = "~/Downloads"; + versioning = staggeredVersioning; + devices = [ "sparky" "weeber.mou.fo" ]; + }; + "Game/Documents/Bioshock" = { + id = "7zhqz-x6uvw"; + path = "~/Game/Documents/Bioshock"; + versioning = staggeredVersioning; + devices = [ "weeber.mou.fo" ]; + }; + "Game/Epic Games/TheTalosPrinciple/UserData" = { + id = "vek7u-iausx"; + path = "~/Game/Epic Games/TheTalosPrinciple/UserData"; + versioning = staggeredVersioning; + devices = [ "DESKTOP-SFVBFBU" "maxsettings-C6554E6AF22F" "weeber.mou.fo" ]; + }; + "Game/PCSX2" = { + id = "chxsg-hpqgm"; + path = "~/Game/PCSX2"; + versioning = staggeredVersioning; + devices = [ "maxsettings-C6554E6AF22F" "weeber.mou.fo" ]; + }; + "Pictures" = { + id = "vfjsd-4fczh"; + path = "~/Pictures"; + versioning = staggeredVersioning; + devices = [ "sparky" "weeber.mou.fo" ]; + }; + "Sync" = { + id = "7thks-5badk"; + path = "~/Sync"; + versioning = staggeredVersioning; + devices = [ + "Asus Nexus 7" + "DESKTOP-SFVBFBU" + "Joes-iPhone-6" + "iPad" + "penguin" + "sparky" + "weeber.mou.fo" + ]; + }; + "iPad" = { + id = "qtzmu-fqdrs"; + path = "~/iPad"; + versioning = staggeredVersioning; + devices = [ "iPad" "weeber.mou.fo" ]; + }; + }; + }; + }; + + services.nginx.virtualHosts."st.elmo.mou.fo" = { + enableACME = true; + forceSSL = true; + locations."/" = { + proxyPass = "http://[::1]:8384"; + # https://docs.syncthing.net/users/faq.html#why-do-i-get-host-check-error-in-the-gui-api + recommendedProxySettings = false; + }; + }; + + services.oauth2_proxy.nginx.virtualHosts = [ "st.elmo.mou.fo" ]; +} -- cgit v1.3.1