Overview
This guide documents five hardening updates to the HAProxy edge configuration for on-premise AgileSec platforms. Each hardening control is enforced uniformly across all backend services from a single point as every client response passes through HAProxy. Together, these hardening updates reduce AgileSec’s browser-facing attack surface, eliminate server fingerprinting, and remove legacy TLS exposure.
|
Configuration |
Remediation |
|
|---|---|---|
|
1 |
Cross-Origin Resource Sharing (CORS) |
Configure CORS at the HAProxy layer to allow browser requests only from explicitly approved origins. |
|
2 |
HTTP Security Headers |
Configure HTTP Security Headers to protect from browser-based vulnerabilities with |
|
3 |
Content Security Policy (CSP) |
Configure CSP header to restrict which sources the browser is permitted to load resources from, protecting against cross-site scripting (XSS) and data injection attacks. |
|
4 |
Server Technology Disclosure |
Configure removal of server technology disclosing headers at the HAProxy layer to ensure they are stripped before reaching any client. |
|
5 |
TLS 1.3 Configuration Update |
Configure HAProxy SSL configuration to enforce TLS 1.3 exclusively and remove the TLS 1.2 attack surface entirely. |
Deployment port 8443 note: This guide uses both the standard HTTPS listener, bind :443 ssl ..., and examples without an explicit port. If HTTPS is exposed directly on port 8443, configure the client-facing HAProxy frontend with bind :8443 ssl ... and add :8443 to all validation URLs. For example: <https://<agilesec-fqdn>>:8443/health-check.
Note: If using non-privileged port 8443, do not use sudo with ./manage.sh.
Prerequisites
Ensure the following prerequisites are met.
Set Environment Variables
Set environment variables to easily copy and paste instructions.
-
agilesec_install_dir: the location where AgileSec will be installed to.
export agilesec_install_dir=</path/to/installation>
Backup Frontend HAProxy Configuration
Important: Make a backup of your frontend node HAProxy configuration before making any changes:
cp $agilesec_install_dir/services/haproxy/haproxy.cfg $agilesec_install_dir/services/haproxy/haproxy.cfg.bak
Cross-Origin Resource Sharing (CORS) Configuration
Configure CORS at the HAProxy layer to allow browser requests only from explicitly approved origins.
In $agilesec_install_dir/services/haproxy/haproxy.cfg, add the following directives inside frontend https_frontend_local, after the existing path ACL definitions and before the use_backend rules:
frontend https_frontend_local
bind :443 ssl ...
...
# CORS policy
# Origins must include scheme and optional port; do not include a trailing slash.
acl cors_allowed_origin hdr(Origin) -i https://<agilesec-fqdn>
acl cors_origin_present hdr(Origin) -m found
acl cors_preflight method OPTIONS
# Store an approved Origin for use in response headers.
http-request set-var(txn.cors_origin) hdr(Origin) if path_api !path_ingestion cors_allowed_origin
# Reject requests with a supplied but unapproved Origin.
# Requests without an Origin header, such as health checks and server-to-server
# traffic, are not affected.
http-request deny deny_status 403 if path_api !path_ingestion cors_origin_present !cors_allowed_origin
# Respond to approved browser preflight requests without forwarding them upstream.
http-request return status 204 \
hdr Access-Control-Allow-Origin "%[var(txn.cors_origin)]" \
hdr Access-Control-Allow-Credentials "true" \
hdr Access-Control-Allow-Methods "GET,HEAD,PUT,PATCH,POST,DELETE" \
hdr Access-Control-Allow-Headers "%[req.hdr(Access-Control-Request-Headers)]" \
hdr Vary "Origin, Access-Control-Request-Headers" \
if path_api !path_ingestion cors_preflight cors_allowed_origin
# Add CORS headers to approved cross-origin responses.
http-response set-header Access-Control-Allow-Origin "%[var(txn.cors_origin)]" if { var(txn.cors_origin) -m found }
http-response set-header Access-Control-Allow-Credentials "true" if { var(txn.cors_origin) -m found }
http-response add-header Vary "Origin" if { var(txn.cors_origin) -m found }
Note: Replace https://<agilesec-fqdn> with your actual external FQDN (e.g. https://agilesec.kf-agilesec.com). An FQDN consists of the protocol, hostname, and optional port. Do not add path components or trailing slashes.
Security requirement: Do not configure a wildcard (*) origin when Access-Control-Allow-Credentials is enabled. Use a restricted allowlist of exact origins.
-
Validate and reload HAProxy:
cd $agilesec_install_dir/services/haproxy
./haproxy -c -f haproxy.cfg
cd $agilesec_install_dir/scripts
sudo ./manage.sh restart haproxy
Note: HAProxy configuration validation must show no alerts before it is reloaded.
Note: If using non-privileged port 8443, do not use sudo with ./manage.sh.
-
Verify an approved preflight request:
curl -ik -X OPTIONS https://<agilesec-fqdn>/health-check \ -H 'Origin: https://<agilesec-fqdn>' \ -H 'Access-Control-Request-Method: GET' \ -H 'Access-Control-Request-Headers: Authorization,Content-Type'Expected response:
HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://<agilesec-fqdn> Access-Control-Allow-Credentials: true -
Repeat the preflight request with an unapproved
Origin. The expected result is HTTP403.curl -ik -X OPTIONS https://<agilesec-fqdn>/health-check \ -H 'Origin: https://example.invalid' \ -H 'Access-Control-Request-Method: GET' \ -H 'Access-Control-Request-Headers: Authorization,Content-Type'
Result: Approved origins receive the required CORS headers and preflight response. Requests with an unapproved supplied origin are rejected.
-
Verify an approved actual request:
curl -ik https://<agilesec-fqdn>/health-check \ -H 'Origin: https://<agilesec-fqdn>'Expected response:
HTTP 200 Access-Control-Allow-Origin: https://<agilesec-fqdn> Access-Control-Allow-Credentials: trueThere should be no wildcard
*. -
Repeat the actual request with an unapproved
Origin. The expected result is HTTP403.curl -ik https://<agilesec-fqdn>/health-check \ -H 'Origin: https://example.invalid'
Result: Approved origins receive the required CORS headers and preflight response. Requests with an unapproved supplied origin are rejected.
HTTP Security Headers Configuration
Configure HTTP Security Headers to protect from browser-based vulnerabilities with X-Frame-Options, X-Content-Type Options, and Strict-Transport-Security.
Add the following directives to the https_frontend_local section in $agilesec_install_dir/services/haproxy/haproxy.cfg on frontend nodes:
frontend https_frontend_local
bind :443 ssl ...
...
# Security headers
http-request set-var(txn.file_path) path
http-response set-header X-Frame-Options "SAMEORIGIN"
http-response set-header X-Content-Type-Options "nosniff"
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Force correct MIME type and download behavior for file download endpoints
http-response set-header Content-Type "application/zip" if { var(txn.file_path) -m sub /v1/file-upload/local/ }
http-response set-header Content-Disposition "attachment" if { var(txn.file_path) -m sub /v1/file-upload/local/ }
http-response set-header Content-Type "application/octet-stream" if { var(txn.file_path) -m sub /v1/platform-sensors/remote-sensor/ }
Note: These directives must be placed inside a frontend or listen section. HAProxy does not allow http-response rules in the defaults section. http-request directive must be added before the http-response directives
Validate and reload HAProxy:
cd $agilesec_install_dir/services/haproxy
./haproxy -c -f haproxy.cfg
cd $agilesec_install_dir/scripts
sudo ./manage.sh restart haproxy
Note: HAProxy config validation must show no alerts.
Note: If using non-privileged port 8443, do not use sudo with ./manage.sh.
Verify headers are present in the response:
curl -I https://<agilesec-fqdn>/health-check
Expected response headers:
x-frame-options: SAMEORIGIN
x-content-type-options: nosniff
strict-transport-security: max-age=31536000; includeSubDomains; preload
Result: X-Frame-Options, X-Content-Type-Options, and Strict-Transport-Security should appear in the response headers.
Content Security Policy (CSP) Configuration
A CSP header restricts which sources the browser is permitted to load resources from, protecting against cross-site scripting (XSS) and data injection attacks.
On frontend nodes, add the following http-response directives to the https_frontend_local section in $agilesec_install_dir/services/haproxy/haproxy.cfg, after the existing security headers:
frontend https_frontend_local
...
# Content Security Policy
http-response set-header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss://<agilesec-fqdn>;"
Note: Replace <agilesec-fqdn> with your actual external FQDN for WebSocket connections (e.g. wss://agilesec.kf-agilesec.com). The unsafe-inline and unsafe-eval directives are required to support the platform frontend framework.
Note: If using non-privileged port 8443, please use wss://agilesec.kf-agilesec.com:8443 for WebSocket connections.
Validate and reload HAProxy:
cd $agilesec_install_dir/services/haproxy
./haproxy -c -f haproxy.cfg
cd $agilesec_install_dir/scripts
sudo ./manage.sh restart haproxy
Note: HA proxy config validation must show no alerts.
Note: If using non-privileged port 8443, do not use sudo with ./manage.sh.
After reloading HAProxy, verify the CSP header is returned by inspecting the response from the sign-in page:
curl -I https://<agilesec-fqdn>/signin
Expected response header:
content-security-policy: default-src 'self'; script-src 'self' 'unsafe-inline'
'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;
connect-src 'self' wss://<agilesec-fqdn>;
Result: The content-security-policy header should appear in the response confirming the directive is applied at the HAProxy layer.
Server Technology Disclosure Configuration
By default, backend applications include HTTP response headers identifying server technology in use. Two headers are of particular concern:
-
x-powered-by: Expressorx-powered-by: Next.js– directly identifies the application framework -
server:– identifies the underlying web server software and version
Removing these headers at the HAProxy layer ensures they are stripped before reaching any client.
Locate the frontend section in your $agilesec_install_dir/services/haproxy/haproxy.cfg on frontend nodes handling client-facing traffic. Add the following two lines inside the frontend section section to strip server fingerprinting headers:
frontend https_frontend_local
bind *:443 ssl ...
...
# Strip server fingerprinting headers
http-response del-header x-powered-by
http-response del-header server
default_backend ...
Note: These directives must be placed inside a frontend or listen section, not in the defaults section.
Validate and reload HAProxy:
cd $agilesec_install_dir/services/haproxy
./haproxy -c -f haproxy.cfg
cd $agilesec_install_dir/scripts
sudo ./manage.sh restart haproxy
Verify the headers are removed by inspecting the response from health-check and signin URLs:
curl -I https://<agilesec-fqdn>/health-check
curl -I https://<agilesec-fqdn>/signin
Result: The x-powered-by and server headers should no longer appear in the responses. All other headers remain unaffected.
TLS 1.3 Configuration Update
Current HAProxy SSL configuration already disables SSLv3, TLS 1.0, and TLS 1.1. However, TLS 1.2 is still permitted for broader compatibility. To enforce TLS 1.3 exclusively and remove the TLS 1.2 attack surface entirely, make the following configuration updates to $agilesec_install_dir/services/haproxy/haproxy.cfgon frontend nodes:
-
Add
no-tlsv12to both thessl-default-bind-optionsandssl-default-server-optionslines. -
Remove the
ssl-default-bind-ciphersandssl-default-server-cipherslines – these are TLS 1.2 cipher lists and have no effect once TLS 1.2 is disabled.
global
...
# TLS 1.3 only – no-tlsv12 added, TLS 1.2 cipher lists removed
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
Note: Update ciphersuites as needed by your security policies.
Validate and reload HAProxy:
cd $agilesec_install_dir/services/haproxy
./haproxy -c -f haproxy.cfg
cd $agilesec_install_dir/scripts
sudo ./manage.sh restart haproxy
Note: HA proxy config validation must show no alerts.
Note: If using non-privileged port 8443, do not use sudo with ./manage.sh.
Verify TLS 1.3 is enforced and TLS 1.2 is rejected:
# Should succeed
openssl s_client -connect <agilesec-fqdn>:443 -tls1_3
# Should fail with handshake error
openssl s_client -connect <agilesec-fqdn>:443 -tls1_2
Result: The TLS 1.3 connection should complete successfully. The TLS 1.2 attempt should return an alert handshake failure, confirming the restriction is in place.
Note: If using non-privileged port 8443, add SNI to verification to avoid false failures in SNI/certificate-based deployments.
# Should succeed
openssl s_client -connect <agilesec-fqdn>:8443 -servername <agilesec-fqdn> -tls1_3
# Should fail with handshake error
openssl s_client -connect <agilesec-fqdn>:8443 -servername <agilesec-fqdn> -tls1_2