5116faf0d3
- caddy: security headers (X-Content-Type-Options/X-XSS-Protection/ X-Frame-Options) on all vhosts + baseline CSP; strip SnappyMail upstream copies via header_down on mail.severijnse.eu - tlsa-updater: compute TLSA 3 1 1 from cert SPKI (SHA-256), sync _25/_465/_993, fail-safe placeholders; coredns zone updated - pre-commit: wire cachix/git-hooks.nix (alejandra, statix, actionlint, deadnix); CI pre-commit job over x86_64 + aarch64 matrix - gitea: enable Gitea Actions + self-hosted runner (native:host, aarch64 via binfmt); add .gitea/workflows/ci.yml and local hook - fix statix warnings (merge repeated systemd/database/configFile keys) + disable empty_pattern via statix.toml (nixpkgs standard) - Clean up unused lambda patterns across 38 .nix files via deadnix - Format whole repo with alejandra (27 files) - Remove .github/workflows/ci.yml (Gitea shadows .github; runner labels differ) - Fix CI nix-not-found: export /run/current-system/sw/bin in PATH - Trim aarch64 from pre-commit matrix (no QEMU binfmt deployed yet)
243 lines
6.7 KiB
Nix
243 lines
6.7 KiB
Nix
{...}: let
|
|
antiScrape = ''
|
|
@bad_bot {
|
|
header_regexp User-Agent "(?i)(scrapy|cpython-requests|python-requests|curl|wget|go-http-client|ltx71|petalbot|bytespider|dotbot|ahrefsbot|semrushbot|mj12bot|dataforseo|facebookexternalhit|claudebot|anthropic-ai|perplexity|gptbot|chatgpt-user|omnisci|imgproxy|ccbot|exabot|360spider|baiduspider|sogou|duckduckgo|amazonbot|cohere-ai|diffbot|imagesiftbot).*"
|
|
}
|
|
respond @bad_bot "" 444
|
|
header {
|
|
X-Robots-Tag "noindex, nofollow, noai, noimageai"
|
|
}
|
|
'';
|
|
in {
|
|
services.caddy = {
|
|
enable = true;
|
|
group = "caddy";
|
|
dataDir = "/var/lib/caddy";
|
|
logDir = "/var/log/caddy";
|
|
globalConfig = ''
|
|
email jory@severijnse.eu
|
|
servers {
|
|
trusted_proxies static private_ranges
|
|
}
|
|
'';
|
|
# Global Caddyfile snippets (shared across all virtual hosts).
|
|
extraConfig = ''
|
|
(admin_gate) {
|
|
@notvpn not remote_ip 10.8.0.0/24
|
|
respond @notvpn "Forbidden" 403
|
|
}
|
|
# Security headers applied to every response of every site that
|
|
# imports this snippet (covers all current and future hosts).
|
|
# For proxied hosts whose upstream sets its own copies, strip them
|
|
# with `header_down` inside the reverse_proxy block instead.
|
|
(security_headers) {
|
|
header {
|
|
X-Content-Type-Options "nosniff"
|
|
X-XSS-Protection "0"
|
|
X-Frame-Options "SAMEORIGIN"
|
|
}
|
|
}
|
|
(csp) {
|
|
header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'; form-action 'self'; connect-src 'self' wss: ws:"
|
|
}
|
|
'';
|
|
virtualHosts = {
|
|
"severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
@logo path /logo.svg
|
|
handle @logo {
|
|
root * /srv
|
|
file_server
|
|
header Content-Type image/svg+xml
|
|
header Cache-Control "public, immutable, max-age=31536000"
|
|
header X-Content-Type-Options nosniff
|
|
}
|
|
handle {
|
|
redir https://jory.severijnse.eu{uri} permanent
|
|
}
|
|
'';
|
|
};
|
|
|
|
"www.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
redir https://jory.severijnse.eu{uri} permanent
|
|
'';
|
|
};
|
|
|
|
"jory.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
root * /srv/jory
|
|
file_server
|
|
try_files {path} /index.html
|
|
encode zstd gzip
|
|
header Strict-Transport-Security "max-age=31536000;"
|
|
@robots path /robots.txt
|
|
handle @robots {
|
|
header Content-Type text/plain
|
|
respond `User-agent: *
|
|
Allow: /
|
|
|
|
User-agent: Googlebot
|
|
Allow: /
|
|
|
|
User-agent: Bingbot
|
|
Allow: /
|
|
|
|
User-agent: Twitterbot
|
|
Allow: /
|
|
|
|
User-agent: facebookexternalhit
|
|
Allow: /
|
|
|
|
User-agent: GPTBot
|
|
User-agent: ChatGPT-User
|
|
User-agent: OAI-SearchBot
|
|
User-agent: ClaudeBot
|
|
User-agent: Claude-Web
|
|
User-agent: anthropic-ai
|
|
User-agent: PerplexityBot
|
|
User-agent: Bytespider
|
|
User-agent: Amazonbot
|
|
User-agent: CCBot
|
|
User-agent: Google-Extended
|
|
User-agent: Applebot-Extended
|
|
Disallow: /
|
|
` 200
|
|
}
|
|
'';
|
|
};
|
|
|
|
"mta-sts.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
root * /srv
|
|
file_server
|
|
header Content-Type text/plain
|
|
header Cache-Control "public, max-age=300"
|
|
header X-Content-Type-Options nosniff
|
|
'';
|
|
};
|
|
|
|
"vault.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
import admin_gate
|
|
header Strict-Transport-Security "max-age=31536000;"
|
|
reverse_proxy 127.0.0.1:1001
|
|
encode zstd gzip
|
|
'';
|
|
};
|
|
|
|
"git.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
reverse_proxy 127.0.0.1:3000
|
|
encode zstd gzip
|
|
'';
|
|
};
|
|
|
|
"mail.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
${antiScrape}
|
|
reverse_proxy 127.0.0.1:8888 {
|
|
# Strip copies set by the upstream SnappyMail container so we
|
|
# emit exactly one correct value of each security header.
|
|
header_down -X-Frame-Options
|
|
header_down -X-XSS-Protection
|
|
header_down -X-Content-Type-Options
|
|
}
|
|
encode zstd gzip
|
|
'';
|
|
};
|
|
|
|
"vpn.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
reverse_proxy 127.0.0.1:51821
|
|
encode zstd gzip
|
|
'';
|
|
};
|
|
|
|
"mine.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
reverse_proxy 127.0.0.1:81
|
|
'';
|
|
};
|
|
|
|
"music.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
reverse_proxy 127.0.0.1:4321
|
|
'';
|
|
};
|
|
|
|
"fail2ban.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
import admin_gate
|
|
reverse_proxy 127.0.0.1:8080
|
|
'';
|
|
};
|
|
|
|
"automate.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
import admin_gate
|
|
reverse_proxy 127.0.0.1:5678
|
|
encode zstd gzip
|
|
'';
|
|
};
|
|
|
|
"pay.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
${antiScrape}
|
|
import admin_gate
|
|
reverse_proxy 127.0.0.1:5000
|
|
encode zstd gzip
|
|
'';
|
|
};
|
|
|
|
"http://ip.severijnse.eu" = {
|
|
extraConfig = ''
|
|
import security_headers
|
|
import csp
|
|
header Content-Type text/plain
|
|
respond {client_ip} 200
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"d /srv 0755 caddy caddy -"
|
|
"d /srv/jory 0755 caddy caddy -"
|
|
];
|
|
}
|