# Edit this configuration file to define what should be installed on # your system. Help is available in the configuration.nix(5) man page, on # https://search.nixos.org/options and in the NixOS manual (`nixos-help`). { config, lib, pkgs, ... }: { imports = [ # Include the results of the hardware scan. ./hardware/samsehu.nix ./samsehu/matrix-conduit.nix ]; # Use the `systemd-boot` boot loader boot.loader.systemd-boot.enable = true; # Added following instructions of openzfs configuration # randomly generated with `head -c4 /dev/urandom | od -A none -t x4` networking.hostId = "3e52e44f"; boot.supportedFilesystems = [ "zfs" ]; boot.zfs.forceImportRoot = false; boot.zfs.extraPools = [ "zroot" ]; networking.hostName = "samsehu"; # Define your hostname. # Pick only one of the below networking options. networking.networkmanager.enable = true; # Easiest to use and most distros use this by default. # Set your time zone. time.timeZone = "America/Denver"; # Select internationalisation properties. i18n.defaultLocale = "en_US.UTF-8"; # console = { # font = "Lat2-Terminus16"; # keyMap = "us"; # useXkbConfig = true; # use xkb.options in tty. # }; # Enable CUPS to print documents. services.printing.enable = true; # Enable sound. sound.enable = true; hardware.pulseaudio.enable = true; # Define a user account. Don't forget to set a password with ‘passwd’. users.users.geemili = { isNormalUser = true; extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. packages = with pkgs; [ ]; }; users.users.desttinghim = { isNormalUser = true; extraGroups = [ "wheel" ]; # Enable ‘sudo’ for the user. packages = with pkgs; [ ]; }; # List packages installed in system profile. To search, run: # $ nix search wget environment.systemPackages = with pkgs; [ helix wget git juanfont-headscale.headscale # install to allow debugging/control of headscale using the CLI # Plugins for cockpit cockpit-tailscale cockpit-zfs-manager ]; environment.variables = { EDITOR = "hx"; VISUAL = "hx"; }; # List services that you want to enable: # Enable the OpenSSH daemon. services.openssh = { enable = true; settings.PasswordAuthentication = false; settings.KbdInteractiveAuthentication = false; settings.PermitRootLogin = "no"; }; services.cockpit = { enable = true; openFirewall = true; settings = { WebService = { Origins = "https://cockpit.samsehu.perli.casa wss://cockpit.samsehu.perli.casa"; ProtocolHeader = "X-Forwarded-Proto"; LoginTo = false; }; }; }; services.udisks2.enable = true; # Multimedia group users.groups.multimedia = {}; users.users.lidarr.extraGroups = [ "aria2" ]; users.users.radarr.extraGroups = [ "aria2" ]; users.users.readarr.extraGroups = [ "aria2" ]; users.users.sonarr.extraGroups = [ "aria2" ]; users.users.bazarr.extraGroups = [ "multimedia" "aria2" ]; systemd.tmpfiles.rules = [ "d /zroot/media 0770 - multimedia - -" ]; services.jellyfin = { enable = true; group = "multimedia"; }; services.lidarr = { enable = true; group = "multimedia"; }; services.radarr = { enable = true; group = "multimedia"; }; services.readarr = { enable = true; group = "multimedia"; }; services.sonarr = { enable = true; group = "multimedia"; }; services.bazarr = { enable = true; user = "bazarr"; group = "multimedia"; }; services.prowlarr = { enable = true; }; users.users.komga.extraGroups = [ "multimedia" ]; services.komgaCustom = { enable = true; group = "multimedia"; settings = { spring.security.oauth2.client = { registration.dex = { provider = "dex"; client-id = "komga"; client-secret = "insecure_secret"; client-name = "Komga"; scope = "openid,email"; authorization-grant-type = "authorization_code"; redirect-uri = "{baseUrl}/{action}/oauth2/code/{registrationId}"; }; provider.dex = { user-name-attribute = "sub"; issuer-uri = "https://dex.samsehu.perli.casa"; }; }; }; }; systemd.services."netns@" = { description = "%I network namespace"; before = [ "network.target" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = "${pkgs.iproute}/bin/ip netns add %I"; ExecStop = "${pkgs.iproute}/bin/ip netns del %I"; }; }; systemd.services.wg = { description = "wg network interface"; bindsTo = [ "netns@wg.service" ]; requires = [ "network-online.target" ]; after = [ "netns@wg.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; ExecStart = with pkgs; writers.writeBash "wg-up" '' set -e # Create wireguard ${iproute}/bin/ip link add wg0 type wireguard # move to wg network namespace ${iproute}/bin/ip link set wg0 netns wg # Connect to vpn ${iproute}/bin/ip -n wg address add 10.65.64.220/32 dev wg0 ${iproute}/bin/ip -n wg -6 address add fc00:bbbb:bbbb:bb01::2:40db/128 dev wg0 ${iproute}/bin/ip netns exec wg ${wireguard-tools}/bin/wg setconf wg0 /var/wireguard-keys/chief-frog.conf # Open network ${iproute}/bin/ip -n wg link set dev lo up ${iproute}/bin/ip -n wg link set wg0 up ${iproute}/bin/ip -n wg route add default dev wg0 ${iproute}/bin/ip -n wg -6 route add default dev wg0 ''; ExecStop = with pkgs; writers.writeBash "wg-down" '' ${iproute}/bin/ip -n wg route del default dev wg0 ${iproute}/bin/ip -n wg -6 route del default dev wg0 ${iproute}/bin/ip -n wg link del wg0 ''; }; }; services.aria2 = { enable = true; extraArguments = "--bt-external-ip=10.65.64.220 --input-file=/var/lib/aria2/aria2.session --save-session-interval=60"; }; systemd.services."aria2" = { bindsTo = [ "netns@wg.service" ]; requires = [ "network-online.target" ]; after = [ "wg.service" ]; serviceConfig = { NetworkNamespacePath = "/var/run/netns/wg"; }; }; # reverse proxy the aria2 rpc from the `wg` network namespace to a unix domain socket systemd.services."aria2-unix-domain-rpc" = { bindsTo = [ "aria2.service" ]; after = [ "aria2.service" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { NetworkNamespacePath = "/var/run/netns/wg"; Type = "simple"; User = config.services.caddy.user; Group = config.services.caddy.group; RuntimeDirectory = "aria2"; ExecStart = with pkgs; writers.writeBash "aria2-unix-domain-rpc-listener" '' set -e ${socat}/bin/socat UNIX-LISTEN:/run/aria2/rpc.sock,reuseaddr,fork TCP:localhost:6800 ''; }; }; systemd.services.dex.serviceConfig = { StateDirectory = "dex"; WorkingDirectory = "%S/dex"; }; services.dex = { enable = true; environmentFile = config.age.secrets.DEX_ENVIRONMENT_FILE.path; settings = { issuer = "https://dex.samsehu.perli.casa"; web.http = "127.0.0.1:5556"; storage = { type = "sqlite3"; config.file = "./dex.db"; }; # services that can get a token from our dex instance staticClients = [ { id = "forgejo"; secretEnv = "OIDC_APP_SECRET_FORGEJO"; name = "Forgejo"; redirectURIs = [ "https://git.samsehu.perli.casa/user/oauth2/dex/callback" ]; } { id = "headscale"; secretEnv = "OIDC_APP_SECRET_HEADSCALE"; name = "Headscale"; redirectURIs = [ "https://headscale.samsehu.perli.casa/oidc/callback" ]; } { id = "nextcloud"; secret = "insecure_secret"; name = "Nextcloud"; redirectURIs = [ "https://nextcloud.samsehu.perli.casa/apps/oidc_login/oidc" "https://nextcloud.samsehu.perli.casa/index.php/apps/oidc_login/oidc" ]; } { id = "jellyfin"; secret = "insecure_secret"; name = "Jellyfin"; redirectURIs = [ "https://jellyfin.samsehu.perli.casa/sso/OID/redirect/dex" ]; } { id = "komga"; secret = "insecure_secret"; name = "Komga"; redirectURIs = [ "https://komga.samsehu.perli.casa/login/oauth2/code/dex" ]; } ]; # authentication sources connectors = [ { type = "ldap"; id = "lldap"; name = "LLDAP"; config = { host = "127.0.0.1:3890"; insecureNoSSL = true; insecureSkipVerify = true; startTLS = false; bindDN = "uid=Immovable1809,ou=people,dc=samsehu,dc=perli,dc=casa"; bindPW = "$LLDAP_ADMIN_PASSWORD"; userSearch = { baseDN = "ou=people,dc=samsehu,dc=perli,dc=casa"; username = "uid"; idAttr = "uid"; emailAttr = "mail"; nameAttr = "displayName"; preferredUsernameAttr = "uid"; }; groupSearch = { baseDN = "ou=groups,dc=samsehu,dc=perli,dc=casa"; filter = "(objectClass=groupOfUniqueNames)"; userMatchers = [ { userAttr = "DN"; groupAttr = "member"; } ]; nameAttr = "cn"; }; }; } ]; }; }; services.blocky = { enable = true; settings = { ports.dns = 53; ports.http = 4000; upstreams = { # Picks 2 random resolvers and returns answer from fastest one. Read docs for more info. strategy = "parallel_best"; groups.default = [ # CloudFlare "https://one.one.one.one/dns-query" # OpenDNS "https://doh.opendns.com/dns-query" # Google "8.8.8.8" "8.8.4.4" "2001:4860:4860::8888" "2001:4860:4860::8844" # Comcast/Our ISP "75.75.75.75" "75.75.76.76" ]; }; bootstrapDns = { upstream = "https://one.one.one.one/dns-query"; ips = [ "1.1.1.1" "1.0.0.1" ]; }; blocking = { blackLists = { ads = ["https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"]; }; clientGroupsBlock = { default = [ "ads" ]; }; }; customDNS = { rewrite = { "cockpit.samsehu.perli.casa" = "samsehu.perli.casa"; "git.samsehu.perli.casa" = "samsehu.perli.casa"; "nextcloud.samsehu.perli.casa" = "samsehu.perli.casa"; }; mapping = { "samsehu.perli.casa" = "192.168.0.69"; }; }; }; }; services.forgejo = { enable = true; settings = { server.ROOT_URL = "https://git.samsehu.perli.casa/"; server.HTTP_ADDR = "127.0.0.1"; }; }; # lldap LDAP authentication server users.users.lldap = { # allocates the `uid` in the range 100-999, which indicates to software like login managers that it should not be displayed to the user. isSystemUser = true; group = "lldap"; }; users.groups.lldap = {}; services.lldap = { enable = true; settings = { ldap_base_dn = "dc=samsehu,dc=perli,dc=casa"; # Sets the root administrator's user name ldap_user_dn = "Immovable1809"; http_host = "127.0.0.1"; }; environment = { LLDAP_LDAP_USER_PASS_FILE = config.age.secrets.LLDAP_ADMIN_PASSWORD.path; }; }; # Dynamic DNS through duck dns users.users.dynamicdns = { # allocates the `uid` in the range 100-999, which indicates to software like login managers that it should not be displayed to the user. isSystemUser = true; group = "dynamicdns"; }; users.groups.dynamicdns = {}; systemd.services.dynamic-dns-updater = { serviceConfig.User = "dynamicdns"; path = [ pkgs.curl ]; script = "curl --silent --url-query domains=samsehuperli --url-query token@${config.age.secrets.samsehu_DUCK_DNS_TOKEN.path} https://www.duckdns.org/update"; startAt = "hourly"; }; systemd.timers.dynamic-dns-updater = { timerConfig.RandomizedDelaySec = "15m"; }; # Next cloud setup services.nginx.enable = false; services.nextcloud = { enable = true; hostName = "nextcloud.samsehu.perli.casa"; config.adminpassFile = "/var/nextcloud-admin-pass"; config.trustedProxies = [ "100.64.0.3" ]; caching.apcu = true; # OIDC configuration extraOptions = { overwritewebroot = "/"; allow_user_to_change_display_name = false; lost_password_link = "disabled"; oidc_login_provider_url = "https://dex.samsehu.perli.casa"; oidc_login_client_id = "nextcloud"; oidc_login_client_secret = "insecure_secret"; oidc_login_auto_redirect = true; oidc_login_end_session_redirect = false; oidc_login_button_text = "Log in with Dex"; oidc_login_hide_password_form = false; oidc_login_use_id_token = true; config.oidc_login_attributes = { "id" = "preferred_username"; "name" = "name"; "mail" = "mail"; "groups" = "groups"; }; oidc_login_default_group = "oidc"; oidc_login_use_external_storage = true; oidc_login_scope = "openid profile email groups"; oidc_login_proxy_ldap = false; oidc_login_disable_registration = false; oidc_login_redir_fallback = false; oidc_login_alt_login_page = "assets/login.php"; oidc_login_tls_verify = true; oidc_create_groups = false; oidc_login_webdav_enabled = false; oidc_login_password_authentication = false; oidc_login_public_key_caching_time = 86400; oidc_login_min_time_between_jwks_requests = 10; oidc_login_well_known_caching_time = 86400; oidc_login_update_avatar = false; }; # Auto update apps autoUpdateApps.enable = true; extraApps = { oidc_login = pkgs.fetchNextcloudApp { appName = "OpenID Connect Login"; appVersion = "3.0.2"; sha256 = "sha256-cN5azlThKPKRVip14yfUNR85of5z+N6NVI7sg6pSGQI="; license = "agpl3Plus"; url = "https://github.com/pulsejet/nextcloud-oidc-login/releases/download/v3.0.2/oidc_login.tar.gz"; }; gpoddersync = pkgs.fetchNextcloudApp { appName = "Gpodder Sync"; appVersion = "3.8.2"; sha256 = "sha256-eeBvRZUDVIaym0ngfPD2d7aY3SI/7lPWkrYPnqSh5Kw="; license = "agpl3Plus"; url = "https://github.com/thrillfall/nextcloud-gpodder/releases/download/3.8.2/gpoddersync.tar.gz"; }; }; }; services.phpfpm.pools.nextcloud.settings = { "listen.owner" = config.services.caddy.user; "listen.group" = config.services.caddy.group; }; # Reverse proxy with Caddy services.caddy = { enable = true; globalConfig = '' email "fresh.car0178@geemili.xyz" ''; virtualHosts."lldap.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:17170 } respond 403 ''; virtualHosts."headscale.samsehu.perli.casa".extraConfig = '' reverse_proxy localhost:64639 ''; virtualHosts."cockpit.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:9090 } respond 403 ''; virtualHosts."git.samsehu.perli.casa".extraConfig = '' reverse_proxy localhost:3000 ''; virtualHosts."jellyfin.samsehu.perli.casa".extraConfig = '' reverse_proxy localhost:8096 ''; virtualHosts."nextcloud.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { # https://docs.nextcloud.com/server/27/admin_manual/issues/general_troubleshooting.html#service-discovery redir /.well-known/carddav /remote.php/dav 301 redir /.well-known/caldav /remote.php/dav 301 root * ${config.services.nextcloud.package} php_fastcgi unix/${config.services.phpfpm.pools.nextcloud.socket} { root ${config.services.nextcloud.package} capture_stderr } file_server } respond 403 ''; virtualHosts."dex.samsehu.perli.casa".extraConfig = '' reverse_proxy localhost:5556 ''; virtualHosts."aria.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 private_ranges handle @connected_via_tailscale { handle /rpc { reverse_proxy unix//run/aria2/rpc.sock } handle /jsonrpc { reverse_proxy unix//run/aria2/rpc.sock } handle_path /ariang* { root * ${pkgs.ariang}/share/ariang file_server } redir / /ariang/#!/settings/rpc/set?protocol=wss&host=aria.samsehu.perli.casa&port=443&secret=YXJpYTJycGM=&interface=jsonrpc } respond 403 ''; virtualHosts."lid.arr.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:8686 } respond 403 ''; virtualHosts."rad.arr.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:7878 } respond 403 ''; virtualHosts."read.arr.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:8787 } respond 403 ''; virtualHosts."son.arr.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:8989 } respond 403 ''; virtualHosts."baz.arr.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:6767 } respond 403 ''; virtualHosts."prowl.arr.samsehu.perli.casa".extraConfig = '' @connected_via_tailscale remote_ip 100.64.0.0/10 fd7a:115c:a1e0::/48 handle @connected_via_tailscale { reverse_proxy localhost:9696 } respond 403 ''; virtualHosts."komga.samsehu.perli.casa".extraConfig = '' reverse_proxy localhost:25600 ''; }; # Headscale for access to the network while away from home users.users.headscale = { isSystemUser = true; group = "headscale"; }; users.groups.headscale = {}; services.headscale = { enable = true; package = pkgs.juanfont-headscale.headscale; settings = { server_url = "https://headscale.samsehu.perli.casa"; listen_addr = "127.0.0.1:64639"; metrics_listen_addr = "127.0.0.1:64640"; tls_cert_path = null; tls_key_path = null; dns_config = { override_local_dns = true; nameservers = [ "100.64.0.3" ]; magic_dns = true; base_domain = "ts.samsehu.perli.casa"; restricted_nameservers = { "samsehu.perli.casa" = [ "100.64.0.3" ]; }; extra_records = [ { name = "samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "cockpit.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "git.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "nextcloud.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "lldap.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "dex.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "jellyfin.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "aria.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "lid.arr.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "rad.arr.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "read.arr.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "son.arr.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "baz.arr.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "prowl.arr.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } { name = "komga.samsehu.perli.casa"; type = "A"; value = "100.64.0.3"; } ]; }; oidc = { issuer = "https://dex.samsehu.perli.casa"; client_id = "headscale"; client_secret_path = config.age.secrets.OIDC_APP_SECRET_HEADSCALE.path; scope = [ "openid" "profile" "email" ]; }; acl_policy_path = pkgs.writeText "acl_policy.hujson" '' { "groups": { "group:servers": [ "samsehu", ], "group:admin": [ "geemili", "desttinghim", ], }, "acls": [ { "action": "accept", "src": ["group:admin"], "dst": ["*:*"], } ], "ssh": [ { "action": "accept", "src": ["group:admin"], "dst": ["group:servers"], "users": ["group:admin", "geemili", "desttinghim", "forgejo"], }, { "action": "accept", "src": ["geemili"], "dst": ["geemili"], "users": ["geemili"], }, ], } ''; }; }; services.tailscale.enable = true; # Enable automatic upgrades system.autoUpgrade.enable = true; system.autoUpgrade.allowReboot = true; system.autoUpgrade.flake = "git+http://127.0.0.1:3000/Twins/server-configuration.git"; # Enable automatic garbage collection nix.gc = { automatic = true; dates = "weekly"; options = "--delete-older-than 30d"; }; nix.settings.trusted-users = [ "geemili" ]; # Open ports in the firewall. networking.firewall.enable = true; networking.firewall.allowedTCPPorts = [ # Blocky DNS 53 # Blocky API 4000 # Caddy HTTP and HTTPS 80 443 ]; networking.firewall.allowedUDPPorts = [ # Blocky DNS 53 # mDNS 5353 # Headscale UDP port for STUN protocol 3478 ]; # Use systemd-resolved and set networkmanager to allow mdns services.resolved = { enable = true; extraConfig = '' DNSStubListener=false ''; }; networking.networkmanager.connectionConfig."connection.mdns" = 2; # 2 == yes # Copy the NixOS configuration file and link it from the resulting system # (/run/current-system/configuration.nix). This is useful in case you # accidentally delete configuration.nix. # system.copySystemConfiguration = true; # This option defines the first version of NixOS you have installed on this particular machine, # and is used to maintain compatibility with application data (e.g. databases) created on older NixOS versions. # # Most users should NEVER change this value after the initial install, for any reason, # even if you've upgraded your system to a new NixOS release. # # This value does NOT affect the Nixpkgs version your packages and OS are pulled from, # so changing it will NOT upgrade your system. # # This value being lower than the current NixOS release does NOT mean your system is # out of date, out of support, or vulnerable. # # Do NOT change this value unless you have manually inspected all the changes it would make to your configuration, # and migrated your data accordingly. # # For more information, see `man configuration.nix` or https://nixos.org/manual/nixos/stable/options#opt-system.stateVersion . system.stateVersion = "23.11"; # Did you read the comment? }