Skip to content

Commit

Permalink
haproxy: restore CIDR
Browse files Browse the repository at this point in the history
  • Loading branch information
msimerson committed Jan 21, 2025
1 parent 034eca1 commit 8cab8ff
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 37 deletions.
13 changes: 6 additions & 7 deletions config/connection.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@


[haproxy]
; Arrays: hosts that Haraka should enable the PROXY protocol from.
hosts_ipv4[] =
hosts_ipv4[] = 192.0.2.4
hosts_ipv4[] = 192.0.2.5
hosts_ipv6[] =
hosts_ipv6[] = [2001:db8::1]
hosts_ipv6[] = [2001:db8::2]
; Array: hosts or CIDRs that Haraka should enable the PROXY protocol from. See docs/HAProxy for format
hosts[] =
; hosts[] = 192.0.2.4
; hosts[] = 192.0.2.5
; hosts[] = [2001:db8::1]
; hosts[] = [2001:db8::2]


[headers]
Expand Down
19 changes: 17 additions & 2 deletions connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ const cfg = config.get('connection.ini', {
]
});

const haproxy_hosts_ipv4 = [];
const haproxy_hosts_ipv6 = [];

for (let ip of cfg.haproxy.hosts) {

Check failure on line 40 in connection.js

View workflow job for this annotation

GitHub Actions / lint / lint

'ip' is never reassigned. Use 'const' instead

Check failure on line 40 in connection.js

View workflow job for this annotation

GitHub Actions / lint / lint

'ip' is never reassigned. Use 'const' instead
if (!ip) continue;
if (net.isIPv6(ip.split('/')[0])) {
haproxy_hosts_ipv6.push([ipaddr.IPv6.parse(ip.split('/')[0]), parseInt(ip.split('/')[1] || 64)]);
}
else {
haproxy_hosts_ipv4.push([ipaddr.IPv4.parse(ip.split('/')[0]), parseInt(ip.split('/')[1] || 32)]);

Check warning on line 46 in connection.js

View check run for this annotation

Codecov / codecov/patch

connection.js#L42-L46

Added lines #L42 - L46 were not covered by tests
}
}

class Connection {
constructor (client, server, smtp_cfg) {
this.client = client;
Expand Down Expand Up @@ -178,8 +191,10 @@ class Connection {
self.process_data(data);
});

const ha_list = net.isIPv6(self.remote.ip) ? cfg.haproxy.hosts_ipv6 : cfg.haproxy.hosts_ipv4;
if (ha_list.includes(self.remote.ip)) {
const ha_list = net.isIPv6(self.remote.ip) ? haproxy_hosts_ipv6 : haproxy_hosts_ipv4;
if (ha_list.some((element, index, array) => {
return ipaddr.parse(self.remote.ip).match(element[0], element[1]);
})) {
self.proxy.allowed = true;
// Wait for PROXY command
self.proxy.timer = setTimeout(() => {
Expand Down
38 changes: 10 additions & 28 deletions docs/HAProxy.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
HAProxy PROXY protocol extension support
========================================
# HAProxy PROXY protocol extension support

Haraka natively supports the PROXY protocol [1].
Haraka supports PROXY protocol [1].

This allows an upstream proxy to pass IP address and port of the client which
Haraka will use instead of the socket IP address (which is of the proxy).
This allows DNSBLs and access control lists to operate on the proxied address.
This allows an upstream proxy to pass the IP address and port of the remote client. Haraka will use the remote IP instead of the socket IP address (which is the proxy). This allows DNSBLs and access control lists to use the correct source address.

Support is disabled by default and if HAProxy or other attempts to send a
PROXY command then Haraka will return a DENYSOFTDISCONNECT error.
DENYSOFT is used to prevent configuration errors from rejecting valid mail.
Support is disabled by default. Attempts to send a PROXY command will return a DENYSOFTDISCONNECT error. DENYSOFT is used to prevent configuration errors from rejecting valid mail.

To enable support for PROXY you must create a `haproxy_hosts` configuration
file which should contain a list of IP addresses of the HAProxy hosts
that should be allowed to send the PROXY command. A range of IP
addresses can be specified by it's CIDR network address.
To enable support for PROXY you must populate connection.ini[haproxy]hosts[] with the IP addresses of the HAProxy hosts that MUST send the PROXY command. Ranges can be specified with CIDR notation.

When a host connects to Haraka that matches an IP address present in the
`haproxy_hosts` file - a banner is not sent, instead Haraka waits for the
PROXY command to be sent before proceeding. The connection will timeout
with `421 PROXY timed out` if the command is not sent within 30 seconds.
When a proxy host connects to Haraka, a banner is not sent. Instead Haraka awaits the PROXY command. The connection will timeout with `421 PROXY timed out` if the command is not sent within 30 seconds.

NOTE: because Haraka does not send a banner when a listed HAProxy host
connects you must set check-send-proxy to ensure that the service checks
send a PROXY command before they run.
NOTE: because Haraka does not send a banner when a listed HAProxy host connects you must set check-send-proxy to ensure that the service checks send a PROXY command before they run.

[1] http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt

HAProxy supports the PROXY protocol in version 1.5 or later however there
are patches available to add support for 1.4.
HAProxy supports the PROXY protocol in version 1.5 or later.

Here is an example listener section for haproxy.cfg:

Expand All @@ -45,13 +31,9 @@ listen smtp :25
server smtp5 ip.of.haraka.server5:25 check-send-proxy check inter 10s send-proxy
```

The important part is `send-proxy` which causes HAProxy to send the PROXY
extension on connection.
The important part is `send-proxy` which causes HAProxy to send the PROXY extension on connection.

When using `option smtpchk` you will see CONNRESET errors reported in the Haraka logs as
smtpchk drops the connection before the HELO response is still being written.
You can use the `option tcp-check` instead to provide a better service check by having
the check wait for the banner, send QUIT and then check the response:
When using `option smtpchk` you will see CONNRESET errors reported in the Haraka logs as smtpchk drops the connection before the HELO response is still being written. You can use the `option tcp-check` instead to provide a better service check by having the check wait for the banner, send QUIT and then check the response:

```
option tcp-check
Expand Down

0 comments on commit 8cab8ff

Please sign in to comment.