summaryrefslogtreecommitdiff
path: root/hostnix
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2024-02-15 02:31:58 -0500
committerJoe Mou <dev@mou.fo>2024-02-15 02:31:58 -0500
commitd1dd864df4f2b8acb0082b1198639cc5d7d9a7f9 (patch)
treea1df89f3db580ac89645cdb4f86c673108dfe8f3 /hostnix
parent0e314643653c1ede88647510bfe103a20f71b0d0 (diff)
Port configs from weebnix
Diffstat (limited to 'hostnix')
-rw-r--r--hostnix/elmo/Makefile7
-rw-r--r--hostnix/elmo/configuration.nix78
-rw-r--r--hostnix/elmo/desktop.nix39
-rw-r--r--hostnix/elmo/dyndns.nix78
-rw-r--r--hostnix/elmo/home-assistant.nix116
-rw-r--r--hostnix/elmo/oidc.nix54
-rw-r--r--hostnix/elmo/privacy-frontends.nix15
-rw-r--r--hostnix/elmo/syncthing.nix136
8 files changed, 448 insertions, 75 deletions
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" ];
+}