summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/basic-config-edge.cfg131
-rw-r--r--examples/content-sw-sample.cfg65
-rw-r--r--examples/errorfiles/400.http9
-rw-r--r--examples/errorfiles/403.http9
-rw-r--r--examples/errorfiles/408.http9
-rw-r--r--examples/errorfiles/500.http9
-rw-r--r--examples/errorfiles/502.http9
-rw-r--r--examples/errorfiles/503.http9
-rw-r--r--examples/errorfiles/504.http9
-rw-r--r--examples/errorfiles/README9
-rw-r--r--examples/haproxy.init137
-rw-r--r--examples/lua/README7
-rw-r--r--examples/lua/event_handler.lua28
-rw-r--r--examples/lua/mailers.lua426
-rw-r--r--examples/option-http_proxy.cfg54
-rw-r--r--examples/quick-test.cfg29
-rw-r--r--examples/socks4.cfg55
-rw-r--r--examples/transparent_proxy.cfg55
-rw-r--r--examples/wurfl-example.cfg41
19 files changed, 1100 insertions, 0 deletions
diff --git a/examples/basic-config-edge.cfg b/examples/basic-config-edge.cfg
new file mode 100644
index 0000000..8ee6bda
--- /dev/null
+++ b/examples/basic-config-edge.cfg
@@ -0,0 +1,131 @@
+# This configuration creates a classical reverse-proxy and load balancer for
+# public services. It presents ports 80 and 443 (with 80 redirecting to 443),
+# enables caching up to one hour, and load-balances the service on a farm of
+# 4 servers on private IP addresses which are checked using HTTP checks and
+# by maintaining stickiness via session cookies. It offloads TLS processing
+# and enables HTTP compression. It uses HAProxy 2.4.
+
+# The global section deals with process-wide settings (security, resource usage)
+global
+ # all file names are relative to the directory containing this config
+ # file by default
+ default-path config
+
+ # refuse to start if any warning is emitted at boot (keep configs clean)
+ zero-warning
+
+ # Security hardening: isolate and drop privileges
+ chroot /var/empty
+ user haproxy
+ group haproxy
+
+ # daemonize
+ daemon
+ pidfile /var/run/haproxy-svc1.pid
+
+ # do not keep old processes longer than that after a reload
+ hard-stop-after 5m
+
+ # The command-line-interface (CLI) used by the admin, by provisionning
+ # tools, and to transfer sockets during reloads
+ stats socket /var/run/haproxy-svc1.sock level admin mode 600 user haproxy expose-fd listeners
+ stats timeout 1h
+
+ # send logs to stderr for logging via the service manager
+ log stderr local0 info
+
+ # intermediate security for SSL, from https://ssl-config.mozilla.org/
+ ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
+ ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
+ ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
+
+# default settings common to all HTTP proxies below
+defaults http
+ mode http
+ option httplog
+ log global
+ timeout client 1m
+ timeout server 1m
+ timeout connect 10s
+ timeout http-keep-alive 2m
+ timeout queue 15s
+ timeout tunnel 4h # for websocket
+
+# provide a stats page on port 8181
+frontend stats
+ bind :8181
+ # provide advanced stats (ssl, h2, ...)
+ stats uri /
+ stats show-modules
+ # some users may want to protect the access to their stats and/or to
+ # enable admin mode on the page from local networks
+ # stats auth admin:mystats
+ # stats admin if { src 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 127.0.0.0/8 }
+
+# First incoming public service. Supports HTTP/1.x, HTTP/2, and HTTP/3 over
+# QUIC when built in, uses HSTS, redirects clear to TLS. Uses a dedicated host
+# name for the stats page.
+frontend pub1
+ bind :80 name clear
+ bind :443 name secure ssl crt pub1.pem
+ option socket-stats # provide per-bind line stats
+
+.if feature(QUIC)
+ # indicate QUIC support for 25 hours
+ bind quic4@:443 name quic ssl crt pub1.pem allow-0rtt
+ http-response add-header alt-svc 'h3=":443"; ma=90000'
+.endif
+
+ # set HSTS for one year after all responses
+ http-after-response set-header Strict-Transport-Security "max-age=31536000"
+ http-request redirect scheme https code 301 if !{ ssl_fc }
+
+ # silently ignore connect probes and pre-connect without request
+ option http-ignore-probes
+
+ # pass client's IP address to the server and prevent against attempts
+ # to inject bad contents
+ http-request del-header x-forwarded-for
+ option forwardfor
+
+ # enable HTTP compression of text contents
+ compression algo deflate gzip
+ compression type text/ application/javascript application/xhtml+xml image/x-icon
+
+ # enable HTTP caching of any cacheable content
+ http-request cache-use cache
+ http-response cache-store cache
+
+ default_backend app1
+
+# The cache instance used by the frontend (200MB, 10MB max object, 1 hour max)
+# May be consulted using "show cache" on the CLI socket
+cache cache
+ total-max-size 200 # RAM cache size in megabytes
+ max-object-size 10485760 # max cacheable object size in bytes
+ max-age 3600 # max cache duration in seconds
+ process-vary on # handle the Vary header (otherwise don't cache)
+
+# First application
+backend app1
+ # Algorithm:
+ # - roundrobin is usually better for short requests,
+ # - leastconn is better for mixed slow ones, and long transfers,
+ # - random is generally good when using multiple load balancers
+ balance random
+
+ # abort if the client clicks on stop.
+ option abortonclose
+
+ # insert a session cookie for user stickiness
+ cookie app1 insert indirect nocache
+
+ # check the servers' health using HTTP requests
+ option httpchk
+ http-check send meth GET uri / ver HTTP/1.1 hdr host svc1.example.com
+
+ # do not overload the servers (100 concurrent conns max each)
+ server srv1 192.0.2.1:80 cookie s1 maxconn 100 check inter 1s
+ server srv2 192.0.2.2:80 cookie s2 maxconn 100 check inter 1s
+ server srv3 192.0.2.3:80 cookie s3 maxconn 100 check inter 1s
+ server srv4 192.0.2.4:80 cookie s4 maxconn 100 check inter 1s
diff --git a/examples/content-sw-sample.cfg b/examples/content-sw-sample.cfg
new file mode 100644
index 0000000..e54f976
--- /dev/null
+++ b/examples/content-sw-sample.cfg
@@ -0,0 +1,65 @@
+#
+# This is a sample configuration. It illustrates how to separate static objects
+# traffic from dynamic traffic, and how to dynamically regulate the server load.
+#
+# It listens on 192.168.1.10:80, and directs all requests for Host 'img' or
+# URIs starting with /img or /css to a dedicated group of servers. URIs
+# starting with /admin/stats deliver the stats page.
+#
+
+global
+ maxconn 10000
+ stats socket /var/run/haproxy.stat mode 600 level admin
+ log 127.0.0.1 local0
+ uid 200
+ gid 200
+ chroot /var/empty
+ daemon
+
+# The public 'www' address in the DMZ
+frontend public
+ bind 192.168.1.10:80 name clear
+ #bind 192.168.1.10:443 ssl crt /etc/haproxy/haproxy.pem
+ mode http
+ log global
+ option httplog
+ option dontlognull
+ monitor-uri /monitoruri
+ maxconn 8000
+ timeout client 30s
+
+ stats uri /admin/stats
+ use_backend static if { hdr_beg(host) -i img }
+ use_backend static if { path_beg /img /css }
+ default_backend dynamic
+
+# The static backend backend for 'Host: img', /img and /css.
+backend static
+ mode http
+ balance roundrobin
+ option prefer-last-server
+ retries 2
+ option redispatch
+ timeout connect 5s
+ timeout server 5s
+ option httpchk HEAD /favicon.ico
+ server statsrv1 192.168.1.8:80 check inter 1000
+ server statsrv2 192.168.1.9:80 check inter 1000
+
+# the application servers go here
+backend dynamic
+ mode http
+ balance roundrobin
+ retries 2
+ option redispatch
+ timeout connect 5s
+ timeout server 30s
+ timeout queue 30s
+ option httpchk HEAD /login.php
+ cookie DYNSRV insert indirect nocache
+ fullconn 4000 # the servers will be used at full load above this number of connections
+ server dynsrv1 192.168.1.1:80 minconn 50 maxconn 500 cookie s1 check inter 1000
+ server dynsrv2 192.168.1.2:80 minconn 50 maxconn 500 cookie s2 check inter 1000
+ server dynsrv3 192.168.1.3:80 minconn 50 maxconn 500 cookie s3 check inter 1000
+ server dynsrv4 192.168.1.4:80 minconn 50 maxconn 500 cookie s4 check inter 1000
+
diff --git a/examples/errorfiles/400.http b/examples/errorfiles/400.http
new file mode 100644
index 0000000..e223e38
--- /dev/null
+++ b/examples/errorfiles/400.http
@@ -0,0 +1,9 @@
+HTTP/1.0 400 Bad request
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>400 Bad request</h1>
+Your browser sent an invalid request.
+</body></html>
+
diff --git a/examples/errorfiles/403.http b/examples/errorfiles/403.http
new file mode 100644
index 0000000..a67e807
--- /dev/null
+++ b/examples/errorfiles/403.http
@@ -0,0 +1,9 @@
+HTTP/1.0 403 Forbidden
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>403 Forbidden</h1>
+Request forbidden by administrative rules.
+</body></html>
+
diff --git a/examples/errorfiles/408.http b/examples/errorfiles/408.http
new file mode 100644
index 0000000..aafb130
--- /dev/null
+++ b/examples/errorfiles/408.http
@@ -0,0 +1,9 @@
+HTTP/1.0 408 Request Time-out
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>408 Request Time-out</h1>
+Your browser didn't send a complete request in time.
+</body></html>
+
diff --git a/examples/errorfiles/500.http b/examples/errorfiles/500.http
new file mode 100644
index 0000000..9c3be96
--- /dev/null
+++ b/examples/errorfiles/500.http
@@ -0,0 +1,9 @@
+HTTP/1.0 500 Internal Server Error
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>500 Internal Server Error</h1>
+An internal server error occurred.
+</body></html>
+
diff --git a/examples/errorfiles/502.http b/examples/errorfiles/502.http
new file mode 100644
index 0000000..94b35d4
--- /dev/null
+++ b/examples/errorfiles/502.http
@@ -0,0 +1,9 @@
+HTTP/1.0 502 Bad Gateway
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>502 Bad Gateway</h1>
+The server returned an invalid or incomplete response.
+</body></html>
+
diff --git a/examples/errorfiles/503.http b/examples/errorfiles/503.http
new file mode 100644
index 0000000..48fde58
--- /dev/null
+++ b/examples/errorfiles/503.http
@@ -0,0 +1,9 @@
+HTTP/1.0 503 Service Unavailable
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>503 Service Unavailable</h1>
+No server is available to handle this request.
+</body></html>
+
diff --git a/examples/errorfiles/504.http b/examples/errorfiles/504.http
new file mode 100644
index 0000000..f925184
--- /dev/null
+++ b/examples/errorfiles/504.http
@@ -0,0 +1,9 @@
+HTTP/1.0 504 Gateway Time-out
+Cache-Control: no-cache
+Connection: close
+Content-Type: text/html
+
+<html><body><h1>504 Gateway Time-out</h1>
+The server didn't respond in time.
+</body></html>
+
diff --git a/examples/errorfiles/README b/examples/errorfiles/README
new file mode 100644
index 0000000..a882632
--- /dev/null
+++ b/examples/errorfiles/README
@@ -0,0 +1,9 @@
+These files are default error files that can be customized
+if necessary. They are complete HTTP responses, so that
+everything is possible, including using redirects or setting
+special headers.
+
+They can be used with the 'errorfile' keyword like this :
+
+ errorfile 503 /etc/haproxy/errors/503.http
+
diff --git a/examples/haproxy.init b/examples/haproxy.init
new file mode 100644
index 0000000..cc120d8
--- /dev/null
+++ b/examples/haproxy.init
@@ -0,0 +1,137 @@
+#!/bin/sh
+#
+# chkconfig: - 85 15
+# description: HAProxy is a TCP/HTTP reverse proxy which is particularly suited \
+# for high availability environments.
+# processname: haproxy
+# config: /etc/haproxy/haproxy.cfg
+# pidfile: /var/run/haproxy.pid
+
+# Script Author: Simon Matter <simon.matter@invoca.ch>
+# Version: 2004060600
+
+# Source function library.
+if [ -f /etc/init.d/functions ]; then
+ . /etc/init.d/functions
+elif [ -f /etc/rc.d/init.d/functions ] ; then
+ . /etc/rc.d/init.d/functions
+else
+ exit 0
+fi
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+# This is our service name
+BASENAME=`basename $0`
+if [ -L $0 ]; then
+ BASENAME=`find $0 -name $BASENAME -printf %l`
+ BASENAME=`basename $BASENAME`
+fi
+
+BIN=/usr/sbin/$BASENAME
+
+CFG=/etc/$BASENAME/$BASENAME.cfg
+[ -f $CFG ] || exit 1
+
+PIDFILE=/var/run/$BASENAME.pid
+LOCKFILE=/var/lock/subsys/$BASENAME
+
+RETVAL=0
+
+start() {
+ quiet_check
+ if [ $? -ne 0 ]; then
+ echo "Errors found in configuration file, check it with '$BASENAME check'."
+ return 1
+ fi
+
+ echo -n "Starting $BASENAME: "
+ daemon $BIN -D -f $CFG -p $PIDFILE
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && touch $LOCKFILE
+ return $RETVAL
+}
+
+stop() {
+ echo -n "Shutting down $BASENAME: "
+ killproc $BASENAME -USR1
+ RETVAL=$?
+ echo
+ [ $RETVAL -eq 0 ] && rm -f $LOCKFILE
+ [ $RETVAL -eq 0 ] && rm -f $PIDFILE
+ return $RETVAL
+}
+
+restart() {
+ quiet_check
+ if [ $? -ne 0 ]; then
+ echo "Errors found in configuration file, check it with '$BASENAME check'."
+ return 1
+ fi
+ stop
+ start
+}
+
+reload() {
+ if ! [ -s $PIDFILE ]; then
+ return 0
+ fi
+
+ quiet_check
+ if [ $? -ne 0 ]; then
+ echo "Errors found in configuration file, check it with '$BASENAME check'."
+ return 1
+ fi
+ $BIN -D -f $CFG -p $PIDFILE -sf $(cat $PIDFILE)
+}
+
+check() {
+ $BIN -c -q -V -f $CFG
+}
+
+quiet_check() {
+ $BIN -c -q -f $CFG
+}
+
+rhstatus() {
+ status $BASENAME
+}
+
+condrestart() {
+ [ -e $LOCKFILE ] && restart || :
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ restart)
+ restart
+ ;;
+ reload)
+ reload
+ ;;
+ condrestart)
+ condrestart
+ ;;
+ status)
+ rhstatus
+ ;;
+ check)
+ check
+ ;;
+ *)
+ echo $"Usage: $BASENAME {start|stop|restart|reload|condrestart|status|check}"
+ exit 1
+esac
+
+exit $?
diff --git a/examples/lua/README b/examples/lua/README
new file mode 100644
index 0000000..620d832
--- /dev/null
+++ b/examples/lua/README
@@ -0,0 +1,7 @@
+These files are example lua scripts that can be customized
+if necessary.
+
+They can be loaded with the 'lua-load' keyword like this:
+
+ lua-load /path/to/lua_script.lua
+
diff --git a/examples/lua/event_handler.lua b/examples/lua/event_handler.lua
new file mode 100644
index 0000000..41ed712
--- /dev/null
+++ b/examples/lua/event_handler.lua
@@ -0,0 +1,28 @@
+-- haproxy event-handling from Lua
+--
+-- This file serves as a demo to show you the various events that
+-- can be handled directly from custom lua functions.
+-- Events captured from lua will be printed directly to STDOUT
+-- It may not be exhaustive, please refer to the lua documentation
+-- in doc/lua-api/index.rst for up-to-date content and further explanations
+
+-- subscribe to every SERVER family events, this is the equivalent of doing:
+-- core.event_sub({"SERVER_ADD", "SERVER_DEL", "SERVER_UP", "SERVER_DOWN"}, ...)
+core.event_sub({"SERVER"}, function(event, data)
+ -- This function will be called when:
+ -- - new server is added from the CLI (SERVER_ADD)
+ -- - existing server is removed from the CLI (SERVER_DEL)
+ -- - existing server state changes from UP to DOWN (SERVER_DOWN)
+ -- - existing server state changes from DOWN to UP (SERVER_UP)
+ -- If the server still exists at the time the function is called, data["reference"]
+ -- contains a valid reference to the lua server object related to the event
+ --
+ sv_status = data["reference"] ~= nil and data["reference"]:get_stats().status or "DELETED"
+ print("[DEBUG - FROM LUA]", "EventType." .. event .. ": " ..
+ "server " .. data["proxy_name"] .. "/" .. data["name"] .. " " ..
+ "is " .. sv_status)
+end)
+-- Please note that you may also use Server.event_sub() method to subscribe to events
+-- relative to a specific server only. See the lua documentation for more information.
+
+-- New event families will be added over time...
diff --git a/examples/lua/mailers.lua b/examples/lua/mailers.lua
new file mode 100644
index 0000000..47a8356
--- /dev/null
+++ b/examples/lua/mailers.lua
@@ -0,0 +1,426 @@
+-- haproxy mailers implementation in lua
+--
+-- Provides a pure lua alternative to tcpcheck mailers.
+--
+-- To be loaded using "lua-load" from haproxy configuration to handle
+-- email-alerts directly from lua and disable legacy tcpcheck implementation.
+
+local SYSLOG_LEVEL = {
+ ["EMERG"] = 0,
+ ["ALERT"] = 1,
+ ["CRIT"] = 2,
+ ["ERROR"] = 3,
+ ["WARN"] = 4,
+ ["NOTICE"] = 5,
+ ["INFO"] = 6,
+ ["DEBUG"] = 7
+}
+
+local mailqueue = core.queue()
+
+-- smtp : send SMTP message
+--
+-- Copyright 2018 Thierry Fournier
+--
+-- This function is compliant with HAProxy cosockets
+-- EHLO was replaced with HELO for better compatibility with
+-- basic mail server implementations
+--
+-- <server> should contain the full server address (including port) in the
+-- same format used in haproxy config file. It will be passed as it is to
+-- tcp::connect() without explicit port argument. See Socket.connect()
+-- manual for more information.
+--
+-- The function will abort after <timeout> ms
+function smtp_send_email(server, timeout, domain, from, to, data)
+ local ret
+ local reason
+ local tcp = core.tcp()
+ local smtp_wait_code = function(tcp, code)
+ local ret
+ -- Read headers until we reac a 2.. code.
+ while true do
+ -- read line
+ ret = tcp:receive("*l")
+ if ret == nil then
+ return false, "Connection unexpectedly closed"
+ end
+ -- expected code
+ if string.match(ret, code) ~= nil then
+ return true, nil
+ end
+ -- other code
+ if string.match(ret, '^%d%d%d ') ~= nil then
+ return false, ret
+ end
+ -- other informational message, wait.
+ end
+ end
+
+ if timeout ~= nil and timeout > 0 then
+ tcp:settimeout(timeout / 1000)
+ end
+
+ if tcp:connect(server) == nil then
+ return false, "Can't connect to \""..server.."\""
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^220 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ if tcp:send("HELO " .. domain .. "\r\n") == nil then
+ tcp:close()
+ return false, "Connection unexpectedly closed"
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^250 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ if tcp:send("MAIL FROM: <" .. from .. ">\r\n") == nil then
+ tcp:close()
+ return false, "Connection unexpectedly closed"
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^250 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ if tcp:send("RCPT TO: <" .. to .. ">\r\n") == nil then
+ tcp:close()
+ return false, "Connection unexpectedly closed"
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^250 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ if tcp:send("DATA\r\n") == nil then
+ tcp:close()
+ return false, "Connection unexpectedly closed"
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^354 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ if tcp:send(data .. "\r\n.\r\n") == nil then
+ tcp:close()
+ return false, "Connection unexpectedly closed"
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^250 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ if tcp:send("QUIT\r\n") == nil then
+ tcp:close()
+ return false, "Connection unexpectedly closed"
+ end
+
+ ret, reason = smtp_wait_code(tcp, '^221 ')
+ if ret == false then
+ tcp:close()
+ return false, reason
+ end
+
+ tcp:close()
+ return true, nil
+end
+
+local function send_email_alert(srv, level, message, when)
+ local mailers = srv:get_proxy():get_mailers()
+
+ if mailers == nil then
+ return -- nothing to do
+ end
+
+ if level > mailers.log_level then
+ return
+ end
+
+ -- email sending is performed asynchronously thanks to mailqueue
+ local job = {}
+
+ job.mailconf = mailers
+ job.when = when
+ job.msg = message
+
+ -- enqueue email job
+ mailqueue:push(job)
+
+end
+
+local function srv_get_check_details(check)
+ local c = core.concat()
+
+ c:add(", ")
+ c:add(string.format("reason: %s", check.reason.desc))
+ if check.reason.code ~= nil
+ then
+ c:add(string.format(", code: %d", check.reason.code))
+ end
+ if check.duration >= 0
+ then
+ c:add(string.format(", check duration: %dms", check.duration))
+ end
+
+ return c:dump()
+end
+
+local function srv_get_status_details(srv, requeued)
+ local c = core.concat()
+
+ c:add(string.format("%d active and %d backup servers left.",
+ srv:get_proxy():get_srv_act(),
+ srv:get_proxy():get_srv_bck()))
+ c:add(" ")
+ c:add(string.format("%d sessions active, %d requeued, %d remaining in queue",
+ srv:get_cur_sess(),
+ requeued,
+ srv:get_pend_conn()))
+ return c:dump()
+end
+
+local function srv_state_handler(event, data, when)
+ local server = data.reference
+ local state = data.state
+ local c = core.concat()
+ local log_level = SYSLOG_LEVEL["ALERT"]
+ local message
+
+ if server == nil then
+ -- server already removed, can't do much
+ return
+ end
+
+ if state.admin then
+ -- don't report if is related to an administrative change and not
+ -- directly due to an operational change
+ return
+ end
+
+ -- we don't send an alert if the server was previously stopping
+ if state.old_state == "STOPPING" or server:is_draining() then
+ log_level = SYSLOG_LEVEL["NOTICE"]
+ end
+
+ -- prepare the message
+ c:add(string.format("Server %s/%s is %s",
+ server:get_proxy():get_name(),
+ server:get_name(),
+ state.new_state == "RUNNING" and "UP" or "DOWN"))
+
+ if server:tracking()
+ then
+ -- server is tracking another server, it means that the operational
+ -- state change is inherited
+ c:add(string.format(" via %s/%s",
+ server:tracking():get_proxy():get_name(),
+ server:tracking():get_name()))
+ end
+
+ if state.check ~= nil
+ then
+ c:add(srv_get_check_details(state.check))
+ else
+ c:add(state.cause)
+ end
+
+ c:add(". ")
+ c:add(srv_get_status_details(server, state.requeued))
+ send_email_alert(server, log_level, c:dump(), when)
+end
+
+local function srv_admin_handler(event, data, when)
+ local server = data.reference
+ local admin = data.admin
+ local c = core.concat()
+
+ if server == nil then
+ -- server already removed, can't do much
+ return
+ end
+
+ -- only send an email when server is entering drain state and not under maint
+ if not (not admin.old_admin["DRAIN"] and
+ admin.new_admin["DRAIN"] and
+ not admin.new_admin["MAINT"]) then
+ return
+ end
+
+ -- prepare the message
+ c:add(string.format("Server %s/%s enters drain state",
+ server:get_proxy():get_name(),
+ server:get_name()))
+
+ if server:tracking() and admin.new_admin["IDRAIN"]
+ then
+ -- server is tracking another server and IDRAIN is set, it means
+ -- that the admin state change is inherited
+ c:add(string.format(" via %s/%s",
+ server:tracking():get_proxy():get_name(),
+ server:tracking():get_name()))
+ end
+
+ c:add(". ")
+ c:add(srv_get_status_details(server, admin.requeued))
+ send_email_alert(server, SYSLOG_LEVEL["NOTICE"], c:dump(), when)
+end
+
+local function srv_check_handler(event, data, when)
+ local server = data.reference
+ local check = data.check
+ local c = core.concat()
+
+ if server == nil then
+ -- server already removed, can't do much
+ return
+ end
+
+ -- we will always send an email, prepare the message
+ c:add(string.format("%s check for %sserver %s/%s ",
+ check.agent and "Agent" or "Health",
+ server:is_backup() and "backup " or "",
+ server:get_proxy():get_name(),
+ server:get_name()))
+
+ if check.result == "CONDPASS" then
+ c:add("conditionally succeeded")
+ elseif check.result == "PASSED" then
+ c:add("succeeded")
+ else
+ c:add("failed")
+ end
+
+ c:add(srv_get_check_details(check))
+
+ c:add(", status: ")
+ if check.health.cur >= check.health.rise then
+ -- good
+ c:add(string.format("%d/%d %s",
+ check.health.cur - check.health.rise + 1,
+ check.health.fall,
+ server:get_weight() and "UP" or "DRAIN"))
+ else
+ -- bad
+ c:add(string.format("%d/%d DOWN",
+ check.health.cur,
+ check.health.rise))
+ end
+
+ send_email_alert(server, SYSLOG_LEVEL["INFO"], c:dump(), when)
+end
+
+-- single function for multiple event types since all events come
+-- from the same subscription to reduce memory footprint
+local function srv_event_dispatch(event, data, mgmt, when)
+ if event == "SERVER_STATE" then srv_state_handler(event, data, when) end
+ if event == "SERVER_ADMIN" then srv_admin_handler(event, data, when) end
+ if event == "SERVER_CHECK" then srv_check_handler(event, data, when) end
+end
+
+local function mailers_track_server_events(srv)
+ local mailer_conf = srv:get_proxy():get_mailers()
+
+ -- don't track server events if the parent proxy did not enable email alerts
+ if mailer_conf == nil
+ then return
+ end
+
+ -- email alerts are enabled, track server state and admin changes
+ local subscriptions = {"SERVER_STATE", "SERVER_ADMIN"}
+
+ if mailer_conf.track_server_health
+ then
+ -- track server check events as well (this event source is expensive)
+ table.insert(subscriptions, "SERVER_CHECK")
+ end
+
+ -- perform the event subscription from the server
+ srv:event_sub(subscriptions, srv_event_dispatch)
+end
+
+local function srv_event_add(event, data)
+ -- do nothing if the server was already removed
+ if data.reference == nil
+ then return
+ end
+
+ -- server still exists, check if it can be tracked for email alerts
+ mailers_track_server_events(data.reference)
+end
+
+
+-- disable legacy email-alerts since email-alerts will be sent from lua directly
+core.disable_legacy_mailers()
+
+-- event subscriptions are purposely performed in an init function to prevent
+-- email alerts from being generated too early (when process is starting up)
+core.register_init(function()
+
+ -- do nothing if not on primary thread
+ -- this prevents emails from being sent multiple times when
+ -- lua-load-per-thread is used to load the script since the task
+ -- will be started on each haproxy thread
+ if core.thread > 1 then core.done() end
+
+ -- subscribe to SERVER_ADD to be notified when new servers are added
+ core.event_sub({"SERVER_ADD"}, srv_event_add)
+
+ -- loop through existing backends to detect existing servers
+ for backend_name, backend in pairs(core.backends) do
+ for srv_name, srv in pairs(backend.servers) do
+ mailers_track_server_events(srv)
+ end
+ end
+
+end)
+
+-- mail queue
+core.register_task(function()
+ while true
+ do
+ local job = mailqueue:pop_wait()
+
+ if job ~= nil then
+ local date = os.date("%a, %d %b %Y %T %z (%Z)", job.when)
+ local c = core.concat()
+
+ -- prepare email body
+ c:add(string.format("From: %s\r\n", job.mailconf.smtp_from))
+ c:add(string.format("To: %s\r\n", job.mailconf.smtp_to))
+ c:add(string.format("Date: %s\r\n", date))
+ c:add(string.format("Subject: [HAProxy Alert] %s\r\n", job.msg))
+ c:add("\r\n")
+ c:add(string.format("%s\r\n", job.msg))
+
+ -- send email to all mailservers
+ for name, mailsrv in pairs(job.mailconf.mailservers) do
+ -- finally, send email to server
+ local ret, reason = smtp_send_email(mailsrv,
+ job.mailconf.mailservers_timeout,
+ job.mailconf.smtp_hostname,
+ job.mailconf.smtp_from,
+ job.mailconf.smtp_to,
+ c:dump())
+ if ret == false then
+ core.Warning("Can't send email alert to ".. name .. ": " .. reason)
+ end
+ end
+ end
+ end
+end)
diff --git a/examples/option-http_proxy.cfg b/examples/option-http_proxy.cfg
new file mode 100644
index 0000000..8b28f67
--- /dev/null
+++ b/examples/option-http_proxy.cfg
@@ -0,0 +1,54 @@
+#
+# demo config for Proxy mode
+#
+
+global
+ maxconn 20000
+ ulimit-n 16384
+ log 127.0.0.1 local0
+ uid 200
+ gid 200
+ chroot /var/empty
+ daemon
+
+frontend test-proxy
+ bind 192.168.200.10:8080
+ mode http
+ log global
+ option httplog
+ option dontlognull
+ maxconn 8000
+ timeout client 30s
+
+ # layer3: Valid users
+ acl allow_host src 192.168.200.150/32
+ http-request deny if !allow_host
+
+ # layer7: prevent private network relaying
+ acl forbidden_dst url_ip 192.168.0.0/24
+ acl forbidden_dst url_ip 172.16.0.0/12
+ acl forbidden_dst url_ip 10.0.0.0/8
+ http-request deny if forbidden_dst
+
+ default_backend test-proxy-srv
+
+
+backend test-proxy-srv
+ mode http
+ timeout connect 5s
+ timeout server 5s
+ retries 2
+
+ # layer7: Only GET method is valid
+ acl valid_method method GET
+ http-request deny if !valid_method
+
+ # take IP address from URL's authority
+ # and drop scheme+authority from URI
+ http-request set-dst url_ip
+ http-request set-dst-port url_port
+ http-request set-uri %[pathq]
+ server next-hop 0.0.0.0
+
+ # layer7: protect bad reply
+ http-response deny if { res.hdr(content-type) audio/mp3 }
diff --git a/examples/quick-test.cfg b/examples/quick-test.cfg
new file mode 100644
index 0000000..f27eeff
--- /dev/null
+++ b/examples/quick-test.cfg
@@ -0,0 +1,29 @@
+# Basic config mapping a listening IP:port to another host's IP:port with
+# support for HTTP/1 and 2.
+
+global
+ strict-limits # refuse to start if insufficient FDs/memory
+ # add some process-wide tuning here if required
+
+ # A stats socket may be added to check live metrics if the load generators
+ # do not report them.
+ # stats socket /tmp/haproxy.sock level admin
+ # stats timeout 1h
+
+defaults
+ mode http
+ balance random # power-of-two-choices
+ timeout client 60s
+ timeout server 60s
+ timeout connect 1s
+
+listen p
+ # this is the address and port we'll listen to, the ones to aim the
+ # load generators at
+ bind :8000
+
+ # create a certificate and uncomment this for SSL
+ # bind :8443 ssl crt my-cert.pem alpn h2,http/1.1
+
+ # Put the server's IP address and port below
+ server s1 172.31.32.33:8000
diff --git a/examples/socks4.cfg b/examples/socks4.cfg
new file mode 100644
index 0000000..2cbd417
--- /dev/null
+++ b/examples/socks4.cfg
@@ -0,0 +1,55 @@
+global
+ log /dev/log local0
+ log /dev/log local1 notice
+ stats timeout 30s
+
+defaults
+ log global
+ mode http
+ option httplog
+ option dontlognull
+ timeout connect 5000
+ timeout client 50000
+ timeout server 50000
+
+listen SMTP-20025
+ bind 0.0.0.0:20025
+ mode tcp
+ option tcplog
+ maxconn 2000
+ timeout connect 5000
+ timeout client 50000
+ timeout server 50000
+ option tcp-check
+ server SMTPS1 192.0.2.1:25 check inter 30000 fastinter 1000
+ server SMTPS2_Via_SocksProxy1 192.0.2.2:25 socks4 127.0.0.1:1080 check-via-socks4 check inter 30000 fastinter 1000 backup
+
+listen SSL-20080
+ bind 0.0.0.0:20080
+ mode tcp
+ option tcplog
+ maxconn 2000
+ timeout connect 5000
+ timeout client 50000
+ timeout server 50000
+ option tcp-check
+ server HTTPS1_Via_SocksProxy1 192.0.2.1:443 ssl verify none socks4 127.0.0.1:1080 check inter 30000 fastinter 1000
+ server HTTPS2 192.0.2.2:443 ssl verify none check inter 30000 fastinter 1000 backup
+
+# HAProxy web ui
+listen stats
+ bind 0.0.0.0:20936
+ mode http
+ log global
+
+ maxconn 10
+ timeout client 100s
+ timeout server 100s
+ timeout connect 100s
+ timeout queue 100s
+
+ stats enable
+ stats uri /haproxy?stats
+ stats realm HAProxy\ Statistics
+ stats admin if TRUE
+ stats show-node
diff --git a/examples/transparent_proxy.cfg b/examples/transparent_proxy.cfg
new file mode 100644
index 0000000..a8cf6d9
--- /dev/null
+++ b/examples/transparent_proxy.cfg
@@ -0,0 +1,55 @@
+#
+# This is an example of how to configure HAProxy to be used as a 'full transparent proxy' for a single backend server.
+#
+# Note that to actually make this work extra firewall/nat rules are required.
+# Also HAProxy needs to be compiled with support for this, in HAProxy1.5-dev19 you can check if this is the case with "haproxy -vv".
+#
+
+global
+defaults
+ timeout client 30s
+ timeout server 30s
+ timeout connect 30s
+
+frontend MyFrontend
+ bind 192.168.1.22:80
+ default_backend TransparentBack_http
+
+backend TransparentBack_http
+ mode http
+ source 0.0.0.0 usesrc client
+ server MyWebServer 192.168.0.40:80
+
+#
+# To create the the nat rules perform the following:
+#
+# ### (FreeBSD 8) ###
+# --- Step 1 ---
+# ipfw is needed to get 'reply traffic' back to the HAProxy process, this can be achieved by configuring a rule like this:
+# fwd localhost tcp from 192.168.0.40 80 to any in recv em0
+#
+# The following would be even better but this did not seam to work on the pfSense2.1 distribution of FreeBSD 8.3:
+# fwd 127.0.0.1:80 tcp from any 80 to any in recv ${outside_iface} uid ${proxy_uid}
+#
+# If only 'pf' is currently used some additional steps are needed to load and configure ipfw:
+# You need to configure this to always run on startup:
+#
+# /sbin/kldload ipfw
+# /sbin/sysctl net.inet.ip.pfil.inbound="pf" net.inet6.ip6.pfil.inbound="pf" net.inet.ip.pfil.outbound="pf" net.inet6.ip6.pfil.outbound="pf"
+# /sbin/sysctl net.link.ether.ipfw=1
+# ipfw add 10 fwd localhost tcp from 192.168.0.40 80 to any in recv em0
+#
+# the above does the following:
+# - load the ipfw kernel module
+# - set pf as the outer firewall to keep control of routing packets for example to route them to a non-default gateway
+# - enable ipfw
+# - set a rule to catches reply traffic on em0 coming from the webserver
+#
+# --- Step 2 ---
+# To also make the client connection transparent its possible to redirect incoming requests to HAProxy with a pf rule:
+# rdr on em1 proto tcp from any to 192.168.0.40 port 80 -> 192.168.1.22
+# here em1 is the interface that faces the clients, and traffic that is originally send straight to the webserver is redirected to HAProxy
+#
+# ### (FreeBSD 9) (OpenBSD 4.4) ###
+# pf supports "divert-reply" which is probably better suited for the job above then ipfw..
+#
diff --git a/examples/wurfl-example.cfg b/examples/wurfl-example.cfg
new file mode 100644
index 0000000..52df68e
--- /dev/null
+++ b/examples/wurfl-example.cfg
@@ -0,0 +1,41 @@
+#
+# This is an example of how to configure HAProxy to be used with WURFL Device Detection module.
+#
+# HAProxy needs to be compiled with support for this. See README section 1.3
+#
+
+global
+
+ # The WURFL data file
+ wurfl-data-file /usr/share/wurfl/wurfl.zip
+
+ # WURFL patches definition (as much as needed, patches will be applied in the same order as specified in this conf file)
+ #wurfl-patch-file /path/to/patch1.xml;
+
+ #wurfl-cache-size 100000
+ ## no cache
+ #wurfl-cache-size 0
+
+ wurfl-information-list-separator |
+
+ # list of WURFL capabilities, virtual capabilities, property names planned to be used in injected headers
+ wurfl-information-list wurfl_id model_name
+
+defaults
+ mode http
+ timeout connect 30s
+ timeout client 30s
+ timeout server 30s
+
+frontend TheFrontend
+ bind 192.168.1.22:80
+ default_backend TheBackend
+
+ # inject a header called X-Wurfl-All with all the WURFL information listed in wurfl-information-list
+ http-request set-header X-Wurfl-All %[wurfl-get-all()]
+
+ # inject a header called X-WURFL-PROPERTIES with the "wurfl_id" information (should be listed in wurfl-information-list)
+ #http-request set-header X-WURFL-PROPERTIES %[wurfl-get(wurfl_id)]
+
+backend TheBackend
+ server TheWebServer 192.168.0.40:80