net/haproxy: add support for resolvers, refs #1787

This commit is contained in:
Frank Wall
2020-08-18 17:40:21 +02:00
parent d7be04b57e
commit 89737ebe2c
8 changed files with 249 additions and 2 deletions
@@ -379,4 +379,34 @@ class SettingsController extends ApiMutableModelControllerBase
{
return $this->searchBase('users.user', array('enabled', 'name', 'description'), 'name');
}
public function getresolverAction($uuid = null)
{
return $this->getBase('resolver', 'resolvers.resolver', $uuid);
}
public function setresolverAction($uuid)
{
return $this->setBase('resolver', 'resolvers.resolver', $uuid);
}
public function addresolverAction()
{
return $this->addBase('resolver', 'resolvers.resolver');
}
public function delresolverAction($uuid)
{
return $this->delBase('resolvers.resolver', $uuid);
}
public function toggleresolverAction($uuid, $enabled = null)
{
return $this->toggleBase('resolvers.resolver', $uuid);
}
public function searchresolversAction()
{
return $this->searchBase('resolvers.resolver', array('enabled', 'name', 'nameservers'), 'name');
}
}
@@ -57,6 +57,7 @@ class IndexController extends \OPNsense\Base\IndexController
$this->view->formDialogErrorfile = $this->getForm("dialogErrorfile");
$this->view->formDialogMapfile = $this->getForm("dialogMapfile");
$this->view->formDialogCpu = $this->getForm("dialogCpu");
$this->view->formDialogResolver = $this->getForm("dialogResolver");
// set additional view parameters
$mdlHAProxy = new \OPNsense\HAProxy\HAProxy();
$this->view->showIntro = (string)$mdlHAProxy->general->showIntro;
@@ -54,6 +54,13 @@
<help><![CDATA[Add servers to this backend. Use TAB key to complete typing.]]></help>
<hint>Type server name or choose from list.</hint>
</field>
<field>
<id>backend.linkedResolver</id>
<label>Resolver</label>
<type>dropdown</type>
<help><![CDATA[Select the custom resolver configuration that should be used for all servers in this backend.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>backend.source</id>
<label>Source address</label>
@@ -0,0 +1,54 @@
<form>
<field>
<id>resolver.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this resolver configuration.</help>
</field>
<field>
<id>resolver.name</id>
<label>Name</label>
<type>text</type>
<help>Choose a name for this resolver configuration.</help>
</field>
<field>
<id>resolver.description</id>
<label>Description</label>
<type>text</type>
<help>Choose a optional description for this resolver configuration.</help>
</field>
<field>
<id>resolver.nameservers</id>
<label>Nameservers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Add nameservers to this resolver configuration, i.e. 127.0.0.1:53 or 192.168.1.1:53. Use TAB key to complete typing.]]></help>
<hint>Enter ip:port here. Finish with TAB.</hint>
</field>
<field>
<id>resolver.parse_resolv_conf</id>
<label>Use resolv.conf</label>
<type>checkbox</type>
<help>Add all nameservers found in /etc/resolv.conf to this resolver configuration.</help>
</field>
<field>
<id>resolver.resolve_retries</id>
<label>Resolve Retries</label>
<type>text</type>
<help><![CDATA[This configures the number of queries to send to resolve a server name before giving up.]]></help>
</field>
<field>
<id>resolver.timeout_resolve</id>
<label>Resolve Timeout</label>
<type>text</type>
<help><![CDATA[This configures the default time to trigger name resolutions when no other time applied. Enter a number followed by one of the supported suffixes "d" (days), "h" (hour), "m" (minute), "s" (seconds), "ms" (miliseconds).]]></help>
</field>
<field>
<id>resolver.timeout_retry</id>
<label>Retry Timeout</label>
<type>text</type>
<help><![CDATA[This configures the default time between two DNS queries, when no valid response has been received. Enter a number followed by one of the supported suffixes "d" (days), "h" (hour), "m" (minute), "s" (seconds), "ms" (miliseconds).]]></help>
</field>
</form>
@@ -1,6 +1,6 @@
<model>
<mount>//OPNsense/HAProxy</mount>
<version>2.9.0</version>
<version>2.10.0</version>
<description>the HAProxy load balancer</description>
<items>
<general>
@@ -792,6 +792,18 @@
<Multiple>Y</Multiple>
<Required>N</Required>
</linkedServers>
<linkedResolver type="ModelRelationField">
<Model>
<template>
<source>OPNsense.HAProxy.HAProxy</source>
<items>resolvers.resolver</items>
<display>name</display>
</template>
</Model>
<ValidationMessage>Related resolver not found</ValidationMessage>
<multiple>N</multiple>
<Required>N</Required>
</linkedResolver>
<source type="TextField">
<mask>/^((([0-9a-zA-Z._\-\*:]+)))*/u</mask>
<ChangeCase>lower</ChangeCase>
@@ -2482,5 +2494,57 @@
</cpu_id>
</cpu>
</cpus>
<resolvers>
<resolver type="ArrayField">
<id type="UniqueIdField">
<Required>Y</Required>
</id>
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<name type="TextField">
<mask>/^[^\t^,^;^\.^\[^\]^\{^\}]{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
<Required>Y</Required>
</name>
<description type="TextField">
<Required>N</Required>
<mask>/^.{1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters.</ValidationMessage>
</description>
<nameservers type="CSVListField">
<Required>N</Required>
<Sorted>Y</Sorted>
<multiple>Y</multiple>
<mask>/^((([0-9a-zA-Z._\-\*:\[\]]+:[0-9]+(-[0-9]+)?)([,]){0,1}))*/u</mask>
<ChangeCase>lower</ChangeCase>
<ValidationMessage>Please provide a valid nameserver address, i.e. 127.0.0.1:53, [::1]:53 or 192.168.1.1:53.</ValidationMessage>
</nameservers>
<parse_resolv_conf type="BooleanField">
<default>0</default>
<Required>Y</Required>
</parse_resolv_conf>
<resolve_retries type="IntegerField">
<default>3</default>
<MinimumValue>0</MinimumValue>
<MaximumValue>100000</MaximumValue>
<ValidationMessage>Please specify a value between 0 and 100000.</ValidationMessage>
<Required>N</Required>
</resolve_retries>
<timeout_resolve type="TextField">
<default>1s</default>
<mask>/^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u</mask>
<ValidationMessage>Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".</ValidationMessage>
<Required>N</Required>
</timeout_resolve>
<timeout_retry type="TextField">
<default>1s</default>
<mask>/^([0-9]{1,8}(?:us|ms|s|m|h|d)?)/u</mask>
<ValidationMessage>Should be a number between 1 and 8 characters, optionally followed by either "d", "h", "m", "s", "ms" or "us".</ValidationMessage>
<Required>N</Required>
</timeout_retry>
</resolver>
</resolvers>
</items>
</model>
@@ -20,6 +20,7 @@
<Luas VisibleName="Lua Scripts" url="/ui/haproxy#luas"/>
<Errorfiles VisibleName="Error Files" url="/ui/haproxy#errorfiles"/>
<Mapfiles VisibleName="Map Files" url="/ui/haproxy#mapfiles"/>
<Resolvers VisibleName="Resolvers" url="/ui/haproxy#resolvers"/>
</Settings>
<Statistics order="20" url="/ui/haproxy/statistics">
<Overview VisibleName="Overview" url="/ui/haproxy/statistics#info"/>
@@ -198,6 +198,19 @@ POSSIBILITY OF SUCH DAMAGE.
}
);
$("#grid-resolvers").UIBootgrid(
{ search:'/api/haproxy/settings/searchResolvers',
get:'/api/haproxy/settings/getResolver/',
set:'/api/haproxy/settings/setResolver/',
add:'/api/haproxy/settings/addResolver/',
del:'/api/haproxy/settings/delResolver/',
toggle:'/api/haproxy/settings/toggleResolver/',
options: {
rowCount:[10,25,50,100,500,1000]
}
}
);
// hook into on-show event for dialog to extend layout.
$('#DialogAcl').on('shown.bs.modal', function (e) {
$("#acl\\.expression").change(function(){
@@ -538,6 +551,7 @@ POSSIBILITY OF SUCH DAMAGE.
<li><a data-toggle="tab" href="#luas">{{ lang._('Lua Scripts') }}</a></li>
<li><a data-toggle="tab" href="#mapfiles">{{ lang._('Map Files') }}</a></li>
<li><a data-toggle="tab" href="#cpus">{{ lang._('CPU Affinity Rules') }}</a></li>
<li><a data-toggle="tab" href="#resolvers">{{ lang._('Resolvers') }}</a></li>
</ul>
</li>
</ul>
@@ -626,8 +640,9 @@ POSSIBILITY OF SUCH DAMAGE.
<li>{{ lang._("%sLua scripts:%s Include your own Lua code/scripts to extend HAProxy's functionality. The Lua code can be used in certain %sRules%s, for example.") | format('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ lang._("%sMap Files:%s A map allows to map a data in input to an other one on output. For example, this makes it possible to map a large number of domains to backend pools without using the GUI. Map files need to be used in %sRules%s, otherwise they are ignored.") | format('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ lang._("%sCPU Affinity Rules:%s This feature makes it possible to bind HAProxy's processes/threads to a specific CPU (or a CPU set). Furthermore it is possible to select CPU Affinity Rules in %sPublic Services%s to restrict them to a certain set of processes/threads/CPUs.") | format('<b>', '</b>', '<b>', '</b>') }}</li>
<li>{{ lang._("%sResolvers:%s This feature allows in-depth configuration of how HAProxy handles name resolution and interacts with name resolvers (DNS). Each resolver configuration can be used in %sBackend Pools%s to apply individual name resolution configurations.") | format('<b>', '</b>', '<b>', '</b>') }}</li>
</ul>
<p>{{ lang._("For more details visit HAProxy's official documentation regarding the %sError Messages%s, %sLua Script%s and the %sMap Files%s features. More information on HAProxy's CPU Affinity is also available %shere%s, %shere%s and %shere%s.") | format('<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-errorfile" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#lua-load" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#cpu-map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#bind-process" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#process" target="_blank">', '</a>') }}</p>
<p>{{ lang._("For more details visit HAProxy's official documentation regarding the %sError Messages%s, %sLua Script%s and the %sMap Files%s features. More information on HAProxy's CPU Affinity is also available %shere%s, %shere%s and %shere%s. A detailed explanation of the resolvers feature can be found %shere%s.") | format('<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#4-errorfile" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#lua-load" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#cpu-map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#bind-process" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#process" target="_blank">', '</a>','<a href="http://cbonte.github.io/haproxy-dconv/2.0/configuration.html#5.3.2" target="_blank">', '</a>') }}</p>
<br/>
</div>
</div>
@@ -1067,6 +1082,41 @@ POSSIBILITY OF SUCH DAMAGE.
<br/>
</div>
</div>
<div id="resolvers" class="tab-pane fade">
<!-- tab page "resolvers" -->
<table id="grid-resolvers" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogResolver">
<thead>
<tr>
<th data-column-id="resolverid" data-type="number" data-visible="false">{{ lang._('Resolver ID') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="nameservers" data-type="string">{{ lang._('Nameservers') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
<!-- apply button -->
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureAct-resolvers" type="button"><b>{{ lang._('Apply') }}</b><i id="reconfigureAct_progress" class=""></i></button>
<button class="btn btn-primary" id="configtestAct-resolvers" type="button"><b>{{ lang._('Test syntax') }}</b><i id="configtestAct_progress" class=""></i></button>
<br/>
<br/>
</div>
</div>
</div>
{# include dialogs #}
@@ -1082,3 +1132,4 @@ POSSIBILITY OF SUCH DAMAGE.
{{ partial("layout_partials/base_dialog",['fields':formDialogErrorfile,'id':'DialogErrorfile','label':lang._('Edit Error Message')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogMapfile,'id':'DialogMapfile','label':lang._('Edit Map File')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogCpu,'id':'DialogCpu','label':lang._('Edit CPU Affinity Rule')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogResolver,'id':'DialogResolver','label':lang._('Edit Resolver')])}}
@@ -998,6 +998,40 @@ userlist stats_auth
{% endif %}
{% endif %}
{# ############################### #}
{# RESOLVERS #}
{# ############################### #}
{% if helpers.exists('OPNsense.HAProxy.resolvers') %}
{% for resolver in helpers.toList('OPNsense.HAProxy.resolvers.resolver') %}
{% if resolver.enabled == '1' %}
# Resolver: {{resolver.name}}
resolvers {{resolver.id}}
{% if resolver.nameservers|default("") != "" %}
{% for nameserver in resolver.nameservers.split(",") %}
nameserver {{nameserver}} {{nameserver}}
{% endfor %}
{% endif %}
{% if resolver.parse_resolv_conf|default("") == "1" %}
parse-resolv-conf
{% endif %}
{% if resolver.resolve_retries|default("") != "" %}
resolve_retries {{resolver.resolve_retries}}
{% endif %}
{% if resolver.timeout_resolve|default("") != "" %}
timeout resolve {{resolver.timeout_resolve}}
{% endif %}
{% if resolver.timeout_retry|default("") != "" %}
timeout retry {{resolver.timeout_retry}}
{% endif %}
{% else %}
# Resolver (DISABLED): {{resolver.name}}
{% endif %}
{% endfor %}
{%- endif -%}
{# ############################### #}
{# FRONTENDS #}
{# ############################### #}
@@ -1454,6 +1488,11 @@ backend {{backend.name}}
{% do server_options.append('verify none') %}
{% endif %}
{% endif %}
{# # resolver #}
{% if backend.linkedResolver|default("") != "" %}
{% set resolver_data = helpers.getUUID(backend.linkedResolver) %}
{% do server_options.append('resolvers ' ~ resolver_data.id) %}
{% endif %}
{# # source address #}
{% if backend.source|default("") != "" %}
{# # prefer backend configuration #}