nix-flakes/hosts/server/hikari/hako/services/caddy.nix
2026-09-07 11:16:13 +09:00

404 lines
12 KiB
Nix

{ pkgs, ... }:
let
errorHandlers = ''
handle_errors {
@unauthorized expression {http.error.status_code} == 401
handle @unauthorized {
rewrite * /401.html
root * /var/static/error
header Hey "Are you Hacker??? 😠"
file_server
}
@notFound expression {http.error.status_code} == 404
handle @notFound {
rewrite * /404.html
root * /var/static/error
file_server
}
@gone expression {http.error.status_code} == 410
handle @gone {
rewrite * /410.html
root * /var/static/error
file_server
}
@serverError expression {http.error.status_code} in [500, 502, 504, 505]
handle @serverError {
rewrite * /5xx.html
root * /var/static/error
file_server
}
@clientError expression {http.error.status_code} in [403, 405, 411, 497]
handle @clientError {
rewrite * /client.html
root * /var/static/error
file_server
}
}
'';
withErrors = config: ''
${config}
${errorHandlers}
'';
reverseProxy =
upstream:
withErrors ''
reverse_proxy ${upstream}
'';
reverseProxyWithHeaders =
upstream:
withErrors ''
reverse_proxy ${upstream} {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up Upgrade {>Upgrade}
header_up Connection {>Connection}
}
'';
staticHost = host: {
extraConfig = withErrors ''
root * /var/static/${host}
@notExist {
not file
not file {path}/
}
handle @notExist {
rewrite * /410.html
root * /var/static/error
file_server
}
file_server
@txt path *.txt
header @txt Content-Type "text/plain; charset=utf-8"
header {
Access-Control-Allow-Origin *
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization"
Access-Control-Max-Age 3600
}
'';
};
# Keep these explicit hosts for their existing per-host content. The
# wildcard host below handles any other one-level subdomain.
staticHosts = [
"26layer7beginner.imnya.ng"
"alt.imnya.ng"
"dday.imnya.ng"
"download.imnya.ng"
"f.imnya.ng"
"hako.imnya.ng"
"ilovecloudflare.imnya.ng"
"img.imnya.ng"
"jongyeol.imnya.ng"
"letters.imnya.ng"
"log.imnya.ng"
"nixos.imnya.ng"
"pack.imnya.ng"
"pf.imnya.ng"
"potato.imnya.ng"
"sometesting.imnya.ng"
"spam.imnya.ng"
"twohearts.imnya.ng"
"whs.imnya.ng"
];
cloudflareIpRanges = [
"103.21.244.0/22"
"103.22.200.0/22"
"103.31.4.0/22"
"104.16.0.0/13"
"104.24.0.0/14"
"108.162.192.0/18"
"131.0.72.0/22"
"141.101.64.0/18"
"162.158.0.0/15"
"172.64.0.0/13"
"173.245.48.0/20"
"188.114.96.0/20"
"190.93.240.0/20"
"197.234.240.0/22"
"198.41.128.0/17"
"2400:cb00::/32"
"2606:4700::/32"
"2803:f800::/32"
"2405:b500::/32"
"2405:8100::/32"
"2a06:98c0::/29"
"2c0f:f248::/32"
];
in
{
services.caddy = {
enable = true;
email = "imnyang@pm.me";
openFirewall = true;
package = pkgs.caddy.withPlugins {
plugins = [
"github.com/caddy-dns/cloudflare@v0.2.4"
"github.com/aksdb/caddy-cgi/v2@v2.2.6"
"github.com/shift72/caddy-geo-ip@v0.6.0"
"github.com/lolPants/caddy-requestid@v1.1.2"
"github.com/WeidiDeng/caddy-cloudflare-ip@v0.0.0-20231130002422-f53b62aa13cb"
"github.com/caddyserver/ntlm-transport@v0.1.2"
"github.com/ueffel/caddy-brotli@v1.6.0"
"github.com/RussellLuo/caddy-ext/ratelimit@v0.3.0"
"github.com/neodyme-labs/user_agent_parse@v0.0.1"
];
hash = "sha256-r4MgRk8HpKYx7f8FmA3PKgD/5ZNTIHpQvlE+zRbP0lw=";
};
globalConfig = ''
admin unix//run/caddy/admin.socket
storage file_system /var/lib/caddy
servers {
trusted_proxies static ${builtins.concatStringsSep " " cloudflareIpRanges}
client_ip_headers Cf-Connecting-Ip
}
debug
'';
virtualHosts =
builtins.listToAttrs (
map (host: {
name = host;
value = staticHost host;
}) staticHosts
)
// {
"*.imnya.ng".extraConfig = withErrors ''
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
root * /var/static/{host}
file_server
'';
"api.imnya.ng".extraConfig = reverseProxyWithHeaders "127.0.0.1:10118";
"docs.imnya.ng".extraConfig = reverseProxy "100.70.1.1:3939";
"panel.imnya.ng".extraConfig = reverseProxy "127.0.0.1:32981";
"monitorss.imnya.ng".extraConfig = reverseProxy "127.0.0.1:3132";
"ip.imnya.ng".extraConfig = ''
header Access-Control-Allow-Origin "*"
header Access-Control-Allow-Methods "GET, OPTIONS"
header Access-Control-Allow-Headers "*"
@options method OPTIONS
respond @options 204
@ip path /
respond @ip "{http.request.header.Cf-Connecting-Ip}"
'';
"imnya.ng".extraConfig = withErrors ''
handle_path /.well-known/webfinger {
root * /var/www/imnya.ng
file_server
}
root * /var/static/imnya.ng
file_server {
index index.html
}
redir /testtesttest "https://docs.google.com/forms/d/e/1FAIpQLScFDIU151s-CwKZccCET0kfdPigluiGb8BY7vL2US85dAgmhw/viewform?entry.2088744905=test" 302
redir /discord https://api.imnya.ng/discord_invite 302
redir /al https://f.imnya.ng/archlinux/install 302
redir /a https://cloud.hrts.kr/s/ZSHcpHgb9jdtaMR 302
redir / https://www.figma.com/deck/LtnSWN6FHhHhTMIPECkdKT 302
redir /tree https://colormytree.me/2025/01KB8E3N0YS1X4EQZDZQ855HAG 302
redir /memos https://discord.gg/CrSjzppDed 302
redir /ctf/29hackcampwriteup "https://imneko.notion.site/29-CTF-31ca3fe0643b809291d2c7c3a1a818b6?source=copy_link" 302
redir /ctf/2025logcon "https://imneko.notion.site/2025-LOGCON-Write-UP-17fa3fe0643b80c0833adf1bd85f46fa?source=copy_link" 302
redir /ctf/2024layer7 "https://imneko.notion.site/2024-Layer7-CTF-177a3fe0643b802d96d9e255f32bf034?pvs=74" 302
'';
"cdn-wiki.tpr.kr".extraConfig = withErrors ''
handle_path /i/* {
rewrite * /thetree/i{uri}
reverse_proxy 127.0.0.1:9000
}
'';
"tts.imnya.ng".extraConfig = ''
reverse_proxy 10.11.8.30:8080
'';
"vw.imnya.ng".extraConfig = reverseProxy "127.0.0.1:30505";
"jongyeoliswebscale.imnya.ng".extraConfig = withErrors ''
basic_auth {
jongyeol $2a$10$I00t8tJJLrcl4jZznO/XmOtd84uGAeJxL71wbN/sYksFrAvZ08n0q
}
reverse_proxy 10.20.31.48:3001 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up Upgrade {>Upgrade}
header_up Connection {>Connection}
}
'';
"auth.imnyang.dev".extraConfig = reverseProxy "10.11.8.25:3000";
"imnyang.dev".extraConfig = reverseProxy "82.21.82.30:3000";
"wiki.imnyang.dev".extraConfig = reverseProxy "10.11.8.150:51337";
"adofai.gay".extraConfig = withErrors ''
handle_path /.well-known/discord {
respond "dh=81062ffb49f63e8b7310905aee38aada33a68edb" 200
header Content-Type text/plain
}
redir https://7thbe.at/#adofai 302
'';
"wiki.tpr.kr".extraConfig = reverseProxy "127.0.0.1:51337";
"imlo.li".extraConfig = withErrors ''
handle /rpmfusion.sh {
root * /var/static/scripts
file_server
}
handle {
reverse_proxy 127.0.0.1:4000
}
'';
"http://117.110.40.206".extraConfig = "";
"event.dazzle.st".extraConfig = ''
reverse_proxy 127.0.0.1:9794
'';
"plutos.dazzle.st".extraConfig = ''
reverse_proxy 10.11.8.200:3000
'';
"multiplay.rpc.dazzle.st".extraConfig = ''
@preflight method OPTIONS
header {
Access-Control-Allow-Origin *
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers *
}
respond @preflight "" 204
reverse_proxy 10.20.30.101:6769
'';
"wakapi.hrts.kr".extraConfig = reverseProxy "127.0.0.1:13000";
"ziit.hrts.kr".extraConfig = reverseProxy "127.0.0.1:6030";
"hikari-panel.hrts.kr".extraConfig = reverseProxy "10.11.8.104:8080";
"http://hikari.icn.hrts.kr".extraConfig = withErrors ''
respond "" 200
'';
"pve.hrts.kr".extraConfig = withErrors ''
reverse_proxy https://10.11.8.100:8006 {
header_up X-Real-Ip {remote_host}
header_up X-Http-Version {http.request.proto}
transport http {
tls_insecure_skip_verify
}
}
'';
"auth.hrts.kr".extraConfig = reverseProxy "10.11.8.32:1411";
"git.hrts.kr".extraConfig = reverseProxy "127.0.0.1:3000";
"panel.hrts.kr".extraConfig = reverseProxy "127.0.0.1:3929";
"pgadmin.hrts.kr".extraConfig = reverseProxy "127.0.0.1:5050";
"al-1s.hrts.kr".extraConfig = reverseProxy "127.0.0.1:9120";
"hrts.kr".extraConfig = withErrors ''
redir / https://imnya.ng 302
redir /whyididmakethis.md "https://md.imnya.ng/?c=𣏫𧆘𡚴𢓬𧆬𣺯𢓬𧊈𣪖㸿𧈠𠪕𢷬𧆁𠪏𧈠𣪝𣣪𢓬𧆬𣺯𢓬𧊈𣪖㸿" 302
'';
"cloud.hrts.kr".extraConfig = ''
redir https://cloud.mizuki.guru{uri}
'';
"i.mizuki.guru".extraConfig = reverseProxy "127.0.0.1:10040";
"git.mizuki.guru".extraConfig = reverseProxy "127.0.0.1:3000";
"cloud.mizuki.guru".extraConfig = reverseProxy "10.11.8.100:60300";
"mizuki.guru".extraConfig = withErrors ''
redir / https://akiyama.mizuki.guru 302
reverse_proxy /_matrix* 127.0.0.1:6167
'';
"api.kanade.mizuki.guru".extraConfig = reverseProxy "10.20.31.41:3000";
"akiyama.mizuki.guru".extraConfig = withErrors ''
handle /api {
reverse_proxy 10.11.8.121:1108
}
handle /api/* {
reverse_proxy 10.11.8.121:1108
}
handle {
reverse_proxy 10.11.8.121:3000
}
'';
"phrygiacomo.mizuki.guru".extraConfig = withErrors ''
basic_auth {
imnyang $2a$10$zA83XKt84pcXiwfkuvNmKOpQ5Cw0IP6m/.1AX0ahkVQaPOzrmFlxm
}
reverse_proxy 10.20.31.103:3000
'';
"http://hikari.icn.mizuki.guru".extraConfig = withErrors ''
respond "" 200
'';
"fs.mizuki.guru".extraConfig = reverseProxy "10.11.8.121:9000";
"broadcast.epc.mizuki.guru".extraConfig = ''
reverse_proxy 10.20.30.101:1234
'';
"apijongyeoltts.mizuki.guru".extraConfig = reverseProxy "10.20.30.103:9880";
"demo.mizuki.guru".extraConfig = reverseProxy "10.20.30.103:3000";
};
};
systemd.services.caddy = {
requires = [ "mnt-static.mount" ];
after = [ "mnt-static.mount" "systemd-tmpfiles-setup.service" ];
serviceConfig = {
# Keep the manually provisioned token outside NixOS-managed /etc.
# /var/lib/caddy is persistent across system generations.
EnvironmentFile = "/var/lib/caddy/cloudflare.env";
RuntimeDirectory = "caddy";
RuntimeDirectoryMode = "0755";
};
};
}