├── CHANGELOG.md ├── Readme.md ├── docker-compose.yml └── haproxy ├── Dockerfile ├── Readme.md ├── docker-entrypoint.sh └── src ├── configure.py ├── haproxy.cfg ├── track_dns └── track_hosts /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2023-03-09 (1.8-1.8) 4 | 5 | - Upgrade HAproxy to 1.8.31 6 | 7 | ## 2021-06-14 (1.8-1.7) 8 | 9 | - Upgrade HAProxy to 1.8.30 10 | - Add `COOKIES_NAME` parameter to configure cookie name 11 | - Add `HTTPCHK_HOST` parameter to allow health check to include host HTTP Header - refs #20 12 | 13 | ## 2021-03-23 (1.8-1.6) 14 | 15 | - Upgrade HAProxy to 1.8.29 16 | - Fixed code to work with new HAProxy configuration location - /usr/local/etc/haproxy/haproxy.cfg 17 | - Fix backend port when `DNS_ENABLED` - refs #24 18 | 19 | ## 2019-11-28 (1.8-1.5) 20 | 21 | - Add `COOKIES_PARAMS` variable to give the possibility to add expiration time to cookies 22 | 23 | ## 2019-11-22 (1.8-1.4) 24 | 25 | - Upgrade HAproxy to 1.8.22 26 | - Fix BACKENDS_MODE typo, set the default values of FRONTEND_MODE and BACKENDS_MODE to depend on each other 27 | - Only enable /track_hosts cron when BACKENDS and DNS_ENABLED env vars are not present. 28 | - Only add http check option when backend is of type http. 29 | 30 | ## 2018-11-21 (1.8-1.3) 31 | 32 | - Upgrade to haproxy 1.8.14 33 | - Move the restart of rsyslog and cron to run at every docker start 34 | 35 | ## 2018-08-02 (1.8-1.2) 36 | 37 | - Upgrade to haproxy 1.8.13 38 | 39 | ## 2018-04-11 (1.8-1.1) 40 | 41 | - Fix entrypoint to work when haproxy.cfg externally is provided. 42 | 43 | ## 2017-12-05 (1.8-1.0) 44 | 45 | - Upgrade to haproxy 1.8 46 | 47 | - Possibility to change frontend/backend `mode` to `TCP` via environment variables `FRONTEND_MODE` and `BACKENDS_MODE` [refs #10] 48 | 49 | ## 2017-11-30 (1.7-4.1) 50 | 51 | - bugfix add LOG_LEVEL to /etc/environment as well 52 | 53 | 54 | ## 2017-11-22 (1.7-4.0) 55 | 56 | - Remove chaperone in favour of cron 57 | 58 | - Release 4.0 59 | 60 | ## 2017-03-22 61 | 62 | - Fix /track_dns cron when no backend is up 63 | 64 | - Refactor /track_hosts: use chaperone cron instead of inotify 65 | 66 | - Remove unnecessary image layers 67 | 68 | ## 2017-03-09 69 | 70 | - Add possibility to customize backend health check via environment variables 71 | 72 | ## 2017-02-22 73 | 74 | - Fix HAProxy version inside 1.6 tag 75 | 76 | - Release HAProxy 1.7 77 | 78 | ## 2017-02-17 79 | 80 | - surround cookie with quotas [chiridra refs #82353] 81 | 82 | ## 2017-01-30 83 | 84 | - Fixed session stickiness [chiridra refs #81199] 85 | 86 | ## 2017-01-03 87 | 88 | - Fix DNS lookup to work with latest Rancher version (1.2.0+) 89 | 90 | ## 2016-04-18 91 | 92 | - Support for named backends resolved by an internal/external DNS service (e.g. when deployed using rancher-compose) 93 | 94 | ## 2016-01-15 95 | 96 | - Start HAProxy on port *5000* instead of *80* 97 | 98 | - Start all processes with *haproxy* user instead of *root* 99 | 100 | - Added chaperone process manager 101 | 102 | - Improved haproxy auto-reloading backends 103 | 104 | - Fixed issue #2: How to specify to eeacms/haproxy a different web app port? 105 | Added possibility to define backends port via $BACKENDS_PORT env 106 | 107 | ## 2015-07-20 108 | 109 | - Initial public release 110 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | haproxy/Readme.md -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | haproxy: 4 | image: eeacms/haproxy 5 | depends_on: 6 | - webapp 7 | ports: 8 | - "80:8080" 9 | - "1936:1936" 10 | environment: 11 | FRONTEND_PORT: "8080" 12 | BACKENDS: "webapp" 13 | BACKENDS_PORT: "8080" 14 | DNS_ENABLED: "True" 15 | HTTPCHK: "GET /" 16 | INTER: "5s" 17 | LOG_LEVEL: "info" 18 | webapp: 19 | image: eeacms/hello 20 | environment: 21 | PORT: "8080" 22 | -------------------------------------------------------------------------------- /haproxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM haproxy:1.8.31-buster 2 | LABEL maintainer="EEA: IDM2 A-Team " 3 | 4 | RUN apt-get update \ 5 | && apt-get install -y --no-install-recommends \ 6 | ca-certificates \ 7 | python3-pip \ 8 | cron \ 9 | rsyslog \ 10 | && apt-get clean \ 11 | && rm -rf /var/lib/apt/lists/* \ 12 | && ln -s /usr/local/etc/haproxy /etc/haproxy \ 13 | && sed -i '/#cron./c\cron.* \/proc\/1\/fd\/1' /etc/rsyslog.conf \ 14 | && sed -i '/#$ModLoad imudp/c\$ModLoad imudp' /etc/rsyslog.conf \ 15 | && sed -i '/#$UDPServerRun/c\$UDPServerRun 514' /etc/rsyslog.conf \ 16 | && sed -i '/$UDPServerRun 514/a $UDPServerAddress 127.0.0.1' /etc/rsyslog.conf \ 17 | && sed -i '/cron.*/a local2.* \/proc\/1\/fd\/1' /etc/rsyslog.conf \ 18 | && mv /usr/local/bin/docker-entrypoint.sh /usr/local/bin/haproxy-entrypoint.sh 19 | 20 | COPY src/haproxy.cfg /tmp/ 21 | COPY src/configure.py src/track_hosts src/track_dns / 22 | COPY docker-entrypoint.sh /usr/local/bin/ 23 | 24 | 25 | -------------------------------------------------------------------------------- /haproxy/Readme.md: -------------------------------------------------------------------------------- 1 | ## HAProxy Docker image 2 | 3 | This image is generic, thus you can obviously re-use it within 4 | your non-related EEA projects. 5 | 6 | - Debian: **Buster** 7 | - HAProxy: **1.8** 8 | - Expose: **5000** 9 | 10 | ### Supported tags and respective Dockerfile links 11 | 12 | - `:latest` [*Dockerfile*](https://github.com/eea/eea.docker.haproxy/blob/master/haproxy/Dockerfile) - Debian: **Buster**, HAProxy: **1.8** 13 | 14 | ### Stable and immutable tags 15 | 16 | - `:1.8-1.8` [*Dockerfile*](https://github.com/eea/eea.docker.haproxy/tree/1.8-1.8/haproxy/Dockerfile) - HAProxy: **1.8.31** Release: **1.8** 17 | - `:1.8-1.7` [*Dockerfile*](https://github.com/eea/eea.docker.haproxy/tree/1.8-1.7/haproxy/Dockerfile) - HAProxy: **1.8.30** Release: **1.7** 18 | - `:1.8-1.6` [*Dockerfile*](https://github.com/eea/eea.docker.haproxy/tree/1.8-1.6/haproxy/Dockerfile) - HAProxy: **1.8.29** Release: **1.6** 19 | - `:1.8-1.5` [*Dockerfile*](https://github.com/eea/eea.docker.haproxy/tree/1.8-1.5/haproxy/Dockerfile) - HAProxy: **1.8.22** Release: **1.5** 20 | 21 | 22 | See [older versions](https://github.com/eea/eea.docker.haproxy/releases) 23 | 24 | 25 | ### Changes 26 | 27 | - [CHANGELOG.md](https://github.com/eea/eea.docker.haproxy/blob/master/CHANGELOG.md) 28 | 29 | ### Base docker image 30 | 31 | - [hub.docker.com](https://hub.docker.com/r/eeacms/haproxy) 32 | 33 | 34 | ### Source code 35 | 36 | - [github.com](http://github.com/eea/eea.docker.haproxy) 37 | 38 | 39 | ### Installation 40 | 41 | 1. Install [Docker](https://www.docker.com/) 42 | 2. Install [Docker Compose](https://docs.docker.com/compose/install/). 43 | 44 | ## Usage 45 | 46 | 47 | ### Run with Docker Compose 48 | 49 | Here is a basic example of a `docker-compose.yml` file using the `eeacms/haproxy` docker image: 50 | 51 | version: "2" 52 | services: 53 | haproxy: 54 | image: eeacms/haproxy 55 | depends_on: 56 | - webapp 57 | ports: 58 | - "80:5000" 59 | - "1936:1936" 60 | environment: 61 | BACKENDS: "webapp" 62 | DNS_ENABLED: "true" 63 | LOG_LEVEL: "info" 64 | 65 | webapp: 66 | image: eeacms/hello 67 | 68 | 69 | The application can be scaled to use more server instances, with `docker-compose scale`: 70 | 71 | $ docker-compose up -d --scale webapp=4 72 | 73 | The results can be checked in a browser, navigating to http://localhost. 74 | By refresing the page multiple times it is noticeable that the IP of the server 75 | that served the page changes, as HAProxy switches between them. 76 | The stats page can be accessed at http://localhost:1936 where you have to log in 77 | using the `STATS_AUTH` authentication details (default `admin:admin`). 78 | 79 | Note that it may take **up to one minute** until backends are plugged-in due to the 80 | minimum possible `DNS_TTL`. 81 | 82 | 83 | ### Run with backends specified as environment variable 84 | 85 | $ docker run --env BACKENDS="192.168.1.5:80 192.168.1.6:80" eeacms/haproxy 86 | 87 | Using the `BACKENDS` variable is a way to quick-start the container. 88 | The servers are written as `server_ip:server_listening_port`, 89 | separated by spaces (and enclosed in quotes, to avoid issues). 90 | The contents of the variable are evaluated in a python script that writes 91 | the HAProxy configuration file automatically. 92 | 93 | If there are multiple DNS records for one or more of your `BACKENDS` (e.g. when deployed using rancher-compose), 94 | you can use `DNS_ENABLED` environment variable. This way, haproxy will load-balance 95 | all of your backends instead of only the first entry found: 96 | 97 | $ docker run --link=webapp -e BACKENDS="webapp" -e DNS_ENABLED=true eeacms/haproxy 98 | 99 | 100 | ### Use a custom configuration file mounted as a volume 101 | 102 | $ docker run -v conf.d/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg eeacms/haproxy:latest 103 | 104 | 105 | If you edit `haproxy.cfg` you can reload it without having to restart the container: 106 | 107 | $ docker exec reload 108 | 109 | 110 | ### Extend the image with a custom haproxy.cfg file 111 | 112 | Additionally, you can supply your own static `haproxy.cfg` file by extending the image 113 | 114 | FROM eeacms/haproxy:latest 115 | COPY conf.d/haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg 116 | 117 | RUN apt-get install... 118 | 119 | and then run 120 | 121 | $ docker build -t your-image-name:your-image-tag path/to/Dockerfile 122 | 123 | ## Supported environment variables ## 124 | 125 | As HAProxy has close to no purpose by itself, this image should be used in 126 | combination with others (for example with [Docker Compose](https://docs.docker.com/compose/)). 127 | 128 | HAProxy can be configured by modifying the following env variables, 129 | either when running the container or in a `docker-compose.yml` file. 130 | 131 | * `STATS_PORT` The port to bind statistics to - default `1936` 132 | * `STATS_AUTH` The authentication details (written as `user:password` for the statistics page - default `admin:admin` 133 | * `FRONTEND_NAME` The label of the frontend - default `http-frontend` 134 | * `FRONTEND_PORT` The port to bind the frontend to - default `5000` 135 | * `FRONTEND_MODE` Frontend mode - default `http` or `BACKENDS_MODE` if declared 136 | * `PROXY_PROTOCOL_ENABLED` The option to enable or disable accepting proxy protocol (`true` stands for enabled, `false` or anything else for disabled) - default `false` 137 | * `COOKIES_ENABLED` The option to enable or disable cookie-based sessions (`true` stands for enabled, `false` or anything else for disabled) - default `false` 138 | * `COOKIES_NAME` Will be added on cookie declaration - default `SRV_ID` 139 | * `COOKIES_PARAMS` Will be added on cookie declaration - example `indirect nocache maxidle 30m maxlife 8h` or `maxlife 24h` - documentation https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4-cookie 140 | * `BACKEND_NAME` The label of the backend - default `http-backend` 141 | * `BACKENDS` The list of `server_ip:server_listening_port` to be load-balanced by HAProxy, separated by space - by default it is not set 142 | * `BACKENDS_PORT` Port to use when auto-discovering backends, or when `BACKENDS` are specified without port - by default `80` 143 | * `BACKENDS_MODE` Backends mode - default `http` or `FRONTEND_MODE` if declared 144 | * `BALANCE` The algorithm used for load-balancing - default `roundrobin` 145 | * `SERVICE_NAMES` An optional prefix for services to be included when discovering services separated by space. - by default it is not set 146 | * `LOGGING` Override logging ip address:port - default is udp `127.0.0.1:514` inside container 147 | * `LOG_LEVEL` Set haproxy log level, default is `notice` ( only send important events ). Can be: `emerg`,`alert`,`crit`,`err`,`warning`,`notice`,`info`,`debug` 148 | * `DNS_ENABLED` DNS lookup provided `BACKENDS`. Use this option when your backends are resolved by an internal/external DNS service (e.g. **Docker 1.11+**, **Rancher**) 149 | * `DNS_TTL` DNS lookup backends every `DNS_TTL` minutes. Default `1` minute. 150 | * `TIMEOUT_CONNECT` the maximum time to wait for a connection attempt to a VPS to succeed. Default `5000` ms 151 | * `TIMEOUT_CLIENT` timeouts apply when the client is expected to acknowledge or send data during the TCP process. Default `50000` ms 152 | * `TIMEOUT_SERVER` timeouts apply when the server is expected to acknowledge or send data during the TCP process. Default `50000` ms 153 | * `HTTPCHK` The HTTP method and uri used to check on the servers health - default `HEAD /` 154 | * `HTTPCHK_HOST` Host Header override on http Health Check - default `localhost` 155 | * `INTER` parameter sets the interval between two consecutive health checks. If not specified, the default value is `2s` 156 | * `FAST_INTER` parameter sets the interval between two consecutive health checks when the server is any of the transition state (read above): UP - transitionally DOWN or DOWN - transitionally UP. If not set, then `INTER` is used. 157 | * `DOWN_INTER` parameter sets the interval between two consecutive health checks when the server is in the DOWN state. If not set, then `INTER` is used. 158 | * `RISE` number of consecutive valid health checks before considering the server as UP. Default value is `2` 159 | * `FALL` number of consecutive invalid health checks before considering the server as DOWN. Default value is `3` 160 | 161 | 162 | ## Logging 163 | 164 | By default the logs from haproxy are present in the docker log, by using the rsyslog inside the container (UDP port 514). No access logs are present by default, but this can be changed by setting the log level. 165 | 166 | You can change the logging level by providing the `LOG_LEVEL` environment variable: 167 | 168 | docker run -e LOG_LEVEL=info ... eeacms/haproxy 169 | 170 | You can override the log output by providing the `LOGGING` environment variable: 171 | 172 | docker run -e LOGGING=logs.example.com:5005 ... eeacms/haproxy 173 | 174 | Now make sure that `logs.example.com` listen on UDP port `5005` 175 | 176 | ## Copyright and license 177 | 178 | The Initial Owner of the Original Code is European Environment Agency (EEA). 179 | All Rights Reserved. 180 | 181 | The Original Code is free software; 182 | you can redistribute it and/or modify it under the terms of the GNU 183 | General Public License as published by the Free Software Foundation; 184 | either version 2 of the License, or (at your option) any later 185 | version. 186 | 187 | 188 | ## Funding 189 | 190 | [European Environment Agency (EU)](http://eea.europa.eu) 191 | -------------------------------------------------------------------------------- /haproxy/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #fix variable _name to not have / 4 | if [ -n "$FRONTEND_NAME" ]; then 5 | export FRONTEND_NAME="${FRONTEND_NAME//\//}" 6 | fi 7 | if [ -n "$BACKEND_NAME" ]; then 8 | export BACKEND_NAME="${BACKEND_NAME//\//}" 9 | fi 10 | 11 | # haproxy not directly configured within /usr/local/etc/haproxy/haproxy.cfg 12 | if ! test -e /usr/local/etc/haproxy/haproxy.cfg; then 13 | if [ -n "$DNS_ENABLED" ]; then 14 | # Backends are resolved using internal or external DNS service 15 | touch /etc/haproxy/dns.backends 16 | python3 /configure.py dns 17 | echo "*/${DNS_TTL:-1} * * * * /track_dns | logger " > /var/crontab.txt 18 | 19 | else 20 | 21 | if [ -n "$BACKENDS" ]; then 22 | # Backend provided via $BACKENDS env 23 | python3 /configure.py env 24 | else 25 | # Find backend within /etc/hosts 26 | touch /etc/haproxy/hosts.backends 27 | python3 /configure.py hosts 28 | echo "*/${DNS_TTL:-1} * * * * /track_hosts | logger " > /var/crontab.txt 29 | fi 30 | 31 | fi 32 | 33 | #add crontab 34 | crontab /var/crontab.txt 35 | chmod 600 /etc/crontab 36 | 37 | #Add env variables for haproxy 38 | echo "export PATH=$PATH"':$PATH' >> /etc/environment 39 | if [ -n "$BACKENDS" ]; then echo "export BACKENDS=\"$BACKENDS\"" >> /etc/environment; fi 40 | if [ -n "$BACKENDS_PORT" ]; then echo "export BACKENDS_PORT=\"$BACKENDS_PORT\"" >> /etc/environment; fi 41 | if [ -n "$BACKENDS_MODE" ]; then echo "export BACKENDS_MODE=\"$BACKENDS_MODE\"" >> /etc/environment; fi 42 | if [ -n "$BACKEND_NAME" ]; then echo "export BACKEND_NAME=\"$BACKEND_NAME\"" >> /etc/environment; fi 43 | if [ -n "$BALANCE" ]; then echo "export BALANCE=\"$BALANCE\"" >> /etc/environment; fi 44 | if [ -n "$COOKIES_ENABLED" ]; then echo "export COOKIES_ENABLED=\"$COOKIES_ENABLED\"" >> /etc/environment; fi 45 | if [ -n "$COOKIES_NAME" ]; then echo "export COOKIES_NAME=\"$COOKIES_NAME\"" >> /etc/environment; fi 46 | if [ -n "$COOKIES_PARAMS" ]; then echo "export COOKIES_PARAMS=\"$COOKIES_PARAMS\"" >> /etc/environment; fi 47 | if [ -n "$DOWN_INTER" ]; then echo "export DOWN_INTER=\"$DOWN_INTER\"" >> /etc/environment; fi 48 | if [ -n "$FALL" ]; then echo "export FALL=\"$FALL\"" >> /etc/environment; fi 49 | if [ -n "$FAST_INTER" ]; then echo "export FAST_INTER=\"$FAST_INTER\"" >> /etc/environment; fi 50 | if [ -n "$FRONTEND_NAME" ]; then echo "export FRONTEND_NAME=\"$FRONTEND_NAME\"" >> /etc/environment; fi 51 | if [ -n "$FRONTEND_PORT" ]; then echo "export FRONTEND_PORT=\"$FRONTEND_PORT\"" >> /etc/environment; fi 52 | if [ -n "$FRONTEND_MODE" ]; then echo "export FRONTEND_MODE=\"$FRONTEND_MODE\"" >> /etc/environment; fi 53 | if [ -n "$HTTPCHK" ]; then echo "export HTTPCHK=\"$HTTPCHK\"" >> /etc/environment; fi 54 | if [ -n "$HTTPCHK_HOST" ]; then echo "export HTTPCHK_HOST=\"$HTTPCHK_HOST\"" >> /etc/environment; fi 55 | if [ -n "$INTER" ]; then echo "export INTER=\"$INTER\"" >> /etc/environment; fi 56 | if [ -n "$LOGGING" ]; then echo "export LOGGING=\"$LOGGING\"" >> /etc/environment; fi 57 | if [ -n "$LOG_LEVEL" ]; then echo "export LOG_LEVEL=\"$LOG_LEVEL\"" >> /etc/environment; fi 58 | if [ -n "$PROXY_PROTOCOL_ENABLED" ]; then echo "export PROXY_PROTOCOL_ENABLED=\"$PROXY_PROTOCOL_ENABLED\"" >> /etc/environment; fi 59 | if [ -n "$RISE" ]; then echo "export RISE=\"$RISE\"" >> /etc/environment; fi 60 | if [ -n "$SERVICE_NAMES" ]; then echo "export SERVICE_NAMES=\"$SERVICE_NAMES\"" >> /etc/environment; fi 61 | if [ -n "$STATS_AUTH" ]; then echo "export STATS_AUTH=\"$STATS_AUTH\"" >> /etc/environment; fi 62 | if [ -n "$STATS_PORT" ]; then echo "export STATS_PORT=\"$STATS_PORT\"" >> /etc/environment; fi 63 | if [ -n "$TIMEOUT_CLIENT" ]; then echo "export TIMEOUT_CLIENT=\"$TIMEOUT_CLIENT\"" >> /etc/environment; fi 64 | if [ -n "$TIMEOUT_CONNECT" ]; then echo "export TIMEOUT_CONNECT=\"$TIMEOUT_CONNECT\"" >> /etc/environment; fi 65 | if [ -n "$TIMEOUT_SERVER" ]; then echo "export TIMEOUT_SERVER=\"$TIMEOUT_SERVER\"" >> /etc/environment; fi 66 | fi 67 | 68 | 69 | #start logging 70 | service rsyslog restart 71 | 72 | #start crontab 73 | service cron restart 74 | 75 | exec /usr/local/bin/haproxy-entrypoint.sh "$@" 76 | 77 | -------------------------------------------------------------------------------- /haproxy/src/configure.py: -------------------------------------------------------------------------------- 1 | import os 2 | import socket 3 | import sys 4 | from string import Template 5 | import subprocess 6 | 7 | ################################################################################ 8 | # INIT 9 | ################################################################################ 10 | 11 | FRONTEND_NAME = os.environ.get('FRONTEND_NAME', 'http-frontend') 12 | FRONTEND_PORT = os.environ.get('FRONTEND_PORT', '5000') 13 | FRONTEND_MODE = os.environ.get('FRONTEND_MODE', os.environ.get('BACKENDS_MODE','http')) 14 | BACKEND_NAME = os.environ.get('BACKEND_NAME', 'http-backend') 15 | BALANCE = os.environ.get('BALANCE', 'roundrobin') 16 | SERVICE_NAMES = os.environ.get('SERVICE_NAMES', '') 17 | COOKIES_ENABLED = (os.environ.get('COOKIES_ENABLED', 'false').lower() == "true") 18 | COOKIES_NAME = os.environ.get('COOKIES_NAME','SRV_ID') 19 | COOKIES_PARAMS = os.environ.get('COOKIES_PARAMS','') 20 | PROXY_PROTOCOL_ENABLED = (os.environ.get('PROXY_PROTOCOL_ENABLED', 'false').lower() == "true") 21 | STATS_PORT = os.environ.get('STATS_PORT', '1936') 22 | STATS_AUTH = os.environ.get('STATS_AUTH', 'admin:admin') 23 | BACKENDS = os.environ.get('BACKENDS', '').split(' ') 24 | BACKENDS_PORT = os.environ.get('BACKENDS_PORT', '80') 25 | BACKENDS_MODE = os.environ.get('BACKENDS_MODE', FRONTEND_MODE) 26 | LOGGING = os.environ.get('LOGGING', '127.0.0.1') 27 | LOG_LEVEL = os.environ.get('LOG_LEVEL', 'notice') 28 | TIMEOUT_CONNECT = os.environ.get('TIMEOUT_CONNECT', '5000') 29 | TIMEOUT_CLIENT = os.environ.get('TIMEOUT_CLIENT', '50000') 30 | TIMEOUT_SERVER = os.environ.get('TIMEOUT_SERVER', '50000') 31 | HTTPCHK = os.environ.get('HTTPCHK', 'HEAD /') 32 | HTTPCHK_HOST = os.environ.get('HTTPCHK_HOST', 'localhost') 33 | INTER = os.environ.get('INTER', '2s') 34 | FAST_INTER = os.environ.get('FAST_INTER', INTER) 35 | DOWN_INTER = os.environ.get('DOWN_INTER', INTER) 36 | RISE = os.environ.get('RISE', '2') 37 | FALL = os.environ.get('FALL', '3') 38 | 39 | 40 | listen_conf = Template(""" 41 | listen stats 42 | bind *:$port 43 | stats enable 44 | stats uri / 45 | stats hide-version 46 | stats auth $auth 47 | """) 48 | 49 | frontend_conf = Template(""" 50 | frontend $name 51 | bind *:$port $accept_proxy 52 | mode $mode 53 | default_backend $backend 54 | """) 55 | 56 | if COOKIES_ENABLED: 57 | #if we choose to enable session stickiness 58 | #then insert a cookie named $COOKIES_NAME(SRV_ID) to the request: 59 | #all responses from HAProxy to the client will contain a Set-Cookie: 60 | #header with a specific value for each backend server as its cookie value. 61 | backend_conf = Template(""" 62 | backend $backend 63 | mode $mode 64 | balance $balance 65 | default-server inter $inter fastinter $fastinter downinter $downinter fall $fall rise $rise 66 | cookie $cookies_name insert $cookies_params 67 | """) 68 | cookies = "cookie \\\"@@value@@\\\"" 69 | else: 70 | #the old template and behaviour for backward compatibility 71 | #in this case the cookie will not be set - see below the value for 72 | #cookies variable (is set to empty) 73 | backend_conf = Template(""" 74 | backend $backend 75 | mode $mode 76 | balance $balance 77 | default-server inter $inter fastinter $fastinter downinter $downinter fall $fall rise $rise 78 | cookie $cookies_name prefix $cookies_params 79 | """) 80 | cookies = "" 81 | 82 | backend_type_http = Template(""" 83 | option forwardfor 84 | http-request set-header X-Forwarded-Port %[dst_port] 85 | http-request add-header X-Forwarded-Proto https if { ssl_fc } 86 | option httpchk $httpchk HTTP/1.1\\r\\nHost:$httpchk_host 87 | """) 88 | 89 | backend_conf_plus = Template(""" 90 | server $name-$index $host:$port $cookies check 91 | """) 92 | 93 | health_conf = """ 94 | listen default 95 | bind *:4242 96 | """ 97 | 98 | backend_conf = backend_conf.substitute( 99 | backend=BACKEND_NAME, 100 | mode=BACKENDS_MODE, 101 | balance=BALANCE, 102 | inter=INTER, 103 | fastinter=FAST_INTER, 104 | downinter=DOWN_INTER, 105 | fall=FALL, 106 | rise=RISE, 107 | cookies_name=COOKIES_NAME, 108 | cookies_params=COOKIES_PARAMS 109 | ) 110 | 111 | if BACKENDS_MODE == 'http': 112 | backend_conf += backend_type_http.substitute( 113 | httpchk=HTTPCHK, 114 | httpchk_host=HTTPCHK_HOST 115 | ) 116 | 117 | ################################################################################ 118 | # Backends are resolved using internal or external DNS service 119 | ################################################################################ 120 | if sys.argv[1] == "dns": 121 | ips = {} 122 | for index, backend_server in enumerate(BACKENDS): 123 | server_port = backend_server.split(':') 124 | host = server_port[0] 125 | port = server_port[1] if len(server_port) > 1 else BACKENDS_PORT 126 | 127 | try: 128 | records = subprocess.check_output(["getent", "hosts", host]) 129 | except Exception as err: 130 | print(err) 131 | else: 132 | for record in records.splitlines(): 133 | ip = record.split()[0].decode() 134 | ips[ip] = (host, port) 135 | 136 | with open('/etc/haproxy/dns.backends', 'w') as bfile: 137 | bfile.write(' '.join(sorted(ips))) 138 | 139 | for ip, (host, port) in ips.items(): 140 | backend_conf += backend_conf_plus.substitute( 141 | name=host.replace(".", "-"), 142 | index=ip.replace(".", "-"), 143 | host=ip, 144 | port=port, 145 | cookies=cookies.replace('@@value@@', ip)) 146 | 147 | ################################################################################ 148 | # Backends provided via BACKENDS environment variable 149 | ################################################################################ 150 | 151 | elif sys.argv[1] == "env": 152 | for index, backend_server in enumerate(BACKENDS): 153 | server_port = backend_server.split(':') 154 | host = server_port[0] 155 | port = server_port[1] if len(server_port) > 1 else BACKENDS_PORT 156 | backend_conf += backend_conf_plus.substitute( 157 | name=host.replace(".", "-"), 158 | index=index, 159 | host=host, 160 | port=port, 161 | cookies=cookies.replace('@@value@@', host)) 162 | 163 | ################################################################################ 164 | # Look for backend within /etc/hosts 165 | ################################################################################ 166 | 167 | elif sys.argv[1] == "hosts": 168 | try: 169 | hosts = open("/etc/hosts") 170 | except: 171 | exit(0) 172 | 173 | index = 1 174 | localhost = socket.gethostbyname(socket.gethostname()) 175 | existing_hosts = set() 176 | 177 | #BBB 178 | if ';' in SERVICE_NAMES: 179 | service_names = SERVICE_NAMES.split(';') 180 | else: 181 | service_names = SERVICE_NAMES.split() 182 | 183 | for host in hosts: 184 | if "0.0.0.0" in host: 185 | continue 186 | if "127.0.0.1" in host: 187 | continue 188 | if localhost in host: 189 | continue 190 | if "::" in host: 191 | continue 192 | 193 | part = host.split() 194 | if len(part) < 2: 195 | continue 196 | 197 | (host_ip, host_name) = part[0:2] 198 | if host_ip in existing_hosts: 199 | continue 200 | 201 | if service_names and not any(name in host_name for name in service_names): 202 | continue 203 | 204 | existing_hosts.add(host_ip) 205 | host_port = BACKENDS_PORT 206 | backend_conf += backend_conf_plus.substitute( 207 | name='http-server', 208 | index=index, 209 | host=host_ip, 210 | port=host_port, 211 | cookies=cookies.replace('@@value@@', host_ip) 212 | ) 213 | index += 1 214 | 215 | with open('/etc/haproxy/hosts.backends', 'w') as bfile: 216 | bfile.write(' '.join(sorted(existing_hosts))) 217 | 218 | if PROXY_PROTOCOL_ENABLED: 219 | accept_proxy = "accept-proxy" 220 | else: 221 | accept_proxy = "" 222 | 223 | with open("/usr/local/etc/haproxy/haproxy.cfg", "w") as configuration: 224 | with open("/tmp/haproxy.cfg", "r") as default: 225 | conf = Template(default.read()) 226 | conf = conf.substitute( 227 | LOGGING=LOGGING, 228 | LOG_LEVEL=LOG_LEVEL, 229 | TIMEOUT_CLIENT=TIMEOUT_CLIENT, 230 | TIMEOUT_CONNECT=TIMEOUT_CONNECT, 231 | TIMEOUT_SERVER=TIMEOUT_SERVER 232 | ) 233 | 234 | configuration.write(conf) 235 | 236 | configuration.write( 237 | listen_conf.substitute( 238 | port=STATS_PORT, auth=STATS_AUTH 239 | ) 240 | ) 241 | 242 | configuration.write( 243 | frontend_conf.substitute( 244 | name=FRONTEND_NAME, 245 | port=FRONTEND_PORT, 246 | mode=FRONTEND_MODE, 247 | backend=BACKEND_NAME, 248 | accept_proxy=accept_proxy 249 | ) 250 | ) 251 | 252 | configuration.write(backend_conf) 253 | configuration.write(health_conf) 254 | -------------------------------------------------------------------------------- /haproxy/src/haproxy.cfg: -------------------------------------------------------------------------------- 1 | global 2 | log $LOGGING local2 $LOG_LEVEL 3 | pidfile /run/haproxy.pid 4 | daemon 5 | 6 | # Default SSL material locations 7 | ca-base /etc/ssl/certs 8 | crt-base /etc/ssl/private 9 | 10 | # Default ciphers to use on SSL-enabled listening sockets. 11 | # For more information, see ciphers(1SSL). This list is from: 12 | # https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/ 13 | ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS 14 | ssl-default-bind-options no-sslv3 15 | 16 | defaults 17 | log global 18 | mode http 19 | option httplog 20 | option dontlognull 21 | timeout connect $TIMEOUT_CONNECT 22 | timeout client $TIMEOUT_CLIENT 23 | timeout server $TIMEOUT_SERVER 24 | errorfile 400 /etc/haproxy/errors/400.http 25 | errorfile 403 /etc/haproxy/errors/403.http 26 | errorfile 408 /etc/haproxy/errors/408.http 27 | errorfile 500 /etc/haproxy/errors/500.http 28 | errorfile 502 /etc/haproxy/errors/502.http 29 | errorfile 503 /etc/haproxy/errors/503.http 30 | errorfile 504 /etc/haproxy/errors/504.http 31 | -------------------------------------------------------------------------------- /haproxy/src/track_dns: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | 5 | if [ -f /etc/environment ]; then 6 | source /etc/environment 7 | fi 8 | 9 | IPS_1=`cat /etc/haproxy/dns.backends` 10 | python3 /configure.py dns 11 | IPS_2=`cat /etc/haproxy/dns.backends` 12 | 13 | if [ "$IPS_1" != "$IPS_2" ]; then 14 | echo "DNS backends changed: $IPS_2" 15 | kill -s HUP 1 16 | fi 17 | -------------------------------------------------------------------------------- /haproxy/src/track_hosts: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | if [ -f /etc/environment ]; then 5 | source /etc/environment 6 | fi 7 | 8 | IPS_1=`cat /etc/haproxy/hosts.backends` 9 | python3 /configure.py hosts 10 | IPS_2=`cat /etc/haproxy/hosts.backends` 11 | 12 | if [ "$IPS_1" != "$IPS_2" ]; then 13 | echo "HOSTS backends changed: $IPS_2" 14 | kill -s HUP 1 15 | fi 16 | --------------------------------------------------------------------------------