Compare commits

...

36 commits
v0.1 ... master

Author SHA1 Message Date
baritone 3b3e6d4641 :) 2025-04-15 13:41:00 +02:00
baritone 4b8afb9a33 actually made web ui accessible from local network 2025-04-15 13:40:49 +02:00
baritone f940982b40 cleaned up config 2025-04-15 12:49:38 +02:00
baritone c6cd05576d removed redundant files 2025-04-15 12:06:41 +02:00
baritone 8a504a00b6 updated readme 2025-04-15 11:50:17 +02:00
baritone 2be4a5df9d added more comments to disko config 2025-04-15 11:30:05 +02:00
baritone 6dad7e836a re-added flood-for-transmission web ui for managing transmission.
This time its only available to the local network though
2025-04-15 11:23:02 +02:00
baritone 7f08d8e02f fixed website not starting on reboot 2025-04-01 23:11:24 +02:00
baritone 2fa176db17 bunch of stuff
zero downtime website deployment

forgejo runners

media folder stuff
2025-04-01 15:50:31 +02:00
baritone 9668273e5d hardlinks for incomplete downloads now works aswell 2025-04-01 10:40:49 +02:00
baritone 8b79a672db hardlinks now work. but at what cost? 2025-04-01 10:21:20 +02:00
baritone 10141b93e7 removed swp file 2025-04-01 09:56:21 +02:00
baritone 6ac9ddb8f9 migrated location of /media folder 2025-04-01 09:55:56 +02:00
baritone e6ce4b32c7 conduit not working 2025-03-31 00:50:17 +02:00
baritone aadea2bf48 added port 2025-03-31 00:16:54 +02:00
baritone d63c5d6364 added conduwuit 2025-03-31 00:13:43 +02:00
baritone 67146a509d refactored nginx config 2025-03-31 00:12:43 +02:00
baritone 7310892cf9 updated readme 2025-03-30 23:48:17 +02:00
baritone eb655915d0 added readme 2025-03-30 23:47:45 +02:00
baritone be4069c234 fixed website not automatically coming online after reboot 2025-03-30 22:10:42 +02:00
baritone 22aa9a9ae6 updated flake.lock 2025-03-30 21:54:52 +02:00
baritone 810e8b4086 changed vm timezone 2025-03-29 21:52:23 +01:00
baritone 3c7dc25094 fucking people locked me out of my torrent client by trying to brute force the password, cant have shit in this house 2025-03-28 14:56:18 +01:00
baritone 425dc9d60f when tf did this get here 2025-03-27 20:07:50 +01:00
baritone 11fcc2af53 fixed typo that broke the vm 2025-03-27 20:06:00 +01:00
baritone bca28a54e5 i put vaultwarden in the wrong spot lmao 2025-03-27 15:43:38 +01:00
baritone 8e1a53272b fixed typo 2025-03-27 15:41:33 +01:00
baritone 8d848d0c7b added more fail2ban fiters 2025-03-27 15:40:42 +01:00
baritone 871658d3e8 removed test file 2025-03-27 15:29:06 +01:00
baritone e0a1fceb84 fail2ban banning works now 2025-03-27 15:07:32 +01:00
baritone 80e689dc7b removed old unused service files 2025-03-27 14:18:07 +01:00
baritone 4da1bdac03 better logging for forgejo 2025-03-27 14:17:50 +01:00
baritone 8d6b83fec2 added transmission webui 2025-03-25 15:08:22 +01:00
baritone e97d5e7152 website 2025-03-25 12:09:39 +01:00
baritone 2897fb75a5 fixed zed emails 2025-03-20 10:17:26 +01:00
baritone fd6bcd05b6 Updated nginx configuration for vm and for undefined virtualHosts 2025-03-20 09:57:01 +01:00
29 changed files with 507 additions and 649 deletions

BIN
.README.md.swp Normal file

Binary file not shown.

68
README.md Normal file
View file

@ -0,0 +1,68 @@
# Server nixos configuration
This is the configuration running on my homebrew server.
It is perfect in and no other config is in any way shape or form better.
# Features
- Forgejo git instance
- Vaultwarden password manager
- Jellyfin media server
- Sonnar, Radarr, Jackett and transmission auto-torrenting setup in declarative [astro/microvm](https://github.com/astro/microvm.nix) virtual machine behind a VPN.
- ZFS RaidZ (Raid5) for data resilience with automated emails for failed drives
- Self-hosted mail server for sending emails
- Simple file structure for easy extension
# How to Install
## Prepare device
Install the [nixos boot ISO](https://nixos.org/download/) and run it on your device.
Once booted run
```sh
$ sudo passwd
$ ip a
```
to set a password for the `nix` user and display your ip.
Once done you can ssh into the device and generate a nix hardware config with
```sh
$ ssh nix@<ip>
$ sudo mkdir /mnt
$ sudo nixos-generate-config --root /mnt --no-filesystem
```
This will generate the file `/mnt/hardware-config.nix` that you can then copy to your
computer.
## Install configuration
### Prepare repo
1. Clone the repo
- `git clone https://git.spoodythe.one/spoody/server-configuration.git`
1. Copy the `hardware-configuration.nix` from your device to the repo folder.
- `scp nix@<ip>:/mnt/hardware-configuration.nix /path/to/repo/hardware-configuration.nix`
1. Add/change the device configuration in `flake.nix`
- This includes configuring which (if any) disks should be included in the raid array, as well as which disk is the main root disk.
### Install remotely
Run the command
```sh
nix run github:nix-community/nixos-anywhere -- --flake '.#<your configuration>' --target-host nixos@<ip>
```
This will automatically install the configuration to your server.
## Copy configuration to server
Once the configuration has been installed push your modified repo to a git service and pull it on the server.

View file

@ -8,26 +8,23 @@
./services/nginx.nix
./services/openssh.nix
./services/forgejo.nix
# ./services/nextcloud.nix
# ./services/seafile.nix
./services/vaultwarden.nix
./services/jellyfin.nix
./services/website.nix
./services/mailserver.nix
./services/fail2ban.nix
./services/zed.nix # IMPORTANTE
./services/auto-torrent.nix
./services/misc.nix
./services/website.nix
./modules/age.nix
./modules/git.nix
./modules/nix-settings.nix
./modules/zfs.nix
./modules/microvm.nix
(import ./modules/microvm.nix {routed = true;})
(import ./modules/networking-shared.nix {hostname = "server";})
(import ./modules/users.nix {main-user = "baritone";})
];
@ -42,21 +39,11 @@
# Enable microcode updates
hardware.enableRedistributableFirmware = true;
# programs.zsh = {
# enable = true;
# enableGlobalCompInit = true;
# shellAliases = {
# "nrb" = "sudo nixos-rebuild switch --flake /etc/nixos";
# };
# };
environment.systemPackages = with pkgs; [
wget
curl
git
vim
inputs.agenix.packages."${system}".default
];
programs.nano.enable = false;

View file

@ -8,12 +8,14 @@
disko.devices = {
disk =
{
# Main SSD where /nix/store /etc /boot etc. is located.
main = {
type = "disk";
device = root-disk;
content = {
type = "gpt";
partitions = {
# Define a 512 megabyte boot partition
boot = {
size = "512M";
type = "EF00";
@ -24,6 +26,7 @@
mountOptions = ["umask=0077"];
};
};
# If the swap argument is defined then create a swap partition of the given size
swap = lib.mkIf (swap-size != -1) {
size = swap-size;
content = {
@ -32,6 +35,7 @@
resumeDevice = true;
};
};
# Finally allocate the rest of the disk as a zfs device
root = {
size = "100%";
content = {
@ -62,12 +66,13 @@
});
zpool = {
# Set up zroot pool
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
acltype = "posixacl";
xattr = "sa";
acltype = "posixacl"; # POSIX compliant file permissions
xattr = "sa"; # Store attributes in inodes to reduce disk reads
};
datasets = {
@ -83,16 +88,17 @@
};
};
# If any disks where defined to be in the raid we set up the raid5 pool
raid5 = lib.mkIf (builtins.length raid-disks > 0) {
type = "zpool";
mode = "raidz";
rootFsOptions = {
compression = "zstd";
mountpoint = "none";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
compression = "zstd"; # Compress filesystem for more storage
mountpoint = "none"; # Dont mount the zfs pool anywhere
acltype = "posixacl"; # POSIX compliant file permissions
xattr = "sa"; # Store attributes in inodes to reduce disk reads
"com.sun:auto-snapshot" = "true"; # Never a bad idea to have backups
};
datasets = {
@ -112,10 +118,10 @@
type = "zfs_fs";
mountpoint = "/opt";
};
media = {
type = "zfs_fs";
mountpoint = "/media";
};
# media = {
# type = "zfs_fs";
# mountpoint = "/media";
# };
};
};
};

View file

@ -143,11 +143,11 @@
]
},
"locked": {
"lastModified": 1742326330,
"narHash": "sha256-Tumt3tcMXJniSh7tw2gW+WAnVLeB3WWm+E+yYFnLBXo=",
"lastModified": 1743295846,
"narHash": "sha256-hKKz07d4RV9gzxzE5Qu3RQWX8a7XpzRrP5timoxoGRQ=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "22a36aa709de7dd42b562a433b9cefecf104a6ee",
"rev": "717030011980e9eb31eb8ce011261dd532bce92c",
"type": "github"
},
"original": {
@ -165,11 +165,11 @@
"spectrum": "spectrum"
},
"locked": {
"lastModified": 1741893540,
"narHash": "sha256-NEsR+FQ+AsY4cOZ3OL69JVdPTAYzSzBqeAhHGRRuDGk=",
"lastModified": 1743083165,
"narHash": "sha256-Fz7AiCJWtoWZ2guJwO3B1h3RuJxYWaCzFIqY0Kmkyrs=",
"owner": "astro",
"repo": "microvm.nix",
"rev": "5bbc126db87b5ffc36394df630eead403c48fac8",
"rev": "773d5a04e2e10ca7b412270dea11276a496e1b61",
"type": "github"
},
"original": {
@ -180,11 +180,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1742288794,
"narHash": "sha256-Txwa5uO+qpQXrNG4eumPSD+hHzzYi/CdaM80M9XRLCo=",
"lastModified": 1743095683,
"narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b6eaf97c6960d97350c584de1b6dcff03c9daf42",
"rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6",
"type": "github"
},
"original": {

View file

@ -1,4 +1,15 @@
{config, ...}: {
# This is where the age files go
# age.secrets.nextcloud.file = ../secrets/nextcloud.age;
{
config,
pkgs,
inputs,
...
}: {
age.identityPaths = [
"/home/baritone/.ssh/id_ed25519"
];
age.secrets = {
forgejo-runner-token.file = ../secrets/forgejo-runner-token.age;
};
environment.systemPackages = [inputs.agenix.packages."${pkgs.system}".default];
}

View file

@ -1,4 +1,4 @@
{routed ? false}: {
{
pkgs,
config,
...

View file

@ -34,5 +34,8 @@
"nrb" = "sudo nixos-rebuild switch --flake /etc/nixos";
"vmr" = "rm ~/.ssh/known_hosts; ssh root@10.0.0.1";
};
shellAliases = {
"fucking" = "sudo";
};
};
}

View file

@ -8,8 +8,4 @@
environment.systemPackages = with pkgs; [
zfs
];
# Microvm be fucking shit up
# fileSystems."/nix/store".fsType = lib.mkForce "zfs";
# fileSystems."/nix/store".device = lib.mkForce "zroot/nix/store";
}

View file

@ -0,0 +1,5 @@
age-encryption.org/v1
-> ssh-ed25519 rgw77A B/0cZTeHin54r5rLOPknrID5HqDdXUP1f3n6q28B11s
I8lGmJNvkJQQUlWD8fbCt+IuYisKhoVb59ulqQZsGJw
--- 5qC6VjzF8ldbN+AuD+G5y8ABxl+hqD1mt8QyEgVxrjI
ÊcåºÃMï·Ö UÞ;œm0à6kª°;‚ª-sŸKÿÀÔWM;·ã_Š<5F>&<26>Yk\èŒMNê1¨qO¨¤hM7k A1m•1

View file

@ -2,4 +2,5 @@ let
public-keys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIC562Woe/yT/3dNVceN9rKPJQcvgTFzIhJVdVGv7sqn1 baritone@server"];
in {
"nextcloud.age".publicKeys = public-keys;
"forgejo-runner-token.age".publicKeys = public-keys;
}

View file

@ -5,14 +5,28 @@
}: let
lib = pkgs.lib;
host = "10.0.${toString vm-index}.1";
port = 8989;
ports = {
sonarr.port = 8989;
radarr.port = 7878;
jackett.port = 9117;
};
vm-index = 1;
vm-mac = "02:00:00:00:00:02";
vm-name = "auto-torrent";
vpn-endpoint = "193.32.248.70";
enable-services = true;
wg-config = "/mnt/wg.conf";
jellyfin-gid = 989;
ports-list = (pkgs.lib.attrsets.mapAttrsToList (name: value: value.port) ports) ++ [9091];
in {
microvm.autostart = [vm-name];
imports = [
./nginx.nix
../modules/microvm.nix
];
users.extraUsers.microvm.extraGroups = [
"jellyfin" # access to media folder
@ -20,9 +34,7 @@ in {
system.activationScripts."make-${vm-name}-data-dir" = lib.stringAfter ["var"] ''
mkdir -p /var/lib/${vm-name}
chmod -R 777 /var/lib/${vm-name}
chown -R microvm /var/lib/${vm-name}
chmod -R 777 /media
# chown -R microvm:jellyfin /var/lib/${vm-name}
'';
microvm.vms.${vm-name} = {
@ -36,6 +48,8 @@ in {
}
];
time.timeZone = "Europe/Copenhagen";
# 1gb of memory
microvm.mem = 1024;
@ -55,7 +69,7 @@ in {
}
{
tag = "media-dir";
source = "/media";
source = "/var/lib/media";
mountPoint = "/media";
inherit proto;
}
@ -101,7 +115,7 @@ in {
serviceConfig = {
type = "oneshot";
ExecStart = pkgs.writeShellScript "wgconf.sh" ''
${pkgs.wireguard-tools}/bin/wg-quick up /mnt/wg.conf
${pkgs.wireguard-tools}/bin/wg-quick up ${wg-config}
'';
RemainAfterExit = "yes";
};
@ -119,6 +133,18 @@ in {
programs.nano.enable = lib.mkForce false;
programs.vim.enable = true;
# Fix permissions with groups
users.users = {
sonarr.extraGroups = ["jellyfin"];
radarr.extraGroups = ["jellyfin"];
transmission.extraGroups = ["jellyfin"];
jackett.extraGroups = ["jellyfin"];
};
users.groups.jellyfin = {
gid = jellyfin-gid;
};
# Services
services.sonarr = pkgs.lib.mkIf enable-services {
enable = true;
@ -138,11 +164,25 @@ in {
services.transmission = pkgs.lib.mkIf enable-services {
enable = true;
openFirewall = true;
openPeerPorts = true;
openRPCPort = true;
home = "/mnt/transmission";
settings.download-dir = "/mnt/transmission";
settings.incomplete-dir = "/mnt/transmission/.incomplete";
webHome = "${pkgs.flood-for-transmission}";
settings = {
download-dir = "/media/.transmission/";
incomplete-dir = "/media/.transmission/.incomplete";
peer-port-random-low = 65500;
peer-port-random-high = 65535;
peer-port-random-on-start = true;
download-queue-enabled = false;
rpc-authentication-required = false;
rpc-bind-address = "0.0.0.0";
rpc-host-whitelist = builtins.concatStringsSep "," [host "127.0.0.1" "10.0.101.10" "10.0.${toString vm-index}.254"];
rpc-whitelist = builtins.concatStringsSep "," [host "127.0.0.1" "10.0.101.10" "10.0.${toString vm-index}.254"];
};
downloadDirPermissions = "775";
performanceNetParameters = true;
};
# debugging
@ -169,13 +209,16 @@ in {
};
};
# Sonarr
services.nginx.virtualHosts."sonarr.spoodythe.one" = {
# Add virtual hosts for all the different services running in the vm
services.nginx.virtualHosts = builtins.listToAttrs (pkgs.lib.attrsets.mapAttrsToList (
name: value: {
name = "${name}.spoodythe.one";
value = {
addSSL = true;
enableACME = true;
listen = [
{
inherit port;
port = value.port;
addr = "0.0.0.0";
ssl = false;
}
@ -186,56 +229,21 @@ in {
}
];
locations."/" = {
proxyPass = "http://${host}:${toString port}";
proxyPass = "http://${host}:${toString value.port}";
};
};
# Radarr
services.nginx.virtualHosts."radarr.spoodythe.one" = let
port = 7878;
in {
addSSL = true;
enableACME = true;
listen = [
{
inherit port;
addr = "0.0.0.0";
ssl = false;
}
)
ports);
# Forward transmission web port to vm
networking.nat.forwardPorts = [
{
port = 443;
addr = "0.0.0.0";
ssl = true;
sourcePort = 9091;
destination = "${host}:9091";
}
];
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
# Jackett
services.nginx.virtualHosts."jackett.spoodythe.one" = let
port = 9117;
in {
addSSL = true;
enableACME = true;
listen = [
{
inherit port;
addr = "0.0.0.0";
ssl = false;
}
{
port = 443;
addr = "0.0.0.0";
ssl = true;
}
];
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
networking.firewall.allowedTCPPorts = [port 9117 7878 9696 80 433];
networking.firewall.allowedUDPPorts = [port 9117 7878 9696 80 433];
networking.firewall.allowedTCPPorts = ports-list;
networking.firewall.allowedUDPPorts = ports-list;
}

56
services/conduwuit.nix Normal file
View file

@ -0,0 +1,56 @@
{...}: let
port = 6167;
host = "127.0.0.1";
domain = "matrix.spoodythe.one";
mb = 1024 * 1024;
max-request-size = 20;
in {
imports = [
./nginx.nix
];
services.conduwuit = {
enable = true;
settings = {
global = {
port = [port];
address = [host];
server_name = domain;
max_request_size = max-request-size * mb;
};
};
};
services.nginx.virtualHosts."${domain}" = {
forceSSL = true;
enableACME = true;
listen = [
{
port = 443;
ssl = true;
addr = "0.0.0.0";
}
{
port = 8448;
ssl = true;
addr = "0.0.0.0";
}
];
locations."/" = {
proxyPass = "http://${host}:${toString port}";
extraConfig = ''
client_max_body_size ${toString max-request-size}M;
'';
};
locations."/_matrix" = {
proxyPass = "http://${host}:${toString port}";
extraConfig = ''
client_max_body_size ${toString max-request-size}M;
'';
};
};
# Open port so i can access it on my local network
networking.firewall.allowedTCPPorts = [port 8448];
networking.firewall.allowedUDPPorts = [port 8448];
}

6
services/docker.nix Normal file
View file

@ -0,0 +1,6 @@
{...}: {
virtualisation.docker = {
enable = true;
storageDriver = "zfs";
};
}

View file

@ -3,8 +3,34 @@
lib,
config,
...
}: {
services.fail2ban = {
}: let
jails = {
forgejo = mkJail "forgejo" "^.*Failed authentication attempt for .* from <ADDR>.*$";
jellyfin = mkJail "jellyfin" "^.*Authentication request for .* has been denied \\(IP: <ADDR>\\)\\.";
sonarr = mkJail "sonarr" "^.*Auth-Failure ip <ADDR> username.*$";
radarr = mkJail "sonarr" "^.*Auth-Failure ip <ADDR> username.*$";
vaultwarden = mkJail "vaultwarden" "^.*Username or password is incorrect. Try again. IP: <ADDR>\\. Username: .*$";
};
mkJail = name: filter: {
jail.settings = {
enabled = true;
filter = name;
backend = "systemd";
port = "80,443";
maxretry = 8;
bantime = "24h";
findtime = "30m";
journalmatch = "_SYSTEMD_UNIT=${name}.service";
};
filter = ''
[Definition]
failregex = ${filter}
'';
};
in {
services.fail2ban = let
in {
enable = true;
bantime = "24h";
@ -15,7 +41,8 @@
overalljails = true;
};
jails = {
jails =
{
dovecot = lib.mkIf config.services.dovecot2.enable {
settings = {
# block IPs which failed to log-in
@ -24,31 +51,24 @@
maxretry = 3;
};
};
jellyfin = lib.mkIf config.services.jellyfin.enable {
settings = {
filter = "jellyfin";
backend = "auto";
enabled = true;
port = "8096,8920";
maxretry = 3;
bantime = 86400;
findtime = 43200;
logpath = "/var/lib/jellyfin/log/*.log";
};
};
};
}
// (lib.attrsets.mapAttrs (name: value: value.jail) jails);
};
environment.etc = {
"fail2ban/filter.d/jellyfin.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
[Definition]
failregex = ^.*Authentication request for .* has been denied \(IP: <ADDR>\)\.
'');
environment.etc =
{
# Defines a filter that detects URL probing by reading the Nginx access log
"fail2ban/filter.d/nginx-url-probe.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
"fail2ban/filter.d/nginx-url-probe.local".text = lib.mkDefault (lib.mkAfter ''
[Definition]
failregex = ^<HOST>.*(GET /(wp-|admin|boaform|phpmyadmin|\.env|\.git)|\.(dll|so|cfm|asp)|(\?|&)(=PHPB8B5F2A0-3C92-11d3-A3A9-4C7B08C10000|=PHPE9568F36-D428-11d2-A769-00AA001ACF42|=PHPE9568F35-D428-11d2-A769-00AA001ACF42|=PHPE9568F34-D428-11d2-A769-00AA001ACF42)|\\x[0-9a-zA-Z]{2})
'');
};
}
// (with lib.attrsets;
mapAttrs' (
name: value:
nameValuePair
"fail2ban/filter.d/${name}.local"
{text = lib.mkDefault (lib.mkAfter value.filter);}
)
jails);
}

View file

@ -2,6 +2,10 @@
host = "127.0.0.1";
port = 6969;
in {
imports = [
./nginx.nix
./gitea-actions-runner.nix
];
services.forgejo = {
enable = true;
@ -13,6 +17,10 @@ in {
APP_NAME = "An idiot admires complexity. A genius admires simplicity";
};
repository = {
ENABLE_PUSH_CREATE_USER = true;
};
server = {
DOMAIN = "git.spoodythe.one";
HTTP_PORT = port;
@ -38,8 +46,4 @@ in {
'';
};
};
# Open port 80 and 443 for reverse proxy
networking.firewall.allowedTCPPorts = [80 443];
networking.firewall.allowedUDPPorts = [80 443];
}

View file

@ -0,0 +1,93 @@
{
config,
pkgs,
...
}: {
imports = [
./docker.nix
../modules/age.nix
];
services.gitea-actions-runner = {
package = pkgs.forgejo-runner;
instances = {
agurk = {
enable = true;
name = "agurk";
url = "http://127.0.0.1:${toString config.services.forgejo.settings.server.HTTP_PORT}";
tokenFile = config.age.secrets.forgejo-runner-token.path;
labels = [
"native:host"
];
hostPackages = pkgs.lib.attrValues {
inherit
(pkgs)
nix
nodejs
git
bash
fd
ripgrep
openssh
;
};
settings = {
log.level = "info";
runner = {
file = ".runner";
capacity = 2;
timeout = "3h";
insecure = false;
fetch_timeout = "5s";
fetch_interval = "2s";
};
};
};
hval = pkgs.lib.mkIf false {
enable = true;
name = "hval";
url = "http://host.docker.internal:${toString config.services.forgejo.settings.server.HTTP_PORT}";
tokenFile = config.age.secrets.forgejo-runner-token.path;
labels = [
docker:docker://node:16-bullseye
];
hostPackages = pkgs.lib.attrValues {
inherit
(pkgs)
nix
nodejs
git
bash
fd
ripgrep
openssh
;
};
settings = {
log.level = "info";
runner = {
file = ".runner";
capacity = 2;
timeout = "3h";
insecure = false;
fetch_timeout = "5s";
fetch_interval = "2s";
};
};
};
};
};
system.activationScripts."make-gitea-runner-dir" = pkgs.lib.stringAfter ["var"] ''
mkdir -p /var/lib/gitea-runner/
# chown -R microvm:jellyfin /var/lib/gitea-runner
'';
# systemd.services = builtins.listToAttrs (pkgs.lib.attrsets.mapAttrsToList (
# name: value: {
# name = "gitea-runner-${name}";
# value = {
# serviceConfig.ReadWritePaths = "/srv/web";
# };
# }
# ) config.services.gitea-actions-runner.instances);
}

View file

@ -1,44 +0,0 @@
{...}: let
host = "127.0.0.1";
port = 8082;
in {
services.homepage-dashboard = {
enable = true;
listenPort = port;
openFirewall = false;
widgets = [
{
resources = {
cpu = true;
disk = "/";
memory = true;
};
}
];
services = [
{
"WebUI" = [
{
"Jellyfin" = {
description = "Jellyfin";
href = "https://media.spoodythe.one";
};
}
];
}
];
};
services.nginx.virtualHosts."dashboard.spoodythe.one" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
# Open port 80 and 443 for reverse proxy
networking.firewall.allowedTCPPorts = [80 443];
networking.firewall.allowedUDPPorts = [80 443];
}

View file

@ -6,35 +6,9 @@
}: let
host = "127.0.0.1";
port = 8096;
jellyfin =
if config.services.mullvad-vpn.enable == true
then
pkgs.callPackage ({...}:
pkgs.stdenv.mkDerivation {
pname = "jellyfin-excluded";
version = "1.0.0";
phases = ["installPhase"];
buildInputs = [pkgs.jellyfin];
# Define the install phase
installPhase = ''
mkdir -p $out/bin
# Create a wrapper script
echo "${pkgs.mullvad-vpn}/bin/mullvad-exclude ${pkgs.jellyfin}/bin/jellyfin \"$@\"" > $out/bin/jellyfin-excluded
chmod +x $out/bin/jellyfin-excluded
'';
# Specify the output
meta = with pkgs.lib; {
description = "A wrapper for the hello command";
mainProgram = "jellyfin-excluded";
license = licenses.mit;
};
}) {}
else pkgs.jellyfin;
gid = 989;
in {
imports = [./nginx.nix];
# Enable VAAPI
config.nixpkgs.config.packageOverrides = pkgs: {
vaapiIntel = pkgs.vaapiIntel.override {enableHybridCodec = true;};
@ -50,17 +24,19 @@ in {
];
};
# Set declarative gid
config.users.groups.jellyfin.gid = gid;
# Create folder for media
config.system.activationScripts."jellyfinMediaFolder" = lib.stringAfter ["var"] ''
mkdir -p /media
chmod -R 775 /media
chown -R jellyfin:jellyfin /media
mkdir -p /var/lib/media
chmod 770 /var/lib/media
chown -R jellyfin:jellyfin /var/lib/media
'';
# Enable Jellyfin
config.services.jellyfin = {
enable = true;
package = jellyfin;
openFirewall = false; # We want jellyfin behind a reverse proxy
};
@ -78,6 +54,7 @@ in {
enableACME = true;
locations."/" = {
proxyPass = "http://${host}:${toString port}";
proxyWebsockets = true;
extraConfig = ''
# Websocket support
proxy_set_header Upgrade $http_upgrade;
@ -89,7 +66,7 @@ in {
};
};
# Open port 80 and 443 for reverse proxy
config.networking.firewall.allowedTCPPorts = [port 80 443];
config.networking.firewall.allowedUDPPorts = [port 80 443];
# Open ports for local network access
config.networking.firewall.allowedTCPPorts = [port];
config.networking.firewall.allowedUDPPorts = [port];
}

View file

@ -5,17 +5,12 @@
recommendedOptimisation = true;
recommendedProxySettings = true;
recommendedTlsSettings = true;
virtualHosts."spoodythe.one" = {
forceSSL = true;
enableACME = true;
default = true;
locations."/" = {
return = 404;
};
};
};
security.acme.acceptTerms = true;
security.acme.defaults.email = "snorre@altschul.dk";
# Open port 80 and 443 for reverse proxy
networking.firewall.allowedTCPPorts = [80 443];
networking.firewall.allowedUDPPorts = [80 443];
}

View file

@ -1,18 +0,0 @@
{pkgs, ...}: let
port = 9091;
in {
# services.rtorrent = {
# enable = true;
# dataDir = "/mnt/rtorrent";
# user = "rtorrent";
# group = "rtorrent";
# package = pkgs.jesec-rtorrent;
# inherit port;
# };
services.transmission = {
enable = true;
openFirewall = false;
settings.rpc-port = port;
};
}

View file

@ -1,28 +0,0 @@
{
config,
pkgs,
...
}: let
host = "127.0.0.1";
port = 8008;
in {
services.seafile = {
enable = true;
adminEmail = "snorre@altschul.dk";
seahubAddress = "http://${host}:${toString port}";
# seafileSettings.fileserver = {
# inherit host port;
# };
};
services.nginx.virtualHosts."files.spoodythe.one" = {
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
# Open port 80 and 443 for reverse proxy
networking.firewall.allowedTCPPorts = [80 443];
networking.firewall.allowedUDPPorts = [80 443];
}

View file

@ -1,189 +0,0 @@
{
pkgs,
lib,
config,
...
}: let
host = "127.0.0.1";
port = 8989;
vm-index = 1;
vm-mac = "00:00:00:00:00:01";
vm-name = "necoarc";
in {
config.microvm.autostart = [vm-name];
config.users.extraUsers.microvm.extraGroups = [
"jellyfin" # access to media folder
];
config.system.activationScripts."make${vm-name}DataDir" = lib.stringAfter ["var"] ''
mkdir -p /var/lib/${vm-name}
chmod -R 777 /var/lib/${vm-name}
chown -R microvm /var/lib/${vm-name}
chmod -R 777 /media
'';
# config.networking.wireguard.enable = true;
# # config.boot.extraModulePackages = [config.boot.kernelPackages.wireguard];
# config.networking.wireguard.interfaces.wg0 = {
# ips = ["10.75.60.108/32"];
# listenPort = 51820;
# privateKeyFile = "${./wireguard-secret}";
# peers = [
# {
# publicKey = "TPAIPTgu9jIitgX1Bz5xMCZJ9pRRZTdtZEOIxArO0Hc=";
# endpoint = "185.254.75.4:51820";
# allowedIPs = ["0.0.0.0/0"];
# persistentKeepalive = 25;
# }
# ];
# };
# config.systemd.network.networks.wg0 = {
# matchConfig.Name = "wg0";
# address = ["10.0.1.${toString vm-index}/24"];
# networkConfig = {
# IPMasquerade = "ipv4";
# IPv4Forwarding = true;
# };
# };
config.microvm.vms.${vm-name} = {
config = {config, ...}: {
system.stateVersion = "24.11";
# Storage share configuration
microvm.shares = [
{
tag = "ro-store";
source = "/nix/store";
mountPoint = "/nix/.ro-store";
}
{
tag = "data-dir";
source = "/var/lib/${vm-name}";
mountPoint = "/mnt";
proto = "virtiofs";
}
{
tag = "media-dir";
source = "/media/shows";
mountPoint = "/media/shows";
proto = "virtiofs";
}
];
# Allow the service to use the share
system.activationScripts."chownDataDir" = lib.stringAfter ["var"] ''
mkdir -p /mnt
chmod -R 770 /mnt
chown -R sonarr:sonarr /mnt
'';
systemd.services.sonarr.serviceConfig.ExecStartPre = "/run/current-system/sw/bin/sleep 5";
systemd.services.rutorrent.serviceConfig.ExecStartPre = "/run/current-system/sw/bin/sleep 5";
microvm.hypervisor = "qemu";
# VM Networking
microvm.interfaces = [
{
id = "vm${toString vm-index}";
type = "tap";
mac = vm-mac;
}
];
networking.useNetworkd = true;
systemd.network.networks."10-eth" = {
matchConfig.MACAddress = vm-mac;
address = [
"10.0.0.${toString vm-index}/32"
];
routes = [
# Host Route
{
Destination = "10.0.0.0/32";
GatewayOnLink = true;
}
# Default route
{
Destination = "0.0.0.0/0";
Gateway = "10.0.0.0";
GatewayOnLink = true;
}
];
networkConfig = {
DNS = [
"9.9.9.9"
"8.8.8.8"
"8.8.4.4"
];
};
};
networking.useDHCP = false;
networking.nameservers = [
"10.0.101.1"
"8.8.8.8"
"8.8.4.4"
];
programs.nano.enable = lib.mkForce false;
programs.vim.enable = true;
# Services
services.sonarr = {
enable = true;
openFirewall = true;
dataDir = "/mnt/sonarr";
};
services.rtorrent = {
enable = true;
dataDir = "/mnt/rtorrent";
user = "rtorrent";
group = "rtorrent";
port = 9999;
};
# Debug user
users.users."root" = {
password = "1234";
};
environment.systemPackages = [pkgs.dig];
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "yes";
AllowUsers = null;
PasswordAuthentication = true;
KbdInteractiveAuthentication = true;
};
};
};
};
config.networking.firewall.allowedTCPPorts = [port 80 433];
config.networking.firewall.allowedUDPPorts = [port 80 433];
config.services.nginx.virtualHosts."sonarr.spoodythe.one" = {
addSSL = true;
enableACME = true;
listen = [
{
port = 8989;
addr = "0.0.0.0";
ssl = false;
}
{
port = 443;
addr = "0.0.0.0";
ssl = true;
}
];
locations."/" = {
proxyPass = "http://10.0.0.${toString vm-index}:${toString port}";
};
};
}

View file

@ -1,25 +0,0 @@
{...}: let
host = "127.0.0.1";
port = 8989;
in {
users.extraUsers.sonarr.extraGroups = ["jellyfin"]; # Access to the media folder
services.sonarr = {
enable = true;
openFirewall = true;
};
services.rtorrent = {
enable = true;
dataDir = "/mnt/rtorrent";
user = "rtorrent";
group = "rtorrent";
port = 9999;
};
services.nginx.virtualHosts."sonarr.spoodythe.one" = {
addSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
}

View file

@ -2,6 +2,7 @@
host = "127.0.0.1";
port = 8222;
in {
imports = [./nginx.nix];
services.vaultwarden = {
enable = true;
@ -24,9 +25,6 @@ in {
};
};
networking.firewall.allowedTCPPorts = [port];
networking.firewall.allowedUDPPorts = [port];
services.nginx.virtualHosts."vaultwarden.spoodythe.one" = {
enableACME = true;
forceSSL = true;

View file

@ -1,133 +0,0 @@
{
config,
pkgs,
...
}: let
vm-index = 1;
vm-mac = "02:00:00:00:00:02";
in {
config.microvm.vms."vm-test" = {
config = {...}: {
microvm.interfaces = [
{
id = "vm${toString vm-index}";
type = "tap";
mac = vm-mac;
}
];
microvm.shares = [
{
tag = "ro-store";
source = "/nix/store";
mountPoint = "/nix/.ro-store";
}
];
networking.useNetworkd = true;
networking.usePredictableInterfaceNames = false;
systemd.network.networks."10-eth" = {
matchConfig.MACAddress = vm-mac;
address = [
"10.0.0.${toString vm-index}/32"
];
routes = [
# Host Route
{
Destination = "10.0.0.0/32";
GatewayOnLink = true;
}
# Default route
{
Destination = "0.0.0.0/0";
Gateway = "10.0.0.0";
GatewayOnLink = true;
}
];
networkConfig = {
DNS = [
"9.9.9.9"
"8.8.8.8"
"8.8.4.4"
];
};
};
networking.useDHCP = false;
networking.nameservers = [
"10.0.101.1"
"8.8.8.8"
"8.8.4.4"
];
systemd.services."wireguard-kill-switch" = {
description = "Wireguard Kill Switch";
after = ["network-online.target"];
wants = ["network-online.target"];
wantedBy = ["multi-user.target"];
serviceConfig = {
type = "oneshot";
ExecStart = pkgs.writeShellScript "wgconf.sh" ''
# Stay a while and listen
# ${pkgs.toybox}/bin/sleep 5
# Route local traffic through wg0 except local traffic
${pkgs.iproute2}/bin/ip route add 10.0.0.0/32 dev eth0 && \
${pkgs.iproute2}/bin/ip route add 0.0.0.0/1 dev wg0
# Block all traffic that isnt local or through the vpn
${pkgs.iptables}/bin/iptables -I OUTPUT ! -o wg0 -m mark ! --mark 42 -m addrtype ! --dst-type LOCAL ! -d 10.0.0.0/32 -j REJECT
'';
RemainAfterExit = "yes";
};
};
networking.wireguard.enable = true;
systemd.network = {
netdevs."10-wg0" = {
netdevConfig = {
Kind = "wireguard";
Name = "wg0";
MTUBytes = "1300";
};
wireguardConfig = {
PrivateKeyFile = "${./wireguard-secret}";
FirewallMark = 42;
ListenPort = 51820;
};
wireguardPeers = [
{
PublicKey = "0qSP0VxoIhEhRK+fAHVvmfRdjPs2DmmpOCNLFP/7cGw=";
AllowedIPs = ["0.0.0.0/0"];
Endpoint = "193.32.248.66:51820";
# PersistentKeepalive = 25;
}
];
};
networks."wg0" = {
matchConfig.Name = "wg0";
address = [
" 10.65.241.123/32"
];
DHCP = "no";
dns = ["10.64.0.1"];
gateway = [
"10.0.0.0"
];
};
};
users.users.root = {
password = "1234";
};
services.openssh = {
enable = true;
settings = {
PermitRootLogin = "yes";
AllowUsers = null;
PasswordAuthentication = true;
KbdInteractiveAuthentication = true;
};
};
};
};
}

View file

@ -1,4 +0,0 @@
{pkgs, ...}: {
services.mullvad-vpn.enable = true;
environment.systemPackages = [pkgs.wireguard-tools];
}

View file

@ -5,12 +5,77 @@
}: let
host = "127.0.0.1";
port = 8080;
user = "website-host-user";
repo = "https://git.spoodythe.one/spoody/website";
in {
imports = [./nginx.nix];
services.nginx.virtualHosts."spoodythe.one" = {
enableACME = true;
forceSSL = true;
default = true;
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
users.users."${user}" = {
isSystemUser = true;
home = "/home/${user}";
createHome = true;
group = "${user}";
shell = pkgs.bash;
openssh.authorizedKeys.keys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG90JNokb4/4DZ/2tHS8Lj/jq+UiA0B2g+MFeM8KuA6Y website-host-user@server"
];
};
users.groups."${user}" = {};
security.sudo.extraRules = [
{
users = ["${user}"];
commands = [
{
command = "/run/current-system/sw/bin/systemctl restart rebuild-website.service";
options = ["SETENV" "NOPASSWD"];
}
{
command = "/run/current-system/sw/bin/systemd-run -d --uid website-host-user ./webbisitey";
options = ["SETENV" "NOPASSWD"];
}
];
}
];
systemd.services."rebuild-website" = {
description = "Service for running my website";
wants = ["network-online.target"];
after = ["network-online.target"];
wantedBy = ["multi-user.target"];
script = ''
echo "Sleeping for 25 seconds to allow forgejo to start"
${pkgs.busybox}/bin/sleep 25
echo "Making temporary folder"
path=$(mktemp -d)
echo "Cloning repo"
${pkgs.git}/bin/git clone ${repo} "$path"
echo "cd $path"
cd "$path"
echo "[nix build] Rebuilding website flake..."
${pkgs.nix}/bin/nix build # build
echo "[killall] Killing previous website process"
${pkgs.killall}/bin/killall webbisitey || true # stop old website
echo "Changing directory to build result..."
cd result/bin # cd into result folder
echo "[systemd-run] Running webbisitey-wrapped..."
/run/wrappers/bin/sudo /run/current-system/sw/bin/systemd-run -d --uid ${user} ./webbisitey # run new website
'';
serviceConfig = {
Type = "oneshot";
RemainsAfterExit = true;
User = user;
WorkingDirectory = "/tmp";
};
};
}

View file

@ -1,11 +1,11 @@
{...}: {
{pkgs, ...}: {
services.zfs.zed = {
enableMail = true;
settings = {
ZED_DEBUG_LOG = "/tmp/zed.debug.log";
ZED_EMAIL_ADDR = "snorre@altschul.dk";
ZED_EMAIL_PROG = "sendmail";
ZED_EMAIL_OPTS = "-s '@SUBJECT@' @ADDRESS@";
ZED_EMAIL_PROG = "${pkgs.postfix}/bin/sendmail";
ZED_EMAIL_OPTS = " @ADDRESS@";
ZED_LOCKDIR = "/var/lock";
ZED_NOTIFY_INTERVAL_SECS = 3600;