summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2023-05-19 20:51:58 +0200
committerGitHub <noreply@github.com>2023-05-19 20:51:58 +0200
commit63380de9b57e2ad6f2634ce967e16275f418f186 (patch)
tree645c7f26b72aa75bbe8851911890e0ae95845249
parenta66648596dc126b7bed37d8119ee8faa14909613 (diff)
parente9dce894eec252ae9224257a26f23c0b70fba4fd (diff)
downloadvyos-1x-63380de9b57e2ad6f2634ce967e16275f418f186.tar.gz
vyos-1x-63380de9b57e2ad6f2634ce967e16275f418f186.zip
Merge pull request #2013 from sever-sever/T5222
T5222: reverse-proxy fix listen-address template and add smoketest
-rw-r--r--data/templates/load-balancing/haproxy.cfg.j210
-rw-r--r--interface-definitions/load-balancing-haproxy.xml.in12
-rwxr-xr-xsmoketest/scripts/cli/test_load_balancing_reverse_proxy.py112
-rwxr-xr-xsrc/conf_mode/load-balancing-haproxy.py2
4 files changed, 134 insertions, 2 deletions
diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index 1a8ce13f8..f8e1587f8 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -51,7 +51,13 @@ defaults
{% for front, front_config in service.items() %}
frontend {{ front }}
{% set ssl_front = 'ssl crt /run/haproxy/' ~ front_config.ssl.certificate ~ '.pem' if front_config.ssl.certificate is vyos_defined else '' %}
- bind {{ front_config.listen_address if front_config.listen_address if vyos_defined else '*' }}:{{ front_config.port }} {{ ssl_front }}
+{% if front_config.listen_address is vyos_defined %}
+{% for address in front_config.listen_address %}
+ bind {{ address | bracketize_ipv6 }}:{{ front_config.port }} {{ ssl_front }}
+{% endfor %}
+{% else %}
+ bind :::{{ front_config.port }} v4v6 {{ ssl_front }}
+{% endif %}
{% if front_config.redirect_http_to_https is vyos_defined %}
http-request redirect scheme https unless { ssl_fc }
{% endif %}
@@ -140,7 +146,7 @@ backend {{ back }}
{% if back_config.server is vyos_defined %}
{% set ssl_back = 'ssl ca-file /run/haproxy/' ~ back_config.ssl.ca_certificate ~ '.pem' if back_config.ssl.ca_certificate is vyos_defined else '' %}
{% for server, server_config in back_config.server.items() %}
- server {{ server }} {{ server_config.address }}:{{ server_config.port }} {{ 'check' if server_config.check is vyos_defined }} {{ ssl_back }}
+ server {{ server }} {{ server_config.address }}:{{ server_config.port }}{{ ' check' if server_config.check is vyos_defined }}{{ ' send-proxy' if server_config.send_proxy is vyos_defined }}{{ ' send-proxy-v2' if server_config.send_proxy_v2 is vyos_defined }} {{ ssl_back }}
{% endfor %}
{% endif %}
{% if back_config.timeout.check is vyos_defined %}
diff --git a/interface-definitions/load-balancing-haproxy.xml.in b/interface-definitions/load-balancing-haproxy.xml.in
index e295dcb63..f955a2fb7 100644
--- a/interface-definitions/load-balancing-haproxy.xml.in
+++ b/interface-definitions/load-balancing-haproxy.xml.in
@@ -131,6 +131,18 @@
</properties>
</leafNode>
#include <include/port-number.xml.i>
+ <leafNode name="send-proxy">
+ <properties>
+ <help>Send a Proxy Protocol version 1 header (text format)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="send-proxy-v2">
+ <properties>
+ <help>Send a Proxy Protocol version 2 header (binary format)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
</children>
</tagNode>
<node name="ssl">
diff --git a/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py b/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
new file mode 100755
index 000000000..23a681321
--- /dev/null
+++ b/smoketest/scripts/cli/test_load_balancing_reverse_proxy.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.util import process_named_running
+from vyos.util import read_file
+
+PROCESS_NAME = 'haproxy'
+HAPROXY_CONF = '/run/haproxy/haproxy.cfg'
+base_path = ['load-balancing', 'reverse-proxy']
+proxy_interface = 'eth1'
+
+
+class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ self.cli_delete(['interfaces', 'ethernet', proxy_interface, 'address'])
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Process must be terminated after deleting the config
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_lb_reverse_proxy_domain(self):
+ domains_bk_first = ['n1.example.com', 'n2.example.com', 'n3.example.com']
+ domain_bk_second = 'n5.example.com'
+ frontend = 'https_front'
+ front_port = '4433'
+ bk_server_first = '192.0.2.11'
+ bk_server_second = '192.0.2.12'
+ bk_first_name = 'bk-01'
+ bk_second_name = 'bk-02'
+ bk_server_port = '9090'
+ mode = 'http'
+ rule_ten = '10'
+ rule_twenty = '20'
+ send_proxy = 'send-proxy'
+ max_connections = '1000'
+
+ back_base = base_path + ['backend']
+
+ self.cli_set(base_path + ['service', frontend, 'mode', mode])
+ self.cli_set(base_path + ['service', frontend, 'port', front_port])
+ for domain in domains_bk_first:
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_ten, 'domain-name', domain])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_ten, 'set', 'backend', bk_first_name])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_twenty, 'domain-name', domain_bk_second])
+ self.cli_set(base_path + ['service', frontend, 'rule', rule_twenty, 'set', 'backend', bk_second_name])
+
+ self.cli_set(back_base + [bk_first_name, 'mode', mode])
+ self.cli_set(back_base + [bk_first_name, 'server', bk_first_name, 'address', bk_server_first])
+ self.cli_set(back_base + [bk_first_name, 'server', bk_first_name, 'port', bk_server_port])
+ self.cli_set(back_base + [bk_first_name, 'server', bk_first_name, send_proxy])
+
+ self.cli_set(back_base + [bk_second_name, 'mode', mode])
+ self.cli_set(back_base + [bk_second_name, 'server', bk_second_name, 'address', bk_server_second])
+ self.cli_set(back_base + [bk_second_name, 'server', bk_second_name, 'port', bk_server_port])
+
+ self.cli_set(base_path + ['global-parameters', 'max-connections', max_connections])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(HAPROXY_CONF)
+
+ # Global
+ self.assertIn(f'maxconn {max_connections}', config)
+
+ # Frontend
+ self.assertIn(f'frontend {frontend}', config)
+ self.assertIn(f'bind :::{front_port} v4v6', config)
+ self.assertIn(f'mode {mode}', config)
+ for domain in domains_bk_first:
+ self.assertIn(f'acl {rule_ten} hdr(host) -i {domain}', config)
+ self.assertIn(f'use_backend {bk_first_name} if {rule_ten}', config)
+ self.assertIn(f'acl {rule_twenty} hdr(host) -i {domain_bk_second}', config)
+ self.assertIn(f'use_backend {bk_second_name} if {rule_twenty}', config)
+
+ # Backend
+ self.assertIn(f'backend {bk_first_name}', config)
+ self.assertIn(f'balance roundrobin', config)
+ self.assertIn(f'option forwardfor', config)
+ self.assertIn('http-request add-header X-Forwarded-Proto https if { ssl_fc }', config)
+ self.assertIn(f'mode {mode}', config)
+ self.assertIn(f'server {bk_first_name} {bk_server_first}:{bk_server_port} send-proxy', config)
+
+ self.assertIn(f'backend {bk_second_name}', config)
+ self.assertIn(f'mode {mode}', config)
+ self.assertIn(f'server {bk_second_name} {bk_server_second}:{bk_server_port}', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/src/conf_mode/load-balancing-haproxy.py b/src/conf_mode/load-balancing-haproxy.py
index 938af6cda..b29fdffc7 100755
--- a/src/conf_mode/load-balancing-haproxy.py
+++ b/src/conf_mode/load-balancing-haproxy.py
@@ -95,6 +95,8 @@ def verify(lb):
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!')
+ if {'send_proxy', 'send_proxy_v2'} <= set(bk_server_conf):
+ raise ConfigError(f'Cannot use both "send-proxy" and "send-proxy-v2" for server "{bk_server}"')
def generate(lb):
if not lb: