summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2024-05-23 21:23:24 +0200
committerGitHub <noreply@github.com>2024-05-23 21:23:24 +0200
commit3e69d8bbe01b7d03c894e21ac322974799098676 (patch)
tree75ad19124b91c3e29142b40660a839c2fa17f8f9
parent5678e37fd0b37b266330cf2d1fde79071a96bf2b (diff)
parente1450096b4c667a4c33a3fcd8f67ebf6a39d441d (diff)
downloadvyos-1x-3e69d8bbe01b7d03c894e21ac322974799098676.tar.gz
vyos-1x-3e69d8bbe01b7d03c894e21ac322974799098676.zip
Merge pull request #3487 from Embezzle/T6370
reverse-proxy: T6370: Set custom HTTP headers in reverse-proxy responses
-rw-r--r--data/templates/load-balancing/haproxy.cfg.j210
-rw-r--r--interface-definitions/include/haproxy/http-response-headers.xml.i29
-rw-r--r--interface-definitions/load-balancing_reverse-proxy.xml.in2
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_reverse-proxy.py21
-rwxr-xr-xsrc/conf_mode/load-balancing_reverse-proxy.py6
5 files changed, 68 insertions, 0 deletions
diff --git a/data/templates/load-balancing/haproxy.cfg.j2 b/data/templates/load-balancing/haproxy.cfg.j2
index 7917c8257..797bf17e7 100644
--- a/data/templates/load-balancing/haproxy.cfg.j2
+++ b/data/templates/load-balancing/haproxy.cfg.j2
@@ -81,6 +81,11 @@ frontend {{ front }}
{% endif %}
{% endfor %}
{% endif %}
+{% if front_config.http_response_headers is vyos_defined %}
+{% for header, header_config in front_config.http_response_headers.items() %}
+ http-response set-header {{ header }} '{{ header_config['value'] }}'
+{% endfor %}
+{% endif %}
{% endif %}
{% if front_config.rule is vyos_defined %}
{% for rule, rule_config in front_config.rule.items() %}
@@ -158,6 +163,11 @@ backend {{ back }}
{% endif %}
{% if back_config.mode is vyos_defined %}
mode {{ back_config.mode }}
+{% if back_config.http_response_headers is vyos_defined %}
+{% for header, header_config in back_config.http_response_headers.items() %}
+ http-response set-header {{ header }} '{{ header_config['value'] }}'
+{% endfor %}
+{% endif %}
{% endif %}
{% if back_config.rule is vyos_defined %}
{% for rule, rule_config in back_config.rule.items() %}
diff --git a/interface-definitions/include/haproxy/http-response-headers.xml.i b/interface-definitions/include/haproxy/http-response-headers.xml.i
new file mode 100644
index 000000000..9e7ddfd28
--- /dev/null
+++ b/interface-definitions/include/haproxy/http-response-headers.xml.i
@@ -0,0 +1,29 @@
+<!-- include start from haproxy/http-response-headers.xml.i -->
+<tagNode name="http-response-headers">
+ <properties>
+ <help>Headers to include in HTTP response</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>HTTP header name</description>
+ </valueHelp>
+ <constraint>
+ <regex>[-a-zA-Z]+</regex>
+ </constraint>
+ <constraintErrorMessage>Header names must only include alphabetical characters and hyphens</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="value">
+ <properties>
+ <help>HTTP header value</help>
+ <valueHelp>
+ <format>txt</format>
+ <description>HTTP header value</description>
+ </valueHelp>
+ <constraint>
+ <regex>[[:ascii:]]{1,256}</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+</tagNode>
+<!-- include end -->
diff --git a/interface-definitions/load-balancing_reverse-proxy.xml.in b/interface-definitions/load-balancing_reverse-proxy.xml.in
index 6a3b3cef1..011e1b53c 100644
--- a/interface-definitions/load-balancing_reverse-proxy.xml.in
+++ b/interface-definitions/load-balancing_reverse-proxy.xml.in
@@ -39,6 +39,7 @@
#include <include/port-number.xml.i>
#include <include/haproxy/rule-frontend.xml.i>
#include <include/haproxy/tcp-request.xml.i>
+ #include <include/haproxy/http-response-headers.xml.i>
<leafNode name="redirect-http-to-https">
<properties>
<help>Redirect HTTP to HTTPS</help>
@@ -90,6 +91,7 @@
</leafNode>
#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>
diff --git a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
index c8b17316f..370a9276a 100755
--- a/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
+++ b/smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
@@ -385,5 +385,26 @@ class TestLoadBalancingReverseProxy(VyOSUnitTestSHIM.TestCase):
self.assertIn(f'mode {mode}', config)
self.assertIn(f'server {bk_name} {bk_server}:{bk_server_port}', config)
+ def test_07_lb_reverse_proxy_http_response_headers(self):
+ # Setup base
+ self.configure_pki()
+ self.base_config()
+
+ # Set example headers in both frontend and backend
+ self.cli_set(base_path + ['service', 'https_front', 'http-response-headers', 'Cache-Control', 'value', 'max-age=604800'])
+ self.cli_set(base_path + ['backend', 'bk-01', 'http-response-headers', 'Proxy-Backend-ID', 'value', 'bk-01'])
+ self.cli_commit()
+
+ # Test headers are present in generated configuration file
+ config = read_file(HAPROXY_CONF)
+ self.assertIn('http-response set-header Cache-Control \'max-age=604800\'', config)
+ self.assertIn('http-response set-header Proxy-Backend-ID \'bk-01\'', config)
+
+ # Test setting alongside modes other than http is blocked by validation conditions
+ self.cli_set(base_path + ['service', 'https_front', 'mode', 'tcp'])
+ with self.assertRaises(ConfigSessionError) as e:
+ self.cli_commit()
+
+
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 1569d8d71..a4efb1cd8 100755
--- a/src/conf_mode/load-balancing_reverse-proxy.py
+++ b/src/conf_mode/load-balancing_reverse-proxy.py
@@ -88,6 +88,12 @@ def verify(lb):
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}"')
+ # Check if http-response-headers are configured in any frontend/backend where mode != http
+ for group in ['service', 'backend']:
+ for config_name, config in lb[group].items():
+ if 'http_response_headers' in config and ('mode' not in config or config['mode'] != 'http'):
+ raise ConfigError(f'{group} {config_name} must be set to http mode to use http_response_headers!')
+
if 'ssl' in back_config:
if {'no_verify', 'ca_certificate'} <= set(back_config['ssl']):
raise ConfigError(f'backend {back} cannot have both ssl options no-verify and ca-certificate set!')