Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4aee70b07a | |||
| 24034ad284 | |||
| d08e03e28c | |||
| d8ab34029f | |||
| ac36dec16c | |||
| c6edcc9bc5 | |||
| df42ad6e88 | |||
| a213a75f72 | |||
| d7b6e468d7 | |||
| 7e4857f762 | |||
| ccdabff0be | |||
| cbef8530f0 | |||
| 798e748a1f | |||
| 43af15bd4d | |||
| 10a7ac53aa | |||
| 6f17dcffc6 | |||
| 4e30f03c0e | |||
| 5257840222 | |||
| 52cd626f82 | |||
| 78be28c68e | |||
| 1208e5092b | |||
| 717aaca7eb | |||
| 98506f9803 | |||
| 309a0cd971 | |||
| c5b82f5b48 | |||
| 82a299bf33 | |||
| a717aa378f | |||
| e40cd47d28 | |||
| a7bcb0728b | |||
| bc46ae6ada |
@@ -1,8 +1,10 @@
|
||||
name: Publish collection to Galagxy
|
||||
name: Publish collection to Galaxy
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
branches:
|
||||
- main
|
||||
- "main"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
@@ -10,10 +12,22 @@ jobs:
|
||||
steps:
|
||||
- run: apt install -y pipx
|
||||
- run: pipx install ansible==12.3.0 --include-deps
|
||||
- run: pipx inject ansible ansible-lint --include-apps
|
||||
- run: pipx install yq
|
||||
- uses: actions/checkout@v6
|
||||
- run: cd seb4itik/byow && /root/.local/bin/ansible-galaxy collection build
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: seb4itik-byow
|
||||
path: /workspace/itik/bgwaf/seb4itik/byow/seb4itik-byow-0.1.2.tar.gz
|
||||
- run: >
|
||||
cd seb4itik/byow &&
|
||||
VER=$(yq -r '.version' galaxy.yml) &&
|
||||
/root/.local/bin/ansible-galaxy collection build &&
|
||||
/root/.local/bin/ansible-galaxy collection publish seb4itik-byow-${VER}.tar.gz --api-key ${{ secrets.GALAXY_API_KEY }}
|
||||
# - uses: actions/gitea-upload-artifact@v3
|
||||
# with:
|
||||
# path: /workspace/itik/bgwaf/seb4itik/byow/seb4itik-byow-0.1.2.tar.gz
|
||||
- name: Push artifacts
|
||||
run: |
|
||||
GITEA_REPO_OWNER=$(echo $GITEA_REPOSITORY | cut -d"/" -f1) && \
|
||||
GITEA_REPO_NAME=$(echo $GITEA_REPOSITORY | cut -d"/" -f2) && \
|
||||
curl \
|
||||
-X PUT \
|
||||
-H "Authorization: token ${{ secrets.CI_GITEA_PACKAGES_TOKEN }}" \
|
||||
--upload-file seb4itik-byow-${VER}.tar.gz \
|
||||
$GITEA_SERVER_URL/api/packages/${GITEA_REPO_OWNER}/generic/${GITEA_REPO_NAME}/${GITEA_SHA}/seb4itik-byow-${VER}.tar.gz
|
||||
|
||||
@@ -2,4 +2,6 @@
|
||||
|
||||
A WAF _à la carte_.
|
||||
|
||||
**WARNING: This is still a work in process**
|
||||
|
||||
See [seb4itik/byow/README.md](seb4itik/byow/README.md)
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
stub_to: "127.0.0.1@8600"
|
||||
caddy_sites: "{{ common.sites }}"
|
||||
ovh_ldp_cluster: "gra2.logs.ovh.com"
|
||||
ovh_ldp_token: ¨{{ secrets.ovh_ldp.token }}
|
||||
ovh_ldp_token: "{{ secrets.ovh_ldp.token }}"
|
||||
roles:
|
||||
- "local_ca_certs"
|
||||
- "consul"
|
||||
|
||||
@@ -69,7 +69,7 @@ Voir: https://github.com/coreruleset/plugin-registry
|
||||
|
||||
Fonctionnalités déjà implémentées:
|
||||
|
||||
- Déploiement et configuration avec Ansible
|
||||
- Déploiement et configuration avec une collection Ansible
|
||||
- Serveurs transient
|
||||
- VIP (avec Keepalived)
|
||||
- DNS dynamique (via Nomad)
|
||||
@@ -105,16 +105,17 @@ Fonctionnalités déjà implémentées:
|
||||
- Collecte des logs via OVH LDP
|
||||
- Whitelist
|
||||
- Static files et FastCGI pour PHP (NON, par design !)
|
||||
- Rate limit (non distribué)
|
||||
- CORS
|
||||
|
||||
Urgemment:
|
||||
|
||||
- Connexion à consul, ACL consul
|
||||
- Collection Ansible
|
||||
- Revérifier gestion CORS
|
||||
- Active backend checks
|
||||
- Correction de la collecte des logs via OVH LDP
|
||||
- Faire fonctionner le filtre dict2str via la collection Ansible
|
||||
|
||||
Fomctionnalités à ajouter:
|
||||
Fonctionnalités à ajouter:
|
||||
|
||||
- Rate limiting distribué
|
||||
- Antibot par:
|
||||
@@ -156,10 +157,6 @@ Fomctionnalités à ajouter:
|
||||
- [Documentation AWOSP CRS](https://coreruleset.org/docs/)
|
||||
- https://github.com/owasp-modsecurity/ModSecurity/wiki/Reference-Manual-(v3.x)
|
||||
|
||||
Modules
|
||||
- []()
|
||||
- []()
|
||||
|
||||
|
||||
### Installation d'Ansible avec pipx
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace: "seb4itik"
|
||||
name: "byow"
|
||||
version: "0.1.2"
|
||||
version: "0.2.1"
|
||||
readme: "README.md"
|
||||
authors:
|
||||
- "S. Namèche <sebastien@itik.fr>"
|
||||
|
||||
@@ -1,41 +1,36 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'dict2str': self.dict2str
|
||||
}
|
||||
|
||||
def dict2str(self, d, sep1='=', sep2=';', sep3=''):
|
||||
return sep2.join(
|
||||
[ str(k) + (sep1+str(v) if v!=None and v!='' else '') for k, v in d.items() ]
|
||||
) + sep3
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
DOCUMENTATION = r"""
|
||||
name: dict2str
|
||||
author: Sébastien Namèche (@seb4itik)
|
||||
short_description: This filter transforms a flat dict into a string using 4 separators.
|
||||
version_added: "0.1.1"
|
||||
short_description: This filter transforms a flat dict into a string using 3 separators.
|
||||
author: Sébastien Namèche (@seb4itik)
|
||||
description:
|
||||
- If my_dict is {one: "un", two: "deux", three: "trois"}, then:
|
||||
- {{ my_dict | dict2str(sep1=' is ', sep2=', ', sep3='.') }}
|
||||
- will produce:
|
||||
- "one is un, two is deux, three is trois."
|
||||
options:
|
||||
_input:
|
||||
description: A flat dict.
|
||||
type: dict
|
||||
elements: str, int, bool
|
||||
required: true
|
||||
sep0:
|
||||
description: Unique initial separator at the beginning of string.
|
||||
type: str
|
||||
default: ""
|
||||
sep1:
|
||||
description:
|
||||
- separator between each key and value pairs of the dict
|
||||
description: Separator between each key and value pairs of the dict.
|
||||
type: str
|
||||
default: "="
|
||||
sep2:
|
||||
description:
|
||||
- separator between key/value pairs of the dict that has been combined with sep1
|
||||
description: Separator between key/value pairs of the dict that has been combined with sep1.
|
||||
type: str
|
||||
default: ";"
|
||||
sep3:
|
||||
description:
|
||||
- unique final separator at the end of string
|
||||
description: Unique final separator at the end of string.
|
||||
type: str
|
||||
default: ""
|
||||
notes:
|
||||
@@ -43,9 +38,22 @@ DOCUMENTATION = """
|
||||
- sep2 will not be used.
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
data:
|
||||
|
||||
RETURN = r"""
|
||||
_value:
|
||||
type: str
|
||||
description:
|
||||
- String representating the dict using provided separators.
|
||||
"""
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'dict2str': self.dict2str
|
||||
}
|
||||
|
||||
def dict2str(self, d, sep0='', sep1='=', sep2=';', sep3=''):
|
||||
return sep0 + sep2.join(
|
||||
[ str(k) + (sep1+str(v) if v!=None and v!='' else '') for k, v in d.items() ]
|
||||
) + sep3
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
{
|
||||
http_port {{ caddy_http_port }}
|
||||
https_port {{ caddy_https_port }}
|
||||
{% if caddy_default_bind != "" %}
|
||||
default_bind {{ caddy_default_bind }}
|
||||
{% endif %}
|
||||
order geoip2_vars first
|
||||
order coraza_waf first
|
||||
admin off
|
||||
@@ -44,6 +46,43 @@
|
||||
}
|
||||
}
|
||||
|
||||
# 0=origin, 1=methods, 2=headers, 3=allow credentials, 4=max age, 5=vary, 6=expose headers
|
||||
(cors) {
|
||||
@match-cors-preflight-{args.0} {
|
||||
header Origin {args.0}
|
||||
method OPTIONS
|
||||
}
|
||||
header @match-cors-preflight-{args.0} {
|
||||
Access-Control-Allow-Origin "{args.0}"
|
||||
Access-Control-Allow-Methods "{args.1}"
|
||||
Access-Control-Allow-Headers "{args.2}"
|
||||
Access-Control-Allow-Credentials "{args.3}"
|
||||
Access-Control-Max-Age "{args.4}"
|
||||
Vary "{args.5}"
|
||||
}
|
||||
handle @match-cors-preflight-{args.0} {
|
||||
respond "" 204
|
||||
}
|
||||
|
||||
@match-cors-request-{args.0} {
|
||||
header Origin {args.0}
|
||||
not method OPTIONS
|
||||
}
|
||||
header @match-cors-request-{args.0} {
|
||||
>Access-Control-Allow-Origin "{args.0}"
|
||||
>Access-Control-Allow-Credentials "{args.3}"
|
||||
>Vary "{args.5}"
|
||||
>Access-Control-Expose-Headers "{args.6}"
|
||||
}
|
||||
}
|
||||
|
||||
(cors-deny) {
|
||||
@cors-preflight-deny method OPTIONS
|
||||
handle @cors-preflight-deny {
|
||||
respond "" 403
|
||||
}
|
||||
}
|
||||
|
||||
{% for site in caddy_sites %}
|
||||
|
||||
{% if (site.site_redirect_from_aliases | default(caddy_sites_defaults.site_redirect_from_aliases)) and (site.site_aliases | default([]) | length) > 0 %}
|
||||
@@ -103,33 +142,33 @@
|
||||
respond @geofilter "Access Denied" 403
|
||||
|
||||
{% if site.geoip_debug | default(caddy_sites_defaults.geoip_debug) %}
|
||||
header geoip-is_anonymous "{geoip2.is_anonymous}"
|
||||
header geoip-is_anonymous_vpn "{geoip2.is_anonymous_vpn}"
|
||||
header geoip-is_hosting_provider "{geoip2.is_hosting_provider}"
|
||||
header geoip-is_public_proxy "{geoip2.is_public_proxy}"
|
||||
header geoip-is_residential_proxy "{geoip2.is_residential_proxy}"
|
||||
header geoip-is_tor_exit_node "{geoip2.is_tor_exit_node}"
|
||||
header geoip-connection_type "{geoip2.connection_type}"
|
||||
header geoip-domain "{geoip2.domain}"
|
||||
header geoip-country_code "{geoip2.country_code}"
|
||||
header geoip-country_confidence "{geoip2.country_confidence}"
|
||||
header geoip-country_eu "{geoip2.country_eu}"
|
||||
header geoip-country_geoname_id "{geoip2.country_geoname_id}"
|
||||
header geoip-country_name "{geoip2.country_name}"
|
||||
header geoip-continent_code "{geoip2.continent_code}"
|
||||
header geoip-continent_geoname_id "{geoip2.continent_geoname_id}"
|
||||
header geoip-continent_name "{geoip2.continent_name}"
|
||||
header geoip-city_confidence "{geoip2.city_confidence}"
|
||||
header geoip-city_geoname_id "{geoip2.city_geoname_id}"
|
||||
header geoip-city_name "{geoip2.city_name}"
|
||||
header geoip-location_time_zone "{geoip2.location_time_zone}"
|
||||
header geoip-autonomous_system_number "{geoip2.autonomous_system_number}"
|
||||
header geoip-autonomous_system_organization "{geoip2.autonomous_system_organization}"
|
||||
header geoip-isp "{geoip2.isp}"
|
||||
header geoip-mobile_country_code "{geoip2.mobile_country_code}"
|
||||
header geoip-mobile_network_code "{geoip2.mobile_network_code}"
|
||||
header geoip-organization "{geoip2.organization}"
|
||||
header geoip-ip_address "{geoip2.ip_address}"
|
||||
header x-geoip-is-anonymous "{geoip2.is_anonymous}"
|
||||
header x-geoip-is-anonymous-vpn "{geoip2.is_anonymous_vpn}"
|
||||
header x-geoip-is-hosting-provider "{geoip2.is_hosting_provider}"
|
||||
header x-geoip-is-public-proxy "{geoip2.is_public_proxy}"
|
||||
header x-geoip-is-residential-proxy "{geoip2.is_residential_proxy}"
|
||||
header x-geoip-is-tor-exit-node "{geoip2.is_tor_exit_node}"
|
||||
header x-geoip-connection-type "{geoip2.connection_type}"
|
||||
header x-geoip-domain "{geoip2.domain}"
|
||||
header x-geoip-country-code "{geoip2.country_code}"
|
||||
# header x-geoip-country-confidence "{geoip2.country_confidence}"
|
||||
header x-geoip-country-eu "{geoip2.country_eu}"
|
||||
header x-geoip-country-geoname-id "{geoip2.country_geoname_id}"
|
||||
header x-geoip-country-name "{geoip2.country_name}"
|
||||
header x-geoip-continent-code "{geoip2.continent_code}"
|
||||
header x-geoip-continent-geoname-id "{geoip2.continent_geoname_id}"
|
||||
header x-geoip-continent-name "{geoip2.continent_name}"
|
||||
# header x-geoip-city-confidence "{geoip2.city_confidence}"
|
||||
header x-geoip-city-geoname-id "{geoip2.city_geoname_id}"
|
||||
header x-geoip-city-name "{geoip2.city_name}"
|
||||
header x-geoip-location-time-zone "{geoip2.location_time_zone}"
|
||||
header x-geoip-autonomous-system-number "{geoip2.autonomous_system_number}"
|
||||
header x-geoip-autonomous-system-organization "{geoip2.autonomous_system_organization}"
|
||||
header x-geoip-isp "{geoip2.isp}"
|
||||
header x-geoip-mobile-country-code "{geoip2.mobile_country_code}"
|
||||
header x-geoip-mobile-network-code "{geoip2.mobile_network_code}"
|
||||
header x-geoip-organization "{geoip2.organization}"
|
||||
header x-geoip-ip-address "{geoip2.ip_address}"
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -139,18 +178,6 @@
|
||||
{% endfor %}
|
||||
}
|
||||
|
||||
{% if site.bot_barrier | default(caddy_sites_defaults.bot_barrier) %}
|
||||
bot_barrier @not_whitelisted {
|
||||
secret {{ site.bot_barrier_secret | default(caddy_sites_defaults.bot_barrier_secret) | mandatory }}
|
||||
complexity {{ site.bot_barrier_complexity | default(caddy_sites_defaults.bot_barrier_complexity) }}
|
||||
valid_for {{ site.bot_barrier_valid_for | default(caddy_sites_defaults.bot_barrier_valid_for) }}
|
||||
seed_cookie_name __chall_{{ site.id }}_seed
|
||||
solution_cookie_name __chall_{{ site.id }}_solution
|
||||
mac_cookie_name __chall_{{ site.id }}_mac
|
||||
template {{ caddy_config_dir }}/bot_barrier_template.html
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if site.rate_limit | default(caddy_sites_defaults.rate_limit) %}
|
||||
rate_limit @not_whitelisted {
|
||||
# distributed
|
||||
@@ -183,11 +210,31 @@
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if site.cors | default(caddy_sites_defaults.cors) %}
|
||||
{% for origin in site.cors_allow_origins | mandatory %}
|
||||
import cors {{ origin }} "{{ site.cors_allow_methods | default(caddy_sites_defaults.cors_allow_methods) | join(', ') }}" "{{ site.cors_allow_headers | default(caddy_sites_defaults.cors_allow_headers) | join(', ') }}" "{{ site.cors_allow_credentials | default(caddy_sites_defaults.cors_allow_credentials) | ternary('true', 'false') }}" "{{ site.cors_max_age | default(caddy_sites_defaults.cors_max_age) }}" "{{ site.cors_vary | default(caddy_sites_defaults.cors_vary) | join(', ') }}" "{{ site.cors_expose_headers | default(caddy_sites_defaults.cors_expose_headers) | join(', ') }}"
|
||||
{% endfor %}
|
||||
import cors-deny
|
||||
{% endif %}
|
||||
|
||||
{% if site.bot_barrier | default(caddy_sites_defaults.bot_barrier) %}
|
||||
bot_barrier @not_whitelisted {
|
||||
secret {{ site.bot_barrier_secret | default(caddy_sites_defaults.bot_barrier_secret) | mandatory }}
|
||||
complexity {{ site.bot_barrier_complexity | default(caddy_sites_defaults.bot_barrier_complexity) }}
|
||||
valid_for {{ site.bot_barrier_valid_for | default(caddy_sites_defaults.bot_barrier_valid_for) }}
|
||||
seed_cookie_name __chall_{{ site.id }}_seed
|
||||
solution_cookie_name __chall_{{ site.id }}_solution
|
||||
mac_cookie_name __chall_{{ site.id }}_mac
|
||||
template {{ caddy_config_dir }}/bot_barrier_template.html
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
header {
|
||||
Content-Security-Policy "{{ caddy_sites_defaults.header_content_security_policy | combine(site.header_content_security_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}"
|
||||
Cross-Origin-Embedder-Policy "{{ site.header_cross_origin_embedder_policy | default(caddy_sites_defaults.header_cross_origin_embedder_policy) }}"
|
||||
Cross-Origin-Opener-Policy "{{ site.header_cross_origin_opener_policy | default(caddy_sites_defaults.header_cross_origin_opener_policy) }}"
|
||||
Cross-Origin-Resource-Policy "{{ site.header_cross_origin_resource_policy | default(caddy_sites_defaults.header_cross_origin_resource_policy) }}"
|
||||
# FIXME
|
||||
# Cross-Origin-Embedder-Policy "{{ site.header_cross_origin_embedder_policy | default(caddy_sites_defaults.header_cross_origin_embedder_policy) }}"
|
||||
# Cross-Origin-Opener-Policy "{{ site.header_cross_origin_opener_policy | default(caddy_sites_defaults.header_cross_origin_opener_policy) }}"
|
||||
# Cross-Origin-Resource-Policy "{{ site.header_cross_origin_resource_policy | default(caddy_sites_defaults.header_cross_origin_resource_policy) }}"
|
||||
Feature-Policy "{{ caddy_sites_defaults.header_feature_policy | combine(site.header_feature_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}"
|
||||
Permissions-Policy "{{ caddy_sites_defaults.header_permissions_policy | combine(site.header_permissions_policy | default({})) | dict2str(sep2=', ') }}"
|
||||
Referrer-Policy "{{ site.header_referrer_policy | default(caddy_sites_defaults.header_referrer_policy) }}"
|
||||
|
||||
@@ -78,7 +78,7 @@ SecResponseBodyAccess On
|
||||
# Which response MIME types do you want to inspect? You should adjust the
|
||||
# configuration below to catch documents but avoid static files
|
||||
# (e.g., images and archives).
|
||||
SecResponseBodyMimeType text/plain text/html text/xml
|
||||
SecResponseBodyMimeType text/plain text/html text/xml application/json
|
||||
|
||||
# Buffer response bodies of up to 512 KB in length.
|
||||
SecResponseBodyLimit 524288
|
||||
|
||||
@@ -11,7 +11,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.blocking_crs_paranoia_level={{ site.crs_paranoia_level | default(caddy_sites_defaults.crs_paranoia_level) }}"
|
||||
setvar:tx.blocking_paranoia_level={{ site.crs_paranoia_level | default(caddy_sites_defaults.crs_paranoia_level) }}"
|
||||
{% if site.crs_detection_paranoia_level | default(caddy_sites_defaults.crs_detection_paranoia_level) != None %}
|
||||
|
||||
## Detection paranoia level
|
||||
@@ -23,7 +23,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_detection_paranoia_level={{ site.crs_detection_paranoia_level | default(caddy_sites_defaults.crs_detection_paranoia_level) }}"
|
||||
setvar:tx.detection_paranoia_level={{ site.crs_detection_paranoia_level | default(caddy_sites_defaults.crs_detection_paranoia_level) }}"
|
||||
{% endif %}
|
||||
|
||||
## Enforce Body Processor URLENCODED
|
||||
@@ -35,7 +35,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_enforce_bodyproc_urlencoded={{ site.crs_enforce_bodyproc_urlencoded | default(caddy_sites_defaults.crs_enforce_bodyproc_urlencoded) }}"
|
||||
setvar:tx.enforce_bodyproc_urlencoded={{ site.crs_enforce_bodyproc_urlencoded | default(caddy_sites_defaults.crs_enforce_bodyproc_urlencoded) }}"
|
||||
|
||||
## Anomaly Scoring Mode Severity Levels
|
||||
SecAction \
|
||||
@@ -46,10 +46,10 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_critical_anomaly_score={{ site.crs_critical_anomaly_score | default(caddy_sites_defaults.crs_critical_anomaly_score) }},\
|
||||
setvar:tx.crs_error_anomaly_score={{ site.crs_error_anomaly_score | default(caddy_sites_defaults.crs_error_anomaly_score) }},\
|
||||
setvar:tx.crs_warning_anomaly_score={{ site.crs_warning_anomaly_score | default(caddy_sites_defaults.crs_warning_anomaly_score) }},\
|
||||
setvar:tx.crs_notice_anomaly_score={{ site.crs_notice_anomaly_score | default(caddy_sites_defaults.crs_notice_anomaly_score) }}"
|
||||
setvar:tx.critical_anomaly_score={{ site.crs_critical_anomaly_score | default(caddy_sites_defaults.crs_critical_anomaly_score) }},\
|
||||
setvar:tx.error_anomaly_score={{ site.crs_error_anomaly_score | default(caddy_sites_defaults.crs_error_anomaly_score) }},\
|
||||
setvar:tx.warning_anomaly_score={{ site.crs_warning_anomaly_score | default(caddy_sites_defaults.crs_warning_anomaly_score) }},\
|
||||
setvar:tx.notice_anomaly_score={{ site.crs_notice_anomaly_score | default(caddy_sites_defaults.crs_notice_anomaly_score) }}"
|
||||
|
||||
## Anomaly Scoring Mode Blocking Threshold Levels
|
||||
SecAction \
|
||||
@@ -60,8 +60,8 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_inbound_anomaly_score_threshold={% if site.crs_log_only | default(caddy_sites_defaults.crs_log_only) %}1000{% else %}{{ site.crs_inbound_anomaly_score_threshold | default(caddy_sites_defaults.crs_inbound_anomaly_score_threshold) }}{% endif %},\
|
||||
setvar:tx.crs_outbound_anomaly_score_threshold={% if site.crs_log_only | default(caddy_sites_defaults.crs_log_only) %}1000{% else %}{{ site.crs_outbound_anomaly_score_threshold | default(caddy_sites_defaults.crs_outbound_anomaly_score_threshold) }}{% endif %}"
|
||||
setvar:tx.inbound_anomaly_score_threshold={% if site.crs_log_only | default(caddy_sites_defaults.crs_log_only) %}1000{% else %}{{ site.crs_inbound_anomaly_score_threshold | default(caddy_sites_defaults.crs_inbound_anomaly_score_threshold) }}{% endif %},\
|
||||
setvar:tx.outbound_anomaly_score_threshold={% if site.crs_log_only | default(caddy_sites_defaults.crs_log_only) %}1000{% else %}{{ site.crs_outbound_anomaly_score_threshold | default(caddy_sites_defaults.crs_outbound_anomaly_score_threshold) }}{% endif %}"
|
||||
|
||||
## Application Specific Rule Exclusions
|
||||
# FIXME
|
||||
@@ -88,7 +88,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_reporting_level={{ site.crs_reporting_level | default(caddy_sites_defaults.crs_reporting_level) }}"
|
||||
setvar:tx.reporting_level={{ site.crs_reporting_level | default(caddy_sites_defaults.crs_reporting_level) }}"
|
||||
|
||||
## Early Anomaly Scoring Mode Blocking
|
||||
SecAction \
|
||||
@@ -121,7 +121,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_allowed_methods={{ site.crs_allowed_methods | default(caddy_sites_defaults.crs_allowed_methods) | join(" ") }}'"
|
||||
setvar:'tx.allowed_methods={{ site.crs_allowed_methods | default(caddy_sites_defaults.crs_allowed_methods) | join(" ") }}'"
|
||||
|
||||
## Content-Types that a client is allowed to send in a request
|
||||
SecAction \
|
||||
@@ -132,7 +132,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_allowed_request_content_type={{ site.crs_allowed_request_content_type | default(caddy_sites_defaults.crs_allowed_request_content_type) | join(" ") }}'"
|
||||
setvar:'tx.allowed_request_content_type={{ site.crs_allowed_request_content_type | default(caddy_sites_defaults.crs_allowed_request_content_type) | join(" ") }}'"
|
||||
|
||||
## Allowed HTTP versions
|
||||
SecAction \
|
||||
@@ -143,7 +143,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_allowed_http_versions={{ site.crs_allowed_http_versions | default(caddy_sites_defaults.crs_allowed_http_versions) | join(" ") }}'"
|
||||
setvar:'tx.allowed_http_versions={{ site.crs_allowed_http_versions | default(caddy_sites_defaults.crs_allowed_http_versions) | join(" ") }}'"
|
||||
|
||||
## Forbidden file extensions
|
||||
SecAction \
|
||||
@@ -154,7 +154,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_restricted_extensions={{ site.crs_restricted_extensions | default(caddy_sites_defaults.crs_restricted_extensions) | join(" ") }}'"
|
||||
setvar:'tx.restricted_extensions={{ site.crs_restricted_extensions | default(caddy_sites_defaults.crs_restricted_extensions) | join(" ") }}'"
|
||||
|
||||
## Restricted request headers
|
||||
SecAction \
|
||||
@@ -165,7 +165,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_restricted_headers_basic={{ site.crs_restricted_headers_basic | default(caddy_sites_defaults.crs_restricted_headers_basic) | join(" ") }}'"
|
||||
setvar:'tx.restricted_headers_basic={{ site.crs_restricted_headers_basic | default(caddy_sites_defaults.crs_restricted_headers_basic) | join(" ") }}'"
|
||||
|
||||
## Extended restricted request headers (forbidden at a higher paranoia level)
|
||||
SecAction \
|
||||
@@ -176,7 +176,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_restricted_headers_extended={{ site.crs_restricted_headers_extended | default(caddy_sites_defaults.crs_restricted_headers_extended) | join(" ") }}'"
|
||||
setvar:'tx.restricted_headers_extended={{ site.crs_restricted_headers_extended | default(caddy_sites_defaults.crs_restricted_headers_extended) | join(" ") }}'"
|
||||
|
||||
## Content-Types charsets that a client is allowed to send in a request
|
||||
SecAction \
|
||||
@@ -187,7 +187,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:'tx.crs_allowed_request_content_type_charset={{ caddy_sites_defaults.crs_allowed_request_content_type_charset | default(caddy_sites_defaults.crs_allowed_request_content_type_charset) | join(" ") }}'"
|
||||
setvar:'tx.allowed_request_content_type_charset={{ caddy_sites_defaults.crs_allowed_request_content_type_charset | default(caddy_sites_defaults.crs_allowed_request_content_type_charset) | join(" ") }}'"
|
||||
|
||||
## Block request if number of arguments is too high
|
||||
SecAction \
|
||||
@@ -198,7 +198,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_max_num_args={{ site.crs_max_num_args | default(caddy_sites_defaults.crs_max_num_args) }}"
|
||||
setvar:tx.max_num_args={{ site.crs_max_num_args | default(caddy_sites_defaults.crs_max_num_args) }}"
|
||||
|
||||
## Block request if the length of any argument name is too high
|
||||
SecAction \
|
||||
@@ -209,7 +209,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_arg_name_length={{ site.crs_arg_name_length | default(caddy_sites_defaults.crs_arg_name_length) }}"
|
||||
setvar:tx.name_length={{ site.crs_arg_name_length | default(caddy_sites_defaults.crs_arg_name_length) }}"
|
||||
|
||||
## Block request if the length of any argument value is too high
|
||||
SecAction \
|
||||
@@ -220,7 +220,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_arg_length={{ site.crs_arg_length | default(caddy_sites_defaults.crs_arg_length) }}"
|
||||
setvar:tx.arg_length={{ site.crs_arg_length | default(caddy_sites_defaults.crs_arg_length) }}"
|
||||
|
||||
## Block request if the total length of all combined arguments is too high
|
||||
SecAction \
|
||||
@@ -231,7 +231,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_total_arg_length={{ site.crs_total_arg_length | default(caddy_sites_defaults.crs_total_arg_length) }}"
|
||||
setvar:tx.total_arg_length={{ site.crs_total_arg_length | default(caddy_sites_defaults.crs_total_arg_length) }}"
|
||||
|
||||
## Block request if the file size of any individual uploaded file is too high
|
||||
SecAction \
|
||||
@@ -242,7 +242,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_max_file_size={{ site.crs_max_file_size | default(caddy_sites_defaults.crs_max_file_size) }}"
|
||||
setvar:tx.max_file_size={{ site.crs_max_file_size | default(caddy_sites_defaults.crs_max_file_size) }}"
|
||||
|
||||
## Block request if the total size of all combined uploaded files is too high
|
||||
SecAction \
|
||||
@@ -253,7 +253,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_combined_file_sizes={{ site.crs_combined_file_sizes | default(caddy_sites_defaults.crs_combined_file_sizes) }}"
|
||||
setvar:tx.combined_file_sizes={{ site.crs_combined_file_sizes | default(caddy_sites_defaults.crs_combined_file_sizes) }}"
|
||||
|
||||
## Easing In / Sampling Percentage
|
||||
SecAction \
|
||||
@@ -263,7 +263,7 @@ SecAction \
|
||||
nolog,\
|
||||
tag:'OWASP_CRS',\
|
||||
ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\
|
||||
setvar:tx.crs_sampling_percentage={{ site.crs_sampling_percentage | default(caddy_sites_defaults.crs_sampling_percentage) }}"
|
||||
setvar:tx.sampling_percentage={{ site.crs_sampling_percentage | default(caddy_sites_defaults.crs_sampling_percentage) }}"
|
||||
|
||||
## Check UTF-8 encoding
|
||||
SecAction \
|
||||
|
||||
@@ -21,16 +21,38 @@ caddy_global_sites_defaults:
|
||||
# cookie [<name> [<secret>]]
|
||||
|
||||
|
||||
# Filters
|
||||
filter_blacklist: [] # Superseeds filter_whitelist
|
||||
filter_whitelist: ["127.0.0.1"]
|
||||
|
||||
|
||||
# Custom TLS certificate
|
||||
custom_cert: false
|
||||
custom_cert_file: ""
|
||||
custom_cert_key_file: ""
|
||||
|
||||
|
||||
# Filters
|
||||
filter_blacklist: [] # Superseeds filter_whitelist
|
||||
filter_whitelist: ["127.0.0.1"]
|
||||
|
||||
# CORS - Cross Origin Resource Sharing
|
||||
cors: false
|
||||
cors_allow_origins: null # Mandatory
|
||||
cors_allow_methods:
|
||||
- "GET"
|
||||
- "HEAD"
|
||||
- "POST"
|
||||
- "OPTIONS"
|
||||
cors_allow_headers:
|
||||
- "Cache-Control"
|
||||
- "Content-Type"
|
||||
- "If-Modified-Since"
|
||||
- "Range"
|
||||
- "User-Agent"
|
||||
cors_max_age: 3600
|
||||
cors_allow_credentials: false
|
||||
cors_vary:
|
||||
- "Origin"
|
||||
cors_expose_headers:
|
||||
- "Content-Length"
|
||||
- "Content-Range"
|
||||
|
||||
# Security headers
|
||||
header_content_security_policy:
|
||||
@@ -391,7 +413,3 @@ caddy_global_sites_defaults:
|
||||
geoip: false
|
||||
geoip_debug: false
|
||||
geoip_filter_expression: '{geoip2.country_code} == "UNK"'
|
||||
# geoip_deny_countries: [] # ISO country codes (2 letters), "UNK" for unknown country
|
||||
# geoip_allow_countries: [] # ISO country codes (2 letters), "UNK" for unknown country
|
||||
# geopip_deny_asn: []
|
||||
# geopip_allow_asn: []
|
||||
|
||||
Reference in New Issue
Block a user