Browse Source
This is backwards compatible with what is already used internally @ungleich, but adds on top of that the ability to customise ports and, most importantly, it adds PROXY protocol support.haproxy-dualstack
9 changed files with 322 additions and 0 deletions
@ -0,0 +1,8 @@
|
||||
frontend http |
||||
bind BIND@:80 |
||||
mode http |
||||
option httplog |
||||
default_backend http |
||||
|
||||
backend http |
||||
mode http |
@ -0,0 +1,10 @@
|
||||
frontend https |
||||
bind BIND@:443 |
||||
mode tcp |
||||
option tcplog |
||||
tcp-request inspect-delay 5s |
||||
tcp-request content accept if { req_ssl_hello_type 1 } |
||||
default_backend https |
||||
|
||||
backend https |
||||
mode tcp |
@ -0,0 +1,12 @@
|
||||
frontend imaps |
||||
bind BIND@:143 |
||||
bind BIND@:993 |
||||
|
||||
mode tcp |
||||
option tcplog |
||||
tcp-request inspect-delay 5s |
||||
tcp-request content accept if { req_ssl_hello_type 1 } |
||||
default_backend imaps |
||||
|
||||
backend imaps |
||||
mode tcp |
@ -0,0 +1,12 @@
|
||||
frontend smtps |
||||
bind BIND@:25 |
||||
bind BIND@:465 |
||||
|
||||
mode tcp |
||||
option tcplog |
||||
tcp-request inspect-delay 5s |
||||
tcp-request content accept if { req_ssl_hello_type 1 } |
||||
default_backend smtps |
||||
|
||||
backend smtps |
||||
mode tcp |
@ -0,0 +1,121 @@
|
||||
cdist-type__haproxy_dualstack(7) |
||||
================================ |
||||
|
||||
|
||||
NAME |
||||
---- |
||||
cdist-type__haproxy_dualstack - Proxy services from a dual-stack server |
||||
|
||||
|
||||
DESCRIPTION |
||||
----------- |
||||
This (singleton) type installs and configures haproxy to act as a dual-stack |
||||
proxy for single-stack services. |
||||
|
||||
This can be useful to add IPv4 support to IPv6-only services while only using |
||||
one IPv4 for many such services. |
||||
|
||||
By default this type uses the plain TCP proxy mode, which means that there is no |
||||
need for TLS termination on this host when SNI is supported. |
||||
This also means that proxied services will not receive the client's IP address, |
||||
but will see the proxy's IP address instead (that of `$__target_host`). |
||||
|
||||
This can be solved by using the PROXY protocol, but do take into account that, |
||||
e.g. nginx cannot serve both regular HTTP(S) and PROXY protocols on the same |
||||
port, so you will need to use other ports for that. |
||||
|
||||
As a recommendation in this type: use TCP ports 8080 and 591 respectively to |
||||
serve HTTP and HTTPS using the PROXY protocol. |
||||
|
||||
See the EXAMPLES for more details. |
||||
|
||||
|
||||
OPTIONAL PARAMETERS |
||||
------------------- |
||||
v4proxy |
||||
Proxy incoming IPv4 connections to the equivalent IPv6 endpoint. |
||||
In its simplest use, it must be a NAME with an `AAAA` DNS entry, which is |
||||
the IP address actually providing the proxied services. |
||||
The full format of this argument is: |
||||
`[proxy:]NAME[[:PROTOCOL_1=PORT_1]...[:PROTOCOL_N=PORT_N]]` |
||||
Where starting with `proxy:` determines that the PROXY protocol must be |
||||
used and each `:PROTOCOL=PORT` (e.g. `:http=8080` or `:https=591`) is a PORT |
||||
override for the given PROTOCOL (see `--protocol`), if not present the |
||||
PROTOCOL's default port will be used. |
||||
|
||||
|
||||
v6proxy |
||||
Proxy incoming IPv6 connections to the equivalent IPv4 endpoint. |
||||
In its simplest use, it must be a NAME with an `A` DNS entry, which is |
||||
the IP address actually providing the proxied services. |
||||
See `--v4proxy` for more options and details. |
||||
|
||||
protocol |
||||
Can be passed multiple times or as a space-separated list of protocols. |
||||
Currently supported protocols are: `http`, `https`, `imaps`, `smtps`. |
||||
This defaults to: `http https imaps smtps`. |
||||
|
||||
|
||||
EXAMPLES |
||||
-------- |
||||
|
||||
.. code-block:: sh |
||||
|
||||
# Proxy the IPv6-only services so IPv4-only clients can access them |
||||
# This uses HAProxy's TCP mode for http, https, imaps and smtps |
||||
__haproxy_dualstack \ |
||||
--v4proxy ipv6.chat \ |
||||
--v4proxy matrix.ungleich.ch |
||||
|
||||
# Proxy the IPv6-only HTTP(S) services so IPv4-only clients can access them |
||||
# Note this means that the backend IPv6-only server will only see |
||||
# the IPv6 address of the haproxy host managed by cdist, which can be |
||||
# troublesome if this information is relevant for analytics/security/... |
||||
# See the PROXY example below |
||||
__haproxy_dualstack \ |
||||
--protocol http --protocol https \ |
||||
--v4proxy ipv6.chat \ |
||||
--v4proxy matrix.ungleich.ch |
||||
|
||||
# Use the PROXY protocol to proxy the IPv6-only HTTP(S) services enabling |
||||
# IPv4-only clients to access them while maintaining the client's IP address |
||||
__haproxy_dualstack \ |
||||
--protocol http --protocol https \ |
||||
--v4proxy proxy:ipv6.chat:http=8080:https=591 \ |
||||
--v4proxy proxy:matrix.ungleich.ch:http=8080:https=591 |
||||
# Note however that the PROXY protocol is not compatible with regular |
||||
# HTTP(S) protocols, so your nginx will have to listen on different ports |
||||
# with the PROXY settings. |
||||
# Note that you will need to restrict access to the 8080 port to prevent |
||||
# Client IP spoofing. |
||||
# This can be something like: |
||||
# server { |
||||
# # listen for regular HTTP connections |
||||
# listen [::]:80 default_server; |
||||
# listen 80 default_server; |
||||
# # listen for PROXY HTTP connections |
||||
# listen [::]:8080 proxy_protocol; |
||||
# # Accept the Client's IP from the PROXY protocol |
||||
# real_ip_header proxy_protocol; |
||||
# } |
||||
|
||||
|
||||
SEE ALSO |
||||
-------- |
||||
- https://www.haproxy.com/blog/enhanced-ssl-load-balancing-with-server-name-indication-sni-tls-extension/ |
||||
- https://www.haproxy.com/blog/haproxy/proxy-protocol/ |
||||
- https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/ |
||||
|
||||
|
||||
AUTHORS |
||||
------- |
||||
ungleich <foss--@--ungleich.ch> |
||||
Evilham <cvs--@--evilham.com> |
||||
|
||||
|
||||
COPYING |
||||
------- |
||||
Copyright \(C) 2021 ungleich glarus ag. You can redistribute it |
||||
and/or modify it under the terms of the GNU General Public License as |
||||
published by the Free Software Foundation, either version 3 of the |
||||
License, or (at your option) any later version. |
@ -0,0 +1,155 @@
|
||||
#!/bin/sh -eu |
||||
|
||||
__package haproxy |
||||
require="__package/haproxy" __start_on_boot haproxy |
||||
|
||||
tmpdir="$__object/files" |
||||
mkdir "$tmpdir" |
||||
configtmp="$__object/files/haproxy.cfg" |
||||
|
||||
os=$(cat "$__global/explorer/os") |
||||
case $os in |
||||
freebsd) |
||||
CONFIG_FILE="/usr/local/etc/haproxy.conf" |
||||
cat <<EOF > "$configtmp" |
||||
global |
||||
maxconn 4000 |
||||
user nobody |
||||
group nogroup |
||||
daemon |
||||
|
||||
EOF |
||||
|
||||
;; |
||||
*) |
||||
CONFIG_FILE="/etc/haproxy/haproxy.cfg" |
||||
cat <<EOF > "$configtmp" |
||||
global |
||||
log [::1] local2 |
||||
chroot /var/lib/haproxy |
||||
pidfile /var/run/haproxy.pid |
||||
maxconn 4000 |
||||
user haproxy |
||||
group haproxy |
||||
daemon |
||||
|
||||
# turn on stats unix socket |
||||
stats socket /var/lib/haproxy/stats |
||||
|
||||
EOF |
||||
;; |
||||
esac |
||||
|
||||
cat <<EOF >> "$configtmp" |
||||
defaults |
||||
retries 3 |
||||
log global |
||||
timeout http-request 10s |
||||
timeout queue 1m |
||||
timeout connect 10s |
||||
timeout client 1m |
||||
timeout server 1m |
||||
timeout http-keep-alive 10s |
||||
timeout check 10s |
||||
EOF |
||||
|
||||
dig_cmd="$(command -v dig || true)" |
||||
get_ip() { |
||||
# Usage: get_ip (ipv4|ipv6) NAME |
||||
# uses "dig" if available, else fallback to "host" |
||||
case $1 in |
||||
ipv4) |
||||
if [ -n "${dig_cmd}" ]; then |
||||
${dig_cmd} +short A "$2" |
||||
else |
||||
host -t A "$2" | cut -d ' ' -f 4 | grep -v 'found:' |
||||
fi |
||||
;; |
||||
ipv6) |
||||
if [ -n "${dig_cmd}" ]; then |
||||
${dig_cmd} +short AAAA "$2" |
||||
else |
||||
host -t AAAA "$2" | cut -d ' ' -f 5 | grep -v 'NXDOMAIN' |
||||
fi |
||||
;; |
||||
esac |
||||
} |
||||
|
||||
PROTOCOLS="$(cat "$__object/parameter/protocol")" |
||||
|
||||
for proxy in v4proxy v6proxy; do |
||||
param=$__object/parameter/$proxy |
||||
# no backend? skip generating code |
||||
if [ ! -f "$param" ]; then |
||||
continue |
||||
fi |
||||
|
||||
# turn backend name into bind parameter: v4backend -> ipv4@ |
||||
bind=$(echo $proxy | sed -e 's/^/ip/' -e 's/proxy//') |
||||
|
||||
case $bind in |
||||
ipv4) |
||||
backendproto=ipv6 |
||||
;; |
||||
ipv6) |
||||
backendproto=ipv4 |
||||
;; |
||||
esac |
||||
|
||||
for proto in ${PROTOCOLS}; do |
||||
# Add protocol "header" |
||||
printf "\n# %s %s \n" "${bind}" "${proto}" >> "$configtmp" |
||||
|
||||
sed -e "s/BIND/$bind/" \ |
||||
-e "s/\(frontend[[:space:]].*\)/\1$bind/" \ |
||||
-e "s/\(backend[[:space:]].*\)/\\1$bind/" \ |
||||
"$__type/files/$proto" >> "$configtmp" |
||||
|
||||
while read -r hostdefinition; do |
||||
if echo "$hostdefinition" | grep -qE '^proxy:'; then |
||||
# Proxy protocol was requested |
||||
host="$(echo "$hostdefinition" | sed -E 's/^proxy:([^:]+).*$/\1/')" |
||||
send_proxy=" send-proxy" |
||||
else |
||||
# Just use tcp proxy mode |
||||
host="$hostdefinition" |
||||
send_proxy="" |
||||
fi |
||||
if echo "$hostdefinition" | grep -qE ":${proto}="; then |
||||
# Use custom port definition if requested |
||||
port="$(echo "$hostdefinition" | sed -E "s/^(.*:)?${proto}=([0-9]+).*$/:\2/")" |
||||
else |
||||
# Else use the default |
||||
port="" |
||||
fi |
||||
servername=$host |
||||
|
||||
res=$(get_ip "$bind" "$servername") |
||||
|
||||
if [ -z "$res" ]; then |
||||
echo "$servername does not resolve - aborting config" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
# Treat protocols without TLS+SNI specially |
||||
if [ "$proto" = http ]; then |
||||
echo " use-server $servername if { hdr(host) -i $host }" >> "$configtmp" |
||||
else |
||||
echo " use-server $servername if { req_ssl_sni -i $host }" >> "$configtmp" |
||||
fi |
||||
|
||||
# Create the "server" itself. |
||||
# Note that port and send_proxy will be empty unless |
||||
# they were requested by the type user |
||||
echo " server $servername ${backendproto}@${host}${port}${send_proxy}" >> "$configtmp" |
||||
|
||||
done < "$param" |
||||
done |
||||
done |
||||
|
||||
# Create config file |
||||
require="__package/haproxy" __file ${CONFIG_FILE} --source "$configtmp" --mode 0644 |
||||
|
||||
require="__file${CONFIG_FILE}" __check_messages "haproxy_reload" \ |
||||
--pattern "^__file${CONFIG_FILE}" \ |
||||
--execute "service haproxy reload || service haproxy restart" |
@ -0,0 +1 @@
|
||||
http https imaps smtps |
@ -0,0 +1,3 @@
|
||||
protocol |
||||
v4proxy |
||||
v6proxy |
Loading…
Reference in new issue