From c217cc8d7ca4ea4d75e3215d2aef07112ea93890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=2E=20Nam=C3=A8che?= Date: Fri, 23 Jan 2026 11:20:54 +0300 Subject: [PATCH] caddy: refact variables name, global options, add caddy_my_name --- example/ansible/vars/common.yml | 7 +- seb4itik/byow/roles/caddy/defaults/main.yml | 404 ++---------------- seb4itik/byow/roles/caddy/handlers/main.yml | 2 +- .../byow/roles/caddy/tasks/caddy_config.yml | 21 +- seb4itik/byow/roles/caddy/tasks/main.yml | 2 +- seb4itik/byow/roles/caddy/templates/Caddyfile | 128 +++--- .../byow/roles/caddy/templates/caddy.service | 7 +- .../byow/roles/caddy/templates/crs-setup.conf | 102 ++--- .../templates/exclusions-request-before.conf | 2 +- .../templates/exclusions-response-after.conf | 2 +- seb4itik/byow/roles/caddy/vars/main.yml | 388 ++++++++++++++++- 11 files changed, 560 insertions(+), 505 deletions(-) diff --git a/example/ansible/vars/common.yml b/example/ansible/vars/common.yml index 6337f4c..94e7031 100644 --- a/example/ansible/vars/common.yml +++ b/example/ansible/vars/common.yml @@ -11,8 +11,8 @@ common: datacenter: "lab" sites: - - name: "test" - site: "test.lab" + - id: "test" + name: "test.lab" paths: - addrs: - "http://www1" @@ -20,7 +20,8 @@ common: - "http://www3" custom_cert: true custom_cert_file: "/etc/ssl/certs/test.lab.crt" - custom_key_file: "/etc/ssl/private/test.lab.key" + custom_cert_key_file: "/etc/ssl/private/test.lab.key" + filter_whitelist: ["10.60.117.1"] crs_plugins: - "wordpress-rule-exclusions" crs_exceptions: diff --git a/seb4itik/byow/roles/caddy/defaults/main.yml b/seb4itik/byow/roles/caddy/defaults/main.yml index b34ae79..e42365f 100644 --- a/seb4itik/byow/roles/caddy/defaults/main.yml +++ b/seb4itik/byow/roles/caddy/defaults/main.yml @@ -1,16 +1,28 @@ # Defaults caddy_config_only: false caddy_debug: false -caddy_go_version: "1.25.5" +caddy_http_port: 80 +caddy_https_port: 443 +caddy_default_bind: "" + +caddy_version: "2.10.2" +caddy_go_version: "1.25.6" caddy_xcaddy_version: "0.4.5" -caddy_caddy_version: "2.10.2" caddy_coraza_caddy_version: "2.1.0" # Coraza v3.3.3 -caddy_owasp_crs_version: "4.22.0" -caddy_system_user: "byow" -caddy_system_group: "byow" -caddy_home_dir: "/opt/{{ caddy_system_user }}" -caddy_config_dir: "/etc/{{ caddy_system_user }}" -caddy_log_dir: "/var/log/{{ caddy_system_user }}" +caddy_owasp_crs_versions: ["4.22.0"] + +caddy_my_name: "mywaf" +caddy_system_user: "{{ caddy_my_name }}" +caddy_system_group: "{{ caddy_my_name }}" +caddy_home_dir: "/opt/{{ caddy_my_name }}" +caddy_config_dir: "/etc/{{ caddy_my_name }}" +caddy_log_dir: "/var/log/{{ caddy_my_name }}" + +# Required for Let's Encrypt auto certificates +caddy_email: null + +# If a custom CA root have to be used +caddy_ca_root: null # Cf.: https://github.com/coreruleset/plugin-registry caddy_crs_plugins: @@ -32,378 +44,16 @@ caddy_crs_plugins: version: "1.0.4" provider: "EsadCetiner" -# Required unless not site.custom_cert -caddy_email: null - -# Required if site.bot_barrier -caddy_bot_barrier_secret: null - -# Optionnal -caddy_local_ca_name: null -caddy_ca_root: null +# For each site, mandatories parameters caddy_sites: [] -# - name: "" -# site: "" +# - id: "" +# name: "" # paths: -# - path: "*" -# addrs: +# - path: "" (default "*") +# addrs: (minimum 1 required) # - "" -# blacklist: -# - "" -# aliases: -# - "" -# crs_exceptions: -# before_request: -# - "" -# after_response: -# - "" -caddy_waf_defaults: - redirect_from_aliases: true - custom_cert: false - custom_cert_file: "" - custom_key_file: "" - # Security headers - header_content_security_policy: - - # https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#detailed-csp-directives - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#directives - ## Fetch directives - child-src: "'self'" - connect-src: "'self'" - font-src: "'self'" - img-src: "'self'" - manifest-src: "'self'" - media-src: "'self'" - # Deprecated: prefetch-src: "'self'" - object-src: "'none'" - script-src: "'none'" - script-src-attr: "'none'" - script-src-elem: "'none'" - style-src: "'self'" - style-src-attr: "'self'" - style-src-elem: "'self'" - default-src: "'self'" - ## Document directives - # base-uri - # plugin-types - # sandbox - ## Navigation directives - form-action: "'self'" - frame-ancestors: "'self'" - # https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-src - frame-src: "'self'" - # https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/worker-src - worker-src: "'self'" - # https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/block-all-mixed-content - block-all-mixed-content: - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/fenced-frame-src - fenced-frame-src: "'self'" - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/upgrade-insecure-requests - upgrade-insecure-requests: - - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy - header_cross_origin_embedder_policy: "require-corp" - - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy - header_cross_origin_opener_policy: "same-origin" - - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Resource-Policy - header_cross_origin_resource_policy: "same-site" - - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy - header_permissions_policy: - accelerometer: "()" - ambient-light-sensor: "()" - aria-notify: "()" - attribution-reporting: "()" - autoplay: "()" - bluetooth: "()" - browsing-topics: "()" - camera: "()" - captured-surface-control: "()" - compute-pressure: "()" - cross-origin-isolated: "()" - deferred-fetch: "()" - deferred-fetch-minimal: "()" - display-capture: "()" - encrypted-media: "()" - fullscreen: "()" - gamepad: "()" - geolocation: "()" - gyroscope: "()" - hid: "()" - identity-credentials-get: "()" - idle-detection: "()" - language-detector: "()" - local-fonts: "()" - magnetometer: "()" - microphone: "()" - midi: "()" - on-device-speech-recognition: "()" - otp-credentials: "()" - payment: "()" - picture-in-picture: "()" - private-state-token-issuance: "()" - private-state-token-redemption: "()" - publickey-credentials-create: "()" - publickey-credentials-get: "()" - screen-wake-lock: "()" - serial: "()" - speaker-selection: "()" - storage-access: "()" - translator: "()" - summarizer: "()" - usb: "()" - web-share: "()" - window-management: "()" - xr-spatial-tracking: "()" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#floc-federated-learning-of-cohorts - interest-cohort: "()" - - # ??? - battery: "()" - document-domain: "()" - execution-while-not-rendered: "()" - execution-while-out-of-viewport: "()" - navigation-override: "()" - - # https://www.geeksforgeeks.org/computer-networks/http-headers-feature-policy/ - header_feature_policy: - accelerometer: "'none'" - ambient-light-sensor: "'none'" - autoplay: "'none'" - battery: "'none'" - camera: "'none'" - display-capture: "'none'" - document-domain: "'none'" - encrypted-media: "'none'" - fullscreen: "'none'" - geolocation: "'none'" - gyroscope: "'none'" - layout-animation: "'none'" - legacy-image-formats: "'none'" - magnetometer: "'none'" - microphone: "'none'" - midi: "'none'" - oversized-images: "'none'" - payment: "'none'" - picture-in-picture: "'none'" - publickey-credentials-get: "'none'" - sync-xhr: "'none'" - usb: "'none'" - screen-wake-lock: "'none'" - xr-spatial-tracking: "'none'" - - # ??? - execution-while-not-rendered: "'none'" - execution-while-out-of-viewport: "'none'" - navigation-override: "'none'" - speaker-selection: "'none'" - unoptimized-images: "'none'" - unsized-media: "'none'" - web-share: "'none'" - - # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy - header_referrer_policy: "strict-origin-when-cross-origin" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html - header_strict_transport_security: "max-age=2635200; includeSubDomains" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options - header_x_content_type_options: "nosniff" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options - header_x_frame_options: "DENY" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-robots-tag - header_x_robots_tag: "noindex, nofollow" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#content-type - default_content_type: "text/html; charset=UTF-8" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#set-cookie - cookies_attributes: "SameSite=None; Secure;" - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-dns-prefetch-control - # FIXME - - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#server - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-aspnet-version - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-aspnetmvc-version - # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-powered-by - delete_headers: - - "Server" - - "Via" - - "X-AspNet-Version" - - "X-AspNetMvc-Version" - - "X-Powered-By" - - custom_headers: {} -# - name: -# value: - - # caddy-ratelimit - rate_events: 1000 - rate_window: "1m" - - # steffenbusch/caddy-bot-barrier - bot_barrier: false - - # corazawaf/coraza-caddy - enable_crs: true - crs_plugins: [] - log_only: false - paranoia_level: 1 - detection_paranoia_level: null - enforce_bodyproc_urlencoded: 1 - critical_anomaly_score: 5 - error_anomaly_score: 4 - warning_anomaly_score: 3 - notice_anomaly_score: 2 - inbound_anomaly_score_threshold: 5 - outbound_anomaly_score_threshold: 4 - reporting_level: 4 - max_num_args: 255 - arg_name_length: 100 - arg_length: 1000 - total_arg_length: 64000 - max_file_size: 10485760 # 10 Mo - combined_file_sizes: 20970520 # 20 Mo - sampling_percentage: 100 - allowed_methods: - - "GET" - - "HEAD" - - "POST" - - "OPTIONS" - allowed_request_content_type: - - "|application/csp-report|" - - "|application/json|" - - "|application/reports+json|" - - "|application/soap+xml|" - - "|application/x-www-form-urlencoded|" - - "|application/xml|" - - "|multipart/form-data|" - - "|text/xml|" - restricted_extensions: - - ".ani/" - - ".asa/" - - ".asax/" - - ".ascx/" - - ".back/" - - ".backup/" - - ".bak/" - - ".bat/" - - ".bck/" - - ".bk/" - - ".bkp/" - - ".cdx/" - - ".cer/" - - ".cfg/" - - ".cmd/" - - ".cnf/" - - ".com/" - - ".compositefont/" - - ".conf/" - - ".config/" - - ".copy/" - - ".crt/" - - ".cs/" - - ".csproj/" - - ".csr/" - - ".dat/" - - ".db/" - - ".dbf/" - - ".dist/" - - ".dll/" - - ".dos/" - - ".dpkg-dist/" - - ".drv/" - - ".gadget/" - - ".hta/" - - ".htr/" - - ".htw/" - - ".ida/" - - ".idc/" - - ".idq/" - - ".inc/" - - ".inf/" - - ".ini/" - - ".jse/" - - ".key/" - - ".licx/" - - ".lnk/" - - ".log/" - - ".mdb/" - - ".msc/" - - ".ocx/" - - ".old/" - - ".pass/" - - ".pdb/" - - ".pem/" - - ".pfx/" - - ".pif/" - - ".pol/" - - ".prf/" - - ".printer/" - - ".pwd/" - - ".rdb/" - - ".rdp/" - - ".reg/" - - ".resources/" - - ".resx/" - - ".sav/" - - ".save/" - - ".scr/" - - ".sct/" - - ".sh/" - - ".shs/" - - ".sql/" - - ".sqlite/" - - ".sqlite3/" - - ".swp/" - - ".sys/" - - ".temp/" - - ".tlb/" - - ".tmp/" - - ".vb/" - - ".vbe/" - - ".vbproj/" - - ".vbs/" - - ".vsdisco/" - - ".vxd/" - - ".webinfo/" - - ".ws/" - - ".wsc/" - - ".wsf/" - - ".wsh/" - - ".xsd/" - - ".xsx/" - restricted_headers_basic: - - "/content-encoding/" - - "/content-range/" - - "/expect/" - - "/if/" - - "/lock-token/" - - "/proxy/" - - "/x-http-method-override/" - - "/x-http-method/" - - "/x-method-override/" - - "/x-middleware-subrequest/" - restricted_headers_extended: - - "/accept-charset/" - allowed_http_versions: - - "HTTP/1.0" - - "HTTP/1.1" - - "HTTP/2" - - "HTTP/2.0" - - "HTTP/3" - - "HTTP/3.0" - allowed_request_content_type_charset: - - "|utf-8|" - - "|iso-8859-1|" - - "|iso-8859-15|" - - "|windows-1252|" +# Will be merged with caddy_global_sites_defaults +caddy_my_sites_defaults: {} diff --git a/seb4itik/byow/roles/caddy/handlers/main.yml b/seb4itik/byow/roles/caddy/handlers/main.yml index 1ddbd4b..f4ec056 100644 --- a/seb4itik/byow/roles/caddy/handlers/main.yml +++ b/seb4itik/byow/roles/caddy/handlers/main.yml @@ -1,4 +1,4 @@ - name: Restart Caddy ansible.builtin.service: - name: "{{ caddy_system_user }}" + name: "{{ caddy_my_name }}" state: "restarted" diff --git a/seb4itik/byow/roles/caddy/tasks/caddy_config.yml b/seb4itik/byow/roles/caddy/tasks/caddy_config.yml index e4179c3..13f359c 100644 --- a/seb4itik/byow/roles/caddy/tasks/caddy_config.yml +++ b/seb4itik/byow/roles/caddy/tasks/caddy_config.yml @@ -1,7 +1,11 @@ +- name: Set sites defaults + ansible.builtin.set_fact: + caddy_sites_defaults: "{{ caddy_global_sites_defaults | combine(caddy_my_sites_defaults) }}" + - name: Create sites config directories loop: "{{ caddy_sites }}" ansible.builtin.file: - path: "{{ caddy_config_dir }}/sites/{{ item.name }}" + path: "{{ caddy_config_dir }}/sites/{{ item.id }}" state: "directory" group: "{{ caddy_system_group }}" mode: "0750" @@ -12,7 +16,7 @@ loop_var: "site" ansible.builtin.template: src: "templates/coraza.conf" - dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/coraza.conf" + dest: "{{ caddy_config_dir }}/sites/{{ site.id }}/coraza.conf" group: "{{ caddy_system_group }}" mode: "0640" notify: @@ -24,7 +28,7 @@ loop_var: "site" ansible.builtin.template: src: "templates/crs-setup.conf" - dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/crs-setup.conf" + dest: "{{ caddy_config_dir }}/sites/{{ site.id }}/crs-setup.conf" group: "{{ caddy_system_group }}" mode: "0640" notify: @@ -36,7 +40,7 @@ loop_var: "site" ansible.builtin.template: src: "templates/exclusions-request-before.conf" - dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-request-before.conf" + dest: "{{ caddy_config_dir }}/sites/{{ site.id }}/exclusions-request-before.conf" group: "{{ caddy_system_group }}" mode: "0640" notify: @@ -48,7 +52,7 @@ loop_var: "site" ansible.builtin.template: src: "templates/exclusions-response-after.conf" - dest: "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-response-after.conf" + dest: "{{ caddy_config_dir }}/sites/{{ site.id }}/exclusions-response-after.conf" group: "{{ caddy_system_group }}" mode: "0640" notify: @@ -71,3 +75,10 @@ mode: "0640" notify: - "Restart Caddy" + +- name: Enable and start Caddy + ansible.builtin.service: + name: "{{ caddy_my_name }}" + state: "started" + enabled: true + daemon_reload: true diff --git a/seb4itik/byow/roles/caddy/tasks/main.yml b/seb4itik/byow/roles/caddy/tasks/main.yml index 8c94a87..06cf576 100644 --- a/seb4itik/byow/roles/caddy/tasks/main.yml +++ b/seb4itik/byow/roles/caddy/tasks/main.yml @@ -2,5 +2,5 @@ when: "not(caddy_config_only)" ansible.builtin.include_tasks: "caddy_install.yml" -- name: Config Caddy +- name: Configure Caddy ansible.builtin.include_tasks: "caddy_config.yml" diff --git a/seb4itik/byow/roles/caddy/templates/Caddyfile b/seb4itik/byow/roles/caddy/templates/Caddyfile index c47cf6f..90ff8cd 100644 --- a/seb4itik/byow/roles/caddy/templates/Caddyfile +++ b/seb4itik/byow/roles/caddy/templates/Caddyfile @@ -1,6 +1,11 @@ { - grace_period 10s + http_port {{ caddy_http_port }} + https_port {{ caddy_https_port }} + default_bind {{ caddy_default_bind }} order coraza_waf first + admin off + persist_config off + {% if caddy_email | default(None) != None %} email "{{ caddy_email }}" {% endif %} @@ -9,43 +14,38 @@ tls { ca_root "{{ caddy_ca_root }}" } - {% endif %} + {% if caddy_debug %} log { format json level DEBUG output file {{ caddy_log_dir }}/debug.log } - {% endif %} + log waf { format json include "http.handlers.waf" output file {{ caddy_log_dir }}/waf.log } -{% if caddy_local_ca_name | default(None) != None %} - - pki { - ca local { - name "{{ caddy_local_ca_name }}" - } - } -{% endif %} } {% for site in caddy_sites %} -{% if (site.redirect_from_aliases | default(true)) and (site.aliases | default([]) | length) > 0 %} -{{ site.aliases | join(', ') }} { - redir https://{{ site.site }}{uri} + +{% if (site.site_redirect_from_aliases | default(caddy_sites_defaults.site_redirect_from_aliases)) and (site.site_aliases | default([]) | length) > 0 %} + +{{ site.site_aliases | join(', ') }} { + redir https://{{ site.name }}{uri} } {% else %} -{% for alias in site.aliases | default([]) %} + +{% for alias in site.site_aliases | default(caddy_sites_defaults.site_aliases) %} {{ alias }}, {% endfor %} {% endif %} -{{ site.site }} { +{{ site.name }} { encode gzip zstd {% for path in site.paths %} @@ -53,7 +53,7 @@ reverse_proxy { to {{ path.addrs | join(' ') }} header_up X-Real-IP {remote} - lb_policy ip_hash + lb_policy {{ site.proxy_lb_policy | default(caddy_sites_defaults.proxy_lb_policy) }} # Active checks # health_uri / # health_interval 10s @@ -72,53 +72,61 @@ {% endfor %} {% if site.custom_cert | default (false) %} - tls {{ site.custom_cert_file }} {{ site.custom_key_file }} - + tls {{ site.custom_cert_file }} {{ site.custom_cert_key_file }} {% endif %} -{% if site.blacklist | default([]) | length > 0 %} + +{% if site.filter_blacklist | default(caddy_sites_defaults.filter_blacklist) | length > 0 %} @blocked { -{% for ip in site.blacklist | default([]) %} +{% for ip in site.filter_blacklist | default(caddy_sites_defaults.filter_blacklist) %} remote_ip {{ ip }} {% endfor %} } respond @blocked "Access Denied" 403 - {% endif %} -{% if site.bot_barrier | default (false) %} - bot_barrier { - secret {{ caddy_bot_barrier_secret }} - 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 {{ caddy_config_dir }}/bot_barrier_template.html + + @not_whitelisted not { +{% for ip in site.filter_whitelist | default(caddy_sites_defaults.filter_whitelist) %} + remote_ip {{ ip }} +{% 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 %} - rate_limit { + +{% if site.rate_limit | default(caddy_sites_defaults.rate_limit) %} + rate_limit @not_whitelisted { # distributed zone remote_ip { key {remote.ip} - events {{ site.rate_events | default('1000') }} - window {{ site.rate_window | default('1m') }} + events {{ site.rate_limit_events | default(caddy_sites_defaults.rate_limit_events) }} + window {{ site.rate_limit_window | default(caddy_sites_defaults.rate_limit_window) }} } } -{% if site.enable_crs | default(caddy_waf_defaults.enable_crs) %} +{% endif %} - coraza_waf { +{% if site.crs | default(caddy_sites_defaults.crs) %} + coraza_waf @not_whitelisted { load_owasp_crs directives ` - Include "{{ caddy_config_dir }}/sites/{{ site.name }}/coraza.conf" - Include "{{ caddy_config_dir }}/sites/{{ site.name }}/crs-setup.conf" -{% for plugin in site.crs_plugins | default(caddy_waf_defaults.crs_plugins) %} + Include "{{ caddy_config_dir }}/sites/{{ site.id }}/coraza.conf" + Include "{{ caddy_config_dir }}/sites/{{ site.id }}/crs-setup.conf" +{% for plugin in site.crs_plugins | default(caddy_sites_defaults.crs_plugins) %} Include "{{ caddy_config_dir }}/crs-plugins/{{ plugin }}-config.conf" Include "{{ caddy_config_dir }}/crs-plugins/{{ plugin }}-before.conf" {% endfor %} - Include "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-request-before.conf" - Include "{{ caddy_config_dir }}/coreruleset-{{ caddy_owasp_crs_version }}/rules/*.conf" - Include "{{ caddy_config_dir }}/sites/{{ site.name }}/exclusions-response-after.conf" -{% for plugin in site.crs_plugins | default(caddy_waf_defaults.crs_plugins) %} + Include "{{ caddy_config_dir }}/sites/{{ site.id }}/exclusions-request-before.conf" + Include "{{ caddy_config_dir }}/coreruleset-{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}/rules/*.conf" + Include "{{ caddy_config_dir }}/sites/{{ site.id }}/exclusions-response-after.conf" +{% for plugin in site.crs_plugins | default(caddy_sites_defaults.crs_plugins) %} Include "{{ caddy_config_dir }}/crs-plugins/{{ plugin }}-after.conf" {% endfor %} SecRuleEngine On @@ -127,35 +135,35 @@ {% endif %} header { - Content-Security-Policy "{{ caddy_waf_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_waf_defaults.header_cross_origin_embedder_policy) }}" - Cross-Origin-Opener-Policy "{{ site.header_cross_origin_opener_policy | default(caddy_waf_defaults.header_cross_origin_opener_policy) }}" - Cross-Origin-Resource-Policy "{{ site.header_cross_origin_resource_policy | default(caddy_waf_defaults.header_cross_origin_resource_policy) }}" - Feature-Policy "{{ caddy_waf_defaults.header_feature_policy | combine(site.header_feature_policy | default({})) | dict2str(sep1=' ', sep2='; ', sep3=';') }}" - Permissions-Policy "{{ caddy_waf_defaults.header_permissions_policy | combine(site.header_permissions_policy | default({})) | dict2str(sep2=', ') }}" - Referrer-Policy "{{ site.header_referrer_policy | default(caddy_waf_defaults.header_referrer_policy) }}" - Strict-Transport-Security "{{ site.header_strict_transport_security | default(caddy_waf_defaults.header_strict_transport_security) }}" - X-Content-Type-Options "{{ site.header_x_content_type_options | default(caddy_waf_defaults.header_x_content_type_options) }}" - X-Frame-Options "{{ site.header_x_frame_options | default(caddy_waf_defaults.header_x_frame_options) }}" - X-Robots-Tag "{{ site.header_x_robots_tag | default(caddy_waf_defaults.header_x_robots_tag) }}" - ?Content-Type "{{ site.default_content_type | default(caddy_waf_defaults.default_content_type) }}" - >Set-Cookie "(.*)" "$1; {{ site.cookies_attributes | default(caddy_waf_defaults.cookies_attributes) }}" -{% for header in site.delete_headers | default(caddy_waf_defaults.delete_headers) %} + 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) }}" + 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) }}" + Strict-Transport-Security "{{ site.header_strict_transport_security | default(caddy_sites_defaults.header_strict_transport_security) }}" + X-Content-Type-Options "{{ site.header_x_content_type_options | default(caddy_sites_defaults.header_x_content_type_options) }}" + X-Frame-Options "{{ site.header_x_frame_options | default(caddy_sites_defaults.header_x_frame_options) }}" + X-Robots-Tag "{{ site.header_x_robots_tag | default(caddy_sites_defaults.header_x_robots_tag) }}" + ?Content-Type "{{ site.header_default_content_type | default(caddy_sites_defaults.header_default_content_type) }}" + >Set-Cookie "(.*)" "$1; {{ site.header_cookies_attributes | default(caddy_sites_defaults.header_cookies_attributes) }}" +{% for header in site.header_delete | default(caddy_sites_defaults.header_delete) %} -{{ header }} {% endfor %} -{% for header in site.custom_headers | default(caddy_waf_defaults.custom_headers) %} +{% for header in site.header_add | default(caddy_sites_defaults.header_add) %} {{ header.name }} "{{ header.value }}" {% endfor %} } handle_errors 403 { header X-Blocked "true" - respond "Your request was blocked." + respond "Your request was blocked. 🔴" } log { format json - output file {{ caddy_log_dir }}/site_{{ site.name }}.log + output file {{ caddy_log_dir }}/site_{{ site.id }}.log } } diff --git a/seb4itik/byow/roles/caddy/templates/caddy.service b/seb4itik/byow/roles/caddy/templates/caddy.service index 62ea5ea..0f90130 100644 --- a/seb4itik/byow/roles/caddy/templates/caddy.service +++ b/seb4itik/byow/roles/caddy/templates/caddy.service @@ -1,6 +1,5 @@ [Unit] -Description=Caddy -Documentation=https://caddyserver.com/docs/ +Description=WAF {{ caddy_my_name }} (built with BYOW: Build Your Own WAF) After=network.target network-online.target Requires=network-online.target @@ -8,8 +7,8 @@ Requires=network-online.target Type=notify User={{ caddy_system_user }} Group={{ caddy_system_group }} -ExecStart=/usr/local/bin/caddy run --environ --config {{ caddy_config_dir }}/Caddyfile -ExecReload=/usr/local/bin/caddy reload --config {{ caddy_config_dir }}/Caddyfile --force +ExecStart=/usr/local/bin/{{ caddy_my_name }} run --environ --config {{ caddy_config_dir }}/Caddyfile +ExecReload=/usr/local/bin/{{ caddy_my_name }} reload --config {{ caddy_config_dir }}/Caddyfile --force TimeoutStopSec=15s LimitNOFILE=1048576 PrivateTmp=true diff --git a/seb4itik/byow/roles/caddy/templates/crs-setup.conf b/seb4itik/byow/roles/caddy/templates/crs-setup.conf index 569865f..4e3c108 100644 --- a/seb4itik/byow/roles/caddy/templates/crs-setup.conf +++ b/seb4itik/byow/roles/caddy/templates/crs-setup.conf @@ -10,9 +10,9 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.blocking_paranoia_level={{ site.paranoia_level | default(caddy_waf_defaults.paranoia_level) }}" -{% if site.detection_paranoia_level | default(caddy_waf_defaults.detection_paranoia_level) != None %} + 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) }}" +{% if site.crs_detection_paranoia_level | default(caddy_sites_defaults.crs_detection_paranoia_level) != None %} ## Detection paranoia level SecAction \ @@ -22,8 +22,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.detection_paranoia_level={{ site.detection_paranoia_level | default(caddy_waf_defaults.detection_paranoia_level) }}" + 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) }}" {% endif %} ## Enforce Body Processor URLENCODED @@ -34,8 +34,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.enforce_bodyproc_urlencoded={{ site.enforce_bodyproc_urlencoded | default(caddy_waf_defaults.enforce_bodyproc_urlencoded) }}" + 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) }}" ## Anomaly Scoring Mode Severity Levels SecAction \ @@ -45,11 +45,11 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.critical_anomaly_score={{ site.critical_anomaly_score | default(caddy_waf_defaults.critical_anomaly_score) }},\ - setvar:tx.error_anomaly_score={{ site.error_anomaly_score | default(caddy_waf_defaults.error_anomaly_score) }},\ - setvar:tx.warning_anomaly_score={{ site.warning_anomaly_score | default(caddy_waf_defaults.warning_anomaly_score) }},\ - setvar:tx.notice_anomaly_score={{ site.notice_anomaly_score | default(caddy_waf_defaults.notice_anomaly_score) }}" + 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) }}" ## Anomaly Scoring Mode Blocking Threshold Levels SecAction \ @@ -59,9 +59,9 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.inbound_anomaly_score_threshold={% if site.log_only | default(caddy_waf_defaults.log_only) %}1000{% else %}{{ site.inbound_anomaly_score_threshold | default(caddy_waf_defaults.inbound_anomaly_score_threshold) }}{% endif %},\ - setvar:tx.outbound_anomaly_score_threshold={% if site.log_only | default(caddy_waf_defaults.log_only) %}1000{% else %}{{ site.outbound_anomaly_score_threshold | default(caddy_waf_defaults.outbound_anomaly_score_threshold) }}{% endif %}" + 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 %}" ## Application Specific Rule Exclusions # FIXME @@ -87,8 +87,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.reporting_level={{ site.reporting_level | default(caddy_waf_defaults.reporting_level) }}" + 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) }}" ## Early Anomaly Scoring Mode Blocking SecAction \ @@ -98,7 +98,7 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ + ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\ setvar:tx.early_blocking=0" ## Initialize Default Collections @@ -109,7 +109,7 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ + ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\ setvar:tx.enable_default_collections=1" ## HTTP Policy Settings @@ -120,8 +120,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.allowed_methods={{ site.allowed_methods | default(caddy_waf_defaults.allowed_methods) | join(" ") }}'" + 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(" ") }}'" ## Content-Types that a client is allowed to send in a request SecAction \ @@ -131,8 +131,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.allowed_request_content_type={{ site.allowed_request_content_type | default(caddy_waf_defaults.allowed_request_content_type) | join(" ") }}'" + 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(" ") }}'" ## Allowed HTTP versions SecAction \ @@ -142,8 +142,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.allowed_http_versions={{ site.allowed_http_versions | default(caddy_waf_defaults.allowed_http_versions) | join(" ") }}'" + 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(" ") }}'" ## Forbidden file extensions SecAction \ @@ -153,8 +153,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.restricted_extensions={{ site.restricted_extensions | default(caddy_waf_defaults.restricted_extensions) | join(" ") }}'" + 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(" ") }}'" ## Restricted request headers SecAction \ @@ -164,8 +164,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.restricted_headers_basic={{ site.restricted_headers_basic | default(caddy_waf_defaults.restricted_headers_basic) | join(" ") }}'" + 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(" ") }}'" ## Extended restricted request headers (forbidden at a higher paranoia level) SecAction \ @@ -175,8 +175,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.restricted_headers_extended={{ site.restricted_headers_extended | default(caddy_waf_defaults.restricted_headers_extended) | join(" ") }}'" + 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(" ") }}'" ## Content-Types charsets that a client is allowed to send in a request SecAction \ @@ -186,8 +186,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:'tx.allowed_request_content_type_charset={{ caddy_waf_defaults.allowed_request_content_type_charset | default(caddy_waf_defaults.allowed_request_content_type_charset) | join(" ") }}'" + 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(" ") }}'" ## Block request if number of arguments is too high SecAction \ @@ -197,8 +197,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.max_num_args={{ site.max_num_args | default(caddy_waf_defaults.max_num_args) }}" + 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) }}" ## Block request if the length of any argument name is too high SecAction \ @@ -208,8 +208,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.arg_name_length={{ site.arg_name_length | default(caddy_waf_defaults.arg_name_length) }}" + 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) }}" ## Block request if the length of any argument value is too high SecAction \ @@ -219,8 +219,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.arg_length={{ site.arg_length | default(caddy_waf_defaults.arg_length) }}" + 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) }}" ## Block request if the total length of all combined arguments is too high SecAction \ @@ -230,8 +230,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.total_arg_length={{ site.total_arg_length | default(caddy_waf_defaults.total_arg_length) }}" + 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) }}" ## Block request if the file size of any individual uploaded file is too high SecAction \ @@ -241,8 +241,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.max_file_size={{ site.max_file_size | default(caddy_waf_defaults.max_file_size) }}" + 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) }}" ## Block request if the total size of all combined uploaded files is too high SecAction \ @@ -252,8 +252,8 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.combined_file_sizes={{ site.combined_file_sizes | default(caddy_waf_defaults.combined_file_sizes) }}" + 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) }}" ## Easing In / Sampling Percentage SecAction \ @@ -262,8 +262,8 @@ SecAction \ pass,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.sampling_percentage={{ site.sampling_percentage | default(caddy_waf_defaults.sampling_percentage) }}" + 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) }}" ## Check UTF-8 encoding SecAction \ @@ -273,7 +273,7 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ + ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\ setvar:tx.crs_validate_utf8_encoding=1" ## Skip Checking Responses @@ -290,7 +290,7 @@ SecAction \ # t:none,\ # nolog,\ # tag:'OWASP_CRS',\ -# ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ +# ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\ # setvar:tx.crs_skip_response_analysis=1" ## End of setup @@ -301,5 +301,5 @@ SecAction \ t:none,\ nolog,\ tag:'OWASP_CRS',\ - ver:'OWASP_CRS/{{ caddy_owasp_crs_version }}',\ - setvar:tx.crs_setup_version={{ caddy_owasp_crs_version | regex_replace('\.', '') }}" + ver:'OWASP_CRS/{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}',\ + setvar:tx.crs_setup_version={{ site.crs_version | default(caddy_sites_defaults.crs_version) | regex_replace('\.', '') }}" diff --git a/seb4itik/byow/roles/caddy/templates/exclusions-request-before.conf b/seb4itik/byow/roles/caddy/templates/exclusions-request-before.conf index 22a033f..12decd8 100644 --- a/seb4itik/byow/roles/caddy/templates/exclusions-request-before.conf +++ b/seb4itik/byow/roles/caddy/templates/exclusions-request-before.conf @@ -1,3 +1,3 @@ -{% for exclusion in site.crs_exceptions.before_request | default([]) %} +{% for exclusion in site.crs_exceptions.before_request | default(caddy_sites_defaults.crs_exceptions.before_request) %} {{ exclusion }} {% endfor %} diff --git a/seb4itik/byow/roles/caddy/templates/exclusions-response-after.conf b/seb4itik/byow/roles/caddy/templates/exclusions-response-after.conf index c376d77..d781647 100644 --- a/seb4itik/byow/roles/caddy/templates/exclusions-response-after.conf +++ b/seb4itik/byow/roles/caddy/templates/exclusions-response-after.conf @@ -1,3 +1,3 @@ -{% for exclusion in site.crs_exceptions.after_response | default([]) %} +{% for exclusion in site.crs_exceptions.after_response | default(caddy_sites_defaults.crs_exceptions.after_response) %} {{ exclusion }} {% endfor %} diff --git a/seb4itik/byow/roles/caddy/vars/main.yml b/seb4itik/byow/roles/caddy/vars/main.yml index a8b2875..dd7d61d 100644 --- a/seb4itik/byow/roles/caddy/vars/main.yml +++ b/seb4itik/byow/roles/caddy/vars/main.yml @@ -1 +1,387 @@ -caddy_xcaddy_download: "https://github.com/caddyserver/xcaddy/releases/download" +# Sites default parameter values +caddy_global_sites_defaults: + + # Site settings + site_aliases: [] + site_redirect_from_aliases: true + + + # Proxy settings + proxy_lb_policy: "ip_hash" # random + # random_choose + # first + # round_robin + # weighted_round_robin + # least_conn + # ip_hash + # client_ip_hash + # uri_hash + # query [key] + # header [field] + # cookie [ []] + + + # Custom TLS certificate + custom_cert: false + custom_cert_file: "" + custom_cert_key_file: "" + + + # Filters + filter_whitelist: ["127.0.0.1"] + filter_blacklist: [] # Superseeds filter_whitelist + + + # Security headers + header_content_security_policy: + + # https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html#detailed-csp-directives + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#directives + ## Fetch directives + child-src: "'self'" + connect-src: "'self'" + font-src: "'self'" + img-src: "'self'" + manifest-src: "'self'" + media-src: "'self'" + # Deprecated: prefetch-src: "'self'" + object-src: "'none'" + script-src: "'none'" + script-src-attr: "'none'" + script-src-elem: "'none'" + style-src: "'self'" + style-src-attr: "'self'" + style-src-elem: "'self'" + default-src: "'self'" + ## Document directives + # base-uri + # plugin-types + # sandbox + ## Navigation directives + form-action: "'self'" + frame-ancestors: "'self'" + # https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/frame-src + frame-src: "'self'" + # https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/worker-src + worker-src: "'self'" + # https://developer.mozilla.org/fr/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/block-all-mixed-content + block-all-mixed-content: + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/fenced-frame-src + fenced-frame-src: "'self'" + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/upgrade-insecure-requests + upgrade-insecure-requests: + + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy + header_cross_origin_embedder_policy: "require-corp" + + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy + header_cross_origin_opener_policy: "same-origin" + + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Resource-Policy + header_cross_origin_resource_policy: "same-site" + + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Permissions-Policy + header_permissions_policy: + accelerometer: "()" + ambient-light-sensor: "()" + aria-notify: "()" + attribution-reporting: "()" + autoplay: "()" + bluetooth: "()" + browsing-topics: "()" + camera: "()" + captured-surface-control: "()" + compute-pressure: "()" + cross-origin-isolated: "()" + deferred-fetch: "()" + deferred-fetch-minimal: "()" + display-capture: "()" + encrypted-media: "()" + fullscreen: "()" + gamepad: "()" + geolocation: "()" + gyroscope: "()" + hid: "()" + identity-credentials-get: "()" + idle-detection: "()" + language-detector: "()" + local-fonts: "()" + magnetometer: "()" + microphone: "()" + midi: "()" + on-device-speech-recognition: "()" + otp-credentials: "()" + payment: "()" + picture-in-picture: "()" + private-state-token-issuance: "()" + private-state-token-redemption: "()" + publickey-credentials-create: "()" + publickey-credentials-get: "()" + screen-wake-lock: "()" + serial: "()" + speaker-selection: "()" + storage-access: "()" + translator: "()" + summarizer: "()" + usb: "()" + web-share: "()" + window-management: "()" + xr-spatial-tracking: "()" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#floc-federated-learning-of-cohorts + interest-cohort: "()" + + # ??? + battery: "()" + document-domain: "()" + execution-while-not-rendered: "()" + execution-while-out-of-viewport: "()" + navigation-override: "()" + + # https://www.geeksforgeeks.org/computer-networks/http-headers-feature-policy/ + header_feature_policy: + accelerometer: "'none'" + ambient-light-sensor: "'none'" + autoplay: "'none'" + battery: "'none'" + camera: "'none'" + display-capture: "'none'" + document-domain: "'none'" + encrypted-media: "'none'" + fullscreen: "'none'" + geolocation: "'none'" + gyroscope: "'none'" + layout-animation: "'none'" + legacy-image-formats: "'none'" + magnetometer: "'none'" + microphone: "'none'" + midi: "'none'" + oversized-images: "'none'" + payment: "'none'" + picture-in-picture: "'none'" + publickey-credentials-get: "'none'" + sync-xhr: "'none'" + usb: "'none'" + screen-wake-lock: "'none'" + xr-spatial-tracking: "'none'" + + # ??? + execution-while-not-rendered: "'none'" + execution-while-out-of-viewport: "'none'" + navigation-override: "'none'" + speaker-selection: "'none'" + unoptimized-images: "'none'" + unsized-media: "'none'" + web-share: "'none'" + + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referrer-Policy + header_referrer_policy: "strict-origin-when-cross-origin" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Strict_Transport_Security_Cheat_Sheet.html + header_strict_transport_security: "max-age=2635200; includeSubDomains" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-content-type-options + header_x_content_type_options: "nosniff" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-frame-options + header_x_frame_options: "DENY" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-robots-tag + header_x_robots_tag: "noindex, nofollow" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#content-type + header_default_content_type: "text/html; charset=UTF-8" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#set-cookie + header_cookies_attributes: "SameSite=None; Secure;" + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-dns-prefetch-control + # FIXME + + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#server + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-aspnet-version + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-aspnetmvc-version + # https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html#x-powered-by + header_delete: + - "Server" + - "Via" + - "X-AspNet-Version" + - "X-AspNetMvc-Version" + - "X-Powered-By" + + header_add: + - name: "X-BYOW" + value: "🍷" + + + # caddy-ratelimit + rate_limit: true + rate_limit_events: 1000 + rate_limit_window: "1m" + + + # steffenbusch/caddy-bot-barrier + bot_barrier: false + bot_barrier_secret: null # Required + bot_barrier_complexity: 18 + bot_barrier_valid_for: "60m" + + + # corazawaf/coraza-caddy + crs: true + crs_version: "4.22.0" + crs_log_only: false + crs_plugins: [] + crs_exceptions: + before_request: [] + after_response: [] + crs_paranoia_level: 1 + crs_detection_paranoia_level: null + crs_enforce_bodyproc_urlencoded: 1 + crs_critical_anomaly_score: 5 + crs_error_anomaly_score: 4 + crs_warning_anomaly_score: 3 + crs_notice_anomaly_score: 2 + crs_inbound_anomaly_score_threshold: 5 + crs_outbound_anomaly_score_threshold: 4 + crs_reporting_level: 4 + crs_max_num_args: 255 + crs_arg_name_length: 100 + crs_arg_length: 1000 + crs_total_arg_length: 64000 + crs_max_file_size: 10485760 # 10 Mo + crs_combined_file_sizes: 20970520 # 20 Mo + crs_sampling_percentage: 100 + crs_allowed_methods: + - "GET" + - "HEAD" + - "POST" + - "OPTIONS" + crs_allowed_request_content_type: + - "|application/csp-report|" + - "|application/json|" + - "|application/reports+json|" + - "|application/soap+xml|" + - "|application/x-www-form-urlencoded|" + - "|application/xml|" + - "|multipart/form-data|" + - "|text/xml|" + crs_restricted_extensions: + - ".ani/" + - ".asa/" + - ".asax/" + - ".ascx/" + - ".back/" + - ".backup/" + - ".bak/" + - ".bat/" + - ".bck/" + - ".bk/" + - ".bkp/" + - ".cdx/" + - ".cer/" + - ".cfg/" + - ".cmd/" + - ".cnf/" + - ".com/" + - ".compositefont/" + - ".conf/" + - ".config/" + - ".copy/" + - ".crt/" + - ".cs/" + - ".csproj/" + - ".csr/" + - ".dat/" + - ".db/" + - ".dbf/" + - ".dist/" + - ".dll/" + - ".dos/" + - ".dpkg-dist/" + - ".drv/" + - ".gadget/" + - ".hta/" + - ".htr/" + - ".htw/" + - ".ida/" + - ".idc/" + - ".idq/" + - ".inc/" + - ".inf/" + - ".ini/" + - ".jse/" + - ".key/" + - ".licx/" + - ".lnk/" + - ".log/" + - ".mdb/" + - ".msc/" + - ".ocx/" + - ".old/" + - ".pass/" + - ".pdb/" + - ".pem/" + - ".pfx/" + - ".pif/" + - ".pol/" + - ".prf/" + - ".printer/" + - ".pwd/" + - ".rdb/" + - ".rdp/" + - ".reg/" + - ".resources/" + - ".resx/" + - ".sav/" + - ".save/" + - ".scr/" + - ".sct/" + - ".sh/" + - ".shs/" + - ".sql/" + - ".sqlite/" + - ".sqlite3/" + - ".swp/" + - ".sys/" + - ".temp/" + - ".tlb/" + - ".tmp/" + - ".vb/" + - ".vbe/" + - ".vbproj/" + - ".vbs/" + - ".vsdisco/" + - ".vxd/" + - ".webinfo/" + - ".ws/" + - ".wsc/" + - ".wsf/" + - ".wsh/" + - ".xsd/" + - ".xsx/" + crs_restricted_headers_basic: + - "/content-encoding/" + - "/content-range/" + - "/expect/" + - "/if/" + - "/lock-token/" + - "/proxy/" + - "/x-http-method-override/" + - "/x-http-method/" + - "/x-method-override/" + - "/x-middleware-subrequest/" + crs_restricted_headers_extended: + - "/accept-charset/" + crs_allowed_http_versions: + - "HTTP/1.0" + - "HTTP/1.1" + - "HTTP/2" + - "HTTP/2.0" + - "HTTP/3" + - "HTTP/3.0" + crs_allowed_request_content_type_charset: + - "|utf-8|" + - "|iso-8859-1|" + - "|iso-8859-15|" + - "|windows-1252|"