1
1
Fork 1
mirror of https://github.com/oddlama/nix-config.git synced 2025-10-10 23:00:39 +02:00

feat: add internal proxy to high-volume applications at home

This commit is contained in:
oddlama 2024-05-20 02:30:17 +02:00
parent b01c521830
commit 20a5e1e66a
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
32 changed files with 301 additions and 21 deletions

View file

@ -5,6 +5,7 @@
...
}: let
sentinelCfg = nodes.sentinel.config;
wardWebProxyCfg = nodes.ward-web-proxy.config;
in {
meta.promtail = {
enable = true;
@ -12,7 +13,12 @@ in {
};
# Connect safely via wireguard to skip http authentication
networking.hosts.${sentinelCfg.wireguard.proxy-sentinel.ipv4} = [sentinelCfg.networking.providedDomains.influxdb];
networking.hosts.${
if config.wireguard ? proxy-home
then wardWebProxyCfg.wireguard.proxy-home.ipv4
else sentinelCfg.wireguard.proxy-sentinel.ipv4
} = [sentinelCfg.networking.providedDomains.influxdb];
meta.telegraf = lib.mkIf (!config.boot.isContainer) {
enable = true;
scrapeSensors = false;

View file

@ -4,6 +4,7 @@
...
}: let
sentinelCfg = nodes.sentinel.config;
wardWebProxyCfg = nodes.ward-web-proxy.config;
grafanaDomain = "grafana.${config.repo.secrets.global.domains.me}";
in {
wireguard.proxy-sentinel = {
@ -116,6 +117,11 @@ in {
}
];
networking.hosts.${wardWebProxyCfg.wireguard.proxy-home.ipv4} = [
sentinelCfg.networking.providedDomains.influxdb # technically a duplicate (see ./common.nix)...
sentinelCfg.networking.providedDomains.loki
];
services.grafana = {
enable = true;
settings = {

View file

@ -5,6 +5,7 @@
...
}: let
sentinelCfg = nodes.sentinel.config;
wardWebProxyCfg = nodes.ward-web-proxy.config;
immichDomain = "immich.${config.repo.secrets.global.domains.me}";
ipImmichMachineLearning = "10.89.0.10";
@ -169,10 +170,15 @@ in {
client.via = "sentinel";
firewallRuleForNode.sentinel.allowedTCPPorts = [2283];
};
wireguard.proxy-home = {
client.via = "ward";
firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [2283];
};
networking.nftables.chains.forward.into-immich-container = {
after = ["conntrack"];
rules = [
"iifname proxy-sentinel ip saddr ${sentinelCfg.wireguard.proxy-sentinel.ipv4} tcp dport 3001 accept"
"iifname proxy-home ip saddr ${wardWebProxyCfg.wireguard.proxy-home.ipv4} tcp dport 3001 accept"
"iifname podman1 oifname lan accept"
];
};
@ -202,6 +208,31 @@ in {
};
};
nodes.ward-web-proxy = {
services.nginx = {
upstreams.immich = {
servers."${config.wireguard.proxy-home.ipv4}:2283" = {};
extraConfig = ''
zone immich 64k;
keepalive 2;
'';
};
virtualHosts.${immichDomain} = {
forceSSL = true;
useACMEWildcardHost = true;
locations."/" = {
proxyPass = "http://immich";
proxyWebsockets = true;
};
extraConfig = ''
client_max_body_size 10G;
allow 192.168.1.0/24;
deny all;
'';
};
};
};
systemd.tmpfiles.settings = {
"10-immich" = {
${upload_folder}.d = {

View file

@ -6,6 +6,7 @@
...
}: let
sentinelCfg = nodes.sentinel.config;
wardCfg = nodes.ward.config;
influxdbDomain = "influxdb.${config.repo.secrets.global.domains.me}";
influxdbPort = 8086;
in {
@ -14,6 +15,11 @@ in {
firewallRuleForNode.sentinel.allowedTCPPorts = [influxdbPort];
};
wireguard.proxy-home = {
client.via = "ward";
firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [influxdbPort];
};
nodes.sentinel = {
networking.providedDomains.influxdb = influxdbDomain;
@ -50,6 +56,40 @@ in {
};
};
nodes.ward-web-proxy = {
services.nginx = {
upstreams.influxdb = {
servers."${config.wireguard.proxy-home.ipv4}:${toString influxdbPort}" = {};
extraConfig = ''
zone influxdb 64k;
keepalive 2;
'';
};
virtualHosts.${influxdbDomain} = let
accessRules = ''
${lib.concatMapStrings (ip: "allow ${ip};\n") wardCfg.wireguard.proxy-home.server.reservedAddresses}
deny all;
'';
in {
forceSSL = true;
useACMEWildcardHost = true;
locations."/" = {
proxyPass = "http://influxdb";
proxyWebsockets = true;
extraConfig = accessRules;
};
locations."/api/v2/write" = {
proxyPass = "http://influxdb/api/v2/write";
proxyWebsockets = true;
extraConfig = ''
${accessRules}
access_log off;
'';
};
};
};
};
age.secrets.influxdb-admin-password = {
generator.script = "alnum";
mode = "440";

View file

@ -1,9 +1,12 @@
{
config,
lib,
nodes,
...
}: let
sentinelCfg = nodes.sentinel.config;
wardWebProxyCfg = nodes.ward-web-proxy.config;
wardCfg = nodes.ward.config;
lokiDomain = "loki.${config.repo.secrets.global.domains.me}";
in {
wireguard.proxy-sentinel = {
@ -11,6 +14,11 @@ in {
firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.loki.configuration.server.http_listen_port];
};
wireguard.proxy-home = {
client.via = "ward";
firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [config.services.loki.configuration.server.http_listen_port];
};
nodes.sentinel = {
networking.providedDomains.loki = lokiDomain;
@ -28,6 +36,51 @@ in {
keepalive 2;
'';
};
virtualHosts.${lokiDomain} = {
forceSSL = true;
useACMEWildcardHost = true;
locations."/" = {
proxyPass = "http://loki";
proxyWebsockets = true;
extraConfig = ''
auth_basic "Authentication required";
auth_basic_user_file ${wardWebProxyCfg.age.secrets.loki-basic-auth-hashes.path};
proxy_read_timeout 1800s;
proxy_connect_timeout 1600s;
${lib.concatMapStrings (ip: "allow ${ip};\n") wardCfg.wireguard.proxy-home.server.reservedAddresses}
deny all;
access_log off;
'';
};
locations."= /ready" = {
proxyPass = "http://loki";
extraConfig = ''
auth_basic off;
access_log off;
'';
};
};
};
};
nodes.ward-web-proxy = {
age.secrets.loki-basic-auth-hashes = {
inherit (nodes.sentinel.config.age.secrets.loki-basic-auth-hashes) rekeyFile;
mode = "440";
group = "nginx";
};
services.nginx = {
upstreams.loki = {
servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.loki.configuration.server.http_listen_port}" = {};
extraConfig = ''
zone loki 64k;
keepalive 2;
'';
};
virtualHosts.${lokiDomain} = {
forceSSL = true;
useACMEWildcardHost = true;

View file

@ -6,12 +6,23 @@
...
}: let
sentinelCfg = nodes.sentinel.config;
wardWebProxyCfg = nodes.ward-web-proxy.config;
paperlessDomain = "paperless.${config.repo.secrets.global.domains.me}";
paperlessBackupDir = "/var/cache/paperless-backup";
in {
microvm.mem = 1024 * 9;
microvm.vcpu = 8;
wireguard.proxy-sentinel = {
client.via = "sentinel";
firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.paperless.port];
};
wireguard.proxy-home = {
client.via = "ward";
firewallRuleForNode.ward-web-proxy.allowedTCPPorts = [config.services.paperless.port];
};
nodes.sentinel = {
networking.providedDomains.paperless = paperlessDomain;
@ -38,9 +49,30 @@ in {
};
};
wireguard.proxy-sentinel = {
client.via = "sentinel";
firewallRuleForNode.sentinel.allowedTCPPorts = [config.services.paperless.port];
nodes.ward-web-proxy = {
services.nginx = {
upstreams.paperless = {
servers."${config.wireguard.proxy-home.ipv4}:${toString config.services.paperless.port}" = {};
extraConfig = ''
zone paperless 64k;
keepalive 2;
'';
};
virtualHosts.${paperlessDomain} = {
forceSSL = true;
useACMEWildcardHost = true;
extraConfig = ''
client_max_body_size 512M;
allow 192.168.1.0/24;
deny all;
'';
locations."/" = {
proxyPass = "http://paperless";
proxyWebsockets = true;
X-Frame-Options = "SAMEORIGIN";
};
};
};
};
age.secrets.paperless-admin-password = {
@ -75,7 +107,10 @@ in {
PAPERLESS_URL = "https://${paperlessDomain}";
PAPERLESS_ALLOWED_HOSTS = paperlessDomain;
PAPERLESS_CORS_ALLOWED_HOSTS = "https://${paperlessDomain}";
PAPERLESS_TRUSTED_PROXIES = sentinelCfg.wireguard.proxy-sentinel.ipv4;
PAPERLESS_TRUSTED_PROXIES = lib.concatStringSep "," [
sentinelCfg.wireguard.proxy-sentinel.ipv4
wardWebProxyCfg.wireguard.proxy-home.ipv4
];
# Authentication via kanidm
PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect";

View file

@ -74,23 +74,26 @@ in {
];
dhcp.enabled = false;
};
filtering.rewrites = [
# Undo the /etc/hosts entry so we don't answer with the internal
# wireguard address for influxdb
{
domain = nodes.sentinel.config.networking.providedDomains.influxdb;
answer = config.repo.secrets.global.domains.me;
}
filtering.rewrites =
[
# Undo the /etc/hosts entry so we don't answer with the internal
# wireguard address for influxdb
{
domain = nodes.sentinel.config.networking.providedDomains.influxdb;
answer = config.repo.secrets.global.domains.me;
}
]
# Use the local mirror-proxy for some services (not necessary, just for speed)
{
domain = nodes.sentinel.config.networking.providedDomains.grafana;
answer = "192.168.1.4"; # web-proxy
}
{
domain = nodes.sentinel.config.networking.providedDomains.immich;
answer = "192.168.1.4"; # web-proxy
}
];
++ map (domain: {
inherit domain;
answer = "192.168.1.4";
}) [
nodes.sentinel.config.networking.providedDomains.grafana
nodes.sentinel.config.networking.providedDomains.immich
nodes.sentinel.config.networking.providedDomains.influxdb
nodes.sentinel.config.networking.providedDomains.loki
nodes.sentinel.config.networking.providedDomains.paperless
];
filters = [
{
name = "AdGuard DNS filter";

View file

@ -0,0 +1,8 @@
age-encryption.org/v1
-> ssh-ed25519 U8ytLQ ZDczMuStTpVUMGlObtJB5uA07U/OsrOXaocAGJQ5SUQ
D4Lg2MwHZVFHhTBlCDB3ZAnigTCVnNOFII5Hs9FxoL0
-> oV-grease Y>Wk^oz
lG4J8UNTiqKwws8XmfgOZBtLBf83/OciQN+bWAFbbVd5JSl1SSUDuyu94bp34Udq
MyziULMJLT/tgjRM8H/TmBbuuIhWImHegnSA0WAZ
--- lSARhYuFG3dOCOJmNhgEhToUWyUxwBDQaYTrJ4KJQM0
*¿� ù}Œ˜ãêR@íŸï§]F… ÕÞ\ª‰ŒH€Gl}Õûœ¸�4¥™¿'J‘‚g³<å% ‚ä1>€µï=Rë03Iˆª\Jñ�

View file

@ -0,0 +1,7 @@
age-encryption.org/v1
-> ssh-ed25519 U8ytLQ Q49jP/1k8wgMHasJRs3j4qw4kDjmYMxzx190cqJpD34
97gvdGUGDqP2LMdxuIM6u0FdNgKbUuKZl6p5irO+BeM
-> 4FcwR4h*-grease Yn]g)b %taX> 066d`Ecg
6cpXlQaMcTQU7dHNzQgZMeExv0KnJxzAov0BPBpFeiVfQPJqoDc+qgU
--- 94bvmt9LqBAL3sqQRhc1k9vYo91+Fa7/r8nDpqnyXZ4
ËLp#VÎP%7º† …çdÖH~Æ}c¬�“|ž/UvF{[�¶Èö›sD¼ªÂC©y>š®Z° ®õ³V@0Õ–éáÙ^}1Þ¤ý$(�

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> ssh-ed25519 Dbt6cA 3F3ffVdjqoNE4nNpgk03uASXUQqblxHp7fRRd6fyQWY
e2zBfUZrG+9ABnB0FJ5nk30akz1It0w2tCz/KJVSpjg
-> HR^-grease
4p8h4NkKY88xZf5Xk63KxigmHQP8WUDMQPD0Vyfe8qCZ5YbhSgVcDHaTuUj858yw
5xUPwfAjlWsonle5KdBtc0ym7AstzWTTrA10oM6chm/mUvRYDJDQslp5Cw
--- fp/its46uEjme2IwXthKFS8GhsIwXqmDDKnLgAFxRAQ
ó
.å•öKݬâX‚$^®•w˜ ÕbËTŸª[Ë9_¾U\lòعšC ¹ïJQů)âߦÑÄÀrÍÚ7æ…?y¯Ê°

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> ssh-ed25519 vqFVQw cCoX+F+7E65ZyrMstKoMuXiml6Cto+mbEXlZj42EgFM
wuV8ZDpI3ARBI7/JLGQd9lbtXEIYBPeIwnmAl7m9uBQ
-> :d>Hp-grease tCr`4 p4^OM^c r _s0m
fiVaHu4f3uYpHnJIoUZQXvF7eNMbsRvLPGdJ2/7jDno9oieC9RpISOLmiFpfagwR
QcW/E5mYFqqxTSBj5qsdln8pr6Ngq7UCNyP9LTIinQo
--- xmil/cv39XF68x2ukoH0bIHwxgcFVE3L/pczAcJ4dHI
鈷WtOoK�9�ì, +F—~Í@Á?JçJòŒÄ`‚wÃ|íû
x7Õ–[õÕ^%ïŸíAuÞ•5Î]ì¾¹ŒPl®ƒ¡ðŠÑ?

View file

@ -0,0 +1,7 @@
age-encryption.org/v1
-> ssh-ed25519 iNceIg A1acm4APgJopInZlGV0zzs5kRZpTJuftDRsU6CIuBVs
XkY/QRvKvaKJjLQ9wlGp+emQ+uamn+K62Beqsru61r8
-> iuB-grease /U =s~<Mo
Qt03xgKv
--- DaTakn+mx7c6rJk97gDfUvKwSRRjwOxvRHbyogd3Zx8
ê6{ûšÊÑ0—|�O%Qœl´%dóyƒ±š¶BhÕÌtQd›úuòxåì d¦xÄøÞtßc€6�!»—Ÿ“•Ò8’Uƅ=í.Ù1.

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> ssh-ed25519 iNceIg sXXiTAH7s2O/UyUZHmuMHnQMRAvOVIXxEc65AXqewXI
Smfsb4y9aHWTX9KJKCRkiDCfOkSTNsci4kzFKHQ73WM
-> CKelskGG-grease i[ 7 +}sM
NlSd4X9OnX5luuy7kGXpJeOoeQg6Nbb4TK+/CyrmkMyRU+3swH+KOfKlcqr90pNV
S+2cOg
--- 727nBW/4ALXLr7W79wMIPyqhJbPzxCI3+W14kbRHx84
ÙI£šåÉO�0V^ …N ÓL8jÇ#gÞvÄÊû Š£ìü
ð@Záè¦ùÕ€"œ*;ØmGÊ&KÙHj68×g#e#_Mì�-È

View file

@ -0,0 +1,7 @@
age-encryption.org/v1
-> ssh-ed25519 iNceIg JvzQt+2ZkDJMDm1KlZQdDml8H4ycJ6AokJPSoZP5cU4
wpodFTm/MHvNUgNMfKsRkBcqixtW01beo6sAiEdClcM
-> ]s%j-grease F80K
+qNWHTRpraF9RkyWQgtAKTyx6zHnRE186qaTSMkEA6aRCsT6Gg
--- eVGyjUp6M/kxFZahyFU1yzoLJSYuGduGZHf6tqkblCI
(=qÕ“±›É<š$S±BãY©³a9jû~´Ý|Az®±·./€Å~JMÀw˜_¨%ò•9ÿbqg(Y>*3V^­ÝèÇ&•ù»+Ja

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> X25519 uJ3uiXX1C9PpMhT3kcYvUf8mIGxD8KTB6gGKdPGJnCs
ei8KR51jD/rWUp494k6M20oTrwDTiGpdkbOOmW4lOXo
-> piv-p256 xqSe8Q A92Qea9NZuHlV2xGjSo53jlPVnKjwBTbMPF23PeXXDrq
IfzttqGs1jW3RlOKGm08vKtJIIkzwRT1fUoMwkbMbuU
-> (-grease ;&ILFt\Z \H g&6+q2Xa Z
ZribRa/ctUpGLy4veZe+BF+3YnF6tku94bsH72Exo2WulHZS
--- Std/62CowuRVpxSYuzhJLHy5jNWMpnl6ILk4U7oW54s
÷÷È:Ĉrês[á ­½¹oÞ�ýïlâø£;Þl]m3�ÌŽnmËÐ5:Rk§§”Ù4Ç�xN°E#Å$®éd£/ ÒünY�2r

View file

@ -0,0 +1 @@
7Vu1OqBCLq6WNvah8QFBjnwNZUfZqzToFyQH2g/RJR4=

Binary file not shown.

View file

@ -0,0 +1 @@
X2gXwt3IDGXOsg8Vy/yEhEQKCUS6ziLu5Kl8POMa1Sc=

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> X25519 srRVmbOIPGk6sUIAARd6RLzzpKBVwIn+9RyuAPJ7aFI
fxqhR+q4BDsscusJKTZTjKxeMPfLMnp/yZpIbvv6+SE
-> piv-p256 xqSe8Q A/GX9EkFhcY/IjNQju+YdWMPyKVUj4YWuOoWxmszc1ws
rGgn/7HdLObcwxYw8GthJxgiR6XTE3C0kY6UFMlMhfo
-> ?-#-grease s<AE@O? nco.4{
dWT6UI5KRbFdDQCI8WTikEjBNQ
--- ot7MrclD1E66EW1DGzR9g1wHDWZ3wr1/dzCR8SmwBXI
D´z¨±uQ…bD¼©ÿ�[›­û¥PŠ¢:SäŸQ&œv4Ž˜¡¾Ôή„håUæ’•2¢=RYãZµå©Â²Žz&ï²{‚

View file

@ -0,0 +1 @@
ZCoXjgt4eb4uEkxXDluY8t7aM5EKeHeUpqvVFhFcHFA=

Binary file not shown.

View file

@ -0,0 +1 @@
BuccuL6/E5B1xnuhaLQf/pOll+TPeeFEGU1ZPd8PpjA=

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> X25519 ocNApTQlwFHphPMWeXS60TWO8RY4kXv1/G7mpvCRfno
q4tgumwcZKxNrObdkxLpU9tPttrDe5oZzOZYu+boNCE
-> piv-p256 xqSe8Q A8FzxSYN5kOVb8VG57H105SMUC8P+IRBz5oCN4QX7F6D
7YesnMqNXTyR5Ojtli9R8atxm5dqi9cjEvnnuyT6I1g
-> %5P"-grease ,Wf aH@;2_dA ~4s:8[
opJOhAN4Evvp4x7ndCEfALKDUMvvpqlbwUTSplehbPI
--- oMT/RknMnLIf0ujr+Q/xOCxN8qDOVkNYCVEjoJ3AscA
¥vŒúYv](Õ¬–¥*þ™nÛ‡ Eö1 ›x”͘]¯Œ:;ÿi' -µÑ«Klú=úñúPÎô·nv}i|5QÝ £’s

View file

@ -0,0 +1,9 @@
age-encryption.org/v1
-> X25519 n0ZRfnfbYjTn81ODTPvPNmKsBXDGwV6Jwgn4ZMgF/Us
SrmR4Z6rteUSVrji5cbRlj5tSQAcBoWHKvsombOI9O4
-> piv-p256 xqSe8Q A1lLYmIImnShNJs/w0ZVFj6s3RDNvo/nGq9KeqK9Ig7b
6GRcS3PVrcL6zhW/1XpMup1fcCPgelPuXmdt3t+J08s
-> EG"Ke-grease N
37HKhYODIAQxbHJI
--- ub+9rWPOnVMWpczIB/ForaQp96zRfOVV7bMuTij/1oQ
.hý®ÿ6õÄ{JΩ†¨—Åš×Î!˜,õ ¤½X2™ºÿŽPLçF{GXœ¹B†ÈÉõm&ŠÃcÿuZoÜ£B0Gì’ÍŽa×ßöç

View file

@ -0,0 +1,10 @@
age-encryption.org/v1
-> X25519 8SGiO7KFBiQdJk1Jo4E+56tO0pLmMYm+bPKR6g1DIhI
Cw0mWE+UCF3FWQ2FIdgK4kzRSZFfwgWdByHv4z0Abis
-> piv-p256 xqSe8Q AxtYXQlcKaqNDVzSMhqnznLAbkoeK2H0AP077+zm2prm
dWzLjMjdV5ymJ2BNcHB7PbIBFHJffmRr3/gX+U8XCpM
-> `-grease " &J
U2DWpgjq1+nj62O9GDB0BNRgrqHy5fb53tYKHmmK9Q
--- Y1GEP2kbfAqpVO4qWxnvkP3hloEyAAwIzy4PllrnTQc
faˆ�©U¦’yGòYP©hï"pnxÍ’ìV*ÚÅóíGÁÖK=‡÷߇ÛYît5?QuaÞµKBQ…y>²
ûÏ<h&ÕtnÌý2ëyœç