Skip to content

Commit

Permalink
reverse-proxy: T6370: Set custom HTTP headers in reverse-proxy responses
Browse files Browse the repository at this point in the history
  • Loading branch information
Embezzle committed May 21, 2024
1 parent bc34540 commit e145009
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 0 deletions.
10 changes: 10 additions & 0 deletions data/templates/load-balancing/haproxy.cfg.j2
Original file line number Diff line number Diff line change
Expand Up @@ -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() %}
Expand Down Expand Up @@ -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() %}
Expand Down
29 changes: 29 additions & 0 deletions interface-definitions/include/haproxy/http-response-headers.xml.i
Original file line number Diff line number Diff line change
@@ -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 -->
2 changes: 2 additions & 0 deletions interface-definitions/load-balancing_reverse-proxy.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
21 changes: 21 additions & 0 deletions smoketest/scripts/cli/test_load-balancing_reverse-proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,5 +385,26 @@ def test_06_lb_reverse_proxy_tcp_mode(self):
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)
6 changes: 6 additions & 0 deletions src/conf_mode/load-balancing_reverse-proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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!')
Expand Down

0 comments on commit e145009

Please sign in to comment.