2026-01-07 14:27:55 +03:00
|
|
|
{
|
|
|
|
|
grace_period 10s
|
|
|
|
|
order coraza_waf first
|
2026-01-14 11:03:59 +03:00
|
|
|
{% if waf_config_email | default(None) != None %}
|
|
|
|
|
email "{{ waf_config_email }}"
|
2026-01-07 14:27:55 +03:00
|
|
|
{% endif %}
|
|
|
|
|
|
2026-01-14 11:03:59 +03:00
|
|
|
{% if waf_config_ca_root | default(None) != None %}
|
2026-01-07 14:27:55 +03:00
|
|
|
tls {
|
2026-01-14 11:03:59 +03:00
|
|
|
ca_root "{{ waf_config_ca_root }}"
|
2026-01-07 14:27:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
log waf {
|
|
|
|
|
format json
|
|
|
|
|
include "http.handlers.waf"
|
2026-01-19 21:00:14 +03:00
|
|
|
output stdout
|
2026-01-07 14:27:55 +03:00
|
|
|
}
|
2026-01-14 11:03:59 +03:00
|
|
|
{% if waf_config_local_ca_name | default(None) != None %}
|
2026-01-07 14:27:55 +03:00
|
|
|
|
|
|
|
|
pki {
|
|
|
|
|
ca local {
|
2026-01-14 11:03:59 +03:00
|
|
|
name "{{ waf_config_local_ca_name }}"
|
2026-01-07 14:27:55 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{% endif %}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 11:03:59 +03:00
|
|
|
{% for site in waf_config_sites %}
|
2026-01-07 14:27:55 +03:00
|
|
|
{% if (site.redirect_from_aliases | default(true)) and (site.aliases | default([]) | length) > 0 %}
|
|
|
|
|
{{ site.aliases | join(', ') }} {
|
|
|
|
|
redir https://{{ site.site }}{uri}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{% else %}
|
|
|
|
|
{% for alias in site.aliases | default([]) %}
|
|
|
|
|
{{ alias }},
|
|
|
|
|
{% endfor %}
|
|
|
|
|
{% endif %}
|
|
|
|
|
{{ site.site }} {
|
|
|
|
|
encode gzip zstd
|
|
|
|
|
|
|
|
|
|
{% for path in site.paths %}
|
|
|
|
|
handle {{ path.path | default('*') }} {
|
|
|
|
|
reverse_proxy {
|
|
|
|
|
to {{ path.addrs | join(' ') }}
|
|
|
|
|
header_up X-Real-IP {remote}
|
|
|
|
|
lb_policy ip_hash
|
|
|
|
|
# Active checks
|
|
|
|
|
# health_uri /
|
|
|
|
|
# health_interval 10s
|
|
|
|
|
# health_timeout 5s
|
|
|
|
|
# health_status 200
|
|
|
|
|
# Passive checks
|
|
|
|
|
fail_duration 30s
|
|
|
|
|
max_fails 3
|
|
|
|
|
unhealthy_latency 2000ms
|
|
|
|
|
transport http {
|
|
|
|
|
# tls_server_name {host}
|
|
|
|
|
# tls_insecure_skip_verify
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
{% endfor %}
|
|
|
|
|
|
|
|
|
|
{% if site.custom_cert | default (false) %}
|
|
|
|
|
tls {{ site.custom_cert_file }} {{ site.custom_key_file }}
|
|
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
{% if site.blacklist | default([]) | length > 0 %}
|
|
|
|
|
@blocked {
|
|
|
|
|
{% for ip in site.blacklist | default([]) %}
|
|
|
|
|
remote_ip {{ ip }}
|
|
|
|
|
{% endfor %}
|
|
|
|
|
}
|
|
|
|
|
respond @blocked "Access Denied" 403
|
|
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
{% if site.bot_barrier | default (false) %}
|
|
|
|
|
bot_barrier {
|
2026-01-14 11:03:59 +03:00
|
|
|
secret {{ waf_config_bot_barrier_secret }}
|
2026-01-07 14:27:55 +03:00
|
|
|
complexity 18
|
|
|
|
|
valid_for 30m
|
|
|
|
|
seed_cookie_name __chall_{{ site.name }}_seed
|
|
|
|
|
solution_cookie_name __chall_{{ site.name }}_solution
|
|
|
|
|
mac_cookie_name __chall_{{ site.name }}_mac
|
|
|
|
|
template /etc/caddy/bot_barrier_template.html
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{% endif %}
|
|
|
|
|
rate_limit {
|
|
|
|
|
# distributed
|
|
|
|
|
zone remote_ip {
|
|
|
|
|
key {remote.ip}
|
|
|
|
|
events {{ site.rate_events | default('1000') }}
|
|
|
|
|
window {{ site.rate_window | default('1m') }}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coraza_waf {
|
|
|
|
|
load_owasp_crs
|
|
|
|
|
directives `
|
|
|
|
|
Include "/etc/caddy/sites/{{ site.name }}/coraza.conf"
|
|
|
|
|
Include "/etc/caddy/sites/{{ site.name }}/crs-setup.conf"
|
2026-01-19 15:06:34 +03:00
|
|
|
{% for plugin in site.plugins | default(waf_config_defaults.plugins) %}
|
|
|
|
|
Include "/etc/caddy/plugins/{{ plugin }}-config.conf"
|
|
|
|
|
Include "/etc/caddy/plugins/{{ plugin }}-before.conf"
|
|
|
|
|
{% endfor %}
|
2026-01-07 14:27:55 +03:00
|
|
|
Include "/etc/caddy/sites/{{ site.name }}/exclusions-request-before.conf"
|
2026-01-14 11:03:59 +03:00
|
|
|
Include "/etc/caddy/coreruleset-{{ waf_config_owasp_crs_version }}/rules/*.conf"
|
2026-01-07 14:27:55 +03:00
|
|
|
Include "/etc/caddy/sites/{{ site.name }}/exclusions-response-after.conf"
|
2026-01-19 15:06:34 +03:00
|
|
|
{% for plugin in site.plugins | default(waf_config_defaults.plugins) %}
|
|
|
|
|
Include "/etc/caddy/plugins/{{ plugin }}-after.conf"
|
|
|
|
|
{% endfor %}
|
2026-01-07 14:27:55 +03:00
|
|
|
SecRuleEngine On
|
|
|
|
|
`
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-19 21:00:14 +03:00
|
|
|
# Cf. https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html
|
|
|
|
|
header {
|
|
|
|
|
# Access-Control-Allow-Origin "https://secure.gravatar.com"
|
|
|
|
|
Content-Security-Policy "upgrade-insecure-requests; worker-src 'self' blob:; connect-src 'self'; media-src 'self'; img-src 'self' data: https://secure.gravatar.com/avatar/; font-src 'self' data:; style-src 'self' 'unsafe-inline'; object-src 'none'; frame-src 'self' blob:; child-src 'self'; form-action 'self'; frame-ancestors 'self'"
|
|
|
|
|
# Cross-Origin-Embedder-Policy "require-corp"
|
|
|
|
|
Cross-Origin-Opener-Policy "same-origin"
|
|
|
|
|
Cross-Origin-Resource-Policy "same-site"
|
|
|
|
|
Permissions-Policy "geolocation=(), camera=(), microphone=(), interest-cohort=()"
|
|
|
|
|
Referrer-Policy "strict-origin-when-cross-origin"
|
|
|
|
|
Strict-Transport-Security "max-age=2635200; includeSubDomains"
|
|
|
|
|
X-Content-Type-Options "nosniff"
|
|
|
|
|
X-Frame-Options "DENY"
|
|
|
|
|
X-Robots-Tag "noindex, nofollow"
|
|
|
|
|
?Content-Type "text/html; charset=UTF-8"
|
|
|
|
|
>Set-Cookie "(.*)" "$1; SameSite=None; Secure;"
|
|
|
|
|
-Server
|
|
|
|
|
-Via
|
|
|
|
|
-X-Powered-By
|
|
|
|
|
-X-AspNet-Version
|
|
|
|
|
-X-AspNetMvc-Version
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-07 14:27:55 +03:00
|
|
|
handle_errors 403 {
|
|
|
|
|
header X-Blocked "true"
|
|
|
|
|
respond "Your request was blocked."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log {
|
|
|
|
|
format json
|
|
|
|
|
output file /var/log/caddy/site_{{ site.name }}.log
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{% endfor %}
|