net/haproxy: add support for e-mail alerts and mailers, closes #1669

This commit is contained in:
Frank Wall
2021-02-23 17:11:10 +01:00
parent bf6a08d338
commit 8189ff71df
9 changed files with 261 additions and 0 deletions
+1
View File
@@ -17,6 +17,7 @@ Added:
* add config diff
* guard against broken config by using a staging config file
* add basic OCSP stapling support (#1430)
* add support for e-mail alerts and mailers (#1669)
Fixed:
* fix maintenance page (python error: 'list' object has no attribute 'strip')
@@ -409,4 +409,34 @@ class SettingsController extends ApiMutableModelControllerBase
{
return $this->searchBase('resolvers.resolver', array('enabled', 'name', 'nameservers'), 'name');
}
public function getmailerAction($uuid = null)
{
return $this->getBase('mailer', 'mailers.mailer', $uuid);
}
public function setmailerAction($uuid)
{
return $this->setBase('mailer', 'mailers.mailer', $uuid);
}
public function addmailerAction()
{
return $this->addBase('mailer', 'mailers.mailer');
}
public function delmailerAction($uuid)
{
return $this->delBase('mailers.mailer', $uuid);
}
public function togglemailerAction($uuid, $enabled = null)
{
return $this->toggleBase('mailers.mailer', $uuid);
}
public function searchmailersAction()
{
return $this->searchBase('mailers.mailer', array('enabled', 'name', 'mailservers', 'sender', 'recipient'), 'name');
}
}
@@ -53,6 +53,7 @@ class IndexController extends \OPNsense\Base\IndexController
$this->view->formDialogGroup = $this->getForm("dialogGroup");
$this->view->formDialogHealthcheck = $this->getForm("dialogHealthcheck");
$this->view->formDialogLua = $this->getForm("dialogLua");
$this->view->formDialogMailer = $this->getForm("dialogMailer");
$this->view->formDialogMapfile = $this->getForm("dialogMapfile");
$this->view->formDialogResolver = $this->getForm("dialogResolver");
$this->view->formDialogServer = $this->getForm("dialogServer");
@@ -119,6 +119,12 @@
<help><![CDATA[The number of consecutive successful health checks before a server is considered as available.]]></help>
<advanced>true</advanced>
</field>
<field>
<id>backend.linkedMailer</id>
<label>E-Mail Alert</label>
<type>dropdown</type>
<help><![CDATA[Select an e-mail alert configuration. An e-mail is sent when the state of a server changes.]]></help>
</field>
<field>
<label>HTTP(S) settings</label>
<type>header</type>
@@ -0,0 +1,61 @@
<form>
<field>
<id>mailer.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this mailer configuration.</help>
</field>
<field>
<id>mailer.name</id>
<label>Name</label>
<type>text</type>
<help>Choose a name for this mailer configuration.</help>
</field>
<field>
<id>mailer.description</id>
<label>Description</label>
<type>text</type>
<help>Choose a optional description for this mailer configuration.</help>
</field>
<field>
<id>mailer.mailservers</id>
<label>Mail Servers</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<sortable>true</sortable>
<help><![CDATA[Add mailservers to this mailer configuration, i.e. 192.168.1.1:25. Use TAB key to complete typing.]]></help>
<hint>Enter ip:port here. Finish with TAB.</hint>
</field>
<field>
<id>mailer.sender</id>
<label>Mail Sender</label>
<type>text</type>
<help><![CDATA[Declare the from email address to be used in both the envelope and header of email alerts.]]></help>
</field>
<field>
<id>mailer.recipient</id>
<label>Mail Recipient</label>
<type>text</type>
<help><![CDATA[Declare both the recipient address in the envelope and to address in the header of email alerts.]]></help>
</field>
<field>
<id>mailer.loglevel</id>
<label>Alert Log Level</label>
<type>dropdown</type>
<help><![CDATA[Declare the maximum log level of messages for which email alerts will be sent. This acts as a filter on the sending of email alerts.]]></help>
</field>
<field>
<id>mailer.timeout</id>
<label>Timeout</label>
<type>text</type>
<help><![CDATA[Defines the time (in seconds) available for a mail/connection to be made and send to the mail server.]]></help>
</field>
<field>
<id>mailer.hostname</id>
<label>Hostname</label>
<type>text</type>
<help><![CDATA[Declare the to hostname address to be used when communicating with mailers.]]></help>
<advanced>true</advanced>
</field>
</form>
@@ -928,6 +928,18 @@
<ValidationMessage>Please specify a value between 1 and 100.</ValidationMessage>
<Required>N</Required>
</healthCheckRise>
<linkedMailer type="ModelRelationField">
<Model>
<template>
<source>OPNsense.HAProxy.HAProxy</source>
<items>mailers.mailer</items>
<display>name</display>
</template>
</Model>
<ValidationMessage>Related mailer not found</ValidationMessage>
<multiple>N</multiple>
<Required>N</Required>
</linkedMailer>
<http2Enabled type="BooleanField">
<default>0</default>
<Required>N</Required>
@@ -2623,5 +2635,64 @@
</timeout_retry>
</resolver>
</resolvers>
<mailers>
<mailer 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>
<mailservers type="CSVListField">
<Required>Y</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 mailserver addresses, i.e. 192.168.1.1:25.</ValidationMessage>
</mailservers>
<sender type="EmailField">
<Required>Y</Required>
</sender>
<recipient type="EmailField">
<Required>Y</Required>
</recipient>
<loglevel type="OptionField">
<Required>Y</Required>
<default>alert</default>
<OptionValues>
<emerg>emerg</emerg>
<alert>alert</alert>
<crit>crit</crit>
<err>err</err>
<warning>warning</warning>
<notice>notice</notice>
<info>info</info>
<debug>debug</debug>
</OptionValues>
</loglevel>
<timeout type="TextField">
<default>30</default>
<MinimumValue>4</MinimumValue>
<MaximumValue>10000</MaximumValue>
<ValidationMessage>Please specify a value between 4 and 10000 seconds.</ValidationMessage>
<Required>Y</Required>
</timeout>
<hostname type="HostnameField">
<Required>N</Required>
</hostname>
</mailer>
</mailers>
</items>
</model>
@@ -21,6 +21,7 @@
<Errorfiles VisibleName="Error Files" url="/ui/haproxy#errorfiles"/>
<Mapfiles VisibleName="Map Files" url="/ui/haproxy#mapfiles"/>
<Resolvers VisibleName="Resolvers" url="/ui/haproxy#resolvers"/>
<Mailers VisibleName="E-Mail Alerts" url="/ui/haproxy#mailers"/>
</Settings>
<Statistics order="20" url="/ui/haproxy/statistics">
<Overview VisibleName="Overview" url="/ui/haproxy/statistics#info"/>
@@ -212,6 +212,19 @@ POSSIBILITY OF SUCH DAMAGE.
}
);
$("#grid-mailers").UIBootgrid(
{ search:'/api/haproxy/settings/searchMailers',
get:'/api/haproxy/settings/getMailer/',
set:'/api/haproxy/settings/setMailer/',
add:'/api/haproxy/settings/addMailer/',
del:'/api/haproxy/settings/delMailer/',
toggle:'/api/haproxy/settings/toggleMailer/',
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(){
@@ -608,6 +621,7 @@ POSSIBILITY OF SUCH DAMAGE.
<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>
<li><a data-toggle="tab" href="#mailers">{{ lang._('E-Mail Alerts') }}</a></li>
</ul>
</li>
</ul>
@@ -715,6 +729,7 @@ POSSIBILITY OF SUCH DAMAGE.
<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>
<li>{{ lang._("%sE-Mail Alerts:%s It is possible to send email alerts when the state of servers changes. Each configuration can be used in %sBackend Pools%s to send e-mail alerts to the configured recipient.") | 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. A detailed explanation of the resolvers feature can be found %shere%s.") | format('<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4-errorfile" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#lua-load" target="_blank">', '</a>', '<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#cpu-map" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#bind-process" target="_blank">', '</a>' ,'<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#process" target="_blank">', '</a>','<a href="http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#5.3.2" target="_blank">', '</a>') }}</p>
<br/>
@@ -1149,6 +1164,40 @@ POSSIBILITY OF SUCH DAMAGE.
</div>
</div>
<div id="mailers" class="tab-pane fade">
<table id="grid-mailers" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogMailer">
<thead>
<tr>
<th data-column-id="mailerid" data-type="number" data-visible="false">{{ lang._('Mailer 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="Sender" data-type="string">{{ lang._('Sender') }}</th>
<th data-column-id="Recipient" data-type="string">{{ lang._('Recipient') }}</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>
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureAct-mailers" type="button"><b>{{ lang._('Apply') }}</b><i id="reconfigureAct_progress" class=""></i></button>
<button class="btn btn-primary" id="configtestAct-mailers" type="button"><b>{{ lang._('Test syntax') }}</b><i id="configtestAct_progress" class=""></i></button>
<br/>
<br/>
</div>
</div>
<!-- subtabs for general "Settings" tab below -->
<div id="general-settings" class="tab-pane fade">
<div class="content-box" style="padding-bottom: 1.5em;">
@@ -1242,3 +1291,4 @@ POSSIBILITY OF SUCH DAMAGE.
{{ 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')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogMailer,'id':'DialogMailer','label':lang._('Edit E-Mail Alert')])}}
@@ -1049,6 +1049,29 @@ resolvers {{resolver.id}}
{% endfor %}
{%- endif -%}
{# ############################### #}
{# MAILERS #}
{# ############################### #}
{% if helpers.exists('OPNsense.HAProxy.mailers') %}
{% for mailer in helpers.toList('OPNsense.HAProxy.mailers.mailer') %}
{% if mailer.enabled == '1' %}
# Mailer: {{mailer.name}}
mailers {{mailer.id}}
timeout mail {{mailer.timeout}}s
{% if mailer.mailservers|default("") != "" %}
{% for mailserver in mailer.mailservers.split(",") %}
mailer {{mailserver}} {{mailserver}}
{% endfor %}
{% endif %}
{% else %}
# Mailer (DISABLED): {{mailer.name}}
{% endif %}
{% endfor %}
{%- endif -%}
{# ############################### #}
{# FRONTENDS #}
{# ############################### #}
@@ -1353,6 +1376,23 @@ backend {{backend.name}}
# health checking is DISABLED
{% set healthcheck_enabled = '0' %}
{% endif %}
{# # mailer #}
{% if backend.linkedMailer|default("") != "" %}
{% set mailer_data = helpers.getUUID(backend.linkedMailer) %}
{% if mailer_data == {} %}
# ERROR: mailer data not found ({{backend.linkedMailer}})
{% elif mailer_data.enabled == '0' %}
# NOTE: specified mailer is disabled ({{mailer_data.name}})
{% else %}
email-alert mailers {{mailer_data.id}}
email-alert from {{mailer_data.sender}}
email-alert to {{mailer_data.recipient}}
email-alert level {{mailer_data.loglevel}}
{% if mailer_data.hostname|default("") != "" %}
email-alert myhostname {{mailer_data.hostname}}
{% endif %}
{% endif %}
{% endif %}
{# # NOTE: Usually the frontend and the backend are in the same mode, #}
{# # but we have no way to know what frontend uses this backend. #}
{# # Hence we can't automatically set the mode and thus need a #}