summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.sops.yaml5
-rw-r--r--certs/wilkuu_rsa.pub1
-rw-r--r--flake.lock22
-rw-r--r--flake.nix7
-rw-r--r--hosts/apocalypse/default.nix3
-rw-r--r--hosts/omega-relay/default.nix74
-rw-r--r--hosts/omega-relay/disko.nix52
-rw-r--r--hosts/omega-relay/firewall.nix2
-rw-r--r--hosts/omega-relay/vm.nix18
-rw-r--r--modules/default.nix2
-rw-r--r--secrets/omega-relay/freshrss.yaml27
-rw-r--r--secrets/omega-relay/stalwart.yaml26
-rw-r--r--secrets/omega-relay/vaultwarden.yaml27
-rw-r--r--secrets/omega-relay/wakapi.yaml26
-rw-r--r--services/email.nix142
-rw-r--r--services/freshrss.nix81
-rw-r--r--services/mysql.nix125
-rw-r--r--services/uptimekuma.nix78
-rw-r--r--services/vaultwarden.nix76
-rw-r--r--services/wakapi.nix110
-rw-r--r--users/wilkuu-server.nix19
21 files changed, 902 insertions, 21 deletions
diff --git a/.sops.yaml b/.sops.yaml
index 1bb53dd..0903cbd 100644
--- a/.sops.yaml
+++ b/.sops.yaml
@@ -1,6 +1,7 @@
keys:
- &admin_wilkuu age1hsxrd9shau86k7zz9wfdeplldyg5c2al6umnr7rn6tf7ewus64uswufr0a
- &server_apocalypse age19cw4x4ycrdhfdkwvp009czmvfhucpdtn8lme4ja0rvq7er87z3wsd2fxz7
+ - &server_omega_relay age13x9grwfpqjgvjfcnwg5snuntp5emze85hqc8qwuz9nfphfe2pg4skl29r4
creation_rules:
- path_regex: secrets/[^/]+\.(yaml|json|env|ini)$
@@ -12,4 +13,8 @@ creation_rules:
age:
- *admin_wilkuu
- *server_apocalypse
+ - path_regex: secrets/omega-relay/[^/]+\.(yaml|json|env|ini)$
+ age:
+ - *admin_wilkuu
+ - *server_omega_relay
diff --git a/certs/wilkuu_rsa.pub b/certs/wilkuu_rsa.pub
new file mode 100644
index 0000000..de478a5
--- /dev/null
+++ b/certs/wilkuu_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCc+ueAAfocDsWyNVyzW6NK59I0v79xUALL9gX2OAmh3zKdpoMq1/MiVvdI73HWs9pRzaeH1JrySjENdvScoj1FeEIu2Q1D8bJ9WlTZzXNwwGsVd9IyCPWwwQYah6toZXlpSrpC9pHwGvnjzN6Yz6ka1BDnZTa1bGrGEKMiADIL9yaRAObHcjIVJ9eonViUn3wjyZr3FWUuXXJtfROOAxvWH1OdVJK39Y1QFgp0H4FjAJ3dfJlV81qDMy1+uVTzEfatrm8DUnfmS9xFsTn++KLKUmUXknyeQQ0c0dh/RkG6vbzaNOVZ7YMyFyiJsXM2xLNbWxMDBx+71fYWjR+5I4zBpe19EoARf1ZB78GMOxOJcX0PS1c8jDoLUzZKII3QjDZi81OvhApno6wvyHXHk/3edh5pxCae/rMa1OH9W6Ncdk5CE2JCozR7vdm20NzeEjXtJJzN3pPzm0CrOeQRIsodzV7c9OBKDJz7NjjD9aZxnf9X50edslMxz0Xw0AkWgac= j.stachurski@student.utwente.nl
diff --git a/flake.lock b/flake.lock
index 3e3351a..f8248ba 100644
--- a/flake.lock
+++ b/flake.lock
@@ -15,6 +15,27 @@
"type": "github"
}
},
+ "disko": {
+ "inputs": {
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1768920986,
+ "narHash": "sha256-CNzzBsRhq7gg4BMBuTDObiWDH/rFYHEuDRVOwCcwXw4=",
+ "owner": "nix-community",
+ "repo": "disko",
+ "rev": "de5708739256238fb912c62f03988815db89ec9a",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "ref": "latest",
+ "repo": "disko",
+ "type": "github"
+ }
+ },
"flake-utils": {
"inputs": {
"systems": "systems"
@@ -87,6 +108,7 @@
},
"root": {
"inputs": {
+ "disko": "disko",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"nixpkgs-stable": "nixpkgs-stable",
diff --git a/flake.nix b/flake.nix
index 8148ee3..aacd740 100644
--- a/flake.nix
+++ b/flake.nix
@@ -9,6 +9,11 @@
url = "github:Mic92/sops-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
+
+ disko = {
+ url = "github:nix-community/disko/latest";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
home-manager = {
url = "github:nix-community/home-manager";
@@ -31,6 +36,7 @@
self,
nixpkgs,
treefmt-nix,
+ disko,
...
}@inputs:
let
@@ -112,6 +118,7 @@
./users/wilkuu-server.nix
./hosts/omega-relay
inputs.home-manager.nixosModules.default
+ disko.nixosModules.disko
inputs.sops-nix.nixosModules.sops
];
diff --git a/hosts/apocalypse/default.nix b/hosts/apocalypse/default.nix
index 5e13448..6882cbb 100644
--- a/hosts/apocalypse/default.nix
+++ b/hosts/apocalypse/default.nix
@@ -9,6 +9,9 @@
## TODO REMOVE LATER TO PREVENT ELI FROM BUILDING REMOTELY
boot.binfmt.emulatedSystems = [ "aarch64-linux" ];
+ networking.hosts = {
+ "127.0.0.1" = ["omega-relay.local"];
+ };
## Addons for this system
addons = {
diff --git a/hosts/omega-relay/default.nix b/hosts/omega-relay/default.nix
index 25a8f30..4154e2c 100644
--- a/hosts/omega-relay/default.nix
+++ b/hosts/omega-relay/default.nix
@@ -1,18 +1,87 @@
{ pkgs, lib, config, ...}: {
imports = [
./firewall.nix
+ ./disko.nix
./vm.nix
+ ../../services/mysql.nix
+ ../../services/email.nix
+ ../../services/vaultwarden.nix
+ ../../services/uptimekuma.nix
+ ../../services/freshrss.nix
+ ../../services/wakapi.nix
];
+
addons = {
desktop.hyprland.enable = lib.mkForce false;
+ # desktop.cosmic.enable = lib.mkForce false;
desktop.xfce.enable = lib.mkForce false;
gpg.enable = true;
};
-
boot.loader.grub = {
- useOSProber = true;
+ enable = true;
+ efiSupport = false;
+ };
+
+ environment.systemPackages = with pkgs; [
+ lynx
+ chawan
+ ];
+
+ wilkuu.services = let
+ isVM = config.addons.virtualisation.isTestVM;
+ in {
+ stalwart = {
+ enable = true;
+ domain = if isVM then "mail.omega-relay.local" else "mail.wilkuu.xyz";
+ doACME = !isVM;
+ };
+
+ vaultwarden = {
+ enable = true;
+ signupWhitelist = ["wilkuu.xyz" "omega-relay.local"];
+ backupDir = "/srv/data/vaultwarden";
+ domain = if isVM then "vaultwarden.omega-relay.local" else "vaultwarden.wilkuu.xyz";
+ doACME = !isVM;
+ };
+ uptimekuma = {
+ enable = true;
+ domain = if isVM then "uptime.omega-relay.local" else "uptime.wilkuu.xyz";
+ dataDir = "/srv/data/uptimekuma";
+ doACME = !isVM;
+ };
+ freshrss = {
+ enable = true;
+ domain = if isVM then "rss.omega-relay.local" else "rss.wilkuu.xyz";
+ doACME = !isVM;
+ };
+ wakapi = {
+ enable = true;
+ domain = if isVM then "wakapi.omega-relay.local" else "wakapi.wilkuu.xyz";
+ doACME = !isVM;
+ };
+ };
+
+ # TODO: Make a nginx module
+ security.acme = lib.mkIf (!config.addons.virtualisation.isTestVM) {
+ acceptTerms = true;
+ defaults.email = "jakub@wilkuu.xyz";
+ };
+ services.nginx = let
+ isVM = config.addons.virtualisation.isTestVM;
+ domain = if isVM then "omega-relay.local" else "wilkuu.xyz";
+ in {
+ enable = true;
+ virtualHosts."${domain}" = {
+ enableACME = !isVM;
+ addSSL = !isVM;
+ locations."/" = {
+ root = "/srv/www/wilkuu.xyz/";
+ index = "index.html";
+ tryFiles = "$uri $uri/ =404";
+ };
+ };
};
networking.hostName = "omega-relay";
@@ -35,7 +104,6 @@
};
};
-
diff --git a/hosts/omega-relay/disko.nix b/hosts/omega-relay/disko.nix
new file mode 100644
index 0000000..9694e2a
--- /dev/null
+++ b/hosts/omega-relay/disko.nix
@@ -0,0 +1,52 @@
+{pkgs, config, lib, ...}: {
+ config = lib.mkIf (!config.addons.virtualisation.isTestVM) ({
+ services.btrfs.autoScrub = {
+ enable = true;
+ interval = "weekly";
+ };
+
+ boot.loader.grub.device = "/dev/sda";
+ # Workaround
+ boot.loader.grub.devices = lib.mkForce ["/dev/sda"];
+ boot.loader.efi.canTouchEfiVariables = false;
+
+ # TODO: Mount points
+
+ # Disko for formatting
+ disko.devices = {
+ disk = {
+ main-disk = {
+ device = "/dev/sda";
+ type = "disk";
+ content = {
+ type = "gpt";
+ partitions = {
+ BOOT = {
+ type = "EF02";
+ size = "1M";
+ };
+ ESP = {
+ type = "EF00";
+ size = "128M";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot";
+ mountOptions = [ "umask=0077" ];
+ };
+ };
+ root = {
+ size = "100%";
+ content = {
+ type = "filesystem";
+ format = "btrfs";
+ mountpoint = "/";
+ };
+ };
+ };
+ };
+ };
+ };
+ };
+ });
+}
diff --git a/hosts/omega-relay/firewall.nix b/hosts/omega-relay/firewall.nix
index 7b04813..6311337 100644
--- a/hosts/omega-relay/firewall.nix
+++ b/hosts/omega-relay/firewall.nix
@@ -1,7 +1,7 @@
{ ... }:
let
baseTCP = [
- 20 22 433
+ 20 22 25 80 143 443 993 465
];
baseUDP = [
16555 # Wireguard
diff --git a/hosts/omega-relay/vm.nix b/hosts/omega-relay/vm.nix
index a0da57b..06310f9 100644
--- a/hosts/omega-relay/vm.nix
+++ b/hosts/omega-relay/vm.nix
@@ -1,17 +1,18 @@
{lib, config,...}: let
- tcpFromHost = gport: hport: {
+ forward = proto: gport: hport: {
from = "host";
- proto = "tcp";
+ proto = proto;
host = {
port = hport;
- address = "10.0.69.1";
+ # address = "10.0.69.1";
};
guest = {
port = gport;
- address = "10.0.69.2";
+ # address = "10.0.69.2";
};
};
in {
+ # TODO: Make this into a more global module.
virtualisation.vmVariant = {
addons.virtualisation.isTestVM = true;
addons.virtualisation.guest = true;
@@ -26,12 +27,15 @@ in {
};
virtualisation = {
forwardPorts = [
- # (tcpFromHost 80 8080)
- # (tcpFromHost 22 8022)
+ (forward "tcp" 80 9080)
+ (forward "tcp" 443 9443)
+ (forward "tcp" 143 9143)
+ (forward "tcp" 25 9025)
+ (forward "tcp" 22 9022)
];
memorySize = 2048; # Use 2048MiB memory.
cores = 3;
graphics = false;
};
- };
+ };
}
diff --git a/modules/default.nix b/modules/default.nix
index 774b40d..90f65d8 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -36,7 +36,7 @@
time.timeZone = "Europe/Amsterdam"; # Set timezone
sops.defaultSopsFile = ./secrets/secrets.yaml;
- sops.age.sshKeyPaths = [ "/etc/ssh_host_ed25519_key" ];
+ sops.age.sshKeyPaths = [ "/etc/ssh/ssh_host_ed25519_key" ];
sops.age.keyFile = "/var/lib/sops-nix/key.txt";
sops.age.generateKey = true;
diff --git a/secrets/omega-relay/freshrss.yaml b/secrets/omega-relay/freshrss.yaml
new file mode 100644
index 0000000..d6ad4b6
--- /dev/null
+++ b/secrets/omega-relay/freshrss.yaml
@@ -0,0 +1,27 @@
+fresh-rss:
+ admin_password: ENC[AES256_GCM,data:aAKl2jCR4w==,iv:xYQXINEeywuyg8+guhS+kv79L/crXLquJp+k6l4vUkI=,tag:4UA4D9jcYtIGHh1FHBv7Nw==,type:str]
+ db_password: ENC[AES256_GCM,data:Ov79Vw1I8A==,iv:T7oJEszUQoNyK8xxSg9jUEEmGlAVHTm98idrcLBWC3E=,tag:dow8CF+HwZFDh7PjF1dSUg==,type:str]
+sops:
+ age:
+ - recipient: age1hsxrd9shau86k7zz9wfdeplldyg5c2al6umnr7rn6tf7ewus64uswufr0a
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxNjgybzlud0F0Tnd5UzA2
+ YkNTTGsyR1EvZGM3bGdQY3V5cnZWbWtiVGd3CnZac1RER3NhYisrVm9TVDcva1Nx
+ Z2RBdi9TUWxkY2lTYWlMVFVqYXNGN0kKLS0tIDZtd3hmZGdkMVRSZ0NsY0N6Nk16
+ cWs5SElDSjF1RytaTWZlMHl6OUxWRWsK6EVAusVlZb2skuQBa8hfgc9uH3MBgwWE
+ UoLJgeZ+7NLvj0SY6XbLJVm7mzY9BH1ezSU0ZZlJEJyUdgwc+HdV9w==
+ -----END AGE ENCRYPTED FILE-----
+ - recipient: age13x9grwfpqjgvjfcnwg5snuntp5emze85hqc8qwuz9nfphfe2pg4skl29r4
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTbTcyelc1YTgvYytESnNy
+ TnFkNGFhbUg2djdqK3dUeFFpRlVjd2tENW04CnRNUmtmc0NBeWFwYzlNTUNwTmJy
+ L3RNMDk2S3ZRYUVNUENUSWVNOFpkQTgKLS0tIE56RW95bVRYR0NWdGQrUlRaSFlu
+ alBWOE94TnA2ZnFEOHg2dElDS0ZuZ3MKem45+6LBH4GXrZpbAn9RUrbF/8A8Ecyd
+ oOjFC7bRxEDSbq1bU9LV4PurnjIFXJjrZbFlIZkiqQz8IfievsKY/w==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-01-25T12:43:00Z"
+ mac: ENC[AES256_GCM,data:SzUHVvZ9eDMMuXFSsmqxEPvpbLDd+b0x8c9vkypCduLyoi7W2kvM4hT/F5hWbdDcCcI262BETceMq19a3hkH9H0I2U8zxnYkPZuPjCI53v9Rb4tQkhrSc35afHijcVOsQmfqoP/olvESdNi+Db1ghkay8C3Dl7/SJ+J6chClmFs=,iv:ayViaJ5Ammcqi2mmf1y6W2BcLcheoMwlPN7CHEFaXYc=,tag:harswwEu4WVISwz6KyxDdg==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.11.0
diff --git a/secrets/omega-relay/stalwart.yaml b/secrets/omega-relay/stalwart.yaml
new file mode 100644
index 0000000..6cb5cba
--- /dev/null
+++ b/secrets/omega-relay/stalwart.yaml
@@ -0,0 +1,26 @@
+stalwart:
+ user_admin_password: ENC[AES256_GCM,data:oHKLJRLYCbVO7AQuzqwOd40jTV5Y7M76do9EduXpbas=,iv:u+jwIZL8c603XlbuANNYjXTY1vKan0xO3eWTiW6NlQo=,tag:C81dfQOuqtqnXPxJv5QuHw==,type:str]
+sops:
+ age:
+ - recipient: age1hsxrd9shau86k7zz9wfdeplldyg5c2al6umnr7rn6tf7ewus64uswufr0a
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnc0NEREVWOVNCemtja1hx
+ RTZNMFFhdllrNTUrRzIxUCtJYW9YaStzMXlZCk5BOEFDRWJ4d0wrNVdRZmNxSUtq
+ OTV5Y1p5Q1hrdm4vci9STHRtSzQzbEEKLS0tIFJXdWxjUTQ1WVlJZW1aQU5MZXk0
+ anNhdWx0dnNML2JOd3A0K2pUMGp3THcKFULx1PTapR1/6f5SnLTdmrj1SIsqUkFS
+ oQ66VJrsJTUAtYkTPADyLwvTwTrhlGVOLw1S7iuSvvzgkb9j5k8lIg==
+ -----END AGE ENCRYPTED FILE-----
+ - recipient: age13x9grwfpqjgvjfcnwg5snuntp5emze85hqc8qwuz9nfphfe2pg4skl29r4
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByZ0ZFdVR3QlN6ajJjSDdz
+ RnlYL1FXaTl5cmxJYW9wYVVlMGNXNDUwNFFBCmRocUM5S3o1aWNBSjNBNmdXYTBh
+ Z0paUkNldzlkRWw2cnI0cGZwZGw3RzgKLS0tIFpwRGgzYzhCUUljOHZhY2g0cUc3
+ L3FBTTEyYU10c3pJUFdodTRwM0RkTVEK5DwewdrHKP8REJN3dCrx2Swkcat/15ep
+ wH4bb+3fy6jndgaczNpI9MUr6OfsS904mItw879OCIiRrxcjnuN5nw==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-01-17T21:45:45Z"
+ mac: ENC[AES256_GCM,data:ZN8Zmo6yvhuyJKrnAw4btIf6FxEWiQuCCBpWedYGj0S973YRgEbl7Q9HKsYrVOwNwGIMS5MBv+hjjvOMZvJU0331V7PefRy4yzqN10pJLgLwyB63ZrhOdY87q9U8Gz3K1qOoPJievJmyxBwUvbdwO7JWoeiNVxsC+YwGZiJ19yg=,iv:OillC3jS5bILDrZahmsN/sztSlFqrr2BpqzUYjdd7vU=,tag:28az0UH1hVcEq9532yY8nw==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.11.0
diff --git a/secrets/omega-relay/vaultwarden.yaml b/secrets/omega-relay/vaultwarden.yaml
new file mode 100644
index 0000000..69d49d1
--- /dev/null
+++ b/secrets/omega-relay/vaultwarden.yaml
@@ -0,0 +1,27 @@
+vaultwarden:
+ admin_token: ENC[AES256_GCM,data:d37MTTlBAelmO1ZAv9Qg5DlCUVF1YkqzvkCAq4/CPObvyIi7Km7u1zJGb8lCZx8p9qJRGaJQz6CRhusIH4iGWvg317j9WtO2kUYHhrduXkk9/BIDoW/BaN+9FmxEPSmH7Xpyoss/HnGNNce03KCUjqs7ZA81rXfPa6suVn4HV4+GnoiZJBs=,iv:gQncevzr8VBTLaA9Zd2zWH8xKYzuy8kR04xpwFwYNdM=,tag:H4MQfWrOQGkbY2Ghpd0/XA==,type:str]
+ admin_pass: ENC[AES256_GCM,data:/Acr0LuuWBzMx4Vcz/lV/5HUgNqxyAbhxVpvBFyo,iv:kANWTVPsPo+1VFxSsPqH9sCaH2lFJ5p9X15j12jZ5iU=,tag:4zsUNvFDADvFmW9DmQ/9cw==,type:str]
+sops:
+ age:
+ - recipient: age1hsxrd9shau86k7zz9wfdeplldyg5c2al6umnr7rn6tf7ewus64uswufr0a
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzSTdRN1EvdG1DMzhMaDRV
+ VGdQaXE4SjZSVkJFNkRxbEE4blJPREM4Mmh3CmkrZFRvUytaUmpkNGdOSGhlWTJM
+ RWRRY0piOHBoQTg5cFUyOW03RDZKN1EKLS0tIFJKb0t2YXlvWTMvMm1BRWZRRE9G
+ NUhUUlUzUkxZNFlBa1lscU53K2RFRTQKrhP4y8tqkwXaSqoYzRNrAVQ9TFrxKOCM
+ PD3nI/R64Smou/ZHsXqv3EbdCeiccXH9WPoN/BNAh36Y3VsqlHSk9A==
+ -----END AGE ENCRYPTED FILE-----
+ - recipient: age13x9grwfpqjgvjfcnwg5snuntp5emze85hqc8qwuz9nfphfe2pg4skl29r4
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5K201WTQrOWM3OEQwaUlp
+ RXlXMDExL0ZWcnR6aU0wNWQrRS9zZG5Ec2tJCnVLb1RTQkh0UkpDckFnRlc5RVo3
+ M2dKQjh2NU5rZGNRN281RGR0dC9qbWsKLS0tIDExbldNY1hNenBvNTdHcWVjRzl5
+ aUt2YTF4WkE0clFDMWhUSTJsNlhsMHMKtlGJ1nIhBtZ8OM9bbLyinroS1dUpPAmI
+ EnUnl2X+bACL0OK125T5SNyC2T/tNjO9TOzc9YlaJSlGLUbfwb1U1Q==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-01-24T19:54:55Z"
+ mac: ENC[AES256_GCM,data:7xNKZSR9CD0U7LhQ0ziHhgafNcvwZdWx+bQAffEVr+zk/1uC4LZn8vpab9Mwkma4ycW0w++LZZxNWx4lDJTLSl/+bGYb13pBGKiai5+4Bi7JIt5z9UAjLasPKTNCZXvXKv4Y2QLGT+Jqqkaa+e92RG3WSdnLwfZpoGUu89v/ifo=,iv:cwYU8650Ro437NpamndB6yMGUsGFxmtmWlwPwyC4VKE=,tag:qJlnztMaE6DhZ2WBQpCzLw==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.11.0
diff --git a/secrets/omega-relay/wakapi.yaml b/secrets/omega-relay/wakapi.yaml
new file mode 100644
index 0000000..06d0112
--- /dev/null
+++ b/secrets/omega-relay/wakapi.yaml
@@ -0,0 +1,26 @@
+wakapi:
+ password_salt: ENC[AES256_GCM,data:u8gNo915MwX9nWxJHsxI1xx+S7384EahefW5SrdqJVQ=,iv:1SCDw0CwgTIPEICmHwWlrzsguDmlBWSw7yWhw5aguJw=,tag:LvJwUGpNVHaj1EWx8XFXbQ==,type:str]
+sops:
+ age:
+ - recipient: age1hsxrd9shau86k7zz9wfdeplldyg5c2al6umnr7rn6tf7ewus64uswufr0a
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCZ3Y4NkdXeFM2U2svMXdu
+ U044QlpPQjBsN3VhREtnTTJhempqWmNpNVNZClVGK1JNWUUvODBBSmpleXdqeEtz
+ YWM5ZVVodGJzZGV5QjBzN1BTRUFiOEEKLS0tIE9FOXFEa3NEd1BHeUIrcW95UUlm
+ NnFhSFJUZ0Q5bHBJVW95R1VHenU0bjgKHG88y2VtLZ7XoG3iNLda48jk+MvlQg+T
+ vRfjQIvcChsxZbttZk78DXmz2Z+wj+Cr0kyuZWAhjVJgCRV9owGp1w==
+ -----END AGE ENCRYPTED FILE-----
+ - recipient: age13x9grwfpqjgvjfcnwg5snuntp5emze85hqc8qwuz9nfphfe2pg4skl29r4
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvblNnQzdLZnV5bFoyYjhL
+ enBPaHYvUXhNbWdEMy9LYk9aTVZWMkVobTBJClpuNVpTWmVnKzYwNUhFSTJlSDlW
+ bkpHbDRJelMyaldkcmc0ZVFxQnFNaTAKLS0tIGNKRXZodFBUditMWmd3M3h3L1lS
+ dHlSTnY0a21xa2FSUFAvNmRZQXZGZ1UKWzPXR/HiynJDO8AHNZn/kEfM3lynsVsp
+ 9zHFzqeywBwPkqYrxEABVXASk7k9AXw8MTL/mgvUcATkKCWjic6Geg==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-01-25T12:52:57Z"
+ mac: ENC[AES256_GCM,data:tkmq5bwnqmucoRJuD867q4TL/GWPjDvkQO4LUIsVwXtIj+CUmrh3CS9rAMMS+dcMGuG9e9H3hJX3LKWNWIdHTuA6GqIAU94LmmKI57gm1EPmaakJWB9P9gIP/N1IVQS3fc451RSU1cd94Dy1rApTjsspzpBxeed24KqoY67/76E=,iv:cfQqv2s2bSiB7+T/9OZcaA7N05LqvWIBlMr6NfLoTWE=,tag:4/o3V1CS7urudjqUjN5FkQ==,type:str]
+ unencrypted_suffix: _unencrypted
+ version: 3.11.0
diff --git a/services/email.nix b/services/email.nix
new file mode 100644
index 0000000..d3f60ab
--- /dev/null
+++ b/services/email.nix
@@ -0,0 +1,142 @@
+{config, pkgs, lib, ...}:
+let
+ cfg = config.wilkuu.services.stalwart;
+ hostname = config.networking.hostName;
+in
+{
+ options.wilkuu.services.stalwart = with lib; {
+ domain = mkOption {
+ type = types.str;
+ default = "mail.${hostname}.local";
+ example = "mail.wilkuu.xyz";
+ description = "Domain for http connections.";
+ };
+ wellKnownDomains = mkOption {
+ type = types.listOf types.str;
+ default = [ "${hostname}.local" ];
+ example = [ "wilkuu.xyz"] ;
+ description = "Domain for well-known items";
+ };
+ doACME = mkEnableOption "Enable ACME for stalwart here";
+ enable = mkEnableOption "Enable the email service";
+ dataDir = mkOption {
+ type = types.path;
+ description = "Storage localtion for Stalwart user data";
+ default = "/srv/data/stalwart";
+ example = "/srv/data/stalwart";
+ };
+ };
+
+ config = lib.mkIf cfg.enable (let
+ sopsPath = ../secrets/${hostname}/stalwart.yaml;
+ secrets = ["user_admin_password"];
+ toSops = (sname: "stalwart/${sname}");
+ toCredfilePath = (name: config.sops.secrets.${toSops name}.path);
+ toStalwartCred = name: "%{file:/run/credentials/stalwart-mail.service/${name}}%";
+
+ basicListener = proto: port: tls: {
+ bind = ["[::]:${toString port}"];
+ protocol = proto;
+ tls.implicit = tls;
+ };
+
+ proxyWellKnown = names: let
+ uris = map (n: "/.well-known/${n}") names;
+ in (lib.genAttrs uris (uri: {
+ proxyPass = "http://localhost:3080${uri}";
+ recommendedProxySettings = true;
+ }));
+
+ makeHttpRedirect = target: https: { return = "302 ${if https then "https" else "http"}://${target}";};
+
+ in {
+ networking.hosts = {
+ "127.0.0.1" = [cfg.domain];
+ };
+
+ # Need this bc otherwise sops will complain for some reason
+ users = {
+ groups.stalwart-mail = { };
+ users.stalwart-mail = {
+ isSystemUser = true;
+ group = "stalwart-mail";
+ };
+ };
+
+
+ # TODO: Move this into a util function or option;
+ sops.secrets =
+ (lib.genAttrs (map toSops secrets)
+ (name: {
+ sopsFile = sopsPath;
+ mode = "0440";
+ owner = "stalwart-mail";
+ }));
+
+
+ services.nginx.virtualHosts = (lib.genAttrs cfg.wellKnownDomains (
+ (wdomain: {
+ locations = (proxyWellKnown ["jmap" "mta-sts.txt" "mail-v1.xml" "autoconfig/mail"])
+ // (lib.genAttrs ["/.well_known/caldav" "/.well_known/webdav" ] (uri: (makeHttpRedirect "${cfg.domain}${uri}") cfg.doACME ));
+ })))
+ //
+
+ (lib.genAttrs (map (x: "${x}${cfg.domain}") ["" "autodiscover." "autoconfig."]) (domain: {
+ addSSL = cfg.doACME;
+ enableACME = cfg.doACME;
+ serverName = "${domain}";
+ locations."/" = {
+ proxyPass = "http://localhost:3080";
+ recommendedProxySettings = true;
+ };
+ }));
+
+
+ services.stalwart-mail = {
+ enable = true;
+ dataDir = cfg.dataDir;
+ openFirewall = false;
+ credentials = lib.genAttrs secrets toCredfilePath;
+ settings = {
+ server.listener = {
+ smtp = basicListener "smtp" 25 false;
+ submission = basicListener "smtp" 465 true;
+ imaptls = basicListener "imap" 993 true;
+ imap = basicListener "imap" 143 true;
+ webdav = basicListener "http" 3080 false;
+ jmap = basicListener "http" 3080 false;
+ http = basicListener "http" 3080 false;
+ };
+
+ store.rocksdb = {
+ type = "rocksdb";
+ path = cfg.dataDir;
+ compression = "lz4";
+ };
+
+ directory.internal = {
+ type = "internal";
+ store = "rocksdb";
+ };
+
+ storage = {
+ data = "rocksdb";
+ fts = "rocksdb";
+ blob = "rocksdb";
+ lookup = "rocksdb";
+ directory = "internal";
+ };
+
+ authentication.fallback-admin = {
+ user = "admin";
+ secret = toStalwartCred "user_admin_password";
+ };
+
+ http = {
+ use-x-forwarded = true;
+ url = "protocol + \"${cfg.domain}\"";
+ };
+ };
+ };
+ });
+}
diff --git a/services/freshrss.nix b/services/freshrss.nix
new file mode 100644
index 0000000..0b66755
--- /dev/null
+++ b/services/freshrss.nix
@@ -0,0 +1,81 @@
+{config, lib, pkgs, ...}:
+let
+ cfg = config.wilkuu.services.freshrss;
+ hostname = config.networking.hostName;
+in
+{
+
+ options.wilkuu.services.freshrss = with lib; {
+ domain = mkOption {
+ type = types.str;
+ default = "rss.${hostname}.local";
+ example = "rss.wilkuu.xyz";
+ description = "Domain for http connections.";
+ };
+ doACME = mkEnableOption "Enable ACME for fresh-rss here";
+ enable = mkEnableOption "Enable the fresh-rss service";
+ dataDir = mkOption {
+ type = types.path;
+ description = "Storage localtion for fresh-rss data";
+ default = "/srv/data/freshrss";
+ example = "/srv/data/freshrss";
+ };
+ };
+
+ config = lib.mkIf cfg.enable (let
+ sopsPath = ../secrets/${hostname}/freshrss.yaml;
+ secrets = ["admin_password" "db_pass"];
+ toSops = (sname: "fresh-rss/${sname}");
+ in {
+ networking.hosts = {
+ "127.0.0.1" = [cfg.domain];
+ };
+
+ sops.secrets =
+ (lib.genAttrs (map toSops secrets)
+ (name: {
+ sopsFile = sopsPath;
+ mode = "0440";
+ owner = config.services.freshrss.user;
+ }));
+
+ services.nginx.virtualHosts."${cfg.domain}" = {
+ addSSL = cfg.doACME;
+ enableACME = cfg.doACME;
+ };
+
+ wilkuu.services.mysql = {
+ enable = true;
+ users."freshrss" = {
+ sopsPlaceholder = config.sops.placeholder."fresh-rss/db_pass";
+ };
+ databases."freshrss" = {
+ enable = true;
+ allowedUsers = ["freshrss"];
+ };
+ };
+
+ services.freshrss = {
+ enable = true;
+ # api.enable = true;
+ dataDir = cfg.dataDir;
+ baseUrl = "https://${cfg.domain}";
+ extensions = with pkgs.freshrss-extensions; [
+ youtube
+ title-wrap
+ auto-ttl
+ reading-time
+ ];
+ passwordFile = config.sops.secrets."fresh-rss/admin_password".path;
+ virtualHost = cfg.domain;
+ database = {
+ passFile = config.sops.secrets."fresh-rss/db_pass".path;
+ host = "localhost";
+ port = config.wilkuu.services.mysql.port;
+ name = "freshrss";
+ user = "freshrss";
+ type = "mysql";
+ };
+ };
+ });
+}
diff --git a/services/mysql.nix b/services/mysql.nix
new file mode 100644
index 0000000..8a7b5e2
--- /dev/null
+++ b/services/mysql.nix
@@ -0,0 +1,125 @@
+{
+ pkgs,
+ config,
+ lib,
+ ...
+}:
+let
+ cfg = config.wilkuu.services.mysql;
+
+ create_users_ensure =
+ uname:
+ (lib.genAttrs (lib.map (dn: "${dn}.*") (
+ lib.attrNames (lib.filterAttrs (_: dcfg: (builtins.elem uname dcfg.allowedUsers)) cfg.databases)
+ )) (_: "ALL PRIVILEGES"));
+
+ priviledge_clause =
+ name: db: priv:
+ ("GRANT ${priv} ON ${db} TO ${name};");
+
+ add-user-clauses =
+ name: ucfg:
+ if (!isNull ucfg.sopsPlaceholder) then
+ (
+ ''
+ -- Clauses for user ${name}
+ ALTER USER IF EXISTS '${name}'@'%' IDENTIFIED BY '${ucfg.sopsPlaceholder}';
+ CREATE USER IF NOT EXISTS '${name}'@'%' IDENTIFIED BY '${ucfg.sopsPlaceholder}';
+ ''
+ + (lib.concatMapAttrsStringSep "\n" (priviledge_clause name) (create_users_ensure name))
+ )
+ else
+ " -- Ommitted user ${name}";
+
+ add-unix-user-clauses =
+ name: ''
+ -- Clauses for user ${name}
+ ALTER USER IF EXISTS '${name}'@'localhost' IDENTIFIED BY unix_socket';
+ CREATE USER IF NOT EXISTS '${name}'@'localhost' IDENTIFIED BY unix_socket';
+ ''
+ + (lib.concatMapAttrsStringSep "\n" (priviledge_clause name) (create_users_ensure name));
+
+in
+{
+ options.wilkuu.services.mysql = {
+ enable = lib.mkEnableOption "Enable database for containers";
+ port =
+ with lib;
+ mkOption {
+ type = types.port;
+ default = 3306;
+ };
+ databases =
+ with lib;
+ mkOption {
+ type = types.attrsOf (
+ types.submodule {
+ options = {
+ enable = mkEnableOption "Enable the database";
+ allowedUsers = mkOption {
+ type = types.listOf types.str;
+ };
+ };
+ }
+ );
+ default = { };
+ };
+ users =
+ with lib;
+ mkOption {
+ type = types.attrsOf (
+ types.submodule {
+ options = {
+ scramPassword = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ };
+ sopsPlaceholder = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ };
+ allowedRanges = mkOption {
+ type = types.listOf types.str;
+ };
+ };
+ }
+ );
+ default = { };
+ };
+ unix_users = lib.mkOption {
+ type = lib.types.listOf lib.types.str;
+ description = "Users that can identify using the unix socket";
+ default = [];
+ example = ["wakapi"];
+ };
+ };
+ # config.sops.secrets = lib.mkIf cfg.enable {
+ # "database/root_pass" = {
+ # sopsFile = ../secrets/${config.networking.hostName}/secrets.yaml;
+ # };
+ # };
+ config.sops.templates."init-mysql" = {
+ owner = config.systemd.services.mysql.serviceConfig.User;
+ content = (lib.concatLines ((builtins.attrValues (builtins.mapAttrs add-user-clauses cfg.users)) ++ (map add-unix-user-clauses cfg.unix_users)));
+ };
+
+ config.services.mysql = {
+ enable = cfg.enable;
+ ensureDatabases = builtins.attrNames cfg.databases;
+ initialScript = config.sops.templates."init-mysql".path;
+ package = pkgs.mariadb;
+ settings = {
+ mysqld = {
+ log_error = "/var/log/mysql_err.log";
+ log_warnings = 2;
+ };
+ };
+ };
+
+ # config.host-config.utilpkgs = lib.mkIf (cfg.enable) (
+ # with pkgs;
+ # [
+ # mycli
+ # ]
+ # );
+}
diff --git a/services/uptimekuma.nix b/services/uptimekuma.nix
new file mode 100644
index 0000000..4cdd5f2
--- /dev/null
+++ b/services/uptimekuma.nix
@@ -0,0 +1,78 @@
+{config, lib, pkgs, ...}:
+let
+ cfg = config.wilkuu.services.uptimekuma;
+ hostname = config.networking.hostName;
+in
+{
+ options.wilkuu.services.uptimekuma = with lib; {
+ domain = mkOption {
+ type = types.str;
+ default = "uptime.${hostname}.local";
+ example = "uptime.wilkuu.xyz";
+ description = "Domain for http connections.";
+ };
+ doACME = mkEnableOption "Enable ACME for uptime kuma here";
+ enable = mkEnableOption "Enable the uptime-kuma service";
+ dataDir = mkOption {
+ type = types.path;
+ description = "Storage localtion for uptime kuma data, currently ignored, because nixpkgs sucks";
+ default = "/srv/data/uptimekuma";
+ example = "/srv/data/uptimekuma";
+ };
+ };
+
+ config = lib.mkIf cfg.enable (let
+ # sopsPath = ../secrets/${hostname}/vaultwarden.yaml;
+ # secrets = [];
+ # toSops = (sname: "uptime-kuma/${sname}");
+ in {
+ networking.hosts = {
+ "127.0.0.1" = [cfg.domain];
+ };
+
+ users.users.uptimekuma = {
+ isSystemUser = true;
+ group = "uptimekuma";
+ };
+ users.groups.uptimekuma = {};
+
+ systemd.services.uptime-kuma.serviceConfig.User = "uptimekuma";
+
+ # sops.secrets =
+ # (lib.genAttrs (map toSops secrets)
+ # (name: {
+ # sopsFile = sopsPath;
+ # mode = "0440";
+ # owner = "uptime-kuma";
+ # }));
+
+ services.nginx.virtualHosts."${cfg.domain}" = {
+ addSSL = cfg.doACME;
+ enableACME = cfg.doACME;
+ locations."/" = {
+ proxyPass = "http://localhost:3111";
+ recommendedProxySettings = true;
+ };
+ };
+
+ wilkuu.services.mysql = let
+ user = config.systemd.services.uptime-kuma.serviceConfig.User;
+ in {
+ unix_users = [user];
+ databases.uptimekuma = {
+ enable = true;
+ allowedUsers = [user];
+ };
+ };
+
+ services.uptime-kuma = {
+ enable = true;
+ settings = {
+ UPTIME_KUMA_PORT = "3111";
+ UPTIME_KUMA_HOST="127.0.0.1";
+ UPTIME_KUMA_DB_TYPE = "sqlite";
+ UPTIME_KUMA_DB_SOCKET = "/var/lib/mysql/mysql.sock";
+ };
+ };
+ });
+}
diff --git a/services/vaultwarden.nix b/services/vaultwarden.nix
new file mode 100644
index 0000000..f68a2fa
--- /dev/null
+++ b/services/vaultwarden.nix
@@ -0,0 +1,76 @@
+{config, pkgs, lib, ...}:
+let
+ cfg = config.wilkuu.services.vaultwarden;
+ hostname = config.networking.hostName;
+in
+{
+ options.wilkuu.services.vaultwarden = with lib; {
+ domain = mkOption {
+ type = types.str;
+ default = "bitwarden.${hostname}.local";
+ example = "bitwarden.wilkuu.xyz";
+ description = "Domain for http connections.";
+ };
+ doACME = mkEnableOption "Enable ACME for vaultwarden here";
+ enable = mkEnableOption "Enable the vaultwarden service";
+ backupDir = mkOption {
+ type = types.path;
+ description = "Storage localtion for Vaultwarden user data backup";
+ default = "/srv/data/vaultwarden";
+ example = "/srv/data/vaultwarden";
+ };
+ signupWhitelist = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ example = ["wilkuu.xyz"];
+ description = "Domains that can sign up on vaultwarden";
+ };
+ };
+
+ config = lib.mkIf cfg.enable (let
+ sopsPath = ../secrets/${hostname}/vaultwarden.yaml;
+ secrets = ["admin_token"];
+ toSops = (sname: "vaultwarden/${sname}");
+ toCredfilePath = (name: config.sops.secrets.${toSops name}.path);
+ in {
+ networking.hosts = {
+ "127.0.0.1" = [cfg.domain];
+ };
+
+ sops.secrets =
+ (lib.genAttrs (map toSops secrets)
+ (name: {
+ sopsFile = sopsPath;
+ mode = "0440";
+ owner = "vaultwarden";
+ }));
+
+ sops.templates.vaultwardenEnvFile.content = ''
+ ADMIN_TOKEN=${config.sops.placeholder."vaultwarden/admin_token"}
+ '';
+
+ services.nginx.virtualHosts."${cfg.domain}" = {
+ enableACME = cfg.doACME;
+ addSSL = cfg.doACME;
+ locations."/" = {
+ proxyPass = "http://localhost:3222";
+ recommendedProxySettings = true;
+ };
+ };
+
+ services.vaultwarden = {
+ enable = cfg.enable;
+ backupDir = "/srv/data/vaultwarden";
+ config = {
+ DOMAIN=cfg.domain;
+ ROCKET_ADDRESS = "127.0.0.1";
+ ROCKET_PORT ="3222";
+ SIGNUPS_DOMAINS_WHITELIST=(lib.concatStringsSep "," cfg.signupWhitelist);
+ SIGNUPS_ALLOWED="false";
+ IP_HEADER="X-Forwarded-For";
+ };
+ environmentFile = config.sops.templates.vaultwardenEnvFile.path;
+ };
+
+ });
+}
diff --git a/services/wakapi.nix b/services/wakapi.nix
new file mode 100644
index 0000000..1b1e312
--- /dev/null
+++ b/services/wakapi.nix
@@ -0,0 +1,110 @@
+{config, lib, pkgs, ...}:
+let
+ cfg = config.wilkuu.services.wakapi;
+ hostname = config.networking.hostName;
+ service_user = config.systemd.services.wakapi.serviceConfig.User;
+in
+{
+ options.wilkuu.services.wakapi = with lib; {
+ domain = mkOption {
+ type = types.str;
+ default = "wakapi.${hostname}.local";
+ example = "wakapi.wilkuu.xyz";
+ description = "Domain for http connections.";
+ };
+ email = mkOption {
+ type = types.str;
+ default = "wakapi@${hostname}.local";
+ example = "noreply@wilkuu.xyz";
+ description = "Mailer address";
+ };
+ doACME = mkEnableOption "Enable ACME for wakapi here";
+ enable = mkEnableOption "Enable the wakapi service";
+ dataDir = mkOption {
+ type = types.path;
+ description = "Storage localtion for wakapi data";
+ default = "/srv/data/wakapi";
+ example = "/srv/data/wakapi";
+ };
+ };
+
+ config = lib.mkIf cfg.enable (let
+ sopsPath = ../secrets/${hostname}/wakapi.yaml;
+ secrets = ["password_salt"];
+ toSops = (sname: "wakapi/${sname}");
+ in {
+ networking.hosts = {
+ "127.0.0.1" = [cfg.domain];
+ };
+
+ sops.secrets =
+ (lib.genAttrs (map toSops secrets)
+ (name: {
+ sopsFile = sopsPath;
+ mode = "0440";
+ owner = service_user;
+ }));
+
+ services.nginx.virtualHosts."${cfg.domain}" = {
+ enableACME = cfg.doACME;
+ addSSL = cfg.doACME;
+ locations."/" = {
+ proxyPass = "http://localhost:3111";
+ recommendedProxySettings = true;
+ };
+ };
+
+ wilkuu.services.mysql = let
+ in {
+ unix_users = [service_user];
+ databases.wakapi = {
+ enable = true;
+ allowedUsers = [service_user];
+ };
+ };
+
+ services.wakapi = {
+ enable = true;
+ stateDir = cfg.dataDir;
+ passwordSaltFile = config.sops.secrets.wakapi/password_salt;
+ settings = {
+ server = {
+ port = 3111;
+ public_url = cfg.domain;
+ };
+ app = {
+ leaderboard_enabled = false;
+ leaderboard_require_auth = true;
+ inactive_days = 7; # time of previous days within a user must have logged in to be considered active
+ # go time format strings to format human-readable dates
+ # for details, check https://pkg.go.dev/time#Time.Format
+ date_format= "Mon, 02 Jan 2006";
+ datetime_format= "Mon, 02 Jan 2006 15:04";
+ };
+ db = {
+ socket= "/var/lib/mysql/mysql.sock";
+ name = "wakapi";
+ dialect = "mysql";
+ charset = "utf8mb4";
+ };
+ security = {
+ insecure_cookies = false;
+ trust_reverse_proxy_ips= "127.0.0.1";
+ };
+ mail = {
+ # FIXME: Add email
+ enabled = false;
+ provider = "smtp";
+ sender = "<Wakapi ${cfg.email}>";
+ smtp = {
+ };
+ };
+ };
+ database = {
+ dialect = "mysql";
+ createLocally = false;
+ };
+
+ };
+ });
+}
diff --git a/users/wilkuu-server.nix b/users/wilkuu-server.nix
index 732366f..ba5fec2 100644
--- a/users/wilkuu-server.nix
+++ b/users/wilkuu-server.nix
@@ -9,6 +9,7 @@
programs.adb.enable = true;
users.users.wilkuu = {
shell = pkgs.zsh;
+ openssh.authorizedKeys.keyFiles = [../certs/wilkuu_rsa.pub ];
isNormalUser = true;
initialPassword = "PleazeChangeThis123";
extraGroups = [
@@ -34,15 +35,15 @@
(_: {enable = true; });
# homeapps.vnc = true;
- services.wayvnc = {
- enable = true;
- autoStart = false;
- settings = {
- # Todo, bind to vpn and LAN explicitly somehow
- address = "0.0.0.0";
- port = 5900;
- };
- };
+ # services.wayvnc = {
+ # enable = true;
+ # autoStart = false;
+ # settings = {
+ # # Todo, bind to vpn and LAN explicitly somehow
+ # address = "0.0.0.0";
+ # port = 5900;
+ # };
+ # };
home.username = "wilkuu";
home.homeDirectory = "/home/wilkuu";