diff --git a/configuration.nix b/configuration.nix new file mode 100644 index 0000000..7f28b99 --- /dev/null +++ b/configuration.nix @@ -0,0 +1,265 @@ +{ config, pkgs, ... }: + +let + hostConfigs = { + crossbox = [ ./sdr.nix ./syncthing.nix ./forgejo.nix ./radicale.nix ./ollama.nix ./docuseal.nix ./nginx.nix ]; + anvil = [ ./sdr.nix ./syncthing.nix ]; + }; +in +{ + imports = + [ # Include the results of the hardware scan. + ./hardware-configuration.nix + ] ++ hostConfigs.crossbox; + + nix.settings.experimental-features = [ "nix-command" "flakes" ]; + nix.gc = { + automatic = true; + dates = "weekly"; + options = "--delete-older-than 30d"; + }; + + # List packages installed in system profile. + environment.systemPackages = with pkgs; [ + cargo + curl + docker-compose + gcc + git + gnupg + imv + lmstudio + mpv + neovim + openssl + # Alias vi even with sudo. + (pkgs.writeShellScriptBin "vi" '' + exec ${pkgs.neovim}/bin/nvim "$@" + '') + pinentry-curses + rsync + rustc + tldr + vscodium + + # Hyprland essentials + hyprpaper # Wallpaper daemon + hypridle # Idle daemon + hyprlock # Lock screen + xdg-utils # XDG utilities + #polkit-kde-agent # Polkit authentication agent + ]; + + boot.loader.systemd-boot.enable = true; + boot.loader.efi.canTouchEfiVariables = true; + + hardware.graphics = { + enable = true; + extraPackages = with pkgs; [ + rocmPackages.clr.icd # ROCm OpenCL runtime + rocmPackages.clr + rocmPackages.rocminfo + rocmPackages.rocm-runtime + ]; + }; + + networking.hostName = "crossbox"; + # Enables wireless support via wpa_supplicant. + # networking.wireless.enable = true; + + # Configure network proxy if necessary + # networking.proxy.default = "http://user:password@proxy:port/"; + # networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain"; + + networking.networkmanager.enable = true; + + time.timeZone = "America/Los_Angeles"; + + i18n.defaultLocale = "en_US.UTF-8"; + + i18n.extraLocaleSettings = { + LC_ADDRESS = "en_US.UTF-8"; + LC_IDENTIFICATION = "en_US.UTF-8"; + LC_MEASUREMENT = "en_US.UTF-8"; + LC_MONETARY = "en_US.UTF-8"; + LC_NAME = "en_US.UTF-8"; + LC_NUMERIC = "en_US.UTF-8"; + LC_PAPER = "en_US.UTF-8"; + LC_TELEPHONE = "en_US.UTF-8"; + LC_TIME = "en_US.UTF-8"; + }; + + programs.gnupg.agent = { + enable = true; + pinentryPackage = pkgs.pinentry-curses; + }; + + # Hyprland configuration + programs.hyprland = { + enable = true; + xwayland.enable = true; + }; + + # Display manager for Hyprland + services.displayManager.sddm = { + enable = true; + wayland.enable = true; + }; + + # XDG portal for screen sharing and other desktop features + xdg.portal = { + enable = true; + extraPortals = [ pkgs.xdg-desktop-portal-gtk ]; + }; + + services.xserver = { + enable = true; + xkb = { + layout = "us"; + variant = ""; + }; + }; + + # Enable CUPS to print documents. + services.printing.enable = true; + + # Enable sound with pipewire. + services.pulseaudio.enable = false; + security.rtkit.enable = true; + services.pipewire = { + enable = true; + alsa.enable = true; + alsa.support32Bit = true; + pulse.enable = true; + # If you want to use JACK applications, uncomment this + #jack.enable = true; + }; + + # Define a user account. Don't forget to set a password with 'passwd'. + users.users.brimlock = { + isNormalUser = true; + home = "/home/brimlock"; + description = "brimlock"; + extraGroups = [ "docker" "networkmanager" "wheel" "video" "render" ]; + packages = with pkgs; [ + # Hyprland utilities and applications + waybar # Status bar + wofi # Application launcher + kitty # Terminal emulator + dunst # Notification daemon + swaylock # Screen locker + swayidle # Idle management daemon + wlogout # Logout menu + grim # Screenshot tool + slurp # Screen area selector + wl-clipboard # Clipboard utilities + brightnessctl # Brightness control + pavucontrol # Audio control + networkmanagerapplet # Network manager applet + + # File manager and utilities + nautilus + gnome-themes-extra + + # Additional tools + libreoffice + grub2_efi + exfatprogs + ]; + }; + + # Install firefox system-wide as well + programs.firefox.enable = true; + + # Allow unfree packages + nixpkgs.config.allowUnfree = true; + + # Add cachix for faster builds + nix.settings = { + substituters = [ + "https://cache.nixos.org/" + "https://nix-community.cachix.org" + ]; + trusted-public-keys = [ + "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=" + "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" + ]; + }; + + environment.variables.EDITOR = "nvim"; + # Polkit for privilege escalation + #security.polkit.enable = true; + + networking.firewall.allowedTCPPorts = [ 22 ]; + + system.stateVersion = "25.11"; + + # Crossbox Configuration + + environment.shellAliases = { + vi = "nvim"; + vim = "nvim"; + }; + + security.sudo = { + enable = true; + extraRules = [ + { + users = [ "brimlock" ]; + commands = [ + { + command = "ALL"; + options = [ "NOPASSWD" ]; + } + ]; + } + ]; + }; + + programs.steam = { + enable = true; + remotePlay.openFirewall = true; + dedicatedServer.openFirewall = true; + }; + + # List services that you want to enable: + services.openssh = { + enable = true; + settings = { + PermitRootLogin = "no"; + PasswordAuthentication = false; + KbdInteractiveAuthentication = false; + }; + }; + + # Disable automatic suspend. + # Otherwise SSH tunnels and HDMI signals break. + services.logind = { + settings = { + Login = { + HandleLidSwitch = "ignore"; + HandleHibernateKey = "ignore"; + HandleSuspendKey = "ignore"; + HandlePowerKey = "ignore"; + }; + }; + }; + + # Disable automatic suspend for SystemD. + systemd.targets.sleep.enable = false; + systemd.targets.suspend.enable = false; + systemd.targets.hibernate.enable = false; + systemd.targets.hybrid-sleep.enable = false; + + virtualisation.docker = { + enable = true; + autoPrune = { + enable = true; + dates = "weekly"; + }; + rootless = { + enable = true; + setSocketVariable = true; + }; + }; +} diff --git a/docuseal.nix b/docuseal.nix new file mode 100644 index 0000000..10f8c05 --- /dev/null +++ b/docuseal.nix @@ -0,0 +1,43 @@ +{ config, pkgs, lib, ... }: + +let + # Generate a secret key if it doesn't exist + secretKeyFile = "/var/lib/docuseal/secret-key-base"; +in +{ + services.docuseal = { + enable = true; + port = 3030; + host = "docuseal.binning.net"; + + # Point to the secret key file in the state directory + # The service will have access to this since StateDirectory is set + secretKeyBaseFile = secretKeyFile; + }; + + # Create the secret key file if it doesn't exist + # This runs before the docuseal service starts + systemd.services.docuseal-init-secret = { + description = "Initialize DocuSeal secret key"; + wantedBy = [ "docuseal.service" ]; + before = [ "docuseal.service" ]; + + serviceConfig = { + Type = "oneshot"; + StateDirectory = "docuseal"; + StateDirectoryMode = "0750"; + DynamicUser = true; + }; + + script = '' + if [ ! -f ${secretKeyFile} ]; then + echo "Generating new secret key for DocuSeal..." + ${pkgs.openssl}/bin/openssl rand -hex 64 > ${secretKeyFile} + chmod 640 ${secretKeyFile} + echo "Secret key generated at ${secretKeyFile}" + else + echo "Secret key already exists at ${secretKeyFile}" + fi + ''; + }; +} diff --git a/forgejo.nix b/forgejo.nix new file mode 100644 index 0000000..586dbfd --- /dev/null +++ b/forgejo.nix @@ -0,0 +1,49 @@ +{ config, pkgs, lib, ... }: + +{ + services.forgejo = { + enable = true; + + # Set data directory + stateDir = "/srv/forgejo"; + + # Database configuration + database = { + type = "sqlite3"; + path = "/srv/forgejo/data/forgejo.db"; + }; + + # Server settings + settings = { + server = { + DOMAIN = "forgejo.binning.net"; + ROOT_URL = "https://forgejo.binning.net/"; + HTTP_ADDR = "127.0.0.1"; + HTTP_PORT = 3000; + }; + + # Repository settings - uses default: /srv/forgejo/repositories + # No need to override repository.ROOT as the default location is good + + service = { + DISABLE_REGISTRATION = true; # Set to true to disable new user registration + }; + + # Session and security + session = { + COOKIE_SECURE = true; # Since we're using HTTPS + }; + + # Recommended security settings + security = { + INSTALL_LOCK = true; + }; + }; + }; + + # Ensure the data directory exists with proper permissions + systemd.tmpfiles.rules = [ + "d /srv/forgejo 0750 forgejo forgejo -" + "d /srv/forgejo/data 0750 forgejo forgejo -" + ]; +} diff --git a/nginx.nix b/nginx.nix new file mode 100644 index 0000000..407d14a --- /dev/null +++ b/nginx.nix @@ -0,0 +1,177 @@ +{ config, pkgs, lib, ... }: + +let + # Read multiple API keys from the secrets file at build time + # Note: This embeds the secrets in the Nix store, which is a trade-off + # Alternative: Keep secrets file and read via njs module or external auth service + secretsFile = "/srv/nginx/secrets"; + + # Read API keys from file (one key per line, will be evaluated at build time) + # If the file doesn't exist yet, this will fail - create it first + apiKeysRaw = builtins.readFile secretsFile; + apiKeys = lib.filter (k: k != "") (lib.splitString "\n" apiKeysRaw); + + # Generate map entries for each key + mapEntries = lib.concatMapStringsSep "\n " + (key: ''"Bearer ${key}" "authorized";'') + apiKeys; + +in +{ + services.nginx = { + enable = true; + + # Recommended settings + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + # Increase bucket size for long Bearer tokens + mapHashBucketSize = 128; + + # Map directive to check Authorization header against multiple keys + appendHttpConfig = '' + # Check if the Authorization header matches any expected value + map $http_authorization $auth_status { + default "unauthorized"; + "" "no_auth"; + ${mapEntries} + } + ''; + + # Virtual hosts configuration + virtualHosts = { + + # Main website - Static HTML/CSS + "www.binning.net" = { + forceSSL = true; + + #enableACME = true; + sslCertificate = "/srv/nginx/binning.net.pem"; + sslCertificateKey = "/srv/nginx/binning.net.key.pem"; + + root = "/srv/www/binning.net"; + + locations."/" = { + index = "index.html"; + tryFiles = "$uri $uri/ =404"; + extraConfig = '' + # Enable Server Side Includes for navbar/footer includes + ssi on; + ''; + }; + + # Private blog articles with HTTP basic authentication + locations."/blog/private/" = { + extraConfig = '' + auth_basic "Private Articles"; + auth_basic_user_file /srv/nginx/.htpasswd; + + # Enable Server Side Includes + ssi on; + ''; + }; + + # Optional: Custom 404 page + extraConfig = '' + error_page 404 /404.html; + ''; + }; + + # Ollama with Bearer token authentication + "ollama.binning.net" = { + forceSSL = true; + + #enableACME = true; + sslCertificate = "/srv/nginx/binning.net.pem"; + sslCertificateKey = "/srv/nginx/binning.net.key.pem"; + + locations."/" = { + extraConfig = '' + # Check auth status + if ($auth_status = "no_auth") { + return 401 "Unauthorized: Bearer token required\n"; + } + if ($auth_status = "unauthorized") { + return 403 "Forbidden: Invalid API key\n"; + } + + # Proxy to Ollama (only if authorized) + proxy_pass http://localhost:11434; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # Timeouts for long-running requests + proxy_read_timeout 300s; + proxy_connect_timeout 300s; + proxy_send_timeout 300s; + + # Allow large request bodies + client_max_body_size 100M; + + # Logging + access_log /var/log/nginx/ollama_access.log; + error_log /var/log/nginx/ollama_error.log; + ''; + }; + }; + + # Forgejo + "forgejo.binning.net" = { + forceSSL = true; + + #enableACME = true; + sslCertificate = "/srv/nginx/binning.net.pem"; + sslCertificateKey = "/srv/nginx/binning.net.key.pem"; + + locations."/" = { + proxyPass = "http://127.0.0.1:3000"; + # No extraConfig needed - recommendedProxySettings handles headers + }; + }; + + # Radicale + "radicale.binning.net" = { + forceSSL = true; + + #enableACME = true; + sslCertificate = "/srv/nginx/binning.net.pem"; + sslCertificateKey = "/srv/nginx/binning.net.key.pem"; + + locations."/" = { + proxyPass = "http://127.0.0.1:5232"; + # recommendedProxySettings handles most headers + extraConfig = '' + proxy_set_header X-Script-Name ""; + ''; + }; + }; + + # DocuSeal + "docuseal.binning.net" = { + forceSSL = true; + + #enableACME = true; + sslCertificate = "/srv/nginx/binning.net.pem"; + sslCertificateKey = "/srv/nginx/binning.net.key.pem"; + + locations."/" = { + proxyPass = "http://127.0.0.1:3030"; + proxyWebsockets = true; + }; + }; + }; + }; + + # Firewall + networking.firewall.allowedTCPPorts = [ 80 443 ]; + + # ACME/Let's Encrypt + security.acme = { + acceptTerms = true; + defaults.email = "hvanb@pm.me"; + }; +} diff --git a/ollama.nix b/ollama.nix new file mode 100644 index 0000000..9f40eee --- /dev/null +++ b/ollama.nix @@ -0,0 +1,22 @@ +{ config, pkgs, lib, ... }: + +{ + services.ollama = { + enable = true; + host = "0.0.0.0"; + port = 11434; + }; + + # Open firewall port for Ollama + networking.firewall.allowedTCPPorts = [ 11434 ]; + + # Install ollama-rocm package + environment.systemPackages = with pkgs; [ + ollama-rocm + ]; + + # Add CA certificate for Ollama + security.pki.certificateFiles = [ + /home/brimlock/ollama-ca.crt + ]; +} diff --git a/radicale.nix b/radicale.nix new file mode 100644 index 0000000..d74e53e --- /dev/null +++ b/radicale.nix @@ -0,0 +1,37 @@ +{ config, pkgs, lib, ... }: + +{ + services.radicale = { + enable = true; + + # Configuration settings + settings = { + server = { + hosts = [ "127.0.0.1:5232" "[::1]:5232" ]; + }; + + # Authentication (you can customize this) + auth = { + type = "htpasswd"; + htpasswd_filename = "/srv/radicale/users"; + htpasswd_encryption = "bcrypt"; + }; + + # Storage configuration + storage = { + filesystem_folder = "/srv/radicale/collections"; + }; + + # Logging + logging = { + level = "info"; + }; + + rights = { + type = "from_file"; + file = "/srv/radicale/rights"; + }; + }; + + }; +} diff --git a/sdr.nix b/sdr.nix new file mode 100644 index 0000000..58718b3 --- /dev/null +++ b/sdr.nix @@ -0,0 +1,13 @@ +{ config, pkgs, ... }: + +{ + environment.systemPackages = with pkgs; [ + hackrf + urh + usbutils + ]; + services.udev.packages = with pkgs; [ + hackrf + rtl-sdr + ]; +} diff --git a/syncthing.nix b/syncthing.nix new file mode 100644 index 0000000..3f1ce08 --- /dev/null +++ b/syncthing.nix @@ -0,0 +1,26 @@ +{ config, pkgs, ... }: + +{ + services.syncthing = { + enable = true; + openDefaultPorts = true; + user = "brimlock"; + group = "users"; + dataDir = "/home/brimlock/syncthing"; + configDir = "/home/brimlock/.config/syncthing"; + settings = { + /* gui = { + user = "brimlock"; + };*/ + devices = { + "dagger" = { id = "TODPOIO-CEK5N7S-I2X6PLL-YZ4NVEA-L4QRN4Y-7R4FR46-53S2EC2-C7AEAA4-ID-GOES-HERE"; }; + }; + folders = { + "Sync" = { + path = "/home/brimlock/Sync"; + devices = [ "dagger" ]; + }; + }; + }; + }; +}