www/caddy: Add client_ip_headers (#4519)

* www/caddy: Add client_ip_headers for https://github.com/opnsense/plugins/issues/4517 , Rewrite copy_headers logic for https://github.com/opnsense/plugins/issues/4488 . Since headers are used in multiple parts of the configuration this creates a single point of truth to ease maintenance burden.

* www/caddy: Forgot to add general form options for https://github.com/opnsense/plugins/issues/4517

* www/caddy: Change directive name from AuthCopyHeaders to CopyHeaders since Field Type changed to empty it. The field was introduced in the prior version and optional, so impact low. Changelogs.

* www/caddy: Improve helptext for client_ip_headers feature
This commit is contained in:
Monviech
2025-02-05 14:11:04 +01:00
committed by GitHub
parent b6b0cf292a
commit 6644d16b6b
6 changed files with 79 additions and 34 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
PLUGIN_NAME= caddy
PLUGIN_VERSION= 1.8.1
PLUGIN_VERSION= 1.8.2
PLUGIN_DEPENDS= caddy-custom
PLUGIN_COMMENT= Modern Reverse Proxy with Automatic HTTPS, Dynamic DNS and Layer4 Routing
PLUGIN_MAINTAINER= cedrik@pischem.com
+7
View File
@@ -13,6 +13,13 @@ DOC: https://docs.opnsense.org/manual/how-tos/caddy.html
Plugin Changelog
================
1.8.2
* Add: client_ip_headers (opnsense/plugins/issues/4517)
* Add: CloudDNS provider (opnsense/plugins/pull/4507)
* Change: Generalize forward_auth copy_headers directive. Existing configuration from (issues/4488) will be emptied. (opnsense/plugins/pull/4519)
* Fix: Shortcut buttons in reverse_proxy.volt (opnsense/plugins/pull/4525)
1.8.1
* Add: Optional "Authorization" header to forward_auth (opnsense/plugins/issues/4488)
@@ -58,6 +58,16 @@
<type>dropdown</type>
<help><![CDATA[Select an Access List to set IP ranges of Trusted Proxies. If Caddy is not the first server being connected to by clients (for example, when a "CDN" is in front of Caddy), configure "Trusted Proxies" with a list of IP ranges (CIDRs) from which incoming requests are trusted to have sent good values for these headers. Additionally, set the same Access List to the domains the Trusted Proxies connect to.]]></help>
</field>
<field>
<id>caddy.general.ClientIpHeaders</id>
<label>Client IP Headers</label>
<type>dropdown</type>
<type>select_multiple</type>
<size>5</size>
<style>selectpicker</style>
<hint>X-Forwarded-For</hint>
<help><![CDATA[Select one or multiple headers to extract the real client IP. Headers can be added in "Reverse Proxy - Headers". As example, setting "X-Forwarded-For" and "Cf-Connecting-Ip" can extract the real client IP from Cloudflare.]]></help>
</field>
<field>
<id>caddy.general.GracePeriod</id>
<label>Grace Period</label>
@@ -228,11 +238,13 @@
<help><![CDATA[Enter the URI of the authz api endpoint.]]></help>
</field>
<field>
<id>caddy.general.AuthCopyHeaders</id>
<id>caddy.general.CopyHeaders</id>
<label>Copy Headers</label>
<type>dropdown</type>
<type>select_multiple</type>
<size>5</size>
<style>selectpicker</style>
<help><![CDATA[If nothing is selected, the correct default headers for the chosen provider will be used. If you change the default, you must select the required headers manually. "copy_headers" is a list of HTTP header fields to copy from the response to the original request, when the request has a success status code.]]></help>
<help><![CDATA[Select headers to copy in addition to the default of the chosen provider. Headers can be added in "Reverse Proxy - Headers". As example, copying "Authorization" can pass Basic Auth credentials from the Auth Provider to the reverse proxied application.]]></help>
</field>
</tab>
<activetab>general-settings</activetab>
@@ -1,7 +1,7 @@
<model>
<mount>//Pischem/caddy</mount>
<description>Caddy Reverse Proxy</description>
<version>1.3.4</version>
<version>1.3.5</version>
<items>
<general>
<enabled type="BooleanField">
@@ -84,6 +84,17 @@
</reverseproxy>
</Model>
</accesslist>
<ClientIpHeaders type="ModelRelationField">
<Model>
<reverseproxy>
<source>OPNsense.Caddy.Caddy</source>
<items>reverseproxy.header</items>
<display>HeaderType,description</display>
<display_format>%s %s</display_format>
</reverseproxy>
</Model>
<Multiple>Y</Multiple>
</ClientIpHeaders>
<DisableSuperuser type="OptionField">
<Required>Y</Required>
<Default>0</Default>
@@ -168,27 +179,17 @@
<Mask>/^(\/.*)?$/u</Mask>
<ValidationMessage>Please enter a valid 'URI' that starts with '/'.</ValidationMessage>
</AuthToUri>
<AuthCopyHeaders type="OptionField">
<CopyHeaders type="ModelRelationField">
<Model>
<reverseproxy>
<source>OPNsense.Caddy.Caddy</source>
<items>reverseproxy.header</items>
<display>HeaderType,description</display>
<display_format>%s %s</display_format>
</reverseproxy>
</Model>
<Multiple>Y</Multiple>
<OptionValues>
<Authorization>Authorization</Authorization>
<Remote-User>Remote-User</Remote-User>
<Remote-Groups>Remote-Groups</Remote-Groups>
<Remote-Name>Remote-Name</Remote-Name>
<Remote-Email>Remote-Email</Remote-Email>
<X-Authentik-Username>X-Authentik-Username</X-Authentik-Username>
<X-Authentik-Groups>X-Authentik-Groups</X-Authentik-Groups>
<X-Authentik-Email>X-Authentik-Email</X-Authentik-Email>
<X-Authentik-Name>X-Authentik-Name</X-Authentik-Name>
<X-Authentik-Uid>X-Authentik-Uid</X-Authentik-Uid>
<X-Authentik-Jwt>X-Authentik-Jwt</X-Authentik-Jwt>
<X-Authentik-Meta-Jwks>X-Authentik-Meta-Jwks</X-Authentik-Meta-Jwks>
<X-Authentik-Meta-Outpost>X-Authentik-Meta-Outpost</X-Authentik-Meta-Outpost>
<X-Authentik-Meta-Provider>X-Authentik-Meta-Provider</X-Authentik-Meta-Provider>
<X-Authentik-Meta-App>X-Authentik-Meta-App</X-Authentik-Meta-App>
<X-Authentik-Meta-Version>X-Authentik-Meta-Version</X-Authentik-Meta-Version>
</OptionValues>
</AuthCopyHeaders>
</CopyHeaders>
</general>
<reverseproxy>
<reverse type="ArrayField">
@@ -85,6 +85,14 @@
{% if accessList %}
trusted_proxies static {{ accessList.clientIps.split(',') | join(' ') }}
{% endif %}
{% if generalSettings.ClientIpHeaders %}
{% for header_uuid in generalSettings.ClientIpHeaders.split(',') %}
{% set header = helpers.toList('Pischem.caddy.reverseproxy.header') | selectattr('@uuid', 'equalto', header_uuid) | first %}
{% if header and header.HeaderType %}
client_ip_headers {{ header.HeaderType }}
{% endif %}
{% endfor %}
{% endif %}
{% if generalSettings.LogCredentials|default("0") == "1" %}
log_credentials
{% endif %}
@@ -7,16 +7,26 @@
{% set is_ipv6 = (':' in generalSettings.AuthToDomain and generalSettings.AuthToDomain.count(':') >= 2) %}
{% set auth_url = (generalSettings.AuthToTls|default("0") == "1" and 'https://' or 'http://') + (is_ipv6 and '[' or '') + generalSettings.AuthToDomain|default("") + (is_ipv6 and ']' or '') + (generalSettings.AuthToPort and ':' + generalSettings.AuthToPort or '') %}
{% endif %}
{% macro generate_copy_headers() %}
{% if generalSettings.CopyHeaders %}
{% for header_uuid in generalSettings.CopyHeaders.split(',') %}
{% set header = helpers.toList('Pischem.caddy.reverseproxy.header') | selectattr('@uuid', 'equalto', header_uuid) | first %}
{% if header and header.HeaderType %}
copy_headers {{ header.HeaderType }}
{% endif %}
{% endfor %}
{% endif %}
{% endmacro %}
{% if generalSettings.AuthProvider == 'authelia' %}
forward_auth {{ auth_url }} {
{% if generalSettings.AuthToUri %}
uri {{ generalSettings.AuthToUri|default("") }}
{% endif %}
{% if generalSettings.AuthCopyHeaders|default("") == "" %}
copy_headers Remote-User Remote-Groups Remote-Name Remote-Email
{% else %}
copy_headers {{ generalSettings.AuthCopyHeaders.split(',') | join(' ') }}
{% endif %}
copy_headers Remote-User
copy_headers Remote-Groups
copy_headers Remote-Name
copy_headers Remote-Email
{{ generate_copy_headers() }}
}
{% elif generalSettings.AuthProvider == 'authentik' %}
reverse_proxy /outpost.goauthentik.io/* {{ auth_url }} {
@@ -28,10 +38,17 @@
{% if generalSettings.AuthToUri %}
uri {{ generalSettings.AuthToUri|default("") }}
{% endif %}
{% if generalSettings.AuthCopyHeaders|default("") == "" %}
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
{% else %}
copy_headers {{ generalSettings.AuthCopyHeaders.split(',') | join(' ') }}
{% endif %}
copy_headers X-Authentik-Username
copy_headers X-Authentik-Groups
copy_headers X-Authentik-Email
copy_headers X-Authentik-Name
copy_headers X-Authentik-Uid
copy_headers X-Authentik-Jwt
copy_headers X-Authentik-Meta-Jwks
copy_headers X-Authentik-Meta-Outpost
copy_headers X-Authentik-Meta-Provider
copy_headers X-Authentik-Meta-App
copy_headers X-Authentik-Meta-Version
{{ generate_copy_headers() }}
}
{% endif %}