/*
if ur fans get loud:
# enable manual fan control
sudo nix run nixpkgs#ipmitool -- raw 0x30 0x30 0x01 0x00
# set fan speed to last byte as decimal
sudo nix run nixpkgs#ipmitool -- raw 0x30 0x30 0x02 0xff 0x00
*/
{
# inputs,
# outputs,
lib,
config,
pkgs,
...
}: {
system.stateVersion = "24.05";
home-manager.users.daniel.home.stateVersion = "24.05";
networking.hostName = "beefcake";
imports = [
{
# hardware
boot = {
initrd.availableKernelModules = ["ehci_pci" "mpt3sas" "usbhid" "sd_mod"];
kernelModules = ["kvm-intel"];
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
};
fileSystems."/" = {
device = "/dev/disk/by-uuid/992ce55c-7507-4d6b-938c-45b7e891f395";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/B6C4-7CF4";
fsType = "vfat";
options = ["fmask=0022" "dmask=0022"];
};
# fileSystems."/storage" = {
# device = "/dev/disk/by-uuid/ea8258d7-54d1-430e-93b3-e15d33231063";
# fsType = "btrfs";
# options = [
# "compress=zstd:5"
# "space_cache=v2"
# ];
# };
}
{
boot.kernelParams = ["nohibernate"];
}
{
# sops secrets stuff
sops = {
defaultSopsFile = ../secrets/beefcake/secrets.yml;
age = {
sshKeyPaths = ["/etc/ssh/ssh_host_ed25519_key"];
keyFile = "/var/lib/sops-nix/key.txt";
generateKey = true;
};
secrets = {
# example-key = {
# # see these and other options' documentation here:
# # https://github.com/Mic92/sops-nix#set-secret-permissionowner-and-allow-services-to-access-it
# # set permissions:
# # mode = "0440";
# # owner = config.users.users.nobody.name;
# # group = config.users.users.nobody.group;
# # restart service when a secret changes or is newly initialized
# # restartUnits = [ "home-assistant.service" ];
# # symlink to certain directories
# path = "/var/lib/my-example-key/secrets.yaml";
# # for use as a user password
# # neededForUsers = true;
# };
# subdirectory
# "myservice/my_subdir/my_secret" = { };
# "jland.env" = {
# path = "/var/lib/jland/jland.env";
# # TODO: would be cool to assert that it's correctly-formatted JSON? probably should be done in a pre-commit hook?
# mode = "0440";
# owner = config.users.users.daniel.name;
# group = config.users.groups.daniel.name;
# };
# "dawncraft.env" = {
# path = "/var/lib/dawncraft/dawncraft.env";
# # TODO: would be cool to assert that it's correctly-formatted JSON? probably should be done in a pre-commit hook?
# mode = "0440";
# owner = config.users.users.daniel.name;
# group = config.users.groups.daniel.name;
# };
# plausible-admin-password = {
# # TODO: path = "${config.systemd.services.plausible.serviceConfig.WorkingDirectory}/plausible-admin-password.txt";
# path = "/var/lib/plausible/plausible-admin-password";
# mode = "0440";
# owner = config.systemd.services.plausible.serviceConfig.User;
# group = config.systemd.services.plausible.serviceConfig.Group;
# };
# plausible-secret-key-base = {
# path = "/var/lib/plausible/plausible-secret-key-base";
# mode = "0440";
# owner = config.systemd.services.plausible.serviceConfig.User;
# group = config.systemd.services.plausible.serviceConfig.Group;
# };
# nextcloud-admin-password.path = "/var/lib/nextcloud/admin-password";
"forgejo-runner.env" = {mode = "0400";};
restic-rascal-passphrase = {
mode = "0400";
};
restic-rascal-ssh-private-key = {
mode = "0400";
};
};
};
systemd.services.gitea-runner-beefcake.after = ["sops-nix.service"];
}
{
# nix binary cache
# TODO: move /nix to a big drive?
services.nix-serve = {
enable = false; # TODO: true
secretKeyFile = "/var/cache-priv-key.pem";
};
services.caddy.virtualHosts."nix.h.lyte.dev" = {
extraConfig = ''
reverse_proxy :${toString config.services.nix-serve.port}
'';
};
networking.firewall.allowedTCPPorts = [
80
443
];
# regularly build this flake so we have stuff in the cache
# TODO: schedule this for nightly builds instead of intervals based on boot time
# systemd.timers."build-lytedev-flake" = {
# wantedBy = ["timers.target"];
# timerConfig = {
# OnBootSec = "30m"; # 30 minutes after booting
# OnUnitActiveSec = "1d"; # every day afterwards
# Unit = "build-lytedev-flake.service";
# };
# };
# systemd.services."build-lytedev-flake" = {
# 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
# '';
# path = with pkgs; [openssh git nixos-rebuild];
# serviceConfig = {
# # TODO: mkdir -p...?
# WorkingDirectory = "/home/daniel/.home/nightly-flake-builds";
# Type = "oneshot";
# User = "daniel"; # might have to run as me for git ssh access to the repo
# };
# };
networking = {
extraHosts = ''
::1 nix.h.lyte.dev
127.0.0.1 nix.h.lyte.dev
'';
};
}
# {
# services.headscale = {
# enable = true;
# 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";
# };
# 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" = {
# extraConfig = ''
# reverse_proxy http://localhost:${toString config.services.headscale.port}
# '';
# };
# networking.firewall.allowedUDPPorts = [3478];
# }
{
services.soju = {
enable = true;
listen = ["irc+insecure://:6667"];
};
networking.firewall.allowedTCPPorts = [
6667
];
}
# {
# # samba
# users.users.guest = {
# # used for anonymous samba access
# isSystemUser = true;
# group = "users";
# createHome = true;
# };
# users.users.scannerupload = {
# # used for scanner samba access
# isSystemUser = true;
# group = "users";
# createHome = true;
# };
# systemd.tmpfiles.rules = [
# "d /var/spool/samba 1777 root root -"
# ];
# services.samba-wsdd = {
# enable = true;
# };
# services.samba = {
# enable = true;
# openFirewall = true;
# securityType = "user";
# # not needed since I don't think I use printer sharing?
# # https://nixos.wiki/wiki/Samba#Printer_sharing
# # package = pkgs.sambaFull; # broken last I checked in nixpkgs?
# extraConfig = ''
# workgroup = WORKGROUP
# server string = beefcake
# netbios name = beefcake
# security = user
# #use sendfile = yes
# #max protocol = smb2
# # note: localhost is the ipv6 localhost ::1
# hosts allow = 100.64.0.0/10 192.168.0.0/16 127.0.0.1 localhost
# hosts deny = 0.0.0.0/0
# guest account = guest
# map to guest = never
# # load printers = yes
# # printing = cups
# # printcap name = cups
# '';
# shares = {
# libre = {
# path = "/storage/libre";
# browseable = "yes";
# "read only" = "no";
# "guest ok" = "yes";
# "create mask" = "0666";
# "directory mask" = "0777";
# # "force user" = "nobody";
# # "force group" = "users";
# };
# public = {
# path = "/storage/public";
# browseable = "yes";
# "read only" = "no";
# "guest ok" = "yes";
# "create mask" = "0664";
# "directory mask" = "0775";
# # "force user" = "nobody";
# # "force group" = "users";
# };
# family = {
# path = "/storage/family";
# browseable = "yes";
# "read only" = "no";
# "guest ok" = "no";
# "create mask" = "0660";
# "directory mask" = "0770";
# # "force user" = "nobody";
# # "force group" = "family";
# };
# scannerdocs = {
# path = "/storage/scannerdocs";
# browseable = "yes";
# "read only" = "no";
# "guest ok" = "no";
# "create mask" = "0600";
# "directory mask" = "0700";
# "valid users" = "scannerupload";
# "force user" = "scannerupload";
# "force group" = "users";
# };
# daniel = {
# path = "/storage/daniel";
# browseable = "yes";
# "read only" = "no";
# "guest ok" = "no";
# "create mask" = "0600";
# "directory mask" = "0700";
# # "force user" = "daniel";
# # "force group" = "users";
# };
# # printers = {
# # comment = "All Printers";
# # path = "/var/spool/samba";
# # public = "yes";
# # browseable = "yes";
# # # to allow user 'guest account' to print.
# # "guest ok" = "yes";
# # writable = "no";
# # printable = "yes";
# # "create mode" = 0700;
# # };
# };
# };
# }
{
# nextcloud
# users.users.nextcloud = {
# isSystemUser = true;
# createHome = false;
# group = "nextcloud";
# };
}
# {
# # plausible
# users.users.plausible = {
# isSystemUser = true;
# createHome = false;
# group = "plausible";
# };
# users.extraGroups = {
# "plausible" = {};
# };
# services.plausible = {
# # TODO: enable
# 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;
# };
# };
# systemd.services.plausible = let
# cfg = config.services.plausible;
# in {
# serviceConfig.User = "plausible";
# serviceConfig.Group = "plausible";
# # since createdb is not gated behind postgres.setup, this breaks
# script = lib.mkForce ''
# # Elixir does not start up if `RELEASE_COOKIE` is not set,
# # even though we set `RELEASE_DISTRIBUTION=none` so the cookie should be unused.
# # Thus, make a random one, which should then be ignored.
# export RELEASE_COOKIE=$(tr -dc A-Za-z0-9 < /dev/urandom | head -c 20)
# export ADMIN_USER_PWD="$(< $CREDENTIALS_DIRECTORY/ADMIN_USER_PWD )"
# export SECRET_KEY_BASE="$(< $CREDENTIALS_DIRECTORY/SECRET_KEY_BASE )"
# ${lib.optionalString (cfg.mail.smtp.passwordFile != null)
# ''export SMTP_USER_PWD="$(< $CREDENTIALS_DIRECTORY/SMTP_USER_PWD )"''}
# # setup
# ${
# if cfg.database.postgres.setup
# then "${cfg.package}/createdb.sh"
# else ""
# }
# ${cfg.package}/migrate.sh
# export IP_GEOLOCATION_DB=${pkgs.dbip-country-lite}/share/dbip/dbip-country-lite.mmdb
# ${cfg.package}/bin/plausible eval "(Plausible.Release.prepare() ; Plausible.Auth.create_user(\"$ADMIN_USER_NAME\", \"$ADMIN_USER_EMAIL\", \"$ADMIN_USER_PWD\"))"
# ${lib.optionalString cfg.adminUser.activate ''
# psql -d plausible <<< "UPDATE users SET email_verified=true where email = '$ADMIN_USER_EMAIL';"
# ''}
# exec plausible start
# '';
# };
# services.caddy.virtualHosts."a.lyte.dev" = {
# extraConfig = ''
# reverse_proxy :${toString config.services.plausible.server.port}
# '';
# };
# }
# {
# # clickhouse
# environment.etc = {
# "clickhouse-server/users.d/disable-logging-query.xml" = {
# text = ''
#
#
#
# 0
# 0
#
#
#
# '';
# };
# "clickhouse-server/config.d/reduce-logging.xml" = {
# text = ''
#
#
# warning
# true
#
#
#
#
#
#
#
#
#
#
# '';
# };
# };
# }
{
# daniel augments
users.groups.daniel.members = ["daniel"];
users.groups.nixadmin.members = ["daniel"];
users.users.daniel = {
extraGroups = [
# "nixadmin" # write access to /etc/nixos/ files
"wheel" # sudo access
"caddy" # write access to public static files
"users" # general users group
"jellyfin" # write access to jellyfin files
"audiobookshelf" # write access to audiobookshelf files
"flanilla" # minecraft server manager
];
};
}
# {
# 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'';
# };
# # 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
# # jellyfin hardware encoding
# # 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; };
# # };
# }
# {
# services.postgresql = {
# enable = true;
# ensureDatabases = [
# "daniel"
# "plausible"
# "nextcloud"
# # "atuin"
# ];
# ensureUsers = [
# {
# name = "daniel";
# ensureDBOwnership = true;
# }
# {
# name = "plausible";
# ensureDBOwnership = true;
# }
# {
# name = "nextcloud";
# ensureDBOwnership = true;
# }
# # {
# # name = "atuin";
# # ensureDBOwnership = true;
# # }
# ];
# dataDir = "/storage/postgres";
# enableTCPIP = true;
# package = pkgs.postgresql_15;
# # https://www.postgresql.org/docs/current/auth-pg-hba-conf.html
# 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
# # local plausible plausible peer
# # local nextcloud nextcloud peer
# # local atuin atuin peer
# # 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
# '';
# 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
# '';
# };
# services.postgresqlBackup = {
# enable = true;
# backupAll = true;
# compression = "none"; # hoping for deduplication here?
# location = "/storage/postgres-backups";
# startAt = "*-*-* 03:00:00";
# };
# }
# {
# # friends
# users.users.ben = {
# isNormalUser = true;
# packages = [pkgs.vim];
# openssh.authorizedKeys.keys = [
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKUfLZ+IX85p9355Po2zP1H2tAxiE0rE6IYb8Sf+eF9T ben@benhany.com"
# ];
# };
# users.users.alan = {
# isNormalUser = true;
# packages = [pkgs.vim];
# openssh.authorizedKeys.keys = [
# ""
# ];
# };
# networking.firewall.allowedTCPPorts = [
# 64022
# ];
# networking.firewall.allowedUDPPorts = [
# 64020
# ];
# }
# {
# # flanilla family minecraft server
# users.groups.flanilla = {};
# users.users.flanilla = {
# isSystemUser = true;
# createHome = false;
# group = "flanilla";
# };
# }
# {
# # restic backups
# users.users.restic = {
# # used for other machines to backup to
# isNormalUser = true;
# openssh.authorizedKeys.keys =
# [
# "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJbPqzKB09U+i4Kqu136yOjflLZ/J7pYsNulTAd4x903 root@chromebox.h.lyte.dev"
# ]
# ++ config.users.users.daniel.openssh.authorizedKeys.keys;
# };
# # TODO: move previous backups over and put here
# # clickhouse and plausible analytics once they're up and running?
# services.restic.backups = let
# defaults = {
# passwordFile = "/root/restic-remotebackup-password";
# paths = [
# "/storage/files.lyte.dev"
# "/storage/daniel"
# "/storage/forgejo" # TODO: should maybe use configuration.nix's services.forgejo.dump ?
# "/storage/postgres-backups"
# # https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault
# # specifically, https://github.com/dani-garcia/vaultwarden/wiki/Backing-up-your-vault#sqlite-database-files
# "/var/lib/bitwarden_rs" # does this need any sqlite preprocessing?
# # TODO: backup *arr configs?
# ];
# initialize = true;
# exclude = [];
# timerConfig = {
# OnCalendar = ["04:45" "17:45"];
# };
# };
# in {
# local =
# defaults
# // {
# passwordFile = "/root/restic-localbackup-password";
# repository = "/storage/backups/local";
# };
# rascal =
# defaults
# // {
# extraOptions = [
# "sftp.command='ssh beefcake@rascal -i /root/.ssh/id_ed25519 -s sftp'"
# ];
# repository = "sftp://beefcake@rascal://storage/backups/beefcake";
# };
# # TODO: add ruby?
# benland =
# defaults
# // {
# extraOptions = [
# "sftp.command='ssh daniel@n.benhaney.com -p 10022 -i /root/.ssh/id_ed25519 -s sftp'"
# ];
# repository = "sftp://daniel@n.benhaney.com://storage/backups/beefcake";
# };
# };
# }
# {
# services.caddy = {
# # TODO: 502 and other error pages
# enable = true;
# email = "daniel@lyte.dev";
# adapter = "caddyfile";
# virtualHosts = {
# "dev.h.lyte.dev" = {
# extraConfig = ''
# reverse_proxy :8000
# '';
# };
# "files.lyte.dev" = {
# # TODO: customize the files.lyte.dev template?
# extraConfig = ''
# # @options {
# # method OPTIONS
# # }
# # @corsOrigin {
# # header_regexp Origin ^https?://([a-zA-Z0-9-]+\.)*lyte\.dev$
# # }
# 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
# }
# # reverse_proxy shuwashuwa:8848 {
# # header_down -Access-Control-Allow-Origin
# # }
# file_server browse {
# # browse template
# # hide .*
# root /storage/files.lyte.dev
# }
# '';
# };
# };
# # acmeCA = "https://acme-staging-v02.api.letsencrypt.org/directory";
# };
# networking.firewall.allowedTCPPorts = [
# 8000 # random development stuff
# ];
# }
# {
# services.forgejo = {
# enable = true;
# 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";
# };
# actions = {
# ENABLED = true;
# };
# service = {
# DISABLE_REGISTRATION = true;
# };
# session = {
# COOKIE_SECURE = true;
# };
# log = {
# # TODO: raise the log level
# # LEVEL = "Debug";
# };
# ui = {
# THEMES = "forgejo-auto,forgejo-light,forgejo-dark,catppuccin-mocha-sapphire";
# DEFAULT_THEME = "forgejo-auto";
# };
# indexer = {
# REPO_INDEXER_ENABLED = "true";
# REPO_INDEXER_PATH = "indexers/repos.bleve";
# MAX_FILE_SIZE = "1048576";
# # REPO_INDEXER_INCLUDE =
# REPO_INDEXER_EXCLUDE = "resources/bin/**";
# };
# };
# lfs = {
# enable = true;
# };
# dump = {
# enable = true;
# };
# database = {
# # TODO: move to postgres?
# type = "sqlite3";
# };
# };
# services.gitea-actions-runner = {
# # TODO: simple git-based automation would be dope? maybe especially for
# # mirroring to github super easy?
# # enable = true;
# 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";
# };
# };
# 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
# ];
# };
# };
# # 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}
# '';
# };
# }
# {
# services.vaultwarden = {
# enable = true;
# config = {
# DOMAIN = "https://bw.lyte.dev";
# SIGNUPS_ALLOWED = "false";
# ROCKET_ADDRESS = "127.0.0.1";
# ROCKET_PORT = 8222;
# };
# };
# services.caddy.virtualHosts."bw.lyte.dev" = {
# extraConfig = ''reverse_proxy :${toString config.services.vaultwarden.config.ROCKET_PORT}'';
# };
# }
# {
# # TODO: make the client declarative? right now I think it's manually git
# # clone'd to /root
# systemd.services.deno-netlify-ddns-client = {
# serviceConfig.Type = "oneshot";
# path = with pkgs; [curl bash];
# environment = {
# NETLIFY_DDNS_RC_FILE = "/root/deno-netlify-ddns-client/.env";
# };
# script = ''
# bash /root/deno-netlify-ddns-client/netlify-ddns-client.sh
# '';
# };
# systemd.timers.deno-netlify-ddns-client = {
# wantedBy = ["timers.target"];
# partOf = ["deno-netlify-ddns-client.service"];
# timerConfig = {
# OnBootSec = "10sec";
# OnUnitActiveSec = "5min";
# Unit = "deno-netlify-ddns-client.service";
# };
# };
# }
# {
# services.atuin = {
# enable = true;
# database = {
# createLocally = true;
# # uri = "postgresql://atuin@localhost:5432/atuin";
# };
# openRegistration = false;
# };
# services.caddy.virtualHosts."atuin.h.lyte.dev" = {
# extraConfig = ''reverse_proxy :${toString config.services.atuin.port}'';
# };
# }
# {
# # jland minecraft server
# 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
# ];
# }
# {
# virtualisation.oci-containers.containers.minecraft-flanilla = {
# autoStart = true;
# image = "docker.io/itzg/minecraft-server";
# user = "${toString config.users.users.flanilla.uid}:${toString config.users.groups.flanilla.gid}";
# extraOptions = ["--tty" "--interactive"];
# environment = {
# EULA = "true";
# UID = toString config.users.users.flanilla.uid;
# GID = toString config.users.groups.flanilla.gid;
# STOP_SERVER_ANNOUNCE_DELAY = "20";
# TZ = "America/Chicago";
# VERSION = "1.20.4";
# OPS = "lytedev";
# MODE = "creative";
# DIFFICULTY = "peaceful";
# ONLINE_MODE = "false";
# MEMORY = "8G";
# MAX_MEMORY = "16G";
# ALLOW_FLIGHT = "true";
# ENABLE_QUERY = "true";
# ENABLE_COMMAND_BLOCK = "true";
# };
# environmentFiles = [
# # config.sops.secrets."flanilla.env".path
# ];
# ports = ["26966:25565"];
# volumes = [
# "/storage/flanilla/data:/data"
# "/storage/flanilla/worlds:/worlds"
# ];
# };
# networking.firewall.allowedTCPPorts = [
# 26966
# ];
# }
# ({options, ...}: let
# toml = pkgs.formats.toml {};
# package = pkgs.kanidm;
# domain = "idm.h.lyte.dev";
# name = "kanidm";
# storage = "/storage/${name}";
# cert = "${storage}/certs/idm.h.lyte.dev.crt";
# key = "${storage}/certs/idm.h.lyte.dev.key";
# serverSettings = {
# inherit domain;
# bindaddress = "127.0.0.1:8443";
# # ldapbindaddress
# tls_chain = cert;
# tls_key = key;
# origin = "https://${domain}";
# db_path = "${storage}/data/kanidm.db";
# log_level = "info";
# online_backup = {
# path = "${storage}/backups/";
# schedule = "00 22 * * *";
# # versions = 7;
# };
# };
# unixdSettings = {
# hsm_pin_path = "/var/cache/${name}-unixd/hsm-pin";
# pam_allowed_login_groups = [];
# };
# clientSettings = {
# uri = "https://idm.h.lyte.dev";
# };
# user = name;
# group = name;
# serverConfigFile = toml.generate "server.toml" serverSettings;
# unixdConfigFile = toml.generate "kanidm-unixd.toml" unixdSettings;
# clientConfigFile = toml.generate "kanidm-config.toml" clientSettings;
# defaultServiceConfig = {
# BindReadOnlyPaths = [
# "/nix/store"
# "-/etc/resolv.conf"
# "-/etc/nsswitch.conf"
# "-/etc/hosts"
# "-/etc/localtime"
# ];
# CapabilityBoundingSet = [];
# # ProtectClock= adds DeviceAllow=char-rtc r
# DeviceAllow = "";
# # Implies ProtectSystem=strict, which re-mounts all paths
# # DynamicUser = true;
# LockPersonality = true;
# MemoryDenyWriteExecute = true;
# NoNewPrivileges = true;
# PrivateDevices = true;
# PrivateMounts = true;
# PrivateNetwork = true;
# PrivateTmp = true;
# PrivateUsers = true;
# ProcSubset = "pid";
# ProtectClock = true;
# ProtectHome = true;
# ProtectHostname = true;
# # Would re-mount paths ignored by temporary root
# #ProtectSystem = "strict";
# ProtectControlGroups = true;
# ProtectKernelLogs = true;
# ProtectKernelModules = true;
# ProtectKernelTunables = true;
# ProtectProc = "invisible";
# RestrictAddressFamilies = [];
# RestrictNamespaces = true;
# RestrictRealtime = true;
# RestrictSUIDSGID = true;
# SystemCallArchitectures = "native";
# SystemCallFilter = ["@system-service" "~@privileged @resources @setuid @keyring"];
# # Does not work well with the temporary root
# #UMask = "0066";
# };
# in {
# # kanidm
# config = {
# # we need a mechanism to get the certificates that caddy provisions for us
# 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";
# };
# };
# systemd.services."copy-kanidm-certificates-from-caddy" = {
# script = ''
# umask 077
# install -d -m 0700 -o "${user}" -g "${group}" "${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 "${user}" -g "${group}" idm.h.lyte.dev.key idm.h.lyte.dev.crt "${storage}/certs"
# '';
# path = with pkgs; [rsync];
# serviceConfig = {
# Type = "oneshot";
# User = "root";
# };
# };
# environment.systemPackages = [package];
# # TODO: should I use this for /storage/kanidm/certs etc.?
# systemd.tmpfiles.settings."10-kanidm" = {
# "${serverSettings.online_backup.path}".d = {
# inherit user group;
# mode = "0700";
# };
# # "${builtins.dirOf unixdSettings.hsm_pin_path}".d = {
# # user = "${user}-unixd";
# # group = "${group}-unixd";
# # mode = "0700";
# # };
# "${storage}/data".d = {
# inherit user group;
# mode = "0700";
# };
# "${storage}/certs".d = {
# inherit user group;
# mode = "0700";
# };
# };
# users.groups = {
# ${group} = {};
# "${group}-unixd" = {};
# };
# users.users.${user} = {
# inherit group;
# description = "kanidm server";
# isSystemUser = true;
# packages = [package];
# };
# users.users."${user}-unixd" = {
# group = "${group}-unixd";
# description = lib.mkForce "kanidm PAM daemon";
# isSystemUser = true;
# };
# # the kanidm module in nixpkgs was not working for me, so I rolled my own
# # loosely based off it
# systemd.services.kanidm = {
# enable = true;
# path = with pkgs; [openssl] ++ [package];
# description = "kanidm identity management daemon";
# wantedBy = ["multi-user.target"];
# after = ["network.target"];
# requires = ["copy-kanidm-certificates-from-caddy.service"];
# script = ''
# pwd
# ls -la
# ls -laR /storage/kanidm
# ${package}/bin/kanidmd server -c ${serverConfigFile}
# '';
# # environment.RUST_LOG = serverSettings.log_level;
# serviceConfig = lib.mkMerge [
# defaultServiceConfig
# {
# StateDirectory = name;
# StateDirectoryMode = "0700";
# RuntimeDirectory = "${name}d";
# User = user;
# Group = group;
# AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
# CapabilityBoundingSet = ["CAP_NET_BIND_SERVICE"];
# PrivateUsers = lib.mkForce false;
# PrivateNetwork = lib.mkForce false;
# RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"];
# # TemporaryFileSystem = "/:ro";
# BindReadOnlyPaths = [
# "${storage}/certs"
# ];
# BindPaths = [
# "${storage}/data"
# # socket
# "/run/${name}d:/run/${name}d"
# # backups
# serverSettings.online_backup.path
# ];
# }
# ];
# };
# systemd.services.kanidm-unixd = {
# description = "Kanidm PAM daemon";
# wantedBy = ["multi-user.target"];
# after = ["network.target"];
# restartTriggers = [unixdConfigFile clientConfigFile];
# serviceConfig = lib.mkMerge [
# defaultServiceConfig
# {
# CacheDirectory = "${name}-unixd";
# CacheDirectoryMode = "0700";
# RuntimeDirectory = "${name}-unixd";
# ExecStart = "${package}/bin/kanidm_unixd";
# User = "${user}-unixd";
# Group = "${group}-unixd";
# BindReadOnlyPaths = [
# "-/etc/kanidm"
# "-/etc/static/kanidm"
# "-/etc/ssl"
# "-/etc/static/ssl"
# "-/etc/passwd"
# "-/etc/group"
# ];
# BindPaths = [
# # socket
# "/run/kanidm-unixd:/var/run/kanidm-unixd"
# ];
# # Needs to connect to kanidmd
# PrivateNetwork = lib.mkForce false;
# RestrictAddressFamilies = ["AF_INET" "AF_INET6" "AF_UNIX"];
# TemporaryFileSystem = "/:ro";
# }
# ];
# environment.RUST_LOG = serverSettings.log_level;
# };
# systemd.services.kanidm-unixd-tasks = {
# description = "Kanidm PAM home management daemon";
# wantedBy = ["multi-user.target"];
# after = ["network.target" "kanidm-unixd.service"];
# partOf = ["kanidm-unixd.service"];
# restartTriggers = [unixdConfigFile clientConfigFile];
# serviceConfig = {
# ExecStart = "${package}/bin/kanidm_unixd_tasks";
# BindReadOnlyPaths = [
# "/nix/store"
# "-/etc/resolv.conf"
# "-/etc/nsswitch.conf"
# "-/etc/hosts"
# "-/etc/localtime"
# "-/etc/kanidm"
# "-/etc/static/kanidm"
# ];
# BindPaths = [
# # To manage home directories
# "/home"
# # To connect to kanidm-unixd
# "/run/kanidm-unixd:/var/run/kanidm-unixd"
# ];
# # CAP_DAC_OVERRIDE is needed to ignore ownership of unixd socket
# CapabilityBoundingSet = ["CAP_CHOWN" "CAP_FOWNER" "CAP_DAC_OVERRIDE" "CAP_DAC_READ_SEARCH"];
# IPAddressDeny = "any";
# # Need access to users
# PrivateUsers = false;
# # Need access to home directories
# ProtectHome = false;
# RestrictAddressFamilies = ["AF_UNIX"];
# TemporaryFileSystem = "/:ro";
# Restart = "on-failure";
# };
# environment.RUST_LOG = serverSettings.log_level;
# };
# environment.etc = {
# "kanidm/server.toml".source = serverConfigFile;
# "kanidm/config".source = clientConfigFile;
# "kanidm/unixd".source = unixdConfigFile;
# };
# system.nssModules = [package];
# system.nssDatabases.group = [name];
# system.nssDatabases.passwd = [name];
# # environment.etc."kanidm/server.toml" = {
# # mode = "0600";
# # group = "kanidm";
# # user = "kanidm";
# # };
# # environment.etc."kanidm/config" = {
# # mode = "0600";
# # group = "kanidm";
# # user = "kanidm";
# # };
# services.caddy.virtualHosts."idm.h.lyte.dev" = {
# extraConfig = ''reverse_proxy https://idm.h.lyte.dev:8443'';
# };
# networking = {
# extraHosts = ''
# ::1 idm.h.lyte.dev
# 127.0.0.1 idm.h.lyte.dev
# '';
# };
# };
# })
# {
# services.audiobookshelf = {
# enable = true;
# # dataDir = "/storage/audiobookshelf";
# port = 8523;
# };
# services.caddy.virtualHosts."audio.lyte.dev" = {
# extraConfig = ''reverse_proxy :8523'';
# };
# }
];
# TODO: non-root processes and services that access secrets need to be part of
# the 'keys' group
# maybe this will fix plausible?
# systemd.services.some-service = {
# serviceConfig.SupplementaryGroups = [ config.users.groups.keys.name ];
# };
# or
# users.users.example-user.extraGroups = [ config.users.groups.keys.name ];
# TODO: directory attributes for /storage subdirectories?
# example: user daniel should be able to write to /storage/files.lyte.dev and
# caddy should be able to serve it
# TODO: declarative directory quotas? for storage/$USER and /home/$USER
# TODO: would be nice to get ALL the storage stuff declared in here
# should I be using btrfs subvolumes? can I capture file ownership, perimssions, and ACLs?
virtualisation.oci-containers.backend = "podman";
virtualisation.podman = {
# autoPrune.enable = true;
# defaultNetwork.settings = {
# driver = "host";
# };
};
environment.systemPackages = with pkgs; [
restic
btrfs-progs
zfs
smartmontools
htop
bottom
curl
xh
];
services.tailscale.useRoutingFeatures = "server";
# 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;
# }
# ];
}