HAProxy

This is about HAProxy introduced on the wiki.

HAProxy is free, open source software that provides a high availability load balancer and proxy server for TCP and HTTP-based applications that spreads requests across multiple servers.[3] It is written in C[4] and has a reputation for being fast and efficient (in terms of processor and memory usage).[5]

HAProxy is used by a number of high-profile websites including GoDaddy, GitHub, Bitbucket,[6] Stack Overflow,[7] Reddit, Speedtest.net, Tumblr, Twitter[8][9] and Tuenti[10] and is used in the OpsWorks product from Amazon Web Services.[11]


HAProxy is mainly used for load balancing for load balancing of web servers. It is similar to L4 switch for load balancing.
<Web server load balancing using HAProxy>

However, HAProxy can be used not only for web server load balancing, but also for load balancing or security enhancement of general TCP/IP servers. In this article, I will mainly explain the configuration for strengthening security than load balancing of general TCP/IP services.
Like a web server, a TCP/IP server providing a general TCP/IP service must establish a connection through a public IP to access from a remote location. However, as soon as the server is located in the public network, it is exposed to numerous external attacks. When providing services for clients with unspecified IPs, firewall processing is also difficult on servers that provide TCP/IP services. HAProxy is a powerful tool to protect your server from this risk. With HAProxy, the TCP/IP server needs to allow only the HAProxy IP lists in the firewall.  Therefore, the client cannot directly connect to the TCP/IP server and receive the service. The client must connect to HAProxy and receive service. Therefore, the information of the TCP/IP server is not exposed to the client.
In addition, HAProxy can be dually configured by using HA S/W such as Keepalived as shown in the following figure.



Today's test setup configuration is like this:


For simple testing, I created a TCP/IP echo server and echo client program using Python.

import socket
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--ip', type=str, default="192.168.126.133", help='target server(proxy) IP address')
parser.add_argument('--port', type=int, default=12280, help='port number')
args = parser.parse_args()

# Proxy address 
PROXY = args.ip
# Proxy port number
PORT = args.port


print('Connect To :', PROXY, ' Port:', PORT)
# Create python socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


# connect to proxy server
client_socket.connect((PROXY, PORT))


index = 1
for i in range(10):
    #send simple message
    client_socket.sendall('Hello'.encode())
    data = client_socket.recv(1024)
    #receive echo message
    print('index:%03d'%index,  ' Received', repr(data.decode()))
    index += 1
# close socket
client_socket.close()
<tcpclient.py that runs on 192.168.126.131>


import socket
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--port', type=int, default=12280, help='port number')
args = parser.parse_args()
# accept any client
HOST = ''

# port number for accept
PORT = args.port


# create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# If you restart the program, the socket might be blocked.
# To prevent this, use SO_REUSEADDR options. 
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

server_socket.bind((HOST, PORT))
server_socket.listen()

client_socket, addr = server_socket.accept()

# client information
print('Connected by', addr)

loop = True
index = 1
while loop:
    for i in range(10):
        data = client_socket.recv(1024)

        if not data:
            print('Received NULL')
            loop = False
            break
            #continue

        # Print the message received from client
        print('index:%03d'%index,  ' Received from', addr, data.decode())

        # echo the messgae
        client_socket.sendall(data)
        index += 1

client_socket.close()
server_socket.close()
<tcpserver.py that runs on 192.168.126.130>

Be careful : If the firewall of the 192.168.126.130 server does not allow tcp:12280, the service will not work. Be sure to test after changing the firewall settings or stopping the firewall service.


HAProxy Installation

Ubuntu 18.04 installation

In Ubuntu, the package version update is fast, so you can conveniently work with the package managers apt and apt-get. I always use Python3. Therefore, install the package using pip3 instead of pip.
Installing HAProxy on Ubuntu is not difficult.

apt install lua5.3  liblua5.3-dev libssl-dev libsystemd-dev
apt show haproxy
apt install -y haproxy

CentOS7 installation

On CentOS, package updates are relatively slow. If you check it in CentOS 7.5.1804 version, it is haproxy 1.5.18 version.

[root@gcloud-seoul /]# yum info haproxy
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
 * base: ftp.kaist.ac.kr
 * epel: ftp.riken.jp
 * extras: ftp.kaist.ac.kr
 * nux-dextop: mirror.li.nux.ro
 * updates: ftp.kaist.ac.kr
Available Packages
Name        : haproxy
Arch        : x86_64
Version     : 1.5.18
Release     : 9.el7
Size        : 834 k
Repo        : base/7/x86_64
Summary     : TCP/HTTP proxy and load balancer for high availability environments
URL         : http://www.haproxy.org/
License     : GPLv2+
Description : HAProxy is a TCP/HTTP reverse proxy which is particularly suited for high
            : availability environments. Indeed, it can:
            :  - route HTTP requests depending on statically assigned cookies
            :  - spread load among several servers while assuring server persistence
            :    through the use of HTTP cookies
            :  - switch to backup servers in the event a main server fails
            :  - accept connections to special ports dedicated to service monitoring
            :  - stop accepting connections without breaking existing ones
            :  - add, modify, and delete HTTP headers in both directions
            :  - block requests matching particular patterns
            :  - report detailed status to authenticated users from a URI
            :    intercepted by the application


Since the haproxy version is too low, I will try to build the source code.

#install required packages first
#haproxy needs lua language, update or install lua 5.3
#check current lua version, in my case lua 5.1 is installed
[root@gcloud-seoul lua-5.3.5]# lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio

yum install gcc openssl-devel readline-devel systemd-devel make pcre-devel
cd /usr/local/src
wget https://www.lua.org/ftp/lua-5.3.5.tar.gz 
wget http://www.haproxy.org/download/2.2/src/haproxy-2.2.0.tar.gz 



#build lua and haproxy
tar -xvzf lua-5.3.5.tar.gz 
tar -xvzf haproxy-2.2.0.tar.gz 
#build lua 5.3
cd lua-5.3.5
make INSTALL_TOP=/opt/lua-5.3.5 linux install

#backup old lua if exists and update the lua to 5.3.5
mv /usr/bin/lua /usr/bin/lua.old
ln -s /opt/lua-5.3.5/bin/lua /usr/bin/lua


#build haproxy 2.2.0
cd /usr/local/src/haproxy-2.2.0
[root@gcloud-seoul haproxy-2.2.0]# make USE_NS=1 \
USE_TFO=1 \
USE_OPENSSL=1 \
USE_ZLIB=1 \
USE_LUA=1 \
USE_PCRE=1 \
USE_SYSTEMD=1 \
USE_LIBCRYPT=1 \
USE_THREAD=1 \
TARGET=linux-glibc \
LUA_INC=/opt/lua-5.3.5/include \
LUA_LIB=/opt/lua-5.3.5/lib 

[root@gcloud-seoul haproxy-2.2.0]# make install
[root@gcloud-seoul haproxy-2.2.0]# whereis haproxy
haproxy: /usr/local/sbin/haproxy
[root@gcloud-seoul haproxy-2.2.0]# ln -s /usr/local/sbin/haproxy /usr/sbin/haproxy 


When installation is complete, start the haproxy configuration.

#Setting up HAProxy for your server
mkdir -p /etc/haproxy
mkdir -p /var/lib/haproxy 
touch /var/lib/haproxy/stats 

Now let's make haproxy as a service.
The init file in the example directory is for ubuntu. Therefore, do not use this file, but use the following script.

copy this file to /etc/init.d/haproxy and to grant execution rights do "chmod 755 /etc/init.d/haproxy" command.

#!/bin/sh
#
# Init Script to run HaProxy in daemon mode at boot time.
#
# Author: Riccardo Riva - RPM S.r.l.
# Revision 1.0  -  2011 November, 18</pre>

#====================================================================
# Run level information:
#
# chkconfig: 2345 85 15
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited
#              for high availability environments.
# processname: haproxy
# config: /etc/haproxy.cfg
# pidfile: /var/run/haproxy.pid
#
# Run "/sbin/chkconfig --add haproxy" to add the Run levels.
# This will setup the symlinks and set the process to run at boot.
#====================================================================

# 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

[ -f /etc/haproxy/haproxy.cfg ] || exit 1

RETVAL=0

start() {
  /usr/local/sbin/haproxy -c -q -f /etc/haproxy/haproxy.cfg
  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file."
    return 1
  fi

  echo -n "Starting HAproxy: "
  daemon /usr/local/sbin/haproxy -D -f /etc/haproxy/haproxy.cfg -p /var/run/haproxy.pid
  RETVAL=$?
  echo
  [ $RETVAL -eq 0 ] && touch /var/lock/subsys/haproxy
  return $RETVAL
}

stop() {
  echo -n "Shutting down HAproxy: "
  killproc haproxy -USR1
  RETVAL=$?
  echo
  [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/haproxy
  [ $RETVAL -eq 0 ] && rm -f /var/run/haproxy.pid
  return $RETVAL
}

restart() {
  /usr/local/sbin/haproxy -c -q -f /etc/haproxy/haproxy.cfg
  if [ $? -ne 0 ]; then
    echo "Errors found in configuration file, check it with 'haproxy check'."
    return 1
  fi
  stop
  start
}

check() {
  /usr/local/sbin/haproxy -c -q -V -f /etc/haproxy/haproxy.cfg
}

rhstatus() {
  status haproxy
}

condrestart() {
  [ -e /var/lock/subsys/haproxy ] && restart || :
}

# See how we were called.
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  restart)
    restart
    ;;
  reload)
    restart
    ;;
  condrestart)
    condrestart
    ;;
  status)
    rhstatus
    ;;
  check)
    check
    ;;
  *)
    echo $"Usage: haproxy {start|stop|restart|reload|condrestart|status|check}"
    RETVAL=1
esac

exit $RETVAL
<haproxy.sample>


cp haproxy.sample /etc/init.d/haproxy
chmod 755 /etc/init.d/haproxy
chkconfig haproxy on
systemctl daemon-reload

Configuration

After installation, edit the /etc/haproxy/haproxy.cfg file. After modifying the haproxy.cfg file, be sure to reload the service settings with the "systemctl relaod haproxy" command.

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon

        # Default SSL material locations
        ca-base /etc/ssl/certs
        crt-base /etc/ssl/private

        # Default ciphers to use on SSL-enabled listening sockets.
        # For more information, see ciphers(1SSL). This list is from:
        #  https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
        # An alternative list with additional directives can be obtained from
        #  https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
        ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
        ssl-default-bind-options no-sslv3

defaults
        log     global
        mode    tcp
        timeout client  50000
        timeout server  50000

frontend cti_1_front
   bind *:12280
   mode tcp
   default_backend cti_1_back

frontend cti_2_front
   bind *:12281
   default_backend cti_2_back

backend cti_1_back
   balance roundrobin
   server server_1 192.168.126.130:12280 check

backend cti_2_back
   balance roundrobin
   server server_1 192.168.126.130:12282 check

The most important parts are the bottom frontend cti_1_front, frontend cti_2_front, backend cti_1_back, and backend cti_2_back. In brief, it is as follows.

  • frontend cti_1_front : Connection through HAProxy 12280 port is regarded as general TCP communication and routed to backend cti_1_back.
  • frontend cti_2_front : Connection through HAProxy 12281 port is regarded as general TCP communication and routed to backend cti_2_back.
  • backend cti_1_back : Route to 12280 port on 192.168.126.130 server.
  • backend cti_2_back : Route to 12282 port on 192.168.126.130 server. Note that the port number has changed.
Documentation on configuring the cfg file is detailed in https://www.haproxy.org/download/1.4/doc/configuration.txt.
I will only explain the important parts.

bind

bind specifies the connection port requested by the client.

bind [<address>]:<port_range> [, ...]
bind [<address>]:<port_range> [, ...] interface <interface>
bind [<address>]:<port_range> [, ...] mss <maxseg>
bind [<address>]:<port_range> [, ...] transparent
bind [<address>]:<port_range> [, ...] id <id>
bind [<address>]:<port_range> [, ...] name <name>
bind [<address>]:<port_range> [, ...] defer-accept
  Define one or several listening addresses and/or ports in a frontend.
  May be used in sections :   defaults | frontend | listen | backend
                                  no   |    yes   |   yes  |   no
  Arguments :
    <address>     is optional and can be a host name, an IPv4 address, an IPv6
                  address, or '*'. It designates the address the frontend will
                  listen on. If unset, all IPv4 addresses of the system will be
                  listened on. The same will apply for '*' or the system's
                  special address "0.0.0.0".

    <port_range>  is either a unique TCP port, or a port range for which the
                  proxy will accept connections for the IP address specified
                  above. The port is mandatory. Note that in the case of an
                  IPv6 address, the port is always the number after the last
                  colon (':'). A range can either be :
                   - a numerical port (ex: '80')
                   - a dash-delimited ports range explicitly stating the lower
                     and upper bounds (ex: '2000-2100') which are included in
                     the range.

                  Particular care must be taken against port ranges, because
                  every <address:port> couple consumes one socket (= a file
                  descriptor), so it's easy to consume lots of descriptors
                  with a simple range, and to run out of sockets. Also, each
                  <address:port> couple must be used only once among all
                  instances running on a same system. Please note that binding
                  to ports lower than 1024 generally require particular
                  privileges to start the program, which are independant of
                  the 'uid' parameter.

    <interface>   is an optional physical interface name. This is currently
                  only supported on Linux. The interface must be a physical
                  interface, not an aliased interface. When specified, all
                  addresses on the same line will only be accepted if the
                  incoming packet physically come through the designated
                  interface. It is also possible to bind multiple frontends to
                  the same address if they are bound to different interfaces.
                  Note that binding to a physical interface requires root
                  privileges.

    <maxseg>      is an optional TCP Maximum Segment Size (MSS) value to be
                  advertised on incoming connections. This can be used to force
                  a lower MSS for certain specific ports, for instance for
                  connections passing through a VPN. Note that this relies on a
                  kernel feature which is theorically supported under Linux but
                  was buggy in all versions prior to 2.6.28. It may or may not
                  work on other operating systems. The commonly advertised
                  value on Ethernet networks is 1460 = 1500(MTU) - 40(IP+TCP).

    <id>          is a persistent value for socket ID. Must be positive and
                  unique in the proxy. An unused value will automatically be
                  assigned if unset. Can only be used when defining only a
                  single socket.

    <name>        is an optional name provided for stats

    transparent   is an optional keyword which is supported only on certain
                  Linux kernels. It indicates that the addresses will be bound
                  even if they do not belong to the local machine. Any packet
                  targeting any of these addresses will be caught just as if
                  the address was locally configured. This normally requires
                  that IP forwarding is enabled. Caution! do not use this with
                  the default address '*', as it would redirect any traffic for
                  the specified port. This keyword is available only when
                  HAProxy is built with USE_LINUX_TPROXY=1.

    defer-accept  is an optional keyword which is supported only on certain
                  Linux kernels. It states that a connection will only be
                  accepted once some data arrive on it, or at worst after the
                  first retransmit. This should be used only on protocols for
                  which the client talks first (eg: HTTP). It can slightly
                  improve performance by ensuring that most of the request is
                  already available when the connection is accepted. On the
                  other hand, it will not be able to detect connections which
                  don't talk. It is important to note that this option is
                  broken in all kernels up to 2.6.31, as the connection is
                  never accepted until the client talks. This can cause issues
                  with front firewalls which would see an established
                  connection while the proxy will only see it in SYN_RECV.

  It is possible to specify a list of address:port combinations delimited by
  commas. The frontend will then listen on all of these addresses. There is no
  fixed limit to the number of addresses and ports which can be listened on in
  a frontend, as well as there is no limit to the number of "bind" statements
  in a frontend.

  Example :
        listen http_proxy
            bind :80,:443
            bind 10.0.0.1:10080,10.0.0.1:10443

  See also : "source".



mode

mode has one of tcp, http, and health. Generally, web server load balancing uses http, but tcp is used to route user-created tcp/ip services.

mode { tcp|http|health }
  Set the running mode or protocol of the instance
  May be used in sections :   defaults | frontend | listen | backend
                                 yes   |    yes   |   yes  |   yes
  Arguments :
    tcp       The instance will work in pure TCP mode. A full-duplex connection
              will be established between clients and servers, and no layer 7
              examination will be performed. This is the default mode. It
              should be used for SSL, SSH, SMTP, ...

    http      The instance will work in HTTP mode. The client request will be
              analyzed in depth before connecting to any server. Any request
              which is not RFC-compliant will be rejected. Layer 7 filtering,
              processing and switching will be possible. This is the mode which
              brings HAProxy most of its value.

    health    The instance will work in "health" mode. It will just reply "OK"
              to incoming connections and close the connection. Nothing will be
              logged. This mode is used to reply to external components health
              checks. This mode is deprecated and should not be used anymore as
              it is possible to do the same and even better by combining TCP or
              HTTP modes with the "monitor" keyword.

  When doing content switching, it is mandatory that the frontend and the
  backend are in the same mode (generally HTTP), otherwise the configuration
  will be refused.

  Example :
     defaults http_instances
         mode http



balance

balance specifies how client requests are distributed. In general, round robin is used the most. It can be used when there is only one server, and in this case, the client's connection is routed to only one server.

balance <algorithm> [ <arguments> ]
balance url_param <param> [check_post [<max_wait>]]
  Define the load balancing algorithm to be used in a backend.
  May be used in sections :   defaults | frontend | listen | backend
                                 yes   |    no    |   yes  |   yes
  Arguments :
    <algorithm> is the algorithm used to select a server when doing load
                balancing. This only applies when no persistence information
                is available, or when a connection is redispatched to another
                server. <algorithm> may be one of the following :

      roundrobin  Each server is used in turns, according to their weights.
                  This is the smoothest and fairest algorithm when the server's
                  processing time remains equally distributed. This algorithm
                  is dynamic, which means that server weights may be adjusted
                  on the fly for slow starts for instance. It is limited by
                  design to 4095 active servers per backend. Note that in some
                  large farms, when a server becomes up after having been down
                  for a very short time, it may sometimes take a few hundreds
                  requests for it to be re-integrated into the farm and start
                  receiving traffic. This is normal, though very rare. It is
                  indicated here in case you would have the chance to observe
                  it, so that you don't worry.

      static-rr   Each server is used in turns, according to their weights.
                  This algorithm is as similar to roundrobin except that it is
                  static, which means that changing a server's weight on the
                  fly will have no effect. On the other hand, it has no design
                  limitation on the number of servers, and when a server goes
                  up, it is always immediately reintroduced into the farm, once
                  the full map is recomputed. It also uses slightly less CPU to
                  run (around -1%).

      leastconn   The server with the lowest number of connections receives the
                  connection. Round-robin is performed within groups of servers
                  of the same load to ensure that all servers will be used. Use
                  of this algorithm is recommended where very long sessions are
                  expected, such as LDAP, SQL, TSE, etc... but is not very well
                  suited for protocols using short sessions such as HTTP. This
                  algorithm is dynamic, which means that server weights may be
                  adjusted on the fly for slow starts for instance.

      source      The source IP address is hashed and divided by the total
                  weight of the running servers to designate which server will
                  receive the request. This ensures that the same client IP
                  address will always reach the same server as long as no
                  server goes down or up. If the hash result changes due to the
                  number of running servers changing, many clients will be
                  directed to a different server. This algorithm is generally
                  used in TCP mode where no cookie may be inserted. It may also
                  be used on the Internet to provide a best-effort stickiness
                  to clients which refuse session cookies. This algorithm is
                  static by default, which means that changing a server's
                  weight on the fly will have no effect, but this can be
                  changed using "hash-type".

      uri         This algorithm hashes either the left part of the URI (before
                  the question mark) or the whole URI (if the "whole" parameter
                  is present) and divides the hash value by the total weight of
                  the running servers. The result designates which server will
                  receive the request. This ensures that the same URI will
                  always be directed to the same server as long as no server
                  goes up or down. This is used with proxy caches and
                  anti-virus proxies in order to maximize the cache hit rate.
                  Note that this algorithm may only be used in an HTTP backend.
                  This algorithm is static by default, which means that
                  changing a server's weight on the fly will have no effect,
                  but this can be changed using "hash-type".

                  This algorithm supports two optional parameters "len" and
                  "depth", both followed by a positive integer number. These
                  options may be helpful when it is needed to balance servers
                  based on the beginning of the URI only. The "len" parameter
                  indicates that the algorithm should only consider that many
                  characters at the beginning of the URI to compute the hash.
                  Note that having "len" set to 1 rarely makes sense since most
                  URIs start with a leading "/".

                  The "depth" parameter indicates the maximum directory depth
                  to be used to compute the hash. One level is counted for each
                  slash in the request. If both parameters are specified, the
                  evaluation stops when either is reached.

      url_param   The URL parameter specified in argument will be looked up in
                  the query string of each HTTP GET request.

                  If the modifier "check_post" is used, then an HTTP POST
                  request entity will be searched for the parameter argument,
                  when it is not found in a query string after a question mark
                  ('?') in the URL. Optionally, specify a number of octets to
                  wait for before attempting to search the message body. If the
                  entity can not be searched, then round robin is used for each
                  request. For instance, if your clients always send the LB
                  parameter in the first 128 bytes, then specify that. The
                  default is 48. The entity data will not be scanned until the
                  required number of octets have arrived at the gateway, this
                  is the minimum of: (default/max_wait, Content-Length or first
                  chunk length). If Content-Length is missing or zero, it does
                  not need to wait for more data than the client promised to
                  send. When Content-Length is present and larger than
                  <max_wait>, then waiting is limited to <max_wait> and it is
                  assumed that this will be enough data to search for the
                  presence of the parameter. In the unlikely event that
                  Transfer-Encoding: chunked is used, only the first chunk is
                  scanned. Parameter values separated by a chunk boundary, may
                  be randomly balanced if at all.

                  If the parameter is found followed by an equal sign ('=') and
                  a value, then the value is hashed and divided by the total
                  weight of the running servers. The result designates which
                  server will receive the request.

                  This is used to track user identifiers in requests and ensure
                  that a same user ID will always be sent to the same server as
                  long as no server goes up or down. If no value is found or if
                  the parameter is not found, then a round robin algorithm is
                  applied. Note that this algorithm may only be used in an HTTP
                  backend. This algorithm is static by default, which means
                  that changing a server's weight on the fly will have no
                  effect, but this can be changed using "hash-type".

      hdr(<name>) The HTTP header <name> will be looked up in each HTTP request.
                  Just as with the equivalent ACL 'hdr()' function, the header
                  name in parenthesis is not case sensitive. If the header is
                  absent or if it does not contain any value, the roundrobin
                  algorithm is applied instead.

                  An optional 'use_domain_only' parameter is available, for
                  reducing the hash algorithm to the main domain part with some
                  specific headers such as 'Host'. For instance, in the Host
                  value "haproxy.1wt.eu", only "1wt" will be considered.

                  This algorithm is static by default, which means that
                  changing a server's weight on the fly will have no effect,
                  but this can be changed using "hash-type".

      rdp-cookie
      rdp-cookie(name)
                  The RDP cookie <name> (or "mstshash" if omitted) will be
                  looked up and hashed for each incoming TCP request. Just as
                  with the equivalent ACL 'req_rdp_cookie()' function, the name
                  is not case-sensitive. This mechanism is useful as a degraded
                  persistence mode, as it makes it possible to always send the
                  same user (or the same session ID) to the same server. If the
                  cookie is not found, the normal roundrobin algorithm is
                  used instead.

                  Note that for this to work, the frontend must ensure that an
                  RDP cookie is already present in the request buffer. For this
                  you must use 'tcp-request content accept' rule combined with
                  a 'req_rdp_cookie_cnt' ACL.

                  This algorithm is static by default, which means that
                  changing a server's weight on the fly will have no effect,
                  but this can be changed using "hash-type".

    <arguments> is an optional list of arguments which may be needed by some
                algorithms. Right now, only "url_param" and "uri" support an
                optional argument.

                balance uri [len <len>] [depth <depth>]
                balance url_param <param> [check_post [<max_wait>]]

  The load balancing algorithm of a backend is set to roundrobin when no other
  algorithm, mode nor option have been set. The algorithm may only be set once
  for each backend.

  Examples :
        balance roundrobin
        balance url_param userid
        balance url_param session_id check_post 64
        balance hdr(User-Agent)
        balance hdr(host)
        balance hdr(Host) use_domain_only

  Note: the following caveats and limitations on using the "check_post"
  extension with "url_param" must be considered :

    - all POST requests are eligible for consideration, because there is no way
      to determine if the parameters will be found in the body or entity which
      may contain binary data. Therefore another method may be required to
      restrict consideration of POST requests that have no URL parameters in
      the body. (see acl reqideny http_end)

    - using a <max_wait> value larger than the request buffer size does not
      make sense and is useless. The buffer size is set at build time, and
      defaults to 16 kB.

    - Content-Encoding is not supported, the parameter search will probably
      fail; and load balancing will fall back to Round Robin.

    - Expect: 100-continue is not supported, load balancing will fall back to
      Round Robin.

    - Transfer-Encoding (RFC2616 3.6.1) is only supported in the first chunk.
      If the entire parameter value is not present in the first chunk, the
      selection of server is undefined (actually, defined by how little
      actually appeared in the first chunk).

    - This feature does not support generation of a 100, 411 or 501 response.

    - In some cases, requesting "check_post" MAY attempt to scan the entire
      contents of a message body. Scanning normally terminates when linear
      white space or control characters are found, indicating the end of what
      might be a URL parameter list. This is probably not a concern with SGML
      type message bodies.

  See also : "dispatch", "cookie", "appsession", "transparent", "hash-type" and
             "http_proxy".




timeout client, timeout server

HAProxy terminates the connection if there is no packet transmission from the client or server during this time. If the service without a packet transmission can last a long time, set this value large.
The unit can be us, ms, s, m, h, d.
If set to 2d, it means that the connection is maintained even if there is no data transmission for 2 days.
The values ​​of timeout client and timeout server generally apply the same values.

    Some parameters involve values representing time, such as timeouts. These
    values are generally expressed in milliseconds (unless explicitly stated
    otherwise) but may be expressed in any other unit by suffixing the unit to the
    numeric value. It is important to consider this because it will not be repeated
    for every keyword. Supported units are :

    - us : microseconds. 1 microsecond = 1/1000000 second
    - ms : milliseconds. 1 millisecond = 1/1000 second. This is the default.
    - s  : seconds. 1s = 1000ms
    - m  : minutes. 1m = 60s = 60000ms
    - h  : hours.   1h = 60m = 3600s = 3600000ms
    - d  : days.    1d = 24h = 1440m = 86400s = 86400000ms


In the cfg file of the above example, 50000ms (50 seconds) was specified.

Be Careful : If this value is neglected, a connection may be suddenly terminated. Adjust according to the characteristics of the service.


If the configuration of the cfg file is completed, restart the HAProxy service of Ububtu Server.

#if started
systemctl reload haproxy
#if not started
systemctl start haproxy

Then run the tcpserver example on the server with the address 192.168.126.130. Then, run the tcpclient example on the client with the address 192.168.126.131. If you look at this example code, you can see that you are connecting to the HAProxy server 133, not the 130 server.

Be Careful : In this way, when a client directly accesses the address of a proxy server, the proxy server is called a reverse proxy.


#Run at 192.168.126.130
[root@zabbix-server python]# python3 tcpserver.py --port=12282


#Run at 192.168.126.131
[root@zabbix-server python]# python3 tcpclient.py --ip='192.168.126.133' --port=12281

As can be seen in the figure below, the service requested in 131 was delivered to server 130 via HAProxy 133. And it can be seen that the response from server 130 is transmitted back to 131 through HAProxy.


Wrapping up

HAProxy is not only for web server load balancing, but also for load balancing of general TCP/IP services or security enhancement of TCP/IP servers.











댓글

이 블로그의 인기 게시물

Connecting to SQL Server on Raspberry Pi

Making VoIP Phone Using Raspberry Pi

MQTT - Mosquitto MQTT Broker setup on the Ubuntu 20.04