Skip to content

Instantly share code, notes, and snippets.

@the-darkvoid
Last active January 8, 2022 17:05
Show Gist options
  • Save the-darkvoid/8bb8e98768782cf69424db70d5ff2959 to your computer and use it in GitHub Desktop.
Save the-darkvoid/8bb8e98768782cf69424db70d5ff2959 to your computer and use it in GitHub Desktop.
net.ipv4.tcp_fastopen=3
net.ipv4.tcp_fastopen_key=b9b738a4-9cad4a74-4c832cbf-e20f561f

haproxy Configuration

Sample configuration for haproxy supporting the following:

  • HTTP on port 80 redirecting to HTTPS on port 443
  • HTTPS on port 443 using PROXY protocol to connect to an nginx instance on 127.0.0.1:81
    HTTPS uses npn and alpn to offer HTTP/1.0 and HTTP/2 on nginx
  • SSH on port 443, TCP tunnel through protocol detection to 127.0.0.1:22
  • SSH over SSL on port 443, SSL TCP tunnel through protocol detection to 127.0.0.1:22
  • Socks on port 443, TCP tunnel through protocol detection for SOCKS5 to 127.0.0.1:1080
  • Shadowsocks on port 443, TCP tunnel through fallback to 127.0.0.1:7688
  • Let's Encrypt ACME Challenge protocol to request/renew SSL certificates, through haproxy ACME validation plugin

Included are the dehydrated configuration and associated scripts to automatically renew certificates and OCSP stapling.

In short the configuration allows HTTPS, SSH, SSL+SSH & Shadowsocks all over the same port.

Note: Be sure to include your own certificate and backup public key in the public key pinning configuration.

#!/bin/sh
if [ "$1" == "deploy_cert" ]; then
DOMAIN=$2
PRIVKEY=$3
CERT=$4
FULLCHAIN=$5
cat "${FULLCHAIN}" \
"${PRIVKEY}" > \
"/etc/haproxy/certs/${DOMAIN}.pem"
chown haproxy:haproxy "/etc/haproxy/certs/${DOMAIN}.pem"
chmod 0640 "/etc/haproxy/certs/${DOMAIN}.pem"
rc-service haproxy restart
fi
########################################################
# This is the main config file for dehydrated #
# #
# This file is looked for in the following locations: #
# $SCRIPTDIR/config (next to this script) #
# /usr/local/etc/dehydrated/config #
# /etc/dehydrated/config #
# ${PWD}/config (in current working-directory) #
# #
# Default values of this config are in comments #
########################################################
# Resolve names to addresses of IP version only. (curl)
# supported values: 4, 6
# default: <unset>
IP_VERSION=4
# Path to certificate authority (default: https://acme-v01.api.letsencrypt.org/directory)
#CA="https://acme-v01.api.letsencrypt.org/directory"
# Path to certificate authority license terms redirect (default: https://acme-v01.api.letsencrypt.org/terms)
#CA_TERMS="https://acme-v01.api.letsencrypt.org/terms"
# Path to license agreement (default: <unset>)
#LICENSE=""
# Which challenge should be used? Currently http-01 and dns-01 are supported
CHALLENGETYPE="http-01"
# Path to a directory containing additional config files, allowing to override
# the defaults found in the main configuration file. Additional config files
# in this directory needs to be named with a '.sh' ending.
# default: <unset>
#CONFIG_D=
# Base directory for account key, generated certificates and list of domains (default: $SCRIPTDIR -- uses config directory if undefined)
#BASEDIR=$SCRIPTDIR
# File containing the list of domains to request certificates for (default: $BASEDIR/domains.txt)
#DOMAINS_TXT="${BASEDIR}/domains.txt"
# Output directory for generated certificates
#CERTDIR="${BASEDIR}/certs"
# Directory for account keys and registration information
#ACCOUNTDIR="${BASEDIR}/accounts"
# Output directory for challenge-tokens to be served by webserver or deployed in HOOK (default: /var/www/dehydrated)
WELLKNOWN="/usr/share/nginx/dehydrated"
# Default keysize for private keys (default: 4096)
#KEYSIZE="4096"
# Path to openssl config file (default: <unset> - tries to figure out system default)
#OPENSSL_CNF=
# Path to OpenSSL binary (default: "openssl")
#OPENSSL="openssl"
# Extra options passed to the curl binary (default: <unset>)
#CURL_OPTS=
# Program or function called in certain situations
#
# After generating the challenge-response, or after failed challenge (in this case altname is empty)
# Given arguments: clean_challenge|deploy_challenge altname token-filename token-content
#
# After successfully signing certificate
# Given arguments: deploy_cert domain path/to/privkey.pem path/to/cert.pem path/to/fullchain.pem
#
# BASEDIR and WELLKNOWN variables are exported and can be used in an external program
# default: <unset>
HOOK=/opt/dehydrated/cert_renew.sh
# Chain clean_challenge|deploy_challenge arguments together into one hook call per certificate (default: no)
#HOOK_CHAIN="no"
# Minimum days before expiration to automatically renew certificate (default: 30)
#RENEW_DAYS="30"
# Regenerate private keys instead of just signing new certificates on renewal (default: yes)
PRIVATE_KEY_RENEW="no"
# Create an extra private key for rollover (default: no)
PRIVATE_KEY_ROLLOVER="yes"
# Which public key algorithm should be used? Supported: rsa, prime256v1 and secp384r1
KEY_ALGO=secp384r1
# E-mail to use during the registration (default: <unset>)
#CONTACT_EMAIL=
# Lockfile location, to prevent concurrent access (default: $BASEDIR/lock)
#LOCKFILE="${BASEDIR}/lock"
# Option to add CSR-flag indicating OCSP stapling to be mandatory (default: no)
OCSP_MUST_STAPLE="yes"
# Fetch OCSP responses (default: no)
#OCSP_FETCH="no"
# Issuer chain cache directory (default: $BASEDIR/chains)
#CHAINCACHE="${BASEDIR}/chains"
# Automatic cleanup (default: no)
#AUTO_CLEANUP="no"
global
ulimit-n 51200
tune.ssl.default-dh-param 4096
log /dev/log local2 debug
user haproxy
group haproxy
lua-load /etc/haproxy/acme-http01-webroot.lua
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11 #no-tls-tickets
ssl-default-bind-ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!ECDHE-RSA-AES128-GCM-SHA256:!ECDHE-ECDSA-AES128-GCM-SHA256:!DHE-RSA-AES128-GCM-SHA256:!DHE-DSS-AES128-GCM-SHA256:!ECDHE-RSA-AES128-SHA256:!ECDHE-ECDSA-AES128-SHA256:!ECDHE-RSA-AES128-SHA:!ECDHE-ECDSA-AES128-SHA:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES128-SHA:!DHE-DSS-AES128-SHA256:!AES128-GCM-SHA256:!AES128-SHA256:!AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 #no-tls-tickets
ssl-default-server-ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!ECDHE-RSA-AES128-GCM-SHA256:!ECDHE-ECDSA-AES128-GCM-SHA256:!DHE-RSA-AES128-GCM-SHA256:!DHE-DSS-AES128-GCM-SHA256:!ECDHE-RSA-AES128-SHA256:!ECDHE-ECDSA-AES128-SHA256:!ECDHE-RSA-AES128-SHA:!ECDHE-ECDSA-AES128-SHA:!DHE-RSA-AES128-SHA256:!DHE-RSA-AES128-SHA:!DHE-DSS-AES128-SHA256:!AES128-GCM-SHA256:!AES128-SHA256:!AES128-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
# openssl dhparam -out /etc/haproxy/certs/dhparams.pem 4096
ssl-dh-param-file /etc/haproxy/dhparams.pem
defaults
timeout connect 20s
timeout client 50s
timeout server 50s
timeout tunnel 1h
log global
option dontlognull
frontend ssl
mode tcp
# option tcplog
bind 0.0.0.0:443 tfo
tcp-request inspect-delay 2s
# "SSH-2.0"
acl is_ssh payload(0,7) -m bin 5353482d322e30
# Socks 5
acl is_socks5_bin payload(0,1) -m bin 05
acl is_socks5_len req_len le 5
# SSL / TLS
acl is_ssl req_ssl_ver 1:4
tcp-request content accept if is_ssl
use_backend main-ssl if is_ssl
use_backend ssh if !is_ssl is_ssh
use_backend socks if is_socks5_bin is_socks5_len
default_backend shadowsocks
frontend main
mode tcp
# option tcplog
bind 127.0.0.1:443 tfo ssl ecdhe secp384r1 crt /etc/haproxy/certs/ npn h2,http/1.1 alpn h2,http/1.1 accept-proxy
tcp-request inspect-delay 2s
tcp-request content accept if HTTP
#option forwardfor
# "SSH-2.0"
acl is_ssh payload(0,7) -m bin 5353482d322e30
use_backend ssh if is_ssh || !HTTP
use_backend webserver-http2 if { ssl_fc_alpn -i h2 }
default_backend webserver
frontend http
bind 0.0.0.0:80 tfo
reqadd X-Forwarded-Proto:\ http
default_backend webserver
backend main-ssl
mode tcp
# option tcplog
# Connect to frontend main
server main-ssl 127.0.0.1:443 send-proxy
backend shadowsocks
mode tcp
# option tcplog
server shadowsocks-localhost 127.0.0.1:7688 maxconn 20480
timeout server 2h
backend ssh
mode tcp
# option tcplog
# source 0.0.0.0 usesrc clientip
server ssh 127.0.0.1:22
timeout server 2h
backend socks
mode tcp
option tcplog
server socks 127.0.0.1:1080
timeout server 2h
backend webserver
mode http
# option httplog
option forwardfor
# Support for https://github.com/ietf-wg-acme/acme/ challenge protocol
acl url_acme_http01 path_beg /.well-known/acme-challenge/
http-request use-service lua.acme-http01 if url_acme_http01
# HSTS header
http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;"
# Public Key Pinning
http-response set-header Public-Key-Pins "pin-sha256=\"xxxxxxx=\"; max-age=2592000; includeSubDomains"
redirect scheme https code 301 if !{ ssl_fc }
server webserver-localhost 127.0.0.1:81 send-proxy
backend webserver-http2
mode tcp
server webserver-localhost 127.0.0.1:82 check send-proxy
#!/bin/sh
for f in /etc/haproxy/certs/*.pem
do
DOMAIN=$(basename ${f} .pem)
openssl ocsp \
-no_nonce \
-respout /etc/haproxy/certs/${DOMAIN}.pem.ocsp \
-issuer /etc/dehydrated/certs/${DOMAIN}/chain.pem \
-verify_other /etc/dehydrated/certs/${DOMAIN}/chain.pem \
-cert /etc/dehydrated/certs/${DOMAIN}/cert.pem \
-url http://ocsp.int-x3.letsencrypt.org/ \
-header "HOST" "ocsp.int-x3.letsencrypt.org"
chown haproxy:haproxy /etc/haproxy/certs/${DOMAIN}.pem.ocsp
chmod 0640 /etc/haproxy/certs/${DOMAIN}.pem.ocsp
done
rc-service haproxy restart
#!/bin/sh
# Generate HPKP headers for haproxy certificates
pin_from_cert () {
openssl x509 -in "$1" -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
}
pin_from_key () {
openssl ec -in "$1" -pubout 2>/dev/null | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
}
for f in /etc/haproxy/certs/*.pem
do
DOMAIN=$(basename ${f} .pem)
echo "-----------------"
echo -e "Domain: ${DOMAIN}\n"
echo -e "\tDST ROOT CA X3":
ROOT1=$(pin_from_cert /etc/dehydrated/certs/DST_ROOT_CA_X3.pem)
echo -e "\t$ROOT1\n"
echo -e "\tISRG ROOT X1:"
ROOT2=$(pin_from_cert /etc/dehydrated/certs/ISRG_ROOT_X1.pem)
echo -e "\t$ROOT2\n"
echo -e "\tLets_Encrypt_Authority_X3:"
INTERMEDIATE1=$(pin_from_cert /etc/dehydrated/certs/Lets_Encrypt_Authority_X3.pem)
echo -e "\t$INTERMEDIATE1\n"
echo -e "\tLets_Encrypt_Authority_X4:"
INTERMEDIATE2=$(pin_from_cert /etc/dehydrated/certs/Lets_Encrypt_Authority_X4.pem)
echo -e "\t$INTERMEDIATE2\n"
echo -e "\t${DOMAIN}:"
LEAF=$(pin_from_cert /etc/dehydrated/certs/${DOMAIN}/fullchain.pem)
echo -e "\t$LEAF\n"
echo -e "\t${DOMAIN} (backup):"
LEAF_BACKUP=$(pin_from_key /etc/dehydrated/certs/${DOMAIN}/privkey.roll.pem)
echo -e "\t$LEAF_BACKUP\n"
echo -e "\tPublic-Key-Pins:\ pin-sha256=\"${LEAF}=\";\ pin-sha256=\"${ROOT1}\";\ pin-sha256=\"${ROOT2}\";\ pin-sha256=\"${INTERMEDIATE1}\";\ pin-sha256=\"${INTERMEDIATE2}\";\ pin-sha256=\"${LEAF_BACKUP}\";\ max-age=60;\ includeSubDomains"
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment