feat: network overhaul

This commit is contained in:
Leon Schwarzäugl 2025-06-15 04:36:40 +02:00
parent 22fe55c284
commit ed15ef02bb
Signed by: swarsel
GPG key ID: 26A54C31F2A4FD84
34 changed files with 1704 additions and 1037 deletions

View file

@ -3,6 +3,7 @@ let
cfg = config.services.firefly-iii;
fireflyDomain = "stonks.swarsel.win";
fireflyUser = "firefly-iii";
serviceName = "firefly";
in
{
options.swarselsystems.modules.server.firefly = lib.mkEnableOption "enable firefly-iii on server";
@ -31,6 +32,7 @@ in
APP_KEY_FILE = config.sops.secrets.firefly-iii-app-key.path;
APP_ENV = "local";
DB_CONNECTION = "sqlite";
TRUSTED_PROXIES = "**";
# AUTHENTICATION_GUARD = "remote_user_guard";
# AUTHENTICATION_GUARD_HEADER = "X-User";
# AUTHENTICATION_GUARD_EMAIL = "X-Email";
@ -42,52 +44,7 @@ in
nginx = {
virtualHosts = {
"${fireflyDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
# main config is automatically added by nixos firefly config.
# hence, only provide certificate
locations = {
"/" = {
extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag (done by NixOS)
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
'';
};
"/oauth2/" = {
proxyPass = "http://oauth2-proxy";
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
"= /oauth2/auth" = {
proxyPass = "http://oauth2-proxy/oauth2/auth";
extraConfig = ''
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
"/api" = {
extraConfig = ''
index index.php;
@ -102,5 +59,70 @@ in
};
};
};
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:80" = { };
};
};
};
virtualHosts = {
"${fireflyDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
# main config is automatically added by nixos firefly config.
# hence, only provide certificate
locations = {
"/" = {
proxyPass = "http://${serviceName}";
extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# pass information via X-User and X-Email headers to backend,
# requires running with --set-xauthrequest flag (done by NixOS)
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# if you enabled --pass-access-token, this will pass the token to the backend
auth_request_set $token $upstream_http_x_auth_request_access_token;
proxy_set_header X-Access-Token $token;
# if you enabled --cookie-refresh, this is needed for it to work with auth_request
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
'';
};
"/oauth2/" = {
proxyPass = "http://oauth2-proxy";
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
"= /oauth2/auth" = {
proxyPass = "http://oauth2-proxy/oauth2/auth";
extraConfig = ''
internal;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
'';
};
"/api" = {
proxyPass = "http://${serviceName}";
};
};
};
};
};
};
}

View file

@ -1,4 +1,7 @@
{ lib, config, ... }:
let
serviceName = "freshrss";
in
{
options.swarselsystems.modules.server.freshrss = lib.mkEnableOption "enable freshrss on server";
config = lib.mkIf config.swarselsystems.modules.server.freshrss {
@ -52,7 +55,14 @@
# config.sops.templates.freshrss-env.path
# ];
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:80" = { };
};
};
};
virtualHosts = {
"signpost.swarsel.win" = {
enableACME = true;
@ -60,6 +70,7 @@
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://${serviceName}";
extraConfig = ''
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;

View file

@ -1,17 +1,22 @@
{ lib, config, ... }:
let
serviceDomain = "shots.swarsel.win";
servicePort = 3001;
serviceUser = "immich";
serviceName = "immich";
in
{
options.swarselsystems.modules.server.immich = lib.mkEnableOption "enable immich on server";
config = lib.mkIf config.swarselsystems.modules.server.immich {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
users.users.immich = {
users.users."${serviceUser}" = {
extraGroups = [ "video" "render" "users" ];
};
# sops.secrets.nextcloudadminpass = { owner = "nextcloud"; };
services.immich = {
enable = true;
port = 3001;
host = "0.0.0.0";
port = servicePort;
openFirewall = true;
mediaLocation = "/Vault/Eternor/Immich";
environment = {
@ -19,16 +24,24 @@
};
};
networking.firewall.allowedTCPPorts = [ 3001 ];
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"shots.swarsel.win" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:3001";
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 0;

View file

@ -1,8 +1,14 @@
{ pkgs, lib, config, ... }:
let
serviceDomain = "screen.swarsel.win";
servicePort = 8096;
serviceName = "jellyfin";
serviceUser = "jellyfin";
in
{
options.swarselsystems.modules.server.jellyfin = lib.mkEnableOption "enable jellyfin on server";
config = lib.mkIf config.swarselsystems.modules.server.jellyfin {
users.users.jellyfin = {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
users.users."${serviceUser}" = {
extraGroups = [ "video" "render" "users" ];
};
nixpkgs.config.packageOverrides = pkgs: {
@ -19,19 +25,26 @@
};
services.jellyfin = {
enable = true;
user = "jellyfin";
user = serviceUser;
openFirewall = true; # this works only for the default ports
};
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"screen.swarsel.win" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:8096";
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 0;
'';

View file

@ -1,62 +1,52 @@
{ self, lib, pkgs, config, ... }:
let
certsSopsFile = self + /secrets/certs/secrets.yaml;
kanidmDomain = "sso.swarsel.win";
serviceDomain = "sso.swarsel.win";
servicePort = 8300;
serviceUser = "kanidm";
serviceGroup = serviceUser;
serviceName = "kanidm";
oauth2ProxyDomain = "soauth.swarsel.win";
kanidmPort = 8300;
oauth2ProxyPort = 3004;
in
{
options.swarselsystems.modules.server.kanidm = lib.mkEnableOption "enable kanidm on server";
config = lib.mkIf config.swarselsystems.modules.server.kanidm {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
users.users.kanidm = {
group = "kanidm";
users.users."${serviceUser}" = {
group = serviceGroup;
isSystemUser = true;
};
users.groups.kanidm = { };
users.groups."${serviceGroup}" = { };
sops = {
secrets = {
"oauth2-cookie-secret" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; };
"kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-idm-admin-pw" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-immich" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-paperless" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-forgejo" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-grafana" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-nextcloud" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-freshrss" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-oauth2-proxy" = { owner = "kanidm"; group = "kanidm"; mode = "0440"; };
"kanidm-oauth2-proxy-client" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; };
};
templates = {
"kanidm-oauth2-proxy-client-env" = {
content = ''
OAUTH2_PROXY_CLIENT_SECRET="${config.sops.placeholder.kanidm-oauth2-proxy-client}"
OAUTH2_PROXY_COOKIE_SECRET=${config.sops.placeholder.oauth2-cookie-secret}
'';
owner = "oauth2-proxy";
group = "oauth2-proxy";
mode = "0440";
};
"kanidm-self-signed-crt" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-self-signed-key" = { sopsFile = certsSopsFile; owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-admin-pw" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-idm-admin-pw" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-immich" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-paperless" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-forgejo" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-grafana" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-nextcloud" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-freshrss" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
"kanidm-oauth2-proxy" = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
};
};
networking.firewall.allowedTCPPorts = [ servicePort ];
services = {
kanidm = {
package = pkgs.kanidmWithSecretProvisioning;
enableServer = true;
serverSettings = {
domain = kanidmDomain;
origin = "https://${kanidmDomain}";
domain = serviceDomain;
origin = "https://${serviceDomain}";
tls_chain = config.sops.secrets.kanidm-self-signed-crt.path;
tls_key = config.sops.secrets.kanidm-self-signed-key.path;
bindaddress = "0.0.0.0:${toString kanidmPort}";
bindaddress = "0.0.0.0:${toString servicePort}";
trust_x_forward_for = true;
};
enableClient = true;
@ -177,19 +167,6 @@ in
};
};
};
# freshrss = {
# displayName = "FreshRSS";
# originUrl = "https://signpost.swarsel.win/apps/sociallogin/custom_oidc/kanidm";
# originLanding = "https://signpost.swarsel.win/";
# basicSecretFile = config.sops.secrets.kanidm-freshrss.path;
# allowInsecureClientDisablePkce = true;
# scopeMaps."freshrss.access" = [
# "openid"
# "email"
# "profile"
# ];
# preferShortUsername = true;
# };
oauth2-proxy = {
displayName = "Oauth2-Proxy";
originUrl = "https://${oauth2ProxyDomain}/oauth2/callback";
@ -226,96 +203,34 @@ in
};
};
};
oauth2-proxy = {
enable = true;
cookie = {
domain = ".swarsel.win";
secure = true;
expire = "900m";
secret = null; # set by service EnvironmentFile
};
clientSecret = null; # set by service EnvironmentFile
reverseProxy = true;
httpAddress = "0.0.0.0:${builtins.toString oauth2ProxyPort}";
redirectURL = "https://${oauth2ProxyDomain}/oauth2/callback";
setXauthrequest = true;
extraConfig = {
code-challenge-method = "S256";
whitelist-domain = ".swarsel.win";
set-authorization-header = true;
pass-access-token = true;
skip-jwt-bearer-tokens = true;
upstream = "static://202";
oidc-issuer-url = "https://${kanidmDomain}/oauth2/openid/oauth2-proxy";
provider-display-name = "Kanidm";
};
provider = "oidc";
scope = "openid email";
loginURL = "https://${kanidmDomain}/ui/oauth2";
redeemURL = "https://${kanidmDomain}/oauth2/token";
validateURL = "https://${kanidmDomain}/oauth2/openid/oauth2-proxy/userinfo";
clientID = "oauth2-proxy";
email.domains = [ "*" ];
};
};
systemd.services = {
kanidm.serviceConfig.RestartSec = "30";
oauth2-proxy = {
after = [ "kanidm.service" ];
serviceConfig = {
RuntimeDirectory = "oauth2-proxy";
RuntimeDirectoryMode = "0750";
UMask = "007"; # TODO remove once https://github.com/oauth2-proxy/oauth2-proxy/issues/2141 is fixed
RestartSec = "60"; # Retry every minute
EnvironmentFile = [
config.sops.templates.kanidm-oauth2-proxy-client-env.path
];
};
};
};
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
kanidm = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString kanidmPort}" = { };
};
};
oauth2-proxy = {
servers = {
"192.168.1.2:${builtins.toString oauth2ProxyPort}" = { };
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"${kanidmDomain}" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "https://kanidm";
proxyPass = "https://${serviceName}";
};
};
extraConfig = ''
proxy_ssl_verify off;
'';
};
"${oauth2ProxyDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://oauth2-proxy";
};
};
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
};
};
};

View file

@ -1,36 +1,48 @@
{ pkgs, lib, config, ... }:
let
serviceName = "kavita";
serviceUser = "kavita";
serviceDomain = "scroll.swarsel.win";
servicePort = 8080;
in
{
options.swarselsystems.modules.server.kavita = lib.mkEnableOption "enable kavita on server";
config = lib.mkIf config.swarselsystems.modules.server.kavita {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
environment.systemPackages = with pkgs; [
calibre
];
users.users.kavita = {
users.users."${serviceUser}" = {
extraGroups = [ "users" ];
};
sops.secrets.kavita = { owner = "kavita"; };
sops.secrets.kavita = { owner = serviceUser; };
networking.firewall.allowedTCPPorts = [ 8080 ];
services.kavita = {
enable = true;
user = "kavita";
settings.Port = 8080;
user = serviceUser;
settings.Port = servicePort;
tokenKeyFile = config.sops.secrets.kavita.path;
};
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"scroll.swarsel.win" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:8080";
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 0;
'';

View file

@ -55,7 +55,7 @@ in
};
};
networking.firewall.allowedTCPPorts = [ postgresPort ];
networking.firewall.allowedTCPPorts = [ servicePort postgresPort ];
systemd.services.postgresql.postStart =
let
@ -86,17 +86,24 @@ in
host ${serviceDB} ${serviceDB} 10.88.0.0/16 scram-sha-256
'';
};
};
nginx = {
virtualHosts = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:${toString servicePort}";
};
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://${serviceName}";
};
};
};

View file

@ -1,6 +1,13 @@
{ config, lib, pkgs, sops, ... }:
let
matrixDomain = "swatrix.swarsel.win";
serviceName = "matrix";
synapsePort = 8008;
synapseUser = "matrix-synapse";
whatsappPort = 29318;
telegramPort = 29317;
signalPort = 29328;
baseUrl = "https://${matrixDomain}";
clientConfig."m.homeserver".base_url = baseUrl;
serverConfig."m.server" = "${matrixDomain}:443";
@ -11,8 +18,8 @@ let
'';
in
{
options.swarselsystems.modules.server.matrix = lib.mkEnableOption "enable matrix on server";
config = lib.mkIf config.swarselsystems.modules.server.matrix {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
environment.systemPackages = with pkgs; [
matrix-synapse
lottieconverter
@ -21,24 +28,24 @@ in
sops = {
secrets = {
matrixsharedsecret = { owner = "matrix-synapse"; };
mautrixtelegram_as = { owner = "matrix-synapse"; };
mautrixtelegram_hs = { owner = "matrix-synapse"; };
mautrixtelegram_api_id = { owner = "matrix-synapse"; };
mautrixtelegram_api_hash = { owner = "matrix-synapse"; };
matrixsharedsecret = { owner = synapseUser; };
mautrixtelegram_as = { owner = synapseUser; };
mautrixtelegram_hs = { owner = synapseUser; };
mautrixtelegram_api_id = { owner = synapseUser; };
mautrixtelegram_api_hash = { owner = synapseUser; };
};
templates = {
"matrix_user_register.sh".content = ''
register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:8008
register_new_matrix_user -k ${config.sops.placeholder.matrixsharedsecret} http://localhost:${builtins.toString synapsePort}
'';
matrixshared = {
owner = "matrix-synapse";
owner = synapseUser;
content = ''
registration_shared_secret: ${config.sops.placeholder.matrixsharedsecret}
'';
};
mautrixtelegram = {
owner = "matrix-synapse";
owner = synapseUser;
content = ''
MAUTRIX_TELEGRAM_APPSERVICE_AS_TOKEN=${config.sops.placeholder.mautrixtelegram_as}
MAUTRIX_TELEGRAM_APPSERVICE_HS_TOKEN=${config.sops.placeholder.mautrixtelegram_hs}
@ -49,6 +56,8 @@ in
};
};
networking.firewall.allowedTCPPorts = [ 8008 8448 ];
systemd = {
timers."restart-bridges" = {
wantedBy = [ "timers.target" ];
@ -118,9 +127,9 @@ in
public_baseurl = "https://${matrixDomain}";
listeners = [
{
port = 8008;
port = synapsePort;
bind_addresses = [
"127.0.0.1"
"0.0.0.0"
# "::1"
];
type = "http";
@ -146,13 +155,13 @@ in
registerToSynapse = false;
settings = {
homeserver = {
address = "http://localhost:8008";
address = "http://localhost:${builtins.toString synapsePort}";
domain = matrixDomain;
};
appservice = {
address = "http://localhost:29317";
hostname = "localhost";
port = "29317";
address = "http://localhost:${builtins.toString telegramPort}";
hostname = "0.0.0.0";
port = telegramPort;
provisioning.enabled = true;
id = "telegram";
# ephemeral_events = true; # not needed due to double puppeting
@ -192,13 +201,13 @@ in
registerToSynapse = false;
settings = {
homeserver = {
address = "http://localhost:8008";
address = "http://localhost:${builtins.toString synapsePort}";
domain = matrixDomain;
};
appservice = {
address = "http://localhost:29318";
hostname = "127.0.0.1";
port = 29318;
address = "http://localhost:${builtins.toString whatsappPort}";
hostname = "0.0.0.0";
port = whatsappPort;
database = {
type = "postgres";
uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
@ -239,14 +248,13 @@ in
registerToSynapse = false;
settings = {
homeserver = {
address = "http://localhost:8008";
address = "http://localhost:${builtins.toString synapsePort}";
domain = matrixDomain;
};
appservice = {
address = "http://localhost:29328";
hostname = "127.0.0.1";
port = 29328;
address = "http://localhost:${builtins.toString signalPort}";
hostname = "0.0.0.0";
port = signalPort;
database = {
type = "postgres";
uri = "postgresql:///mautrix-signal?host=/run/postgresql";
@ -265,61 +273,65 @@ in
};
};
};
};
# restart the bridges daily. this is done for the signal bridge mainly which stops carrying
# messages out after a while.
# restart the bridges daily. this is done for the signal bridge mainly which stops carrying
# messages out after a while.
nginx = {
virtualHosts = {
"swatrix.swarsel.win" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
listen = [
{
addr = "0.0.0.0";
port = 8448;
ssl = true;
extraParameters = [
"default_server"
];
}
{
addr = "[::0]";
port = 8448;
ssl = true;
extraParameters = [
"default_server"
];
}
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
{
addr = "[::0]";
port = 443;
ssl = true;
}
];
locations = {
"~ ^(/_matrix|/_synapse/client)" = {
# proxyPass = "http://localhost:8008";
proxyPass = "http://localhost:8008";
extraConfig = ''
client_max_body_size 0;
'';
};
"= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
"= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString synapsePort}" = { };
};
};
};
virtualHosts = {
"${matrixDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
listen = [
{
addr = "0.0.0.0";
port = 8448;
ssl = true;
extraParameters = [
"default_server"
];
}
{
addr = "[::0]";
port = 8448;
ssl = true;
extraParameters = [
"default_server"
];
}
{
addr = "0.0.0.0";
port = 443;
ssl = true;
}
{
addr = "[::0]";
port = 443;
ssl = true;
}
];
locations = {
"~ ^(/_matrix|/_synapse/client)" = {
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 0;
'';
};
"= /.well-known/matrix/server".extraConfig = mkWellKnown serverConfig;
"= /.well-known/matrix/client".extraConfig = mkWellKnown clientConfig;
};
};
};
};
};
}

View file

@ -1,23 +1,23 @@
{ self, lib, config, ... }:
let
grafanaDomain = "status.swarsel.win";
serviceDomain = "status.swarsel.win";
servicePort = 3000;
serviceUser = "grafana";
serviceGroup = serviceUser;
moduleName = "monitoring";
grafanaUpstream = "grafana";
prometheusUpstream = "prometheus";
prometheusPort = 9090;
prometheusWebRoot = "prometheus";
in
{
options.swarselsystems.modules.server.monitoring = lib.mkEnableOption "enable monitoring on server";
config = lib.mkIf config.swarselsystems.modules.server.monitoring {
options.swarselsystems.modules.server."${moduleName}" = lib.mkEnableOption "enable ${moduleName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${moduleName}" {
sops.secrets = {
grafanaadminpass = {
owner = "grafana";
};
prometheusadminpass = {
owner = "grafana";
};
kanidm-grafana-client = {
owner = "grafana";
group = "grafana";
mode = "0440";
};
grafanaadminpass = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
prometheusadminpass = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
kanidm-grafana-client = { owner = serviceUser; group = serviceGroup; mode = "0440"; };
};
users = {
@ -26,12 +26,14 @@ in
extraGroups = [ "nextcloud" ];
};
grafana = {
"${serviceUser}" = {
extraGroups = [ "users" ];
};
};
};
networking.firewall.allowedTCPPorts = [ servicePort prometheusPort ];
services = {
grafana = {
enable = true;
@ -43,7 +45,7 @@ in
{
name = "prometheus";
type = "prometheus";
url = "https://${grafanaDomain}/prometheus";
url = "https://${serviceDomain}/prometheus";
editable = false;
access = "proxy";
basicAuth = true;
@ -66,13 +68,21 @@ in
};
settings = {
security.admin_password = "$__file{/run/secrets/grafanaadminpass}";
analytics.reporting_enabled = false;
users.allow_sign_up = false;
security = {
admin_password = "$__file{/run/secrets/grafanaadminpass}";
cookie_secure = true;
disable_gravatar = true;
};
server = {
domain = grafanaDomain;
root_url = "https://${grafanaDomain}";
http_port = 3000;
domain = serviceDomain;
root_url = "https://${serviceDomain}";
http_port = servicePort;
http_addr = "0.0.0.0";
protocol = "http";
enforce_domain = true;
enable_gzip = true;
};
"auth.generic_oauth" = {
enabled = true;
@ -98,9 +108,9 @@ in
prometheus = {
enable = true;
webExternalUrl = "https://status.swarsel.win/prometheus";
port = 9090;
listenAddress = "127.0.0.1";
webExternalUrl = "https://status.swarsel.win/${prometheusWebRoot}";
port = prometheusPort;
listenAddress = "0.0.0.0";
globalConfig = {
scrape_interval = "10s";
};
@ -164,33 +174,44 @@ in
};
};
};
};
nginx = {
virtualHosts = {
"${grafanaDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:3000";
proxyWebsockets = true;
extraConfig = ''
client_max_body_size 0;
'';
};
"/prometheus" = {
proxyPass = "http://localhost:9090";
extraConfig = ''
client_max_body_size 0;
'';
};
nodes.moonside.services.nginx = {
upstreams = {
"${grafanaUpstream}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
"${prometheusUpstream}" = {
servers = {
"192.168.1.2:${builtins.toString prometheusPort}" = { };
};
};
};
virtualHosts = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://${grafanaUpstream}";
proxyWebsockets = true;
extraConfig = ''
client_max_body_size 0;
'';
};
"/${prometheusWebRoot}" = {
proxyPass = "http://${prometheusUpstream}";
extraConfig = ''
client_max_body_size 0;
'';
};
};
};
};
};
};
}

View file

@ -1,7 +1,14 @@
{ pkgs, config, lib, ... }:
let
serviceDomain = "sound.swarsel.win";
servicePort = 4040;
serviceName = "navidrome";
serviceUser = "navidrome";
serviceGroup = serviceUser;
in
{
options.swarselsystems.modules.server.navidrome = lib.mkEnableOption "enable navidrome on server";
config = lib.mkIf config.swarselsystems.modules.server.navidrome {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
environment.systemPackages = with pkgs; [
pciutils
alsa-utils
@ -10,16 +17,16 @@
users = {
groups = {
navidrome = {
"$(serviceGroup}" = {
gid = 61593;
};
};
users = {
navidrome = {
"${serviceUser}" = {
isSystemUser = true;
uid = 61593;
group = "navidrome";
group = serviceGroup;
extraGroups = [ "audio" "utmp" "users" "pipewire" ];
};
};
@ -37,8 +44,8 @@
openFirewall = true;
settings = {
LogLevel = "debug";
Address = "127.0.0.1";
Port = 4040;
Address = "0.0.0.0";
Port = servicePort;
MusicFolder = "/Vault/Eternor/Music";
PlaylistsPath = "./Playlists";
EnableSharing = true;
@ -70,15 +77,22 @@
};
};
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"sound.swarsel.win" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:4040";
proxyPass = "http://navidrome";
proxyWebsockets = true;
extraConfig = ''
auth_request /oauth2/auth;
@ -125,7 +139,7 @@
'';
};
"/share" = {
proxyPass = "http://localhost:4040";
proxyPass = "http://navidrome";
proxyWebsockets = true;
extraConfig = ''
proxy_redirect http:// https://;
@ -139,7 +153,7 @@
'';
};
"/rest" = {
proxyPass = "http://localhost:4040";
proxyPass = "http://navidrome";
proxyWebsockets = true;
extraConfig = ''
proxy_redirect http:// https://;

View file

@ -1,20 +1,23 @@
{ pkgs, lib, config, ... }:
let
nextcloudDomain = "stash.swarsel.win";
serviceDomain = "stash.swarsel.win";
serviceUser = "nextcloud";
serviceGroup = serviceUser;
serviceName = "nextcloud";
in
{
options.swarselsystems.modules.server.nextcloud = lib.mkEnableOption "enable nextcloud on server";
config = lib.mkIf config.swarselsystems.modules.server.nextcloud {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
sops.secrets = {
nextcloudadminpass = {
owner = "nextcloud";
group = "nextcloud";
owner = serviceUser;
group = serviceGroup;
mode = "0440";
};
kanidm-nextcloud-client = {
owner = "nextcloud";
group = "nextcloud";
owner = serviceUser;
group = serviceGroup;
mode = "0440";
};
};
@ -22,8 +25,12 @@ in
services = {
nextcloud = {
enable = true;
settings = {
trusted_proxies = [ "0.0.0.0" ];
overwriteprotocol = "https";
};
package = pkgs.nextcloud31;
hostName = nextcloudDomain;
hostName = serviceDomain;
home = "/Vault/apps/nextcloud";
datadir = "/Vault/data/nextcloud";
https = true;
@ -39,19 +46,28 @@ in
dbtype = "sqlite";
};
};
};
nginx = {
virtualHosts = {
"${nextcloudDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
# config is automatically added by nixos nextcloud config.
# hence, only provide certificate
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:80" = { };
};
};
};
virtualHosts = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://${serviceName}";
};
};
};
};
};
};
}

View file

@ -0,0 +1,107 @@
{ lib, config, ... }:
let
kanidmDomain = "sso.swarsel.win";
oauth2ProxyDomain = "soauth.swarsel.win";
oauth2ProxyPort = 3004;
in
{
options.swarselsystems.modules.server.oauth2Proxy = lib.mkEnableOption "enable oauth2-proxy on server";
config = lib.mkIf config.swarselsystems.modules.server.oauth2Proxy {
sops = {
secrets = {
"oauth2-cookie-secret" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; };
"kanidm-oauth2-proxy-client" = { owner = "oauth2-proxy"; group = "oauth2-proxy"; mode = "0440"; };
};
templates = {
"kanidm-oauth2-proxy-client-env" = {
content = ''
OAUTH2_PROXY_CLIENT_SECRET="${config.sops.placeholder.kanidm-oauth2-proxy-client}"
OAUTH2_PROXY_COOKIE_SECRET=${config.sops.placeholder.oauth2-cookie-secret}
'';
owner = "oauth2-proxy";
group = "oauth2-proxy";
mode = "0440";
};
};
};
networking.firewall.allowedTCPPorts = [ oauth2ProxyPort ];
services = {
oauth2-proxy = {
enable = true;
cookie = {
domain = ".swarsel.win";
secure = true;
expire = "900m";
secret = null; # set by service EnvironmentFile
};
clientSecret = null; # set by service EnvironmentFile
reverseProxy = true;
httpAddress = "0.0.0.0:${builtins.toString oauth2ProxyPort}";
redirectURL = "https://${oauth2ProxyDomain}/oauth2/callback";
setXauthrequest = true;
extraConfig = {
code-challenge-method = "S256";
whitelist-domain = ".swarsel.win";
set-authorization-header = true;
pass-access-token = true;
skip-jwt-bearer-tokens = true;
upstream = "static://202";
oidc-issuer-url = "https://${kanidmDomain}/oauth2/openid/oauth2-proxy";
provider-display-name = "Kanidm";
};
provider = "oidc";
scope = "openid email";
loginURL = "https://${kanidmDomain}/ui/oauth2";
redeemURL = "https://${kanidmDomain}/oauth2/token";
validateURL = "https://${kanidmDomain}/oauth2/openid/oauth2-proxy/userinfo";
clientID = "oauth2-proxy";
email.domains = [ "*" ];
};
};
systemd.services = {
oauth2-proxy = {
# after = [ "kanidm.service" ];
serviceConfig = {
RuntimeDirectory = "oauth2-proxy";
RuntimeDirectoryMode = "0750";
UMask = "007"; # TODO remove once https://github.com/oauth2-proxy/oauth2-proxy/issues/2141 is fixed
RestartSec = "60"; # Retry every minute
EnvironmentFile = [
config.sops.templates.kanidm-oauth2-proxy-client-env.path
];
};
};
};
services.nginx = {
upstreams = {
oauth2-proxy = {
servers = {
"localhost:${builtins.toString oauth2ProxyPort}" = { };
};
};
};
virtualHosts = {
"${oauth2ProxyDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://oauth2-proxy";
};
};
extraConfig = ''
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
'';
};
};
};
};
}

View file

@ -1,30 +1,39 @@
{ lib, pkgs, config, ... }:
let
serviceDomain = "scan.swarsel.win";
servicePort = 28981;
serviceUser = "paperless";
serviceGroup = serviceUser;
serviceName = "paperless";
in
{
options.swarselsystems.modules.server.paperless = lib.mkEnableOption "enable paperless on server";
config = lib.mkIf config.swarselsystems.modules.server.paperless {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
users.users.paperless = {
users.users."${serviceUser}" = {
extraGroups = [ "users" ];
};
sops.secrets = {
paperless_admin = { owner = "paperless"; };
paperless_admin = { owner = serviceUser; };
kanidm-paperless-client = {
owner = "paperless";
group = "paperless";
owner = serviceUser;
group = serviceGroup;
mode = "0440";
};
};
networking.firewall.allowedTCPPorts = [ servicePort ];
services = {
paperless = {
enable = true;
mediaDir = "/Vault/Eternor/Paperless";
dataDir = "/Vault/data/paperless";
user = "paperless";
port = 28981;
user = serviceUser;
port = servicePort;
passwordFile = config.sops.secrets.paperless_admin.path;
address = "127.0.0.1";
address = "0.0.0.0";
settings = {
PAPERLESS_OCR_LANGUAGE = "deu+eng";
PAPERLESS_URL = "https://scan.swarsel.win";
@ -84,15 +93,22 @@
)
'';
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"scan.swarsel.win" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:28981";
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 0;
proxy_connect_timeout 300;

View file

@ -1,26 +1,34 @@
{ lib, config, ... }:
let
inherit (config.repo.secrets.common) workHostName;
serviceDomain = "storync.swarsel.win";
servicePort = 8384;
serviceUser = "syncthing";
serviceGroup = serviceUser;
serviceName = "syncthing";
in
{
options.swarselsystems.modules.server.syncthing = lib.mkEnableOption "enable syncthing on server";
config = lib.mkIf config.swarselsystems.modules.server.syncthing {
options.swarselsystems.modules.server."${serviceName}" = lib.mkEnableOption "enable ${serviceName} on server";
config = lib.mkIf config.swarselsystems.modules.server."${serviceName}" {
users.users.syncthing = {
users.users."${serviceUser}" = {
extraGroups = [ "users" ];
group = "syncthing";
group = serviceGroup;
isSystemUser = true;
};
users.groups.syncthing = { };
users.groups."${serviceGroup}" = { };
networking.firewall.allowedTCPPorts = [ servicePort ];
services.syncthing = {
enable = true;
user = "swarsel";
user = serviceUser;
group = serviceGroup;
dataDir = "/Vault/data/syncthing";
configDir = "/Vault/apps/syncthing";
guiAddress = "0.0.0.0:8384";
openDefaultPorts = true;
guiAddress = "0.0.0.0:${builtins.toString servicePort}";
openDefaultPorts = true; # opens ports TCP/UDP 22000 and UDP 21027 for discovery
relay.enable = false;
settings = {
urAccepted = -1;
@ -34,13 +42,16 @@ in
"${workHostName}" = {
id = "YAPV4BV-I26WPTN-SIP32MV-SQP5TBZ-3CHMTCI-Z3D6EP2-MNDQGLP-53FT3AB";
};
"moonside (@oracle)" = {
id = "VPCDZB6-MGVGQZD-Q6DIZW3-IZJRJTO-TCC3QUQ-2BNTL7P-AKE7FBO-N55UNQE";
};
};
folders = {
"Default Folder" = lib.mkForce {
path = "/Vault/data/syncthing/Sync";
type = "receiveonly";
versioning = null;
devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
id = "default";
};
"Obsidian" = {
@ -50,7 +61,7 @@ in
type = "simple";
params.keep = "5";
};
devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
id = "yjvni-9eaa7";
};
"Org" = {
@ -60,7 +71,7 @@ in
type = "simple";
params.keep = "5";
};
devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
id = "a7xnl-zjj3d";
};
"Vpn" = {
@ -70,7 +81,7 @@ in
type = "simple";
params.keep = "5";
};
devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
devices = [ "sync (@oracle)" "magicant" "${workHostName}" "moonside (@oracle)" ];
id = "hgp9s-fyq3p";
};
"Documents" = {
@ -80,27 +91,29 @@ in
type = "simple";
params.keep = "5";
};
devices = [ "magicant" "${workHostName}" ];
devices = [ "magicant" "${workHostName}" "moonside (@oracle)" ];
id = "hgr3d-pfu3w";
};
# ".elfeed" = {
# path = "/Vault/data/syncthing/.elfeed";
# devices = [ "sync (@oracle)" "magicant" "${workHostName}" ];
# id = "h7xbs-fs9v1";
# };
};
};
};
services.nginx = {
nodes.moonside.services.nginx = {
upstreams = {
"${serviceName}" = {
servers = {
"192.168.1.2:${builtins.toString servicePort}" = { };
};
};
};
virtualHosts = {
"storync.swarsel.win" = {
"${serviceDomain}" = {
enableACME = true;
forceSSL = true;
acmeRoot = null;
locations = {
"/" = {
proxyPass = "http://localhost:8384";
proxyPass = "http://${serviceName}";
extraConfig = ''
client_max_body_size 0;
'';
@ -110,5 +123,4 @@ in
};
};
};
}