diff --git a/.gitignore b/.gitignore index 3023c68..7a63f2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea *~ +venv \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 2f64f12..2161b44 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ FROM haproxy:1.8-alpine -RUN apk add --no-cache bash +RUN apk add --no-cache bash python3 py-yaml -COPY *.sh / +COPY entrypoint.* / COPY conf.d /etc/haproxy/conf.d COPY assets/errors-custom/* /etc/haproxy/errors-custom/ diff --git a/README.md b/README.md index d8482c1..759ce06 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,53 @@ # Easy HAProxy -This Docker image will create dynamically the `haproxy.cfg` based on the arguments defined in the command line. +This Docker image will create dynamically the `haproxy.cfg` based on very simple Yaml. ## Features - Enable or disable Stats on port 1936 with custom password -- Add a mapping from a host to another based on Environment Variables -- Dont need to setup .cfg file -- You can add custom .cfg file by mapping the `conf.d` folder +- Simple mapping host => host:port +- Simple redirect host => host:port -## Usages - -The most common use is to use on Docker Swarm environment when you don't want to expose -the container ports and use a Load Balance to deliver to this component in the same -overlay network - ## Basic Usage -You have to define 3 variables (LB_STATS_USER, LB_STATS_PASS, LB_HOSTS). +Create a yaml file in your machine called `easyconfig.cfg` and put the contents: -Example: -- HAProxy with statistics and password "mypassowrd" for the user "admin" -- Domain `my.domain.com` routing to container/host `container1` on port 8080 -- Domain `other.domain.com` routing to container/host `other` on port 7000 +```yaml +stats: + username: admin + password: senha + port: 1936 # Optional (default 1936) +customerrors: true # Optional (default false) + +easymapping: + - port: 80 + hosts: + host1.com.br: container:5000 + host2.com.br: other:3000 + redirect: + www.host1.com.br: http://host1.com.br + + - port: 8080 + hosts: + host3.com.br: domain:8181 +``` + +Then run (remember to enable the proper ports): ```bash docker run \ - -e LB_STATS_USER=admin \ - -e LB_STATS_PASS=mypassword \ - -e LB_HOSTS="my.domain.com container1:8080 other.domain.com other:7000" \ + -v /path/to/local:/etc/easyconfig \ -p 80:80 \ + -p 8080:8080 \ + -p 1936:1936 \ --name easy-haproxy-instance \ -d byjg/easy-haproxy ``` + + ## Mapping custom .cfg files Just create a folder and put files with the extension .cfg. and map the volume to the container. @@ -57,6 +69,20 @@ docker run \ -byjg/easy-haproxy -c -f /etc/haproxy/haproxy.cfg ``` +## Docker Compose + +```yaml +version: "3.4" +services: + front: + image: byjg/easy-haproxy + volume: + - /path/to/local:/etc/easyconfig + ports: + - 80:80 + - 8080:8080 + - 1936:1936 +``` ## Build diff --git a/entrypoint.py b/entrypoint.py new file mode 100644 index 0000000..894120e --- /dev/null +++ b/entrypoint.py @@ -0,0 +1,106 @@ +import yaml + + +def defaults(custom): + result = """ +defaults + log global + + timeout connect 3s + timeout client 10s + timeout server 10m +""" + if custom: + result += """ + errorfile 400 /etc/haproxy/errors-custom/400.http + errorfile 403 /etc/haproxy/errors-custom/403.http + errorfile 408 /etc/haproxy/errors-custom/408.http + errorfile 500 /etc/haproxy/errors-custom/500.http + errorfile 502 /etc/haproxy/errors-custom/502.http + errorfile 503 /etc/haproxy/errors-custom/503.http + errorfile 504 /etc/haproxy/errors-custom/504.http +""" + result += """ +global + log /dev/log local0 + maxconn 2000 +""" + return result + + +def stats(map): + return """ +frontend stats + bind *:{2} + mode http + stats enable + stats hide-version + stats realm Haproxy\ Statistics + stats uri / + stats auth {0}:{1} +# acl is_proxystats hdr_dom(host) -i some.host.com +# default_backend srv_stats +# use_backend srv_stats if is_proxystats + default_backend srv_stats + +backend srv_stats + mode http + server Local 127.0.0.1:{2} +""".format(map["username"], map["password"], map["port"] if "port" in map else 1936) + + +def easymapping(o): + port = o["port"] + hosts = o["hosts"] if "hosts" in o else dict() + redir = o["redirect"] if "redirect" in o else dict() + + result = """ +frontend http_in + bind *:{0} + mode http + +""".format(port) + + for k in redir: + host = k.replace(".", "_") + result += " acl is_redir_{0} hdr_dom(host) -i -m end {1}\n".format(host, k) + result += " use_backend redir_{1}_{0} if is_redir_{0}\n\n".format(host, port) + + for k in hosts: + host = k.replace(".", "_") + result += " acl is_rule_{0} hdr_dom(host) -i -m end {1}\n".format(host, k) + result += " use_backend srv_{1}_{0} if is_rule_{0}\n\n".format(host, port) + + for k in redir: + host = k.replace(".", "_") + result += """ +backend redir_{1}_{0} + mode http + redirect location {2} code 302 +""".format(host, port, redir[k]) + + for k in hosts: + host = k.replace(".", "_") + result += """ +backend srv_{1}_{0} + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port]""".format(host, port) + """ + http-request add-header X-Forwarded-Proto https if { ssl_fc }""" + """ + server srv {0} check weight 1 +""".format(hosts[k]) + + return result + + +parsed = yaml.load("/etc/haproxy/haproxy.cfg") + +print(defaults(parsed["customerrors"] if "customerrors" in parsed else False)) +if "stats" in parsed: + print(stats(parsed["stats"])) +if "easymapping" in parsed: + for k in parsed["easymapping"]: + print(easymapping(k)) + + diff --git a/entrypoint.sh b/entrypoint.sh index aa0b358..d5442db 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -12,132 +12,17 @@ echo " __/ | __/ |" echo " |___/ |___/ " echo "" -defaults() { -echo " -defaults - log global - - timeout connect 3s - timeout client 10s - timeout server 10m - - errorfile 400 /etc/haproxy/errors-custom/400.http - errorfile 403 /etc/haproxy/errors-custom/403.http - errorfile 408 /etc/haproxy/errors-custom/408.http - errorfile 500 /etc/haproxy/errors-custom/500.http - errorfile 502 /etc/haproxy/errors-custom/502.http - errorfile 503 /etc/haproxy/errors-custom/503.http - errorfile 504 /etc/haproxy/errors-custom/504.http - -global - log /dev/log local0 - maxconn 2000 -" -} - -stats() { -echo " -frontend stats - bind *:1936 - mode http - stats enable - stats hide-version - stats realm Haproxy\ Statistics - stats uri / - stats auth $1:$2 -# acl is_proxystats hdr_dom(host) -i some.host.com -# default_backend srv_stats -# use_backend srv_stats if is_proxystats - default_backend srv_stats - -backend srv_stats - mode http - server Local 127.0.0.1:1936 -" -} - -frontend() { - echo " -frontend http_in - bind *:80 - mode http -" - - COUNT=1 - for FEACL in `echo $1` - do - echo " acl is_rule_$COUNT hdr_dom(host) -i -m end $FEACL" - COUNT=$((COUNT + 1)) - done - echo - COUNT=1 - for FEACL in `echo $1` - do - echo " use_backend srv_80_$COUNT if is_rule_$COUNT" - COUNT=$((COUNT + 1)) - done -} - -backend () { - COUNT=1 - for BESRV in `echo $1` - do - echo " -backend srv_80_$COUNT - balance roundrobin - mode http - option forwardfor - http-request set-header X-Forwarded-Port %[dst_port] - http-request add-header X-Forwarded-Proto https if { ssl_fc } -" - echo " server srv $BESRV check weight 1" - COUNT=$((COUNT + 1)) - done -} +if [ ! -f "/etc/easyconfig/easyconfig.cfg" ] +then + echo "File '/etc/easyconfig/easyconfig.cfg' does not exist" + exit 1 +fi HAPROXY_CFG="/etc/haproxy/haproxy.cfg" -FRONTEND="" -BACKEND="" -TURN="FB" -for LB_HOST in `echo ${LB_HOSTS}`; -do - if [ "$TURN" == "FB" ] - then - echo "Mapping: $LB_HOST:80" - FRONTEND="$FRONTEND $LB_HOST" - TURN="BE" - continue - fi - if [ "$TURN" == "BE" ] - then - echo " to: $LB_HOST" - echo - BACKEND="$BACKEND $LB_HOST" - TURN="FB" - continue - fi -done +python3 /entrypoint.py > $HAPROXY_CFG -# Write -defaults > ${HAPROXY_CFG} -if [ ! -z "${LB_STATS_USER}" ] -then - echo "Stats Enabled: ${LB_STATS_USER}" - stats ${LB_STATS_USER} ${LB_STATS_PASS} >> ${HAPROXY_CFG} -fi -frontend "$FRONTEND" >> ${HAPROXY_CFG} -backend "$BACKEND" >> ${HAPROXY_CFG} - -# More -for config in `ls /etc/haproxy/conf.d/*.cfg` -do - echo Loading extra config: ${config} - cat ${config} >> ${HAPROXY_CFG} -done - -echo /sbin/syslogd -O /proc/1/fd/1 /docker-entrypoint.sh "$@" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..4818cc5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +pyyaml \ No newline at end of file