forked from ungleich-public/cdist-contrib
[__haproxy_dualstack] New type with PROXY protocol support
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.
This commit is contained in:
parent
2038244ec4
commit
1d867f4778
9 changed files with 323 additions and 0 deletions
8
type/__haproxy_dualstack/files/http
Normal file
8
type/__haproxy_dualstack/files/http
Normal file
|
@ -0,0 +1,8 @@
|
|||
frontend http
|
||||
bind BIND@:80
|
||||
mode http
|
||||
option httplog
|
||||
default_backend http
|
||||
|
||||
backend http
|
||||
mode http
|
10
type/__haproxy_dualstack/files/https
Normal file
10
type/__haproxy_dualstack/files/https
Normal file
|
@ -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
|
12
type/__haproxy_dualstack/files/imaps
Normal file
12
type/__haproxy_dualstack/files/imaps
Normal file
|
@ -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
|
12
type/__haproxy_dualstack/files/smtps
Normal file
12
type/__haproxy_dualstack/files/smtps
Normal file
|
@ -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
|
122
type/__haproxy_dualstack/man.rst
Normal file
122
type/__haproxy_dualstack/man.rst
Normal file
|
@ -0,0 +1,122 @@
|
|||
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 service 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/haproxy/proxy-protocol/
|
||||
- https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/
|
||||
- https://www.haproxy.com/blog/enhanced-ssl-load-balancing-with-server-name-indication-sni-tls-extension/
|
||||
|
||||
|
||||
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.
|
155
type/__haproxy_dualstack/manifest
Normal file
155
type/__haproxy_dualstack/manifest
Normal file
|
@ -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"
|
1
type/__haproxy_dualstack/parameter/default/protocol
Normal file
1
type/__haproxy_dualstack/parameter/default/protocol
Normal file
|
@ -0,0 +1 @@
|
|||
http https imaps smtps
|
3
type/__haproxy_dualstack/parameter/optional_multiple
Normal file
3
type/__haproxy_dualstack/parameter/optional_multiple
Normal file
|
@ -0,0 +1,3 @@
|
|||
protocol
|
||||
v4proxy
|
||||
v6proxy
|
0
type/__haproxy_dualstack/singleton
Normal file
0
type/__haproxy_dualstack/singleton
Normal file
Loading…
Reference in a new issue