From c9a1d2da80bca0a85e8c18f6d2db71c4d1127eda Mon Sep 17 00:00:00 2001 From: Collin Williams <96917990+bluedragon1221@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:15:19 -0600 Subject: Spring Cleaning - create new module, `system`, that consumes the `boot` module and takes in the more system-interested services from the `services` module - touch up left over services (which are more self-hosting interested) - touch up yo and yoshi configs --- hosts/ganymede/config.nix | 55 +++---- hosts/mercury/config.nix | 26 ++-- hosts/mercury/hjem.nix | 12 +- lib/lib.nix | 8 +- modules/boot/nixos/default.nix | 81 ---------- modules/boot/nixos/plymouth.nix | 21 --- modules/boot/options.nix | 25 --- modules/desktop/hjem/wm/dunst.nix | 9 +- modules/desktop/hjem/wm/sway.nix | 6 +- modules/desktop/nixos/greeters/greetd.nix | 7 +- modules/desktop/options.nix | 13 +- modules/options.nix | 11 +- modules/services/nixos/adguard.nix | 40 +++++ modules/services/nixos/audio.nix | 21 --- modules/services/nixos/bluetooth.nix | 40 ----- modules/services/nixos/caddy.nix | 23 +++ modules/services/nixos/default.nix | 13 +- modules/services/nixos/forgejo.nix | 48 ++++++ modules/services/nixos/headscale.nix | 69 +++++++++ modules/services/nixos/jellyfin.nix | 10 ++ modules/services/nixos/mkCaddyCfg.nix | 15 ++ modules/services/nixos/navidrome.nix | 19 +++ modules/services/nixos/networking/default.nix | 13 -- modules/services/nixos/networking/iwd.nix | 27 ---- modules/services/nixos/networking/networkd.nix | 60 -------- .../services/nixos/networking/networkmanager.nix | 20 --- modules/services/nixos/networking/resolved.nix | 18 --- modules/services/nixos/openssh.nix | 96 ++++++++++++ modules/services/nixos/selfhost/adguard.nix | 47 ------ modules/services/nixos/selfhost/caddy.nix | 28 ---- modules/services/nixos/selfhost/default.nix | 8 - modules/services/nixos/selfhost/forgejo.nix | 54 ------- modules/services/nixos/selfhost/headscale.nix | 75 --------- modules/services/nixos/selfhost/jellyfin.nix | 20 --- modules/services/nixos/selfhost/navidrome.nix | 19 --- modules/services/nixos/ssh.nix | 86 ----------- modules/services/nixos/tailscale.nix | 34 ----- modules/services/options.nix | 167 +++++++++------------ modules/system/nixos/audio.nix | 23 +++ modules/system/nixos/bluetooth.nix | 40 +++++ modules/system/nixos/boot.nix | 89 +++++++++++ modules/system/nixos/default.nix | 9 ++ modules/system/nixos/networking/default.nix | 13 ++ modules/system/nixos/networking/iwd.nix | 27 ++++ modules/system/nixos/networking/networkd.nix | 60 ++++++++ modules/system/nixos/networking/networkmanager.nix | 18 +++ modules/system/nixos/networking/resolved.nix | 18 +++ modules/system/nixos/networking/tailscale.nix | 33 ++++ modules/system/nixos/polkit.nix | 75 +++++++++ modules/system/options.nix | 68 +++++++++ modules/user/nixos/default.nix | 9 +- modules/user/options.nix | 1 + pkgs/yo.nix | 14 +- pkgs/yoshi.nix | 6 +- 54 files changed, 970 insertions(+), 877 deletions(-) delete mode 100644 modules/boot/nixos/default.nix delete mode 100644 modules/boot/nixos/plymouth.nix delete mode 100644 modules/boot/options.nix create mode 100644 modules/services/nixos/adguard.nix delete mode 100644 modules/services/nixos/audio.nix delete mode 100644 modules/services/nixos/bluetooth.nix create mode 100644 modules/services/nixos/caddy.nix create mode 100644 modules/services/nixos/forgejo.nix create mode 100644 modules/services/nixos/headscale.nix create mode 100644 modules/services/nixos/jellyfin.nix create mode 100644 modules/services/nixos/mkCaddyCfg.nix create mode 100644 modules/services/nixos/navidrome.nix delete mode 100644 modules/services/nixos/networking/default.nix delete mode 100644 modules/services/nixos/networking/iwd.nix delete mode 100644 modules/services/nixos/networking/networkd.nix delete mode 100644 modules/services/nixos/networking/networkmanager.nix delete mode 100644 modules/services/nixos/networking/resolved.nix create mode 100644 modules/services/nixos/openssh.nix delete mode 100644 modules/services/nixos/selfhost/adguard.nix delete mode 100644 modules/services/nixos/selfhost/caddy.nix delete mode 100644 modules/services/nixos/selfhost/default.nix delete mode 100644 modules/services/nixos/selfhost/forgejo.nix delete mode 100644 modules/services/nixos/selfhost/headscale.nix delete mode 100644 modules/services/nixos/selfhost/jellyfin.nix delete mode 100644 modules/services/nixos/selfhost/navidrome.nix delete mode 100644 modules/services/nixos/ssh.nix delete mode 100644 modules/services/nixos/tailscale.nix create mode 100644 modules/system/nixos/audio.nix create mode 100644 modules/system/nixos/bluetooth.nix create mode 100644 modules/system/nixos/boot.nix create mode 100644 modules/system/nixos/default.nix create mode 100644 modules/system/nixos/networking/default.nix create mode 100644 modules/system/nixos/networking/iwd.nix create mode 100644 modules/system/nixos/networking/networkd.nix create mode 100644 modules/system/nixos/networking/networkmanager.nix create mode 100644 modules/system/nixos/networking/resolved.nix create mode 100644 modules/system/nixos/networking/tailscale.nix create mode 100644 modules/system/nixos/polkit.nix create mode 100644 modules/system/options.nix diff --git a/hosts/ganymede/config.nix b/hosts/ganymede/config.nix index e44e4a8..ba7c854 100644 --- a/hosts/ganymede/config.nix +++ b/hosts/ganymede/config.nix @@ -1,6 +1,6 @@ {config, ...}: { collinux = { - user.name = "collin"; + theme = "terminal"; secrets = { "williams-psk" = { @@ -18,40 +18,41 @@ userName = "Collin Williams"; userEmail = "96917990+bluedragon1221@users.noreply.github.com"; }; + helix.enable = true; }; }; - boot.systemd-boot.enable = true; - - services = { - networking = { + system.network = { + networkd = { enable = true; - networkd = { - enable = true; - ssid = "williams"; - pskFile = config.collinux.secrets."williams-psk".path; - static = { - ip = "192.168.50.2/24"; - gateway = "192.168.50.1"; - }; - }; - sshd = { - enable = true; - bind_host = "0.0.0.0"; + static = { + ip = "192.168.50.2/24"; + gateway = "192.168.50.1"; }; + + ssid = "williams"; + pskFile = config.collinux.secrets."williams-psk".path; }; + }; - selfhost = { - caddy = { - enable = true; - envFile = config.collinux.secrets."caddy-env".path; - }; + services = { + sshd = { + enable = true; + portConfig = [ + { + port = 2222; + rootLogin = true; + } + { + port = 22; + otp = true; + } + ]; + }; - forgejo = { - enable = true; - bind_host = "127.0.0.1"; - root_url = "ganymede.collinux.tailnet:8010"; - }; + caddy = { + enable = true; + envFile = config.collinux.secrets."caddy-env".path; }; }; }; diff --git a/hosts/mercury/config.nix b/hosts/mercury/config.nix index b122e5e..abf38fb 100644 --- a/hosts/mercury/config.nix +++ b/hosts/mercury/config.nix @@ -2,10 +2,7 @@ collinux = { theme = "catppuccin"; - user = { - name = "collin"; - useRun0 = true; - }; + user.useRun0 = true; desktop = { wallpaper = ./wallpapers/abstract-swirls.jpg; @@ -32,16 +29,17 @@ }; }; - services = { - audio.enable = true; - networking = { - enable = true; + system = { + boot = { + systemd-boot.enable = true; + secureBoot.enable = true; + }; + network = { iwd.enable = true; - networkd = { - enable = true; - }; + networkd.enable = true; }; + audio.enable = true; bluetooth.enable = true; }; @@ -62,7 +60,6 @@ tmux.enable = true; lazygit.enable = true; - # nh.enable = true; git = { enable = true; userName = "Collin Williams"; @@ -71,10 +68,5 @@ }; }; }; - - boot = { - systemd-boot.enable = true; - secureBoot.enable = true; - }; }; } diff --git a/hosts/mercury/hjem.nix b/hosts/mercury/hjem.nix index a9d6d08..35f21b8 100644 --- a/hosts/mercury/hjem.nix +++ b/hosts/mercury/hjem.nix @@ -2,7 +2,16 @@ pkgs, inputs, ... -}: { +}: let + captiveBrowserDesktop = pkgs.makeDesktopItem { + name = "captive-browser"; + desktopName = "Captive Browser"; + exec = "${pkgs.captive-browser}/bin/captive-browser"; + icon = "web-browser"; + terminal = false; + categories = ["Application"]; + }; +in { packages = with pkgs; [ tor-browser # don't ask @@ -21,6 +30,7 @@ vital captive-browser # https://words.filippo.io/captive-browser + captiveBrowserDesktop inputs.glide-browser.packages."x86_64-linux".default (pkgs.callPackage ../../pkgs/yoshi.nix {inherit inputs;}) diff --git a/lib/lib.nix b/lib/lib.nix index 56f0dfd..9dd60d4 100644 --- a/lib/lib.nix +++ b/lib/lib.nix @@ -27,6 +27,11 @@ let (builtins.readDir dir))); }; + netTypes = {lib, ...}: { + ipAddr = lib.types.strMatching "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"; + ipAddrCidr = lib.types.strMatching "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])/(3[0-2]|[12]?[0-9])$"; + }; + options = { lib, config, @@ -36,7 +41,7 @@ let mkThemeOption = name: mkOption { description = "Pre-made theme for ${name}"; - type = lib.types.enum ["catppuccin" "adwaita" "kanagawa"]; + type = lib.types.enum ["catppuccin" "adwaita" "kanagawa" "terminal"]; default = config.collinux.theme; defaultText = "config.collinux.theme"; }; @@ -51,6 +56,7 @@ let in { inherit globimport; inherit options; + inherit netTypes; flatten = builtins.foldl' (a: b: a ++ b) []; } diff --git a/modules/boot/nixos/default.nix b/modules/boot/nixos/default.nix deleted file mode 100644 index 49cca5e..0000000 --- a/modules/boot/nixos/default.nix +++ /dev/null @@ -1,81 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: let - cfg = config.collinux.boot; -in { - imports = [ - ./plymouth.nix - ]; - - boot = - { - bcache.enable = false; # why is this default on? I DON'T CARE ABOUT bcache - initrd = { - verbose = false; - systemd.enable = true; - checkJournalingFS = false; - }; - loader = { - systemd-boot = lib.optionalAttrs (cfg.systemd-boot.enable && !cfg.secureBoot.enable) { - enable = true; - configurationLimit = 3; - }; - efi.canTouchEfiVariables = true; - timeout = cfg.timeout; # hold space to show boot menu - }; - - # from hardened.nix - blacklistedKernelModules = [ - # Obscure network protocols - "ax25" - "netrom" - "rose" - - # Old or rare or insufficiently audited filesystems - "adfs" - "affs" - "bfs" - "befs" - "cramfs" - "efs" - # "erofs" # necessary for system.etc.overlay - "exofs" - "freevxfs" - "f2fs" - "hfs" - "hpfs" - "jfs" - "minix" - "nilfs2" - "ntfs" - "omfs" - "qnx4" - "qnx6" - "sysv" - "ufs" - ]; - } - // (lib.optionalAttrs cfg.secureBoot.enable { - lanzaboote = { - enable = true; - pkiBundle = "/var/lib/sbctl"; - }; - }); - - system.etc.overlay = { - enable = true; - mutable = true; # would love this to be false, but we're not there yet - }; - system.nixos-init.enable = true; - - # store journald logs in memory - services.journald.extraConfig = '' - Storage=volatile - RuntimeMaxUse=100M - ''; - - environment.systemPackages = [pkgs.efibootmgr] ++ lib.optional cfg.secureBoot.enable pkgs.sbctl; -} diff --git a/modules/boot/nixos/plymouth.nix b/modules/boot/nixos/plymouth.nix deleted file mode 100644 index 5f7952b..0000000 --- a/modules/boot/nixos/plymouth.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: let - cfg = config.collinux.boot.plymouth; -in - lib.mkIf cfg.enable { - boot.plymouth = { - enable = true; - theme = - if (cfg.theme == "catppuccin") - then "catppuccin-macchiato" # for whatever reason catppuccin-mocha has errors - else "nixos-bgrt"; - themePackages = - if (cfg.theme == "catppuccin") - then [pkgs.catppuccin-plymouth] - else [pkgs.nixos-bgrt-plymouth]; - }; - } diff --git a/modules/boot/options.nix b/modules/boot/options.nix deleted file mode 100644 index 9e85570..0000000 --- a/modules/boot/options.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ - my-lib, - config, - lib, - ... -}: let - inherit (lib) mkOption mkEnableOption; - inherit (my-lib.options {inherit lib config;}) mkThemeOption; -in { - options = { - collinux.boot = { - systemd-boot.enable = mkEnableOption "systemd-boot"; - timeout = mkOption { - description = "bootloader timeout"; - type = lib.types.int; - default = 0; - }; - plymouth = { - enable = mkEnableOption "plymouth bootsplash"; - theme = mkThemeOption "plymouth"; - }; - secureBoot.enable = mkEnableOption "lanzaboote"; - }; - }; -} diff --git a/modules/desktop/hjem/wm/dunst.nix b/modules/desktop/hjem/wm/dunst.nix index 9366975..7528dd0 100644 --- a/modules/desktop/hjem/wm/dunst.nix +++ b/modules/desktop/hjem/wm/dunst.nix @@ -18,17 +18,16 @@ origin = "top-right" [urgency_low] - background = "#${base00}99" foreground = "#${base05}ff" + background = "#ffffff00" [urgency_normal] - background = "#${base00}99" foreground = "#${base05}ff" + background = "#ffffff00" [urgency_critical] - background = "#${base00}99" - foreground = "#${base05}ff" - frame_color = "#${base08}" + foreground = "#${base08}ff" + background = "#ffffff00" ''; in lib.mkIf cfg.enable { diff --git a/modules/desktop/hjem/wm/sway.nix b/modules/desktop/hjem/wm/sway.nix index 4cecdea..7051402 100644 --- a/modules/desktop/hjem/wm/sway.nix +++ b/modules/desktop/hjem/wm/sway.nix @@ -38,11 +38,12 @@ settings = '' exec ${config.collinux.desktop.wallpaper_cmd} exec ${pkgs.dunst}/bin/dunst + exec ${pkgs.soteria}/bin/soteria include catppuccin-mocha # target title bg text indicator border - client.focused $lavender $base $text $rosewater $lavender + client.focused $lavender $base $text $rosewater $blue client.focused_inactive $overlay0 $base $text $rosewater $overlay0 client.unfocused $overlay0 $base $text $rosewater $overlay0 client.urgent $peach $base $peach $overlay0 $peach @@ -93,8 +94,11 @@ bindsym Insert exec 'util.lua music next' default_border pixel 2 + default_floating_border pixel 2 smart_borders on smart_gaps on + + floating_modifier Mod4 normal ''; in lib.mkIf cfg.enable { diff --git a/modules/desktop/nixos/greeters/greetd.nix b/modules/desktop/nixos/greeters/greetd.nix index 02af31f..8e0a3ba 100644 --- a/modules/desktop/nixos/greeters/greetd.nix +++ b/modules/desktop/nixos/greeters/greetd.nix @@ -14,13 +14,8 @@ } else if cfg.autologin.enable then { + command = cfg.autologin.command; user = config.collinux.user.name; - command = with config.collinux.desktop; - if (wm.sway.enable && !gnome.enable && !wm.niri.enable) - then lib.getExe pkgs.sway - else if (wm.niri.enable && !gnome.enable && !wm.sway.enable) - then "${pkgs.niri}/bin/niri-session" - else null; } else {}; in diff --git a/modules/desktop/options.nix b/modules/desktop/options.nix index 5ba4423..1bd03fe 100644 --- a/modules/desktop/options.nix +++ b/modules/desktop/options.nix @@ -22,7 +22,18 @@ in { greetd = { enable = mkEnableOption "greetd greeter"; - autologin.enable = mkEnableOption "autologin"; + autologin = { + enable = mkEnableOption "autologin"; + command = mkOption { + type = lib.types.str; + default = with config.collinux.desktop; + if (wm.sway.enable && !gnome.enable && !wm.niri.enable) + then "${pkgs.sway}/bin/sway" + else if (wm.niri.enable && !gnome.enable && !wm.sway.enable) + then "${pkgs.niri}/bin/niri-session" + else null; + }; + }; cosmic-greeter.enable = mkEnableOption "cosmic-greeter"; }; gdm.enable = mkEnableOption "gdm display manager"; diff --git a/modules/options.nix b/modules/options.nix index 8ecf1d7..3e3065d 100644 --- a/modules/options.nix +++ b/modules/options.nix @@ -1,15 +1,14 @@ { lib, config, + my-lib, ... }: let - inherit (lib) mkOption types; + inherit (lib) mkOption; + inherit (my-lib.options {inherit lib config;}) mkThemeOption; in { options = { - collinux.theme = mkOption { - description = "System-wide theme"; - type = types.enum ["catppuccin" "adwaita" "kanagawa"]; - }; + collinux.theme = mkThemeOption "The entire system"; collinux.palette = let colorOption = lib.mkOption { @@ -17,7 +16,7 @@ in { internal = true; }; in - lib.mkOption { + mkOption { internal = true; type = lib.types.submodule { options = { diff --git a/modules/services/nixos/adguard.nix b/modules/services/nixos/adguard.nix new file mode 100644 index 0000000..f59934e --- /dev/null +++ b/modules/services/nixos/adguard.nix @@ -0,0 +1,40 @@ +{ + config, + lib, + ... +}: let + cfg = config.collinux.services.adguard; +in { + imports = [ + (import ./mkCaddyCfg.nix cfg) + ]; + + config = lib.mkIf cfg.enable { + services.adguardhome = { + enable = true; + port = cfg.port; + mutableSettings = true; + settings = { + http = { + pprof.enabled = false; + address = "localhost:${toString cfg.port}"; + }; + users = []; # disable auth (only accessable over tailscale anyway) + dns = { + bind_hosts = ["127.0.0.1" cfg.bind_host]; + upstream_dns = ["https://dns.quad9.net/dns-query"]; + enable_dnssec = true; + }; + tls.enabled = false; + dhcp.enabled = false; + }; + }; + + # disable systemd-resolved (https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse) + services.resolved.extraConfig = lib.mkIf config.services.resolved.enable '' + DNS=127.0.0.1 + DNSStubListener=no + ''; + services.tailscale.extraSetFlags = lib.optional config.collinux.services.networking.tailscale.enable "--accept-dns=false"; # would create an infinite loop of dns lookups + }; +} diff --git a/modules/services/nixos/audio.nix b/modules/services/nixos/audio.nix deleted file mode 100644 index 9de6787..0000000 --- a/modules/services/nixos/audio.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: let - cfg = config.collinux.services.audio; -in - lib.mkIf cfg.enable { - security.rtkit.enable = true; - services.pipewire = { - enable = true; - wireplumber.enable = true; - alsa.enable = true; - pulse.enable = false; - }; - - boot.blacklistedKernelModules = ["snd_seq_dummy"]; # remove extraneous alsa midi devices - - environment.systemPackages = with pkgs; [pwvucontrol qpwgraph]; - } diff --git a/modules/services/nixos/bluetooth.nix b/modules/services/nixos/bluetooth.nix deleted file mode 100644 index acf628c..0000000 --- a/modules/services/nixos/bluetooth.nix +++ /dev/null @@ -1,40 +0,0 @@ -{ - lib, - config, - ... -}: let - cfg = config.collinux.services.bluetooth; -in - lib.mkIf cfg.enable { - hardware.bluetooth = { - enable = true; - powerOnBoot = true; - - settings.General = { - FastConnectable = true; - ControllerMode = "bredr"; - JustWorksRepairing = "always"; - }; - }; - - systemd.user.services."mpris-proxy" = { - unitConfig = { - BindsTo = ["bluetooth.target"]; - After = ["bluetooth.target"]; - }; - - wantedBy = ["bluetooth.target"]; - - # serviceConfig already exists (?) - }; - - # hardening (down to 2.1 OK) - systemd.services."bluetooth".serviceConfig = { - IPAddressDeny = "any"; - ProtectKernelLogs = true; - ProtectKernelModules = lib.mkForce true; - RestrictAddressFamilies = ["AF_UNIX" "AF_BLUETOOTH"]; - ProtectClock = true; - ProcSubset = "pid"; - }; - } diff --git a/modules/services/nixos/caddy.nix b/modules/services/nixos/caddy.nix new file mode 100644 index 0000000..06f040c --- /dev/null +++ b/modules/services/nixos/caddy.nix @@ -0,0 +1,23 @@ +{ + pkgs, + lib, + config, + ... +}: let + cfg = config.collinux.services.caddy; +in + lib.mkIf cfg.enable { + services.caddy = { + # just always install this. No harm in installing an extra plugin + package = pkgs.caddy.withPlugins { + plugins = [ + "github.com/tailscale/caddy-tailscale@v0.0.0-20251204171825-f070d146dd61" + ]; + hash = "sha256-cK7C5ISsTwX0FMf891s/Vr22JvRqYEC8GkLfP1L1Mus="; + }; + enable = true; + environmentFile = cfg.envFile; + }; + networking.firewall.allowedTCPPorts = [80 443]; + environment.systemPackages = with pkgs; [nss]; # required for caddy https stuff + } diff --git a/modules/services/nixos/default.nix b/modules/services/nixos/default.nix index 9c95180..234a712 100644 --- a/modules/services/nixos/default.nix +++ b/modules/services/nixos/default.nix @@ -1,10 +1,11 @@ { imports = [ - ./networking - ./audio.nix - ./bluetooth.nix - ./tailscale.nix - ./selfhost - ./ssh.nix + # ./navidrome.nix + ./headscale.nix + ./forgejo.nix + ./openssh.nix + # ./jellyfin.nix + ./caddy.nix + ./adguard.nix ]; } diff --git a/modules/services/nixos/forgejo.nix b/modules/services/nixos/forgejo.nix new file mode 100644 index 0000000..5c98633 --- /dev/null +++ b/modules/services/nixos/forgejo.nix @@ -0,0 +1,48 @@ +{ + lib, + config, + ... +}: let + cfg = config.collinux.services.forgejo; +in { + imports = [ + (import ./mkCaddyCfg.nix cfg) + ]; + + config = lib.mkIf cfg.enable { + services.forgejo = { + enable = true; + database.type = "sqlite3"; + settings = { + server = { + DOMAIN = "localhost"; + ROOT_URL = cfg.root_url; + HTTP_PORT = cfg.port; + + # ssh + START_SSH_SERVER = true; # use builtin ssh server + BUILTIN_SSH_SERVER_USER = "git"; + SSH_DOMAIN = cfg.root_url or "ganymede"; + SSH_PORT = cfg.git_ssh_port; # don't conflict with system ssh + SSH_LISTEN_HOST = cfg.bind_host; + SSH_LISTEN_PORT = cfg.git_ssh_port; + }; + service = { + DISABLE_REGISTRATION = false; + ENABLE_REVERSE_PROXY_AUTHENTICATION = true; + }; + repository = { + # disable stuff + DISABLE_MIGRATIONS = true; + DISABLE_STARS = true; + DISABLE_DOWNLOAD_SOURCE_ARCHIVES = true; + }; + }; + }; + + systemd.services."forgejo" = lib.mkIf config.collinux.services.networking.networkd.enable { + after = lib.mkAfter ["network-online.target"]; + wants = lib.mkAfter ["network-online.target"]; + }; + }; +} diff --git a/modules/services/nixos/headscale.nix b/modules/services/nixos/headscale.nix new file mode 100644 index 0000000..51d3da5 --- /dev/null +++ b/modules/services/nixos/headscale.nix @@ -0,0 +1,69 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.collinux.services.headscale; + + acl_file = (pkgs.formats.json {}).generate "acl.json" { + ssh = [ + { + src = ["collin@"]; + dst = ["collin@"]; + users = ["autogroup:nonroot" "root"]; + action = "accept"; + } + ]; + }; +in { + imports = [ + (import ./mkCaddyCfg.nix cfg) + ]; + + config = lib.mkIf cfg.enable { + services.headscale = { + enable = true; + address = cfg.bind_host; + port = cfg.port; + settings = { + server_url = "https://${cfg.root_url}"; + + database.type = "sqlite"; + + dns = { + magic_dns = true; + base_domain = "collinux.tailnet"; + override_local_dns = true; + nameservers.global = ["9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9"]; + }; + + policy.path = acl_file; + + prefixes = { + "v4" = "100.100.0.0/16"; + allocation = "random"; + }; + + # leave tls for caddy to worry about + tls_cert_path = null; + tls_key_path = null; + + logtail.enabled = false; + }; + }; + + # make sure headscale can start before tailscale + systemd.services."headscale" = lib.mkIf config.collinux.system.networking.tailscale.enable { + after = lib.mkForce ["network.target"]; + before = lib.mkForce ["headscale.target"]; + wants = lib.mkForce ["network.target" "headscale.target"]; + }; + + systemd.targets."headscale" = { + description = "Target represents headscale is running. started by headscale.service"; + }; + + environment.systemPackages = [pkgs.headscale]; + }; +} diff --git a/modules/services/nixos/jellyfin.nix b/modules/services/nixos/jellyfin.nix new file mode 100644 index 0000000..65193f2 --- /dev/null +++ b/modules/services/nixos/jellyfin.nix @@ -0,0 +1,10 @@ +{ + config, + lib, + ... +}: let + cfg = config.collinux.services.selfhost.jellyfin; +in + lib.mkIf cfg.enable { + services.jellyfin.enable = true; + } diff --git a/modules/services/nixos/mkCaddyCfg.nix b/modules/services/nixos/mkCaddyCfg.nix new file mode 100644 index 0000000..bb2e06d --- /dev/null +++ b/modules/services/nixos/mkCaddyCfg.nix @@ -0,0 +1,15 @@ +cfg: { + services.caddy = + if cfg.caddy.enable + then { + virtualHosts.${cfg.root_url}.extraConfig = '' + ${ + if cfg.caddy.bind_tailscale + then "bind tailscale/${cfg.service_name}" + else "" + } + reverse_proxy ${cfg.bind_host}:${toString cfg.port} + ''; + } + else {}; +} diff --git a/modules/services/nixos/navidrome.nix b/modules/services/nixos/navidrome.nix new file mode 100644 index 0000000..5250b85 --- /dev/null +++ b/modules/services/nixos/navidrome.nix @@ -0,0 +1,19 @@ +{ + config, + lib, + ... +}: let + cfg = config.collinux.services.selfhost.navidrome; +in + lib.mkIf cfg.enable { + services.navidrome = { + enable = true; + inherit (cfg) user; + settings = { + Port = 4533; + Address = "0.0.0.0"; + EnableInsightsCollector = false; + MusicFolder = "/home/${cfg.user}/Music"; + }; + }; + } diff --git a/modules/services/nixos/networking/default.nix b/modules/services/nixos/networking/default.nix deleted file mode 100644 index a042411..0000000 --- a/modules/services/nixos/networking/default.nix +++ /dev/null @@ -1,13 +0,0 @@ -{ - imports = [ - ./iwd.nix - ./networkmanager.nix - ./networkd.nix - ./resolved.nix - ]; - - networking.firewall = { - enable = true; - checkReversePath = "loose"; - }; -} diff --git a/modules/services/nixos/networking/iwd.nix b/modules/services/nixos/networking/iwd.nix deleted file mode 100644 index db5cc2e..0000000 --- a/modules/services/nixos/networking/iwd.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ - lib, - config, - ... -}: let - cfg = config.collinux.services.networking.iwd; -in - lib.mkIf cfg.enable { - networking = { - wireless.iwd = { - enable = true; - settings = { - General = { - EnableNetworkConfiguration = !config.collinux.services.networking.networkd.enable; - AddressRandomization = "once"; - AddressRandomizationRange = "full"; - }; - Network.NameResolvingService = "systemd"; # either systemd or resolvconf - }; - }; - - # Disable default networking stuff - dhcpcd.enable = false; - useDHCP = false; - networkmanager.enable = false; - }; - } diff --git a/modules/services/nixos/networking/networkd.nix b/modules/services/nixos/networking/networkd.nix deleted file mode 100644 index e84fd2f..0000000 --- a/modules/services/nixos/networking/networkd.nix +++ /dev/null @@ -1,60 +0,0 @@ -{ - lib, - config, - ... -}: let - cfg = config.collinux.services.networking.networkd; - - dhcp_enabled = cfg.static == null; -in - lib.mkIf cfg.enable { - networking = { - wireless = - if !config.collinux.services.networking.iwd.enable - then { - enable = true; - networks.${cfg.ssid}.pskRaw = "ext:psk"; - secretsFile = cfg.pskFile; - } - else {}; - - useNetworkd = true; - - # Disable default networking stuff - dhcpcd.enable = false; - useDHCP = false; - networkmanager.enable = false; - }; - - systemd.network = { - enable = true; - - wait-online = { - enable = true; - ignoredInterfaces = ["docker0"]; - anyInterface = true; - }; - - networks."11-lan" = { - name = "wl*"; - - networkConfig = - ( - if dhcp_enabled - then {DHCP = "yes";} - else { - Address = cfg.static.ip; - Gateway = cfg.static.gateway; - DHCP = "no"; - # DNS is managed by systemd-resolved (not specified here) - } - ) - // { - LinkLocalAddressing = "no"; - }; - - dhcpV4Config.UseDNS = "no"; - dhcpV6Config.UseDNS = "no"; - }; - }; - } diff --git a/modules/services/nixos/networking/networkmanager.nix b/modules/services/nixos/networking/networkmanager.nix deleted file mode 100644 index e032bd0..0000000 --- a/modules/services/nixos/networking/networkmanager.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - lib, - config, - ... -}: let - cfg = config.collinux.services.networking.networkmanager; -in - lib.mkIf cfg.enable { - networking = { - networkmanager = { - enable = true; - dns = "systemd-resolved"; - dhcp = "internal"; - }; - - dhcpcd.enable = false; - }; - - services.tailscale.extraSetFlags = lib.optional config.collinux.services.networking.tailscale.enable "--accept-dns=true"; - } diff --git a/modules/services/nixos/networking/resolved.nix b/modules/services/nixos/networking/resolved.nix deleted file mode 100644 index 03333b1..0000000 --- a/modules/services/nixos/networking/resolved.nix +++ /dev/null @@ -1,18 +0,0 @@ -{ - networking.resolvconf.enable = false; - - networking.nameservers = [ - "9.9.9.9#dns.quad9.net" - "149.112.112.112#dns.quad9.net" - ]; - - services.resolved = { - enable = true; - dnsovertls = "opportunistic"; - dnssec = "allow-downgrade"; - - # disable extra stuff - llmnr = "false"; - extraConfig = "MulticastDNS=no"; - }; -} diff --git a/modules/services/nixos/openssh.nix b/modules/services/nixos/openssh.nix new file mode 100644 index 0000000..c0c8c30 --- /dev/null +++ b/modules/services/nixos/openssh.nix @@ -0,0 +1,96 @@ +{ + config, + lib, + pkgs, + hosts, + ... +}: let + cfg = config.collinux.services.sshd; + + anyAttr = attr: cfg.portConfig |> builtins.map (x: x.${attr} != null) |> builtins.any (x: x); + anyOTP = cfg.portConfig |> builtins.map (x: x.otp == true) |> builtins.any (x: x); + + authorizedKeys = + hosts + |> builtins.mapAttrs (_: data: data.user_pubkey or null) + |> builtins.attrValues + |> builtins.filter (x: x != null); +in { + config = lib.mkIf cfg.enable { + networking.firewall.allowedTCPPorts = cfg.portConfig |> builtins.map (x: x.port); + + services.openssh = { + enable = true; + allowSFTP = false; + + hostKeys = [ + { + path = "/etc/ssh/ssh_host_ed25519_key"; + type = "ed25519"; + } + ]; + + listenAddresses = + cfg.portConfig + |> builtins.map (x: { + addr = "0.0.0.0"; + port = x.port; + }); + + # Lock down everything by default + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + PubkeyAuthentication = false; + KbdInteractiveAuthentication = false; + AllowAgentForwarding = false; + }; + + extraConfig = + cfg.portConfig + |> builtins.map (x: + lib.concatStringsSep "\n" [ + "Match LocalPort ${toString x.port}" + ( + if x.otp + then '' + ChallengeResponseAuthentication yes + PubkeyAuthentication yes + KbdInteractiveAuthentication yes + AuthenticationMethods publickey,keyboard-interactive:pam + '' + else '' + PubkeyAuthentication yes + AuthenticationMethods publickey + '' + ) + (lib.optionalString x.rootLogin "PermitRootLogin yes") + ]) + |> lib.concatStringsSep "\n\n"; + }; + + security.pam.services = lib.optionalAttrs anyOTP { + login.googleAuthenticator.enable = true; + + sshd.text = '' + account required pam_unix.so + + auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so nullok no_increment_hotp + auth sufficient pam_permit.so + + session required pam_env.so conffile=/etc/pam/environment readenv=0 + session required pam_unix.so + session required pam_loginuid.so + session optional ${pkgs.systemd}/lib/security/pam_systemd.so + ''; + }; + + users.users.${config.collinux.user.name}.openssh.authorizedKeys.keys = authorizedKeys; + users.users."root".openssh.authorizedKeys.keys = lib.mkIf (anyAttr "rootLogin") authorizedKeys; + + systemd.services."openssh" = { + after = lib.mkAfter ["network-online.target"]; + wants = lib.mkAfter ["network-online.target"]; + }; + }; +} diff --git a/modules/services/nixos/selfhost/adguard.nix b/modules/services/nixos/selfhost/adguard.nix deleted file mode 100644 index 69fb8a4..0000000 --- a/modules/services/nixos/selfhost/adguard.nix +++ /dev/null @@ -1,47 +0,0 @@ -{ - config, - lib, - ... -}: let - cfg = config.collinux.services.selfhost.adguard; -in - lib.mkIf cfg.enable { - services.adguardhome = { - enable = true; - port = cfg.port; - mutableSettings = true; - settings = { - http = { - pprof.enabled = false; - address = "localhost:${toString cfg.port}"; - }; - users = []; # disable auth (only accessable over tailscale anyway) - dns = { - bind_hosts = ["127.0.0.1" cfg.bind_host]; - upstream_dns = ["https://dns.quad9.net/dns-query"]; - enable_dnssec = true; - }; - tls.enabled = false; - dhcp.enabled = false; - }; - }; - - # disable systemd-resolved (https://github.com/AdguardTeam/AdGuardHome/wiki/FAQ#bindinuse) - services.resolved.extraConfig = lib.mkIf config.services.resolved.enable '' - DNS=127.0.0.1 - DNSStubListener=no - ''; - - services.tailscale.extraSetFlags = lib.optional config.collinux.services.networking.tailscale.enable "--accept-dns=false"; # would create an infinite loop of dns lookups - - services.caddy = lib.mkIf cfg.caddy.enable { - virtualHosts.${cfg.root_url}.extraConfig = '' - ${ - if cfg.caddy.bind_tailscale - then "bind tailscale/${cfg.service_name}" - else "" - } - reverse_proxy ${cfg.bind_host}:${toString cfg.port} - ''; - }; - } diff --git a/modules/services/nixos/selfhost/caddy.nix b/modules/services/nixos/selfhost/caddy.nix deleted file mode 100644 index f625f62..0000000 --- a/modules/services/nixos/selfhost/caddy.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - pkgs, - lib, - config, - ... -}: let - cfg = config.collinux.services.selfhost.caddy; -in - lib.mkIf cfg.enable (lib.mkMerge [ - { - services.caddy = { - enable = true; - environmentFile = cfg.envFile; - }; - networking.firewall.allowedTCPPorts = [80 443]; - environment.systemPackages = with pkgs; [nss]; # required for caddy https stuff - } - (lib.mkIf config.collinux.services.networking.tailscale.enable { - services.caddy = { - package = pkgs.caddy.withPlugins { - plugins = [ - "github.com/tailscale/caddy-tailscale@v0.0.0-20251204171825-f070d146dd61" - ]; - hash = "sha256-cK7C5ISsTwX0FMf891s/Vr22JvRqYEC8GkLfP1L1Mus="; - }; - }; - }) - ]) diff --git a/modules/services/nixos/selfhost/default.nix b/modules/services/nixos/selfhost/default.nix deleted file mode 100644 index de1078b..0000000 --- a/modules/services/nixos/selfhost/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - imports = [ - ./adguard.nix - ./forgejo.nix - ./headscale.nix - ./caddy.nix - ]; -} diff --git a/modules/services/nixos/selfhost/forgejo.nix b/modules/services/nixos/selfhost/forgejo.nix deleted file mode 100644 index 1b02628..0000000 --- a/modules/services/nixos/selfhost/forgejo.nix +++ /dev/null @@ -1,54 +0,0 @@ -{ - lib, - config, - ... -}: let - cfg = config.collinux.services.selfhost.forgejo; -in - lib.mkIf cfg.enable { - services.forgejo = { - enable = true; - database.type = "sqlite3"; - settings = { - server = { - DOMAIN = "localhost"; - ROOT_URL = cfg.root_url; - HTTP_PORT = cfg.port; - - # ssh - START_SSH_SERVER = true; # use builtin ssh server - BUILTIN_SSH_SERVER_USER = "git"; - SSH_DOMAIN = "ganymede"; - SSH_PORT = 2225; # don't conflict with system ssh - SSH_LISTEN_HOST = cfg.bind_host; - SSH_LISTEN_PORT = 2225; - }; - service = { - DISABLE_REGISTRATION = false; - ENABLE_REVERSE_PROXY_AUTHENTICATION = true; - }; - repository = { - # disable stuff - DISABLE_MIGRATIONS = true; - DISABLE_STARS = true; - DISABLE_DOWNLOAD_SOURCE_ARCHIVES = true; - }; - }; - }; - - systemd.services."forgejo" = lib.mkIf config.collinux.services.networking.networkd.enable { - after = lib.mkAfter ["network-online.target"]; - wants = lib.mkAfter ["network-online.target"]; - }; - - services.caddy = lib.mkIf cfg.caddy.enable { - virtualHosts.${cfg.root_url}.extraConfig = '' - ${ - if cfg.caddy.bind_tailscale - then "bind tailscale/${cfg.service_name}" - else "" - } - reverse_proxy ${cfg.bind_host}:${toString cfg.port} - ''; - }; - } diff --git a/modules/services/nixos/selfhost/headscale.nix b/modules/services/nixos/selfhost/headscale.nix deleted file mode 100644 index 2bdcca1..0000000 --- a/modules/services/nixos/selfhost/headscale.nix +++ /dev/null @@ -1,75 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: let - cfg = config.collinux.services.selfhost.headscale; - - acl_file = (pkgs.formats.json {}).generate "acl.json" { - ssh = [ - { - src = ["collin@"]; - dst = ["collin@"]; - users = ["autogroup:nonroot" "root"]; - action = "accept"; - } - ]; - }; -in - lib.mkIf cfg.enable { - services.headscale = { - enable = true; - address = cfg.bind_host; - port = cfg.port; - settings = { - server_url = "https://${cfg.root_url}"; - - database.type = "sqlite"; - - dns = { - magic_dns = true; - base_domain = "collinux.tailnet"; - override_local_dns = true; - nameservers.global = ["9.9.9.9" "149.112.112.112" "2620:fe::fe" "2620:fe::9"]; - }; - - policy.path = "${acl_file}"; - - prefixes = { - "v4" = "100.100.0.0/16"; - allocation = "random"; - }; - - # leave tls for caddy to worry about - tls_cert_path = null; - tls_key_path = null; - - logtail.enabled = false; - }; - }; - - # make sure headscale can start before tailscale - systemd.services."headscale" = lib.mkIf config.collinux.services.networking.tailscale.enable { - after = lib.mkForce ["network.target"]; - before = lib.mkForce ["headscale.target"]; - wants = lib.mkForce ["network.target" "headscale.target"]; - }; - - systemd.targets."headscale" = { - description = "Target represents headscale is running. started by headscale.service"; - }; - - environment.systemPackages = [pkgs.headscale]; - - services.caddy = lib.mkIf cfg.caddy.enable { - virtualHosts.${cfg.root_url}.extraConfig = '' - ${ - if cfg.caddy.bind_tailscale - then "bind tailscale/${cfg.service_name}" - else "" - } - reverse_proxy ${cfg.bind_host}:${toString cfg.port} - ''; - }; - } diff --git a/modules/services/nixos/selfhost/jellyfin.nix b/modules/services/nixos/selfhost/jellyfin.nix deleted file mode 100644 index 59ea473..0000000 --- a/modules/services/nixos/selfhost/jellyfin.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ - config, - lib, - ... -}: let - cfg = config.collinux.services.selfhost.jellyfin; -in - lib.mkIf cfg.enable (lib.mkMerge [ - { - services.jellyfin = { - enable = true; - }; - } - # (lib.mkIf (with config.collinux.services; networking.tailscale.enable && selfhost.caddy.enable) { - # services.caddy.virtualHosts."https://jellyfin.tail7cca06.ts.net".extraConfig = '' - # bind tailscale/jellyfin - # reverse_proxy localhost:${config.services.jellyfin.port} - # ''; - # }) - ]) diff --git a/modules/services/nixos/selfhost/navidrome.nix b/modules/services/nixos/selfhost/navidrome.nix deleted file mode 100644 index 5250b85..0000000 --- a/modules/services/nixos/selfhost/navidrome.nix +++ /dev/null @@ -1,19 +0,0 @@ -{ - config, - lib, - ... -}: let - cfg = config.collinux.services.selfhost.navidrome; -in - lib.mkIf cfg.enable { - services.navidrome = { - enable = true; - inherit (cfg) user; - settings = { - Port = 4533; - Address = "0.0.0.0"; - EnableInsightsCollector = false; - MusicFolder = "/home/${cfg.user}/Music"; - }; - }; - } diff --git a/modules/services/nixos/ssh.nix b/modules/services/nixos/ssh.nix deleted file mode 100644 index fa8b2ef..0000000 --- a/modules/services/nixos/ssh.nix +++ /dev/null @@ -1,86 +0,0 @@ -{ - config, - lib, - pkgs, - hosts, - ... -}: let - cfg = config.collinux.services.networking.sshd; -in { - config = lib.mkIf cfg.enable { - networking.firewall.allowedTCPPorts = [2222]; # only on local network - - services.openssh = { - enable = true; - - allowSFTP = false; - - hostKeys = [ - { - path = "/etc/ssh/ssh_host_ed25519_key"; - type = "ed25519"; - } - ]; - - listenAddresses = [ - { - addr = cfg.bind_host; - port = 22; - } - { - addr = cfg.bind_host; - port = 2222; - } - ]; - - settings = { - PermitRootLogin = "no"; - PasswordAuthentication = false; - PubkeyAuthentication = true; - KbdInteractiveAuthentication = true; # for google authenticator totp codes - AuthenticationMethods = "publickey,keyboard-interactive:pam"; - }; - - extraConfig = '' - Match LocalPort 2222 - AuthenticationMethods publickey - PermitRootLogin prohibit-password - ''; - }; - - security.pam.services = { - login.googleAuthenticator.enable = true; - - sshd.text = '' - account required pam_unix.so # unix (order 10900) - - auth required ${pkgs.google-authenticator}/lib/security/pam_google_authenticator.so nullok no_increment_hotp # google_authenticator (order 12500) - auth sufficient pam_permit.so - - session required pam_env.so conffile=/etc/pam/environment readenv=0 # env (order 10100) - session required pam_unix.so # unix (order 10200) - session required pam_loginuid.so # loginuid (order 10300) - session optional ${pkgs.systemd}/lib/security/pam_systemd.so # systemd (order 12000) - ''; - }; - - users.users.${config.collinux.user.name}.openssh.authorizedKeys.keys = - hosts - |> (builtins.mapAttrs (_: data: data.user_pubkey or null)) - |> builtins.attrValues - |> (builtins.filter (x: x != null)); - - users.users."root".openssh.authorizedKeys.keys = - hosts - |> (builtins.mapAttrs (_: data: data.user_pubkey or null)) - |> builtins.attrValues - |> (builtins.filter (x: x != null)); # only possible over home network (:2222) - - systemd.services."openssh" = lib.mkIf config.collinux.services.networking.networkd.enable { - after = lib.mkAfter ["network-online.target"]; - wants = lib.mkAfter ["network-online.target"]; - }; - - services.tailscale.extraSetFlags = lib.optional config.services.tailscale.enable "--ssh=true"; - }; -} diff --git a/modules/services/nixos/tailscale.nix b/modules/services/nixos/tailscale.nix deleted file mode 100644 index 50e35d3..0000000 --- a/modules/services/nixos/tailscale.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ - config, - pkgs, - lib, - ... -}: let - cfg = config.collinux.services.networking.tailscale; -in - lib.mkIf cfg.enable { - services.tailscale = { - enable = true; - useRoutingFeatures = "both"; - }; - - networking.firewall = { - checkReversePath = "loose"; - trustedInterfaces = ["tailscale0"]; - allowedUDPPorts = [config.services.tailscale.port]; - }; - - # don't start tailscale until after headscale starts - systemd.services."tailscaled" = - if config.collinux.services.selfhost.headscale.enable - then { - wants = lib.mkForce ["network.target" "headscale.target"]; - after = lib.mkForce ["network.target" "headscale.target"]; - } - else { - wants = lib.mkForce ["network.target"]; - after = lib.mkForce ["network.target"]; - }; - - environment.systemPackages = [pkgs.tailscale]; - } diff --git a/modules/services/options.nix b/modules/services/options.nix index 9a1f662..880206f 100644 --- a/modules/services/options.nix +++ b/modules/services/options.nix @@ -1,122 +1,93 @@ { lib, - config, + my-lib, ... }: let inherit (lib) mkOption mkEnableOption; + inherit ((my-lib {inherit lib;}).netTypes) ipAddr; - ip_addr = lib.types.strMatching "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"; - ip_addr_cidr = lib.types.strMatching "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])/(3[0-2]|[12]?[0-9])$"; + selfhostOptions = { + service_name, + default_port ? null, + }: { + enable = mkEnableOption "${service_name} selfhosted service"; + + service_name = mkOption { + type = lib.types.str; + internal = true; + }; + + bind_host = mkOption { + description = "The IP address on which ${service_name} will listen for incoming connections. The default, `0.0.0.0`, means 'all interfaces'"; + type = ipAddr; + default = "0.0.0.0"; + }; + port = mkOption { + description = "The port on which ${service_name} will listen for incomming connections"; + type = lib.types.port; + default = default_port; + }; + + root_url = mkOption { + description = "The final url that this service will be hosted on. Required for caddy, otherwise optional"; + type = lib.types.nullOr lib.types.str; + }; + + caddy = { + enable = mkEnableOption "Automatically create caddy configurations for this service"; + bind_tailscale = mkEnableOption "Bind the service to ${service_name}.{tailnet}"; + }; + }; in { options = { collinux.services = { - networking = { - enable = mkEnableOption "wifi"; + sshd = { + enable = mkEnableOption "OpenSSH server"; - iwd.enable = mkEnableOption "lightweight wifi daemon"; - networkmanager.enable = mkEnableOption "heavier wifi daemon"; - - networkd = { - enable = mkEnableOption "use systemd-networkd"; - ssid = mkOption { - description = "SSID for this network"; - type = lib.types.str; - }; - pskFile = mkOption { - description = "Absolute path to a file containing the pre-shared key for this network"; - type = lib.types.str; - example = "/run/secrets.d/wifi-psk"; - }; - - static = lib.mkOption { - description = "Set a static IP address for this device on this network. Set to null to use DHCP"; - type = lib.types.nullOr (lib.types.submodule { - options = { - ip = mkOption { - description = "IP address"; - type = ip_addr_cidr; - }; - gateway = mkOption { - description = "default gateway"; - type = ip_addr; - }; + portConfig = mkOption { + description = "List of ssh bind hosts. see submodule options for details"; + type = lib.types.listOf (lib.types.submodule { + options = { + port = mkOption { + description = "Port to run on"; + type = lib.types.port; }; - }); - default = null; - }; - }; - tailscale.enable = mkEnableOption "tailscale"; - sshd = { - enable = mkEnableOption "OpenSSH server"; - bind_host = mkOption { - description = "The IP address on which OpenSSH will listen for incomming connections. The default, `0.0.0.0`, means 'all interfaces'"; - type = ip_addr; - default = "0.0.0.0"; - }; + otp = mkEnableOption "Whether to require TOTP (Google Authenticator) 2fa codes for this port"; + rootLogin = mkEnableOption "Whether to allow root login for this port"; + }; + }); }; }; - audio.enable = mkEnableOption "pipewire and wireplumber"; - - bluetooth.enable = mkEnableOption "bluetooth"; - - selfhost = let - selfhostOptions = { - service_name, - default_port ? null, - }: { - enable = mkEnableOption "${service_name} selfhosted service"; - - service_name = mkOption { - type = lib.types.str; - internal = true; - }; - - bind_host = mkOption { - description = "The IP address on which ${service_name} will listen for incoming connections. The default, `0.0.0.0`, means 'all interfaces'"; - type = ip_addr; - default = "0.0.0.0"; - }; - port = mkOption { - description = "The port on which ${service_name} will listen for incomming connections"; - type = lib.types.port; - default = default_port; - }; - - root_url = mkOption { - description = "The final url that this service will be hosted on. Required for caddy, otherwise optional"; - type = lib.types.nullOr lib.types.str; - }; - - caddy = { - enable = mkEnableOption "Automatically create caddy configurations for this service"; - bind_tailscale = mkEnableOption "Bind the service to ${service_name}.{tailnet}"; - }; - }; - in { - adguard = selfhostOptions { - service_name = "adguard"; - default_port = 8001; - }; + adguard = selfhostOptions { + service_name = "adguard"; + default_port = 8001; + }; - forgejo = selfhostOptions { + forgejo = + (selfhostOptions { service_name = "forgejo"; default_port = 8010; + }) + // { + git_ssh_port = mkOption { + type = lib.types.port; + default = 2225; + }; }; - headscale = selfhostOptions { - service_name = "headscale"; - default_port = 8080; - }; + headscale = selfhostOptions { + service_name = "headscale"; + default_port = 8080; + }; - caddy = { - enable = mkEnableOption "caddy https server"; - envFile = mkOption { - description = "Absolute path to file that contains environment variables for caddy operations"; - type = lib.types.str; - example = "/run/secrets.d/caddy-env"; - }; + caddy = { + enable = mkEnableOption "caddy https server"; + envFile = mkOption { + description = "Absolute path to file that contains environment variables for caddy operations"; + type = lib.types.str; + example = "/run/secrets.d/caddy-env"; }; }; }; diff --git a/modules/system/nixos/audio.nix b/modules/system/nixos/audio.nix new file mode 100644 index 0000000..85232f6 --- /dev/null +++ b/modules/system/nixos/audio.nix @@ -0,0 +1,23 @@ +{ + pkgs, + config, + lib, + ... +}: let + cfg = config.collinux.system.audio; +in + lib.mkIf cfg.enable { + security.rtkit.enable = true; + services.pipewire = { + enable = true; + wireplumber.enable = true; + alsa.enable = true; + pulse.enable = false; + }; + + boot.blacklistedKernelModules = ["snd_seq_dummy"]; # remove extraneous alsa midi devices + + environment.systemPackages = with pkgs; [pwvucontrol qpwgraph]; + + users.users.${config.collinux.user.name}.extraGroups = ["audio" "pipewire"]; + } diff --git a/modules/system/nixos/bluetooth.nix b/modules/system/nixos/bluetooth.nix new file mode 100644 index 0000000..1795547 --- /dev/null +++ b/modules/system/nixos/bluetooth.nix @@ -0,0 +1,40 @@ +{ + lib, + config, + ... +}: let + cfg = config.collinux.system.bluetooth; +in + lib.mkIf cfg.enable { + hardware.bluetooth = { + enable = true; + powerOnBoot = true; + + settings.General = { + ControllerMode = "bredr"; + FastConnectable = true; + JustWorksRepairing = "always"; + }; + }; + + systemd.user.services."mpris-proxy" = { + unitConfig = { + BindsTo = ["bluetooth.target"]; + After = ["bluetooth.target"]; + }; + + wantedBy = ["bluetooth.target"]; + + # serviceConfig already exists (?) + }; + + # hardening (down to 2.1 OK) + systemd.services."bluetooth".serviceConfig = { + IPAddressDeny = "any"; + ProtectKernelLogs = true; + ProtectKernelModules = lib.mkForce true; + RestrictAddressFamilies = ["AF_UNIX" "AF_BLUETOOTH"]; + ProtectClock = true; + ProcSubset = "pid"; + }; + } diff --git a/modules/system/nixos/boot.nix b/modules/system/nixos/boot.nix new file mode 100644 index 0000000..b7f5266 --- /dev/null +++ b/modules/system/nixos/boot.nix @@ -0,0 +1,89 @@ +{ + pkgs, + config, + lib, + ... +}: let + cfg = config.collinux.system.boot; +in { + boot = + { + bcache.enable = false; # why is this default on? I DON'T CARE ABOUT bcachefs + initrd = { + verbose = false; + systemd.enable = true; + checkJournalingFS = false; + }; + loader = { + systemd-boot = lib.optionalAttrs (cfg.systemd-boot.enable && !cfg.secureBoot.enable) { + enable = true; + configurationLimit = 3; + }; + efi.canTouchEfiVariables = true; + timeout = cfg.timeout; # hold space to show boot menu if timeout == 0 + }; + + plymouth = lib.mkIf cfg.plymouth.enable { + enable = true; + theme = + if (cfg.plymouth.theme == "catppuccin") + then "catppuccin-macchiato" # for whatever reason catppuccin-mocha has errors + else "nixos-bgrt"; + themePackages = + if (cfg.plymouth.theme == "catppuccin") + then [pkgs.catppuccin-plymouth] + else [pkgs.nixos-bgrt-plymouth]; + }; + + # from hardened.nix + blacklistedKernelModules = [ + # Obscure network protocols + "ax25" + "netrom" + "rose" + + # Old or rare or insufficiently audited filesystems + "adfs" + "affs" + "bfs" + "befs" + "cramfs" + "efs" + # "erofs" # necessary for system.etc.overlay + "exofs" + "freevxfs" + "f2fs" + "hfs" + "hpfs" + "jfs" + "minix" + "nilfs2" + "ntfs" + "omfs" + "qnx4" + "qnx6" + "sysv" + "ufs" + ]; + } + // (lib.optionalAttrs cfg.secureBoot.enable { + lanzaboote = { + enable = true; + pkiBundle = "/var/lib/sbctl"; + }; + }); + + system.etc.overlay = { + enable = true; + mutable = true; # would love this to be false, but we're not there yet + }; + system.nixos-init.enable = true; + + # store journald logs in memory + services.journald.extraConfig = '' + Storage=volatile + RuntimeMaxUse=100M + ''; + + environment.systemPackages = [pkgs.efibootmgr] ++ lib.optional cfg.secureBoot.enable pkgs.sbctl; +} diff --git a/modules/system/nixos/default.nix b/modules/system/nixos/default.nix new file mode 100644 index 0000000..132d99c --- /dev/null +++ b/modules/system/nixos/default.nix @@ -0,0 +1,9 @@ +{ + imports = [ + ./networking + ./boot.nix + ./audio.nix + ./bluetooth.nix + ./polkit.nix + ]; +} diff --git a/modules/system/nixos/networking/default.nix b/modules/system/nixos/networking/default.nix new file mode 100644 index 0000000..a042411 --- /dev/null +++ b/modules/system/nixos/networking/default.nix @@ -0,0 +1,13 @@ +{ + imports = [ + ./iwd.nix + ./networkmanager.nix + ./networkd.nix + ./resolved.nix + ]; + + networking.firewall = { + enable = true; + checkReversePath = "loose"; + }; +} diff --git a/modules/system/nixos/networking/iwd.nix b/modules/system/nixos/networking/iwd.nix new file mode 100644 index 0000000..eea35d8 --- /dev/null +++ b/modules/system/nixos/networking/iwd.nix @@ -0,0 +1,27 @@ +{ + lib, + config, + ... +}: let + cfg = config.collinux.system.network.iwd; +in + lib.mkIf cfg.enable { + networking = { + wireless.iwd = { + enable = true; + settings = { + General = { + EnableNetworkConfiguration = !config.collinux.system.network.networkd.enable; + AddressRandomization = "once"; + AddressRandomizationRange = "full"; + }; + Network.NameResolvingService = "systemd"; # either systemd or resolvconf + }; + }; + + # Disable default networking stuff + dhcpcd.enable = false; + useDHCP = false; + networkmanager.enable = false; + }; + } diff --git a/modules/system/nixos/networking/networkd.nix b/modules/system/nixos/networking/networkd.nix new file mode 100644 index 0000000..37b0908 --- /dev/null +++ b/modules/system/nixos/networking/networkd.nix @@ -0,0 +1,60 @@ +{ + lib, + config, + ... +}: let + cfg = config.collinux.system.network.networkd; + + dhcp_enabled = cfg.static == null; +in + lib.mkIf cfg.enable { + networking = { + wireless = + if !config.collinux.system.network.iwd.enable + then { + enable = true; + networks.${cfg.ssid}.pskRaw = "ext:psk"; + secretsFile = cfg.pskFile; + } + else {}; + + useNetworkd = true; + + # Disable default networking stuff + dhcpcd.enable = false; + useDHCP = false; + networkmanager.enable = false; + }; + + systemd.network = { + enable = true; + + wait-online = { + enable = true; + ignoredInterfaces = ["docker0"]; + anyInterface = true; + }; + + networks."11-lan" = { + name = "wl*"; + + networkConfig = + ( + if dhcp_enabled + then {DHCP = "yes";} + else { + Address = cfg.static.ip; + Gateway = cfg.static.gateway; + DHCP = "no"; + # DNS is managed by systemd-resolved (not specified here) + } + ) + // { + LinkLocalAddressing = "no"; + }; + + dhcpV4Config.UseDNS = "no"; + dhcpV6Config.UseDNS = "no"; + }; + }; + } diff --git a/modules/system/nixos/networking/networkmanager.nix b/modules/system/nixos/networking/networkmanager.nix new file mode 100644 index 0000000..c2d6dd1 --- /dev/null +++ b/modules/system/nixos/networking/networkmanager.nix @@ -0,0 +1,18 @@ +{ + lib, + config, + ... +}: let + cfg = config.collinux.system.network.networkmanager; +in + lib.mkIf cfg.enable { + networking = { + networkmanager = { + enable = true; + dns = "systemd-resolved"; + dhcp = "internal"; + }; + + dhcpcd.enable = false; + }; + } diff --git a/modules/system/nixos/networking/resolved.nix b/modules/system/nixos/networking/resolved.nix new file mode 100644 index 0000000..03333b1 --- /dev/null +++ b/modules/system/nixos/networking/resolved.nix @@ -0,0 +1,18 @@ +{ + networking.resolvconf.enable = false; + + networking.nameservers = [ + "9.9.9.9#dns.quad9.net" + "149.112.112.112#dns.quad9.net" + ]; + + services.resolved = { + enable = true; + dnsovertls = "opportunistic"; + dnssec = "allow-downgrade"; + + # disable extra stuff + llmnr = "false"; + extraConfig = "MulticastDNS=no"; + }; +} diff --git a/modules/system/nixos/networking/tailscale.nix b/modules/system/nixos/networking/tailscale.nix new file mode 100644 index 0000000..552ec8b --- /dev/null +++ b/modules/system/nixos/networking/tailscale.nix @@ -0,0 +1,33 @@ +{ + config, + pkgs, + lib, + ... +}: let + cfg = config.collinux.system.network.tailscale; +in + lib.mkIf cfg.enable { + services.tailscale = { + enable = true; + useRoutingFeatures = "both"; + }; + + networking.firewall = { + trustedInterfaces = ["tailscale0"]; + allowedUDPPorts = [config.services.tailscale.port]; + }; + + systemd.services."tailscaled" = + if config.collinux.services.selfhost.headscale.enable + then { + # don't start tailscale until after headscale starts + wants = lib.mkForce ["network.target" "headscale.target"]; + after = lib.mkForce ["network.target" "headscale.target"]; + } + else { + wants = lib.mkForce ["network.target"]; + after = lib.mkForce ["network.target"]; + }; + + environment.systemPackages = [pkgs.tailscale]; + } diff --git a/modules/system/nixos/polkit.nix b/modules/system/nixos/polkit.nix new file mode 100644 index 0000000..29a06f4 --- /dev/null +++ b/modules/system/nixos/polkit.nix @@ -0,0 +1,75 @@ +{lib, ...}: let + defaultDenyRule = '' + polkit.addRule(function(action, subject) { + // Log denied actions for debugging + polkit.log("DENY: action=" + action.id + " user=" + subject.user); + return polkit.Result.NO; + }); + ''; + + run0Rules = '' + polkit.addRule(function(action, subject) { + if (subject.isInGroup("wheel") && action.id === "org.freedesktop.systemd1.manage-units") { + return polkit.Result.AUTH_ADMIN_KEEP; + } + }); + ''; + + networkRules = '' + polkit.addRule(function(action, subject) { + // Only allow network modifications for wheel group (admins) + if (action.id.startsWith("org.freedesktop.NetworkManager.") && + subject.isInGroup("wheel")) { + return polkit.Result.YES; + } + + // Allow reading network status for all users + if (action.id == "org.freedesktop.NetworkManager.network-control" || + action.id == "org.freedesktop.NetworkManager.settings.modify.system") { + if (subject.isInGroup("wheel")) { + return polkit.Result.YES; + } + return polkit.Result.NO; + } + + return polkit.Result.NOT_HANDLED; + }); + ''; + + powerRules = '' + polkit.addRule(function(action, subject) { + if (action.id.match("org.freedesktop.login1.")) { + return polkit.Result.YES; + } + }); + ''; + + bluetoothRules = '' + polkit.addRule(function(action, subject) { + // Allow users to manage bluetooth devices + if (action.id.startsWith("org.bluez.") && + subject.local && subject.active) { + return polkit.Result.YES; + } + + return polkit.Result.NOT_HANDLED; + }); + ''; +in { + security = { + polkit = { + enable = true; + adminIdentities = ["unix-group:wheel"]; + + extraConfig = lib.concatStringsSep "\n\n" [ + run0Rules + networkRules + powerRules + bluetoothRules + defaultDenyRule + ]; + }; + + soteria.enable = true; + }; +} diff --git a/modules/system/options.nix b/modules/system/options.nix new file mode 100644 index 0000000..72d0392 --- /dev/null +++ b/modules/system/options.nix @@ -0,0 +1,68 @@ +{ + lib, + my-lib, + config, + ... +}: let + inherit (lib) mkOption mkEnableOption; + inherit (my-lib.netTypes {inherit lib;}) ipAddr ipAddrCidr; + inherit (my-lib.options {inherit lib config;}) mkThemeOption; +in { + options.collinux.system = { + boot = { + systemd-boot.enable = mkOption { + description = "Whether to use systemd-boot on this system"; + default = true; + }; + timeout = mkOption { + description = "bootloader timeout"; + type = lib.types.int; + default = 0; + }; + plymouth = { + enable = mkEnableOption "plymouth bootsplash"; + theme = mkThemeOption "plymouth"; + }; + secureBoot.enable = mkEnableOption "lanzaboote"; + }; + + network = { + iwd.enable = mkEnableOption "lightweight wifi daemon"; + networkmanager.enable = mkEnableOption "heavier wifi daemon"; + networkd = { + enable = mkEnableOption "use systemd-networkd"; + ssid = mkOption { + description = "SSID for this network"; + type = lib.types.str; + }; + pskFile = mkOption { + description = "Absolute path to a file containing the pre-shared key for this network in the form `psk:`"; + type = lib.types.str; + example = "/run/secrets.d/wifi-psk"; + }; + + static = lib.mkOption { + description = "Set a static IP address for this device on this network. Leave unset to use DHCP"; + type = lib.types.nullOr (lib.types.submodule { + options = { + ip = mkOption { + description = "IP address"; + type = ipAddrCidr; + }; + gateway = mkOption { + description = "default gateway"; + type = ipAddr; + }; + }; + }); + default = null; + }; + }; + + tailscale.enable = mkEnableOption "tailscale"; + }; + + audio.enable = mkEnableOption "pipewire and wireplumber"; + bluetooth.enable = mkEnableOption "bluetooth"; + }; +} diff --git a/modules/user/nixos/default.nix b/modules/user/nixos/default.nix index 5a871af..71f6c14 100644 --- a/modules/user/nixos/default.nix +++ b/modules/user/nixos/default.nix @@ -12,7 +12,7 @@ in { users.${cfg.name} = { isNormalUser = true; description = cfg.name; - extraGroups = ["networkmanager" "pipewire" "disks" "input" "video" "dialout" "kvm"] ++ (lib.optional cfg.isAdmin "wheel"); + extraGroups = ["networkmanager" "disks" "input" "video" "dialout" "kvm"] ++ (lib.optional cfg.isAdmin "wheel"); }; }; services.userborn.enable = true; @@ -20,7 +20,12 @@ in { # sudo security = { sudo.enable = false; - sudo-rs.enable = !cfg.useRun0; + sudo-rs = lib.mkIf (!cfg.useRun0) { + enable = true; + extraConfig = '' + Defaults pwfeedback + ''; + }; run0.enableSudoAlias = cfg.useRun0; }; diff --git a/modules/user/options.nix b/modules/user/options.nix index 31f166d..6ea2793 100644 --- a/modules/user/options.nix +++ b/modules/user/options.nix @@ -6,6 +6,7 @@ in { name = mkOption { description = "Name for the sole user of this system"; type = types.str; + default = "collin"; }; isAdmin = mkOption { description = "Whether this user is an admin"; diff --git a/pkgs/yo.nix b/pkgs/yo.nix index b98926a..91f30f7 100644 --- a/pkgs/yo.nix +++ b/pkgs/yo.nix @@ -4,25 +4,29 @@ local flags = {} if arg[2] ~= nil then - for a = 2, #arg do - table.insert(flags, a) + for i = 2, #arg do + table.insert(flags, arg[i]) end end if arg[1] == "deploy" or arg[1] == "dep" or arg[1] == "d" then - cmd = ("${pkgs.deploy-rs}/bin/deploy-rs --skip-checks %s -- --log-format internal-json |& ${pkgs.nix-output-monitor}/bin/nom --json"):format(table.concat(flags, " ")) + cmd = ("nix run github:serokell/deploy-rs -- --skip-checks %s -- --log-format internal-json |& ${pkgs.nix-output-monitor}/bin/nom --json"):format(table.concat(flags, " ")) print("+ "..cmd) os.execute(cmd) elseif arg[1] == "switch" or arg[1] == "sw" or arg[1] == "s" then verb = "switch" - elseif arg[1] == "boot" or arg[1] == "b" then + elseif arg[1] == "boot" or arg[1] == "bo" then verb = "boot" elseif arg[1] == "test" or arg[1] == "t" then verb = "test" + elseif arg[1] == "build" or arg[1] == "bu" then + cmd = ("nixos-rebuild build --flake /home/collin/nixos %s --log-format internal-json |& ${pkgs.nix-output-monitor}/bin/nom --json"):format(table.concat(flags, " ")) + print("+ "..cmd) + os.execute(cmd) end if verb then - cmd = ("run0 sh -c 'nixos-rebuild %s --flake /home/collin/nixos %s --log-format internal-json |& ${pkgs.nix-output-monitor}/bin/nom --json'"):format(verb, table.concat(flags, " ")) + cmd = ("run0 --background= sh -c 'nixos-rebuild %s --flake /home/collin/nixos %s --log-format internal-json |& ${pkgs.nix-output-monitor}/bin/nom --json'"):format(verb, table.concat(flags, " ")) print("+ "..cmd) os.execute(cmd) end diff --git a/pkgs/yoshi.nix b/pkgs/yoshi.nix index 14341a0..3679ecf 100644 --- a/pkgs/yoshi.nix +++ b/pkgs/yoshi.nix @@ -18,14 +18,12 @@ if isHome() then return yoshi.ssh{ HostName = "192.168.50.2", - Port = 2222, - LocalForward = 8010 + Port = 2222 } else return yoshi.ssh{ HostName = "williamsfam.us.com", - LocalForward = 8010, - DynamicFoward = 9090 + DynamicForward = 9090 } end end -- cgit v1.3.1