net/haproxy: support more advanced sample fetches and converters

This commit is contained in:
Frank Wall 2026-02-08 22:43:34 +01:00
parent b9dcae8a9c
commit 3c2dd310fe
5 changed files with 118 additions and 1 deletions

View file

@ -27,7 +27,8 @@ Added:
* add support for GPC/GPT/SC to conditions and rules (#1123, #5109)
* add support for SSL SNI expression to servers (#3756)
* add column "mode" to servers overview (#4632)
* add support for loading mapfiles in conditions
* add support for loading mapfiles in conditions and rules
* add support for sample fetches in rules
Fixed:
* Maintenance tab "SSL Certificates" not working with only one cert

View file

@ -266,6 +266,27 @@
<type>text</type>
<help><![CDATA[Specify the value for the URL parameter.]]></help>
</field>
<field>
<label>Parameters</label>
<type>header</type>
<style>expression_table table_var</style>
</field>
<field>
<id>acl.var_comparison</id>
<label>Comparison</label>
<type>dropdown</type>
</field>
<field>
<id>acl.var</id>
<label>Variable</label>
<type>text</type>
<help><![CDATA[The name of a variable, followed by an optional default value, e.g. (req.rate_limit) or (req.rate_limit,10).]]></help>
</field>
<field>
<id>acl.var_value</id>
<label>Value</label>
<type>text</type>
</field>
<field>
<label>Parameters</label>
<type>header</type>
@ -1788,4 +1809,10 @@
<type>dropdown</type>
<help><![CDATA[Load patterns from a map file.]]></help>
</field>
<field>
<id>acl.converter</id>
<label>Converter</label>
<type>text</type>
<help><![CDATA[Transforms or processes the output of a sample fetch (e.g. variable, mapping, arithmetic, string manipulation) and passes the result to the next converter or final evaluation. Make more complex conditions possible, e.g. acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0.]]></help>
</field>
</form>

View file

@ -325,4 +325,22 @@
<type>text</type>
<help><![CDATA[Refers to the number of the Stick Counter, e.g. 0 or 1 when using sc0 or sc1 respectively. This value will be ignored in rules that do not support SC.]]></help>
</field>
<field>
<id>action.mapfile</id>
<label>Mapfile</label>
<type>dropdown</type>
<help><![CDATA[Load patterns from a map file.]]></help>
</field>
<field>
<id>action.map_default</id>
<label>Map default value</label>
<type>text</type>
<help><![CDATA[When a map file is specified, this is the default value.]]></help>
</field>
<field>
<id>action.sample_fetch</id>
<label>Sample fetch</label>
<type>text</type>
<help><![CDATA[Extracts a value from the request or connection context (e.g. path, header, source IP) to be used as input for converters or matching rules. May be used in combination with set-var and map files to create more complex rules, e.g.<br/>http-request set-var(req.rate_limit) path,map_beg(/path/to/mapfile,20)</br>http-request set-var(req.request_rate) base32+src,table_http_req_rate()]]></help>
</field>
</form>

View file

@ -1951,6 +1951,7 @@
<ssl_sni_sub>ssl_sni_sub SNI TLS extension contains (TCP request content inspection)</ssl_sni_sub>
<stopping>stopping HAProxy process is currently stopping</stopping>
<url_param>url_param URL parameter contains</url_param>
<var>var Compare the value of a variable</var>
<wait_end>wait_end Inspection period is over</wait_end>
<custom_acl>Custom condition (option pass-through)</custom_acl>
</OptionValues>
@ -2076,6 +2077,25 @@
<Mask>/^.{1,4096}$/u</Mask>
<Required>N</Required>
</url_param_value>
<var type="TextField">
<Mask>/^.{1,4096}$/u</Mask>
<Required>N</Required>
</var>
<var_value type="TextField">
<Mask>/^.{1,4096}$/u</Mask>
<Required>N</Required>
</var_value>
<var_comparison type="OptionField">
<Required>N</Required>
<Default>gt</Default>
<OptionValues>
<gt>greater than</gt>
<ge>greater equal</ge>
<eq>equal</eq>
<lt>less than</lt>
<le>less equal</le>
</OptionValues>
</var_comparison>
<ssl_c_verify_code type="IntegerField">
<MinimumValue>0</MinimumValue>
<MaximumValue>500000</MaximumValue>
@ -3467,6 +3487,10 @@
<ValidationMessage>Related mapfile item not found</ValidationMessage>
<Required>N</Required>
</mapfile>
<converter type="TextField">
<Mask>/^.{1,4096}$/u</Mask>
<Required>N</Required>
</converter>
</acl>
</acls>
<actions>
@ -4142,6 +4166,25 @@
<ValidationMessage>Please specify a value between 0 and 99.</ValidationMessage>
<Required>N</Required>
</sc_number>
<mapfile type="ModelRelationField">
<Model>
<template>
<source>OPNsense.HAProxy.HAProxy</source>
<items>mapfiles.mapfile</items>
<display>name</display>
</template>
</Model>
<ValidationMessage>Related mapfile item not found</ValidationMessage>
<Required>N</Required>
</mapfile>
<map_default type="TextField">
<Mask>/^.{1,4096}$/u</Mask>
<Required>N</Required>
</map_default>
<sample_fetch type="TextField">
<Mask>/^.{1,4096}$/u</Mask>
<Required>N</Required>
</sample_fetch>
</action>
</actions>
<luas>

View file

@ -1381,6 +1381,18 @@
{% set acl_enabled = '0' %}
# ERROR: missing parameters
{% endif %}
{% elif acl_data.expression == 'var' %}
{% if acl_data.var|default("") != "" and acl_data.var_value|default("") != "" %}
{% if acl_data.converter|default("") != "" %}
{% set converter_data = ',' ~ acl_data.converter %}
{% else %}
{% set converter_data = '' %}
{% endif %}
{% do acl_options.append('var' ~ acl_data.var ~ converter_data ~ ' ' ~ acl_data.var_comparison ~ ' ' ~ acl_data.var_value) %}
{% else %}
{% set acl_enabled = '0' %}
# ERROR: missing parameters
{% endif %}
{# # handle boolean ACL types that do not require any input #}
{% elif acl_data.expression in acl_boolean_types %}
{% do acl_options.append(acl_data.expression) %}
@ -1637,6 +1649,22 @@
{% set action_enabled = '0' %}
{% do global_action_options.append('# ERROR: unsupported rule type ' ~ action_data.type) %}
{% endif %}
{# # Add sample fetch to map file config. #}
{% if action_data.mapfile|default("") != "" %}
{% set mapfile_data = helpers.getUUID(action_data.mapfile) %}
{% set mapfile_path = '/tmp/haproxy/mapfiles/' ~ mapfile_data.id ~ '.txt' %}
{% set mapfile_config = 'map_' ~ mapfile_data.type %}
{% if action_data.map_default|default("") != "" %}
{% set mapfile_default = ',' ~ action_data.map_default %}
{% endif %}
{% if action_data.sample_fetch|default("") != "" %}
{% set mapfile_sf = action_data.sample_fetch ~ ',' %}
{% endif %}
{% do action_options.append(mapfile_sf ~ mapfile_config ~ '(' ~ mapfile_path ~ mapfile_default ~ ')') %}
{# # Add/append sample fetch. #}
{% elif action_data.sample_fetch|default("") != "" %}
{% do action_options.append(action_data.sample_fetch) %}
{% endif %}
{# # Is this rule enabled in the GUI? #}
{% if action_data.enabled|default('') == '1' %}
{# # check if action is valid #}