feat: refactor the modules/servers directory.
This commit is contained in:
parent
4e783c052b
commit
8b754d3a7e
69 changed files with 61 additions and 62 deletions
14
modules/servers/per-server/baldur/default.nix
Normal file
14
modules/servers/per-server/baldur/default.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{...}: {
|
||||
virtualisation.oci-containers.containers.crafty-controller = {
|
||||
image = "registry.gitlab.com/crafty-controller/crafty-4:latest";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"8443:8443"
|
||||
"8123:8123"
|
||||
"19132:19132"
|
||||
"25500-25600:25500-25600"
|
||||
];
|
||||
labels = {
|
||||
"io.containers.autoupdate" = "registry";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/crafty-controller/backups:/crafty/backups"
|
||||
"/var/lib/crafty-controller/logs:/crafty/logs"
|
||||
"/var/lib/crafty-controller/servers:/crafty/servers"
|
||||
"/var/lib/crafty-controller/config:/crafty/app/config"
|
||||
"/var/lib/crafty-controller/import:/crafty/import"
|
||||
];
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
allowedTCPPorts = [25500];
|
||||
allowedUDPPorts = [25500];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.crafty.loadBalancer.servers = [
|
||||
{
|
||||
url = "https://localhost:8443";
|
||||
}
|
||||
];
|
||||
|
||||
routers.crafty = {
|
||||
rule = "Host(`crafty.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "crafty";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/crafty-controller"];
|
||||
server.paths = ["/var/lib/crafty-controller"];
|
||||
};
|
||||
}
|
||||
15
modules/servers/per-server/bragi/default.nix
Normal file
15
modules/servers/per-server/bragi/default.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
./secrets.nix
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
12
modules/servers/per-server/bragi/secrets.nix
Normal file
12
modules/servers/per-server/bragi/secrets.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
age = {
|
||||
secrets = {
|
||||
rclone = {
|
||||
file = ../../../../secrets/rclone.age;
|
||||
};
|
||||
navidrome = {
|
||||
file = ../../../../secrets/navidrome.age;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
32
modules/servers/per-server/bragi/services/audiobookshelf.nix
Normal file
32
modules/servers/per-server/bragi/services/audiobookshelf.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{...}: {
|
||||
services.audiobookshelf = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
systemd.services.audiobookshelf = {
|
||||
after = ["mnt.mount"];
|
||||
bindsTo = ["mnt.mount"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.audiobookshelf.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.audiobookshelf = {
|
||||
rule = "Host(`abs.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "audiobookshelf";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/audiobookshelf"];
|
||||
server.paths = ["/var/lib/audiobookshelf"];
|
||||
};
|
||||
}
|
||||
37
modules/servers/per-server/bragi/services/navidrome.nix
Normal file
37
modules/servers/per-server/bragi/services/navidrome.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{config, ...}: {
|
||||
services.navidrome = {
|
||||
enable = true;
|
||||
settings = {
|
||||
MusicFolder = "/mnt/music";
|
||||
BaseUrl = "https://nd.cronyakatsuki.xyz";
|
||||
};
|
||||
environmentFile = "${config.age.secrets.navidrome.path}";
|
||||
};
|
||||
|
||||
systemd.services.navidrome = {
|
||||
after = ["mnt.mount"];
|
||||
bindsTo = ["mnt.mount"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.navidrome.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:4533";
|
||||
}
|
||||
];
|
||||
|
||||
routers.navidrome = {
|
||||
rule = "Host(`nd.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "navidrome";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/navidrome"];
|
||||
server.paths = ["/var/lib/navidrome"];
|
||||
};
|
||||
}
|
||||
19
modules/servers/per-server/bragi/services/storage-box.nix
Normal file
19
modules/servers/per-server/bragi/services/storage-box.nix
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
config,
|
||||
pkgs,
|
||||
...
|
||||
}: {
|
||||
environment.systemPackages = [pkgs.rclone];
|
||||
|
||||
fileSystems."/mnt" = {
|
||||
device = "storage:";
|
||||
fsType = "rclone";
|
||||
options = [
|
||||
"nodev"
|
||||
"nofail"
|
||||
"allow_other"
|
||||
"args2env"
|
||||
"config=${config.age.secrets.rclone.path}"
|
||||
];
|
||||
};
|
||||
}
|
||||
9
modules/servers/per-server/bragi/services/umurmur.nix
Normal file
9
modules/servers/per-server/bragi/services/umurmur.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{...}: {
|
||||
services.umurmur = {
|
||||
enable = true;
|
||||
openFirewall = true;
|
||||
settings = {
|
||||
welcometext = "Welcome to crony's nutsack.";
|
||||
};
|
||||
};
|
||||
}
|
||||
14
modules/servers/per-server/freyja/default.nix
Normal file
14
modules/servers/per-server/freyja/default.nix
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
39
modules/servers/per-server/freyja/services/immich.nix
Normal file
39
modules/servers/per-server/freyja/services/immich.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{...}: {
|
||||
services.immich = {
|
||||
enable = true;
|
||||
port = 2283;
|
||||
host = "127.0.0.1";
|
||||
accelerationDevices = ["/dev/dri/renderD128"];
|
||||
};
|
||||
|
||||
users.users.immich.extraGroups = ["video" "render"];
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.immich.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:2283";
|
||||
}
|
||||
];
|
||||
|
||||
routers.immich = {
|
||||
rule = "Host(`immich.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "immich";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.postgresqlBackup = {
|
||||
enable = true;
|
||||
databases = [
|
||||
"immich"
|
||||
];
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/immich" "/var/backup/postgresql"];
|
||||
server.paths = ["/var/lib/immich" "/var/backup/postgresql"];
|
||||
};
|
||||
}
|
||||
15
modules/servers/per-server/heimdall/default.nix
Normal file
15
modules/servers/per-server/heimdall/default.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
./secrets.nix
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
9
modules/servers/per-server/heimdall/secrets.nix
Normal file
9
modules/servers/per-server/heimdall/secrets.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
age = {
|
||||
secrets = {
|
||||
wg-heimdall = {
|
||||
file = ../../../../secrets/wg-heimdall.age;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
28
modules/servers/per-server/heimdall/services/atuin.nix
Normal file
28
modules/servers/per-server/heimdall/services/atuin.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
services.atuin = {
|
||||
enable = true;
|
||||
openRegistration = false;
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.atuin.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8888";
|
||||
}
|
||||
];
|
||||
|
||||
routers.atuin = {
|
||||
rule = "Host(`atuin.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "atuin";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/backup/postgresql"];
|
||||
server.paths = ["/var/backup/postgresql"];
|
||||
};
|
||||
}
|
||||
52
modules/servers/per-server/heimdall/services/beszel-hub.nix
Normal file
52
modules/servers/per-server/heimdall/services/beszel-hub.nix
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
{pkgs, ...}: {
|
||||
systemd.services.beszel-hub = {
|
||||
enable = true;
|
||||
description = "Beszel agent";
|
||||
after = ["network.target"];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
Restart = "always";
|
||||
RestartSec = 3;
|
||||
User = "beszel";
|
||||
Group = "beszel";
|
||||
WorkingDirectory = "/var/lib/beszel";
|
||||
};
|
||||
|
||||
script = "${pkgs.beszel}/bin/beszel-hub serve --http '127.0.0.1:6789'";
|
||||
|
||||
wantedBy = ["multi-user.target"];
|
||||
};
|
||||
|
||||
users = {
|
||||
users.beszel = {
|
||||
isSystemUser = true;
|
||||
home = "/var/lib/beszel";
|
||||
createHome = true;
|
||||
group = "beszel";
|
||||
};
|
||||
groups.beszel = {};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.beszel.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:6789";
|
||||
}
|
||||
];
|
||||
|
||||
routers.beszel = {
|
||||
rule = "Host(`beszel.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "beszel";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/beszel"];
|
||||
server.paths = ["/var/lib/beszel"];
|
||||
};
|
||||
}
|
||||
33
modules/servers/per-server/heimdall/services/ntfy-sh.nix
Normal file
33
modules/servers/per-server/heimdall/services/ntfy-sh.nix
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{...}: {
|
||||
services.ntfy-sh = {
|
||||
enable = true;
|
||||
settings = {
|
||||
base-url = "https://ntfy.cronyakatsuki.xyz";
|
||||
listen-http = "127.0.0.1:2586";
|
||||
behind-proxy = true;
|
||||
auth-default-access = "deny-all";
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.ntfy-sh.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:2586";
|
||||
}
|
||||
];
|
||||
|
||||
routers.ntfy-sh = {
|
||||
rule = "Host(`ntfy.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "ntfy-sh";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/ntfy-sh"];
|
||||
server.paths = ["/var/lib/ntfy-sh"];
|
||||
};
|
||||
}
|
||||
39
modules/servers/per-server/heimdall/services/redlib.nix
Normal file
39
modules/servers/per-server/heimdall/services/redlib.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{...}: {
|
||||
services.redlib = {
|
||||
enable = true;
|
||||
address = "127.0.0.1";
|
||||
settings = {
|
||||
ROBOTS_DISABLE_INDEXING = "on";
|
||||
THEME = "gruvboxdark";
|
||||
USE_HLS = "on";
|
||||
};
|
||||
};
|
||||
|
||||
# Setup anubis to block fucking ai bots
|
||||
services.anubis.instances.redlib = {
|
||||
settings = {
|
||||
TARGET = "http://127.0.0.1:8080";
|
||||
BIND_NETWORK = "tcp";
|
||||
BIND = "127.0.0.1:8081";
|
||||
SERVE_ROBOTS_TXT = true;
|
||||
DIFFICULTY = 5;
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.redlib.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8081";
|
||||
}
|
||||
];
|
||||
|
||||
routers.redlib = {
|
||||
rule = "Host(`libreddit.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "redlib";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
29
modules/servers/per-server/heimdall/services/uptime-kuma.nix
Normal file
29
modules/servers/per-server/heimdall/services/uptime-kuma.nix
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{...}: {
|
||||
services.uptime-kuma = {
|
||||
enable = true;
|
||||
settings = {
|
||||
HOST = "127.0.0.1";
|
||||
};
|
||||
};
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.uptime-kuma.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:3001";
|
||||
}
|
||||
];
|
||||
|
||||
routers.uptime-kuma = {
|
||||
rule = "Host(`uptime.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "uptime-kuma";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/uptime-kuma"];
|
||||
server.paths = ["/var/lib/uptime-kuma"];
|
||||
};
|
||||
}
|
||||
24
modules/servers/per-server/heimdall/services/wireguard.nix
Normal file
24
modules/servers/per-server/heimdall/services/wireguard.nix
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{config, ...}: {
|
||||
networking = {
|
||||
nat = {
|
||||
enable = true;
|
||||
enableIPv6 = true;
|
||||
externalInterface = "enp1s0";
|
||||
internalInterfaces = ["wg0"];
|
||||
};
|
||||
firewall = {
|
||||
allowedTCPPorts = [53];
|
||||
allowedUDPPorts = [53 51820];
|
||||
};
|
||||
wg-quick.interfaces.wg0.configFile = "${config.age.secrets.wg-heimdall.path}";
|
||||
};
|
||||
|
||||
services.dnsmasq = {
|
||||
enable = true;
|
||||
settings = {
|
||||
interface = "wg0";
|
||||
};
|
||||
};
|
||||
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
}
|
||||
15
modules/servers/per-server/odin/default.nix
Normal file
15
modules/servers/per-server/odin/default.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
./secrets.nix
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
12
modules/servers/per-server/odin/secrets.nix
Normal file
12
modules/servers/per-server/odin/secrets.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
age = {
|
||||
secrets = {
|
||||
searx = {
|
||||
file = ../../../../secrets/searx.age;
|
||||
};
|
||||
miniflux = {
|
||||
file = ../../../../secrets/miniflux.age;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
27
modules/servers/per-server/odin/services/filebrowser.nix
Normal file
27
modules/servers/per-server/odin/services/filebrowser.nix
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
services.filebrowser = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.filebrowser.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8080";
|
||||
}
|
||||
];
|
||||
|
||||
routers.filebrowser = {
|
||||
rule = "Host(`filebrowser.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "filebrowser";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/filebrowser"];
|
||||
server.paths = ["/var/lib/filebrowser"];
|
||||
};
|
||||
}
|
||||
38
modules/servers/per-server/odin/services/miniflux.nix
Normal file
38
modules/servers/per-server/odin/services/miniflux.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{config, ...}: {
|
||||
services.miniflux = {
|
||||
enable = true;
|
||||
config = {
|
||||
LISTEN_ADDR = "127.0.0.1:8000";
|
||||
BASE_URL = "https://feed.cronyakatsuki.xyz";
|
||||
FETCH_YOUTUBE_WATCH_TIME = "1";
|
||||
};
|
||||
adminCredentialsFile = "${config.age.secrets.miniflux.path}";
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.miniflux.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.miniflux = {
|
||||
rule = "Host(`feed.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "miniflux";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.postgresqlBackup = {
|
||||
enable = true;
|
||||
databases = ["miniflux"];
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/backup/postgresql"];
|
||||
server.paths = ["/var/backup/postgresql"];
|
||||
};
|
||||
}
|
||||
34
modules/servers/per-server/odin/services/searx.nix
Normal file
34
modules/servers/per-server/odin/services/searx.nix
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{config, ...}: {
|
||||
services.searx = {
|
||||
enable = true;
|
||||
settings = {
|
||||
general.instance_name = "Crony's SearXNG";
|
||||
server.port = "8090";
|
||||
server.bind_address = "127.0.0.1";
|
||||
server.secret_key = "@SEARX_SECRET_KEY@";
|
||||
};
|
||||
uwsgiConfig = {
|
||||
http = ":8090";
|
||||
};
|
||||
redisCreateLocally = true;
|
||||
configureUwsgi = true;
|
||||
environmentFile = "${config.age.secrets.searx.path}";
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.searx.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8090";
|
||||
}
|
||||
];
|
||||
|
||||
routers.searx = {
|
||||
rule = "Host(`searx.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "searx";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
37
modules/servers/per-server/odin/services/syncyomi.nix
Normal file
37
modules/servers/per-server/odin/services/syncyomi.nix
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{...}: {
|
||||
virtualisation.oci-containers.containers.syncyomi = {
|
||||
image = "ghcr.io/syncyomi/syncyomi:latest";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"8282:8282"
|
||||
];
|
||||
labels = {
|
||||
"io.containers.autoupdate" = "registry";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/syncyomi:/config"
|
||||
];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.syncyomi.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8282";
|
||||
}
|
||||
];
|
||||
|
||||
routers.syncyomi = {
|
||||
rule = "Host(`syncyomi.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "syncyomi";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/syncyomi"];
|
||||
server.paths = ["/var/lib/syncyomi"];
|
||||
};
|
||||
}
|
||||
47
modules/servers/per-server/odin/services/upfast-cleaner.sh
Executable file
47
modules/servers/per-server/odin/services/upfast-cleaner.sh
Executable file
|
|
@ -0,0 +1,47 @@
|
|||
instance="http://127.0.0.1:8383"
|
||||
|
||||
files=$(curl -s "$instance"/files/)
|
||||
|
||||
# Check for keygens on server
|
||||
if echo "$files" | grep -i "keygen" >> /dev/null; then
|
||||
for file in $(echo "$files" | grep -i "keygen"); do
|
||||
echo "Deleting file $file"
|
||||
curl -X DELETE "$instance/files/$file"
|
||||
done
|
||||
fi
|
||||
|
||||
# Check for common payload names on server
|
||||
if echo "$files" | grep -iE "dorpxy|mner|mnpxy" >> /dev/null; then
|
||||
for file in $(echo "$files" | grep -iE "dorpxy|mner|mnpxy"); do
|
||||
echo "Deleting file $file"
|
||||
curl -X DELETE "$instance/files/$file"
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete common php payloads
|
||||
if echo "$files" | grep -i ".php" >> /dev/null; then
|
||||
for file in $(echo "$files" | grep -i ".php"); do
|
||||
if curl -s "$instance/files/$file" | grep -i "base64_decode" >> /dev/null; then
|
||||
echo "Found payload, deleting file $file"
|
||||
curl -X DELETE "$instance/files/$file"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete all shell scripts that make direct mention of my upfast instance
|
||||
if echo "$files" | grep -i ".sh" >> /dev/null; then
|
||||
for file in $(echo "$files" | grep -i ".sh"); do
|
||||
if curl -s "$instance/files/$file" | grep -i "upfast.cronyakatsuki.xyz/files" >> /dev/null; then
|
||||
echo "Found payload, deleting file $file"
|
||||
curl -X DELETE "$instance/files/$file"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Delete kernel object files
|
||||
if echo "$files" | grep -iE ".*.ko" >> /dev/null; then
|
||||
for file in $(echo "$files" | grep -iE ".*.ko"); do
|
||||
echo "Deleting file $file"
|
||||
curl -X DELETE "$instance/files/$file"
|
||||
done
|
||||
fi
|
||||
98
modules/servers/per-server/odin/services/upfast.nix
Normal file
98
modules/servers/per-server/odin/services/upfast.nix
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
{
|
||||
inputs,
|
||||
pkgs,
|
||||
lib,
|
||||
...
|
||||
}: let
|
||||
upfast-cleaner = pkgs.writeShellApplication {
|
||||
name = "upfast-cleaner";
|
||||
runtimeInputs = with pkgs; [curl];
|
||||
text = ./upfast-cleaner.sh;
|
||||
};
|
||||
in {
|
||||
fileSystems."/var/lib/upfast" = {
|
||||
device = "/root/10gb";
|
||||
fsType = "ext4";
|
||||
options = [
|
||||
"loop"
|
||||
"rw"
|
||||
"usrquota"
|
||||
"grpquota"
|
||||
];
|
||||
};
|
||||
|
||||
users = {
|
||||
users.upfast = {
|
||||
isSystemUser = true;
|
||||
home = "/var/lib/upfast";
|
||||
group = "upfast";
|
||||
};
|
||||
groups.upfast = {};
|
||||
};
|
||||
|
||||
systemd.services.upfast = {
|
||||
enable = true;
|
||||
description = "SelfHosted file upload and share service like 0x0.st";
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
User = "upfast";
|
||||
Group = "upfast";
|
||||
WorkingDirectory = "/var/lib/upfast";
|
||||
Restart = "on-failure";
|
||||
};
|
||||
|
||||
script = "${inputs.upfast.packages.aarch64-linux.default}/bin/upfast -p 8383 -d https://upfast.cronyakatsuki.xyz";
|
||||
|
||||
after = ["var-lib-upfast.mount"];
|
||||
bindsTo = ["var-lib-upfast.mount"];
|
||||
|
||||
wantedBy = ["multi-user.target"];
|
||||
};
|
||||
|
||||
systemd.services.upfast-cleaner = {
|
||||
description = "Script to automatically delete common types of payloads/keygens.";
|
||||
|
||||
requires = ["upfast.service"];
|
||||
after = ["upfast.service"];
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
User = "upfast";
|
||||
Group = "upfast";
|
||||
WorkingDirectory = "/var/lib/upfast";
|
||||
};
|
||||
|
||||
script = "${lib.getExe upfast-cleaner}";
|
||||
};
|
||||
|
||||
systemd.timers.upfast-cleaner = {
|
||||
enable = true;
|
||||
timerConfig = {
|
||||
OnBootSec = "1m";
|
||||
OnUnitActiveSec = "1m";
|
||||
};
|
||||
wantedBy = ["timers.target"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.upfast.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8383";
|
||||
}
|
||||
];
|
||||
|
||||
routers.upfast = {
|
||||
rule = "Host(`upfast.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "upfast";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/upfast"];
|
||||
server.paths = ["/var/lib/upfast"];
|
||||
};
|
||||
}
|
||||
41
modules/servers/per-server/odin/services/wallabag.nix
Normal file
41
modules/servers/per-server/odin/services/wallabag.nix
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{...}: {
|
||||
virtualisation.oci-containers.containers.wallabag = {
|
||||
image = "docker.io/wallabag/wallabag:latest";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"8181:80"
|
||||
];
|
||||
environment = {
|
||||
"SYMFONY__ENV__DOMAIN_NAME" = "https://wallabag.cronyakatsuki.xyz";
|
||||
};
|
||||
labels = {
|
||||
"io.containers.autoupdate" = "registry";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/wallabag/data:/var/www/wallabag/data"
|
||||
"/var/lib/wallabag/images:/var/www/wallabag/web/assets/images"
|
||||
];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.wallabag.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8181";
|
||||
}
|
||||
];
|
||||
|
||||
routers.wallabag = {
|
||||
rule = "Host(`wallabag.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "wallabag";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/wallabag"];
|
||||
server.paths = ["/var/lib/wallabag"];
|
||||
};
|
||||
}
|
||||
32
modules/servers/per-server/odin/services/website.nix
Normal file
32
modules/servers/per-server/odin/services/website.nix
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{...}: {
|
||||
virtualisation.oci-containers.containers.website = {
|
||||
image = "docker.io/nginx:alpine";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"8001:80"
|
||||
];
|
||||
labels = {
|
||||
"io.containers.autoupdate" = "registry";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/website:/usr/share/nginx/html:ro"
|
||||
];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.website.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8001";
|
||||
}
|
||||
];
|
||||
|
||||
routers.website = {
|
||||
rule = "Host(`cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "website";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
15
modules/servers/per-server/thor/default.nix
Normal file
15
modules/servers/per-server/thor/default.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
./secrets.nix
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
18
modules/servers/per-server/thor/secrets.nix
Normal file
18
modules/servers/per-server/thor/secrets.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
age = {
|
||||
secrets = {
|
||||
forgejo-db = {
|
||||
file = ../../../../secrets/forgejo-db.age;
|
||||
};
|
||||
plausible = {
|
||||
file = ../../../../secrets/plausible.age;
|
||||
};
|
||||
conduit = {
|
||||
file = ../../../../secrets/conduit.age;
|
||||
};
|
||||
lemmy-env = {
|
||||
file = ../../../../secrets/lemmy.env.age;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{...}: {
|
||||
services.changedetection-io = {
|
||||
enable = true;
|
||||
playwrightSupport = true;
|
||||
baseURL = "https://changedetection.cronyakatsuki.xyz";
|
||||
behindProxy = true;
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.changedetection.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:5000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.changedetection = {
|
||||
rule = "Host(`changedetection.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "changedetection";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/changedetection-io"];
|
||||
server.paths = ["/var/lib/changedetection-io"];
|
||||
};
|
||||
}
|
||||
39
modules/servers/per-server/thor/services/conduit.nix
Normal file
39
modules/servers/per-server/thor/services/conduit.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{config, ...}: {
|
||||
services.matrix-conduit = {
|
||||
enable = true;
|
||||
settings = {
|
||||
global = {
|
||||
server_name = "cronyakatsuki.xyz";
|
||||
database_backend = "rocksdb";
|
||||
allow_registration = true;
|
||||
allow_check_for_updates = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.conduit.serviceConfig = {
|
||||
EnvironmentFile = ["${config.age.secrets.conduit.path}"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.conduit.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:6167";
|
||||
}
|
||||
];
|
||||
|
||||
routers.conduit = {
|
||||
rule = "Host(`matrix.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "conduit";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/matrix-conduit"];
|
||||
server.paths = ["/var/lib/matrix-conduit"];
|
||||
};
|
||||
}
|
||||
51
modules/servers/per-server/thor/services/forgejo.nix
Normal file
51
modules/servers/per-server/thor/services/forgejo.nix
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{config, ...}: {
|
||||
services.forgejo = {
|
||||
enable = true;
|
||||
settings = {
|
||||
session = {
|
||||
COOKIE_SECURE = true;
|
||||
};
|
||||
service = {
|
||||
REGISTER_MANUAL_CONFIRM = true;
|
||||
ENABLE_CAPTCHA = true;
|
||||
REQUIRE_CAPTCHA_FOR_LOGIN = true;
|
||||
};
|
||||
server = {
|
||||
ROOT_URL = "https://git.cronyakatsuki.xyz";
|
||||
HTTP_ADDR = "127.0.0.1";
|
||||
};
|
||||
};
|
||||
database = {
|
||||
passwordFile = "${config.age.secrets.forgejo-db.path}";
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.forgejo.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:3000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.forgejo = {
|
||||
rule = "Host(`git.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "forgejo";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.openssh = {
|
||||
authorizedKeysFiles = ["/var/lib/%u/.ssh/authorized_keys"];
|
||||
settings = {
|
||||
AllowUsers = ["forgejo"];
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/forgejo"];
|
||||
server.paths = ["/var/lib/forgejo"];
|
||||
};
|
||||
}
|
||||
241
modules/servers/per-server/thor/services/lemmy.nix
Normal file
241
modules/servers/per-server/thor/services/lemmy.nix
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
...
|
||||
}: {
|
||||
# Enable container name DNS for all Podman networks.
|
||||
networking.firewall.interfaces = let
|
||||
matchAll =
|
||||
if !config.networking.nftables.enable
|
||||
then "podman+"
|
||||
else "podman*";
|
||||
in {
|
||||
"${matchAll}".allowedUDPPorts = [53];
|
||||
};
|
||||
|
||||
# Containers
|
||||
virtualisation.oci-containers.containers."lemmy-backend" = {
|
||||
image = "dessalines/lemmy:0.19.13";
|
||||
environmentFiles = [
|
||||
"/run/agenix/lemmy-env"
|
||||
];
|
||||
volumes = [
|
||||
"/var/lib/lemmy/lemmy.hjson:/config/config.hjson:rw,Z"
|
||||
];
|
||||
dependsOn = [
|
||||
"lemmy-db"
|
||||
"lemmy-pictrs"
|
||||
];
|
||||
log-driver = "journald";
|
||||
extraOptions = [
|
||||
"--hostname=lemmy"
|
||||
"--network-alias=lemmy"
|
||||
"--network=lemmy_default"
|
||||
];
|
||||
};
|
||||
systemd.services."podman-lemmy-backend" = {
|
||||
serviceConfig = {
|
||||
Restart = lib.mkOverride 90 "always";
|
||||
};
|
||||
after = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
requires = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
partOf = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
wantedBy = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
};
|
||||
virtualisation.oci-containers.containers."lemmy-db" = {
|
||||
image = "docker.io/postgres:16-alpine";
|
||||
environmentFiles = [
|
||||
"/run/agenix/lemmy-env"
|
||||
];
|
||||
volumes = [
|
||||
"/var/lib/lemmy/volumes/postgres:/var/lib/postgresql/data:rw,Z"
|
||||
];
|
||||
log-driver = "journald";
|
||||
extraOptions = [
|
||||
"--hostname=postgres-lemmy"
|
||||
"--network-alias=postgres"
|
||||
"--network=lemmy_default"
|
||||
];
|
||||
};
|
||||
systemd.services."podman-lemmy-db" = {
|
||||
serviceConfig = {
|
||||
Restart = lib.mkOverride 90 "always";
|
||||
};
|
||||
after = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
requires = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
partOf = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
wantedBy = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
};
|
||||
virtualisation.oci-containers.containers."lemmy-pictrs" = {
|
||||
image = "docker.io/asonix/pictrs:0.5";
|
||||
environmentFiles = [
|
||||
"/run/agenix/lemmy-env"
|
||||
];
|
||||
volumes = [
|
||||
"/var/lib/lemmy/volumes/pictrs:/mnt:rw,Z"
|
||||
];
|
||||
user = "991:991";
|
||||
log-driver = "journald";
|
||||
extraOptions = [
|
||||
"--hostname=pictrs"
|
||||
"--memory=723517440b"
|
||||
"--network-alias=pictrs"
|
||||
"--network=lemmy_default"
|
||||
];
|
||||
};
|
||||
systemd.services."podman-lemmy-pictrs" = {
|
||||
serviceConfig = {
|
||||
Restart = lib.mkOverride 90 "always";
|
||||
};
|
||||
after = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
requires = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
partOf = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
wantedBy = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
};
|
||||
virtualisation.oci-containers.containers."lemmy-proxy" = {
|
||||
image = "nginx:1-alpine";
|
||||
environmentFiles = [
|
||||
"/run/agenix/lemmy-env"
|
||||
];
|
||||
volumes = [
|
||||
"/var/lib/lemmy/nginx_internal.conf:/etc/nginx/nginx.conf:ro,Z"
|
||||
"/var/lib/lemmy/proxy_params:/etc/nginx/proxy_params:ro,Z"
|
||||
];
|
||||
ports = [
|
||||
"127.0.0.1:1236:8536/tcp"
|
||||
];
|
||||
dependsOn = [
|
||||
"lemmy-pictrs"
|
||||
"lemmy-ui"
|
||||
];
|
||||
log-driver = "journald";
|
||||
extraOptions = [
|
||||
"--network-alias=proxy"
|
||||
"--network=lemmy_default"
|
||||
];
|
||||
};
|
||||
systemd.services."podman-lemmy-proxy" = {
|
||||
serviceConfig = {
|
||||
Restart = lib.mkOverride 90 "always";
|
||||
};
|
||||
after = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
requires = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
partOf = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
wantedBy = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
};
|
||||
virtualisation.oci-containers.containers."lemmy-ui" = {
|
||||
image = "dessalines/lemmy-ui:0.19.13";
|
||||
environmentFiles = [
|
||||
"/run/agenix/lemmy-env"
|
||||
];
|
||||
volumes = [
|
||||
"/var/lib/lemmy/volumes/lemmy-ui/extra_themes:/app/extra_themes:rw"
|
||||
];
|
||||
dependsOn = [
|
||||
"lemmy-backend"
|
||||
"lemmy-pictrs"
|
||||
];
|
||||
log-driver = "journald";
|
||||
extraOptions = [
|
||||
"--network-alias=lemmy-ui"
|
||||
"--network=lemmy_default"
|
||||
];
|
||||
};
|
||||
systemd.services."podman-lemmy-ui" = {
|
||||
serviceConfig = {
|
||||
Restart = lib.mkOverride 90 "always";
|
||||
};
|
||||
after = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
requires = [
|
||||
"podman-network-lemmy_default.service"
|
||||
];
|
||||
partOf = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
wantedBy = [
|
||||
"podman-compose-lemmy-root.target"
|
||||
];
|
||||
};
|
||||
|
||||
# Networks
|
||||
systemd.services."podman-network-lemmy_default" = {
|
||||
path = [pkgs.podman];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStop = "podman network rm -f lemmy_default";
|
||||
};
|
||||
script = ''
|
||||
podman network inspect lemmy_default || podman network create lemmy_default
|
||||
'';
|
||||
partOf = ["podman-compose-lemmy-root.target"];
|
||||
wantedBy = ["podman-compose-lemmy-root.target"];
|
||||
};
|
||||
|
||||
# Root service
|
||||
# When started, this will automatically create all resources and start
|
||||
# the containers. When stopped, this will teardown all resources.
|
||||
systemd.targets."podman-compose-lemmy-root" = {
|
||||
unitConfig = {
|
||||
Description = "Root target generated by compose2nix.";
|
||||
};
|
||||
wantedBy = ["multi-user.target"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.lemmy.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:1236";
|
||||
}
|
||||
];
|
||||
|
||||
routers.lemmy = {
|
||||
rule = "Host(`lemmy.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "lemmy";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/lemmy"];
|
||||
server.paths = ["/var/lib/lemmy"];
|
||||
};
|
||||
}
|
||||
31
modules/servers/per-server/thor/services/plausible.nix
Normal file
31
modules/servers/per-server/thor/services/plausible.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{config, ...}: {
|
||||
services.plausible = {
|
||||
enable = true;
|
||||
server = {
|
||||
baseUrl = "https://plausible.cronyakatsuki.xyz";
|
||||
secretKeybaseFile = "${config.age.secrets.plausible.path}";
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.plausible.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.plausible = {
|
||||
rule = "Host(`plausible.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "plausible";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/plausible"];
|
||||
server.paths = ["/var/lib/plausible"];
|
||||
};
|
||||
}
|
||||
15
modules/servers/per-server/tyr/default.nix
Normal file
15
modules/servers/per-server/tyr/default.nix
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{...}: let
|
||||
servicesDir = ./services;
|
||||
serviceFiles =
|
||||
builtins.filter
|
||||
(name: builtins.match "^.*\\.nix$" name != null)
|
||||
(builtins.attrNames (builtins.readDir servicesDir));
|
||||
hostModules = map (fn: import "${servicesDir}/${fn}") serviceFiles;
|
||||
in {
|
||||
imports =
|
||||
[
|
||||
../../common
|
||||
./secrets.nix
|
||||
]
|
||||
++ hostModules;
|
||||
}
|
||||
48
modules/servers/per-server/tyr/secrets.nix
Normal file
48
modules/servers/per-server/tyr/secrets.nix
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
{config, ...}: {
|
||||
age = {
|
||||
secrets = {
|
||||
wg-tyr = {
|
||||
file = ../../../../secrets/wg-tyr.age;
|
||||
};
|
||||
duckdns = {
|
||||
file = ../../../../secrets/duckdns.age;
|
||||
};
|
||||
restic-server-local-pass = {
|
||||
file = ../../../../secrets/restic-server-local-pass.age;
|
||||
};
|
||||
restic-server-pass = {
|
||||
file = ../../../../secrets/restic-server-pass.age;
|
||||
};
|
||||
restic-server-repo = {
|
||||
file = ../../../../secrets/restic-server-repo.age;
|
||||
};
|
||||
restic-server-env = {
|
||||
file = ../../../../secrets/restic-server-env.age;
|
||||
};
|
||||
glance = {
|
||||
file = ../../../../secrets/glance.age;
|
||||
};
|
||||
traefik = {
|
||||
file = ../../../../secrets/traefik.age;
|
||||
owner = "traefik";
|
||||
};
|
||||
ddns = {
|
||||
file = ../../../../secrets/ddns.age;
|
||||
path = "/var/lib/ddns-updater/config.json";
|
||||
owner = "nobody";
|
||||
group = "nogroup";
|
||||
symlink = false;
|
||||
};
|
||||
linkwarden = {
|
||||
file = ../../../../secrets/linkwarden.age;
|
||||
owner = config.services.linkwarden.user;
|
||||
};
|
||||
linkwarden-db = {
|
||||
file = ../../../../secrets/linkwarden.age;
|
||||
};
|
||||
paperless-ngx = {
|
||||
file = ../../../../secrets/paperless-ngx.age;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
118
modules/servers/per-server/tyr/services/dns.nix
Normal file
118
modules/servers/per-server/tyr/services/dns.nix
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
{config, ...}: {
|
||||
# Setup blocky for adblocking
|
||||
services.blocky = {
|
||||
enable = true;
|
||||
settings = {
|
||||
ports.dns = 53;
|
||||
connectIPVersion = "v4";
|
||||
|
||||
upstreams.groups.default = [
|
||||
"127.0.0.1:553"
|
||||
];
|
||||
|
||||
# For initially solving DoH/DoT Requests when no system Resolver is available.
|
||||
bootstrapDns = {
|
||||
upstream = "https://one.one.one.one/dns-query";
|
||||
ips = ["1.1.1.1" "1.0.0.1"];
|
||||
};
|
||||
|
||||
blocking = {
|
||||
denylists = {
|
||||
"default" = [
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/pro.txt"
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/fake.txt"
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/popupads.txt"
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/tif.txt"
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/hoster.txt"
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/gambling.txt"
|
||||
"https://codeberg.org/hagezi/mirror2/raw/branch/main/dns-blocklists/wildcard/native.samsung.txt"
|
||||
];
|
||||
};
|
||||
allowlists = {
|
||||
"default" = [
|
||||
''
|
||||
jnn-pa.googleapis.com
|
||||
challenges.cloudflare.com
|
||||
''
|
||||
];
|
||||
};
|
||||
clientGroupsBlock.default = ["default"];
|
||||
};
|
||||
|
||||
caching = {
|
||||
prefetching = true;
|
||||
minTime = "1m";
|
||||
};
|
||||
|
||||
clientLookup = {
|
||||
upstream = "192.168.0.1";
|
||||
singleNameOrder = [1];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Setup unbound for recursive dns
|
||||
services.unbound = {
|
||||
enable = true;
|
||||
settings = {
|
||||
server = {
|
||||
interface = ["127.0.0.1"];
|
||||
port = 553;
|
||||
do-ip4 = true;
|
||||
do-ip6 = false;
|
||||
access-control = ["127.0.0.1 allow"];
|
||||
harden-glue = true;
|
||||
harden-dnssec-stripped = true;
|
||||
use-caps-for-id = false;
|
||||
edns-buffer-size = 1232;
|
||||
|
||||
hide-identity = true;
|
||||
hide-version = true;
|
||||
|
||||
prefetch = true;
|
||||
cache-max-ttl = 60;
|
||||
cache-max-negative-ttl = 60;
|
||||
serve-original-ttl = true;
|
||||
|
||||
local-zone = [''"home.cronyakatsuki.xyz." transparent''];
|
||||
|
||||
local-data = [
|
||||
''"glance.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"syncthing.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"wallos.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"assistant.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"ddns.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"linkwarden.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"paperless.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
''"komga.home.cronyakatsuki.xyz IN A 192.168.0.5"''
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Setup ddns-updater
|
||||
services.ddns-updater = {
|
||||
enable = true;
|
||||
environment = {
|
||||
RESOLVER_ADDRESS = "127.0.0.1:53";
|
||||
PERIOD = "30s";
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.ddns.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.ddns = {
|
||||
rule = "Host(`ddns.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "ddns";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
614
modules/servers/per-server/tyr/services/glance.nix
Normal file
614
modules/servers/per-server/tyr/services/glance.nix
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
{config, ...}: {
|
||||
services.glance = {
|
||||
enable = true;
|
||||
openFirewall = false;
|
||||
settings = {
|
||||
server = {
|
||||
host = "0.0.0.0";
|
||||
};
|
||||
pages = [
|
||||
{
|
||||
name = "Home";
|
||||
columns = [
|
||||
{
|
||||
size = "small";
|
||||
widgets = [
|
||||
{
|
||||
type = "calendar";
|
||||
first-day-of-week = "monday";
|
||||
}
|
||||
{
|
||||
type = "twitch-channels";
|
||||
channels = [
|
||||
"theprimeagen"
|
||||
];
|
||||
}
|
||||
{
|
||||
type = "rss";
|
||||
title = "Rss Feeds";
|
||||
cache = "1h";
|
||||
feeds = [
|
||||
{
|
||||
url = "https://github.com/NixOS/nixpkgs/commits/nixpkgs-unstable.atom";
|
||||
title = "Nixpkgs Unstable";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
size = "full";
|
||||
widgets = [
|
||||
{
|
||||
type = "group";
|
||||
widgets = [
|
||||
{type = "hacker-news";}
|
||||
{type = "lobsters";}
|
||||
];
|
||||
}
|
||||
{
|
||||
type = "server-stats";
|
||||
servers = [
|
||||
{
|
||||
type = "local";
|
||||
name = "Tyr";
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "Beszel stats";
|
||||
cache = "5m";
|
||||
options = {
|
||||
base-url = "\${BESZEL_URL}";
|
||||
api-key = "\${BESZEL_TOKEN}";
|
||||
};
|
||||
template = ''
|
||||
{{/* Required config options */}}
|
||||
{{ $baseURL := .Options.StringOr "base-url" "" }}
|
||||
{{ $apiKey := .Options.StringOr "api-key" "" }}
|
||||
|
||||
{{/* Error message template */}}
|
||||
{{ define "errorMsg" }}
|
||||
<div class="widget-error-header">
|
||||
<div class="color-negative size-h3">ERROR</div>
|
||||
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="break-all">{{ . }}</p>
|
||||
{{ end }}
|
||||
|
||||
{{ define "formatGigabytes" }}
|
||||
{{ $value := . }}
|
||||
{{ $label := "GB" }}
|
||||
{{- if lt $value 1.0 }}
|
||||
{{ $value = mul $value 1000.0 }}
|
||||
{{ $label = "MB" }}
|
||||
{{- else if lt $value 1000.0 }}
|
||||
{{ else }}
|
||||
{{ $value = div $value 1000.0 }}
|
||||
{{ $label = "TB" }}
|
||||
{{ end }}
|
||||
{{ printf "%.1f" $value }} <span class="color-base size-h5">{{ $label }}</span>
|
||||
{{ end }}
|
||||
|
||||
{{/* Check required fields */}}
|
||||
{{ if or (eq $baseURL "") (eq $apiKey "") }}
|
||||
{{ template "errorMsg" "Some required options are not set." }}
|
||||
{{ else }}
|
||||
|
||||
{{ $token := concat "Bearer " $apiKey }}
|
||||
|
||||
{{ $systemsResponse := newRequest (print $baseURL "/api/collections/systems/records")
|
||||
| withHeader "Authorization" $token
|
||||
| getResponse }}
|
||||
{{ $systems := $systemsResponse.JSON.Array "items" }}
|
||||
|
||||
|
||||
{{ range $n, $system := $systems }}
|
||||
{{ $status := $system.String "status" }}
|
||||
|
||||
{{ $systemStatsRequest := newRequest (print $baseURL "/api/collections/system_stats/records")
|
||||
| withHeader "Authorization" $token
|
||||
| withParameter "sort" "-created"
|
||||
| withParameter "page" "1"
|
||||
| withParameter "perPage" "1"
|
||||
| withParameter "filter" (print "type='1m'&&system='" ($system.String "id") "'")
|
||||
| getResponse }}
|
||||
{{ $systemStats := index ($systemStatsRequest.JSON.Array "items") 0 }}
|
||||
|
||||
{{ $hostname := $system.String "name" }}
|
||||
{{ $uptimeSec := $system.Float "info.u" }}
|
||||
|
||||
{{ $systemTemp := $system.Float "info.dt"}}
|
||||
|
||||
{{ $cpuLoad := $system.Float "info.cpu" }}
|
||||
{{ $cpuLoad1m := $system.Float "info.l1" }}
|
||||
{{ $cpuLoad15m := $system.Float "info.l15" }}
|
||||
|
||||
{{ $memoryUsedPercent := $system.Float "info.mp" }}
|
||||
{{ $memoryTotalGb := $systemStats.Float "stats.m" }}
|
||||
{{ $memoryUsedGb := $systemStats.Float "stats.mu" }}
|
||||
|
||||
{{ $swapTotalGb := $systemStats.Float "stats.s" }}
|
||||
{{ $swapUsedGb := $systemStats.Float "stats.su" }}
|
||||
{{ $swapUsedPercent := mul (div $swapUsedGb $swapTotalGb) 100.0 }}
|
||||
|
||||
{{ $rootUsedPercent := $system.Float "info.dp" }}
|
||||
{{ $rootTotalGb := $systemStats.Float "stats.d" }}
|
||||
{{ $rootUsedGb := $systemStats.Float "stats.du" }}
|
||||
|
||||
<div class="server">
|
||||
<div class="server-info">
|
||||
<div class="server-details">
|
||||
<div class="server-name color-highlight size-h3">{{ $hostname }}</div>
|
||||
<div>
|
||||
{{ if eq $status "up" }}
|
||||
<span>{{ printf "%.1f" (mul $uptimeSec 0.000011574) }}d</span> uptime
|
||||
{{ else }}
|
||||
unreachable
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="shrink-0"{{ if eq $status "up" }} data-popover-type="html" data-popover-margin="0.2rem" data-popover-max-width="400px"{{ end }}>
|
||||
{{- if eq $status "up" }}
|
||||
<div data-popover-html>
|
||||
<div class="flex">
|
||||
<div class="size-h5 text-compact">Kernel</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight">{{ $system.String "info.k" }}</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div class="size-h5 text-compact">CPU</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight">{{ $system.String "info.m" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end }}
|
||||
<svg class="server-icon" stroke="var(--color-{{ if eq $status "up" }}positive{{ else }}negative{{ end }})" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 0 0-.12-1.03l-2.268-9.64a3.375 3.375 0 0 0-3.285-2.602H7.923a3.375 3.375 0 0 0-3.285 2.602l-2.268 9.64a4.5 4.5 0 0 0-.12 1.03v.228m19.5 0a3 3 0 0 1-3 3H5.25a3 3 0 0 1-3-3m19.5 0a3 3 0 0 0-3-3H5.25a3 3 0 0 0-3 3m16.5 0h.008v.008h-.008v-.008Zm-3 0h.008v.008h-.008v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="server-stats">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-end size-h5">
|
||||
<div>CPU</div>
|
||||
{{- if ge $systemTemp 80.0 }}
|
||||
<svg class="server-spicy-cpu-icon" fill="var(--color-negative)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
|
||||
<path fill-rule="evenodd" d="M8.074.945A4.993 4.993 0 0 0 6 5v.032c.004.6.114 1.176.311 1.709.16.428-.204.91-.61.7a5.023 5.023 0 0 1-1.868-1.677c-.202-.304-.648-.363-.848-.058a6 6 0 1 0 8.017-1.901l-.004-.007a4.98 4.98 0 0 1-2.18-2.574c-.116-.31-.477-.472-.744-.28Zm.78 6.178a3.001 3.001 0 1 1-3.473 4.341c-.205-.365.215-.694.62-.59a4.008 4.008 0 0 0 1.873.03c.288-.065.413-.386.321-.666A3.997 3.997 0 0 1 8 8.999c0-.585.126-1.14.351-1.641a.42.42 0 0 1 .503-.235Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
{{- end }}
|
||||
<div class="color-highlight margin-left-auto text-very-compact">{{ $cpuLoad }} <span class="color-base">%</span></div>
|
||||
</div>
|
||||
<div data-popover-type="html">
|
||||
<div data-popover-html>
|
||||
<div class="flex">
|
||||
<div class="size-h5">1M AVG</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">{{ printf "%.1f" $cpuLoad1m }} <span class="color-base size-h5">%</span></div>
|
||||
</div>
|
||||
<div class="flex margin-top-3">
|
||||
<div class="size-h5">15M AVG</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">{{ printf "%.1f" $cpuLoad15m }} <span class="color-base size-h5">%</span></div>
|
||||
</div>
|
||||
<div class="flex margin-top-3">
|
||||
<div class="size-h5">TEMP C</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">{{ printf "%.1f" $systemTemp }} <span class="color-base size-h5">°</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progress-bar progress-bar-combined">
|
||||
<div class="progress-value{{ if ge $cpuLoad1m 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ $cpuLoad1m }}"></div>
|
||||
<div class="progress-value{{ if ge $cpuLoad15m 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ $cpuLoad15m }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between items-end size-h5">
|
||||
<div>RAM</div>
|
||||
<div class="color-highlight text-very-compact">{{ $memoryUsedPercent }} <span class="color-base">%</span></div>
|
||||
</div>
|
||||
<div data-popover-type="html">
|
||||
<div data-popover-html>
|
||||
<div class="flex">
|
||||
<div class="size-h5">RAM</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">
|
||||
{{ template "formatGigabytes" $memoryUsedGb }} <span class="color-base size-h5">/</span> {{ template "formatGigabytes" $memoryTotalGb }}
|
||||
</div>
|
||||
</div>
|
||||
{{- if gt $swapTotalGb 0.0 }}
|
||||
<div class="flex margin-top-3">
|
||||
<div class="size-h5">SWAP</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">
|
||||
{{ template "formatGigabytes" $swapUsedGb }} <span class="color-base size-h5">/</span> {{ template "formatGigabytes" $swapTotalGb }}
|
||||
</div>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-combined">
|
||||
<div class="progress-value{{ if ge $memoryUsedPercent 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ $memoryUsedPercent }}"></div>
|
||||
{{- if gt $swapTotalGb 0.0 }}
|
||||
<div class="progress-value{{ if ge $swapUsedPercent 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ $swapUsedPercent }}"></div>
|
||||
{{- end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between items-end size-h5">
|
||||
<div>DISK</div>
|
||||
<div class="color-highlight text-very-compact">{{ $rootUsedPercent }} <span class="color-base">%</span></div>
|
||||
</div>
|
||||
<div data-popover-type="html">
|
||||
<div data-popover-html>
|
||||
<ul class="list list-gap-2">
|
||||
<li class="flex">
|
||||
<div class="size-h5">/</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">
|
||||
{{ template "formatGigabytes" $rootUsedGb }} <span class="color-base size-h5">/</span> {{ template "formatGigabytes" $rootTotalGb }}
|
||||
</div>
|
||||
</li>
|
||||
{{ range $key, $efs := ($systemStats.Get "stats.efs").Map }}
|
||||
<li class="flex">
|
||||
<div class="size-h5">{{ $key }}</div>
|
||||
<div class="value-separator"></div>
|
||||
<div class="color-highlight text-very-compact">
|
||||
{{ template "formatGigabytes" (($efs.Get "du").Float) }} <span class="color-base size-h5">/</span> {{ template "formatGigabytes" (($efs.Get "d").Float) }}
|
||||
</div>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="progress-bar progress-bar-combined">
|
||||
<div class="progress-value{{ if ge $rootUsedPercent 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ $rootUsedPercent }}"></div>
|
||||
{{ range $key, $efs := ($systemStats.Get "stats.efs").Map }}
|
||||
{{ $efsTotalGb := (($efs.Get "d").Float) }}
|
||||
{{ $efsUsedGb := (($efs.Get "du").Float) }}
|
||||
{{ $efsPercent := mul (div $efsUsedGb $efsTotalGb) 100 }}
|
||||
<div class="progress-value{{ if ge $efsPercent 85.0 }} progress-value-notice{{ end }}" style="--percent: {{ $efsPercent }}"></div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
'';
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
size = "small";
|
||||
widgets = [
|
||||
{
|
||||
type = "weather";
|
||||
units = "metric";
|
||||
hour-format = "24h";
|
||||
location = "Otočac, Hrvatska";
|
||||
}
|
||||
{
|
||||
type = "releases";
|
||||
cache = "1d";
|
||||
repositories = [
|
||||
"glanceapp/glance"
|
||||
"immich-app/immich"
|
||||
"syncthing/syncthing"
|
||||
];
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "GitHub Notifications";
|
||||
url = "https://api.github.com/notifications?all=true&per_page=20";
|
||||
headers = {
|
||||
Authorization = "Bearer \${GITHUB_TOKEN}";
|
||||
Accept = "application/vnd.github+json";
|
||||
};
|
||||
template = ''
|
||||
<ul class="list list-gap-14 collapsible-container" data-collapse-after="6">
|
||||
{{ range .JSON.Array "" }}
|
||||
{{ $url := concat (.String "repository.html_url") "/actions" }}
|
||||
{{ if ne (.String "subject.url") "" }}
|
||||
{{
|
||||
$notification := newRequest (.String "subject.url")
|
||||
| withHeader "Authorization" "Bearer ''${GITHUB_TOKEN}"
|
||||
| getResponse
|
||||
}}
|
||||
{{ if eq $notification.Response.StatusCode 200 }}
|
||||
{{ $url = $notification.JSON.String "html_url" }}
|
||||
{{ else }}
|
||||
{{ $url = (.String "subject.url") | replaceMatches "repos\\/" "" | replaceMatches "api\\." "" | replaceAll "pulls" "pull" }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
<li>
|
||||
<a href="{{ $url }}" class="size-title-dynamic {{ if .Bool "unread" }}color-primary-if-not-visited{{ else }}negative-color{{ end }}" target="_blank" rel="noreferrer">{{ .String "subject.title" }}</a>
|
||||
<ul class="list-horizontal-text flex-nowrap">
|
||||
<li class="min-width-0" {{ .String "updated_at" | parseTime "rfc3339" | toRelativeTime }}></li>
|
||||
<li class="min-width-0"><a target="_blank" href="{{ $url }}">{{ .String "repository.full_name" }}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
'';
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
name = "Gaming";
|
||||
columns = [
|
||||
{
|
||||
size = "small";
|
||||
widgets = [
|
||||
{
|
||||
type = "twitch-top-games";
|
||||
limit = 20;
|
||||
collapse-after = 13;
|
||||
exclude = [
|
||||
"just-chatting"
|
||||
"pools-hot-tubs-and-beaches"
|
||||
"music"
|
||||
"art"
|
||||
"asmr"
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
size = "full";
|
||||
widgets = [
|
||||
{
|
||||
type = "group";
|
||||
widgets = [
|
||||
{
|
||||
type = "reddit";
|
||||
show-thumbnails = true;
|
||||
subreddit = "pcgaming";
|
||||
}
|
||||
{
|
||||
type = "reddit";
|
||||
subreddit = "games";
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
type = "videos";
|
||||
style = "grid-cards";
|
||||
collapse-after-rows = 3;
|
||||
channels = [
|
||||
"UCNvzD7Z-g64bPXxGzaQaa4g" # GameRanx
|
||||
"UCZ7AeeVbyslLM_8-nVy2B8Q" # Skill Up
|
||||
"UCHDxYLv8iovIbhrfl16CNyg" # GameLinked
|
||||
"UC9PBzalIcEQCsiIkq36PyUA" # Digital Foundry
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
size = "small";
|
||||
widgets = [
|
||||
{
|
||||
type = "reddit";
|
||||
subreddit = "gamingnews";
|
||||
limit = 7;
|
||||
style = "vertical-cards";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
name = "SelfHosted Services";
|
||||
columns = [
|
||||
{
|
||||
size = "small";
|
||||
widgets = [
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "Audiobookshelf";
|
||||
title-url = "\${AUDIOBOOKSHELF_URL}";
|
||||
options = {
|
||||
base-url = "\${AUDIOBOOKSHELF_URL}";
|
||||
api-key = "\${AUDIOBOOKSHELF_KEY}";
|
||||
};
|
||||
cache = "5m";
|
||||
template = ''
|
||||
{{ $baseURL := .Options.StringOr "base-url" "" }}
|
||||
{{ $apiKey := .Options.StringOr "api-key" "" }}
|
||||
|
||||
{{ define "errorMsg" }}
|
||||
<div class="widget-error-header">
|
||||
<div class="color-negative size-h3">ERROR</div>
|
||||
<svg class="widget-error-icon" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="break-all">{{ . }}</p>
|
||||
{{ end }}
|
||||
|
||||
{{ $bearer := printf "Bearer %s" $apiKey }}
|
||||
{{ $librariesRequestURL := concat $baseURL "/api/libraries" }}
|
||||
{{ $librariesResponse := newRequest $librariesRequestURL
|
||||
| withHeader "Content-Type" "application/json"
|
||||
| withHeader "Authorization" $bearer
|
||||
| getResponse }}
|
||||
|
||||
{{ if $librariesResponse.JSON.Exists "libraries" }}
|
||||
{{ $all_libraries := $librariesResponse.JSON.Array "libraries" }}
|
||||
|
||||
{{ $books_count := 0 }}
|
||||
{{ $books_duration := 0 }}
|
||||
{{ $podcasts_count := 0 }}
|
||||
{{ $podcasts_duration := 0 }}
|
||||
|
||||
{{ range $library := $all_libraries }}
|
||||
{{ $lib_id := $library.String "id" }}
|
||||
{{ $lib_request_url := concat $baseURL "/api/libraries/" $lib_id "/stats"}}
|
||||
{{ $lib_stats := newRequest $lib_request_url
|
||||
| withHeader "Content-Type" "application/json"
|
||||
| withHeader "Authorization" $bearer
|
||||
| getResponse }}
|
||||
{{ $lib_type := $library.String "mediaType" }}
|
||||
{{ $lib_item_count := $lib_stats.JSON.Int "totalItems" }}
|
||||
{{ $lib_total_duration := $lib_stats.JSON.Int "totalDuration" }}
|
||||
{{ if eq $lib_type "book" }}
|
||||
{{ $books_count = add $books_count $lib_item_count }}
|
||||
{{ $books_duration = add $books_duration $lib_total_duration }}
|
||||
{{ else if eq $lib_type "podcast" }}
|
||||
{{ $podcasts_count = add $podcasts_count $lib_item_count }}
|
||||
{{ $podcasts_duration = add $podcasts_duration $lib_total_duration }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $books_duration = duration (concat (printf "%d" $books_duration) "s") }}
|
||||
{{ $podcasts_duration = duration (concat (printf "%d" $podcasts_duration) "s") }}
|
||||
|
||||
<div class="flex flex-column gap-5">
|
||||
<div class="flex justify-between text-center">
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $books_count }}</div>
|
||||
<div class="size-h5 uppercase">Books</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $books_duration }}</div>
|
||||
<div class="size-h5 uppercase">Duration</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $podcasts_count }}</div>
|
||||
<div class="size-h5 uppercase">Podcasts</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $podcasts_duration }}</div>
|
||||
<div class="size-h5 uppercase">Duration</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
{{ template "errorMsg" "Could not fetch data from API!" }}
|
||||
{{ end }}
|
||||
'';
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "Immich Stats";
|
||||
cache = "1d";
|
||||
url = "https://immich.cronyakatsuki.xyz/api/server/statistics";
|
||||
headers = {
|
||||
x-api-key = "\${IMMICH_API_KEY}";
|
||||
Accept = "application/json";
|
||||
};
|
||||
template = ''
|
||||
<div class="flex justify-between text-center">
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ .JSON.Int "photos" | formatNumber }}</div>
|
||||
<div class="size-h6">PHOTOS</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ .JSON.Int "videos" | formatNumber }}</div>
|
||||
<div class="size-h6">VIDEOS</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ div (.JSON.Int "usage" | toFloat) 1073741824 | toInt | formatNumber }}GB</div>
|
||||
<div class="size-h6">USAGE</div>
|
||||
</div>
|
||||
</div>
|
||||
'';
|
||||
}
|
||||
{
|
||||
type = "custom-api";
|
||||
title = "Jellyfin Stats";
|
||||
base-url = "\${JELLYFIN_URL}";
|
||||
options = {
|
||||
url = "\${JELLYFIN_URL}";
|
||||
key = "\${JELLYFIN_KEY}";
|
||||
};
|
||||
template = ''
|
||||
{{ $url := .Options.StringOr "url" "" }}
|
||||
{{ $key := .Options.StringOr "key" "" }}
|
||||
|
||||
{{- if or (eq $url "") (eq $key "") -}}
|
||||
|
||||
<p>Error: The URL or API Key was not configured in the widget options.</p>
|
||||
|
||||
{{- else -}}
|
||||
|
||||
{{- $requestUrl := printf "%s/emby/Items/Counts?api_key=%s" $url $key -}}
|
||||
{{- $jellyfinData := newRequest $requestUrl | getResponse -}}
|
||||
|
||||
{{- if eq $jellyfinData.Response.StatusCode 200 -}}
|
||||
<div class="flex flex-column gap-5">
|
||||
<div class="flex justify-between text-center">
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "MovieCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">Movies</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "SeriesCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">TV Shows</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "EpisodeCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">Episodes</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="color-highlight size-h3">{{ $jellyfinData.JSON.Int "SongCount" | formatNumber }}</div>
|
||||
<div class="size-h5 uppercase">Songs</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{- else -}}
|
||||
<p>Failed: {{ $jellyfinData.Response.Status }}</p>
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
'';
|
||||
}
|
||||
];
|
||||
}
|
||||
{
|
||||
size = "full";
|
||||
widgets = [
|
||||
];
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.glance.serviceConfig = {
|
||||
EnvironmentFile = ["${config.age.secrets.glance.path}"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.glance.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8080";
|
||||
}
|
||||
];
|
||||
|
||||
routers.glance = {
|
||||
rule = "Host(`glance.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "glance";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
48
modules/servers/per-server/tyr/services/home-assistant.nix
Normal file
48
modules/servers/per-server/tyr/services/home-assistant.nix
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
virtualisation.oci-containers.containers.homeassistant = {
|
||||
image = "docker.io/homeassistant/home-assistant:stable";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"8123:8123"
|
||||
];
|
||||
devices = [
|
||||
"/dev/ttyUSB0:/dev/ttyUSB0"
|
||||
];
|
||||
privileged = true;
|
||||
capabilities = {
|
||||
NET_ADMIN = true;
|
||||
NET_RAW = true;
|
||||
};
|
||||
labels = {
|
||||
"io.containers.autoupdate" = "registry";
|
||||
};
|
||||
extraOptions = ["--network=host"];
|
||||
volumes = [
|
||||
"/etc/localtime:/etc/localtime:ro"
|
||||
"/var/lib/homeassistant:/config"
|
||||
"/run/dbus:/run/dbus:ro"
|
||||
];
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/homeassistant"];
|
||||
server.paths = ["/var/lib/homeassistant"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.assistant.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8123";
|
||||
}
|
||||
];
|
||||
|
||||
routers.assistant = {
|
||||
rule = "Host(`assistant.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "assistant";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
28
modules/servers/per-server/tyr/services/komga.nix
Normal file
28
modules/servers/per-server/tyr/services/komga.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
services.komga = {
|
||||
enable = true;
|
||||
settings.server.port = 8081;
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.komga.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8081";
|
||||
}
|
||||
];
|
||||
|
||||
routers.komga = {
|
||||
rule = "Host(`komga.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "komga";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/komga"];
|
||||
server.paths = ["/var/lib/komga"];
|
||||
};
|
||||
}
|
||||
31
modules/servers/per-server/tyr/services/linkwarden.nix
Normal file
31
modules/servers/per-server/tyr/services/linkwarden.nix
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{config, ...}: {
|
||||
services.linkwarden = {
|
||||
enable = true;
|
||||
secretFiles = {
|
||||
NEXTAUTH_SECRET = config.age.secrets.linkwarden.path;
|
||||
POSTGRES_PASSWORD = config.age.secrets.linkwarden-db.path;
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/linkwarden"];
|
||||
server.paths = ["/var/lib/linkwarden"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.linkwarden.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:3000";
|
||||
}
|
||||
];
|
||||
|
||||
routers.linkwarden = {
|
||||
rule = "Host(`linkwarden.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "linkwarden";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
18
modules/servers/per-server/tyr/services/mosquitto.nix
Normal file
18
modules/servers/per-server/tyr/services/mosquitto.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
services.mosquitto = {
|
||||
enable = true;
|
||||
listeners = [
|
||||
{
|
||||
users.crony = {
|
||||
acl = ["readwrite #"];
|
||||
hashedPassword = "$7$101$3MqAfbz8vp9VMrMG$nvHnl1fEX1H3JeH98JGBjdBiKZ02RW7kSBMSQK2fHzU+3hinebJxW8QMpdaH9TYKoeM9PS0y+pzvYnrk0/tkIQ==";
|
||||
};
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
networking.firewall = {
|
||||
enable = true;
|
||||
allowedTCPPorts = [1883];
|
||||
};
|
||||
}
|
||||
9
modules/servers/per-server/tyr/services/nfs-server.nix
Normal file
9
modules/servers/per-server/tyr/services/nfs-server.nix
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
services.nfs.server = {
|
||||
enable = true;
|
||||
exports = ''
|
||||
/export/nfs 192.168.0.0/24(rw,sync,no_subtree_check) 172.16.0.0/24(rw,sync,no_subtree_check)
|
||||
'';
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [2049];
|
||||
}
|
||||
34
modules/servers/per-server/tyr/services/paperless-ngx.nix
Normal file
34
modules/servers/per-server/tyr/services/paperless-ngx.nix
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{config, ...}: {
|
||||
services.paperless = {
|
||||
enable = true;
|
||||
passwordFile = config.age.secrets.paperless-ngx.path;
|
||||
domain = "paperless.home.cronyakatsuki.xyz";
|
||||
settings = {
|
||||
PAPERLESS_OCR_LANGUAGE = "hrv+eng";
|
||||
PAPERLESS_ADMIN_USER = "crony";
|
||||
PAPERLESS_URL = "https://paperless.home.cronyakatsuki.xyz";
|
||||
};
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.paperless.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:28981";
|
||||
}
|
||||
];
|
||||
|
||||
routers.paperless = {
|
||||
rule = "Host(`paperless.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "paperless";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/paperless"];
|
||||
server.paths = ["/var/lib/paperless"];
|
||||
};
|
||||
}
|
||||
29
modules/servers/per-server/tyr/services/syncthing.nix
Normal file
29
modules/servers/per-server/tyr/services/syncthing.nix
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
services.syncthing = {
|
||||
enable = true;
|
||||
openDefaultPorts = true;
|
||||
guiAddress = "0.0.0.0:8384";
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/syncthing"];
|
||||
server.paths = ["/var/lib/syncthing"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.syncthing.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8384";
|
||||
}
|
||||
];
|
||||
|
||||
routers.syncthing = {
|
||||
rule = "Host(`syncthing.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "syncthing";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
45
modules/servers/per-server/tyr/services/traefik.nix
Normal file
45
modules/servers/per-server/tyr/services/traefik.nix
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
{config, ...}: {
|
||||
services.traefik = {
|
||||
enable = true;
|
||||
staticConfigOptions = {
|
||||
serversTransport.insecureSkipVerify = true;
|
||||
log = {level = "DEBUG";};
|
||||
certificatesResolvers = {
|
||||
porkbun = {
|
||||
acme = {
|
||||
email = "crony@cronyakatsuki.xyz";
|
||||
storage = "/var/lib/traefik/acme.json";
|
||||
caserver = "https://acme-v02.api.letsencrypt.org/directory";
|
||||
dnsChallenge = {
|
||||
provider = "porkbun";
|
||||
resolvers = ["127.0.0.1"];
|
||||
propagation = {
|
||||
delayBeforeChecks = 60;
|
||||
disableChecks = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
api = {};
|
||||
entryPoints = {
|
||||
web = {
|
||||
address = ":80";
|
||||
http.redirections.entryPoint = {
|
||||
to = "websecure";
|
||||
scheme = "https";
|
||||
};
|
||||
};
|
||||
websecure = {
|
||||
address = ":443";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.traefik.serviceConfig = {
|
||||
EnvironmentFile = ["${config.age.secrets.traefik.path}"];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [80 443];
|
||||
}
|
||||
38
modules/servers/per-server/tyr/services/wallos.nix
Normal file
38
modules/servers/per-server/tyr/services/wallos.nix
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
virtualisation.oci-containers.containers.wallos = {
|
||||
image = "docker.io/bellamy/wallos:latest";
|
||||
autoStart = true;
|
||||
ports = [
|
||||
"8282:80/tcp"
|
||||
];
|
||||
labels = {
|
||||
"io.containers.autoupdate" = "registry";
|
||||
};
|
||||
volumes = [
|
||||
"/var/lib/wallos/db:/var/www/html/db"
|
||||
"/var/lib/wallos/logos:/var/www/html/images/uploads/logos"
|
||||
];
|
||||
};
|
||||
|
||||
services.restic.backups = {
|
||||
local.paths = ["/var/lib/wallos"];
|
||||
server.paths = ["/var/lib/wallos"];
|
||||
};
|
||||
|
||||
services.traefik.dynamicConfigOptions.http = {
|
||||
services.wallos.loadBalancer.servers = [
|
||||
{
|
||||
url = "http://localhost:8282";
|
||||
}
|
||||
];
|
||||
|
||||
routers.wallos = {
|
||||
rule = "Host(`wallos.home.cronyakatsuki.xyz`)";
|
||||
tls = {
|
||||
certResolver = "porkbun";
|
||||
};
|
||||
service = "wallos";
|
||||
entrypoints = "websecure";
|
||||
};
|
||||
};
|
||||
}
|
||||
17
modules/servers/per-server/tyr/services/wireguard.nix
Normal file
17
modules/servers/per-server/tyr/services/wireguard.nix
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{config, ...}: {
|
||||
networking = {
|
||||
nat = {
|
||||
enable = true;
|
||||
enableIPv6 = true;
|
||||
externalInterface = "enp1s0";
|
||||
internalInterfaces = ["wg0"];
|
||||
};
|
||||
firewall = {
|
||||
allowedTCPPorts = [53];
|
||||
allowedUDPPorts = [53 51820];
|
||||
};
|
||||
wg-quick.interfaces.wg0.configFile = "${config.age.secrets.wg-tyr.path}";
|
||||
};
|
||||
|
||||
boot.kernel.sysctl."net.ipv4.ip_forward" = 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue