Proxy
In some strict environments, Internet access should be done via a proxy server.
DNF proxy
DNF is the package manager used by Red Hat-based Linux distributions.
To configure the back-end to use a configured proxy, follow these steps:
Create and Ansible override file at /etc/ciphermail/ansible/group_vars/all/override.yml.
Insert the required lines into your configuration, then replace each placeholder with the actual values for your environment (for example, hostnames, IP addresses, service names, usernames, or passwords).
# the proxy configuration for dnf
ciphermail_appliance__dnf_proxy_host: '<scheme>://<ip-or-hostname>[:port]'
ciphermail_appliance__dnf_proxy_username: ''
ciphermail_appliance__dnf_proxy_password: ''
After creating the override file, you need to run the playbook to apply the changes:
sudo cm-run-playbook
Back-end proxy
To configure the back-end to use a configured proxy, follow these steps:
Create an Ansible override file at /etc/ciphermail/ansible/group_vars/all/override.yml.
Insert the required lines into your configuration, then replace each placeholder with the actual values for your environment.
# the http proxy host config for the back-ends
ciphermail_common__http_proxy_host: ''
ciphermail_common__http_proxy_port: 80
ciphermail_common__https_proxy_host: ''
ciphermail_common__https_proxy_port: 443
After creating the override file, you need to run the playbook to apply the changes:
sudo cm-run-playbook
Tip
The back-end does not support authenticated proxies because Java does not
provide a standardized method for proxy authentication. If your environment
requires proxy authentication, please install and configure the tinyproxy
package on your local machine. tinyproxy will act as a local forwarding
proxy to handle authentication. For detailed setup instructions, please
contact us.
Behind a proxy
If the gateway is configured behind a proxy, for example a load balancer or a reverse proxy like HAProxy, the gateway by default sees the IP address of the proxy and not the real IP address of the client.
The real client IP address is passed on by the proxy in the X-Forwarded-For
HTTP header. Because any client can set this header itself, and thereby spoof
its IP address, X-Forwarded-For is stripped by default. It should therefore
be configured which IP addresses are trusted to set the header.
The client IP address is used for the audit log, for restricting an admin account to specific IP addresses and for rate limiting. If the gateway sees the address of the proxy instead of the address of the client, all clients behind that proxy are treated as one.
Configure the trusted proxies
To add a range of trusted IP addresses, create an Ansible override file at
/etc/ciphermail/ansible/group_vars/all/override.yml.
# the IP addresses or ranges of the proxy servers which are allowed to
# set the X-Forwarded-For header
ciphermail_ui__trusted_proxy_ips:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
Replace the IP ranges by the ranges used by the forwarding proxy server.
After creating the override file, you need to run the playbook to apply the changes:
sudo cm-run-playbook
If the connecting IP address comes from a trusted IP address, the
X-Forwarded-For header will not be stripped and the gateway will use the
client IP address from the header.
Warning
Every address in ciphermail_ui__trusted_proxy_ips is trusted to claim
any client IP address it likes. Only add the addresses of the proxy servers
themselves and prefer the exact addresses of the proxies over a complete
RFC 1918 range.
Note
The address which must be trusted is the address the gateway sees the proxy connect from, which is not always the address the proxy is configured with. If the proxy connects over IPv6, or if the connection is translated (NAT), the address will be different from what is expected. The section Testing the proxy configuration below shows how to determine the address.
Forwarded header handling
CipherMail sets the Spring Boot property server.forward-headers-strategy to
native, which means that the handling of the forwarded headers is delegated
to Tomcat.
Tomcat only accepts a X-Forwarded-For header from addresses which match
server.tomcat.remoteip.internal-proxies, and it also ignores any address in
the header itself which matches. The default value covers the RFC 1918 private
ranges (10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16), loopback,
link-local (169.254.0.0/16), the carrier-grade NAT range 100.64.0.0/10
and the IPv6 unique-local and link-local ranges.
The web server resolves the client address itself before the request is passed on, and it connects to the back-end over loopback, which is covered by the default value. This property therefore normally does not need to be changed, also not when the proxy has a public IP address: the address of the proxy is already replaced by the address of the client before the request reaches the back-end.
Warning
If server.tomcat.remoteip.internal-proxies is changed anyway, note that
the value replaces the default instead of extending it, so the loopback
range must always be included. When loopback is not trusted the
X-Forwarded-For header is ignored completely and the back-end reports
127.0.0.1 for every request.
The property is a Java regular expression and can be set by adding it to the system properties of the back-end in the same Ansible override file:
ciphermail_backend__user_defined_system_properties:
- 'server.tomcat.remoteip.internal-proxies=127[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}'
Tip
Write the regular expression with [.] instead of \. and with
[0-9] instead of \d, as in the example above. The system properties
end up in a systemd unit file, which processes backslash escape sequences of
its own. Using character classes avoids the backslashes entirely.
The portal and the company portal run as separate back-ends and have their own
variables, ciphermail_portal_backend__user_defined_system_properties and
ciphermail_company_portal_backend__user_defined_system_properties.
Testing the proxy configuration
First determine the address the gateway sees the proxy connect from. Every
request is logged by the web server with two addresses: the first field is the
client address and the peer field at the end is the address the connection
was actually made from.
sudo journalctl -t httpd --since '10 minutes ago' | tail
The peer field is the address which must be covered by
ciphermail_ui__trusted_proxy_ips. If it is not, the header is ignored and
stripped, and the client address will never reach the back-end.
203.0.113.99 - - [05/Aug/2026:10:12:31 +0200] "GET /api/... HTTP/1.1" 200 41 "-" "curl/8.0" peer=10.0.0.5
In the example above the proxy at 10.0.0.5 is trusted, so the client address
203.0.113.99 was taken from X-Forwarded-For. When the two fields are the
same, the header was not used: either the request was not forwarded, or the
address in peer is not a trusted proxy.
Next, check that a forwarded address reaches the back-end. Run the following from the proxy server, so the request has the same source address as a real forwarded request:
curl -k -H 'X-Forwarded-For: 203.0.113.99' https://example.com:8443/api/auth/getOAuth2Clients
Then inspect the audit log on the gateway:
sudo journalctl -t ciphermail-backend-audit --since '5 minutes ago' | grep API
The logged request contains the address which the back-end resolved:
[API] Method: GET, Path: /api/auth/getOAuth2Clients, IP: 203.0.113.99, User: anonymousUser, Params: , Status: 200
Compare the result against the address which was sent:
IP: 203.0.113.99 means the whole chain works.
Repeat the request without the X-Forwarded-For header to confirm the
difference, and run it once from a machine which is not a trusted proxy to
confirm that a spoofed header is ignored there.
For the portal use port 443 and for the company portal port 8444,
instead of the administration port 8443 used above.