summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--data/templates/load-balancing/haproxy.cfg.j27
-rw-r--r--interface-definitions/include/version/reverseproxy-version.xml.i3
-rw-r--r--interface-definitions/load-balancing_reverse-proxy.xml.in44
-rw-r--r--interface-definitions/service_dns_forwarding.xml.in1
-rw-r--r--interface-definitions/system_conntrack.xml.in8
-rw-r--r--interface-definitions/xml-component-version.xml.in1
-rw-r--r--op-mode-definitions/mtr.xml.in4
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_reverse-proxy.py53
-rwxr-xr-xsmoketest/scripts/cli/test_service_dns_forwarding.py10
-rwxr-xr-xsrc/conf_mode/load-balancing_reverse-proxy.py13
-rwxr-xr-xsrc/conf_mode/protocols_bfd.py2
-rwxr-xr-xsrc/conf_mode/service_dns_forwarding.py15
-rwxr-xr-xsrc/migration-scripts/reverse-proxy/0-to-148
-rwxr-xr-xsrc/op_mode/snmp_v3.py3
15 files changed, 188 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index 44a6e35ed..3b26273d6 100644
--- a/Makefile
+++ b/Makefile
@@ -66,6 +66,7 @@ op_mode_definitions: $(op_xml_obj)
ln -s ../node.tag $(OP_TMPL_DIR)/ping/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/traceroute/node.tag/node.tag/
ln -s ../node.tag $(OP_TMPL_DIR)/mtr/node.tag/node.tag/
+ ln -s ../node.tag $(OP_TMPL_DIR)/monitor/traceroute/node.tag/node.tag/
# XXX: test if there are empty node.def files - this is not allowed as these
# could mask help strings or mandatory priority statements
diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index b786a58f8..c6027e09b 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -131,6 +131,13 @@ frontend {{ front }}
{% if backend is vyos_defined %}
{% for back, back_config in backend.items() %}
backend {{ back }}
+{% if back_config.health_check is vyos_defined %}
+{% if back_config.health_check == 'smtp' %}
+ option smtpchk
+{% else %}
+ option {{ back_config.health_check }}-check
+{% endif %}
+{% endif %}
{% if back_config.http_check is vyos_defined %}
option httpchk
{% endif %}
diff --git a/interface-definitions/include/version/reverseproxy-version.xml.i b/interface-definitions/include/version/reverseproxy-version.xml.i
new file mode 100644
index 000000000..907ea1e5e
--- /dev/null
+++ b/interface-definitions/include/version/reverseproxy-version.xml.i
@@ -0,0 +1,3 @@
+<!-- include start from include/version/reverseproxy-version.xml.i -->
+<syntaxVersion component='reverse-proxy' version='1'></syntaxVersion>
+<!-- include end -->
diff --git a/interface-definitions/load-balancing_reverse-proxy.xml.in b/interface-definitions/load-balancing_reverse-proxy.xml.in
index 011e1b53c..ce757a5d6 100644
--- a/interface-definitions/load-balancing_reverse-proxy.xml.in
+++ b/interface-definitions/load-balancing_reverse-proxy.xml.in
@@ -92,19 +92,6 @@
#include <include/generic-description.xml.i>
#include <include/haproxy/mode.xml.i>
#include <include/haproxy/http-response-headers.xml.i>
- <node name="parameters">
- <properties>
- <help>Backend parameters</help>
- </properties>
- <children>
- <leafNode name="http-check">
- <properties>
- <help>HTTP health check</help>
- <valueless/>
- </properties>
- </leafNode>
- </children>
- </node>
<node name="http-check">
<properties>
<help>HTTP check configuration</help>
@@ -164,6 +151,37 @@
</node>
</children>
</node>
+ <leafNode name="health-check">
+ <properties>
+ <help>Non HTTP health check options</help>
+ <completionHelp>
+ <list>ldap mysql pgsql redis smtp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ldap</format>
+ <description>LDAP protocol check</description>
+ </valueHelp>
+ <valueHelp>
+ <format>mysql</format>
+ <description>MySQL protocol check</description>
+ </valueHelp>
+ <valueHelp>
+ <format>pgsql</format>
+ <description>PostgreSQL protocol check</description>
+ </valueHelp>
+ <valueHelp>
+ <format>redis</format>
+ <description>Redis protocol check</description>
+ </valueHelp>
+ <valueHelp>
+ <format>smtp</format>
+ <description>SMTP protocol check</description>
+ </valueHelp>
+ <constraint>
+ <regex>(ldap|mysql|redis|pgsql|smtp)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
#include <include/haproxy/rule-backend.xml.i>
<tagNode name="server">
<properties>
diff --git a/interface-definitions/service_dns_forwarding.xml.in b/interface-definitions/service_dns_forwarding.xml.in
index b52b4bda3..5667028b7 100644
--- a/interface-definitions/service_dns_forwarding.xml.in
+++ b/interface-definitions/service_dns_forwarding.xml.in
@@ -311,6 +311,7 @@
<constraint>
<regex>[-_a-zA-Z0-9.]{1,63}(?&lt;!\.)</regex>
</constraint>
+ <multi/>
</properties>
</leafNode>
#include <include/dns/time-to-live.xml.i>
diff --git a/interface-definitions/system_conntrack.xml.in b/interface-definitions/system_conntrack.xml.in
index a348097cc..b97fbd80b 100644
--- a/interface-definitions/system_conntrack.xml.in
+++ b/interface-definitions/system_conntrack.xml.in
@@ -400,7 +400,7 @@
<constraint>
<validator name="numeric" argument="--range 1-999999"/>
</constraint>
- <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
+ <constraintErrorMessage>Timeout rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -415,7 +415,7 @@
</node>
<leafNode name="inbound-interface">
<properties>
- <help>Interface to ignore connections tracking on</help>
+ <help>Interface to apply custom connection timers on</help>
<completionHelp>
<list>any</list>
<script>${vyos_completion_dir}/list_interfaces</script>
@@ -458,7 +458,7 @@
<constraint>
<validator name="numeric" argument="--range 1-999999"/>
</constraint>
- <constraintErrorMessage>Ignore rule number must be between 1 and 999999</constraintErrorMessage>
+ <constraintErrorMessage>Timeout rule number must be between 1 and 999999</constraintErrorMessage>
</properties>
<children>
#include <include/generic-description.xml.i>
@@ -473,7 +473,7 @@
</node>
<leafNode name="inbound-interface">
<properties>
- <help>Interface to ignore connections tracking on</help>
+ <help>Interface to apply custom connection timers on</help>
<completionHelp>
<list>any</list>
<script>${vyos_completion_dir}/list_interfaces</script>
diff --git a/interface-definitions/xml-component-version.xml.in b/interface-definitions/xml-component-version.xml.in
index 51a28ef57..3785a7942 100644
--- a/interface-definitions/xml-component-version.xml.in
+++ b/interface-definitions/xml-component-version.xml.in
@@ -47,4 +47,5 @@
#include <include/version/vyos-accel-ppp-version.xml.i>
#include <include/version/wanloadbalance-version.xml.i>
#include <include/version/webproxy-version.xml.i>
+ #include <include/version/reverseproxy-version.xml.i>
</interfaceDefinition>
diff --git a/op-mode-definitions/mtr.xml.in b/op-mode-definitions/mtr.xml.in
index 8239aec4c..66729e2bc 100644
--- a/op-mode-definitions/mtr.xml.in
+++ b/op-mode-definitions/mtr.xml.in
@@ -13,7 +13,7 @@
<children>
<leafNode name="node.tag">
<properties>
- <help>mtr options</help>
+ <help>Traceroute options</help>
<completionHelp>
<script>${vyos_op_scripts_dir}/mtr.py --get-options-nested "${COMP_WORDS[@]}"</script>
</completionHelp>
@@ -35,7 +35,7 @@
<children>
<leafNode name="node.tag">
<properties>
- <help>Traceroute options</help>
+ <help>mtr options</help>
<completionHelp>
<script>${vyos_op_scripts_dir}/mtr.py --get-options "${COMP_WORDS[@]}"</script>
</completionHelp>
diff --git a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
index 2b2f93cdf..aa796f59f 100755
--- a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
@@ -338,6 +338,11 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn('http-check send meth GET uri /health', config)
self.assertIn('http-check expect string success', config)
+ # Test configuring both http-check & health-check fails validation script
+ self.cli_set(base_path + ['backend', 'bk-01', 'health-check', 'ldap'])
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+
def test_06_lb_reverse_proxy_tcp_mode(self):
frontend = 'tcp_8443'
mode = 'tcp'
@@ -405,6 +410,54 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
with self.assertRaises(ConfigSessionError) as e:
self.cli_commit()
+ def test_08_lb_reverse_proxy_tcp_health_checks(self):
+ # Setup PKI
+ self.configure_pki()
+
+ # Define variables
+ frontend = 'fe_ldaps'
+ mode = 'tcp'
+ health_check = 'ldap'
+ front_port = '636'
+ bk_name = 'bk_ldap'
+ bk_servers = ['192.0.2.11', '192.0.2.12']
+ bk_server_port = '389'
+
+ # Configure frontend
+ self.cli_set(base_path + ['service', frontend, 'mode', mode])
+ self.cli_set(base_path + ['service', frontend, 'port', front_port])
+ self.cli_set(base_path + ['service', frontend, 'ssl', 'certificate', 'smoketest'])
+
+ # Configure backend
+ self.cli_set(base_path + ['backend', bk_name, 'mode', mode])
+ self.cli_set(base_path + ['backend', bk_name, 'health-check', health_check])
+ for index, bk_server in enumerate(bk_servers):
+ self.cli_set(base_path + ['backend', bk_name, 'server', f'srv-{index}', 'address', bk_server])
+ self.cli_set(base_path + ['backend', bk_name, 'server', f'srv-{index}', 'port', bk_server_port])
+
+ # Commit & read config
+ self.cli_commit()
+ config = read_file(HAPROXY_CONF)
+
+ # Validate Frontend
+ self.assertIn(f'frontend {frontend}', config)
+ self.assertIn(f'bind [::]:{front_port} v4v6 ssl crt /run/haproxy/smoketest.pem', config)
+ self.assertIn(f'mode {mode}', config)
+ self.assertIn(f'backend {bk_name}', config)
+
+ # Validate Backend
+ self.assertIn(f'backend {bk_name}', config)
+ self.assertIn(f'option {health_check}-check', config)
+ self.assertIn(f'mode {mode}', config)
+ for index, bk_server in enumerate(bk_servers):
+ self.assertIn(f'server srv-{index} {bk_server}:{bk_server_port}', config)
+
+ # Validate SMTP option renders correctly
+ self.cli_set(base_path + ['backend', bk_name, 'health-check', 'smtp'])
+ self.cli_commit()
+ config = read_file(HAPROXY_CONF)
+ self.assertIn(f'option smtpchk', config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_service_dns_forwarding.py b/smoketest/scripts/cli/test_service_dns_forwarding.py
index 079c584ba..4db1d7495 100755
--- a/smoketest/scripts/cli/test_service_dns_forwarding.py
+++ b/smoketest/scripts/cli/test_service_dns_forwarding.py
@@ -291,5 +291,15 @@ class TestServicePowerDNS(VyOSUnitTestSHIM.TestCase):
tmp = get_config_value('edns-subnet-allow-list')
self.assertEqual(tmp, ','.join(options))
+ def test_multiple_ns_records(self):
+ test_zone = 'example.com'
+ self.cli_set(base_path + ['authoritative-domain', test_zone, 'records', 'ns', 'test', 'target', f'ns1.{test_zone}'])
+ self.cli_set(base_path + ['authoritative-domain', test_zone, 'records', 'ns', 'test', 'target', f'ns2.{test_zone}'])
+ self.cli_commit()
+ zone_config = read_file(f'{PDNS_REC_RUN_DIR}/zone.{test_zone}.conf')
+ self.assertRegex(zone_config, fr'test\s+\d+\s+NS\s+ns1\.{test_zone}\.')
+ self.assertRegex(zone_config, fr'test\s+\d+\s+NS\s+ns2\.{test_zone}\.')
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py
index 1c1252df0..09c68dadd 100755
--- a/src/conf_mode/load-balancing_reverse-proxy.py
+++ b/src/conf_mode/load-balancing_reverse-proxy.py
@@ -79,12 +79,21 @@ def verify(lb):
raise ConfigError(f'"TCP" port "{tmp_port}" is used by another service')
for back, back_config in lb['backend'].items():
- if 'http-check' in back_config:
- http_check = back_config['http-check']
+ if 'http_check' in back_config:
+ http_check = back_config['http_check']
if 'expect' in http_check and 'status' in http_check['expect'] and 'string' in http_check['expect']:
raise ConfigError(f'"expect status" and "expect string" can not be configured together!')
+
+ if 'health_check' in back_config:
+ if 'mode' not in back_config or back_config['mode'] != 'tcp':
+ raise ConfigError(f'backend "{back}" can only be configured with {back_config["health_check"]} ' +
+ f'health-check whilst in TCP mode!')
+ if 'http_check' in back_config:
+ raise ConfigError(f'backend "{back}" cannot be configured with both http-check and health-check!')
+
if 'server' not in back_config:
raise ConfigError(f'"{back} server" must be configured!')
+
for bk_server, bk_server_conf in back_config['server'].items():
if 'address' not in bk_server_conf or 'port' not in bk_server_conf:
raise ConfigError(f'"backend {back} server {bk_server} address and port" must be configured!')
diff --git a/src/conf_mode/protocols_bfd.py b/src/conf_mode/protocols_bfd.py
index 1c01a9013..1361bb1a9 100755
--- a/src/conf_mode/protocols_bfd.py
+++ b/src/conf_mode/protocols_bfd.py
@@ -49,7 +49,7 @@ def verify(bfd):
for peer, peer_config in bfd['peer'].items():
# IPv6 link local peers require an explicit local address/interface
if is_ipv6_link_local(peer):
- if 'source' not in peer_config or len(peer_config['source'] < 2):
+ if 'source' not in peer_config or len(peer_config['source']) < 2:
raise ConfigError('BFD IPv6 link-local peers require explicit local address and interface setting')
# IPv6 peers require an explicit local address
diff --git a/src/conf_mode/service_dns_forwarding.py b/src/conf_mode/service_dns_forwarding.py
index 7e863073a..70686534f 100755
--- a/src/conf_mode/service_dns_forwarding.py
+++ b/src/conf_mode/service_dns_forwarding.py
@@ -102,7 +102,7 @@ def get_config(config=None):
'ttl': rdata['ttl'],
'value': address
})
- elif rtype in ['cname', 'ptr', 'ns']:
+ elif rtype in ['cname', 'ptr']:
if not 'target' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: target is required')
continue
@@ -113,6 +113,19 @@ def get_config(config=None):
'ttl': rdata['ttl'],
'value': '{}.'.format(rdata['target'])
})
+ elif rtype == 'ns':
+ if not 'target' in rdata:
+ dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one target is required')
+ continue
+
+ for target in rdata['target']:
+ zone['records'].append({
+ 'name': subnode,
+ 'type': rtype.upper(),
+ 'ttl': rdata['ttl'],
+ 'value': f'{target}.'
+ })
+
elif rtype == 'mx':
if not 'server' in rdata:
dns['authoritative_zone_errors'].append(f'{subnode}.{node}: at least one server is required')
diff --git a/src/migration-scripts/reverse-proxy/0-to-1 b/src/migration-scripts/reverse-proxy/0-to-1
new file mode 100755
index 000000000..d61493815
--- /dev/null
+++ b/src/migration-scripts/reverse-proxy/0-to-1
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T6409: Remove unused 'backend bk-example parameters' node
+
+from sys import argv, exit
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+base = ['load-balancing', 'reverse-proxy', 'backend']
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+# we need to run this for every configured network
+for backend in config.list_nodes(base):
+ param_node = base + [backend, 'parameters']
+ if config.exists(param_node):
+ config.delete(param_node)
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1)
diff --git a/src/op_mode/snmp_v3.py b/src/op_mode/snmp_v3.py
index a1f76f0bc..abeb524dd 100755
--- a/src/op_mode/snmp_v3.py
+++ b/src/op_mode/snmp_v3.py
@@ -85,7 +85,7 @@ if __name__ == '__main__':
'user': [],
'view': []
}
-
+
if c.exists_effective('service snmp v3 group'):
for g in c.list_effective_nodes('service snmp v3 group'):
group = {
@@ -146,7 +146,6 @@ if __name__ == '__main__':
data['trap'].append(trap)
- print(data)
if args.all:
# Special case, print all templates !
tmpl = jinja2.Template(GROUP_OUTP_TMPL_SRC)