Compare commits

..

No commits in common. "master" and "v0.1" have entirely different histories.
master ... v0.1

29 changed files with 651 additions and 509 deletions

Binary file not shown.

View file

@ -1,68 +0,0 @@
# 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,23 +8,26 @@
./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
./services/zed.nix # IMPORTANTE
./services/auto-torrent.nix
./services/misc.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";})
];
@ -39,11 +42,21 @@
# 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,14 +8,12 @@
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";
@ -26,7 +24,6 @@
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 = {
@ -35,7 +32,6 @@
resumeDevice = true;
};
};
# Finally allocate the rest of the disk as a zfs device
root = {
size = "100%";
content = {
@ -66,13 +62,12 @@
});
zpool = {
# Set up zroot pool
zroot = {
type = "zpool";
rootFsOptions = {
mountpoint = "none";
acltype = "posixacl"; # POSIX compliant file permissions
xattr = "sa"; # Store attributes in inodes to reduce disk reads
acltype = "posixacl";
xattr = "sa";
};
datasets = {
@ -88,17 +83,16 @@
};
};
# 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"; # 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
compression = "zstd";
mountpoint = "none";
acltype = "posixacl";
xattr = "sa";
"com.sun:auto-snapshot" = "true";
};
datasets = {
@ -118,10 +112,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": 1743295846,
"narHash": "sha256-hKKz07d4RV9gzxzE5Qu3RQWX8a7XpzRrP5timoxoGRQ=",
"lastModified": 1742326330,
"narHash": "sha256-Tumt3tcMXJniSh7tw2gW+WAnVLeB3WWm+E+yYFnLBXo=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "717030011980e9eb31eb8ce011261dd532bce92c",
"rev": "22a36aa709de7dd42b562a433b9cefecf104a6ee",
"type": "github"
},
"original": {
@ -165,11 +165,11 @@
"spectrum": "spectrum"
},
"locked": {
"lastModified": 1743083165,
"narHash": "sha256-Fz7AiCJWtoWZ2guJwO3B1h3RuJxYWaCzFIqY0Kmkyrs=",
"lastModified": 1741893540,
"narHash": "sha256-NEsR+FQ+AsY4cOZ3OL69JVdPTAYzSzBqeAhHGRRuDGk=",
"owner": "astro",
"repo": "microvm.nix",
"rev": "773d5a04e2e10ca7b412270dea11276a496e1b61",
"rev": "5bbc126db87b5ffc36394df630eead403c48fac8",
"type": "github"
},
"original": {
@ -180,11 +180,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1743095683,
"narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=",
"lastModified": 1742288794,
"narHash": "sha256-Txwa5uO+qpQXrNG4eumPSD+hHzzYi/CdaM80M9XRLCo=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6",
"rev": "b6eaf97c6960d97350c584de1b6dcff03c9daf42",
"type": "github"
},
"original": {

View file

@ -1,15 +1,4 @@
{
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];
{config, ...}: {
# This is where the age files go
# age.secrets.nextcloud.file = ../secrets/nextcloud.age;
}

View file

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

View file

@ -34,8 +34,5 @@
"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,4 +8,8 @@
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

@ -1,5 +0,0 @@
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,5 +2,4 @@ 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,28 +5,14 @@
}: let
lib = pkgs.lib;
host = "10.0.${toString vm-index}.1";
ports = {
sonarr.port = 8989;
radarr.port = 7878;
jackett.port = 9117;
};
port = 8989;
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
@ -34,7 +20,9 @@ in {
system.activationScripts."make-${vm-name}-data-dir" = lib.stringAfter ["var"] ''
mkdir -p /var/lib/${vm-name}
# chown -R microvm:jellyfin /var/lib/${vm-name}
chmod -R 777 /var/lib/${vm-name}
chown -R microvm /var/lib/${vm-name}
chmod -R 777 /media
'';
microvm.vms.${vm-name} = {
@ -48,8 +36,6 @@ in {
}
];
time.timeZone = "Europe/Copenhagen";
# 1gb of memory
microvm.mem = 1024;
@ -69,7 +55,7 @@ in {
}
{
tag = "media-dir";
source = "/var/lib/media";
source = "/media";
mountPoint = "/media";
inherit proto;
}
@ -115,7 +101,7 @@ in {
serviceConfig = {
type = "oneshot";
ExecStart = pkgs.writeShellScript "wgconf.sh" ''
${pkgs.wireguard-tools}/bin/wg-quick up ${wg-config}
${pkgs.wireguard-tools}/bin/wg-quick up /mnt/wg.conf
'';
RemainAfterExit = "yes";
};
@ -133,18 +119,6 @@ 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;
@ -164,25 +138,11 @@ in {
services.transmission = pkgs.lib.mkIf enable-services {
enable = true;
openPeerPorts = true;
openRPCPort = true;
openFirewall = true;
home = "/mnt/transmission";
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"];
};
settings.download-dir = "/mnt/transmission";
settings.incomplete-dir = "/mnt/transmission/.incomplete";
downloadDirPermissions = "775";
performanceNetParameters = true;
};
# debugging
@ -209,41 +169,73 @@ in {
};
};
# 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 = [
{
port = value.port;
addr = "0.0.0.0";
ssl = false;
}
{
port = 443;
addr = "0.0.0.0";
ssl = true;
}
];
locations."/" = {
proxyPass = "http://${host}:${toString value.port}";
};
};
# Sonarr
services.nginx.virtualHosts."sonarr.spoodythe.one" = {
addSSL = true;
enableACME = true;
listen = [
{
inherit port;
addr = "0.0.0.0";
ssl = false;
}
)
ports);
{
port = 443;
addr = "0.0.0.0";
ssl = true;
}
];
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
# Forward transmission web port to vm
networking.nat.forwardPorts = [
{
sourcePort = 9091;
destination = "${host}:9091";
}
];
# 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;
}
{
port = 443;
addr = "0.0.0.0";
ssl = true;
}
];
locations."/" = {
proxyPass = "http://${host}:${toString port}";
};
};
networking.firewall.allowedTCPPorts = ports-list;
networking.firewall.allowedUDPPorts = ports-list;
# 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];
}

View file

@ -1,56 +0,0 @@
{...}: 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];
}

View file

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

View file

@ -3,34 +3,8 @@
lib,
config,
...
}: 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 {
}: {
services.fail2ban = {
enable = true;
bantime = "24h";
@ -41,34 +15,40 @@ in {
overalljails = true;
};
jails =
{
dovecot = lib.mkIf config.services.dovecot2.enable {
settings = {
# block IPs which failed to log-in
# aggressive mode add blocking for aborted connections
filter = "dovecot[mode=aggressive]";
maxretry = 3;
};
jails = {
dovecot = lib.mkIf config.services.dovecot2.enable {
settings = {
# block IPs which failed to log-in
# aggressive mode add blocking for aborted connections
filter = "dovecot[mode=aggressive]";
maxretry = 3;
};
}
// (lib.attrsets.mapAttrs (name: value: value.jail) jails);
};
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";
};
};
};
};
environment.etc =
{
# Defines a filter that detects URL probing by reading the Nginx access log
"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);
environment.etc = {
"fail2ban/filter.d/jellyfin.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
[Definition]
failregex = ^.*Authentication request for .* has been denied \(IP: <ADDR>\)\.
'');
# 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 ''
[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})
'');
};
}

View file

@ -2,10 +2,6 @@
host = "127.0.0.1";
port = 6969;
in {
imports = [
./nginx.nix
./gitea-actions-runner.nix
];
services.forgejo = {
enable = true;
@ -17,10 +13,6 @@ 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;
@ -46,4 +38,8 @@ in {
'';
};
};
# Open port 80 and 443 for reverse proxy
networking.firewall.allowedTCPPorts = [80 443];
networking.firewall.allowedUDPPorts = [80 443];
}

View file

@ -1,93 +0,0 @@
{
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);
}

44
services/homepage.nix Normal file
View file

@ -0,0 +1,44 @@
{...}: 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,9 +6,35 @@
}: let
host = "127.0.0.1";
port = 8096;
gid = 989;
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;
in {
imports = [./nginx.nix];
# Enable VAAPI
config.nixpkgs.config.packageOverrides = pkgs: {
vaapiIntel = pkgs.vaapiIntel.override {enableHybridCodec = true;};
@ -24,19 +50,17 @@ in {
];
};
# Set declarative gid
config.users.groups.jellyfin.gid = gid;
# Create folder for media
config.system.activationScripts."jellyfinMediaFolder" = lib.stringAfter ["var"] ''
mkdir -p /var/lib/media
chmod 770 /var/lib/media
chown -R jellyfin:jellyfin /var/lib/media
mkdir -p /media
chmod -R 775 /media
chown -R jellyfin:jellyfin /media
'';
# Enable Jellyfin
config.services.jellyfin = {
enable = true;
package = jellyfin;
openFirewall = false; # We want jellyfin behind a reverse proxy
};
@ -54,7 +78,6 @@ in {
enableACME = true;
locations."/" = {
proxyPass = "http://${host}:${toString port}";
proxyWebsockets = true;
extraConfig = ''
# Websocket support
proxy_set_header Upgrade $http_upgrade;
@ -66,7 +89,7 @@ in {
};
};
# Open ports for local network access
config.networking.firewall.allowedTCPPorts = [port];
config.networking.firewall.allowedUDPPorts = [port];
# Open port 80 and 443 for reverse proxy
config.networking.firewall.allowedTCPPorts = [port 80 443];
config.networking.firewall.allowedUDPPorts = [port 80 443];
}

View file

@ -5,12 +5,17 @@
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];
}

18
services/rtorrent.nix Normal file
View file

@ -0,0 +1,18 @@
{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;
};
}

28
services/seafile.nix Normal file
View file

@ -0,0 +1,28 @@
{
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];
}

189
services/sonarr-old.nix Executable file
View file

@ -0,0 +1,189 @@
{
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}";
};
};
}

25
services/sonarr.nix Normal file
View file

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

133
services/vm-test.nix Normal file
View file

@ -0,0 +1,133 @@
{
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;
};
};
};
};
}

4
services/vpn.nix Normal file
View file

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

View file

@ -5,77 +5,12 @@
}: 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 = "${pkgs.postfix}/bin/sendmail";
ZED_EMAIL_OPTS = " @ADDRESS@";
ZED_EMAIL_PROG = "sendmail";
ZED_EMAIL_OPTS = "-s '@SUBJECT@' @ADDRESS@";
ZED_LOCKDIR = "/var/lock";
ZED_NOTIFY_INTERVAL_SECS = 3600;