/sys/machines -> /hosts

This commit is contained in:
xunuwu 2025-06-06 11:22:15 +02:00
parent 2c475dd099
commit 9c9a3d543c
Signed by: xun
SSH key fingerprint: SHA256:Uot/1WoAjWAeqLOHA5vYy4phhVydsH7jCPmBjaPZfgI
38 changed files with 2 additions and 2 deletions

71
hosts/hopper/default.nix Normal file
View file

@ -0,0 +1,71 @@
{
inputs,
systemProfiles,
specialArgs,
lib,
...
}: {
imports =
[
inputs.hardware.nixosModules.common-cpu-intel
inputs.vpn-confinement.nixosModules.default
inputs.nix-minecraft.nixosModules.minecraft-servers
inputs.impermanence.nixosModules.impermanence
./hardware.nix
./lab
./roblox-playtime.nix
./desktop.nix
./persistent.nix
{
home-manager = {
backupFileExtension = "hm-backup";
users.desktop.imports = [
./home.nix
{home.stateVersion = "24.11";}
];
extraSpecialArgs = specialArgs;
};
}
]
++ (map (x: systemProfiles + x) [
/programs/home-manager.nix
/core/security.nix
/core/locale.nix
/core/tools.nix
/core/ssh.nix
/core/deploy.nix
/hardware/graphics.nix
/hardware/steam-hardware.nix
/hardware/bluetooth.nix
/nix/default.nix # TODO slim this down
/nix/gc.nix
/network/tailscale.nix
/network/avahi.nix
/network/networkd.nix
]);
nixpkgs.config = {
allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) [
"nvidia-x11"
"nvidia-settings"
"stremio-shell"
"stremio-server"
];
};
networking.hostName = "hopper";
swapDevices = [];
networking.interfaces.eno1.wakeOnLan.enable = true;
system.stateVersion = "23.11";
}

39
hosts/hopper/desktop.nix Normal file
View file

@ -0,0 +1,39 @@
{
pkgs,
lib,
config,
inputs,
...
}: {
users.users.desktop = {
isNormalUser = true;
useDefaultShell = true;
createHome = true;
extraGroups = [
"input"
"video"
"render"
"audio"
];
};
environment.systemPackages = with pkgs; [
firefox
];
services.greetd = {
enable = true;
settings = {
default_session = {
command = lib.getExe config.programs.sway.package;
user = "desktop";
};
};
};
programs.sway = {
enable = true;
wrapperFeatures.gtk = true;
extraOptions = ["--unsupported-gpu"];
};
}

92
hosts/hopper/hardware.nix Normal file
View file

@ -0,0 +1,92 @@
{lib, ...}: {
nixpkgs.hostPlatform.system = "x86_64-linux";
## nvidia gpu
services.xserver.videoDrivers = ["nvidia"];
hardware.nvidia = {
open = false;
};
#hardware.nvidia = {
# modesetting.enable = true;
# package = config.boot.kernelPackages.nvidiaPackages.stable;
#};
boot = {
blacklistedKernelModules = [
"xhci_pci" # was causing issues (100% udevd cpu usage)
];
initrd = {
availableKernelModules = [
"ehci_pci"
"ahci"
"usb_storage"
"usbhid"
"sd_mod"
];
kernelModules = [];
};
kernelModules = ["kvm-intel" "wireguard"];
extraModulePackages = [];
loader = {
systemd-boot = {
enable = true;
configurationLimit = 10;
};
efi.canTouchEfiVariables = true;
};
};
boot.initrd.postResumeCommands = lib.mkAfter ''
mkdir /btrfs_tmp
mount /dev/disk/by-uuid/1297e638-f2ff-49a2-a362-314ac7eeaabc /btrfs_tmp
if [[ -e /btrfs_tmp/root ]]; then
mkdir -p /btrfs_tmp/old_roots
timestamp=$(date --date="@$(stat -c %Y /btrfs_tmp/root)" "+%Y-%m-%-d_%H:%M:%S")
mv /btrfs_tmp/root "/btrfs_tmp/old_roots/$timestamp"
fi
delete_subvolume_recursively() {
IFS=$'\n'
for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
delete_subvolume_recursively "/btrfs_tmp/$i"
done
btrfs subvolume delete "$1"
}
for i in $(find /btrfs_tmp/old_roots/ -maxdepth 1 -mtime +30); do
delete_subvolume_recursively "$i"
done
btrfs subvolume create /btrfs_tmp/root
umount /btrfs_tmp
'';
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/1297e638-f2ff-49a2-a362-314ac7eeaabc";
fsType = "btrfs";
options = ["subvol=root" "compress=zstd" "autodefrag" "noatime"];
};
"/home" = {
device = "/dev/disk/by-uuid/1297e638-f2ff-49a2-a362-314ac7eeaabc";
fsType = "btrfs";
options = ["subvol=home" "compress=zstd"];
};
"/nix" = {
device = "/dev/disk/by-uuid/1297e638-f2ff-49a2-a362-314ac7eeaabc";
fsType = "btrfs";
options = ["subvol=nix" "compress=zstd" "noatime"];
};
"/persist" = {
device = "/dev/disk/by-uuid/1297e638-f2ff-49a2-a362-314ac7eeaabc";
neededForBoot = true;
fsType = "btrfs";
options = ["subvol=persist" "compress=zstd"];
};
"/boot" = {
device = "/dev/disk/by-uuid/8D4C-2F05";
fsType = "vfat";
};
};
}

41
hosts/hopper/home.nix Normal file
View file

@ -0,0 +1,41 @@
{
homeProfiles,
lib,
pkgs,
...
}: {
imports = map (x: homeProfiles + x) [
/terminal/programs/xdg.nix
/terminal/programs/comma.nix
/editors/nvim.nix
/terminal/emulator/foot.nix
/programs/desktop/default.nix
/programs/desktop/sway/default.nix
/programs/media/mpv.nix
/services/playerctl.nix
/services/polkit-agent.nix
];
wayland.windowManager.sway.config.output."HDMI-A-1".scale = "2.0";
services = {
udiskie.enable = true;
};
home.packages = with pkgs; [
pwvucontrol
qpwgraph
stremio
moonlight-qt
];
home = {
username = "desktop";
homeDirectory = lib.mkForce "/home/desktop";
};
programs.home-manager.enable = true;
}

25
hosts/hopper/lab/acme.nix Normal file
View file

@ -0,0 +1,25 @@
{
config,
vars,
...
}: let
inherit (vars) domain;
in {
security.acme = {
acceptTerms = true;
defaults.email = "xunuwu@gmail.com";
certs = {
"${domain}" = {
domain = "${domain}";
extraDomainNames = ["*.${domain}" "*.hopper.priv.${domain}"];
dnsProvider = "cloudflare";
reloadServices = ["caddy.service"];
credentialFiles = {
CF_DNS_API_TOKEN_FILE = config.sops.secrets.cloudflare.path;
};
};
};
};
environment.persistence."/persist".directories = ["/var/lib/acme"];
}

View file

@ -0,0 +1,11 @@
{config, ...}: {
services.audiobookshelf = {
enable = true;
host = "0.0.0.0";
};
environment.persistence."/persist".directories = ["/var/lib/${config.services.audiobookshelf.dataDir}"];
services.restic.backups.hopper.paths = [
"/var/lib/${config.services.audiobookshelf.dataDir}"
];
}

View file

@ -0,0 +1,73 @@
{
config,
vars,
inputs,
pkgs,
...
}: let
inherit (vars) domain;
bridge = config.vpnNamespaces."wg".bridgeAddress;
in {
systemd.services.caddy.vpnConfinement = {
enable = true;
vpnNamespace = "wg";
};
systemd.services.caddy = {
environment.CADDY_ADMIN = "0.0.0.0:2019";
serviceConfig.RuntimeDirectory = "caddy";
};
services.caddy = {
enable = true;
globalConfig = "metrics";
virtualHosts = let
mkPublicEntry = name: destination: {
hostName = "${name}.${domain}:80";
extraConfig = ''
reverse_proxy {
to ${destination}
}
'';
};
mkPrivateEntry = name: destination: {
hostName = "${name}.hopper.priv.${domain}";
useACMEHost = domain;
extraConfig = ''
@blocked not remote_ip ${bridge}
respond @blocked "limited to intranet" 403
reverse_proxy ${destination}
'';
};
in {
navidrome = mkPublicEntry "navidrome" "${bridge}:${toString config.services.navidrome.settings.Port}";
vaultwarden = mkPublicEntry "vw" "${bridge}:${toString config.services.vaultwarden.config.ROCKET_PORT}";
abs = mkPublicEntry "abs" "${bridge}:${toString config.services.audiobookshelf.port}";
navidrome2 = mkPrivateEntry "navidrome" "${bridge}:${toString config.services.navidrome.settings.Port}";
slskd = mkPrivateEntry "slskd" "localhost:${toString config.services.slskd.settings.web.port}";
prometheus = mkPrivateEntry "prometheus" "${bridge}:${toString config.services.prometheus.port}";
transmission = mkPrivateEntry "transmission" "localhost:${toString config.services.transmission.settings.rpc-port}";
dash = mkPrivateEntry "dash" "${bridge}:${toString config.services.homepage-dashboard.listenPort}";
absPriv = mkPrivateEntry "abs" "${bridge}:${toString config.services.audiobookshelf.port}";
glances = mkPrivateEntry "glances" "${bridge}:${toString config.services.glances.port}";
base = {
hostName = "${domain}:80";
extraConfig = ''
root * ${inputs.own-website.packages.${pkgs.system}.default}
file_server
'';
};
other = {
hostName = "*.${domain}:80";
extraConfig = ''
respond 404 {
body "uhh that doesnt exist, i hope this isnt my fault.."
}
'';
};
};
};
}

View file

@ -0,0 +1,33 @@
## TODO look into sops-nix placeholders
## reference: https://github.com/javigomezo/nixos/blob/b3ebe8d570ea9b37aea8bb3a343f6e16e054e322/services/network/authelia/user_database.nix
{
imports = [
./acme.nix
./audiobookshelf.nix
./caddy.nix
./dnsmasq.nix
./glances.nix
./homepage.nix
./minecraft.nix
./navidrome
./prometheus.nix
./restic.nix
./samba.nix
./slskd.nix
./transmission.nix
./vaultwarden.nix
./vpn-namespace.nix
];
users.groups.media = {};
users.users.media = {
isSystemUser = true;
group = "media";
};
networking.firewall = {
allowedUDPPorts = [1900 7359]; # Jellyfin auto-discovery
};
boot.kernel.sysctl."fs.inotify.max_user_watches" = 99999999;
}

View file

@ -0,0 +1,16 @@
{
vars,
lib,
...
}: {
services.dnsmasq = {
enable = true;
resolveLocalQueries = false;
settings = {
server = ["1.1.1.1" "8.8.8.8"];
interface = ["tailscale0"];
bind-interfaces = true;
address = lib.mapAttrsToList (n: v: "/.${n}.priv.${vars.domain}/${v}") vars.tailnet;
};
};
}

View file

@ -0,0 +1,5 @@
{
services.glances = {
enable = true;
};
}

View file

@ -0,0 +1,86 @@
{
config,
vars,
...
}: let
inherit (vars) domain;
bridge = config.vpnNamespaces."wg".bridgeAddress;
in {
services.homepage-dashboard = {
enable = true;
allowedHosts = "dash.hopper.priv.${domain}";
widgets = [
{
resources = {
cpu = true;
disk = "/";
uptime = "";
units = "metric";
cputemp = true;
memory = true;
network = true;
};
}
];
services = [
{
"Downloading" = [
{
"transmission" = {
href = "https://transmission.hopper.priv.${domain}";
icon = "transmission";
widget = {
type = "transmission";
url = "http://${config.vpnNamespaces."wg".namespaceAddress}:${toString config.services.transmission.settings.rpc-port}";
};
};
}
{
"slskd" = {
href = "https://slskd.hopper.priv.${domain}";
icon = "slskd";
};
}
];
}
{
"Services" = [
{
"navidrome" = {
href = "https://navidrome.${domain}";
icon = "navidrome";
};
}
{
"audiobookshelf" = {
href = "https://abs.${domain}";
icon = "audiobookshelf";
};
}
{
"prometheus" = {
href = "https://prometheus.hopper.priv.${domain}";
icon = "prometheus";
widget = {
type = "prometheus";
url = "http://localhost:${toString config.services.prometheus.port}";
};
};
}
{
"glances" = {
href = "https://glances.hopper.priv.${domain}";
icon = "glances";
};
}
{
"vaultwarden" = {
href = "https://vw.${domain}";
icon = "vaultwarden";
};
}
];
}
];
};
}

View file

@ -0,0 +1,50 @@
{
inputs,
pkgs,
...
}: {
services.minecraft-servers = {
enable = false;
eula = true;
openFirewall = true;
servers.owo = {
enable = true;
package = inputs.nix-minecraft.legacyPackages.${pkgs.system}.fabricServers.fabric-1_21_5;
serverProperties = {
max-players = 5;
motd = "owo";
difficulty = "normal";
allow-flight = true;
view-distance = 16;
};
jvmOpts = "-Xms1024M -Xmx4096M";
symlinks.mods = pkgs.linkFarmFromDrvs "mods" (
builtins.attrValues {
Fabric-API = pkgs.fetchurl {
url = "https://cdn.modrinth.com/data/P7dR8mSH/versions/hBmLTbVB/fabric-api-0.121.0%2B1.21.5.jar";
hash = "sha256-GbKETZqAN5vfXJF0yNgwTiogDAI434S3Rj9rZw6B53E=";
};
Lithium = pkgs.fetchurl {
url = "https://cdn.modrinth.com/data/gvQqBUqZ/versions/VWYoZjBF/lithium-fabric-0.16.2%2Bmc1.21.5.jar";
hash = "sha256-XqvnQxASa4M0l3JJxi5Ej6TMHUWgodOmMhwbzWuMYGg=";
};
FerriteCore = pkgs.fetchurl {
url = "https://cdn.modrinth.com/data/uXXizFIs/versions/CtMpt7Jr/ferritecore-8.0.0-fabric.jar";
hash = "sha256-K5C/AMKlgIw8U5cSpVaRGR+HFtW/pu76ujXpxMWijuo=";
};
C2ME = pkgs.fetchurl {
url = "https://cdn.modrinth.com/data/VSNURh3q/versions/VEjpHAOG/c2me-fabric-mc1.21.5-0.3.2%2Brc.1.0.jar";
hash = "sha256-D7Ho8N4vZwHeacmfNe8YMcxsQCSlyNWFsxOp2b+vujE=";
};
Krypton = pkgs.fetchurl {
url = "https://cdn.modrinth.com/data/fQEb0iXm/versions/neW85eWt/krypton-0.2.9.jar";
hash = "sha256-uGYia+H2DPawZQxBuxk77PMKfsN8GEUZo3F1zZ3MY6o=";
};
}
);
};
};
environment.persistence."/persist".directories = ["/srv/minecraft"];
services.restic.backups.hopper.paths = ["/srv/minecraft"];
}

View file

@ -0,0 +1,28 @@
{
config,
pkgs,
...
}: {
users.users.navidrome.extraGroups = ["media"];
services.navidrome = {
enable = true;
package = pkgs.navidrome.overrideAttrs {
patches = [./scrobbleAlbumArtist.patch];
doCheck = false;
};
settings = {
MusicFolder = "/media/library/music";
Address = config.vpnNamespaces."wg".bridgeAddress;
EnableSharing = true;
};
};
systemd.services.navidrome.serviceConfig.EnvironmentFile = config.sops.secrets.navidrome.path;
environment.persistence."/persist".directories = ["/var/lib/navidrome"];
services.restic.backups.hopper = {
paths = ["/var/lib/navidrome"];
exclude = ["/var/lib/navidrome/cache"];
};
}

View file

@ -0,0 +1,23 @@
diff --git a/core/agents/lastfm/client.go b/core/agents/lastfm/client.go
index 6a24ac80..bbc0aebb 100644
--- a/core/agents/lastfm/client.go
+++ b/core/agents/lastfm/client.go
@@ -131,7 +131,7 @@ type ScrobbleInfo struct {
func (c *client) updateNowPlaying(ctx context.Context, sessionKey string, info ScrobbleInfo) error {
params := url.Values{}
params.Add("method", "track.updateNowPlaying")
- params.Add("artist", info.artist)
+ params.Add("artist", info.albumArtist)
params.Add("track", info.track)
params.Add("album", info.album)
params.Add("trackNumber", strconv.Itoa(info.trackNumber))
@@ -154,7 +154,7 @@ func (c *client) scrobble(ctx context.Context, sessionKey string, info ScrobbleI
params := url.Values{}
params.Add("method", "track.scrobble")
params.Add("timestamp", strconv.FormatInt(info.timestamp.Unix(), 10))
- params.Add("artist", info.artist)
+ params.Add("artist", info.albumArtist)
params.Add("track", info.track)
params.Add("album", info.album)
params.Add("trackNumber", strconv.Itoa(info.trackNumber))

View file

@ -0,0 +1,53 @@
{
lib,
config,
...
}: {
services.prometheus = {
enable = true;
port = 9001;
extraFlags = ["--storage.tsdb.retention.time=30d"];
scrapeConfigs = [
{
job_name = "node";
static_configs = lib.singleton {
targets = ["127.0.0.1:${toString config.services.prometheus.exporters.node.port}"];
};
}
{
job_name = "tailscale_client";
static_configs = lib.singleton {
targets = ["100.100.100.100"];
};
}
{
job_name = "caddy";
static_configs = lib.singleton {
targets = ["${config.vpnNamespaces."wg".namespaceAddress}:2019"];
};
}
{
job_name = "slskd";
static_configs = lib.singleton {
targets = ["${config.vpnNamespaces."wg".namespaceAddress}:${toString config.services.slskd.settings.web.port}"];
};
metric_relabel_configs = lib.singleton {
source_labels = ["__name__"];
regex = "node_.*";
action = "drop";
};
}
];
};
services.prometheus.exporters = {
node = {
enable = true;
enabledCollectors = ["systemd"];
};
systemd.enable = true;
};
environment.persistence."/persist".directories = ["/var/lib/prometheus2"];
services.restic.backups.hopper.paths = ["/var/lib/prometheus2"];
}

View file

@ -0,0 +1,21 @@
{config, ...}: {
services.restic.backups.hopper = {
initialize = true;
inhibitsSleep = true;
repository = "rest:http://nixdesk:8000/hopper";
passwordFile = config.sops.secrets.restic-password.path;
timerConfig = {
OnCalendar = "18:00";
Persistent = true;
RandomizedDelaySec = "1h";
};
pruneOpts = [
"--keep-daily 7"
"--keep-weekly 5"
"--keep-monthly 6"
];
paths = [
"/media/library/music"
];
};
}

View file

@ -0,0 +1,80 @@
{
config,
lib,
pkgs,
...
}: {
# only used for samba
users.groups.xun = {};
users.users.xun = {
isSystemUser = true;
group = "xun";
extraGroups = ["transmission" "vault" "media"];
};
users.users.media = {
isSystemUser = true;
group = "media";
};
users.groups.vault = {};
systemd.tmpfiles.rules = [
"d /srv/vault 0770 root vault -"
"d /media/library 0770 media media -"
];
services.samba = {
enable = true;
openFirewall = true;
settings = {
global = {
"log level" = "3 passdb:5 auth:5";
"log file" = "/var/log/samba/samba.log";
"server string" = config.networking.hostName;
"hosts allow" = "192.168.50.0/24";
"map to guest" = "bad user";
"passdb backend" = "smbpasswd:${config.sops.secrets.samba-pass.path}";
};
transmission = {
path = "/var/lib/transmission";
browseable = "yes";
"read only" = "yes";
"guest ok" = "no";
"create mask" = "0660";
"directory mask" = "0770";
};
vault = {
path = "/srv/vault";
browseable = "yes";
"read only" = "no";
"guest ok" = "no";
"create mask" = "0660";
"directory mask" = "0770";
"force user" = "xun";
"force group" = "xun";
};
slskd = {
path = "/var/lib/slskd";
browseable = "yes";
"read only" = "no";
"guest ok" = "no";
"create mask" = "0660";
"directory mask" = "0770";
"force user" = "slskd";
"force group" = "slskd";
};
library = {
path = "media/library";
browseable = "yes";
"read only" = "no";
"guest ok" = "no";
"create mask" = "0660";
"directory mask" = "0770";
"force user" = "media";
"force group" = "media";
};
};
};
environment.persistence."/persist".directories = ["/srv/vault"];
services.restic.backups.hopper.paths = ["/srv/vault"];
}

View file

@ -0,0 +1,43 @@
{
config,
pkgs,
...
}: {
systemd.services.slskd.vpnConfinement = {
enable = true;
vpnNamespace = "wg";
};
users.users.slskd.extraGroups = ["media"];
services.slskd = {
enable = true;
environmentFile = config.sops.secrets.slskd.path;
domain = null; # why isnt this the default?
settings = {
metrics = {
enabled = true;
authentication.disabled = true;
};
remote_file_management = true;
shares.directories = ["/media/library/music"];
soulseek = {
listen_port = 24001;
picture = pkgs.fetchurl {
url = "https://cdn.donmai.us/original/57/65/__kasane_teto_utau_drawn_by_nonounno__576558c9a54c63a268f9b584f1e84c9f.png";
hash = "sha256-7WOClBi4QgOfmcMaMorK/t8FGGO7dNUwxg3AVEjRemw=";
};
};
web.authentication.disabled = true;
global = {
upload = {
slots = 50;
speed_limit = 10000;
};
download.speed_limit = 10000;
};
};
};
environment.persistence."/persist".directories = ["/var/lib/slskd"];
}

View file

@ -0,0 +1,34 @@
{
pkgs,
config,
vars,
...
}: {
systemd.services.transmission.vpnConfinement = {
enable = true;
vpnNamespace = "wg";
};
services.transmission = {
enable = true;
package = pkgs.transmission_4;
performanceNetParameters = true;
settings = let
mbit = 125;
in {
speed-limit-up-enabled = true;
speed-limit-up = 50 * mbit;
speed-limit-down-enabled = true;
speed-limit-down = 150 * mbit;
peer-port = 24003;
rpc-authentication-required = false;
rpc-bind-address = "0.0.0.0";
rpc-host-whitelist = "transmission.hopper.priv.${vars.domain}";
rpc-whitelist-enabled = true;
rpc-whitelist = "127.0.0.1,192.168.\*.\*,100.\*.\*.\*";
};
# credentialsFile = config.sops.secrets.transmission.path;
};
environment.persistence."/persist".directories = ["/var/lib/transmission"];
}

View file

@ -0,0 +1,20 @@
{config, ...}: {
systemd.services.vaultwarden = {
serviceConfig.EnvironmentFile = config.sops.secrets.vaultwarden-env.path;
};
# NOTE send doesnt work, probably due to my cloudflare port rewriting rules
services.vaultwarden = {
enable = true;
config = {
DOMAIN = "https://${config.services.caddy.virtualHosts.vaultwarden.hostName}";
ROCKET_ADDRESS = "0.0.0.0";
ROCKET_PORT = 35381;
ROCKET_LOG = "critical";
SIGNUPS_ALLOWED = false;
IP_HEADER = "X-Forwarded-For";
};
};
environment.persistence."/persist".directories = ["/var/lib/bitwarden_rs"];
services.restic.backups.hopper.paths = ["/var/lib/bitwarden_rs"];
}

View file

@ -0,0 +1,75 @@
{
config,
lib,
...
}: {
networking.firewall = let
allowTcpFromVPNToDefaultPorts = [
config.services.prometheus.port
config.services.vaultwarden.config.ROCKET_PORT
config.services.homepage-dashboard.listenPort
config.services.audiobookshelf.port
config.services.navidrome.settings.Port
config.services.glances.port
];
in {
extraCommands = builtins.concatStringsSep "\n" (map
(port: "iptables -A nixos-fw -p tcp -s ${config.vpnNamespaces."wg".namespaceAddress} --dport ${toString port} -j nixos-fw-accept")
allowTcpFromVPNToDefaultPorts);
extraStopCommands = builtins.concatStringsSep "\n" (
map
(port: "iptables -D nixos-fw -p tcp -s ${config.vpnNamespaces."wg".namespaceAddress} --dport ${toString port} -j nixos-fw-accept || true")
allowTcpFromVPNToDefaultPorts
);
};
vpnNamespaces."wg" = {
enable = true;
wireguardConfigFile = config.sops.secrets.wireguard.path;
accessibleFrom = [
"192.168.0.0/24"
# "127.0.0.1"
];
# Forwarded to my vpn, for making things accessible from outside
openVPNPorts = [
{
port = 443; # caddy
protocol = "tcp";
}
{
port = 80; # caddy
protocol = "tcp";
}
{
port = 24001; # slskd
protocol = "both";
}
{
port = 24002; # slskd
protocol = "both";
}
{
port = 24003; # transmission
protocol = "both";
}
];
# From inside of the vpn namespace to outside of it, for making things inside accessible to LAN
portMappings = let
passthrough = [
8336 # caddy
80 # caddy
443 # caddy
2019 # caddy admin, for prometheus metrics
config.services.transmission.settings.rpc-port
config.services.slskd.settings.web.port
];
in (lib.map (x: {
from = x;
to = x;
})
passthrough);
};
}

View file

@ -0,0 +1,18 @@
{
environment.persistence."/persist" = {
hideMounts = true;
directories = [
"/home/desktop"
"/home/deploy"
"/media"
"/var/log"
"/var/lib/nixos"
"/var/lib/bluetooth"
];
files = [
"/etc/machine-id"
"/etc/ssh/ssh_host_ed25519_key"
"/etc/ssh/ssh_host_ed25519_key.pub"
];
};
}

View file

@ -0,0 +1,15 @@
{
inputs,
config,
...
}: {
imports = [inputs.roblox-playtime.nixosModules.roblox-playtime];
services.roblox-playtime = {
enable = true;
configFile = config.sops.secrets.roblox-playtime.path;
};
environment.persistence."/persist".directories = ["/var/lib/roblox-playtime"];
services.restic.backups.hopper.paths = ["/var/lib/roblox-playtime"];
}

View file

@ -0,0 +1,6 @@
{
services.getty = {
autologinUser = "xun";
autologinOnce = true;
};
}

132
hosts/nixdesk/default.nix Normal file
View file

@ -0,0 +1,132 @@
{
lib,
pkgs,
inputs,
systemProfiles,
specialArgs,
...
}: {
# imports = with systemProfiles; [
imports =
[
./hardware.nix
./hibernate-boot.nix
./samba-mount.nix
./wireguard.nix
./restic-server.nix
./autologin.nix
inputs.impermanence.nixosModules.impermanence
inputs.stylix.nixosModules.stylix
{
home-manager = {
backupFileExtension = "hm-backup";
users.xun.imports = [
./home.nix
inputs.sops-nix.homeManagerModules.sops
{home.stateVersion = "23.11";}
];
extraSpecialArgs = specialArgs;
};
}
]
++ (map (x: systemProfiles + x) [
/core/security.nix
/core/keyring.nix
/core/users.nix
/core/ssh.nix
/core/locale.nix
/nix
/programs/zsh.nix
/programs/fish.nix
/core/tools.nix
/core/compat.nix
/core/boot.nix
# core.docs
/core/gvfs.nix
/nix/gc.nix
/hardware/graphics.nix
/hardware/steam-hardware.nix
/hardware/bluetooth.nix
/hardware/qmk.nix
/network/networkd.nix
/network/avahi.nix
/network/localsend.nix
/network/tailscale.nix
/network/goldberg.nix
/desktop/sway.nix
/programs/dconf.nix
/programs/fonts.nix
/programs/home-manager.nix
# programs.qt
/programs/adb.nix
/programs/openrgb.nix
/programs/tools.nix
/programs/thunar.nix
/services
/services/pipewire.nix
/services/podman.nix
/services/flatpak.nix
# services.syncthing
/services/waydroid.nix
/services/virt-manager.nix
/services/sunshine.nix
/services/locate.nix
# network.wifi
/themes/dark.nix
/programs/gamemode.nix
/programs/gamescope.nix
/programs/steam.nix
/programs/reverse-engineering.nix
]);
services.locate.prunePaths = lib.mkOptionDefault ["/home/xun/backup"];
# for running waydroid as root, needed for cage-xtmapper
services.dbus.packages = [
(pkgs.writeTextDir "/etc/dbus-1/session.d/dbus-allow-root.conf" ''
<busconfig>
<policy context="mandatory">
<allow user="root"/>
</policy>
</busconfig>
'')
];
nixpkgs.config = {
# rocmSupport = true;
allowUnfreePredicate = pkg:
builtins.elem (lib.getName pkg) [
"apple_cursor"
"steam"
"steam-unwrapped"
"discord"
"discord-ptb"
"obsidian"
"rider"
"idea-ultimate"
"android-studio-stable"
"stremio-shell"
"stremio-server"
];
android_sdk.accept_license = true;
};
environment.persistence."/persist".enable = false;
networking.interfaces.eno1.wakeOnLan.enable = true;
networking.hostName = "nixdesk";
system.stateVersion = "23.11";
}

View file

@ -0,0 +1,93 @@
{
inputs,
config,
pkgs,
lib,
...
}: {
imports = [
inputs.hardware.nixosModules.common-cpu-amd
inputs.hardware.nixosModules.common-gpu-amd
inputs.hardware.nixosModules.common-pc-ssd
inputs.hardware.nixosModules.gigabyte-b550
];
boot = {
kernelPackages = pkgs.linuxPackages_latest;
initrd = {
verbose = false;
availableKernelModules = [
"nvme"
"xhci_pci"
"ahci"
"usb_storage"
"usbhid"
"sd_mod"
];
kernelModules = ["amdgpu"];
};
kernelModules = ["kvm-amd" "msr"];
extraModulePackages = with config.boot.kernelPackages; [
# rtl88xxau-aircrack # usb wifi card
];
loader = {
timeout = 0;
systemd-boot = {
enable = true;
consoleMode = "max";
configurationLimit = 120;
editor = false;
};
efi = {
canTouchEfiVariables = true;
efiSysMountPoint = "/boot";
};
};
tmp.cleanOnBoot = true;
};
fileSystems = {
"/" = {
device = "/dev/disk/by-uuid/d87276c0-ef9c-422e-b2de-effc1b47c654";
fsType = "btrfs";
options = ["subvol=root" "compress=zstd"];
};
"/home" = {
device = "/dev/disk/by-uuid/d87276c0-ef9c-422e-b2de-effc1b47c654";
fsType = "btrfs";
options = ["subvol=home" "compress=zstd"];
};
"/nix" = {
device = "/dev/disk/by-uuid/d87276c0-ef9c-422e-b2de-effc1b47c654";
fsType = "btrfs";
options = ["subvol=nix" "compress=zstd" "noatime"];
};
"/.swapvol" = {
device = "/dev/disk/by-uuid/d87276c0-ef9c-422e-b2de-effc1b47c654";
fsType = "btrfs";
options = ["subvol=swap" "noatime"];
};
"/boot" = {
device = "/dev/disk/by-uuid/588B-CB97";
fsType = "vfat";
};
};
boot.resumeDevice = "/dev/disk/by-uuid/d87276c0-ef9c-422e-b2de-effc1b47c654";
boot.kernelParams = [
"nowatchdog"
# btrfs inspect-internal map-swapfile -r /.swapvol/swapfile
"resume_offset=326444288"
];
swapDevices = lib.singleton {
device = "/.swapvol/swapfile";
};
hardware.enableRedistributableFirmware = true;
services.xserver.videoDrivers = ["amdgpu"];
nixpkgs.hostPlatform.system = "x86_64-linux";
hardware.cpu.amd.updateMicrocode = true;
}

View file

@ -0,0 +1,28 @@
{pkgs, ...}: {
# hibernate and reboot to firmware
# this allows me to save linux state and boot into another os (such as windows)
# make sure not to mount any filesystems from the other os or you risk losing data
environment.systemPackages = [
(pkgs.writeShellScriptBin "hib-boot" ''
set -e
if [ ! -v 1 ]; then
echo "no argument provided"
echo "please provide the id for the os you want to boot"
echo "these are the valid id's:"
echo ""
${pkgs.efibootmgr}/bin/efibootmgr
exit
fi
if [ ! -w /sys/power/disk -o ! -w /sys/power/state ]; then
echo "you lack permission to write to /sys/power/{disk,state}, are you not running this script as root?"
exit
fi
${pkgs.efibootmgr}/bin/efibootmgr -n "$1" >/dev/null
echo reboot >/sys/power/disk
echo disk >/sys/power/state
'')
];
}

159
hosts/nixdesk/home.nix Normal file
View file

@ -0,0 +1,159 @@
{
self,
lib,
pkgs,
inputs,
homeProfiles,
...
}: {
imports = map (x: homeProfiles + x) [
/default.nix
/terminal/shell/zsh.nix
/terminal/shell/fish.nix
/terminal/programs/xdg.nix
/terminal/programs/direnv.nix
/terminal/programs/tmux.nix
/terminal/programs/comma.nix
/terminal/programs/irssi.nix
/terminal/programs/git.nix
/terminal/programs/github.nix
/terminal/programs/jujutsu.nix
/editors/nvim.nix
/programs/browsers/firefox/default.nix
/programs/browsers/tor.nix
/programs/browsers/chromium.nix
/terminal/emulator/foot.nix
/themes/dark/default.nix
# desktop
/programs/desktop/default.nix
/programs/desktop/sway/default.nix
# development
/develop/common.nix
/develop/docs.nix
/develop/langs/haskell.nix
/develop/langs/go.nix
/develop/langs/js.nix
/develop/langs/rust.nix
/develop/langs/elixir.nix
/develop/langs/nix.nix
/develop/langs/zig.nix
/develop/langs/lua.nix
/develop/langs/c.nix
/develop/langs/csharp.nix
# programs
/programs/misc/discord.nix
/programs/misc/obs.nix
/programs/music/beets.nix
/programs/music/cmus.nix
/programs/media/mpv.nix
# gaming
/programs/games/default.nix
/programs/games/jc141.nix
/services/playerctl.nix
/services/polkit-agent.nix
];
wayland.windowManager.sway.extraConfig = ''
exec obs --startreplaybuffer --disable-shutdown-check --minimize-to-tray
'';
wayland.windowManager.sway.config.output = {
"DP-3" = {
mode = "1920x1080@165Hz";
position = "1920 0";
# allow_tearing = "yes";
bg = "${inputs.wallpaper.outPath} fill";
};
"HDMI-A-1" = {
position = "0 0";
};
};
xdg.mimeApps = {
enable = true;
defaultApplications = {
"text/html" = "firefox.desktop";
"x-scheme-handler/http" = "firefox.desktop";
"x-scheme-handler/https" = "firefox.desktop";
"x-scheme-handler/about" = "firefox.desktop";
"x-scheme-handler/unknown" = "firefox.desktop";
};
};
home.sessionVariables.DEFAULT_BROWSER = lib.getExe pkgs.firefox;
services = {
udiskie.enable = true;
};
programs = {
starship.enable = true;
zoxide.enable = true;
lazygit.enable = true;
distrobox.enable = true;
};
home.packages = with pkgs; [
# coding
tokei
devenv
lazyjj
jjui
vscodium-fhs
android-studio
jetbrains.rider
jetbrains.idea-ultimate
# communication
element-desktop
# audio
pwvucontrol
qpwgraph
# productivity
tdf # terminal pdf reader
obsidian
# passwords
keepassxc
bitwarden-desktop
# creating
krita
blender
godot
libreoffice-qt6
# system things
kdePackages.dolphin
# music
puddletag
picard
# media
stremio
feishin
foliate
# games
heroic
lutris
ryubing # switch emu
prismlauncher
inputs.sobercookie.packages.${pkgs.system}.default
self.packages.${pkgs.system}.krunker
];
}

View file

@ -0,0 +1,7 @@
{
services.restic.server = {
enable = true;
dataDir = "/srv/backup";
extraFlags = ["--no-auth"];
};
}

View file

@ -0,0 +1,72 @@
{
config,
pkgs,
...
}: {
environment.systemPackages = [pkgs.cifs-utils];
systemd.mounts = [
{
description = "smb hopper transmission download directory";
what = "//192.168.50.97/transmission"; # hopper local ip
where = "/server/transmission";
type = "cifs";
options = "uid=xun,gid=users,credentials=${config.sops.secrets.samba.path}";
}
{
description = "smb hopper vault";
what = "//192.168.50.97/vault"; # hopper local ip
where = "/server/vault";
type = "cifs";
options = "uid=xun,gid=users,credentials=${config.sops.secrets.samba.path}";
}
{
description = "smb hopper library";
what = "//192.168.50.97/library"; # hopper local ip
where = "/server/library";
type = "cifs";
options = "uid=xun,gid=users,credentials=${config.sops.secrets.samba.path},vers=3.0";
}
{
description = "smb hopper slskd files";
what = "//192.168.50.97/slskd"; # hopper local ip
where = "/server/slskd";
type = "cifs";
options = "uid=xun,gid=users,credentials=${config.sops.secrets.samba.path}";
}
];
systemd.automounts = [
{
requires = ["network-online.target"];
where = "/server/transmission";
wantedBy = ["multi-user.target"];
automountConfig = {
TimeoutIdleSec = "10min";
};
}
{
requires = ["network-online.target"];
where = "/server/vault";
wantedBy = ["multi-user.target"];
automountConfig = {
TimeoutIdleSec = "10min";
};
}
{
requires = ["network-online.target"];
where = "/server/library";
wantedBy = ["multi-user.target"];
automountConfig = {
TimeoutIdleSec = "10min";
};
}
{
requires = ["network-online.target"];
where = "/server/slskd";
wantedBy = ["multi-user.target"];
automountConfig = {
TimeoutIdleSec = "10min";
};
}
];
}

View file

@ -0,0 +1,29 @@
{
config,
inputs,
lib,
...
}: {
imports = [inputs.vpn-confinement.nixosModules.default];
# networking.wg-quick.interfaces."wireguard".configFile = config.sops.secrets.wireguard.path;
vpnNamespaces."wg" = {
enable = true;
wireguardConfigFile = config.sops.secrets.wireguard.path;
accessibleFrom = ["192.168.0.0/24"];
# Forwarded to my vpn, for making things accessible from outside
openVPNPorts =
lib.range 23000 23010
|> map (num: {
port = num;
protocol = "both";
});
# From inside of the vpn namespace to outside of it, for making things inside accessible to LAN
portMappings = [];
};
systemd.services.wg.wantedBy = lib.mkForce [];
}

View file

@ -0,0 +1,18 @@
{config, ...}: {
services.restic.backups.rackserv = {
initialize = true;
inhibitsSleep = true;
repository = "rest:http://nixdesk:8000/rackserv";
passwordFile = config.sops.secrets.restic-password.path;
timerConfig = {
OnCalendar = "18:00";
Persistent = true;
RandomizedDelaySec = "1h";
};
pruneOpts = [
"--keep-daily 7"
"--keep-weekly 5"
"--keep-monthly 3"
];
};
}

43
hosts/rackserv/caddy.nix Normal file
View file

@ -0,0 +1,43 @@
{
vars,
config,
...
}: let
inherit (vars) domain;
hopper = "10.0.0.2";
in {
networking.firewall.allowedTCPPorts = [80 443];
security.acme = {
acceptTerms = true;
defaults.email = "xunuwu@gmail.com";
certs = {
"${domain}" = {
domain = "${domain}";
extraDomainNames = ["*.${domain}"];
dnsProvider = "cloudflare";
reloadServices = ["caddy.service"];
credentialFiles.CF_DNS_API_TOKEN_FILE = config.sops.secrets.cloudflare.path;
};
};
};
services.caddy = {
enable = true;
virtualHosts = {
misc = {
hostName = "${domain}";
serverAliases = ["*.${domain}"];
useACMEHost = domain;
extraConfig = ''
reverse_proxy ${hopper}
'';
};
other = {
extraConfig = ''
respond 404
'';
};
};
};
}

View file

@ -0,0 +1,44 @@
{
inputs,
systemProfiles,
...
}: {
imports =
[
"${inputs.nixpkgs}/nixos/modules/profiles/qemu-guest.nix"
inputs.impermanence.nixosModules.impermanence
inputs.disko.nixosModules.disko
./disk-config.nix
./fail2ban.nix
./wireguard-server.nix
./backups.nix
./caddy.nix
]
++ (map (x: systemProfiles + x) [
/core/security.nix
/core/tools.nix
/core/ssh.nix
/core/deploy.nix
/nix/default.nix
/network/tailscale.nix
/network/networkd.nix
]);
boot.loader.grub = {
efiSupport = true;
efiInstallAsRemovable = true;
};
networking.firewall.logRefusedConnections = false; # this spams my journal too much
hardware.enableRedistributableFirmware = true;
environment.persistence."/persist".enable = false;
networking.hostName = "rackserv";
nixpkgs.hostPlatform.system = "x86_64-linux";
system.stateVersion = "25.05";
}

View file

@ -0,0 +1,52 @@
{lib, ...}: {
disko.devices = {
disk.disk1 = {
device = lib.mkDefault "/dev/vda";
type = "disk";
content = {
type = "gpt";
partitions = {
boot = {
name = "boot";
size = "1M";
type = "EF02";
};
esp = {
name = "ESP";
size = "500M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";
mountpoint = "/boot";
};
};
root = {
name = "root";
size = "100%";
content = {
type = "lvm_pv";
vg = "pool";
};
};
};
};
};
lvm_vg = {
pool = {
type = "lvm_vg";
lvs = {
root = {
size = "100%FREE";
content = {
type = "filesystem";
format = "ext4";
mountpoint = "/";
mountOptions = ["defaults"];
};
};
};
};
};
};
}

View file

@ -0,0 +1,14 @@
{
services.openssh.startWhenNeeded = false; # i dont think this works with fail2ban
services.fail2ban = {
enable = true;
ignoreIP = ["100.64.0.0/10"]; # tailscale
bantime = "1h";
bantime-increment = {
enable = true;
maxtime = "168h";
factor = "4";
};
};
}

View file

@ -0,0 +1,111 @@
{
pkgs,
config,
lib,
...
}: {
networking.firewall = let
forwardPorts = {
"10.0.0.2" =
[24001 24002 24003]
|> map (n: {
protocols = ["tcp"];
port = n;
});
"10.0.0.3" =
lib.range 23000 23010
|> map (n: {
protocols = ["tcp" "udp"];
port = n;
});
"10.0.0.4" = [
{
protocols = ["tcp"];
port = 22000;
}
];
};
externalIp = "172.245.52.19";
b = builtins;
portsList = b.attrValues forwardPorts |> b.concatLists;
portsAndIpsList = lib.mapAttrsToList (n: v: map (x: x // {destinationIp = n;}) v) forwardPorts |> b.concatLists;
in {
allowedTCPPorts = b.filter (x: b.elem "tcp" x.protocols) portsList |> map (x: x.port);
allowedUDPPorts = [51820] ++ (b.filter (x: b.elem "udp" x.protocols) portsList |> map (x: x.port));
extraCommands =
portsAndIpsList
|> map (x:
x.protocols
|> map (protocol: ''
iptables -t nat -A PREROUTING -p ${protocol} -d ${externalIp} --dport ${toString x.port} -j DNAT --to-destination ${x.destinationIp}
''))
|> b.concatLists
|> b.concatStringsSep "\n";
extraStopCommands =
portsAndIpsList
|> map (x:
x.protocols
|> map (protocol: ''
iptables -t nat -D PREROUTING -p ${protocol} -d ${externalIp} --dport ${toString x.port} -j DNAT --to-destination ${x.destinationIp} || true
''))
|> b.concatLists
|> b.concatStringsSep "\n";
interfaces.wg0 = {
allowedUDPPorts = [53];
allowedTCPPorts = [53];
};
};
systemd.network.netdevs = {
"50-wg0" = {
netdevConfig = {
Kind = "wireguard";
Name = "wg0";
MTUBytes = "1420";
};
wireguardConfig = {
ListenPort = 51820;
PrivateKeyFile = config.sops.secrets.wireguard-privatekey.path;
RouteTable = "main";
};
wireguardPeers = [
{
# hopper
PublicKey = "P5W5/m9VnWcbdR6e3rs4Yars4Qb2rPjkRmCAbgja4Ug=";
AllowedIPs = ["10.0.0.2" "fd12:1e51:ca23::2"];
}
{
# nixdesk
PublicKey = "DMauL/fv08yXvVtyStsUfg/OM+ZJwMNvguQ59X/KU2Q=";
AllowedIPs = ["10.0.0.3" "fd12:1e51:ca23::3"];
}
{
# alka
PublicKey = "Q90dKQtQTu8RLgkPau7/Y5fY3PVstP0bL6ey3zrdS18=";
AllowedIPs = ["10.0.0.4" "fd12:1e51:ca23::3"];
}
];
};
};
systemd.network.networks.wg0 = {
matchConfig.Name = "wg0";
address = ["10.0.0.1/10" "fd12:1e51:ca23::1/64"];
networkConfig = {
IPMasquerade = "ipv4";
IPv4Forwarding = true;
};
};
services.dnsmasq = {
enable = true;
resolveLocalQueries = false;
settings = {
server = ["1.1.1.1" "8.8.8.8"];
interface = ["wg0"];
bind-interfaces = true;
};
};
}