From b6fe07ecf810ede06cd5007396fd5286937e6616 Mon Sep 17 00:00:00 2001 From: James Gibson Date: Fri, 19 Nov 2021 10:31:29 +0000 Subject: [PATCH] Add security headers to HAProxy Horizon service Security headers are HTTP response headers, that when set increase the security of your application by restricting modern browsers from running easily preventable vulnerabilities. You can inspect your site using https://securityheaders.com/ This patch implements the following headers: - strict-transport-security - HSTS enforces the use of HTTPS - x-content-type-options - Stops the browser from changing the Content-Type - referrer-policy - Control what information a browser includes when it navigates from a page - content-security-policy - CSP protects sites from XSS attacks by controlling what resources a browser is able to load Only enabled if HTTPS in use. There is the option to extend to all haproxy services in the future, but as the headers are only used by browser there maybe limited benefit to doing this other than for keystone and console services. Each of the headers set should have no effect on the operation of the site apart from the CSP header. As the CSP header restricts what resources a browser is allowed to load, if for example a Openstack instance is using federated login, CSP will block the redirect. To fix the the admin will need to override the CSP, using `haproxy_horizon_csp` to set the allowed list of resources. Depends-On: https://review.opendev.org/c/openstack/openstack-ansible-lxc_hosts/+/818532 Change-Id: Ia99da8e4687b0a1d440f86d1c8be723ce2bfe061 --- inventory/group_vars/haproxy/haproxy.yml | 12 ++++++++++++ .../security-headers-87de60203899fdbb.yaml | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 releasenotes/notes/security-headers-87de60203899fdbb.yaml diff --git a/inventory/group_vars/haproxy/haproxy.yml b/inventory/group_vars/haproxy/haproxy.yml index f128e63fca..504b420950 100644 --- a/inventory/group_vars/haproxy/haproxy.yml +++ b/inventory/group_vars/haproxy/haproxy.yml @@ -37,6 +37,17 @@ haproxy_security_txt_acl: rule: "path_end /security.txt" backend_name: keystone_service +# Variables to set security headers used by browsers +haproxy_security_headers_max_age: 31536000 +# To override the CSP used by a specific service define a variable haproxy__csp +haproxy_security_headers_csp: "http-response set-header Content-Security-Policy \"default-src 'self'; frame-ancestors 'none'; form-action 'self'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; child-src 'self' {{ external_lb_vip_address }}:{{ nova_console_port }}; frame-src 'self' {{ external_lb_vip_address }}:{{ nova_console_port }};\"" +# To disable security headers set to [] +haproxy_security_headers: + - "http-response set-header Strict-Transport-Security \"max-age={{ haproxy_security_headers_max_age }}; includeSubDomains;\"" + - 'http-response set-header X-Content-Type-Options "nosniff"' + - 'http-response set-header Referrer-Policy "same-origin"' + - 'http-response set-header Permissions-Policy "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), cross-origin-isolated=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), navigation-override=(self), payment=(), picture-in-picture=(), publickey-credentials-get=(), screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(), xr-spatial-tracking=(), clipboard-read=(self), clipboard-write=(self), gamepad=(), speaker-selection=()"' + haproxy_adjutant_api_service: haproxy_service_name: adjutant_api haproxy_backend_nodes: "{{ groups['adjutant_api'] | default([]) }}" @@ -197,6 +208,7 @@ haproxy_horizon_service: haproxy_redirect_scheme: "{{ (haproxy_ssl_letsencrypt_enable | bool and haproxy_ssl | bool) | ternary('https if !{ ssl_fc } !{ path_beg /.well-known/acme-challenge/ }', 'https if !{ ssl_fc }') }}" haproxy_frontend_acls: "{{ (haproxy_ssl_letsencrypt_enable | bool and haproxy_ssl | bool) | ternary(haproxy_ssl_letsencrypt_acl, {}) }}" haproxy_acls: "{{ keystone_security_txt_content is defined | ternary(haproxy_security_txt_acl, {}) }}" + haproxy_raw: "{{ (haproxy_ssl | bool and haproxy_security_headers is defined) | ternary( haproxy_security_headers + [ haproxy_horizon_csp | default(haproxy_security_headers_csp)], []) }}" haproxy_ironic_api_service: haproxy_service_name: ironic_api diff --git a/releasenotes/notes/security-headers-87de60203899fdbb.yaml b/releasenotes/notes/security-headers-87de60203899fdbb.yaml new file mode 100644 index 0000000000..ebf55a3ee0 --- /dev/null +++ b/releasenotes/notes/security-headers-87de60203899fdbb.yaml @@ -0,0 +1,19 @@ +--- +security: + - | + The following security headers were added to the haproxy Horizon service: + `strict-transport-security`, `x-content-type-options`, `referrer-policy` + and `content-security-policy`. + Care should be taken when deploying the `strict-transport-security` header, + as this header implements Trust on First Use security, meaning that + after a browser first visits the page the browser will enforce the use of + HTTPS until the max age time has expired. + For the time being the `strict-transport-security` `preload` token which + indicates that you are happy to have your site included in the HSTS preload + list that is built into browsers has been excluded. + The headers can be disabled by setting `haproxy_security_headers: []` and + the CSP (Content Security Policy) for Horizon can be overridden to support + things like federated login by setting `haproxy_horizon_csp`. + There is the option to extend to all haproxy services in the future, but as + the headers are only used by browsers there maybe limited benefit to doing + this other than for keystone and console services.