diff --git a/flake.lock b/flake.lock index 6141529..3e8da75 100644 --- a/flake.lock +++ b/flake.lock @@ -27,6 +27,27 @@ "type": "github" } }, + "crowdsec": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1752497357, + "narHash": "sha256-9epXn1+T6U4Kfyw8B9zMzbERxDB3VfaPXhVebtai6CE=", + "ref": "refs/heads/main", + "rev": "84db7dcea77f7f477d79e69e35fb0bb560232667", + "revCount": 42, + "type": "git", + "url": "https://codeberg.org/kampka/nix-flake-crowdsec.git" + }, + "original": { + "type": "git", + "url": "https://codeberg.org/kampka/nix-flake-crowdsec.git" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -48,6 +69,23 @@ "type": "github" } }, + "flake-utils": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -90,64 +128,6 @@ "type": "github" } }, - "nix-rosetta-builder": { - "inputs": { - "nixos-generators": "nixos-generators", - "nixpkgs": [ - "nixpkgs-darwin" - ] - }, - "locked": { - "lastModified": 1756177999, - "narHash": "sha256-aSbB7/jrt7ujiJ55f2uGhOo+usGxVSkqbAMVgg2jDls=", - "owner": "cpick", - "repo": "nix-rosetta-builder", - "rev": "ebb7162a975074fb570a2c3ac02bc543ff2e9df4", - "type": "github" - }, - "original": { - "owner": "cpick", - "repo": "nix-rosetta-builder", - "type": "github" - } - }, - "nixlib": { - "locked": { - "lastModified": 1736643958, - "narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=", - "owner": "nix-community", - "repo": "nixpkgs.lib", - "rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixpkgs.lib", - "type": "github" - } - }, - "nixos-generators": { - "inputs": { - "nixlib": "nixlib", - "nixpkgs": [ - "nix-rosetta-builder", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1737057290, - "narHash": "sha256-3Pe0yKlCc7EOeq1X/aJVDH0CtNL+tIBm49vpepwL1MQ=", - "owner": "nix-community", - "repo": "nixos-generators", - "rev": "d002ce9b6e7eb467cd1c6bb9aef9c35d191b5453", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "nixos-generators", - "type": "github" - } - }, "nixpkgs": { "locked": { "lastModified": 1764522689, @@ -258,9 +238,9 @@ "root": { "inputs": { "agenix": "agenix", + "crowdsec": "crowdsec", "home-manager": "home-manager", "nix-darwin": "nix-darwin", - "nix-rosetta-builder": "nix-rosetta-builder", "nixpkgs": "nixpkgs", "nixpkgs-darwin": "nixpkgs-darwin", "nixpkgs-unstable": "nixpkgs-unstable", @@ -306,6 +286,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 59c9ce0..e01f3d1 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,10 @@ }; # Linux-specific inputs + crowdsec = { + url = "git+https://codeberg.org/kampka/nix-flake-crowdsec.git"; + inputs.nixpkgs.follows = "nixpkgs"; + }; plasma-manager = { url = "github:nix-community/plasma-manager"; inputs = { @@ -44,15 +48,11 @@ url = "github:LnL7/nix-darwin/nix-darwin-25.11"; inputs.nixpkgs.follows = "nixpkgs-darwin"; }; - nix-rosetta-builder = { - url = "github:cpick/nix-rosetta-builder"; - inputs.nixpkgs.follows = "nixpkgs-darwin"; - }; }; outputs = { self, nix-darwin, nixpkgs, nixpkgs-darwin, nixpkgs-unstable - , home-manager, agenix, rycee-nurpkgs, nur, plasma-manager, quadlet-nix - , nix-rosetta-builder, ... }: + , home-manager, agenix, rycee-nurpkgs, nur, crowdsec, plasma-manager + , quadlet-nix, ... }: let mkHomeConfiguration = { hostname, arch ? "x86_64", os ? "linux" , desktop ? false, extraModules ? [ ] }: @@ -123,11 +123,8 @@ }; agenix = agenix; }; - modules = [ - ./systems/darwin/corianne.nix - agenix.darwinModules.default - nix-rosetta-builder.darwinModules.default - ]; + modules = + [ ./systems/darwin/corianne.nix agenix.darwinModules.default ]; }; nixosConfigurations = { @@ -150,6 +147,9 @@ agenix.nixosModules.default home-manager.nixosModules.home-manager quadlet-nix.nixosModules.quadlet + crowdsec.nixosModules.crowdsec + crowdsec.nixosModules.crowdsec-firewall-bouncer + { nixpkgs.overlays = [ crowdsec.overlays.default ]; } ]; }; }; diff --git a/homes/desktop.nix b/homes/desktop.nix index 255db78..7b1b2ad 100644 --- a/homes/desktop.nix +++ b/homes/desktop.nix @@ -22,7 +22,6 @@ nil nixd nixfmt-classic - nixos-rebuild quarto roboto-slab shellcheck diff --git a/programs/zed.nix b/programs/zed.nix index b3f7fd6..19beb45 100644 --- a/programs/zed.nix +++ b/programs/zed.nix @@ -1,4 +1,4 @@ -{ pkgs, ... }: { +{ ... }: { programs.zed-editor = { enable = true; extensions = [ @@ -58,22 +58,6 @@ initialization_options.formatting.command = [ "nixfmt" ]; settings.nix.flake.autoArchive = true; }; - texlab = { - settings = { - build = { - onSave = false; - forwardSearchAfter = true; - }; - forwardSearch = if pkgs.stdenv.hostPlatform.isDarwin then { - executable = - "/Applications/Skim.app/Contents/SharedSupport/displayline"; - args = [ "-r" "%l" "%p" "%f" "-g" ]; - } else { - executable = "/usr/bin/okular"; - args = [ "--unique" "file:%p#src:%l%f" ]; - }; - }; - }; tinymist = { settings = { exportPdf = "onSave"; @@ -97,12 +81,5 @@ ui_font_size = 16; wrap_guides = [ 80 92 120 ]; }; - userTasks = [{ - label = "latexmk (project)"; - command = "latexmk"; - args = [ "-synctex=1" "-pdf" "-recorder" ]; - cwd = "$ZED_DIRNAME"; - tags = [ "latex-build" ]; - }]; }; } diff --git a/secrets.nix b/secrets.nix index 45783ee..8042bf6 100644 --- a/secrets.nix +++ b/secrets.nix @@ -34,8 +34,7 @@ in { ++ [ mcentire-host ]; "secrets/darwin-policies-json.age".publicKeys = system-administrators ++ [ corianne-host ]; - "secrets/freshrss.toml.age".publicKeys = system-administrators - ++ [ mcentire-host ]; "secrets/network-information.age".publicKeys = system-administrators ++ [ bosephus-host ]; + "secrets/pihole.age".publicKeys = system-administrators ++ [ bosephus-host ]; } diff --git a/secrets/freshrss.toml.age b/secrets/freshrss.toml.age deleted file mode 100644 index e253b88..0000000 Binary files a/secrets/freshrss.toml.age and /dev/null differ diff --git a/secrets/pihole.age b/secrets/pihole.age new file mode 100644 index 0000000..1b2bc25 Binary files /dev/null and b/secrets/pihole.age differ diff --git a/services/crowdsec.nix b/services/crowdsec.nix index 5c3e279..6a54584 100644 --- a/services/crowdsec.nix +++ b/services/crowdsec.nix @@ -1,49 +1,101 @@ -{ pkgs, config, ... }: { +{ pkgs, config, ... }: +let + crowdsec-port = "2763"; + firewall-bouncer-name = "fw-bouncer"; + # Although this key can be reproduced by anyone who actually cares to, the + # Crowdsec API will not be exposed to the outside world, so keeping this key + # super secret really isn't that important to me. Still make it look random + # so that hungry botnets can't just slurp up the password in plaintext. + firewall-bouncer-key = builtins.hashString "sha256" + "${config.networking.hostName}-crowdsec-bouncer-salt"; + toMultiYAML = items: + pkgs.lib.concatMapStrings (item: + '' + + --- + '' + (pkgs.lib.generators.toYAML { } item) + "\n") items; +in { services = { crowdsec = { enable = true; - localConfig = { - acquisitions = [ - { - source = "journalctl"; - journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; - labels.type = "syslog"; - } - { - filenames = [ "/var/log/auth.log" ]; - labels.type = "syslog"; - } - { - filenames = [ "/var/log/syslog" "/var/log/kern.log" ]; - labels.type = "syslog"; - } - ]; - }; - hub = { - collections = [ - "crowdsecurity/base-http-scenarios" - "crowdsecurity/http-cve" - "crowdsecurity/http-dos" - "crowdsecurity/iptables" - "crowdsecurity/linux" - "crowdsecurity/sshd" - "crowdsecurity/whitelist-good-actors" - ]; - }; + allowLocalJournalAccess = true; settings = { - general = { api.server.enable = true; }; - # See https://github.com/NixOS/nixpkgs/issues/445342 - lapi.credentialsFile = "/var/lib/crowdsec/lapi-credentials.yaml"; + api.server = { listen_uri = "127.0.0.1:${crowdsec-port}"; }; + crowdsec_service.acquisition_path = pkgs.writeText "acquisitions.yaml" + (toMultiYAML [ + { + source = "journalctl"; + journalctl_filter = [ "_SYSTEMD_UNIT=sshd.service" ]; + labels.type = "syslog"; + } + { + filenames = [ "/var/log/auth.log" ]; + labels.type = "syslog"; + } + { + filenames = [ "/var/log/syslog" "/var/log/kern.log" ]; + labels.type = "syslog"; + } + ]); }; - autoUpdateService = true; }; - crowdsec-firewall-bouncer = { enable = true; - registerBouncer.enable = true; + settings = { + api_url = "http://localhost:${crowdsec-port}"; + api_key = firewall-bouncer-key; + }; }; }; - systemd.tmpfiles.rules = let cfg = config.services.crowdsec; - in [ "d /var/lib/crowdsec 0755 ${cfg.user} ${cfg.group}" ]; + systemd.services.crowdsec.serviceConfig = { + ExecStartPre = let + bouncer-script = pkgs.writeScriptBin "register-bouncer" '' + #!${pkgs.runtimeShell} + set -eu + set -o pipefail + + if ! cscli bouncers list | grep -q "${firewall-bouncer-name}"; then + cscli bouncers add "${firewall-bouncer-name}" --key "${firewall-bouncer-key}" + fi + ''; + collection-check = collection: '' + + if ! cscli collections list | grep -q "${collection}"; then + cscli collections --trace install "${collection}" + sleep 1 + fi + + ''; + collections = [ + "crowdsecurity/base-http-scenarios" + "crowdsecurity/http-cve" + "crowdsecurity/http-dos" + "crowdsecurity/iptables" + "crowdsecurity/linux" + "crowdsecurity/sshd" + "crowdsecurity/whitelist-good-actors" + ]; + collection-script = pkgs.writeScriptBin "install-collections" '' + #!${pkgs.runtimeShell} + set -eu + set -o pipefail + + # I had to run these commands in order to manually install collections + # using cscli. + # Not sure how often they should actually be run, but I would rather + # include this here. + # https://discourse.crowdsec.net/t/solved-cant-find-collections-appsec/1830 + cscli capi register + sleep 1 + cscli hub update + sleep 1 + + ${pkgs.lib.concatMapStrings collection-check collections} + ''; + in [ + "${bouncer-script}/bin/register-bouncer" + "${collection-script}/bin/install-collections" + ]; + }; } diff --git a/services/freshrss.nix b/services/freshrss.nix deleted file mode 100644 index 2f142ff..0000000 --- a/services/freshrss.nix +++ /dev/null @@ -1,146 +0,0 @@ -{ config, pkgs, home-manager-quadlet-nix, ... }: - -let - user = "freshrss"; - port = "37374"; - stateDirectory = "/var/lib/freshrss"; - serviceContainer = "freshrss"; - stateSubDir = subDir: "${stateDirectory}/${subDir}"; - createTmpfilesRule = subDir: "d ${stateSubDir subDir} 1755 ${user} ${user}"; - - dbDirectories = [ "database" ]; - serviceDirectories = [ "data" "extensions" ]; -in { - age.secrets = { - "freshrss.toml" = { - file = ./../secrets/freshrss.toml.age; - owner = "${user}"; - }; - }; - - millironx.podman-secrets.freshrss = { - user = "${user}"; - secrets-files = [ config.age.secrets."freshrss.toml".path ]; - }; - - services.caddy.virtualHosts."feeds.millironx.com".extraConfig = '' - reverse_proxy http://127.0.0.1:${port} { - header_up X-Forwarded-Port 443 - } - ''; - - systemd.tmpfiles.rules = builtins.map createTmpfilesRule - ([ stateDirectory ] ++ dbDirectories ++ serviceDirectories); - - services.borgmatic.configurations."${config.networking.hostName}" = { - source_directories = builtins.map stateSubDir dbDirectories; - - postgresql_databases = [{ - name = serviceContainer; - psql_command = - "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db psql --username=${user}"; - pg_dump_command = - "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db pg_dump --username=${user}"; - pg_restore_command = - "/run/wrappers/bin/sudo -iu ${user} ${pkgs.podman}/bin/podman exec ${serviceContainer}-db pg_restore --username=${user}"; - }]; - }; - - users.users."${user}" = { - group = "${user}"; - isNormalUser = true; - home = "${stateDirectory}"; - createHome = true; - linger = true; - autoSubUidGidRange = true; - }; - users.groups."${user}" = { }; - - home-manager.users."${user}" = { config, osConfig, ... }: { - imports = [ home-manager-quadlet-nix ]; - - home.stateVersion = "25.05"; - - virtualisation.quadlet = let - inherit (config.virtualisation.quadlet) containers; - inherit (config.virtualisation.quadlet) networks; - secrets = osConfig.millironx.podman-secrets.freshrss; - - in { - containers = { - "${serviceContainer}-db" = { - autoStart = true; - containerConfig = { - image = "docker.io/library/postgres:16"; - environments = { - POSTGRES_DB = "${user}"; - POSTGRES_USER = "${user}"; - }; - secrets = [ - "POSTGRES_PASSWORD,type=env" - "POSTGRES_PASSWORD,type=env,target=PGPASSWORD" - ]; - healthCmd = "pg_isready -d $\${POSTGRES_DB} -U $\${POSTGRES_USER}"; - healthInterval = "30s"; - healthRetries = 5; - healthStartPeriod = "20s"; - volumes = - [ "${stateDirectory}/database:/var/lib/postgresql/data:U" ]; - networks = [ networks."${serviceContainer}".ref ]; - }; - unitConfig.Requires = [ secrets.ref ]; - unitConfig.After = [ secrets.ref ]; - }; - - "${serviceContainer}" = { - autoStart = true; - containerConfig = { - image = "docker.io/freshrss/freshrss:1"; - # Required to allow the container to talk to the host ports, in - # other words, to resolve Authentik correctly - addHosts = [ "auth.millironx.com:host-gateway" ]; - environments = { - TZ = osConfig.time.timeZone; - CRON_MIN = "2,32"; - LISTEN = "0.0.0.0:${port}"; - TRUSTED_PROXY = "172.16.0.1/12 192.168.0.1/16"; - OIDC_ENABLED = "1"; - OIDC_PROVIDER_METADATA_URL = - "https://auth.millironx.com/application/o/freshrss/.well-known/openid-configuration"; - OIDC_REMOTE_USER_CLAIM = "preferred_username"; - OIDC_SCOPES = "openid email profile"; - OIDC_X_FORWARDED_HEADERS = - "X-Forwarded-Host X-Forwarded-Port X-Forwarded-Proto"; - }; - secrets = [ - "OIDC_CLIENT_ID,type=env" - "OIDC_CLIENT_SECRET,type=env" - "OIDC_CLIENT_CRYPTO_KEY,type=env" - ]; - healthCmd = "cli/health.php"; - healthTimeout = "10s"; - healthStartPeriod = "60s"; - healthStartupInterval = "11s"; - healthInterval = "75s"; - healthRetries = 3; - networks = [ networks."${serviceContainer}".ref ]; - publishPorts = [ "127.0.0.1:${port}:${port}" ]; - volumes = [ - "${stateDirectory}/data:/var/www/FreshRSS/data:U" - "${stateDirectory}/extensions:/var/www/FreshRSS/extensions:U" - ]; - }; - unitConfig.Requires = - [ secrets.ref containers."${serviceContainer}-db".ref ]; - unitConfig.After = - [ secrets.ref containers."${serviceContainer}-db".ref ]; - }; - }; - - networks."${serviceContainer}" = { }; - - autoUpdate.enable = true; - autoEscape = true; - }; - }; -} diff --git a/services/pihole.nix b/services/pihole.nix new file mode 100644 index 0000000..ed4cd19 --- /dev/null +++ b/services/pihole.nix @@ -0,0 +1,27 @@ +{ config, ... }: + +{ + age.secrets = { + pihole-credentials = { + file = ./../secrets/pihole.age; + owner = "root"; + group = "root"; + }; + }; + virtualisation = { + quadlet = { + containers = { + pihole = { + containerConfig = { + image = "docker.io/pihole/pihole:2025.06.2"; + publishPorts = + [ "53:53/tcp" "53:53/udp" "80:80/tcp" "443:443/tcp" ]; + environmentFiles = [ config.age.secrets.pihole-credentials.path ]; + networks = [ "bridge" ]; + dns = [ "127.0.0.1" "194.242.2.9" "9.9.9.9" ]; + }; + }; + }; + }; + }; +} diff --git a/services/samba.nix b/services/samba.nix index b248d33..e6fa607 100644 --- a/services/samba.nix +++ b/services/samba.nix @@ -2,6 +2,7 @@ services.samba = { enable = true; package = pkgs.sambaFull; + securityType = "user"; openFirewall = true; settings = { global = { diff --git a/systems/darwin/corianne.nix b/systems/darwin/corianne.nix index f1c1eb2..dea0f34 100644 --- a/systems/darwin/corianne.nix +++ b/systems/darwin/corianne.nix @@ -26,27 +26,9 @@ in { }; # Auto upgrade nix package and the daemon service. - nix = { - enable = true; - gc = { - automatic = true; - interval = { Weekday = 1; }; - options = '' - --delete-older-than 14d - ''; - }; - # Needed for rosetta-builder, see - # - # - linux-builder = { - enable = true; - ephemeral = true; - }; - extraOptions = '' - extra-platforms = x86_64-darwin - ''; - }; - nix-rosetta-builder.onDemand = true; + nix.enable = true; + #services.nix-daemon.tempDir = "/nix/tmp"; + nix.package = pkgs.nix; # Create /etc/zshrc that loads the nix-darwin environment. programs.zsh.enable = true; # default shell on catalina @@ -224,7 +206,6 @@ in { "rig" "rstudio" "signal" - "skim" "slack" "stats" "steam" diff --git a/systems/linux/bosephus.nix b/systems/linux/bosephus.nix index 6c8d181..98cf0e7 100644 --- a/systems/linux/bosephus.nix +++ b/systems/linux/bosephus.nix @@ -9,6 +9,7 @@ ./hardware-configuration/bosephus.nix ./hardware-configuration/bosephus-external-drives.nix ./../../services/samba.nix + ./../../services/pihole.nix ]; # Bootloader. @@ -16,8 +17,8 @@ boot.loader.efi.canTouchEfiVariables = true; # Ignore lid - so I can close without having the system go into sleep mode - services.logind.settings.Login.HandleLidSwitch = "ignore"; - services.logind.settings.Login.HandleLidSwitchDocked = "ignore"; + services.logind.lidSwitch = "ignore"; + services.logind.lidSwitchDocked = "ignore"; # Secrets age.secrets = { diff --git a/systems/linux/mcentire.nix b/systems/linux/mcentire.nix index fa0c08b..4bd52f3 100644 --- a/systems/linux/mcentire.nix +++ b/systems/linux/mcentire.nix @@ -7,7 +7,6 @@ ./../../services/borgmatic.nix ./../../services/crowdsec.nix ./../../services/authentik.nix - ./../../services/freshrss.nix ]; # Use the GRUB 2 boot loader.