Matrix, Caddy

* Nginx to caddy (#5)

Convert all nginx instances to caddy instances, setup acme as well

* matrix implemented
This commit is contained in:
Crow 2025-09-11 14:56:05 -04:00 committed by GitHub
parent 742be942bb
commit e0590ff20b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 182 additions and 484 deletions

View file

@ -20,12 +20,11 @@ let
# trilium.nix
"/var/lib/trilium/backup"
# grocy
"/var/lib/grocy"
# actualbudget
"${volumePath}/actualbudget"
"/var/lib/tuwunel"
# flamesites
"${volumePath}/flamesites/swgalaxyproject"
"${volumePath}/flamesites/nnsbluegrass"

View file

@ -43,7 +43,6 @@
"modules/services/actualbudget"
"modules/services/bar-assistant"
"modules/services/frigate"
"modules/services/grocy"
"modules/services/homebox"
"modules/services/homepage"
"modules/services/mqtt"
@ -52,10 +51,10 @@
"modules/services/trilium"
"modules/services/fail2ban"
"modules/services/ntfy-sh"
"modules/services/ollama/nginx.nix" # Just host the nginx path back to Parzival
"modules/services/ollama/proxy.nix" # Just host the proxy path back to Parzival
"modules/services/netbox"
"modules/services/system-logging"
"modules/services/system-logging/nginx.nix"
"modules/services/system-logging/proxy.nix"
"modules/services/matrix"
"modules/services/flamesites"
])
@ -83,60 +82,63 @@
};
};
#FIXME(TODO) Migrate this into another file, probably a module
sops = {
secrets = {
"aws/access_key" = {};
"aws/secret_key" = {};
"aws/region" = {};
};
templates = {
"aws_shared_credentials".content = ''
[default]
aws_access_key_id=${config.sops.placeholder."aws/access_key"}
aws_secret_access_key=${config.sops.placeholder."aws/secret_key"}
'';
"aws_env".content = ''
AWS_REGION=${config.sops.placeholder."aws/region"}
'';
};
services.caddy = {
email = "infrastructure@wanderingcrow.net";
acmeCA = "https://acme-v02.api.letsencrypt.org/directory";
};
security.acme = {
acceptTerms = true;
defaults = {
email = "infrastructure@wanderingcrow.net";
group = config.services.nginx.group;
dnsProvider = "route53";
credentialFiles = {
"AWS_SHARED_CREDENTIALS_FILE" = config.sops.templates."aws_shared_credentials".path;
};
environmentFile = config.sops.templates."aws_env".path;
};
certs = {
"wanderingcrow.net" = {};
"umami.wanderingcrow.net" = {};
"garage.wanderingcrow.net" = {};
"bar.wanderingcrow.net" = {};
"home.wanderingcrow.net" = {};
"homebox.wanderingcrow.net" = {};
"cache.wanderingcrow.net" = {};
"openhab.wanderingcrow.net" = {};
"frigate.wanderingcrow.net" = {};
"notes.wanderingcrow.net" = {};
"grocy.wanderingcrow.net" = {};
"barcodebuddy.grocy.wanderingcrow.net" = {};
"budget.wanderingcrow.net" = {};
"matrix.wanderingcrow.net" = {};
"ta.wanderingcrow.net" = {};
"chat.wanderingcrow.net" = {};
"netbox.wanderingcrow.net" = {};
"notify.wanderingcrow.net" = {};
"logs.wanderingcrow.net" = {};
"psychal.link" = {};
# Sites I host for someone else
"swgalaxyproject.com" = {};
"nnsbluegrass.com" = {};
};
};
#FIXME(TODO) Migrate this into another file, probably a module
#sops = {
# secrets = {
# "aws/access_key" = {};
# "aws/secret_key" = {};
# "aws/region" = {};
# };
# templates = {
# "aws_shared_credentials".content = ''
# [default]
# aws_access_key_id=${config.sops.placeholder."aws/access_key"}
# aws_secret_access_key=${config.sops.placeholder."aws/secret_key"}
# '';
# "aws_env".content = ''
# AWS_REGION=${config.sops.placeholder."aws/region"}
# '';
# };
#};
#security.acme = {
# acceptTerms = true;
# defaults = {
# email = "infrastructure@wanderingcrow.net";
# group = config.services.caddy.group;
# dnsProvider = "route53";
# credentialFiles = {
# "AWS_SHARED_CREDENTIALS_FILE" = config.sops.templates."aws_shared_credentials".path;
# };
# environmentFile = config.sops.templates."aws_env".path;
# };
# certs = {
# "wanderingcrow.net" = {};
# "umami.wanderingcrow.net" = {};
# "garage.wanderingcrow.net" = {};
# "bar.wanderingcrow.net" = {};
# "home.wanderingcrow.net" = {};
# "homebox.wanderingcrow.net" = {};
# "cache.wanderingcrow.net" = {};
# "openhab.wanderingcrow.net" = {};
# "frigate.wanderingcrow.net" = {};
# "notes.wanderingcrow.net" = {};
# "budget.wanderingcrow.net" = {};
# "matrix.wanderingcrow.net" = {};
# "ta.wanderingcrow.net" = {};
# "chat.wanderingcrow.net" = {};
# "netbox.wanderingcrow.net" = {};
# "notify.wanderingcrow.net" = {};
# "logs.wanderingcrow.net" = {};
# "psychal.link" = {};
# # Sites I host for someone else
# "swgalaxyproject.com" = {};
# "nnsbluegrass.com" = {};
# };
#};
}

View file

@ -9,21 +9,11 @@ in {
"d ${volumePath}/actualbudget"
];
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"budget.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "budget.wanderingcrow.net";
locations = {
"/" = {
proxyPass = "http://10.88.0.12";
proxyWebsockets = true;
};
};
};
};
virtualHosts."budget.wanderingcrow.net".extraConfig = ''
reverse_proxy http://10.88.0.12
'';
};
virtualisation.oci-containers = {
backend = "podman";

View file

@ -1,57 +0,0 @@
{
lib,
config,
...
}: {
sops = {
secrets."attic/server_token" = {};
secrets."cloudflare/r2/access_key" = {};
secrets."cloudflare/r2/secret_key" = {};
templates."attic-env".content = ''
ATTIC_SERVER_TOKEN_RS256_SECRET_BASE64=${config.sops.placeholder."attic/server_token"}
AWS_ACCESS_KEY_ID=${config.sops.placeholder."cloudflare/r2/access_key"}
AWS_SECRET_ACCESS_KEY=${config.sops.placeholder."cloudflare/r2/secret_key"}
'';
};
services = {
atticd = {
enable = true;
mode = "monolithic";
environmentFile = config.sops.templates."attic-env".path;
settings = {
listen = "[::]:8080";
api-endpoint = "https://cache.wanderingcrow.net/";
jwt = {};
chunking = {
nar-size-threshold = 64 * 1024; # 64 KiB
min-size = 16 * 1024; # 16 KiB
avg-size = 64 * 1024; # 64 KiB
max-size = 256 * 1024; # 256 KiB
};
storage = {
type = "s3";
region = "";
bucket = "wce-attic-cache";
endpoint = "https://68c4b3ab47c1a97037ab5a938f772d69.r2.cloudflarestorage.com";
};
};
};
nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"cache.wanderingcrow.net" = {
forceSSL = true;
extraConfig = ''
client_max_body_size 0;
'';
useACMEHost = "cache.wanderingcrow.net";
locations."/" = {
proxyPass = "http://localhost:8080";
proxyWebsockets = true;
};
};
};
};
};
}

View file

@ -33,33 +33,16 @@ in
# Routing #
###########
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"bar.wanderingcrow.net" = {
extraConfig = ''
allow 192.168.0.0/16;
allow 10.8.0.0/24;
allow ${inputs.nix-secrets.network.primary.publicIP};
deny all;
'';
forceSSL = true;
useACMEHost = "bar.wanderingcrow.net";
locations = {
"/search/" = {
proxyPass = "http://10.88.0.3:7700/";
priority = 1;
};
"/api/" = {
proxyPass = "http://10.88.0.4:8080/";
priority = 1;
};
"/" = {
proxyPass = "http://10.88.0.5:8080/";
};
};
};
"bar.wanderingcrow.net".extraConfig = ''
@block not remote_ip ${inputs.nix-secrets.network.primary.publicIP} private_ranges
abort @block
reverse_proxy /search/ http://10.88.0.3:7700
reverse_proxy /api/ http://10.88.0.4:8080
reverse_proxy http://10.88.0.5:8080
'';
};
};

View file

@ -11,15 +11,6 @@
norestored = true # Needed to avoid receiving a new notification after every restart
actionban = curl -H "Title: <ip> has been banned" -d "<name> jail has banned <ip> from accessing ${config.hostSpec.hostName} after <failures> attempts of attacking the system." https://notify.wanderingcrow.net/Fail2banNotifications
'');
# Defines a filter that detects URL probing by reading the Nginx access log
"fail2ban/filter.d/nginx-ddos-protect.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
[Definition]
failregex = ^<HOST> - - \[.*\] "GET .* HTTP/.*" \d{3} \d+ "-" "ApacheBench/.+"
limiting requests, excess:.* by zone.*client: <HOST>
^.*\[error\]\s+\d+#\d+:\s+\*\d+\s+connect\(\) to unix:.*failed.*while connecting to upstream,\s+client:\s+<HOST>,\s+server:.*
^.*\[error\]\s+\d+#\d+:\s+\*\d+\s+upstream prematurely closed connection while reading response header from upstream,\s+client:\s+<HOST>,.*$
ignoreregex =
'');
};
services.fail2ban = {
enable = true;
@ -29,17 +20,5 @@
ignoreIP = [
inputs.nix-secrets.network.primary.publicIP
];
jails = {
nginx-ddos-protect.settings = {
enabled = true;
filter = "nginx-ddos-protect";
logpath = "/var/log/nginx/*.log";
action = '' %(action_)s[blocktype=DROP]
ntfy'';
backend = "auto"; # Do not forget to specify this if your jail uses a log file
maxretry = 1;
findtime = 3600;
};
};
};
}

View file

@ -10,22 +10,13 @@ in {
"d ${volumePath}/ferdium-server/app/recipes"
];
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"ferdium.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "ferdium.wanderingcrow.net";
locations = {
"/" = {
proxyPass = "http://10.88.0.13:3333";
proxyWebsockets = true;
};
};
};
};
virtualHosts."ferdium.wanderingcrow.net".extraConfig = ''
reverse_proxy http://10.88.0.13:3333
'';
};
virtualisation.oci-containers = {
backend = "podman";
containers = {

View file

@ -32,34 +32,15 @@ in {
};
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"swgalaxyproject.com" = {
forceSSL = true;
useACMEHost = "swgalaxyproject.com";
locations."/" = {
recommendedProxySettings = true;
extraConfig = ''
client_max_body_size 200M;
'';
proxyPass = "http://localhost:8080";
proxyWebsockets = true;
};
};
"nnsbluegrass.com" = {
forceSSL = true;
useACMEHost = "nnsbluegrass.com";
locations."/" = {
recommendedProxySettings = true;
extraConfig = ''
client_max_body_size 200M;
'';
proxyPass = "http://localhost:9821";
proxyWebsockets = true;
};
};
"swgalaxyproject.com".extraConfig = ''
reverse_proxy http://localhost:8080
'';
"nnsbluegrass.com".extraConfig = ''
reverse_proxy http://localhost:9821
'';
};
};
}

View file

@ -9,7 +9,7 @@ in
}: let
frigateConfig = pkgs.writeText "config.yaml" (lib.generators.toYAML {} {
auth.reset_admin_password = true; # roll the admin password every restart, depend on user accounts for long-lived access
tls.enabled = false; # off because we're doing ssl through nginx
tls.enabled = false; # off because we're doing ssl through the proxy
mqtt = {
# TODO: add mqtt broker
enabled = false;
@ -198,18 +198,10 @@ in
};
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"frigate.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "frigate.wanderingcrow.net";
locations."/" = {
proxyPass = "http://10.88.0.10:8971";
proxyWebsockets = true;
};
};
};
virtualHosts."frigate.wanderingcrow.net".extraConfig = ''
reverse_proxy http://10.88.0.10:8971
'';
};
}

View file

@ -1,42 +0,0 @@
let
volumePath = "/overseer/services";
in
{
lib,
config,
...
}: {
systemd.tmpfiles.rules = [
"d ${volumePath}/barcodebuddy"
];
services.nginx.virtualHosts = {
"grocy.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "grocy.wanderingcrow.net";
};
"barcodebuddy.grocy.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "barcodebuddy.grocy.wanderingcrow.net";
locations."/" = {
proxyPass = "http://10.88.0.11:80";
proxyWebsockets = true;
};
};
};
services.grocy = {
enable = true;
hostName = "grocy.wanderingcrow.net";
nginx.enableSSL = false;
};
virtualisation.oci-containers.containers = {
barcodebuddy = {
image = "f0rc3/barcodebuddy:latest";
volumes = ["${volumePath}/barcodebuddy:/config"];
extraOptions = ["--ip=10.88.0.11"];
};
};
}

View file

@ -1,26 +1,13 @@
{inputs, ...}: {
services = {
nginx = {
caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"homebox.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "homebox.wanderingcrow.net";
locations."/" = {
extraConfig = ''
allow 192.168.0.0/16;
allow 10.8.0.0/24;
allow ${inputs.nix-secrets.network.primary.publicIP};
deny all;
'';
proxyPass = "http://localhost:7745";
proxyWebsockets = true;
};
};
};
virtualHosts."homebox.wanderingcrow.net".extraConfig = ''
@block not remote_ip ${inputs.nix-secrets.network.primary.publicIP} private_ranges
abort @block
reverse_proxy http://localhost:7745
'';
};
homebox = {
enable = true;
settings = {

View file

@ -23,24 +23,13 @@ in {
#HOMEPAGE_VAR_LUBELOGGERPASS = ${config.sops.placeholder."lubelogger/pass"}
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"home.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "home.wanderingcrow.net";
locations."/" = {
extraConfig = ''
allow 192.168.0.0/16;
allow ${s.network.primary.publicIP};
deny all;
'';
proxyPass = "http://localhost:8089";
proxyWebsockets = true;
};
};
};
virtualHosts."home.wanderingcrow.net".extraConfig = ''
@block not remote_ip ${inputs.nix-secrets.network.primary.publicIP} private_ranges
abort @block
reverse_proxy http://localhost:8089
'';
};
services = {
@ -98,14 +87,6 @@ in {
bookmarks = [
{
WCE = [
{
Grocy = [
{
icon = "grocy.svg";
href = "https://grocy.wanderingcrow.net";
}
];
}
{
Homebox = [
{

View file

@ -15,21 +15,11 @@ in {
'';
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"ta.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "ta.wanderingcrow.net";
locations = {
"/" = {
proxyPass = "http://10.88.0.14:8000";
proxyWebsockets = true;
};
};
};
};
virtualHosts."ta.wanderingcrow.net".extraConfig = ''
reverse_proxy http://10.88.0.14:8000
'';
};
systemd.tmpfiles.rules = [

View file

@ -34,19 +34,11 @@ in
'';
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"garage.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "garage.wanderingcrow.net";
locations."/" = {
proxyPass = "http://10.88.0.8:8080";
proxyWebsockets = true;
};
};
};
virtualHosts."garage.wanderingcrow.net".extraConfig = ''
reverse_proxy http://10.88.0.8:8080
'';
};
virtualisation.oci-containers = {

View file

@ -8,6 +8,10 @@
"${inputs.nixpkgs-unstable}/nixos/modules/services/matrix/tuwunel.nix"
];
environment.systemPackages = [
pkgs.unstable.fluffychat-web
];
sops.secrets."matrix/registration_token" = {
owner = "tuwunel";
};
@ -22,7 +26,7 @@
new_user_displayname_suffix = "";
unix_socket_path = "/run/tuwunel/tuwunel.sock";
unix_socket_perms = 660;
allow_registration = false;
allow_registration = true;
registration_token_file = config.sops.secrets."matrix/registration_token".path;
allow_encryption = true;
allow_federation = true;
@ -32,18 +36,20 @@
};
};
services.nginx = {
networking.firewall.allowedTCPPorts = [8448];
users.users.caddy.extraGroups = ["tuwunel"];
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"psychal.link" = {
forceSSL = true;
useACMEHost = "psychal.link";
locations."/" = {
proxyPass = "http://unix:/run/tuwunel/tuwunel.sock";
proxyWebsockets = true;
};
};
"psychal.link, psychal.link:8448".extraConfig = ''
reverse_proxy unix//run/tuwunel/tuwunel.sock
'';
"chat.psychal.link".extraConfig = ''
root * ${pkgs.unstable.fluffychat-web}
file_server
'';
};
};
}

View file

@ -6,26 +6,19 @@
}: let
sopsFolder = builtins.toString inputs.nix-secrets + "/sops";
in {
users.users.nginx.extraGroups = ["netbox"];
users.users.caddy.extraGroups = ["netbox"];
sops.secrets."netbox/secret-key" = {
owner = "netbox";
sopsFile = "${sopsFolder}/shared.yaml";
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true; # otherwise you will get CSRF error while login
virtualHosts."netbox.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "netbox.wanderingcrow.net";
locations = {
"/" = {
proxyPass = "http://${config.services.netbox.listenAddress}:${builtins.toString config.services.netbox.port}";
};
"/static/" = {alias = "${config.services.netbox.dataDir}/static/";};
};
};
virtualHosts."netbox.wanderingcrow.net".extraConfig = ''
file_server /static/
reverse_proxy http://${config.services.netbox.listenAddress}:${builtins.toString config.services.netbox.port}
'';
};
services.netbox = {

View file

@ -1,17 +1,9 @@
{
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"notify.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "notify.wanderingcrow.net";
locations."/" = {
proxyPass = "http://localhost:9089";
proxyWebsockets = true;
};
};
};
virtualHosts."notify.wanderingcrow.net".extraConfig = ''
reverse_proxy http://localhost:9089
'';
};
services.ntfy-sh = {
enable = true;

View file

@ -1,21 +0,0 @@
{inputs, ...}: {
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"chat.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "chat.wanderingcrow.net";
locations."/" = {
extraConfig = ''
allow 192.168.0.0/16;
allow ${inputs.nix-secrets.network.primary.publicIP};
deny all;
'';
proxyPass = "http://192.168.0.72:3000";
proxyWebsockets = true;
};
};
};
};
}

View file

@ -0,0 +1,10 @@
{inputs, ...}: {
services.caddy = {
enable = true;
virtualHosts."chat.wanderingcrow.net".extraConfig = ''
@block not remote_ip ${inputs.nix-secrets.network.primary.publicIP} private_ranges
abort @block
reverse_proxy http://192.168.0.72:3000
'';
};
}

View file

@ -30,22 +30,12 @@ in
};
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"openhab.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "openhab.wanderingcrow.net";
locations."/" = {
extraConfig = ''
allow 192.168.0.0/16;
allow ${inputs.nix-secrets.network.primary.publicIP};
deny all;
'';
proxyPass = "http://10.88.0.9:8080";
};
};
};
virtualHosts."openhab.wanderingcrow.net".extraConfig = ''
@block not remote_ip ${inputs.nix-secrets.network.primary.publicIP} private_ranges
abort @block
reverse_proxy http://10.88.0.9:8080
'';
};
}

View file

@ -1,26 +0,0 @@
{
config,
inputs,
...
}: {
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"logs.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "logs.wanderingcrow.net";
locations."/" = {
extraConfig = ''
allow 192.168.0.0/16;
allow ${inputs.nix-secrets.network.primary.publicIP};
deny all;
'';
proxyPass = "http://${builtins.toString config.services.grafana.settings.server.http_addr}:${builtins.toString config.services.grafana.settings.server.http_port}";
proxyWebsockets = true;
recommendedProxySettings = true;
};
};
};
};
}

View file

@ -0,0 +1,14 @@
{
config,
inputs,
...
}: {
services.caddy = {
enable = true;
virtualHosts."logs.wanderingcrow.net".extraConfig = ''
@block not remote_ip ${inputs.nix-secrets.network.primary.publicIP} private_ranges
abort @block
reverse_proxy http://${builtins.toString config.services.grafana.settings.server.http_addr}:${builtins.toString config.services.grafana.settings.server.http_port}
'';
};
}

View file

@ -4,20 +4,11 @@
inputs,
...
}: {
services = {
nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"wanderingcrow.net" = {
default = true;
forceSSL = true;
useACMEHost = "wanderingcrow.net";
locations."/" = {
root = inputs.the-nest.outputs.packages.x86_64-linux.default;
};
};
};
};
services.caddy = {
enable = true;
virtualHosts."wanderingcrow.net".extraConfig = ''
root ${inputs.the-nest.outputs.packages.x86_64-linux.default}
file_server
'';
};
}

View file

@ -12,19 +12,11 @@
port = 8090;
};
nginx = {
caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"notes.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "notes.wanderingcrow.net";
locations."/" = {
proxyPass = "http://127.0.0.1:8090";
proxyWebsockets = true;
};
};
};
virtualHosts."notes.wanderingcrow.net".extraConfig = ''
reverse_proxy http://127.0.0.1:8090
'';
};
};
}

View file

@ -31,23 +31,12 @@ in
'';
};
services.nginx = {
services.caddy = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"umami.wanderingcrow.net" = {
forceSSL = true;
useACMEHost = "umami.wanderingcrow.net";
locations."/" = {
proxyPass = "http://10.88.0.6:3000";
proxyWebsockets = true;
};
locations."/script.js" = {
extraConfig = ''
deny 172.220.132.255;
'';
};
};
virtualHosts."umami.wanderingcrow.net" = {
extraConfig = ''
reverse_proxy http://10.88.0.6:3000
'';
};
};