init: Add crossbox's configurations

This commit is contained in:
matthew.binning 2026-01-08 19:41:55 -08:00
commit b030b3a098
11 changed files with 652 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# ---> Nix
# Ignore build outputs from performing a nix-build or `nix build` command
result
result-*
# Ignore automatically generated direnv output
.direnv

9
LICENSE Normal file
View file

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2026 matthew.binning
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# nixos-config
NixOS configurations for several hosts on a network

265
configuration.nix Normal file
View file

@ -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;
};
};
}

43
docuseal.nix Normal file
View file

@ -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
'';
};
}

49
forgejo.nix Normal file
View file

@ -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 -"
];
}

177
nginx.nix Normal file
View file

@ -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";
};
}

22
ollama.nix Normal file
View file

@ -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
];
}

37
radicale.nix Normal file
View file

@ -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";
};
};
};
}

13
sdr.nix Normal file
View file

@ -0,0 +1,13 @@
{ config, pkgs, ... }:
{
environment.systemPackages = with pkgs; [
hackrf
urh
usbutils
];
services.udev.packages = with pkgs; [
hackrf
rtl-sdr
];
}

26
syncthing.nix Normal file
View file

@ -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" ];
};
};
};
};
}