{
  http_port {{ caddy_http_port }}
  https_port {{ caddy_https_port }}
  admin off
  persist_config off
{% if caddy_default_bind != None %}
  default_bind {{ caddy_default_bind }}
{% endif %}
{% if caddy_crowdsec %}
  order crowdsec first
{% endif %}
{% if caddy_geoip %}
  order geoip2_vars first
{% endif %}
{% if caddy_coraza %}
  order coraza_waf first
{% endif %}

{% if caddy_consul %}
  # See: https://github.com/pteich/caddy-tlsconsul
  storage consul {
    address      "{{ caddy_consul_server }}"
    token        "{{ caddy_out_consul_token.token.SecretID | mandatory }}"
    timeout      10
    prefix       "caddy-{{ caddy_my_name }}-tls"
    value_prefix "{{ caddy_my_name }}"
    aes_key      "{{ caddy_consul_aes_key | mandatory }}"
    tls_enabled  "true"
    tls_insecure "false"
  }
{% endif %}

{% if caddy_geoip %}
  # See: https://github.com/zhangjiayin/caddy-geoip2
  geoip2 {
    accountId         "{{ caddy_geoip_account_id | mandatory }}"
    licenseKey        "{{ caddy_geoip_license_key | mandatory }}"
    databaseDirectory "{{ caddy_home_dir }}/geoip2"
    lockFile          "{{ caddy_home_dir }}/geoip2/.geoip2.lock"
    editionID         "{{ caddy_geoip_edition_ids }}"
    updateUrl         "https://updates.maxmind.com"
    updateFrequency   {{ caddy_geoip_update_frequency }}
  }
{% endif %}

{% if caddy_email | default(None) != None %}
  email "{{ caddy_email }}"
{% endif %}

{% if caddy_crowdsec %}
  # See: https://github.com/hslatman/caddy-crowdsec-bouncer
  crowdsec {
    api_key {{ caddy_crowdsec_api_key | mandatory }}
    api_url {{ caddy_crowdsec_api_url }}
    ticker_interval {{ caddy_crowdsec_ticker_interval }}s
{% if caddy_crowdsec_appsec_url != None %}
    appsec_url {{ caddy_crowdsec_appsec_url }}
{% endif %}
{% if not caddy_crowdsec_streaming %}
    disable_streaming
{% endif %}
{% if caddy_crowdsec_hard_fails %}
    enable_hard_fails
{% endif %}
  }
{% endif %}

{% if caddy_coraza %}
  log waf {
    include "http.handlers.waf"
    format {{ caddy_log_format }}
    format append {
      waf_id {{ caddy_my_name }}
    }
{% if caddy_log_to_file %}
    output file {{ caddy_log_dir }}/waf.log
{% else %}
    output stdout
{% endif %}
  }
{% endif %}

{% if caddy_log_debug %}
  log {
    level DEBUG
    format {{ caddy_log_format }}
    format append {
      waf_id {{ caddy_my_name }}
    }
    output file {{ caddy_log_dir }}/debug.log
  }
{% endif %}
}

# 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_aliases_redirect | default(caddy_sites_defaults.site_aliases_redirect)) and (site.site_aliases | default([]) | length) > 0 %}
{{ site.site_aliases | join(', ') }} {
  redir https://{{ site.name }}{uri}
}
{% else %}
{% for alias in site.site_aliases | default(caddy_sites_defaults.site_aliases) %}
{{ alias }},
{% endfor %}
{% endif %}
{{ site.name }} {
  encode gzip zstd

{% if caddy_crowdsec and site.crowdsec | default(caddy_sites_defaults.crowdsec) %}
  crowdsec
{% endif %}

{% for path in site.paths %}
  handle{% if path.strip_prefix | default(false) %}_path{% endif %} {{ path.path | default('*') }} {
    reverse_proxy {
      to {{ path.addrs | join(' ') }}
      header_up X-Real-IP {remote}
      lb_policy {{ site.proxy_lb_policy | default(caddy_sites_defaults.proxy_lb_policy) }}

{% if site.proxy_active_checks | default(caddy_sites_defaults.proxy_active_checks) %}
      health_uri {{ site.proxy_active_checks_uri | default(caddy_sites_defaults.proxy_active_checks_uri) }}
      health_port {{ site.proxy_active_checks_port | default(caddy_sites_defaults.proxy_active_checks_port) }}
      health_interval {{ site.proxy_active_checks_interval | default(caddy_sites_defaults.proxy_active_checks_interval) }}
      health_timeout {{ site.proxy_active_checks_timeout | default(caddy_sites_defaults.proxy_active_checks_timeout) }}
      health_method {{ site.proxy_active_checks_method | default(caddy_sites_defaults.proxy_active_checks_method) }}
      health_status {{ site.proxy_active_checks_status | default(caddy_sites_defaults.proxy_active_checks_status) }}
{% if site.proxy_active_checks_body | default(caddy_sites_defaults.proxy_active_checks_body) != None %}
      health_body {{ site.proxy_active_checks_body | default(caddy_sites_defaults.proxy_active_checks_body) }}
{% endif %}
{% endif %}
{% if site.proxy_passive_checks | default(caddy_sites_defaults.proxy_passive_checks) %}
      fail_duration {{ site.proxy_passive_checks_fail_duration | default(caddy_sites_defaults.proxy_passive_checks_fail_duration) }}
      max_fails {{ site.proxy_passive_checks_max_fails | default(caddy_sites_defaults.proxy_passive_checks_max_fails) }}
      unhealthy_status {{ site.proxy_passive_checks_unhealthy_status | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_status) }}
      unhealthy_latency {{ site.proxy_passive_checks_unhealthy_latency | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_latency) }}
{% if site.proxy_passive_checks_unhealthy_request_count | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_request_count) != None %}
      unhealthy_request_count {{ site.proxy_passive_checks_unhealthy_request_count | default(caddy_sites_defaults.proxy_passive_checks_unhealthy_request_count) }}
{% endif %}
{% endif %}
    }
  }
{% endfor %}

{% if site.custom_cert | default (false) %}
  tls {{ site.custom_cert_file }} {{ site.custom_cert_key_file }}
{% endif %}

{% if site.filter_blacklist | default(caddy_sites_defaults.filter_blacklist) | length > 0 %}
  @blocked {
{% for ip in site.filter_blacklist | default(caddy_sites_defaults.filter_blacklist) %}
    remote_ip {{ ip }}
{% endfor %}
  }
  respond @blocked "Access Denied" 403
{% endif %}

{% if caddy_geoip and site.geoip | default(caddy_sites_defaults.geoip) %}
  geoip2_vars strict
  @geofilter expression {{ site.geoip_filter_expression | default(caddy_sites_defaults.geoip_filter_expression) }}
  respond @geofilter "Access Denied" 403

{% if site.geoip_debug | default(caddy_sites_defaults.geoip_debug) %}
  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 %}

  @not_whitelisted not {
{% for ip in site.filter_whitelist | default(caddy_sites_defaults.filter_whitelist) %}
    remote_ip {{ ip }}
{% endfor %}
  }

{% if caddy_ratelimit and site.rate_limit | default(caddy_sites_defaults.rate_limit) %}
  # See: https://github.com/mholt/caddy-ratelimit
  rate_limit @not_whitelisted {
{% if caddy_consul %}
    distributed
    storage consul {
      address      "{{ caddy_consul_server }}"
      token        "{{ caddy_out_consul_token.token.SecretID }}"
      timeout      10
      prefix       "caddy-{{ caddy_my_name }}-rl"
      value_prefix "{{ caddy_my_name }}"
      aes_key      "{{ caddy_consul_aes_key }}"
      tls_enabled  "true"
      tls_insecure "false"
    }
{%  endif %}
    log_key
    zone remote_ip {
      key {remote.ip}
      events {{ site.rate_limit_events | default(caddy_sites_defaults.rate_limit_events) }}
      window {{ site.rate_limit_window | default(caddy_sites_defaults.rate_limit_window) }}
    }
  }
{% endif %}

{% if caddy_coraza and site.crs | default(caddy_sites_defaults.crs) %}
  coraza_waf @not_whitelisted {
    load_owasp_crs
    directives `
      Include "{{ caddy_sites_dir }}/{{ site.id }}/coraza.conf"
      Include "{{ caddy_sites_dir }}/{{ site.id }}/crs-setup.conf"
{% for plugin in site.crs_plugins | default(caddy_sites_defaults.crs_plugins) %}
      Include "{{ caddy_crs_plugins_dir }}/{{ plugin }}-config.conf"
      Include "{{ caddy_crs_plugins_dir }}/{{ plugin }}-before.conf"
{% endfor %}
      Include "{{ caddy_sites_dir }}/{{ site.id }}/exclusions-request-before.conf"
      Include "{{ caddy_config_dir }}/coreruleset-{{ site.crs_version | default(caddy_sites_defaults.crs_version) }}/rules/*.conf"
      Include "{{ caddy_sites_dir }}/{{ site.id }}/exclusions-response-after.conf"
{% for plugin in site.crs_plugins | default(caddy_sites_defaults.crs_plugins) %}
      Include "{{ caddy_crs_plugins_dir }}/{{ plugin }}-after.conf"
{% endfor %}
      SecRuleEngine On
    `
  }
{% endif %}

{% if site.cors | default(caddy_sites_defaults.cors) %}
{% for origin in site.cors_allowed_origins | mandatory %}
  import cors https://{{ origin }} "{{
    site.cors_allowed_methods | default(caddy_sites_defaults.cors_allowed_methods) | listadddel(site.cors_allowed_methods_add | default([]), site.cors_allowed_methods_del | default([])) | join(', ') }}" "{{
    site.cors_allowed_headers | default(caddy_sites_defaults.cors_allowed_headers) | listadddel(site.cors_allowed_headers_add | default([]), site.cors_allowed_headers_del | default([])) | join(', ') }}" "{{
    site.cors_allowed_credentials | default(caddy_sites_defaults.cors_allowed_credentials) | ternary('true', 'false') }}" "{{
    site.cors_max_age | default(caddy_sites_defaults.cors_max_age) }}" "{{
    site.cors_vary | default(caddy_sites_defaults.cors_vary) | listadddel(site.cors_vary_add | default([]), site.cors_vary_del | default([]))  | join(', ') }}" "{{
    site.cors_exposed_headers | default(caddy_sites_defaults.cors_exposed_headers) | listadddel(site.cors_exposed_headers_add | default([]), site.cors_exposed_headers_del | default([]))  | join(', ') }}"
{% endfor %}
  import cors-deny
{% endif %}

{% if caddy_bot_barrier and site.bot_barrier | default(caddy_sites_defaults.bot_barrier) %}
  # See: https://github.com/steffenbusch/caddy-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.header | default(caddy_sites_defaults.header) %}
  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) }}"
    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.header_add | default(caddy_sites_defaults.header_add) %}
    {{ header.name }}            "{{ header.value }}"
{% endfor %}
  }
{% endif %}

  handle_errors 403 {
    header X-Blocked "true"
    respond "Your request was blocked. 🔴"
  }

  log {
    level INFO
    format {{ caddy_log_format }}
    format append {
      waf_id {{ caddy_my_name }}
    }
{% if caddy_log_to_file %}
    output file {{ caddy_log_dir }}/site_{{ site.id }}.log
{% else %}
    output stdout
{% endif %}
  }
}

{% endfor %}
