nix/packages/hosts/beefcake.nix

2184 lines
68 KiB
Nix
Raw Normal View History

2023-10-12 22:54:05 -05:00
/*
2025-02-14 13:31:18 -06:00
if ur fans get loud:
2023-10-12 22:54:05 -05:00
2025-02-14 13:31:18 -06:00
# enable manual fan control
sudo nix run nixpkgs#ipmitool -- raw 0x30 0x30 0x01 0x00
2023-10-12 22:54:05 -05:00
2025-02-14 13:31:18 -06:00
# set fan speed to last byte as decimal
sudo nix run nixpkgs#ipmitool -- raw 0x30 0x30 0x02 0xff 0x00
2023-10-12 22:54:05 -05:00
*/
2023-10-03 11:52:44 -05:00
{
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
inputs,
outputs,
2024-09-12 11:58:24 -05:00
*/
2024-02-21 22:15:41 -06:00
lib,
2024-02-21 20:39:10 -06:00
config,
2023-10-03 11:52:44 -05:00
pkgs,
hardware,
2023-10-03 11:52:44 -05:00
...
2025-02-14 13:31:18 -06:00
}:
{
2024-09-04 10:31:06 -05:00
system.stateVersion = "24.05";
2024-03-13 21:12:14 -05:00
networking.hostName = "beefcake";
boot = {
zfs = {
extraPools = [ "zstorage" ];
};
supportedFilesystems = {
zfs = true;
};
initrd.supportedFilesystems = {
zfs = true;
};
# kernelPackages = config.boot.zfs.package.latestCompatibleLinuxPackages;
initrd.availableKernelModules = [
"ehci_pci"
"mpt3sas"
"usbhid"
"sd_mod"
];
kernelModules = [ "kvm-intel" ];
kernelParams = [ "nohibernate" ];
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/992ce55c-7507-4d6b-938c-45b7e891f395";
fsType = "ext4";
};
"/boot" = {
device = "/dev/disk/by-uuid/B6C4-7CF4";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
"/nix" = {
device = "zstorage/nix";
fsType = "zfs";
};
};
2023-09-05 21:46:55 -05:00
networking = {
hostId = "541ede55";
};
2023-09-05 21:46:55 -05:00
services = {
zfs = {
autoScrub.enable = true;
autoSnapshot.enable = true;
};
tailscale.useRoutingFeatures = "server";
2023-09-05 21:46:55 -05:00
};
2024-09-06 08:39:30 -05:00
sops = {
defaultSopsFile = ../../secrets/beefcake/secrets.yml;
secrets = {
netlify-ddns-password.mode = "0400";
nix-cache-priv-key.mode = "0400";
};
};
2024-09-06 08:39:30 -05:00
virtualisation.oci-containers.backend = "podman";
2024-09-06 08:39:30 -05:00
services.deno-netlify-ddns-client = {
enable = true;
passwordFile = config.sops.secrets.netlify-ddns-password.path;
username = "beefcake.h";
};
environment.systemPackages = with pkgs; [
aria2
restic
btrfs-progs
zfs
smartmontools
htop
bottom
curl
xh
];
2025-02-17 00:01:57 -06:00
home-manager.users.daniel = {
lyte.shell.enable = true;
};
imports = [
hardware.common-cpu-intel
2024-03-13 21:12:14 -05:00
{
services.nix-serve = {
enable = true;
secretKeyFile = config.sops.secrets.nix-cache-priv-key.path;
2024-03-13 21:12:14 -05:00
};
services.caddy.virtualHosts."nix.h.lyte.dev" = {
extraConfig = ''
reverse_proxy :${toString config.services.nix-serve.port}
'';
};
# regularly build this flake so we have stuff in the cache
2024-03-28 13:12:00 -05:00
# TODO: schedule this for nightly builds instead of intervals based on boot time
2024-09-06 16:32:10 -05:00
systemd.timers."build-lytedev-flake" = {
2025-02-14 13:31:18 -06:00
wantedBy = [ "timers.target" ];
2024-09-06 16:32:10 -05:00
timerConfig = {
OnBootSec = "30m"; # 30 minutes after booting
OnUnitActiveSec = "1d"; # every day afterwards
Unit = "build-lytedev-flake.service";
};
};
2024-09-12 10:36:29 -05:00
systemd.tmpfiles.settings = {
"10-daniel-nightly-flake-build" = {
"/home/daniel/.home/.cache/nightly-flake-builds" = {
"d" = {
mode = "0750";
user = "daniel";
group = "daniel";
};
};
};
};
2024-09-06 16:32:10 -05:00
systemd.services."build-lytedev-flake" = {
2024-09-11 11:57:27 -05:00
# TODO: might want to add root for the most recent results?
2024-09-06 16:32:10 -05:00
script = ''
# build self (main server) configuration
nixos-rebuild build --flake git+https://git.lyte.dev/lytedev/nix.git --accept-flake-config
# build desktop configuration
nixos-rebuild build --flake git+https://git.lyte.dev/lytedev/nix.git#dragon --accept-flake-config
# build main laptop configuration
nixos-rebuild build --flake git+https://git.lyte.dev/lytedev/nix.git#foxtrot --accept-flake-config
'';
2025-02-14 13:31:18 -06:00
path = with pkgs; [
openssh
git
nixos-rebuild
];
2024-09-06 16:32:10 -05:00
serviceConfig = {
# TODO: mkdir -p...?
2024-09-12 10:36:29 -05:00
WorkingDirectory = "/home/daniel/.home/.cache/nightly-flake-builds";
2024-09-06 16:32:10 -05:00
Type = "oneshot";
2024-09-11 11:57:27 -05:00
User = "daniel";
2024-09-06 16:32:10 -05:00
};
};
2024-03-30 07:50:23 -05:00
networking = {
extraHosts = ''
::1 nix.h.lyte.dev
127.0.0.1 nix.h.lyte.dev
2024-03-13 21:12:14 -05:00
'';
};
}
2024-09-06 16:36:53 -05:00
{
services.headscale = {
2024-09-11 11:57:27 -05:00
enable = false; # TODO: setup headscale?
2024-09-06 16:36:53 -05:00
address = "127.0.0.1";
port = 7777;
settings = {
server_url = "https://tailscale.vpn.h.lyte.dev";
db_type = "sqlite3";
db_path = "/var/lib/headscale/db.sqlite";
derp.server = {
enable = true;
region_id = 999;
stun_listen_addr = "0.0.0.0:3478";
};
2024-09-03 20:03:24 -05:00
2024-09-06 16:36:53 -05:00
dns_config = {
magic_dns = true;
base_domain = "vpn.h.lyte.dev";
domains = [
"ts.vpn.h.lyte.dev"
];
nameservers = [
"1.1.1.1"
# "192.168.0.1"
];
override_local_dns = true;
};
};
};
services.caddy.virtualHosts."tailscale.vpn.h.lyte.dev" = lib.mkIf config.services.headscale.enable {
extraConfig = ''
reverse_proxy http://localhost:${toString config.services.headscale.port}
'';
};
2025-02-14 13:31:18 -06:00
networking.firewall.allowedUDPPorts = lib.mkIf config.services.headscale.enable [ 3478 ];
2024-09-06 16:36:53 -05:00
}
2024-03-13 21:12:14 -05:00
{
2025-02-14 13:31:18 -06:00
services.restic.commonPaths = [
"/var/lib/soju"
"/var/lib/private/soju"
];
2024-03-13 21:12:14 -05:00
services.soju = {
enable = true;
listen = [ "irc+insecure://:6667" ]; # tailscale only
2024-03-13 21:12:14 -05:00
};
}
{
2024-09-11 11:57:27 -05:00
# nextcloud
2024-09-12 22:37:20 -05:00
users.users.nextcloud = {
isSystemUser = true;
createHome = false;
group = "nextcloud";
};
2025-02-14 13:31:18 -06:00
users.groups.nextcloud = { };
2024-09-12 22:37:20 -05:00
sops.secrets = {
nextcloud-admin-password = {
owner = "nextcloud";
group = "nextcloud";
mode = "400";
};
};
systemd.tmpfiles.settings = {
"10-nextcloud" = {
"/storage/nextcloud" = {
"d" = {
mode = "0750";
user = "nextcloud";
group = "nextcloud";
};
};
};
};
services.restic.commonPaths = [
"/storage/nextcloud"
];
2024-09-12 11:58:24 -05:00
services.postgresql = {
2025-02-14 13:31:18 -06:00
ensureDatabases = [ "nextcloud" ];
2024-09-12 11:58:24 -05:00
ensureUsers = [
{
name = "nextcloud";
ensureDBOwnership = true;
}
];
};
2024-09-12 22:37:20 -05:00
services.nextcloud = {
2024-12-02 13:27:49 -06:00
enable = false;
2024-09-12 22:37:20 -05:00
hostName = "nextcloud.h.lyte.dev";
maxUploadSize = "100G";
extraAppsEnable = true;
autoUpdateApps.enable = true;
extraApps = with config.services.nextcloud.package.packages.apps; {
2025-02-14 13:31:18 -06:00
inherit
calendar
contacts
notes
onlyoffice
tasks
;
2024-09-12 22:37:20 -05:00
};
package = pkgs.nextcloud28;
home = "/storage/nextcloud";
configureRedis = true;
caching.redis = true;
settings = {
# TODO: SMTP
maintenance_window_start = 1;
};
config = {
adminpassFile = config.sops.secrets.nextcloud-admin-password.path;
adminuser = "daniel";
dbtype = "pgsql";
dbhost = "/run/postgresql";
};
phpOptions = {
"xdebug.mode" = "debug";
"xdebug.client_host" = "10.0.2.2";
"xdebug.client_port" = "9000";
"xdebug.start_with_request" = "yes";
"xdebug.idekey" = "ECLIPSE";
};
2024-09-12 11:58:24 -05:00
};
2024-09-12 22:37:20 -05:00
services.nginx.enable = false;
systemd.services.nextcloud = {
serviceConfig.User = "nextcloud";
serviceConfig.Group = "nextcloud";
};
2024-12-02 13:27:49 -06:00
services.phpfpm = lib.mkIf config.services.nextcloud.enable {
pools.nextcloud.settings = {
"listen.owner" = "caddy";
"listen.group" = "caddy";
};
2024-09-12 22:37:20 -05:00
};
2025-02-14 13:31:18 -06:00
services.caddy.virtualHosts."nextcloud.h.lyte.dev" =
let
fpm-nextcloud-pool = config.services.phpfpm.pools.nextcloud;
root = config.services.nginx.virtualHosts.${config.services.nextcloud.hostName}.root;
in
2024-09-12 22:37:20 -05:00
lib.mkIf config.services.nextcloud.enable {
extraConfig = ''
encode zstd gzip
root * ${root}
redir /.well-known/carddav /remote.php/dav 301
redir /.well-known/caldav /remote.php/dav 301
redir /.well-known/* /index.php{uri} 301
redir /remote/* /remote.php{uri} 301
header {
Strict-Transport-Security max-age=31536000
Permissions-Policy interest-cohort=()
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
Referrer-Policy no-referrer
X-XSS-Protection "1; mode=block"
X-Permitted-Cross-Domain-Policies none
X-Robots-Tag "noindex, nofollow"
X-Forwarded-Host nextcloud.h.lyte.dev
-X-Powered-By
}
php_fastcgi unix/${fpm-nextcloud-pool.socket} {
root ${root}
env front_controller_active true
env modHeadersAvailable true
}
@forbidden {
path /build/* /tests/* /config/* /lib/* /3rdparty/* /templates/* /data/*
path /.* /autotest* /occ* /issue* /indie* /db_* /console*
not path /.well-known/*
}
error @forbidden 404
@immutable {
path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite
query v=*
}
header @immutable Cache-Control "max-age=15778463, immutable"
@static {
path *.css *.js *.mjs *.svg *.gif *.png *.jpg *.ico *.wasm *.tflite
not query v=*
}
header @static Cache-Control "max-age=15778463"
@woff2 path *.woff2
header @woff2 Cache-Control "max-age=604800"
file_server
'';
};
2024-03-13 21:12:14 -05:00
}
2024-09-06 16:44:15 -05:00
{
# plausible
2024-09-11 11:57:27 -05:00
services.postgresql = {
2025-02-14 13:31:18 -06:00
ensureDatabases = [ "plausible" ];
2024-09-11 11:57:27 -05:00
ensureUsers = [
{
name = "plausible";
ensureDBOwnership = true;
}
];
};
users.users.plausible = {
isSystemUser = true;
createHome = false;
group = "plausible";
};
users.extraGroups = {
2025-02-14 13:31:18 -06:00
"plausible" = { };
2024-09-11 11:57:27 -05:00
};
services.plausible = {
enable = true;
database = {
clickhouse.setup = true;
postgres = {
setup = false;
dbname = "plausible";
};
};
server = {
baseUrl = "https://a.lyte.dev";
disableRegistration = true;
port = 8899;
secretKeybaseFile = config.sops.secrets.plausible-secret-key-base.path;
};
adminUser = {
activate = false;
email = "daniel@lyte.dev";
passwordFile = config.sops.secrets.plausible-admin-password.path;
};
};
sops.secrets = {
plausible-secret-key-base = {
owner = "plausible";
group = "plausible";
};
plausible-admin-password = {
owner = "plausible";
group = "plausible";
};
};
systemd.services.plausible = {
serviceConfig.User = "plausible";
serviceConfig.Group = "plausible";
};
services.caddy.virtualHosts."a.lyte.dev" = {
extraConfig = ''
reverse_proxy :${toString config.services.plausible.server.port}
'';
};
}
{
# clickhouse
time.timeZone = lib.mkForce "America/Chicago";
2024-09-11 11:57:27 -05:00
environment.etc = {
"clickhouse-server/users.d/disable-logging-query.xml" = {
text = ''
<clickhouse>
<profiles>
<default>
<log_queries>0</log_queries>
<log_query_threads>0</log_query_threads>
</default>
</profiles>
</clickhouse>
'';
};
"clickhouse-server/config.d/reduce-logging.xml" = {
text = ''
<clickhouse>
<logger>
<level>warning</level>
<console>true</console>
</logger>
<query_thread_log remove="remove"/>
<query_log remove="remove"/>
<text_log remove="remove"/>
<trace_log remove="remove"/>
<metric_log remove="remove"/>
<asynchronous_metric_log remove="remove"/>
<session_log remove="remove"/>
<part_log remove="remove"/>
</clickhouse>
'';
};
};
services.restic.commonPaths = [
# "/var/lib/clickhouse"
];
}
{
# family storage
2024-09-12 10:36:29 -05:00
users.extraGroups = {
2025-02-14 13:31:18 -06:00
"family" = { };
2024-09-12 10:36:29 -05:00
};
2024-09-11 11:57:27 -05:00
systemd.tmpfiles.settings = {
2024-09-11 14:31:48 -05:00
"10-family" = {
2024-09-11 11:57:27 -05:00
"/storage/family" = {
"d" = {
mode = "0770";
user = "root";
group = "family";
};
};
2024-09-12 15:16:09 -05:00
"/storage/valerie" = {
2024-09-11 11:57:27 -05:00
"d" = {
2024-09-12 15:16:09 -05:00
mode = "0700";
2024-09-11 11:57:27 -05:00
user = "valerie";
group = "family";
};
};
};
};
services.restic.commonPaths = [
"/storage/family"
2024-09-12 15:16:09 -05:00
"/storage/valerie"
2024-09-11 11:57:27 -05:00
];
2024-09-06 16:44:15 -05:00
}
2024-03-13 21:12:14 -05:00
{
# daniel augments
2024-09-11 11:57:27 -05:00
systemd.tmpfiles.settings = {
2024-09-11 14:31:48 -05:00
"10-daniel" = {
2024-09-11 11:57:27 -05:00
"/storage/daniel" = {
"d" = {
mode = "0700";
user = "daniel";
group = "nogroup";
};
};
"/storage/daniel/critical" = {
"d" = {
mode = "0700";
user = "daniel";
group = "nogroup";
};
};
};
};
2025-02-14 13:31:18 -06:00
users.groups.daniel.members = [ "daniel" ];
2024-03-13 21:12:14 -05:00
users.users.daniel = {
extraGroups = [
"wheel" # sudo access
2024-09-03 20:03:24 -05:00
"caddy" # write access to public static files
2024-03-13 21:12:14 -05:00
"users" # general users group
2024-09-03 20:03:24 -05:00
"jellyfin" # write access to jellyfin files
"audiobookshelf" # write access to audiobookshelf files
"flanilla" # minecraft server manager
2024-09-06 16:15:58 -05:00
"forgejo"
2024-03-13 21:12:14 -05:00
];
2024-08-13 14:35:09 -05:00
};
2024-09-11 11:57:27 -05:00
services.restic.commonPaths = [
"/storage/daniel"
];
2024-09-06 16:36:53 -05:00
services.postgresql = {
2025-02-14 13:31:18 -06:00
ensureDatabases = [ "daniel" ];
2024-09-06 16:36:53 -05:00
ensureUsers = [
{
name = "daniel";
2024-09-12 22:37:20 -05:00
ensureClauses = {
# superuser = true;
# createrole = true;
# createdb = true;
# bypassrls = true;
};
2024-09-06 16:36:53 -05:00
ensureDBOwnership = true;
}
];
};
2024-08-13 14:35:09 -05:00
}
2024-09-06 16:36:53 -05:00
{
systemd.tmpfiles.settings = {
"10-jellyfin" = {
"/storage/jellyfin" = {
"d" = {
mode = "0770";
user = "jellyfin";
group = "wheel";
};
};
"/storage/jellyfin/movies" = {
"d" = {
mode = "0770";
user = "jellyfin";
group = "wheel";
};
};
"/storage/jellyfin/tv" = {
"d" = {
mode = "0770";
user = "jellyfin";
group = "wheel";
};
};
"/storage/jellyfin/music" = {
"d" = {
mode = "0770";
user = "jellyfin";
group = "wheel";
};
};
};
};
services.jellyfin = {
enable = true;
openFirewall = false;
# uses port 8096 by default, configurable from admin UI
};
services.caddy.virtualHosts."video.lyte.dev" = {
extraConfig = ''reverse_proxy :8096'';
};
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
NOTE: this server's xeon chips DO NOT seem to support quicksync or graphics in general
but I can probably throw in a crappy GPU (or a big, cheap ebay GPU for ML
stuff, too?) and get good transcoding performance
2024-09-12 11:58:24 -05:00
*/
2024-09-06 16:36:53 -05:00
# jellyfin hardware encoding
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
hardware.graphics = {
enable = true;
extraPackages = with pkgs; [
intel-media-driver
vaapiIntel
vaapiVdpau
libvdpau-va-gl
intel-compute-runtime
];
};
nixpkgs.config.packageOverrides = pkgs: {
vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
};
2024-09-12 11:58:24 -05:00
*/
2024-09-06 16:36:53 -05:00
}
{
2024-09-06 16:44:15 -05:00
systemd.tmpfiles.settings = {
2024-09-11 14:31:48 -05:00
"10-postgres" = {
2024-09-06 16:44:15 -05:00
"/storage/postgres" = {
"d" = {
mode = "0750";
2024-09-06 16:44:15 -05:00
user = "postgres";
group = "postgres";
};
};
};
};
2024-09-06 16:36:53 -05:00
services.postgresql = {
enable = true;
dataDir = "/storage/postgres";
enableTCPIP = true;
package = lib.mkForce pkgs.postgresql_15;
2024-09-06 16:36:53 -05:00
# https://www.postgresql.org/docs/current/auth-pg-hba-conf.html
2024-09-11 11:57:27 -05:00
# TODO: give the "daniel" user access to all databases
2024-09-12 22:37:20 -05:00
/*
2025-02-14 13:31:18 -06:00
authentication = pkgs.lib.mkOverride 10 ''
#type database user auth-method auth-options
local all postgres peer map=superuser_map
local all daniel peer map=superuser_map
local sameuser all peer map=superuser_map
# lan ipv4
host all daniel 192.168.0.0/16 trust
host all daniel 10.0.0.0/24 trust
# tailnet ipv4
host all daniel 100.64.0.0/10 trust
'';
2024-09-12 22:37:20 -05:00
*/
2024-09-03 20:03:24 -05:00
2024-09-12 22:37:20 -05:00
/*
2025-02-14 13:31:18 -06:00
identMap = ''
# map system_user db_user
superuser_map root postgres
superuser_map postgres postgres
superuser_map daniel postgres
# Let other names login as themselves
superuser_map /^(.*)$ \1
'';
2024-09-12 22:37:20 -05:00
*/
2024-09-06 16:36:53 -05:00
};
2024-09-03 20:03:24 -05:00
2024-09-06 16:36:53 -05:00
services.postgresqlBackup = {
enable = true;
backupAll = true;
2024-09-11 11:57:27 -05:00
compression = "none"; # hoping for restic deduplication here?
2024-09-06 16:36:53 -05:00
location = "/storage/postgres-backups";
startAt = "*-*-* 03:00:00";
};
2024-09-11 11:57:27 -05:00
services.restic.commonPaths = [
"/storage/postgres-backups"
];
2024-09-06 16:36:53 -05:00
}
2024-09-11 11:57:27 -05:00
{
# friends
users.users.ben = {
isNormalUser = true;
2025-02-14 13:31:18 -06:00
packages = [ pkgs.vim ];
2024-09-11 11:57:27 -05:00
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKUfLZ+IX85p9355Po2zP1H2tAxiE0rE6IYb8Sf+eF9T"
2024-09-11 11:57:27 -05:00
];
};
2024-09-03 20:03:24 -05:00
2024-09-11 11:57:27 -05:00
users.users.alan = {
isNormalUser = true;
2025-02-14 13:31:18 -06:00
packages = [ pkgs.vim ];
2024-09-11 11:57:27 -05:00
# openssh.authorizedKeys.keys = [];
};
}
{
2024-09-11 11:57:27 -05:00
# restic backups
sops.secrets = {
2025-02-14 13:31:18 -06:00
restic-ssh-priv-key-benland = {
mode = "0400";
};
2024-09-11 11:57:27 -05:00
restic-rascal-passphrase = {
mode = "0400";
};
restic-rascal-ssh-private-key = {
mode = "0400";
};
};
2025-02-14 13:31:18 -06:00
users.groups.restic = { };
users.users.restic = {
# used for other machines to backup to
isSystemUser = true;
2024-09-12 14:47:21 -05:00
createHome = true;
home = "/storage/backups/restic";
group = "restic";
2025-02-14 13:31:18 -06:00
extraGroups = [ "sftponly" ];
openssh.authorizedKeys.keys = [ ] ++ config.users.users.daniel.openssh.authorizedKeys.keys;
};
2024-09-12 14:47:21 -05:00
services.openssh.extraConfig = ''
Match Group sftponly
ChrootDirectory /storage/backups/%u
ForceCommand internal-sftp
AllowTcpForwarding no
'';
systemd.tmpfiles.settings = {
2024-09-11 14:31:48 -05:00
"10-backups-local" = {
"/storage/backups/local" = {
"d" = {
mode = "0750";
user = "root";
group = "wheel";
};
};
};
};
2025-02-14 13:31:18 -06:00
services.restic.backups =
let
# TODO: How do I set things up so that a compromised server doesn't have access to my backups so that it can corrupt or ransomware them?
defaults = {
passwordFile = config.sops.secrets.restic-rascal-passphrase.path;
paths = config.services.restic.commonPaths ++ [
2024-09-11 11:57:27 -05:00
];
2025-02-14 13:31:18 -06:00
initialize = true;
exclude = [ ];
timerConfig = {
OnCalendar = [
"04:45"
"17:45"
];
};
2024-09-11 11:57:27 -05:00
};
2025-02-14 13:31:18 -06:00
in
{
local = defaults // {
2024-09-11 11:57:27 -05:00
repository = "/storage/backups/local";
};
2025-02-14 13:31:18 -06:00
rascal = defaults // {
2024-09-11 11:57:27 -05:00
extraOptions = [
2024-09-17 08:56:54 -05:00
''sftp.command="ssh beefcake@rascal.hare-cod.ts.net -i ${config.sops.secrets.restic-rascal-ssh-private-key.path} -s sftp"''
2024-09-11 11:57:27 -05:00
];
2024-09-17 08:56:54 -05:00
repository = "sftp://beefcake@rascal.hare-cod.ts.net://storage/backups/beefcake";
2024-09-11 11:57:27 -05:00
};
2025-02-14 13:31:18 -06:00
# TODO: add ruby?
benland = defaults // {
2024-09-11 11:57:27 -05:00
extraOptions = [
2024-09-14 07:20:34 -05:00
''sftp.command="ssh daniel@n.benhaney.com -p 10022 -i ${config.sops.secrets.restic-ssh-priv-key-benland.path} -s sftp"''
2024-09-11 11:57:27 -05:00
];
repository = "sftp://daniel@n.benhaney.com://storage/backups/beefcake";
};
2025-02-14 13:31:18 -06:00
};
}
{
systemd.tmpfiles.settings = {
"10-caddy" = {
"/storage/files.lyte.dev" = {
"d" = {
mode = "2775";
user = "root";
group = "wheel";
};
};
};
};
2024-09-11 11:57:27 -05:00
services.restic.commonPaths = [
"/storage/files.lyte.dev"
];
services.caddy = {
# TODO: 502 and other error pages
enable = true;
email = "daniel@lyte.dev";
adapter = "caddyfile";
virtualHosts = {
"files.lyte.dev" = {
# TODO: customize the files.lyte.dev template?
extraConfig = ''
header {
Access-Control-Allow-Origin "{http.request.header.Origin}"
Access-Control-Allow-Credentials true
Access-Control-Allow-Methods *
Access-Control-Allow-Headers *
Vary Origin
defer
}
2024-09-12 11:58:24 -05:00
file_server browse {
2024-09-12 11:58:24 -05:00
## browse template
## hide .*
root /storage/files.lyte.dev
}
'';
};
};
# acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory";
};
networking.firewall.allowedTCPPorts = [
80
443
];
}
2025-02-14 13:31:18 -06:00
(
{ ... }:
let
theme = pkgs.fetchzip {
url = "https://github.com/catppuccin/gitea/releases/download/v1.0.1/catppuccin-gitea.tar.gz";
sha256 = "sha256-et5luA3SI7iOcEIQ3CVIu0+eiLs8C/8mOitYlWQa/uI=";
};
logos = {
png = pkgs.fetchurl {
url = "https://lyte.dev/icon.png";
sha256 = "sha256-o/iZDohzXBGbpJ2PR1z23IF4FZignTAK88QwrfgTwlk=";
};
svg = pkgs.fetchurl {
url = "https://lyte.dev/img/logo.svg";
sha256 = "sha256-G9leVXNanoaCizXJaXn++JzaVcYOgRc3dJKhTQsMhVs=";
};
svg-with-background = pkgs.fetchurl {
url = "https://lyte.dev/img/logo-with-background.svg";
sha256 = "sha256-CdMTRXoQ3AI76aHW/sTqvZo1q/0XQdnQs9V1vGmiffY=";
};
2024-12-14 09:03:57 -06:00
};
2025-02-14 13:31:18 -06:00
forgejoCustomCss = pkgs.writeText "iosevkalyte.css" ''
2024-12-14 09:03:57 -06:00
@font-face {
font-family: ldiosevka;
font-style: normal;
font-weight: 300;
src: local("Iosevka"), url("//lyte.dev/font/iosevkalytewebmin/iosevkalyteweb-regular.subset.woff2");
font-display: swap
}
@font-face {
font-family: ldiosevka;
font-style: italic;
font-weight: 300;
src: local("Iosevka"), url("//lyte.dev/font/iosevkalytewebmin/iosevkalyteweb-italic.subset.woff2");
font-display: swap
}
@font-face {
font-family: ldiosevka;
font-style: italic;
font-weight: 500;
src: local("Iosevka"), url("//lyte.dev/font/iosevkalytewebmin/iosevkalyteweb-bolditalic.woff2");
font-display: swap
}
:root {
--fonts-monospace: ldiosevka, ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace, var(--fonts-emoji);
}
'';
2025-02-14 13:31:18 -06:00
forgejoCustomHeaderTmpl = pkgs.writeText "header.tmpl" ''
2024-12-14 09:03:57 -06:00
<link rel="stylesheet" href="/assets/css/iosevkalyte.css" />
2024-12-27 08:42:32 -06:00
<script async="" defer="" data-domain="git.lyte.dev" src="https://a.lyte.dev/js/script.js"></script>
2024-12-14 09:03:57 -06:00
'';
2025-02-14 13:31:18 -06:00
forgejoCustomHomeTmpl = pkgs.writeText "home.tmpl" ''
2024-12-14 09:03:57 -06:00
{{template "base/head" .}}
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}" class="page-content home">
<div class="tw-mb-8 tw-px-8">
<div class="center">
<img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
<div class="hero">
<h1 class="ui icon header title">
{{AppDisplayName}}
</h1>
<h2>{{ctx.Locale.Tr "startpage.app_desc"}}</h2>
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-flame"}} {{ctx.Locale.Tr "startpage.install"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.install_desc" "https://forgejo.org/download/#installation-from-binary" "https://forgejo.org/download/#container-image" "https://forgejo.org/download"}}
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-device-desktop"}} {{ctx.Locale.Tr "startpage.platform"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.platform_desc"}}
</p>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-rocket"}} {{ctx.Locale.Tr "startpage.lightweight"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.lightweight_desc"}}
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
{{svg "octicon-code"}} {{ctx.Locale.Tr "startpage.license"}}
</h1>
<p class="large">
{{ctx.Locale.Tr "startpage.license_desc" "https://forgejo.org/download" "https://codeberg.org/forgejo/forgejo"}}
</p>
</div>
</div>
</div>
{{template "base/footer" .}}
'';
2025-02-14 13:31:18 -06:00
in
{
# systemd.tmpfiles.settings = {
# "10-forgejo" = {
# "/storage/forgejo" = {
# "d" = {
# mode = "0700";
# user = "forgejo";
# group = "nogroup";
# };
# };
# };
# };
services.forgejo = {
enable = true;
package = pkgs.unstable-packages.forgejo;
stateDir = "/storage/forgejo";
settings = {
DEFAULT = {
APP_NAME = "git.lyte.dev";
};
server = {
ROOT_URL = "https://git.lyte.dev";
HTTP_ADDR = "127.0.0.1";
HTTP_PORT = 3088;
DOMAIN = "git.lyte.dev";
};
migrations = {
ALLOWED_DOMAINS = "*.github.com,github.com,gitlab.com,*.gitlab.com";
};
actions = {
ENABLED = true;
};
service = {
DISABLE_REGISTRATION = true;
};
session = {
COOKIE_SECURE = true;
};
log = {
# LEVEL = "Debug";
};
ui = {
THEMES = "catppuccin-mocha-sapphire,forgejo-auto,forgejo-light,forgejo-dark";
DEFAULT_THEME = "catppuccin-mocha-sapphire";
};
indexer = {
REPO_INDEXER_ENABLED = "true";
REPO_INDEXER_PATH = "indexers/repos.bleve";
MAX_FILE_SIZE = "1048576";
# REPO_INDEXER_INCLUDE =
REPO_INDEXER_EXCLUDE = "resources/bin/**";
};
"markup.asciidoc" = {
ENABLED = true;
NEED_POSTPROCESS = true;
FILE_EXTENSIONS = ".adoc,.asciidoc";
RENDER_COMMAND = "${pkgs.asciidoctor}/bin/asciidoctor --embedded --safe-mode=secure --out-file=- -";
IS_INPUT_FILE = false;
};
2024-09-06 15:39:26 -05:00
};
2025-02-14 13:31:18 -06:00
lfs = {
enable = true;
2024-09-06 15:39:26 -05:00
};
2025-02-14 13:31:18 -06:00
dump = {
enable = false;
2024-09-06 15:39:26 -05:00
};
2025-02-14 13:31:18 -06:00
database = {
# TODO: move to postgres?
type = "sqlite3";
2024-12-13 10:48:50 -06:00
};
2024-09-06 15:39:26 -05:00
};
2025-02-14 13:31:18 -06:00
services.restic.commonPaths = [
config.services.forgejo.stateDir
];
sops.secrets = {
"forgejo-runner.env" = {
mode = "0400";
};
2024-09-06 15:39:26 -05:00
};
2025-02-14 13:31:18 -06:00
systemd.services.gitea-runner-beefcake.after = [ "sops-nix.service" ];
systemd.services.forgejo = {
preStart = lib.mkAfter ''
rm -rf ${config.services.forgejo.stateDir}/custom/public
mkdir -p ${config.services.forgejo.stateDir}/custom/public/
mkdir -p ${config.services.forgejo.stateDir}/custom/public/assets/
mkdir -p ${config.services.forgejo.stateDir}/custom/public/assets/img/
mkdir -p ${config.services.forgejo.stateDir}/custom/public/assets/css/
mkdir -p ${config.services.forgejo.stateDir}/custom/templates/custom/
ln -sf ${logos.png} ${config.services.forgejo.stateDir}/custom/public/assets/img/logo.png
ln -sf ${logos.svg} ${config.services.forgejo.stateDir}/custom/public/assets/img/logo.svg
ln -sf ${logos.png} ${config.services.forgejo.stateDir}/custom/public/assets/img/favicon.png
ln -sf ${logos.svg-with-background} ${config.services.forgejo.stateDir}/custom/public/assets/img/favicon.svg
ln -sf ${theme}/theme-catppuccin-mocha-sapphire.css ${config.services.forgejo.stateDir}/custom/public/assets/css/
ln -sf ${forgejoCustomCss} ${config.services.forgejo.stateDir}/custom/public/assets/css/iosevkalyte.css
ln -sf ${forgejoCustomHeaderTmpl} ${config.services.forgejo.stateDir}/custom/templates/custom/header.tmpl
ln -sf ${forgejoCustomHomeTmpl} ${config.services.forgejo.stateDir}/custom/templates/home.tmpl
'';
2024-09-06 15:39:26 -05:00
};
2024-12-13 20:48:40 -06:00
2025-02-14 13:31:18 -06:00
services.gitea-actions-runner = {
# TODO: simple git-based automation would be dope? maybe especially for
# mirroring to github super easy?
package = pkgs.forgejo-runner;
instances."beefcake" = {
enable = true;
name = "beefcake";
url = "https://git.lyte.dev";
settings = {
container = {
# use the shared network which is bridged by default
# this lets us hit git.lyte.dev just fine
network = "podman";
};
2024-09-06 15:39:26 -05:00
};
2025-02-14 13:31:18 -06:00
labels = [
# type ":host" does not depend on docker/podman/lxc
"podman"
"nix:docker://git.lyte.dev/lytedev/nix:latest"
"beefcake:host"
"nixos-host:host"
];
tokenFile = config.sops.secrets."forgejo-runner.env".path;
hostPackages = with pkgs; [
nix
bash
coreutils
curl
gawk
gitMinimal
gnused
nodejs
gnutar # needed for cache action
wget
];
2024-09-06 15:39:26 -05:00
};
};
2025-02-14 13:31:18 -06:00
# environment.systemPackages = with pkgs; [nodejs];
services.caddy.virtualHosts."git.lyte.dev" = {
extraConfig = ''
reverse_proxy :${toString config.services.forgejo.settings.server.HTTP_PORT}
'';
};
services.caddy.virtualHosts."http://git.beefcake.lan" = {
extraConfig = ''
reverse_proxy :${toString config.services.forgejo.settings.server.HTTP_PORT}
'';
};
}
)
2024-09-06 16:05:29 -05:00
{
2024-09-11 11:57:27 -05:00
services.restic.commonPaths = [
config.services.vaultwarden.backupDir
];
2024-09-06 16:05:29 -05:00
services.vaultwarden = {
enable = true;
2024-09-11 11:57:27 -05:00
backupDir = "/storage/vaultwarden/backups";
2024-09-06 16:05:29 -05:00
config = {
DOMAIN = "https://bw.lyte.dev";
SIGNUPS_ALLOWED = "false";
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 8222;
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
TODO: smtp setup?
right now, I think I configured this manually by temporarily setting ADMIN_TOKEN
and then configuring in https://bw.lyte.dev/admin
2024-09-12 11:58:24 -05:00
*/
2024-09-06 16:05:29 -05:00
};
};
services.caddy.virtualHosts."bw.lyte.dev" = {
extraConfig = ''reverse_proxy :${toString config.services.vaultwarden.config.ROCKET_PORT}'';
};
}
2024-09-06 16:44:15 -05:00
{
2024-09-11 16:04:32 -05:00
users.users.atuin = {
isSystemUser = true;
createHome = false;
group = "atuin";
};
users.extraGroups = {
2025-02-14 13:31:18 -06:00
"atuin" = { };
2024-09-11 16:04:32 -05:00
};
2024-09-06 16:44:15 -05:00
services.postgresql = {
2025-02-14 13:31:18 -06:00
ensureDatabases = [ "atuin" ];
2024-09-06 16:44:15 -05:00
ensureUsers = [
{
name = "atuin";
ensureDBOwnership = true;
}
];
};
services.atuin = {
enable = true;
database = {
createLocally = false;
2024-09-11 11:57:27 -05:00
# NOTE: this uses postgres over the unix domain socket by default
2024-09-06 16:57:30 -05:00
# uri = "postgresql://atuin@localhost:5432/atuin";
2024-09-06 16:44:15 -05:00
};
openRegistration = false;
2024-09-11 16:04:32 -05:00
# TODO: would be neat to have a way to "force" a registration on the server
};
systemd.services.atuin.serviceConfig = {
Group = "atuin";
User = "atuin";
2024-09-06 16:44:15 -05:00
};
services.caddy.virtualHosts."atuin.h.lyte.dev" = {
extraConfig = ''reverse_proxy :${toString config.services.atuin.port}'';
};
}
2024-09-12 11:58:24 -05:00
{
# jland minecraft server
/*
2025-02-14 13:31:18 -06:00
users.groups.jland = {
gid = 982;
};
users.users.jland = {
uid = 986;
isSystemUser = true;
createHome = false;
group = "jland";
};
virtualisation.oci-containers.containers.minecraft-jland = {
autoStart = false;
# sending commands: https://docker-minecraft-server.readthedocs.io/en/latest/commands/
image = "docker.io/itzg/minecraft-server";
# user = "${toString config.users.users.jland.uid}:${toString config.users.groups.jland.gid}";
extraOptions = [
"--tty"
"--interactive"
];
environment = {
EULA = "true";
## UID = toString config.users.users.jland.uid;
## GID = toString config.users.groups.jland.gid;
STOP_SERVER_ANNOUNCE_DELAY = "20";
TZ = "America/Chicago";
VERSION = "1.20.1";
MEMORY = "8G";
MAX_MEMORY = "16G";
TYPE = "FORGE";
FORGE_VERSION = "47.1.3";
ALLOW_FLIGHT = "true";
ENABLE_QUERY = "true";
MODPACK = "/data/origination-files/Server-Files-0.2.14.zip";
## TYPE = "AUTO_CURSEFORGE";
## CF_SLUG = "monumental-experience";
## CF_FILE_ID = "4826863"; # 2.2.53
## due to
## Nov 02 13:45:22 beefcake minecraft-jland[2738672]: me.itzg.helpers.errors.GenericException: The modpack authors have indicated this file is not allowed for project distribution. Please download the client zip file from https://www.curseforge.com/minecraft/modpacks/monumental-experience and pass via CF_MODPACK_ZIP environment variable or place indownloads repo directory.
## we must upload manually
## CF_MODPACK_ZIP = "/data/origination-files/Monumental+Experience-2.2.53.zip";
## ENABLE_AUTOPAUSE = "true"; # TODO: must increate or disable max-tick-time
## May also have mod/loader incompatibilities?
## https://docker-minecraft-server.readthedocs.io/en/latest/misc/autopause-autostop/autopause/
};
environmentFiles = [
# config.sops.secrets."jland.env".path
];
ports = ["26965:25565"];
volumes = [
"/storage/jland/data:/data"
"/storage/jland/worlds:/worlds"
];
};
networking.firewall.allowedTCPPorts = [
26965
];
}
{
# dawncraft minecraft server
systemd.tmpfiles.rules = [
"d /storage/dawncraft/ 0770 1000 1000 -"
"d /storage/dawncraft/data/ 0770 1000 1000 -"
"d /storage/dawncraft/worlds/ 0770 1000 1000 -"
"d /storage/dawncraft/downloads/ 0770 1000 1000 -"
];
virtualisation.oci-containers.containers.minecraft-dawncraft = {
autoStart = false;
# sending commands: https://docker-minecraft-server.readthedocs.io/en/latest/commands/
image = "docker.io/itzg/minecraft-server";
extraOptions = [
"--tty"
"--interactive"
];
environment = {
EULA = "true";
STOP_SERVER_ANNOUNCE_DELAY = "20";
TZ = "America/Chicago";
VERSION = "1.18.2";
MEMORY = "8G";
MAX_MEMORY = "32G";
ALLOW_FLIGHT = "true";
ENABLE_QUERY = "true";
SERVER_PORT = "26968";
QUERY_PORT = "26968";
TYPE = "AUTO_CURSEFORGE";
CF_SLUG = "dawn-craft";
CF_EXCLUDE_MODS = "368398";
CF_FORCE_SYNCHRONIZE = "true";
# CF_FILE_ID = "5247696"; # 2.0.7 server
};
environmentFiles = [
config.sops.secrets."dawncraft.env".path
];
ports = ["26968:26968/tcp" "26968:26968/udp"];
volumes = [
"/storage/dawncraft/data:/data"
"/storage/dawncraft/worlds:/worlds"
"/storage/dawncraft/downloads:/downloads"
];
};
networking.firewall.allowedTCPPorts = [
26968
];
*/
}
(
{ ... }:
let
port = 26969;
dir = "/storage/flanilla";
user = "flanilla";
in
# uid = config.users.users.flanilla.uid;
# gid = config.users.groups.flanilla.gid;
{
# flanilla family minecraft server
users.groups.${user} = { };
users.users.${user} = {
2024-09-12 11:58:24 -05:00
isSystemUser = true;
createHome = false;
2025-02-14 13:31:18 -06:00
home = dir;
group = user;
2024-09-12 11:58:24 -05:00
};
2025-02-14 13:31:18 -06:00
virtualisation.oci-containers.containers.minecraft-flanilla = {
2024-09-12 11:58:24 -05:00
autoStart = false;
2025-02-14 13:31:18 -06:00
environmentFiles = [
# config.sops.secrets."jland.env".path
];
2024-09-12 11:58:24 -05:00
image = "docker.io/itzg/minecraft-server";
2025-02-14 13:31:18 -06:00
# user = "${toString uid}:${toString gid}";
2024-09-12 11:58:24 -05:00
extraOptions = [
"--tty"
"--interactive"
];
environment = {
EULA = "true";
2025-02-14 13:31:18 -06:00
MOTD = "Flanilla Survival! Happy hunting!";
# UID = toString uid;
# GID = toString gid;
2024-09-12 11:58:24 -05:00
STOP_SERVER_ANNOUNCE_DELAY = "20";
TZ = "America/Chicago";
2025-02-14 13:31:18 -06:00
VERSION = "1.21";
OPS = "lytedev";
MODE = "survival";
DIFFICULTY = "easy";
ONLINE_MODE = "false";
2024-09-12 11:58:24 -05:00
MEMORY = "8G";
MAX_MEMORY = "16G";
ALLOW_FLIGHT = "true";
ENABLE_QUERY = "true";
2025-02-14 13:31:18 -06:00
ENABLE_COMMAND_BLOCK = "true";
2024-09-12 11:58:24 -05:00
};
2025-02-14 13:31:18 -06:00
ports = [ "${toString port}:25565" ];
2024-09-12 11:58:24 -05:00
volumes = [
2025-02-14 13:31:18 -06:00
"${dir}/data:/data"
"${dir}/worlds:/worlds"
2024-09-12 11:58:24 -05:00
];
};
2025-02-14 13:31:18 -06:00
systemd.services.podman-minecraft-flanilla.serviceConfig = {
User = user;
Group = user;
};
systemd.tmpfiles.settings = {
"10-${user}-survival" = {
"${dir}/data" = {
"d" = {
mode = "0770";
user = user;
group = user;
};
};
"${dir}/worlds" = {
"d" = {
mode = "0770";
user = user;
group = user;
};
};
};
};
services.restic.commonPaths = [ dir ];
2024-09-12 11:58:24 -05:00
networking.firewall.allowedTCPPorts = [
2025-02-14 13:31:18 -06:00
port
2024-09-12 11:58:24 -05:00
];
}
2025-02-14 13:31:18 -06:00
)
(
{ ... }:
let
port = 26968;
dir = "/storage/flanilla-creative";
user = "flanilla";
in
# uid = config.users.users.flanilla.uid;
# gid = config.users.groups.flanilla.gid;
2024-09-12 11:58:24 -05:00
{
2025-02-14 13:31:18 -06:00
# flanilla family minecraft server
users.groups.${user} = { };
users.users.${user} = {
isSystemUser = true;
createHome = false;
home = lib.mkForce dir;
group = user;
};
virtualisation.oci-containers.containers.minecraft-flanilla-creative = {
autoStart = false;
2024-09-12 11:58:24 -05:00
image = "docker.io/itzg/minecraft-server";
2025-02-14 13:31:18 -06:00
# user = "${toString uid}:${toString gid}";
2024-09-12 11:58:24 -05:00
extraOptions = [
"--tty"
"--interactive"
];
environment = {
EULA = "true";
2025-02-14 13:31:18 -06:00
MOTD = "Flanilla Creative! Have fun building!";
# UID = toString uid;
# GID = toString gid;
2024-09-12 11:58:24 -05:00
STOP_SERVER_ANNOUNCE_DELAY = "20";
TZ = "America/Chicago";
2025-02-14 13:31:18 -06:00
VERSION = "1.21";
OPS = "lytedev";
MODE = "creative";
DIFFICULTY = "peaceful";
ONLINE_MODE = "false";
2024-09-12 11:58:24 -05:00
MEMORY = "8G";
2025-02-14 13:31:18 -06:00
MAX_MEMORY = "16G";
2024-09-12 11:58:24 -05:00
ALLOW_FLIGHT = "true";
ENABLE_QUERY = "true";
2025-02-14 13:31:18 -06:00
ENABLE_COMMAND_BLOCK = "true";
2024-09-12 11:58:24 -05:00
};
2025-02-14 13:31:18 -06:00
ports = [ "${toString port}:25565" ];
2024-09-12 11:58:24 -05:00
volumes = [
2025-02-14 13:31:18 -06:00
"${dir}/data:/data"
"${dir}/worlds:/worlds"
2024-09-12 11:58:24 -05:00
];
};
2025-02-14 13:31:18 -06:00
# systemd.services.podman-minecraft-flanilla-creative.serviceConfig = {
# User = user;
# Group = user;
# };
systemd.tmpfiles.settings = {
"10-${user}-creative" = {
"${dir}/data" = {
"d" = {
mode = "0770";
user = user;
group = user;
};
2024-10-03 09:29:26 -05:00
};
2025-02-14 13:31:18 -06:00
"${dir}/worlds" = {
"d" = {
mode = "0770";
user = user;
group = user;
};
2024-10-03 09:23:44 -05:00
};
};
};
2025-02-14 13:31:18 -06:00
services.restic.commonPaths = [ dir ];
networking.firewall.allowedTCPPorts = [
port
2024-09-12 11:58:24 -05:00
];
2025-02-14 13:31:18 -06:00
}
)
(
{
config,
options,
...
}:
let
domain = "idm.h.lyte.dev";
name = "kanidm";
user = name;
group = name;
storage = "/storage/${name}";
in
{
# kanidm
config = {
# reload certs from caddy every 5 minutes
# TODO: ideally some kind of file watcher service would make way more sense here?
# or we could simply setup the permissions properly somehow?
systemd.timers."copy-kanidm-certificates-from-caddy" = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnBootSec = "10m"; # 10 minutes after booting
OnUnitActiveSec = "5m"; # every 5 minutes afterwards
Unit = "copy-kanidm-certificates-from-caddy.service";
2024-10-03 09:29:26 -05:00
};
};
2025-02-14 13:31:18 -06:00
systemd.services."copy-kanidm-certificates-from-caddy" = {
# get the certificates that caddy provisions for us
script = ''
umask 077
# this line should be unnecessary now that we have this in tmpfiles
install -d -m 0700 -o "${name}" -g "${name}" "${storage}/data" "${storage}/certs"
cd /var/lib/caddy/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/idm.h.lyte.dev
install -m 0700 -o "${name}" -g "${name}" idm.h.lyte.dev.key idm.h.lyte.dev.crt "${storage}/certs"
'';
path = with pkgs; [ rsync ];
serviceConfig = {
Type = "oneshot";
User = "root";
2024-10-03 09:23:44 -05:00
};
};
2024-09-12 11:58:24 -05:00
2025-02-14 13:31:18 -06:00
systemd.tmpfiles.settings."10-kanidm" = {
"${config.services.kanidm.serverSettings.online_backup.path}".d = {
user = name;
group = name;
mode = "0700";
};
"${storage}/data".d = {
inherit user group;
mode = "0700";
};
"${storage}/certs".d = {
inherit user group;
mode = "0700";
};
2024-09-12 11:58:24 -05:00
};
2025-02-14 13:31:18 -06:00
services.kanidm = {
package = pkgs.unstable-packages.kanidm;
2025-02-14 13:31:18 -06:00
enableServer = true;
serverSettings = {
inherit domain;
origin = "https://${domain}";
bindaddress = "127.0.0.1:8443";
tls_chain = "${storage}/certs/idm.h.lyte.dev.crt";
tls_key = "${storage}/certs/idm.h.lyte.dev.key";
log_level = "info";
online_backup = {
path = "${storage}/backups/";
schedule = "00 22 * * *";
versions = 50;
};
};
2024-09-12 11:58:24 -05:00
2025-02-14 13:31:18 -06:00
enablePam = false;
unixSettings = {
# pam_allowed_login_groups = [];
2025-02-11 00:41:52 -06:00
};
2024-09-12 11:58:24 -05:00
2025-02-14 13:31:18 -06:00
enableClient = true;
clientSettings = {
uri = "https://idm.h.lyte.dev";
};
2024-09-12 11:58:24 -05:00
2025-02-14 13:31:18 -06:00
provision = {
# enable = true;
# instanceUrl = "https://${domain}";
# adminPasswordFile = config.sops.secrets.kanidm-admin-password-file.path
# idmAdminPasswordFile = config.sops.secrets.kanidm-admin-password-file.path
# autoRemove = true;
# groups = {
# myGroup = {
# members = ["myUser" /* ...*/];
# }
# };
# persons = {
# myUser = {
# displayName = "display name";
# legalName = "My User";
# mailAddresses = ["myuser@example.com"];
# groups = ["myGroup"];
# }
# };
# systems = {
# oauth2 = {
# mySystem = {
# enableLegacyCrypto = false;
# enableLocalhostRedirects = true; # only for public
# allowInsecureClientDisablePkce = false;
# basicSecretFile = config.sops.secrets.basic-secret-file...
# claimMap = {};
# };
# };
# };
};
2025-02-11 00:41:52 -06:00
};
2024-09-12 11:58:24 -05:00
2025-02-14 13:31:18 -06:00
services.caddy.virtualHosts."idm.h.lyte.dev" = {
extraConfig = ''reverse_proxy https://idm.h.lyte.dev:8443'';
2024-09-12 11:58:24 -05:00
};
2025-02-14 13:31:18 -06:00
networking = {
extraHosts = ''
::1 idm.h.lyte.dev
127.0.0.1 idm.h.lyte.dev
'';
};
2024-09-12 11:58:24 -05:00
};
2025-02-14 13:31:18 -06:00
}
)
2024-09-06 16:48:59 -05:00
{
systemd.tmpfiles.settings = {
"10-audiobookshelf" = {
"/storage/audiobookshelf" = {
"d" = {
mode = "0770";
user = "audiobookshelf";
group = "wheel";
};
};
"/storage/audiobookshelf/audiobooks" = {
"d" = {
mode = "0770";
user = "audiobookshelf";
group = "wheel";
};
};
"/storage/audiobookshelf/podcasts" = {
"d" = {
mode = "0770";
user = "audiobookshelf";
group = "wheel";
};
};
};
};
2025-02-14 13:31:18 -06:00
users.groups.audiobookshelf = { };
users.users.audiobookshelf = {
isSystemUser = true;
group = "audiobookshelf";
};
2024-09-06 16:48:59 -05:00
services.audiobookshelf = {
enable = true;
dataDir = "/storage/audiobookshelf";
port = 8523;
};
systemd.services.audiobookshelf.serviceConfig = {
WorkingDirectory = lib.mkForce config.services.audiobookshelf.dataDir;
StateDirectory = lib.mkForce config.services.audiobookshelf.dataDir;
Group = "audiobookshelf";
User = "audiobookshelf";
};
2024-09-06 16:48:59 -05:00
services.caddy.virtualHosts."audio.lyte.dev" = {
extraConfig = ''reverse_proxy :${toString config.services.audiobookshelf.port}'';
2024-09-06 16:48:59 -05:00
};
}
2024-09-11 14:31:48 -05:00
{
# prometheus
services.restic.commonPaths = [
# TODO: do I want this backed up?
# "/var/lib/prometheus"
];
services.prometheus = {
enable = true;
checkConfig = true;
listenAddress = "127.0.0.1";
port = 9090;
2024-09-11 15:28:52 -05:00
scrapeConfigs = [
{
job_name = "beefcake";
static_configs = [
{
2025-02-14 13:31:18 -06:00
targets =
let
inherit (config.services.prometheus.exporters.node) port listenAddress;
in
[ "${listenAddress}:${toString port}" ];
2024-09-11 15:28:52 -05:00
}
2024-09-11 16:04:32 -05:00
{
2025-02-14 13:31:18 -06:00
targets =
let
inherit (config.services.prometheus.exporters.zfs) port listenAddress;
in
[ "${listenAddress}:${toString port}" ];
2024-09-11 16:04:32 -05:00
}
{
2025-02-14 13:31:18 -06:00
targets =
let
inherit (config.services.prometheus.exporters.postgres) port listenAddress;
in
[ "${listenAddress}:${toString port}" ];
2024-09-11 16:04:32 -05:00
}
2024-09-11 15:28:52 -05:00
];
}
];
2024-09-11 14:31:48 -05:00
exporters = {
postgres = {
enable = true;
2024-09-11 16:04:32 -05:00
listenAddress = "127.0.0.1";
runAsLocalSuperUser = true;
2024-09-11 14:31:48 -05:00
};
2024-09-11 15:28:52 -05:00
node = {
enable = true;
listenAddress = "127.0.0.1";
enabledCollectors = [
"systemd"
];
};
zfs = {
enable = true;
2024-09-11 16:04:32 -05:00
listenAddress = "127.0.0.1";
2024-09-11 15:28:52 -05:00
};
2024-09-11 14:31:48 -05:00
};
};
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
TODO: promtail?
idrac exporter?
restic exporter?
smartctl exporter?
systemd exporter?
NOTE: we probably don't want this exposed
services.caddy.virtualHosts."prometheus.h.lyte.dev" = {
extraConfig = ''reverse_proxy :${toString config.services.prometheus.port}'';
};
2024-09-12 11:58:24 -05:00
*/
2024-09-11 14:31:48 -05:00
}
{
# grafana
systemd.tmpfiles.settings = {
"10-grafana" = {
"/storage/grafana" = {
"d" = {
mode = "0750";
2024-09-13 00:50:31 -05:00
user = "grafana";
group = "grafana";
2024-09-11 14:31:48 -05:00
};
};
};
};
services.restic.commonPaths = [
2024-09-11 14:58:17 -05:00
"/storage/grafana"
2024-09-11 14:31:48 -05:00
];
2024-09-11 14:58:17 -05:00
sops.secrets = {
grafana-admin-password = {
owner = "grafana";
group = "grafana";
mode = "0400";
};
2024-09-11 15:28:52 -05:00
grafana-smtp-password = {
owner = "grafana";
group = "grafana";
mode = "0400";
};
2024-09-11 14:58:17 -05:00
};
2024-09-11 14:31:48 -05:00
services.grafana = {
enable = true;
dataDir = "/storage/grafana";
provision = {
enable = true;
2024-09-11 15:28:52 -05:00
datasources = {
settings = {
datasources = [
{
name = "Prometheus";
type = "prometheus";
access = "proxy";
url = "http://localhost:${toString config.services.prometheus.port}";
isDefault = true;
}
];
};
};
2024-09-11 14:31:48 -05:00
};
settings = {
server = {
http_port = 3814;
2024-09-13 13:25:34 -05:00
root_url = "https://grafana.h.lyte.dev";
2024-09-11 14:31:48 -05:00
};
2024-09-11 15:28:52 -05:00
smtp = {
enabled = true;
from_address = "grafana@lyte.dev";
2024-09-11 15:29:58 -05:00
user = "grafana@lyte.dev";
host = "smtp.mailgun.org:587";
2024-09-11 15:28:52 -05:00
password = ''$__file{${config.sops.secrets.grafana-smtp-password.path}}'';
};
2024-09-11 14:58:17 -05:00
security = {
admin_email = "daniel@lyte.dev";
admin_user = "lytedev";
admin_file = ''$__file{${config.sops.secrets.grafana-admin-password.path}}'';
};
# database = {
# };
2024-09-11 14:31:48 -05:00
};
};
networking.firewall.allowedTCPPorts = [
9000
];
services.caddy.virtualHosts."grafana.h.lyte.dev" = {
extraConfig = ''reverse_proxy :${toString config.services.grafana.settings.server.http_port}'';
};
}
2024-09-12 23:45:03 -05:00
{
2024-09-13 00:38:04 -05:00
systemd.tmpfiles.settings = {
"10-paperless" = {
"/storage/paperless" = {
"d" = {
mode = "0750";
user = "paperless";
group = "paperless";
};
};
};
};
services.restic.commonPaths = [
"/storage/paperless"
];
sops.secrets.paperless-superuser-password = {
owner = "paperless";
group = "paperless";
mode = "400";
};
services.paperless = {
enable = true;
2024-12-02 16:05:05 -06:00
# package = pkgs.paperless-ngx;
2024-09-13 00:38:04 -05:00
dataDir = "/storage/paperless";
passwordFile = config.sops.secrets.paperless-superuser-password.path;
};
services.caddy.virtualHosts."paperless.h.lyte.dev" = {
extraConfig = ''reverse_proxy :${toString config.services.paperless.port}'';
};
2024-09-12 23:45:03 -05:00
}
{
systemd.tmpfiles.settings = {
"10-actual" = {
"/storage/actual" = {
"d" = {
mode = "0750";
user = "root";
group = "family";
};
};
};
};
services.restic.commonPaths = [
2024-09-13 00:02:57 -05:00
"/storage/actual"
2024-09-12 23:45:03 -05:00
];
virtualisation.oci-containers = {
containers.actual = {
2025-02-10 22:44:27 -06:00
image = "ghcr.io/actualbudget/actual-server:25.2.1";
2024-09-12 23:45:03 -05:00
autoStart = true;
2025-02-14 13:31:18 -06:00
ports = [ "5006:5006" ];
volumes = [ "/storage/actual:/data" ];
2024-09-12 23:45:03 -05:00
};
};
services.caddy.virtualHosts."finances.h.lyte.dev" = {
extraConfig = ''reverse_proxy :5006'';
};
}
{
services.factorio = {
enable = false;
package = pkgs.factorio-headless.override {
versionsJson = ./factorio-versions.json;
};
2025-02-14 13:31:18 -06:00
admins = [ "lytedev" ];
autosave-interval = 5;
game-name = "Flanwheel Online";
description = "Space Age 2.0";
openFirewall = true;
lan = true;
# public = true; # NOTE: cannot be true if requireUserVerification is false
port = 34197;
requireUserVerification = false; # critical for DRM-free users
# contains the game password and account password for "public" servers
extraSettingsFile = config.sops.secrets.factorio-server-settings.path;
};
sops.secrets = {
2025-02-14 13:31:18 -06:00
factorio-server-settings = {
mode = "0777";
};
};
}
(
{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.conduwuit;
defaultUser = "conduwuit";
defaultGroup = "conduwuit";
format = pkgs.formats.toml { };
configFile = format.generate "conduwuit.toml" cfg.settings;
in
{
meta.maintainers = with lib.maintainers; [ niklaskorz ];
options.services.conduwuit = {
enable = lib.mkEnableOption "conduwuit";
user = lib.mkOption {
type = lib.types.nonEmptyStr;
description = ''
The user {command}`conduwuit` is run as.
'';
default = defaultUser;
};
group = lib.mkOption {
type = lib.types.nonEmptyStr;
description = ''
The group {command}`conduwuit` is run as.
'';
default = defaultGroup;
};
extraEnvironment = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
description = "Extra Environment variables to pass to the conduwuit server.";
default = { };
example = {
RUST_BACKTRACE = "yes";
};
};
package = lib.mkPackageOption pkgs.unstable-packages "conduwuit" { };
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = format.type;
options = {
global.server_name = lib.mkOption {
type = lib.types.nonEmptyStr;
example = "example.com";
description = "The server_name is the name of this server. It is used as a suffix for user and room ids.";
};
global.address = lib.mkOption {
type = lib.types.nullOr (lib.types.listOf lib.types.nonEmptyStr);
default = null;
example = [
"127.0.0.1"
"::1"
];
description = ''
Addresses (IPv4 or IPv6) to listen on for connections by the reverse proxy/tls terminator.
If set to `null`, conduwuit will listen on IPv4 and IPv6 localhost.
Must be `null` if `unix_socket_path` is set.
'';
};
global.port = lib.mkOption {
type = lib.types.listOf lib.types.port;
default = [ 6167 ];
description = ''
The port(s) conduwuit will be running on.
You need to set up a reverse proxy in your web server (e.g. apache or nginx),
so all requests to /_matrix on port 443 and 8448 will be forwarded to the conduwuit
instance running on this port.
'';
};
global.unix_socket_path = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Listen on a UNIX socket at the specified path. If listening on a UNIX socket,
listening on an address will be disabled. The `address` option must be set to
`null` (the default value). The option {option}`services.conduwuit.group` must
be set to a group your reverse proxy is part of.
This will automatically add a system user "conduwuit" to your system if
{option}`services.conduwuit.user` is left at the default, and a "conduwuit"
group if {option}`services.conduwuit.group` is left at the default.
'';
};
global.unix_socket_perms = lib.mkOption {
type = lib.types.ints.positive;
default = 660;
description = "The default permissions (in octal) to create the UNIX socket with.";
};
global.max_request_size = lib.mkOption {
type = lib.types.ints.positive;
default = 20000000;
description = "Max request size in bytes. Don't forget to also change it in the proxy.";
};
global.allow_registration = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
Whether new users can register on this server.
Registration with token requires `registration_token` or `registration_token_file` to be set.
If set to true without a token configured, and
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
is set to true, users can freely register.
'';
};
global.allow_encryption = lib.mkOption {
type = lib.types.bool;
default = true;
description = "Whether new encrypted rooms can be created. Note: existing rooms will continue to work.";
};
global.allow_federation = lib.mkOption {
type = lib.types.bool;
default = true;
description = ''
Whether this server federates with other servers.
'';
};
global.trusted_servers = lib.mkOption {
type = lib.types.listOf lib.types.nonEmptyStr;
default = [ "matrix.org" ];
description = ''
Servers listed here will be used to gather public keys of other servers
(notary trusted key servers).
Currently, conduwuit doesn't support inbound batched key requests, so
this list should only contain other Synapse servers.
Example: `[ "matrix.org" "constellatory.net" "tchncs.de" ]`
'';
};
global.database_path = lib.mkOption {
readOnly = true;
type = lib.types.path;
default = "/var/lib/conduwuit/";
description = ''
Path to the conduwuit database, the directory where conduwuit will save its data.
Note that database_path cannot be edited because of the service's reliance on systemd StateDir.
'';
};
global.allow_check_for_updates = lib.mkOption {
type = lib.types.bool;
default = false;
description = ''
If enabled, conduwuit will send a simple GET request periodically to
<https://pupbrain.dev/check-for-updates/stable> for any new announcements made.
Despite the name, this is not an update check endpoint, it is simply an announcement check endpoint.
Disabled by default.
'';
};
};
};
default = { };
# TOML does not allow null values, so we use null to omit those fields
apply = lib.filterAttrsRecursive (_: v: v != null);
description = ''
Generates the conduwuit.toml configuration file. Refer to
<https://conduwuit.puppyirl.gay/configuration.html>
for details on supported values.
'';
};
};
config = lib.mkIf cfg.enable {
assertions = [
{
assertion = !(cfg.settings ? global.unix_socket_path) || !(cfg.settings ? global.address);
message = ''
In `services.conduwuit.settings.global`, `unix_socket_path` and `address` cannot be set at the
same time.
Leave one of the two options unset or explicitly set them to `null`.
'';
}
{
assertion = cfg.user != defaultUser -> config ? users.users.${cfg.user};
message = "If `services.conduwuit.user` is changed, the configured user must already exist.";
}
{
assertion = cfg.group != defaultGroup -> config ? users.groups.${cfg.group};
message = "If `services.conduwuit.group` is changed, the configured group must already exist.";
}
];
users.users = lib.mkIf (cfg.user == defaultUser) {
${defaultUser} = {
group = cfg.group;
home = cfg.settings.global.database_path;
isSystemUser = true;
};
};
users.groups = lib.mkIf (cfg.group == defaultGroup) {
${defaultGroup} = { };
};
systemd.services.conduwuit = {
description = "Conduwuit Matrix Server";
documentation = [ "https://conduwuit.puppyirl.gay/" ];
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [ "network-online.target" ];
environment = lib.mkMerge [
{ CONDUWUIT_CONFIG = configFile; }
cfg.extraEnvironment
];
startLimitBurst = 5;
startLimitIntervalSec = 60;
serviceConfig = {
DynamicUser = true;
User = cfg.user;
Group = cfg.group;
DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
PrivateDevices = true;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = true;
PrivateIPC = true;
RemoveIPC = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
SystemCallArchitectures = "native";
SystemCallFilter = [
"@system-service"
"@resources"
"~@clock"
"@debug"
"@module"
"@mount"
"@reboot"
"@swap"
"@cpu-emulation"
"@obsolete"
"@timer"
"@chown"
"@setuid"
"@privileged"
"@keyring"
"@ipc"
];
SystemCallErrorNumber = "EPERM";
StateDirectory = "conduwuit";
StateDirectoryMode = "0700";
RuntimeDirectory = "conduwuit";
RuntimeDirectoryMode = "0750";
ExecStart = lib.getExe cfg.package;
Restart = "on-failure";
RestartSec = 10;
};
};
};
}
)
2025-02-14 13:31:18 -06:00
(
{
pkgs,
config,
...
}:
let
port = builtins.head config.services.conduwuit.settings.global.port;
sPort = toString port;
in
{
sops.secrets.matrix-registration-token-file.mode = "0400";
services.conduwuit = {
enable = true;
settings = {
global = {
allow_check_for_updates = true;
allow_federation = false;
registration_token_file = config.sops.secrets.matrix-registration-token-file.path;
server_name = "lyte.dev";
};
2025-02-11 01:27:43 -06:00
};
};
2025-02-14 13:31:18 -06:00
services.caddy.virtualHosts."matrix.lyte.dev".extraConfig = ''
reverse_proxy /_matrix/* :${sPort}
reverse_proxy /_synapse/client/* :${sPort}
'';
services.caddy.virtualHosts."lyte.dev:8448".extraConfig = ''
reverse_proxy /_matrix/* :${sPort}
'';
# TODO: backups
# TODO: reverse proxy
}
)
2024-03-13 21:12:14 -05:00
];
2023-09-04 11:40:30 -05:00
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
TODO: non-root processes and services that access secrets need to be part of
the 'keys' group
2023-09-04 11:40:30 -05:00
2025-02-14 13:31:18 -06:00
systemd.services.some-service = {
serviceConfig.SupplementaryGroups = [ config.users.groups.keys.name ];
};
or
users.users.example-user.extraGroups = [ config.users.groups.keys.name ];
2023-09-04 11:40:30 -05:00
2025-02-14 13:31:18 -06:00
TODO: declarative directory quotas? for storage/$USER and /home/$USER
2024-09-12 11:58:24 -05:00
*/
2023-09-04 11:40:30 -05:00
2024-09-12 11:58:24 -05:00
/*
2025-02-14 13:31:18 -06:00
# https://github.com/NixOS/nixpkgs/blob/04af42f3b31dba0ef742d254456dc4c14eedac86/nixos/modules/services/misc/lidarr.nix#L72
services.lidarr = {
enable = true;
dataDir = "/storage/lidarr";
};
services.radarr = {
enable = true;
dataDir = "/storage/radarr";
};
services.sonarr = {
enable = true;
dataDir = "/storage/sonarr";
};
services.bazarr = {
enable = true;
listenPort = 6767;
};
networking.firewall.allowedTCPPorts = [9876 9877];
networking.firewall.allowedUDPPorts = [9876 9877];
networking.firewall.allowedUDPPortRanges = [
{
from = 27000;
to = 27100;
}
];
2024-09-12 11:58:24 -05:00
*/
2023-09-04 11:40:30 -05:00
}