From 7b61be60d8cba33e35246394be9a41fcebcf0bdf Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Sat, 11 Feb 2023 11:44:48 -0600 Subject: [PATCH 01/16] Added documentation for volume --- docs/volumes.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/volumes.md diff --git a/docs/volumes.md b/docs/volumes.md new file mode 100644 index 0000000..50f6065 --- /dev/null +++ b/docs/volumes.md @@ -0,0 +1,14 @@ +# Volumes + +You can map the following volumes: + +| Volume | Description | +|------|------| +| /etc/haproxy/static/ | The folder that will contain the [config.yml](static.md) file for static configuration | +| /certs/haproxy/ | The folder that will contain the certificates (`PEM`) for the [SSL](ssl.md) | +| /certs/letsencrypt/ | The folder that will contain the certificates (`PEM`) for the SSL. Use this volume to cache the [letsencrypt](letsencrypt.md) certificate and avoid re-issue certificates between restarts. | +| /etc/haproxy/conf.d/ | The folder that will contain the [custom configuration](other.md) files. | +| /etc/haproxy/errors-custom/ | The folder that will contain the [custom error](other.md) files. | + +---- +[Open source ByJG](http://opensource.byjg.com) \ No newline at end of file From c2beff08fc88dd9e9a7d4e74edcb7a2de6e4da61 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Sat, 11 Feb 2023 15:34:18 -0600 Subject: [PATCH 02/16] Load custom config files --- src/functions/__init__.py | 29 +++++++++++++++----- src/main.py | 4 ++- src/tests/fixtures/00_haproxy.cfg | 2 ++ src/tests/fixtures/10_haproxy.cfg | 2 ++ src/tests/test_daemonize.py | 44 +++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 src/tests/fixtures/00_haproxy.cfg create mode 100644 src/tests/fixtures/10_haproxy.cfg create mode 100644 src/tests/test_daemonize.py diff --git a/src/functions/__init__.py b/src/functions/__init__.py index d174dcf..7f0a331 100644 --- a/src/functions/__init__.py +++ b/src/functions/__init__.py @@ -100,21 +100,19 @@ class Functions: class Consts: easyhaproxy_config = "/etc/haproxy/static/config.yml" haproxy_config = "/etc/haproxy/haproxy.cfg" + custom_config_folder = "/etc/haproxy/conf.d" certs_letsencrypt = "/certs/letsencrypt" certs_haproxy = "/certs/haproxy" class DaemonizeHAProxy: - def __init__(self): + def __init__(self, custom_config_folder = None): self.process = None self.thread = None self.sleep_secs = None + self.custom_config_folder = custom_config_folder if custom_config_folder is not None else Consts.custom_config_folder def haproxy(self, action): - if action == "start": - self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock") - else: - pid = "".join(Functions().run_bash(Functions.HAPROXY_LOG, "cat /run/haproxy.pid", log_output=False)) - self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf %s" % (pid)) + self.__prepare(self.get_haproxy_command(action)) if self.process is None: return @@ -122,6 +120,15 @@ class DaemonizeHAProxy: self.thread = Process(target=self.__start, args=()) self.thread.start() + def get_haproxy_command(self, action): + custom_config_files = " ".join(["-f %s" % (file) for file in self.get_custom_config_files()]) + + if action == "start": + return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (custom_config_files) + else: + pid = "".join(Functions().run_bash(Functions.HAPROXY_LOG, "cat /run/haproxy.pid", log_output=False)) + return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p /run/haproxy.pid -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid) + def __prepare(self, command): source = Functions.HAPROXY_LOG if not isinstance(command, (list, tuple)): @@ -172,6 +179,16 @@ class DaemonizeHAProxy: time.sleep(self.sleep_secs) + def get_custom_config_files(self): + if not os.path.exists(self.custom_config_folder): + return {} + + files = {} + for file in os.listdir(self.custom_config_folder): + if file.endswith(".cfg"): + files[os.path.join(self.custom_config_folder, file)] = os.path.getmtime(os.path.join(self.custom_config_folder, file)) + return dict(sorted(files.items(), key=lambda t: t[0])) + class Certbot: def __init__(self, certs, email, test_server): diff --git a/src/main.py b/src/main.py index 34e29d8..48440e1 100644 --- a/src/main.py +++ b/src/main.py @@ -19,6 +19,7 @@ def start(): old_haproxy = None haproxy = DaemonizeHAProxy() + current_custom_config_files = haproxy.get_custom_config_files() haproxy.haproxy("start") haproxy.sleep() @@ -31,7 +32,7 @@ def start(): try: old_parsed = processor_obj.get_parsed_object() processor_obj.refresh() - if certbot.check_certificates(letsencrypt_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive(): + if certbot.check_certificates(letsencrypt_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive() or DeepDiff(current_custom_config_files, haproxy.get_custom_config_files()) != {}: Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'New configuration found. Reloading...') Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE, 'Object Found: %s' % (processor_obj.get_parsed_object())) processor_obj.save_config(Consts.haproxy_config) @@ -40,6 +41,7 @@ def start(): Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config old_haproxy = haproxy haproxy = DaemonizeHAProxy() + current_custom_config_files = haproxy.get_custom_config_files() haproxy.haproxy("reload") old_haproxy.terminate() diff --git a/src/tests/fixtures/00_haproxy.cfg b/src/tests/fixtures/00_haproxy.cfg new file mode 100644 index 0000000..eaec319 --- /dev/null +++ b/src/tests/fixtures/00_haproxy.cfg @@ -0,0 +1,2 @@ +global + maxconn 4000 \ No newline at end of file diff --git a/src/tests/fixtures/10_haproxy.cfg b/src/tests/fixtures/10_haproxy.cfg new file mode 100644 index 0000000..9a855a9 --- /dev/null +++ b/src/tests/fixtures/10_haproxy.cfg @@ -0,0 +1,2 @@ +global + maxconn 5000 \ No newline at end of file diff --git a/src/tests/test_daemonize.py b/src/tests/test_daemonize.py new file mode 100644 index 0000000..5b8baa3 --- /dev/null +++ b/src/tests/test_daemonize.py @@ -0,0 +1,44 @@ +import json +import pytest +import os +import re +import random +import string +from functions import DaemonizeHAProxy + +def test_daemonize_haproxy(): + daemon = DaemonizeHAProxy() + assert daemon is not None + +def test_daemonize_haproxy_check_config(): + daemon = DaemonizeHAProxy() + filed = daemon.get_custom_config_files() + assert filed == {} + +def test_daemonize_haproxy_get_haproxy_command_start(): + daemon = DaemonizeHAProxy() + command = daemon.get_haproxy_command("start") + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock" + +def test_daemonize_haproxy_get_haproxy_command_reload(): + daemon = DaemonizeHAProxy() + command = daemon.get_haproxy_command("reload") + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf " + +def test_daemonize_haproxy_check_config(): + daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') + filed = daemon.get_custom_config_files() + assert filed == { + os.path.dirname(__file__) + "/fixtures/00_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/00_haproxy.cfg"), + os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg") + } + +def test_daemonize_haproxy_get_haproxy_command_start(): + daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') + command = daemon.get_haproxy_command("start") + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s/00_haproxy.cfg -f %s/10_haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures", os.path.dirname(__file__) + "/fixtures") + +def test_daemonize_haproxy_get_haproxy_command_reload(): + daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') + command = daemon.get_haproxy_command("reload") + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s/00_haproxy.cfg -f %s/10_haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf " % (os.path.dirname(__file__) + "/fixtures", os.path.dirname(__file__) + "/fixtures") From 089d4434d9475478223fdf344f80944d8e40940d Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 12 Feb 2023 20:08:12 -0600 Subject: [PATCH 03/16] Adjustment in the HAProxy conf.d --- src/functions/__init__.py | 4 +++- src/tests/test_daemonize.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/functions/__init__.py b/src/functions/__init__.py index 7f0a331..f4b9b3c 100644 --- a/src/functions/__init__.py +++ b/src/functions/__init__.py @@ -121,7 +121,9 @@ class DaemonizeHAProxy: self.thread.start() def get_haproxy_command(self, action): - custom_config_files = " ".join(["-f %s" % (file) for file in self.get_custom_config_files()]) + custom_config_files = "" + if len(list(self.get_custom_config_files().keys())) != 0: + custom_config_files = "-f %s" % (self.custom_config_folder) if action == "start": return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (custom_config_files) diff --git a/src/tests/test_daemonize.py b/src/tests/test_daemonize.py index 5b8baa3..fb1ea08 100644 --- a/src/tests/test_daemonize.py +++ b/src/tests/test_daemonize.py @@ -36,9 +36,9 @@ def test_daemonize_haproxy_check_config(): def test_daemonize_haproxy_get_haproxy_command_start(): daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') command = daemon.get_haproxy_command("start") - assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s/00_haproxy.cfg -f %s/10_haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures", os.path.dirname(__file__) + "/fixtures") + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures") def test_daemonize_haproxy_get_haproxy_command_reload(): daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') command = daemon.get_haproxy_command("reload") - assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s/00_haproxy.cfg -f %s/10_haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf " % (os.path.dirname(__file__) + "/fixtures", os.path.dirname(__file__) + "/fixtures") + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -x /var/run/haproxy.sock -sf " % (os.path.dirname(__file__) + "/fixtures") From 173a31ea808bcb59754e3f6050a54096d49c546f Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 12 Feb 2023 20:29:33 -0600 Subject: [PATCH 04/16] Upgrade HAProxy 2.6 LTS --- build/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Dockerfile b/build/Dockerfile index 20c3459..74b2970 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 +FROM alpine:3.17 ARG RELEASE_VERSION_ARG From 6e367174d43c3300182ecf7007b2603c76417a4f Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 26 Feb 2023 20:56:32 -0600 Subject: [PATCH 05/16] Add ssl-check --- docs/container-labels.md | 3 ++- docs/kubernetes.md | 1 + src/easymapping/__init__.py | 12 ++++++++---- src/processor/__init__.py | 6 ++++-- src/templates/haproxy.cfg.j2 | 6 +++--- src/tests/fixtures/services-tcp | 2 +- src/tests/test_parser.py | 22 ++++++++++++++++------ 7 files changed, 35 insertions(+), 17 deletions(-) diff --git a/docs/container-labels.md b/docs/container-labels.md index b71cecb..5052c1a 100644 --- a/docs/container-labels.md +++ b/docs/container-labels.md @@ -11,10 +11,11 @@ | easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} | | easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `letsencrypt` is enabled. | *empty* | base64 cert + key | | easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false | -| easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl | +| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl | | easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with `sslcert` parameter. | false | true OR false | | easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false | | easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `letsencrypt` parameters | false | true OR false | +| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based | The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations. diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 24743f5..96e601a 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -87,6 +87,7 @@ Caveats: | easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} | easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp | easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp +| easyhaproxy.balance | (optional) Set the balance algorithm for that ingress. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, leastconn, source, uri, url_param, hdr, rdp-cookie, static-rr, static-est, hdr(host), rdp-cookie, map-based, map-based(backend) | **Important**: The annotations are per ingress and applied to all hosts in that ingress configuration. diff --git a/src/easymapping/__init__.py b/src/easymapping/__init__.py index c2626a6..b683d07 100644 --- a/src/easymapping/__init__.py +++ b/src/easymapping/__init__.py @@ -117,7 +117,7 @@ class HaproxyConfigGenerator: if port not in easymapping: easymapping[port] = { "mode": mode, - "health-check": "", + "ssl-check": "", "port": port, "hosts": dict(), "redirect": dict(), @@ -129,8 +129,8 @@ class HaproxyConfigGenerator: "80" ) - easymapping[port]["health-check"] = self.label.get( - self.label.create([definition, "health-check"]), + easymapping[port]["ssl-check"] = self.label.get( + self.label.create([definition, "ssl-check"]), "" ) @@ -145,6 +145,10 @@ class HaproxyConfigGenerator: easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool( self.label.create([definition, "redirect_ssl"]) ) + easymapping[port]["hosts"][hostname]["balance"] = self.label.get( + self.label.create([definition, "balance"]), + "roundrobin" + ) easymapping[port]["redirect"] = self.label.get_json( self.label.create([definition, "redirect"]) @@ -154,7 +158,7 @@ class HaproxyConfigGenerator: if "443" not in easymapping: easymapping["443"] = { "mode": "http", - "health-check": "ssl", + "ssl-check": "ssl", "port": "443", "hosts": dict(), "redirect": dict(), diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 748ad1e..fa50232 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -188,9 +188,9 @@ class Kubernetes(ProcessorInterface): self.cert_cache = {} super().__init__() - def _check_annotation(self, annotations, key): + def _check_annotation(self, annotations, key, default = None): if key not in annotations: - return None + return default return annotations[key] def inspect_network(self): @@ -258,6 +258,8 @@ class Kubernetes(ProcessorInterface): rule_data["%s.redirect" % (definition)] = redirect if mode is not None: rule_data["%s.mode" % (definition)] = mode + rule_data["%s.balance" % (definition)] = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.balance", "roundrobin") + service_name = rule.http.paths[0].backend.service.name try: diff --git a/src/templates/haproxy.cfg.j2 b/src/templates/haproxy.cfg.j2 index a1842b1..1a71e6b 100644 --- a/src/templates/haproxy.cfg.j2 +++ b/src/templates/haproxy.cfg.j2 @@ -59,7 +59,7 @@ frontend {{ mode }}_in_{{ o["port"] }} {% for k in o["hosts"] -%} {% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %} backend srv_{{ host }} - balance roundrobin + balance {{ o["balance"] | default("roundrobin") }} mode {{ mode }} {% if mode == "http" %} option forwardfor @@ -67,10 +67,10 @@ backend srv_{{ host }} http-request add-header X-Forwarded-Proto https if { ssl_fc } {% elif mode == "tcp" %} option tcp-check - tcp-check connect{{ " ssl" if o["health-check"] == "ssl" }} + tcp-check connect{{ " ssl" if o["ssl-check"] == "ssl" }} {% endif %} {% for c in o["hosts"][k]["containers"] %} - server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["health-check"] == "ssl" }} + server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["ssl-check"] == "ssl" }} {% endfor %} {% endfor %} {% endfor %} diff --git a/src/tests/fixtures/services-tcp b/src/tests/fixtures/services-tcp index 5b5c2b0..bc924ab 100644 --- a/src/tests/fixtures/services-tcp +++ b/src/tests/fixtures/services-tcp @@ -1,2 +1,2 @@ -{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.health-check":"ssl"}, +{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.ssl-check":"ssl"}, "test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}} \ No newline at end of file diff --git a/src/tests/test_parser.py b/src/tests/test_parser.py index a3d35f0..3af83db 100644 --- a/src/tests/test_parser.py +++ b/src/tests/test_parser.py @@ -110,10 +110,11 @@ def test_parser_finds_services_raw(): parsed_object = [ { "mode":"tcp", - "health-check":"", + "ssl-check":"", "port":"31339", "hosts":{ "agent.quantum.example.org": { + "balance": "roundrobin", "containers": [ "my-stack_agent:9001" ], @@ -127,10 +128,11 @@ def test_parser_finds_services_raw(): }, { "mode":"http", - "health-check":"", + "ssl-check":"", "port":"31337", "hosts":{ "cadvisor.quantum.example.org":{ + "balance": "roundrobin", "containers": [ "my-stack_cadvisor:8080" ], @@ -138,6 +140,7 @@ def test_parser_finds_services_raw(): "redirect_ssl": False }, "node-exporter.quantum.example.org":{ + "balance": "roundrobin", "containers": [ "my-stack_node-exporter:9100" ], @@ -151,10 +154,11 @@ def test_parser_finds_services_raw(): }, { "mode":"http", - "health-check":"", + "ssl-check":"", "port":"443", "hosts":{ "node-exporter.quantum.example.org": { + "balance": "roundrobin", "containers": [ "my-stack_node-exporter:9100" ], @@ -162,6 +166,7 @@ def test_parser_finds_services_raw(): "redirect_ssl": False }, "www.somehost.com.br":{ + "balance": "roundrobin", "containers": [ "some-service:80" ], @@ -180,10 +185,11 @@ def test_parser_finds_services_raw(): }, { "mode":"http", - "health-check":"", + "ssl-check":"", "port":"80", "hosts":{ "www.somehost.com.br":{ + "balance": "roundrobin", "containers": [ "some-service:80" ], @@ -444,9 +450,10 @@ def test_parser_finds_services_clone_to_ssl_raw(): parsed_object = [ { - "health-check":"", + "ssl-check":"", "hosts":{ "host2.local":{ + "balance":"roundrobin", "containers":[ "10.152.183.215:8080" ], @@ -454,6 +461,7 @@ def test_parser_finds_services_clone_to_ssl_raw(): "redirect_ssl": False }, "valida.me":{ + "balance":"roundrobin", "containers":[ "10.152.183.62:8080" ], @@ -461,6 +469,7 @@ def test_parser_finds_services_clone_to_ssl_raw(): "redirect_ssl": False }, "www.valida.me":{ + "balance":"roundrobin", "containers":[ "10.152.183.62:8080" ], @@ -475,9 +484,10 @@ def test_parser_finds_services_clone_to_ssl_raw(): } }, { - "health-check":"ssl", + "ssl-check":"ssl", "hosts":{ "host2.local":{ + "balance":"roundrobin", "containers":[ "10.152.183.215:8080" ], From 3cc9547388727fcf1ff17b602a4d9f61d8a7181a Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 26 Feb 2023 20:58:43 -0600 Subject: [PATCH 06/16] Try Fix Swarm connection --- src/processor/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 748ad1e..ef43390 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -170,10 +170,10 @@ class Swarm(ProcessorInterface): ip_address = endpoint["Addr"].split("/")[0] break network_list.append(endpoint["NetworkID"]) - + + # add the network ha_proxy_network_id to the service object if ip_address is None: - network_list.append(ha_proxy_network_id) - service.update(networks = network_list) + self.client.networks.get(ha_proxy_network_id).connect(service.name) continue # skip to the next service to give time to update the network self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"] From 7dc683b25441d7f1af24e20a9a8062dcd9ceb705 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Sun, 26 Feb 2023 21:39:07 -0600 Subject: [PATCH 07/16] Try to fix Docker Swarm with network attachment --- src/processor/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/processor/__init__.py b/src/processor/__init__.py index ef43390..530d077 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -164,16 +164,15 @@ class Swarm(ProcessorInterface): self.parsed_object = {} for service in self.client.services.list(): ip_address = None - network_list = [] for endpoint in service.attrs["Endpoint"]["VirtualIPs"]: if ha_proxy_network_id == endpoint["NetworkID"]: ip_address = endpoint["Addr"].split("/")[0] break - network_list.append(endpoint["NetworkID"]) # add the network ha_proxy_network_id to the service object if ip_address is None: - self.client.networks.get(ha_proxy_network_id).connect(service.name) + network_attachment = docker.types.NetworkAttachmentConfig(target=ha_proxy_network_id) + service.update(networks = [network_attachment]) continue # skip to the next service to give time to update the network self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"] From aa5f8055f54e36c2daac1bdb69164b2ba86b9805 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Date: Mon, 27 Feb 2023 10:08:38 -0600 Subject: [PATCH 08/16] Better examples for Swarm --- docs/volumes.md | 14 ++++ examples/swarm/certs/host1.local.pem | 82 ++++++++++++++++++++++ examples/swarm/{ => certs}/host2.local.pem | 0 examples/swarm/easyhaproxy.yml | 33 +++++++++ examples/swarm/portainer.yml | 28 ++++++++ examples/swarm/services.yml | 51 ++++++++++++++ 6 files changed, 208 insertions(+) create mode 100644 docs/volumes.md create mode 100644 examples/swarm/certs/host1.local.pem rename examples/swarm/{ => certs}/host2.local.pem (100%) create mode 100644 examples/swarm/easyhaproxy.yml create mode 100644 examples/swarm/portainer.yml create mode 100644 examples/swarm/services.yml diff --git a/docs/volumes.md b/docs/volumes.md new file mode 100644 index 0000000..4413095 --- /dev/null +++ b/docs/volumes.md @@ -0,0 +1,14 @@ +# Volumes + +You can map the following volumes: + +| Volume | Description | +|-----------------------------|----------------------------------------------------------------------------------------| +| /etc/haproxy/static/ | The folder that will contain the [config.yml](static.md) file for static configuration | +| /certs/haproxy/ | The folder that will contain the certificates (`PEM`) for the [SSL](ssl.md) | +| /certs/letsencrypt/ | The folder that will contain the certificates (`PEM`) for the SSL. Use this volume to cache the [letsencrypt](letsencrypt.md) certificate and avoid re-issue certificates between restarts. | +| /etc/haproxy/conf.d/ | The folder that will contain the [custom configuration](other.md) files. | +| /etc/haproxy/errors-custom/ | The folder that will contain the [custom error](other.md) html files. | + +---- +[Open source ByJG](http://opensource.byjg.com) diff --git a/examples/swarm/certs/host1.local.pem b/examples/swarm/certs/host1.local.pem new file mode 100644 index 0000000..0d7eb39 --- /dev/null +++ b/examples/swarm/certs/host1.local.pem @@ -0,0 +1,82 @@ +-----BEGIN CERTIFICATE----- +MIIFDTCCAvWgAwIBAgIURi+w1ZVgeedTlNIAwqQBMJv6dXswDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLaG9zdDEubG9jYWwwHhcNMjEwODEwMTg0OTA2WhcNMzEw +ODA4MTg0OTA2WjAWMRQwEgYDVQQDDAtob3N0MS5sb2NhbDCCAiIwDQYJKoZIhvcN +AQEBBQADggIPADCCAgoCggIBAMBDAhLAygJuaW6w6ffigzTAAGXpmEz0tIxn1k4Z +x5wN5rpv/qu0QMYz+Av2u1eOKEKZeaFRVpT0r93dX7IvbEZHt25GPiBvlLGqhjKR +PnSk/7U8XmsnttUAV7rVEK1UrdFw8/IwriQC+dhr0mnYfSDMkvBoMFpdhVNTrbAZ +1TB6rQjE7Ar0Mt8my96XJmwrcjK2Tj+E2rgPIUz1e5cekFYIDSBatmw+3+vr+T5x +FNFkJ2o30W5o8ZflCJJzrVaihqQics6ZKDgpf7iqXMFiwWIlhdQpGvx5Gf/KFTK9 +UaOnRZz/X+2CebAFaTHR3k/PYppWTgBBBuRvlpCw+wdnkmteC0SQRF91QWVr7ejo +7KaOlGI5VtvMUsWvTeAZmpaymIaATETuOJaY0JU11OmLeD9DOj5E2SQ7qIX/pFcp +xpzG5j4c+MlgvxP2VAkNTeAXCaYiPBQH5ZZg0HE2WnB1KhLRFlHd4iHQD2GJ5yN/ +6fCFBfZfKSeK8JauwxgWkra53OcDq/mKd+DA/dK+/ruG7tqwVgIa04HOplzM7LYR +GB0Irs9+lr5/PJbQZmU073Mdn6cXAg3p+6wvwFlDkS5v13gBDYNHtF62bc551edF +Z6kGzJ7wmGRo84aBP7MuRZeReLOrSS67a1wLdzZsMnP1TJ7x9Lfr9MKl2uDnQdnY +ex8DAgMBAAGjUzBRMB0GA1UdDgQWBBSQ/mtZd6h8en9YQVH6HO1PlWWiqzAfBgNV +HSMEGDAWgBSQ/mtZd6h8en9YQVH6HO1PlWWiqzAPBgNVHRMBAf8EBTADAQH/MA0G +CSqGSIb3DQEBCwUAA4ICAQChQYNuah3+mTpIBDYxGrjTJNuOTIMaWzMyi1tkf+L0 +sEGwpbmAO2mWWQYF7WVLsi98PULh3adjt2jiud9VlaaC6gnwn5Zo1+Pilo9sNLLW +6ij0+rN4kwIm/pNqi+jDuu2cvAuHIwZWeh8bEe/5UCxo4ihmWFQN8eJ6TUKCphRC +6Eor/SSZZBQHgPl0BchzHOkwu7R3LCndRqxjhAoVb9yQOV+ZsmTeJXulwNzJ1uLt +T8OIgIiDpmBo7HSN2H0k3chx00AsjUyJ9mmAWPejFe/KXLRPcVZR17jhzgfIBEzs +M5WtWFm1aHDjVv6M6iteVm61E9T+k/M11ru1e2YwsxTDvb6x04mcrNu9soqddBbr +VfpluuoQ/hEAbXtFNPoTySpz0cwOwcHCowVOLmdKgvImszZiMyHHG8VGGmPh88n7 +wVxb0gV0P4RMrcMLdeTdn55YQr1CqBr34eB6ol6AsbTm3VzBHRVmFNksl1o5JB5t +tXLgF/G8/rzJ/4m1PaVuxrB7DxUmIk8EPbSIVkvZvd7LBzKwQ6IfVaucewHfEajQ +VIiexSMiFc7lw3KnxjOHZjf6FM9VYg3No++GdC99s7LkIuJwAMLNqTQ7Hvhn7YvP +4FlSIgc6xj0YkGZEQlb5o/5nauEqQU0ABgw6jtI4NxrNLT6cp7CO4M0xIDEg/3YD +aA== +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDAQwISwMoCbmlu +sOn34oM0wABl6ZhM9LSMZ9ZOGcecDea6b/6rtEDGM/gL9rtXjihCmXmhUVaU9K/d +3V+yL2xGR7duRj4gb5SxqoYykT50pP+1PF5rJ7bVAFe61RCtVK3RcPPyMK4kAvnY +a9Jp2H0gzJLwaDBaXYVTU62wGdUweq0IxOwK9DLfJsvelyZsK3Iytk4/hNq4DyFM +9XuXHpBWCA0gWrZsPt/r6/k+cRTRZCdqN9FuaPGX5QiSc61WooakInLOmSg4KX+4 +qlzBYsFiJYXUKRr8eRn/yhUyvVGjp0Wc/1/tgnmwBWkx0d5Pz2KaVk4AQQbkb5aQ +sPsHZ5JrXgtEkERfdUFla+3o6OymjpRiOVbbzFLFr03gGZqWspiGgExE7jiWmNCV +NdTpi3g/Qzo+RNkkO6iF/6RXKcacxuY+HPjJYL8T9lQJDU3gFwmmIjwUB+WWYNBx +NlpwdSoS0RZR3eIh0A9hiecjf+nwhQX2XyknivCWrsMYFpK2udznA6v5infgwP3S +vv67hu7asFYCGtOBzqZczOy2ERgdCK7Pfpa+fzyW0GZlNO9zHZ+nFwIN6fusL8BZ +Q5Eub9d4AQ2DR7Retm3OedXnRWepBsye8JhkaPOGgT+zLkWXkXizq0kuu2tcC3c2 +bDJz9Uye8fS36/TCpdrg50HZ2HsfAwIDAQABAoICAQC/xZbZ0cctqagsqvaVNTEe +eq1q+hfaGvPEYQaYHIrIE+2i5XcnGcLKcKfodxDjAn8R/zgdOp6cMX0CVn/PohHk +AEDtE8+AVwwAM1FsOwgLHVGaGz8qrxBlYdQgHcpmueIu2PXbC8eHUBiaUOIuhaw5 +/RRMDAC/Ai2ssfi7gOjvVE4oQxQW0QG1KGOOAUJn/uYHw2RFY2Uu1pimxO2kDO53 +gcxmC1WOnyCHmHaiW/Uh7z6JamfSM4dXtTJZslyh37dhHKNbg9VkP7CQKA4hLzop +hbf5qY6rargONiny1HgMPxrmwKuUouJyOtN0yBtxjDCUNaXUBwiy7sNGS+H4vsyB +5P9HhIHStu+FZt3HG7EIqCndiaSKDS4jWaVQAbbo4nZ2Zs2BD+xDePRCRUqX7rM4 +4XzPIRWWXmmWf/7Ig29Hbrp4a9LcOmQ2leCJtbaTFSN96OLUJ5E+hQ0ulCZgBVmQ +RCUYkJP4lOzbaKdzjxgHMrHzm45eUFf8LirOxi2uyxXHQmDNu4b3X18kt3PgUmUm +3dXpl3fqSyJa7SCV8ZNBrsrDq1E+thYtu91QbVSGxHd9HrNVe3XdLbOCdU9CuC69 +Nglznaa7sZLqmyKejTfGsY7xrWdNcMPl4p4fcID/O4EpASZforpTeKNT0ZIfZZew +b0mAQeYZqQM8i/qMYN/uAQKCAQEA5qg1sRNMc6VdM/tRglasGYoxjgRC2OqADZgs +mAXMUJ3kErpyxt+eCimy8ibuYpzRTIQ8fBTWRkCtRZXJ7+KcLVtk9QZIoLbhyNwd +4IxEQZFuUljDbvSjTLSycsHvo65ibWIfTL7bgWlLGgGq/UOzfGsgH6S9wLp5G30G +8ELyjI5eTIYICrfTmVL+c45MRpEMKo+cvz8PysiaOFTn3cyswPVdYaeEEqMQjU8w +IGNsGZLytY7BABBcY0ldrtba/O+Fv/+RH7uUtzP7xpCIwFCx80ZzN+WRy9NvI63U +zq3yIBoW9GyApD2+PLaPNxf7QLTUChY1Zz/dYRltKOxv2Aa5gQKCAQEA1WLWNqp0 +fhB/ZtfSEShxFMM89cjN6Aaz1WKL7uTBou9oSJnxjkhkaV76acnT/iqXtxMNgHi1 +fImDpU3PvM0Y4Ud2T47oHc6P1BrZPN/GmXy/s6BAEdPwLe7J+4nTISHAdGmrh+a/ +5pktu32g9lWqftxecFIVSLPWkxT0XKiMxp1ffkL+OavpMgMFZK41iKs3dNShKPog +L8GSPcP9x/yn78P2eK3N+PGjlA6pPzrANyWU7N0/bmHcB9TKP+udYWcjVhru7MYN +wNrE4kKdC8v8i7x7tDbvb79T+Fo6PIh53p0OsnZzA8UR0QNR+vDQufQuyaj8REC+ +ZG8YyCKsvk8ygwKCAQA/fsSxB0f/eeErYx6wC536teEoYCHqxrsTgvWbr9TryFs1 +kJ/yATLnR01cfb0X5mVzc9+WpMHLuxg31KEvaSlnDwa+sMkjfNSwz29mFhbgGeHN +x2OdUrj1b7TEBIEshN/RjrZhERUqDcs/0H+6kn2BXZgNPfOCb5LRL1zOnQ9aBAMP +e8IQ+UPFrGQheWWj81/vA3O57ekyAID7ytu9Yg+YWrMnI88mtj7jN45fDB+A9sPb +mP2mP9q+9j5U2A6WnHUsQnU30BKDUEsaAUWz80LZXmZvV8IH4x9wKfUwJBBIKAZz +qL7M97Y7zmGkX/Spfl30nOJ8lschaLd1EYlEZa2BAoIBAQCye25T4TV5MJFv0zuZ +MGuNg1Sc/O4Fkn2fEUOceWjhwUBH4cPjT/f1DwWDsNaJ9NRbxCr5931OArPDc5c8 +A404+Y4jM5RBQkKZli94tHAod+jc9UBB6TUvJll59SlMwC9679wS21ZOKnfPKGCX +SsZGQEsZxf6ZhhsHgXJ3gl/lzUJPmPeOA5YVR+Od9/09KIFFTojSfoynhVCuKx49 +xb4uVYn2HOJ4xJ0fPTghdCHMvrmXeeQRjvb88eaNmqVUEHHFFtgb4fklA5fE7RTx +BhliRDBwZ7bUkINK6yVk9n6BTns5mMvRLmgdnJpYvE7KC02LTbZb3I+j8C0ZUa+N +qy7DAoIBAAieribS7WUcl2aBlkm5+W7qNm/INm5zvnoSPo6V3wa5hs6f9+C/kbdF +87jQPA/YFe3uR2sAJ7slX5euZK8WmfpFmgzlu0sEz81MLQ/WypZtZytyVtWzB2Pu +XCW1tdSH9eI2BmhXgokHNTM48Nk/xOENrP/seXrIx5LK0hnDHZotu/z6+YSkB9hF +cm2fZygD1dMLX6liRimxyFY+dICJNB95JifTLWYnWeGddkwPtXUeGXE1olzvNkLD +zMzE09uhkx/lRJnteOBEZaf80OB/09Oi9b9/rxY59dwsH6GaxLoTfEKuPnvBVMNR +YkU14WzQKleFkiBJI9lVvnfgGnOlgg0= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/examples/swarm/host2.local.pem b/examples/swarm/certs/host2.local.pem similarity index 100% rename from examples/swarm/host2.local.pem rename to examples/swarm/certs/host2.local.pem diff --git a/examples/swarm/easyhaproxy.yml b/examples/swarm/easyhaproxy.yml new file mode 100644 index 0000000..3feef60 --- /dev/null +++ b/examples/swarm/easyhaproxy.yml @@ -0,0 +1,33 @@ +# To Install +# docker stack deploy -c easyhaproxy.yml easyhaproxy + + +version: "3" + +services: + haproxy: + image: byjg/easy-haproxy:4.3.1-rc1 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ./certs:/certs/haproxy + - certs_letsencrypt:/certs/letsencrypt + deploy: + replicas: 1 + environment: + EASYHAPROXY_DISCOVER: swarm + EASYHAPROXY_SSL_MODE: "loose" + EASYHAPROXY_LETSENCRYPT_EMAIL: changeme@example.org + HAPROXY_CUSTOMERRORS: "true" + HAPROXY_USERNAME: admin + HAPROXY_PASSWORD: password + HAPROXY_STATS_PORT: 1936 + ports: + - "80:80/tcp" + - "443:443/tcp" + - "1936:1936/tcp" + +volumes: + certs_letsencrypt: + # external: true + # certs_haproxy: + # external: true \ No newline at end of file diff --git a/examples/swarm/portainer.yml b/examples/swarm/portainer.yml new file mode 100644 index 0000000..b438b58 --- /dev/null +++ b/examples/swarm/portainer.yml @@ -0,0 +1,28 @@ +# To install: +# docker stack deploy -c portainer.yml portainer + +version: "3" + +services: + portainer: + image: portainer/portainer-ce:latest + volumes: + - portainer_data:/data portainer + - /var/run/docker.sock:/var/run/docker.sock + deploy: + replicas: 1 + labels: + # easyhaproxy.http.redirect_ssl: true + # easyhaproxy.http.letsencrypt: true + easyhaproxy.http.host: portainer.local + easyhaproxy.http.port: 80 + easyhaproxy.http.localport: 9000 + +volumes: + certs_letsencrypt: + external: true + # certs_haproxy: + # external: true + portainer_data: + # external: true + diff --git a/examples/swarm/services.yml b/examples/swarm/services.yml new file mode 100644 index 0000000..ad16ce2 --- /dev/null +++ b/examples/swarm/services.yml @@ -0,0 +1,51 @@ +# To install: +# docker stack deploy -c services.yml services +# +# To test: +# curl -k -H "Host: host1.local" https://127.0.0.1/ +# curl -k -H "Host: host2.local" https://127.0.0.1/ +# +# curl -I -H Host:host1.local http://127.0.0.1 +# HTTP/1.1 301 Moved Permanently +# content-length: 0 +# location: https://host1.local/ +# +# curl -I -H Host:host2.local http://127.0.0.1 +# HTTP/1.1 301 Moved Permanently +# content-length: 0 +# location: https://host1.local/ +# +# Test SSL: +# openssl s_client -showcerts -connect 127.0.0.1:443 -servername host1.local + +version: "3" + +services: + container: + image: byjg/static-httpserver + deploy: + replicas: 1 + labels: + easyhaproxy.http.redirect_ssl: "true" + easyhaproxy.http.host: host1.local + easyhaproxy.http.port: 80 + + easyhaproxy.https.port: 443 + easyhaproxy.https.localport: 8080 + easyhaproxy.https.host: host1.local + easyhaproxy.https.sslcert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVUmkrdzFaVmdlZWRUbE5JQXdxUUJNSnY2ZFhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhRzl6ZERFdWJHOWpZV3d3SGhjTk1qRXdPREV3TVRnME9UQTJXaGNOTXpFdwpPREE0TVRnME9UQTJXakFXTVJRd0VnWURWUVFEREF0b2IzTjBNUzVzYjJOaGJEQ0NBaUl3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFNQkRBaExBeWdKdWFXNnc2ZmZpZ3pUQUFHWHBtRXowdEl4bjFrNFoKeDV3TjVycHYvcXUwUU1ZeitBdjJ1MWVPS0VLWmVhRlJWcFQwcjkzZFg3SXZiRVpIdDI1R1BpQnZsTEdxaGpLUgpQblNrLzdVOFhtc250dFVBVjdyVkVLMVVyZEZ3OC9Jd3JpUUMrZGhyMG1uWWZTRE1rdkJvTUZwZGhWTlRyYkFaCjFUQjZyUWpFN0FyME10OG15OTZYSm13cmNqSzJUaitFMnJnUElVejFlNWNla0ZZSURTQmF0bXcrMyt2citUNXgKRk5Ga0oybzMwVzVvOFpmbENKSnpyVmFpaHFRaWNzNlpLRGdwZjdpcVhNRml3V0lsaGRRcEd2eDVHZi9LRlRLOQpVYU9uUlp6L1grMkNlYkFGYVRIUjNrL1BZcHBXVGdCQkJ1UnZscEN3K3dkbmttdGVDMFNRUkY5MVFXVnI3ZWpvCjdLYU9sR0k1VnR2TVVzV3ZUZUFabXBheW1JYUFURVR1T0phWTBKVTExT21MZUQ5RE9qNUUyU1E3cUlYL3BGY3AKeHB6RzVqNGMrTWxndnhQMlZBa05UZUFYQ2FZaVBCUUg1WlpnMEhFMlduQjFLaExSRmxIZDRpSFFEMkdKNXlOLwo2ZkNGQmZaZktTZUs4SmF1d3hnV2tyYTUzT2NEcS9tS2QrREEvZEsrL3J1Rzd0cXdWZ0lhMDRIT3Bsek03TFlSCkdCMElyczkrbHI1L1BKYlFabVUwNzNNZG42Y1hBZzNwKzZ3dndGbERrUzV2MTNnQkRZTkh0RjYyYmM1NTFlZEYKWjZrR3pKN3dtR1JvODRhQlA3TXVSWmVSZUxPclNTNjdhMXdMZHpac01uUDFUSjd4OUxmcjlNS2wydURuUWRuWQpleDhEQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QWZCZ05WCkhTTUVHREFXZ0JTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUNoUVlOdWFoMyttVHBJQkRZeEdyalRKTnVPVElNYVd6TXlpMXRrZitMMApzRUd3cGJtQU8ybVdXUVlGN1dWTHNpOThQVUxoM2FkanQyaml1ZDlWbGFhQzZnbnduNVpvMStQaWxvOXNOTExXCjZpajArck40a3dJbS9wTnFpK2pEdXUyY3ZBdUhJd1pXZWg4YkVlLzVVQ3hvNGlobVdGUU44ZUo2VFVLQ3BoUkMKNkVvci9TU1paQlFIZ1BsMEJjaHpIT2t3dTdSM0xDbmRScXhqaEFvVmI5eVFPVitac21UZUpYdWx3TnpKMXVMdApUOE9JZ0lpRHBtQm83SFNOMkgwazNjaHgwMEFzalV5SjltbUFXUGVqRmUvS1hMUlBjVlpSMTdqaHpnZklCRXpzCk01V3RXRm0xYUhEalZ2Nk02aXRlVm02MUU5VCtrL00xMXJ1MWUyWXdzeFREdmI2eDA0bWNyTnU5c29xZGRCYnIKVmZwbHV1b1EvaEVBYlh0Rk5Qb1R5U3B6MGN3T3djSENvd1ZPTG1kS2d2SW1zelppTXlISEc4VkdHbVBoODhuNwp3VnhiMGdWMFA0Uk1yY01MZGVUZG41NVlRcjFDcUJyMzRlQjZvbDZBc2JUbTNWekJIUlZtRk5rc2wxbzVKQjV0CnRYTGdGL0c4L3J6Si80bTFQYVZ1eHJCN0R4VW1JazhFUGJTSVZrdlp2ZDdMQnpLd1E2SWZWYXVjZXdIZkVhalEKVklpZXhTTWlGYzdsdzNLbnhqT0haamY2Rk05VllnM05vKytHZEM5OXM3TGtJdUp3QU1MTnFUUTdIdmhuN1l2UAo0RmxTSWdjNnhqMFlrR1pFUWxiNW8vNW5hdUVxUVUwQUJndzZqdEk0TnhyTkxUNmNwN0NPNE0weElERWcvM1lECmFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJSlF3SUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NDUzB3Z2drcEFnRUFBb0lDQVFEQVF3SVN3TW9DYm1sdQpzT24zNG9NMHdBQmw2WmhNOUxTTVo5Wk9HY2VjRGVhNmIvNnJ0RURHTS9nTDlydFhqaWhDbVhtaFVWYVU5Sy9kCjNWK3lMMnhHUjdkdVJqNGdiNVN4cW9ZeWtUNTBwUCsxUEY1cko3YlZBRmU2MVJDdFZLM1JjUFB5TUs0a0F2blkKYTlKcDJIMGd6Skx3YURCYVhZVlRVNjJ3R2RVd2VxMEl4T3dLOURMZkpzdmVseVpzSzNJeXRrNC9oTnE0RHlGTQo5WHVYSHBCV0NBMGdXclpzUHQvcjYvaytjUlRSWkNkcU45RnVhUEdYNVFpU2M2MVdvb2FrSW5MT21TZzRLWCs0CnFsekJZc0ZpSllYVUtScjhlUm4veWhVeXZWR2pwMFdjLzEvdGdubXdCV2t4MGQ1UHoyS2FWazRBUVFia2I1YVEKc1BzSFo1SnJYZ3RFa0VSZmRVRmxhKzNvNk95bWpwUmlPVmJiekZMRnIwM2dHWnFXc3BpR2dFeEU3amlXbU5DVgpOZFRwaTNnL1F6bytSTmtrTzZpRi82UlhLY2FjeHVZK0hQakpZTDhUOWxRSkRVM2dGd21tSWp3VUIrV1dZTkJ4Ck5scHdkU29TMFJaUjNlSWgwQTloaWVjamYrbndoUVgyWHlrbml2Q1dyc01ZRnBLMnVkem5BNnY1aW5mZ3dQM1MKdnY2N2h1N2FzRllDR3RPQnpxWmN6T3kyRVJnZENLN1BmcGErZnp5VzBHWmxOTzl6SForbkZ3SU42ZnVzTDhCWgpRNUV1YjlkNEFRMkRSN1JldG0zT2VkWG5SV2VwQnN5ZThKaGthUE9HZ1QrekxrV1hrWGl6cTBrdXUydGNDM2MyCmJESno5VXllOGZTMzYvVENwZHJnNTBIWjJIc2ZBd0lEQVFBQkFvSUNBUUMveFpiWjBjY3RxYWdzcXZhVk5URWUKZXExcStoZmFHdlBFWVFhWUhJcklFKzJpNVhjbkdjTEtjS2ZvZHhEakFuOFIvemdkT3A2Y01YMENWbi9Qb2hIawpBRUR0RTgrQVZ3d0FNMUZzT3dnTEhWR2FHejhxcnhCbFlkUWdIY3BtdWVJdTJQWGJDOGVIVUJpYVVPSXVoYXc1Ci9SUk1EQUMvQWkyc3NmaTdnT2p2VkU0b1F4UVcwUUcxS0dPT0FVSm4vdVlIdzJSRlkyVXUxcGlteE8ya0RPNTMKZ2N4bUMxV09ueUNIbUhhaVcvVWg3ejZKYW1mU000ZFh0VEpac2x5aDM3ZGhIS05iZzlWa1A3Q1FLQTRoTHpvcApoYmY1cVk2cmFyZ09OaW55MUhnTVB4cm13S3VVb3VKeU90TjB5QnR4akRDVU5hWFVCd2l5N3NOR1MrSDR2c3lCCjVQOUhoSUhTdHUrRlp0M0hHN0VJcUNuZGlhU0tEUzRqV2FWUUFiYm80bloyWnMyQkQreERlUFJDUlVxWDdyTTQKNFh6UElSV1dYbW1XZi83SWcyOUhicnA0YTlMY09tUTJsZUNKdGJhVEZTTjk2T0xVSjVFK2hRMHVsQ1pnQlZtUQpSQ1VZa0pQNGxPemJhS2R6anhnSE1ySHptNDVlVUZmOExpck94aTJ1eXhYSFFtRE51NGIzWDE4a3QzUGdVbVVtCjNkWHBsM2ZxU3lKYTdTQ1Y4Wk5CcnNyRHExRSt0aFl0dTkxUWJWU0d4SGQ5SHJOVmUzWGRMYk9DZFU5Q3VDNjkKTmdsem5hYTdzWkxxbXlLZWpUZkdzWTd4cldkTmNNUGw0cDRmY0lEL080RXBBU1pmb3JwVGVLTlQwWklmWlpldwpiMG1BUWVZWnFRTThpL3FNWU4vdUFRS0NBUUVBNXFnMXNSTk1jNlZkTS90UmdsYXNHWW94amdSQzJPcUFEWmdzCm1BWE1VSjNrRXJweXh0K2VDaW15OGlidVlwelJUSVE4ZkJUV1JrQ3RSWlhKNytLY0xWdGs5UVpJb0xiaHlOd2QKNEl4RVFaRnVVbGpEYnZTalRMU3ljc0h2bzY1aWJXSWZUTDdiZ1dsTEdnR3EvVU96ZkdzZ0g2Uzl3THA1RzMwRwo4RUx5akk1ZVRJWUlDcmZUbVZMK2M0NU1ScEVNS28rY3Z6OFB5c2lhT0ZUbjNjeXN3UFZkWWFlRUVxTVFqVTh3CklHTnNHWkx5dFk3QkFCQmNZMGxkcnRiYS9PK0Z2LytSSDd1VXR6UDd4cENJd0ZDeDgwWnpOK1dSeTlOdkk2M1UKenEzeUlCb1c5R3lBcEQyK1BMYVBOeGY3UUxUVUNoWTFaei9kWVJsdEtPeHYyQWE1Z1FLQ0FRRUExV0xXTnFwMApmaEIvWnRmU0VTaHhGTU04OWNqTjZBYXoxV0tMN3VUQm91OW9TSm54amtoa2FWNzZhY25UL2lxWHR4TU5nSGkxCmZJbURwVTNQdk0wWTRVZDJUNDdvSGM2UDFCclpQTi9HbVh5L3M2QkFFZFB3TGU3Sis0blRJU0hBZEdtcmgrYS8KNXBrdHUzMmc5bFdxZnR4ZWNGSVZTTFBXa3hUMFhLaU14cDFmZmtMK09hdnBNZ01GWks0MWlLczNkTlNoS1BvZwpMOEdTUGNQOXgveW43OFAyZUszTitQR2psQTZwUHpyQU55V1U3TjAvYm1IY0I5VEtQK3VkWVdjalZocnU3TVlOCndOckU0a0tkQzh2OGk3eDd0RGJ2Yjc5VCtGbzZQSWg1M3AwT3NuWnpBOFVSMFFOUit2RFF1ZlF1eWFqOFJFQysKWkc4WXlDS3N2azh5Z3dLQ0FRQS9mc1N4QjBmL2VlRXJZeDZ3QzUzNnRlRW9ZQ0hxeHJzVGd2V2JyOVRyeUZzMQprSi95QVRMblIwMWNmYjBYNW1WemM5K1dwTUhMdXhnMzFLRXZhU2xuRHdhK3NNa2pmTlN3ejI5bUZoYmdHZUhOCngyT2RVcmoxYjdURUJJRXNoTi9SanJaaEVSVXFEY3MvMEgrNmtuMkJYWmdOUGZPQ2I1TFJMMXpPblE5YUJBTVAKZThJUStVUEZyR1FoZVdXajgxL3ZBM081N2VreUFJRDd5dHU5WWcrWVdyTW5JODhtdGo3ak40NWZEQitBOXNQYgptUDJtUDlxKzlqNVUyQTZXbkhVc1FuVTMwQktEVUVzYUFVV3o4MExaWG1adlY4SUg0eDl3S2ZVd0pCQklLQVp6CnFMN005N1k3em1Ha1gvU3BmbDMwbk9KOGxzY2hhTGQxRVlsRVphMkJBb0lCQVFDeWUyNVQ0VFY1TUpGdjB6dVoKTUd1TmcxU2MvTzRGa24yZkVVT2NlV2pod1VCSDRjUGpUL2YxRHdXRHNOYUo5TlJieENyNTkzMU9BclBEYzVjOApBNDA0K1k0ak01UkJRa0tabGk5NHRIQW9kK2pjOVVCQjZUVXZKbGw1OVNsTXdDOTY3OXdTMjFaT0tuZlBLR0NYClNzWkdRRXNaeGY2Wmhoc0hnWEozZ2wvbHpVSlBtUGVPQTVZVlIrT2Q5LzA5S0lGRlRvalNmb3luaFZDdUt4NDkKeGI0dVZZbjJIT0o0eEowZlBUZ2hkQ0hNdnJtWGVlUVJqdmI4OGVhTm1xVlVFSEhGRnRnYjRma2xBNWZFN1JUeApCaGxpUkRCd1o3YlVrSU5LNnlWazluNkJUbnM1bU12UkxtZ2RuSnBZdkU3S0MwMkxUYlpiM0krajhDMFpVYStOCnF5N0RBb0lCQUFpZXJpYlM3V1VjbDJhQmxrbTUrVzdxTm0vSU5tNXp2bm9TUG82VjN3YTVoczZmOStDL2tiZEYKODdqUVBBL1lGZTN1UjJzQUo3c2xYNWV1Wks4V21mcEZtZ3psdTBzRXo4MU1MUS9XeXBadFp5dHlWdFd6QjJQdQpYQ1cxdGRTSDllSTJCbWhYZ29rSE5UTTQ4TmsveE9FTnJQL3NlWHJJeDVMSzBobkRIWm90dS96NitZU2tCOWhGCmNtMmZaeWdEMWRNTFg2bGlSaW14eUZZK2RJQ0pOQjk1SmlmVExXWW5XZUdkZGt3UHRYVWVHWEUxb2x6dk5rTEQKek16RTA5dWhreC9sUkpudGVPQkVaYWY4ME9CLzA5T2k5YjkvcnhZNTlkd3NINkdheExvVGZFS3VQbnZCVk1OUgpZa1UxNFd6UUtsZUZraUJKSTlsVnZuZmdHbk9sZ2cwPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t + + container2: + image: byjg/static-httpserver + deploy: + replicas: 1 + labels: + easyhaproxy.http.host: host2.local + easyhaproxy.http.port: 80 + easyhaproxy.http.redirect_ssl: "true" + + easyhaproxy.https.port: 443 + easyhaproxy.https.localport: 8080 + easyhaproxy.https.host: host2.local + easyhaproxy.https.ssl: "true" + From c0eb766339a2c954b55f134efed4ad0e19ab26bb Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 27 Feb 2023 23:28:09 -0600 Subject: [PATCH 09/16] Fix Swarm networking --- examples/swarm/docker-compose.yml | 67 ------------------------------- examples/swarm/easyhaproxy.yml | 9 ++++- src/processor/__init__.py | 30 ++++++++++---- 3 files changed, 31 insertions(+), 75 deletions(-) delete mode 100644 examples/swarm/docker-compose.yml diff --git a/examples/swarm/docker-compose.yml b/examples/swarm/docker-compose.yml deleted file mode 100644 index d5fa394..0000000 --- a/examples/swarm/docker-compose.yml +++ /dev/null @@ -1,67 +0,0 @@ -# To test: -# curl -k -H "Host: host1.local" https://127.0.0.1/ -# curl -k -H "Host: host2.local" https://127.0.0.1/ -# -# curl -I -H Host:host1.local http://127.0.0.1 -# HTTP/1.1 301 Moved Permanently -# content-length: 0 -# location: https://host1.local/ -# -# curl -I -H Host:host2.local http://127.0.0.1 -# HTTP/1.1 301 Moved Permanently -# content-length: 0 -# location: https://host1.local/ -# -# Test SSL: -# openssl s_client -showcerts -connect 127.0.0.1:443 -servername host1.local - -version: "3" - -services: - haproxy: - image: byjg/easy-haproxy - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ./host2.local.pem:/certs/haproxy/host2.local.pem - deploy: - replicas: 1 - environment: - EASYHAPROXY_DISCOVER: swarm - EASYHAPROXY_SSL_MODE: "loose" - HAPROXY_CUSTOMERRORS: "true" - HAPROXY_USERNAME: admin - HAPROXY_PASSWORD: password - HAPROXY_STATS_PORT: 1936 - ports: - - "80:80/tcp" - - "443:443/tcp" - - "1936:1936/tcp" - - container: - image: byjg/static-httpserver - deploy: - replicas: 1 - labels: - easyhaproxy.http.redirect_ssl: "true" - easyhaproxy.http.host: host1.local - easyhaproxy.http.port: 80 - - easyhaproxy.https.port: 443 - easyhaproxy.https.localport: 8080 - easyhaproxy.https.host: host1.local - easyhaproxy.https.sslcert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVUmkrdzFaVmdlZWRUbE5JQXdxUUJNSnY2ZFhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhRzl6ZERFdWJHOWpZV3d3SGhjTk1qRXdPREV3TVRnME9UQTJXaGNOTXpFdwpPREE0TVRnME9UQTJXakFXTVJRd0VnWURWUVFEREF0b2IzTjBNUzVzYjJOaGJEQ0NBaUl3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFNQkRBaExBeWdKdWFXNnc2ZmZpZ3pUQUFHWHBtRXowdEl4bjFrNFoKeDV3TjVycHYvcXUwUU1ZeitBdjJ1MWVPS0VLWmVhRlJWcFQwcjkzZFg3SXZiRVpIdDI1R1BpQnZsTEdxaGpLUgpQblNrLzdVOFhtc250dFVBVjdyVkVLMVVyZEZ3OC9Jd3JpUUMrZGhyMG1uWWZTRE1rdkJvTUZwZGhWTlRyYkFaCjFUQjZyUWpFN0FyME10OG15OTZYSm13cmNqSzJUaitFMnJnUElVejFlNWNla0ZZSURTQmF0bXcrMyt2citUNXgKRk5Ga0oybzMwVzVvOFpmbENKSnpyVmFpaHFRaWNzNlpLRGdwZjdpcVhNRml3V0lsaGRRcEd2eDVHZi9LRlRLOQpVYU9uUlp6L1grMkNlYkFGYVRIUjNrL1BZcHBXVGdCQkJ1UnZscEN3K3dkbmttdGVDMFNRUkY5MVFXVnI3ZWpvCjdLYU9sR0k1VnR2TVVzV3ZUZUFabXBheW1JYUFURVR1T0phWTBKVTExT21MZUQ5RE9qNUUyU1E3cUlYL3BGY3AKeHB6RzVqNGMrTWxndnhQMlZBa05UZUFYQ2FZaVBCUUg1WlpnMEhFMlduQjFLaExSRmxIZDRpSFFEMkdKNXlOLwo2ZkNGQmZaZktTZUs4SmF1d3hnV2tyYTUzT2NEcS9tS2QrREEvZEsrL3J1Rzd0cXdWZ0lhMDRIT3Bsek03TFlSCkdCMElyczkrbHI1L1BKYlFabVUwNzNNZG42Y1hBZzNwKzZ3dndGbERrUzV2MTNnQkRZTkh0RjYyYmM1NTFlZEYKWjZrR3pKN3dtR1JvODRhQlA3TXVSWmVSZUxPclNTNjdhMXdMZHpac01uUDFUSjd4OUxmcjlNS2wydURuUWRuWQpleDhEQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QWZCZ05WCkhTTUVHREFXZ0JTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUNoUVlOdWFoMyttVHBJQkRZeEdyalRKTnVPVElNYVd6TXlpMXRrZitMMApzRUd3cGJtQU8ybVdXUVlGN1dWTHNpOThQVUxoM2FkanQyaml1ZDlWbGFhQzZnbnduNVpvMStQaWxvOXNOTExXCjZpajArck40a3dJbS9wTnFpK2pEdXUyY3ZBdUhJd1pXZWg4YkVlLzVVQ3hvNGlobVdGUU44ZUo2VFVLQ3BoUkMKNkVvci9TU1paQlFIZ1BsMEJjaHpIT2t3dTdSM0xDbmRScXhqaEFvVmI5eVFPVitac21UZUpYdWx3TnpKMXVMdApUOE9JZ0lpRHBtQm83SFNOMkgwazNjaHgwMEFzalV5SjltbUFXUGVqRmUvS1hMUlBjVlpSMTdqaHpnZklCRXpzCk01V3RXRm0xYUhEalZ2Nk02aXRlVm02MUU5VCtrL00xMXJ1MWUyWXdzeFREdmI2eDA0bWNyTnU5c29xZGRCYnIKVmZwbHV1b1EvaEVBYlh0Rk5Qb1R5U3B6MGN3T3djSENvd1ZPTG1kS2d2SW1zelppTXlISEc4VkdHbVBoODhuNwp3VnhiMGdWMFA0Uk1yY01MZGVUZG41NVlRcjFDcUJyMzRlQjZvbDZBc2JUbTNWekJIUlZtRk5rc2wxbzVKQjV0CnRYTGdGL0c4L3J6Si80bTFQYVZ1eHJCN0R4VW1JazhFUGJTSVZrdlp2ZDdMQnpLd1E2SWZWYXVjZXdIZkVhalEKVklpZXhTTWlGYzdsdzNLbnhqT0haamY2Rk05VllnM05vKytHZEM5OXM3TGtJdUp3QU1MTnFUUTdIdmhuN1l2UAo0RmxTSWdjNnhqMFlrR1pFUWxiNW8vNW5hdUVxUVUwQUJndzZqdEk0TnhyTkxUNmNwN0NPNE0weElERWcvM1lECmFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJSlF3SUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NDUzB3Z2drcEFnRUFBb0lDQVFEQVF3SVN3TW9DYm1sdQpzT24zNG9NMHdBQmw2WmhNOUxTTVo5Wk9HY2VjRGVhNmIvNnJ0RURHTS9nTDlydFhqaWhDbVhtaFVWYVU5Sy9kCjNWK3lMMnhHUjdkdVJqNGdiNVN4cW9ZeWtUNTBwUCsxUEY1cko3YlZBRmU2MVJDdFZLM1JjUFB5TUs0a0F2blkKYTlKcDJIMGd6Skx3YURCYVhZVlRVNjJ3R2RVd2VxMEl4T3dLOURMZkpzdmVseVpzSzNJeXRrNC9oTnE0RHlGTQo5WHVYSHBCV0NBMGdXclpzUHQvcjYvaytjUlRSWkNkcU45RnVhUEdYNVFpU2M2MVdvb2FrSW5MT21TZzRLWCs0CnFsekJZc0ZpSllYVUtScjhlUm4veWhVeXZWR2pwMFdjLzEvdGdubXdCV2t4MGQ1UHoyS2FWazRBUVFia2I1YVEKc1BzSFo1SnJYZ3RFa0VSZmRVRmxhKzNvNk95bWpwUmlPVmJiekZMRnIwM2dHWnFXc3BpR2dFeEU3amlXbU5DVgpOZFRwaTNnL1F6bytSTmtrTzZpRi82UlhLY2FjeHVZK0hQakpZTDhUOWxRSkRVM2dGd21tSWp3VUIrV1dZTkJ4Ck5scHdkU29TMFJaUjNlSWgwQTloaWVjamYrbndoUVgyWHlrbml2Q1dyc01ZRnBLMnVkem5BNnY1aW5mZ3dQM1MKdnY2N2h1N2FzRllDR3RPQnpxWmN6T3kyRVJnZENLN1BmcGErZnp5VzBHWmxOTzl6SForbkZ3SU42ZnVzTDhCWgpRNUV1YjlkNEFRMkRSN1JldG0zT2VkWG5SV2VwQnN5ZThKaGthUE9HZ1QrekxrV1hrWGl6cTBrdXUydGNDM2MyCmJESno5VXllOGZTMzYvVENwZHJnNTBIWjJIc2ZBd0lEQVFBQkFvSUNBUUMveFpiWjBjY3RxYWdzcXZhVk5URWUKZXExcStoZmFHdlBFWVFhWUhJcklFKzJpNVhjbkdjTEtjS2ZvZHhEakFuOFIvemdkT3A2Y01YMENWbi9Qb2hIawpBRUR0RTgrQVZ3d0FNMUZzT3dnTEhWR2FHejhxcnhCbFlkUWdIY3BtdWVJdTJQWGJDOGVIVUJpYVVPSXVoYXc1Ci9SUk1EQUMvQWkyc3NmaTdnT2p2VkU0b1F4UVcwUUcxS0dPT0FVSm4vdVlIdzJSRlkyVXUxcGlteE8ya0RPNTMKZ2N4bUMxV09ueUNIbUhhaVcvVWg3ejZKYW1mU000ZFh0VEpac2x5aDM3ZGhIS05iZzlWa1A3Q1FLQTRoTHpvcApoYmY1cVk2cmFyZ09OaW55MUhnTVB4cm13S3VVb3VKeU90TjB5QnR4akRDVU5hWFVCd2l5N3NOR1MrSDR2c3lCCjVQOUhoSUhTdHUrRlp0M0hHN0VJcUNuZGlhU0tEUzRqV2FWUUFiYm80bloyWnMyQkQreERlUFJDUlVxWDdyTTQKNFh6UElSV1dYbW1XZi83SWcyOUhicnA0YTlMY09tUTJsZUNKdGJhVEZTTjk2T0xVSjVFK2hRMHVsQ1pnQlZtUQpSQ1VZa0pQNGxPemJhS2R6anhnSE1ySHptNDVlVUZmOExpck94aTJ1eXhYSFFtRE51NGIzWDE4a3QzUGdVbVVtCjNkWHBsM2ZxU3lKYTdTQ1Y4Wk5CcnNyRHExRSt0aFl0dTkxUWJWU0d4SGQ5SHJOVmUzWGRMYk9DZFU5Q3VDNjkKTmdsem5hYTdzWkxxbXlLZWpUZkdzWTd4cldkTmNNUGw0cDRmY0lEL080RXBBU1pmb3JwVGVLTlQwWklmWlpldwpiMG1BUWVZWnFRTThpL3FNWU4vdUFRS0NBUUVBNXFnMXNSTk1jNlZkTS90UmdsYXNHWW94amdSQzJPcUFEWmdzCm1BWE1VSjNrRXJweXh0K2VDaW15OGlidVlwelJUSVE4ZkJUV1JrQ3RSWlhKNytLY0xWdGs5UVpJb0xiaHlOd2QKNEl4RVFaRnVVbGpEYnZTalRMU3ljc0h2bzY1aWJXSWZUTDdiZ1dsTEdnR3EvVU96ZkdzZ0g2Uzl3THA1RzMwRwo4RUx5akk1ZVRJWUlDcmZUbVZMK2M0NU1ScEVNS28rY3Z6OFB5c2lhT0ZUbjNjeXN3UFZkWWFlRUVxTVFqVTh3CklHTnNHWkx5dFk3QkFCQmNZMGxkcnRiYS9PK0Z2LytSSDd1VXR6UDd4cENJd0ZDeDgwWnpOK1dSeTlOdkk2M1UKenEzeUlCb1c5R3lBcEQyK1BMYVBOeGY3UUxUVUNoWTFaei9kWVJsdEtPeHYyQWE1Z1FLQ0FRRUExV0xXTnFwMApmaEIvWnRmU0VTaHhGTU04OWNqTjZBYXoxV0tMN3VUQm91OW9TSm54amtoa2FWNzZhY25UL2lxWHR4TU5nSGkxCmZJbURwVTNQdk0wWTRVZDJUNDdvSGM2UDFCclpQTi9HbVh5L3M2QkFFZFB3TGU3Sis0blRJU0hBZEdtcmgrYS8KNXBrdHUzMmc5bFdxZnR4ZWNGSVZTTFBXa3hUMFhLaU14cDFmZmtMK09hdnBNZ01GWks0MWlLczNkTlNoS1BvZwpMOEdTUGNQOXgveW43OFAyZUszTitQR2psQTZwUHpyQU55V1U3TjAvYm1IY0I5VEtQK3VkWVdjalZocnU3TVlOCndOckU0a0tkQzh2OGk3eDd0RGJ2Yjc5VCtGbzZQSWg1M3AwT3NuWnpBOFVSMFFOUit2RFF1ZlF1eWFqOFJFQysKWkc4WXlDS3N2azh5Z3dLQ0FRQS9mc1N4QjBmL2VlRXJZeDZ3QzUzNnRlRW9ZQ0hxeHJzVGd2V2JyOVRyeUZzMQprSi95QVRMblIwMWNmYjBYNW1WemM5K1dwTUhMdXhnMzFLRXZhU2xuRHdhK3NNa2pmTlN3ejI5bUZoYmdHZUhOCngyT2RVcmoxYjdURUJJRXNoTi9SanJaaEVSVXFEY3MvMEgrNmtuMkJYWmdOUGZPQ2I1TFJMMXpPblE5YUJBTVAKZThJUStVUEZyR1FoZVdXajgxL3ZBM081N2VreUFJRDd5dHU5WWcrWVdyTW5JODhtdGo3ak40NWZEQitBOXNQYgptUDJtUDlxKzlqNVUyQTZXbkhVc1FuVTMwQktEVUVzYUFVV3o4MExaWG1adlY4SUg0eDl3S2ZVd0pCQklLQVp6CnFMN005N1k3em1Ha1gvU3BmbDMwbk9KOGxzY2hhTGQxRVlsRVphMkJBb0lCQVFDeWUyNVQ0VFY1TUpGdjB6dVoKTUd1TmcxU2MvTzRGa24yZkVVT2NlV2pod1VCSDRjUGpUL2YxRHdXRHNOYUo5TlJieENyNTkzMU9BclBEYzVjOApBNDA0K1k0ak01UkJRa0tabGk5NHRIQW9kK2pjOVVCQjZUVXZKbGw1OVNsTXdDOTY3OXdTMjFaT0tuZlBLR0NYClNzWkdRRXNaeGY2Wmhoc0hnWEozZ2wvbHpVSlBtUGVPQTVZVlIrT2Q5LzA5S0lGRlRvalNmb3luaFZDdUt4NDkKeGI0dVZZbjJIT0o0eEowZlBUZ2hkQ0hNdnJtWGVlUVJqdmI4OGVhTm1xVlVFSEhGRnRnYjRma2xBNWZFN1JUeApCaGxpUkRCd1o3YlVrSU5LNnlWazluNkJUbnM1bU12UkxtZ2RuSnBZdkU3S0MwMkxUYlpiM0krajhDMFpVYStOCnF5N0RBb0lCQUFpZXJpYlM3V1VjbDJhQmxrbTUrVzdxTm0vSU5tNXp2bm9TUG82VjN3YTVoczZmOStDL2tiZEYKODdqUVBBL1lGZTN1UjJzQUo3c2xYNWV1Wks4V21mcEZtZ3psdTBzRXo4MU1MUS9XeXBadFp5dHlWdFd6QjJQdQpYQ1cxdGRTSDllSTJCbWhYZ29rSE5UTTQ4TmsveE9FTnJQL3NlWHJJeDVMSzBobkRIWm90dS96NitZU2tCOWhGCmNtMmZaeWdEMWRNTFg2bGlSaW14eUZZK2RJQ0pOQjk1SmlmVExXWW5XZUdkZGt3UHRYVWVHWEUxb2x6dk5rTEQKek16RTA5dWhreC9sUkpudGVPQkVaYWY4ME9CLzA5T2k5YjkvcnhZNTlkd3NINkdheExvVGZFS3VQbnZCVk1OUgpZa1UxNFd6UUtsZUZraUJKSTlsVnZuZmdHbk9sZ2cwPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t - - container2: - image: byjg/static-httpserver - deploy: - replicas: 1 - labels: - easyhaproxy.http.host: host2.local - easyhaproxy.http.port: 80 - easyhaproxy.http.redirect_ssl: "true" - - easyhaproxy.https.port: 443 - easyhaproxy.https.localport: 8080 - easyhaproxy.https.host: host2.local - easyhaproxy.https.ssl: "true" - diff --git a/examples/swarm/easyhaproxy.yml b/examples/swarm/easyhaproxy.yml index 3feef60..3d088ef 100644 --- a/examples/swarm/easyhaproxy.yml +++ b/examples/swarm/easyhaproxy.yml @@ -1,4 +1,5 @@ # To Install +# docker network create --driver overlay --attachable easyhaproxy # docker stack deploy -c easyhaproxy.yml easyhaproxy @@ -6,7 +7,7 @@ version: "3" services: haproxy: - image: byjg/easy-haproxy:4.3.1-rc1 + image: byjg/easy-haproxy:4.3.1-rc2 volumes: - /var/run/docker.sock:/var/run/docker.sock - ./certs:/certs/haproxy @@ -25,6 +26,12 @@ services: - "80:80/tcp" - "443:443/tcp" - "1936:1936/tcp" + networks: + - easyhaproxy + +networks: + easyhaproxy: + external: true volumes: certs_letsencrypt: diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 530d077..511089d 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -40,6 +40,7 @@ class ProcessorInterface: def __init__(self, filename = None): self.filename = filename + self.label = ContainerEnv.read()['lookup_label'] self.refresh() @staticmethod @@ -156,25 +157,40 @@ class Swarm(ProcessorInterface): def inspect_network(self): ha_proxy_service_name = self.client.containers.get(socket.gethostname()).name.split('.')[0] - for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]: - ha_proxy_network_id = endpoint["NetworkID"] - if self.client.networks.get(ha_proxy_network_id).name != 'ingress': + ha_proxy_network_id = None + swarm_ingress_id = None + + # Get the HAProxy network and the ingress network + for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]: + network_name = self.client.networks.get(endpoint["NetworkID"]).name + if swarm_ingress_id is None and network_name == 'ingress': + swarm_ingress_id = endpoint["NetworkID"] + if ha_proxy_network_id is None and network_name != 'ingress': + ha_proxy_network_id = endpoint["NetworkID"] + if ha_proxy_network_id is not None and swarm_ingress_id is not None: break + # Check if the service is attached to the HAProxy network self.parsed_object = {} for service in self.client.services.list(): + if not any(self.label in key for key in service.attrs["Spec"]["Labels"]): + continue + ip_address = None + network_list = [] for endpoint in service.attrs["Endpoint"]["VirtualIPs"]: if ha_proxy_network_id == endpoint["NetworkID"]: ip_address = endpoint["Addr"].split("/")[0] break + elif swarm_ingress_id != endpoint["NetworkID"]: + network_list.append(endpoint["NetworkID"]) - # add the network ha_proxy_network_id to the service object + # Attach the service to the HAProxy network if ip_address is None: - network_attachment = docker.types.NetworkAttachmentConfig(target=ha_proxy_network_id) - service.update(networks = [network_attachment]) + network_list.append(ha_proxy_network_id) + service.update(networks = network_list) continue # skip to the next service to give time to update the network - + self.parsed_object[ip_address] = service.attrs["Spec"]["Labels"] From d63aeef2066462abc1800c695e26411075637642 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Tue, 28 Feb 2023 00:04:51 -0600 Subject: [PATCH 10/16] Make documentation clear --- docs/docker.md | 8 ++++++-- docs/kubernetes.md | 2 +- docs/swarm.md | 12 +++++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index e8d85e4..6e728c7 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -4,8 +4,12 @@ This method will use a docker standalone installation to discover the containers and configure the HAProxy. -The only requirement is that containers and EasyHAProxy must be in the same docker network. -If not, EasyHAProxy will connect the container with the EasyHAProxy network. +You cannot mix docker containers with swarm containers. + +The only request is that containers and EasyHAProxy must be in the same docker network. +If you don't add to your services the same network EasyHAProxy is connected to, EasyHAProxy will attach it network to your container. + +Also, it is highly recommended you create a network external to EasyHAProxy. e.g.: diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 24743f5..5e3c410 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -71,7 +71,7 @@ Once the container is running, EasyHAProxy will detect automatically and start t You don't need to expose any port in your container. -Caveats: +Notes: - At this point, the implementation doesn't support all ingress properties or wildcard domains. - The ingress will publish the ports 80 and 443, plus 1936 if stats are enabled. diff --git a/docs/swarm.md b/docs/swarm.md index fc671d1..6b3469a 100644 --- a/docs/swarm.md +++ b/docs/swarm.md @@ -5,13 +5,17 @@ This method will use a docker swarm installation to discover the containers and configure the HAProxy. The advantage of this method is that you can discover containers in other nodes from the cluster. -The only requirement is that containers and EasyHAProxy must be in the same docker swarm network. -If not, EasyHAProxy will connect the service with the EasyHAProxy service network. +You cannot mix docker containers with swarm containers. + +The only request is that containers and EasyHAProxy must be in the same docker swarm network. +If you don't add to your services the same network EasyHAProxy is connected to, EasyHAProxy will attach it network to your container. + +Also, it is highly recommended you create a network external to EasyHAProxy. e.g.: ```bash -docker network create -d overlay easyhaproxy +docker network create -d overlay --attachable easyhaproxy ``` And then deploy the EasyHAProxy stack: @@ -79,8 +83,6 @@ networks: external: true ``` -Note: The services to be discovered **don't need** to be in the same network as EasyHAProxy is. - Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container. You don't need to expose any port in your container. From 3ee6ee5c9987dfe483e945e1592561c09ee80801 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Tue, 28 Feb 2023 00:08:30 -0600 Subject: [PATCH 11/16] More docs --- docs/docker.md | 3 +-- docs/swarm.md | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 6e728c7..f9248d5 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -51,13 +51,12 @@ Once the container is running, EasyHAProxy will detect automatically and start t You don't need to expose any port in your container. -Please follow the [docker label configuration](container-labels.md) to see other configurations available. +Please follow the [docker label configuration](container-labels.md) to see other configurations available. ## Setup the EasyHAProxy container You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [docker container environment](docker-environment.md) - ## Setup certificates with Letsencrypt Follow [this link](letsencrypt.md) diff --git a/docs/swarm.md b/docs/swarm.md index 6b3469a..9ab29c4 100644 --- a/docs/swarm.md +++ b/docs/swarm.md @@ -87,7 +87,7 @@ Once the container is running, EasyHAProxy will detect automatically and start t You don't need to expose any port in your container. -Please follow the [docker label configuration](container-labels.md) to see other configurations available. +Please follow the [docker label configuration](container-labels.md) to see other configurations available. ## Setup the EasyHAProxy container From f6dbf950b5704ec34b9636a1249c47e490393183 Mon Sep 17 00:00:00 2001 From: Joao M Date: Mon, 26 Jun 2023 14:31:45 -0500 Subject: [PATCH 12/16] Add HTTP2 support for SSL. --- src/templates/bind.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/bind.j2 b/src/templates/bind.j2 index d33d4bd..e4f264e 100644 --- a/src/templates/bind.j2 +++ b/src/templates/bind.j2 @@ -1,5 +1,5 @@ {% if "ssl" in o %} - bind *:{{ o["port"] }} ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1 + bind *:{{ o["port"] }} ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 {% elif "h2" in o and o["h2"] %} bind *:{{ o["port"] }} proto h2 option http-use-htx From dcdccb9236923a1f3d4c769ce982f4324e2ee404 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 26 Jun 2023 22:41:51 -0500 Subject: [PATCH 13/16] Fix unit tests --- src/tests/expected/docker.txt | 2 +- src/tests/expected/services-letsencrypt.txt | 2 +- src/tests/expected/services-redirect-ssl.txt | 2 +- src/tests/expected/services.txt | 2 +- src/tests/expected/static.txt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tests/expected/docker.txt b/src/tests/expected/docker.txt index 7bcdedc..14f0307 100644 --- a/src/tests/expected/docker.txt +++ b/src/tests/expected/docker.txt @@ -36,7 +36,7 @@ backend srv_stats server Local 127.0.0.1:1936 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1 + bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local diff --git a/src/tests/expected/services-letsencrypt.txt b/src/tests/expected/services-letsencrypt.txt index 3e2eafb..be2a8f3 100644 --- a/src/tests/expected/services-letsencrypt.txt +++ b/src/tests/expected/services-letsencrypt.txt @@ -74,7 +74,7 @@ backend srv_test2_example_org_80 server srv-0 83d57d592e26:8080 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1 + bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org diff --git a/src/tests/expected/services-redirect-ssl.txt b/src/tests/expected/services-redirect-ssl.txt index 077123b..48b860a 100644 --- a/src/tests/expected/services-redirect-ssl.txt +++ b/src/tests/expected/services-redirect-ssl.txt @@ -49,7 +49,7 @@ backend srv_host1_local_80 server srv-0 5b69bc7fea1b:80 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1 + bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_host2_local_443_1 hdr(host) -i host2.local diff --git a/src/tests/expected/services.txt b/src/tests/expected/services.txt index bf2cecd..2ee7296 100644 --- a/src/tests/expected/services.txt +++ b/src/tests/expected/services.txt @@ -67,7 +67,7 @@ backend srv_node-exporter_quantum_example_org_31337 server srv-0 my-stack_node-exporter:9100 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1 + bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com.br } redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com } diff --git a/src/tests/expected/static.txt b/src/tests/expected/static.txt index ea0362f..a4ab71a 100644 --- a/src/tests/expected/static.txt +++ b/src/tests/expected/static.txt @@ -74,7 +74,7 @@ backend srv_host2_com_br_80 server srv-0 other:3000 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1 + bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br From 67e88ad3129e2c2bff0e043af5546f1058fb24a3 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 26 Jun 2023 22:52:11 -0500 Subject: [PATCH 14/16] Fix Build --- build/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/Dockerfile b/build/Dockerfile index 74b2970..334f3cb 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.17 +FROM alpine:3.18 ARG RELEASE_VERSION_ARG @@ -11,7 +11,6 @@ COPY src/ /scripts/ COPY build/assets / RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml certbot openssl \ - && ln -s /usr/bin/python3 /usr/bin/python \ && pip3 install --upgrade pip \ && pip install -r requirements.txt \ && pytest -s -vv tests/ \ From 2ddf276919fa0cc80c49e799572767fab03cf251 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 26 Jun 2023 23:30:34 -0500 Subject: [PATCH 15/16] Defining Log Level --- README.md | 10 ++++- docs/docker-environment.md | 30 ++++++------- docs/kubernetes.md | 19 ++++---- docs/static.md | 20 ++++++--- src/easymapping/__init__.py | 2 +- src/processor/__init__.py | 8 +++- src/templates/haproxy.cfg.j2 | 15 ++++++- src/tests/test_containerenv.py | 79 ++++++++++++++++++++++++++++++---- 8 files changed, 141 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index ac4d522..92c70fb 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ EasyHAProxy can detect and configure HAProxy automatically on the following plat - Docker Swarm - Kubernetes +## Who is using? + +EasyHAProxy is part of some projects: +- Dokku +- MicroK8s + +See detailed instructions on how to install below. + ## Features EasyHAProxy will discover the services based on the Docker Tags of the containers running on a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features: @@ -34,7 +42,7 @@ EasyHAProxy will discover the services based on the Docker Tags of the container Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file. -## How Does It Works? +## How Does It Work? You don't need to change your current infrastructure and don't need to learn the HAProxy configuration. diff --git a/docs/docker-environment.md b/docs/docker-environment.md index 0b9ec11..6e86477 100644 --- a/docs/docker-environment.md +++ b/docs/docker-environment.md @@ -1,20 +1,20 @@ # Docker environment variables -| Environment Variable | Description | Default | -|---------------------------------|-------------------------------------------------------------------------------------------------|------------------| -| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** | -| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` | -| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* | -| EASYHAPROXY_LETSENCRYPT_SERVER | (Optional) Can be `staging` or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | *empty* | -| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default`| -| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 | -| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | -| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` | -| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* | -| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` | -| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` | +| Environment Variable | Description | Default | +|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| +| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** | +| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` | +| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* | +| EASYHAPROXY_LETSENCRYPT_SERVER | (Optional) Can be `staging` or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | *empty* | +| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default` | +| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 | +| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG | +| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | INFO | +| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` | +| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* | +| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` | +| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` | diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 8b65f6f..f35020d 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -79,14 +79,17 @@ Caveats: ## Kubernetes annotations -| annotation | Description | Default | Example | -|-----------------------------|-----------------------------------------------------------------------------------------|--------------|--------------| -| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress -| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false -| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencrypt certificates for the ingresses domains. | false | true or false -| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} -| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp -| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp +| annotation | Description | Default | Example | +|----------------------------------|-----------------------------------------------------------------------------------------|--------------|---------------------------------------| +| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress | +| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false | +| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencrypt certificates for the ingresses domains. | false | true or false | +| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} | +| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp | +| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp | +| easyhaproxt.logLevel.certbot | (optional) Certbot log level | DEBUG | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | +| easyhaproxt.logLevel.eashhaproxy | (optional) EasyHAProxy log level | DEBUG | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | +| easyhaproxt.logLevel.haproxy | (optional) HAProxy log level | INFO | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | **Important**: The annotations are per ingress and applied to all hosts in that ingress configuration. diff --git a/docs/static.md b/docs/static.md index 6a35bda..47de056 100644 --- a/docs/static.md +++ b/docs/static.md @@ -20,9 +20,11 @@ customerrors: true # Optional (default false) ssl_mode: default -letsencrypt: { - "email": "acme@example.org" -} +logLevel: + haproxy: INFO + +letsencrypt: + email: "acme@example.org" easymapping: - port: 80 @@ -83,10 +85,14 @@ customerrors: true # Optional (default false) ssl_mode: default # Optional -letsencrypt: { # Optional. If you enable `letsencrypt` will need to setu0p this, - # otherwise the certificate will be issued - "email": "acme@example.org" -} +logLevel: + certbot: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL + easyhaproxy: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL + haproxy: INFO # Optional (default: INFO). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL + +# Optional. If you enable `letsencrypt` will need to set up this, otherwise the certificate will be issued +letsencrypt: + email": "acme@example.org" easymapping: - port: 80 # Listen port diff --git a/src/easymapping/__init__.py b/src/easymapping/__init__.py index c2626a6..a08bddc 100644 --- a/src/easymapping/__init__.py +++ b/src/easymapping/__init__.py @@ -56,7 +56,7 @@ class HaproxyConfigGenerator: self.serving_hosts = [] self.certs = {} - def generate(self, container_metadata = {}): + def generate(self, container_metadata={}): self.mapping.setdefault("easymapping", []) if container_metadata != {}: diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 748ad1e..e296a1c 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -31,7 +31,13 @@ class ContainerEnv: "email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), "server": os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "false").lower() in ["true", "1", "yes"] } - + + env_vars["logLevel"] = { + "easyhaproxy": os.getenv("EASYHAPROXY_LOG_LEVEL") if os.getenv("EASYHAPROXY_LOG_LEVEL") else Functions.DEBUG, + "haproxy": os.getenv("HAPROXY_LOG_LEVEL") if os.getenv("HAPROXY_LOG_LEVEL") else Functions.INFO, + "certbot": os.getenv("CERTBOT_LOG_LEVEL") if os.getenv("CERTBOT_LOG_LEVEL") else Functions.DEBUG, + } + return env_vars diff --git a/src/templates/haproxy.cfg.j2 b/src/templates/haproxy.cfg.j2 index a1842b1..9264ade 100644 --- a/src/templates/haproxy.cfg.j2 +++ b/src/templates/haproxy.cfg.j2 @@ -1,5 +1,18 @@ +{% set log_definition = data["logLevel"] | default({}) %} +{% set log_level = log_definition["haproxy"] | default("INFO") | upper %} +{% if log_level == "TRACE" or log_level == "DEBUG" %} +{% set haproxy_log_level = "debug" %} +{% elif log_level == "INFO" %} +{% set haproxy_log_level = "info" %} +{% elif log_level == "WARN" %} +{% set haproxy_log_level = "warning" %} +{% elif log_level == "ERROR" %} +{% set haproxy_log_level = "err" %} +{% elif log_level == "FATAL" %} +{% set haproxy_log_level = "crit" %} +{% endif %} global - log stdout format raw local0 info + log stdout format raw local0 {{ haproxy_log_level }} maxconn 2000 {% if data["ssl_mode"] == "strict" %} {% include "ssl_strict.j2" %} diff --git a/src/tests/test_containerenv.py b/src/tests/test_containerenv.py index c338871..5145251 100644 --- a/src/tests/test_containerenv.py +++ b/src/tests/test_containerenv.py @@ -1,12 +1,20 @@ import pytest import os + +from functions import Functions from processor import ContainerEnv + def test_container_env_empty(): assert { "customerrors": False, "ssl_mode": "default", - "lookup_label": "easyhaproxy" + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() # os.environ['CERTBOT_LOG_LEVEL'] = 'warn' @@ -17,7 +25,12 @@ def test_container_env_customerrors(): assert { "customerrors": True, "ssl_mode": "default", - "lookup_label": "easyhaproxy" + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_CUSTOMERRORS'] = '' @@ -28,7 +41,12 @@ def test_container_env_sslmode(): assert { "customerrors": False, "ssl_mode": "strict", - "lookup_label": "easyhaproxy" + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['EASYHAPROXY_SSL_MODE'] = '' @@ -41,6 +59,11 @@ def test_container_env_stats(): "customerrors": False, "ssl_mode": "default", "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_USERNAME'] = '' @@ -58,7 +81,12 @@ def test_container_env_stats_password(): "password": "xyz", "port": "1936" - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_PASSWORD'] = '' @@ -78,7 +106,12 @@ def test_container_env_stats_password(): "password": "xyz", "port": "2101" - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['HAPROXY_USERNAME'] = '' @@ -96,7 +129,12 @@ def test_container_env_stats_password(): "letsencrypt": { "email": "acme@example.org", "server": False - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' @@ -112,7 +150,32 @@ def test_container_env_letsencrypt(): "letsencrypt": { "email": "acme@example.org", "server": True - } + }, + "logLevel": { + "easyhaproxy": Functions.DEBUG, + "haproxy": Functions.INFO, + "certbot": Functions.DEBUG, + }, } == ContainerEnv.read() finally: - os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' \ No newline at end of file + os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' + +def test_container_log_level(): + os.environ['CERTBOT_LOG_LEVEL'] = Functions.TRACE + os.environ['EASYHAPROXY_LOG_LEVEL'] = Functions.ERROR + os.environ['HAPROXY_LOG_LEVEL'] = Functions.FATAL + try: + assert { + "customerrors": False, + "ssl_mode": "default", + "lookup_label": "easyhaproxy", + "logLevel": { + "easyhaproxy": Functions.ERROR, + "haproxy": Functions.FATAL, + "certbot": Functions.TRACE, + }, + } == ContainerEnv.read() + finally: + os.environ['CERTBOT_LOG_LEVEL'] = '' + os.environ['EASYHAPROXY_LOG_LEVEL'] = '' + os.environ['HAPROXY_LOG_LEVEL'] = '' From 166b75cd754b0ce4ce6d3a18794ca713ee9d8f08 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Mon, 3 Jul 2023 14:59:17 -0500 Subject: [PATCH 16/16] Minor Fix Pre-Merge --- docs/kubernetes.md | 19 +++++----- docs/volumes.md | 14 ++++---- examples/swarm/easyhaproxy.yml | 4 +-- examples/swarm/portainer.yml | 4 +-- src/functions/__init__.py | 18 +++++++--- src/processor/__init__.py | 2 +- src/templates/bind.j2 | 2 +- src/tests/expected/docker.txt | 2 +- src/tests/expected/services-letsencrypt.txt | 2 +- src/tests/expected/services-redirect-ssl.txt | 2 +- src/tests/expected/services.txt | 2 +- src/tests/expected/static.txt | 2 +- src/tests/test_containerenv.py | 37 +++++++++++--------- src/tests/test_daemonize.py | 22 +++++++----- src/tests/test_docker.py | 2 +- src/tests/test_functions.py | 8 ++--- 16 files changed, 77 insertions(+), 65 deletions(-) diff --git a/docs/kubernetes.md b/docs/kubernetes.md index 9354b91..b3fac3c 100644 --- a/docs/kubernetes.md +++ b/docs/kubernetes.md @@ -79,17 +79,14 @@ Notes: ## Kubernetes annotations -| annotation | Description | Default | Example | -|----------------------------------|-----------------------------------------------------------------------------------------|--------------|---------------------------------------| -| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress | -| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false | -| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencrypt certificates for the ingresses domains. | false | true or false | -| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} | -| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp | -| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp | -| easyhaproxt.logLevel.certbot | (optional) Certbot log level | DEBUG | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | -| easyhaproxt.logLevel.eashhaproxy | (optional) EasyHAProxy log level | DEBUG | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | -| easyhaproxt.logLevel.haproxy | (optional) HAProxy log level | INFO | TRACE,DEBUG,INFO,WARN,ERROR or FATAL | +| annotation | Description | Default | Example | +|----------------------------------|-------------------------------------------------------------------------------------|--------------|---------------------------------------| +| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress | +| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false | +| easyhaproxy.certbot | (optional) Boolean. It will request certbot certificates for the ingresses domains. | false | true or false | +| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} | +| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp | +| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp | **Important**: The annotations are per ingress and applied to all hosts in that ingress configuration. diff --git a/docs/volumes.md b/docs/volumes.md index 4413095..47daa02 100644 --- a/docs/volumes.md +++ b/docs/volumes.md @@ -2,13 +2,13 @@ You can map the following volumes: -| Volume | Description | -|-----------------------------|----------------------------------------------------------------------------------------| -| /etc/haproxy/static/ | The folder that will contain the [config.yml](static.md) file for static configuration | -| /certs/haproxy/ | The folder that will contain the certificates (`PEM`) for the [SSL](ssl.md) | -| /certs/letsencrypt/ | The folder that will contain the certificates (`PEM`) for the SSL. Use this volume to cache the [letsencrypt](letsencrypt.md) certificate and avoid re-issue certificates between restarts. | -| /etc/haproxy/conf.d/ | The folder that will contain the [custom configuration](other.md) files. | -| /etc/haproxy/errors-custom/ | The folder that will contain the [custom error](other.md) html files. | +| Volume | Description | +|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------| +| /etc/haproxy/static/ | The folder that will contain the [config.yml](static.md) file for static configuration | +| /certs/haproxy/ | The folder that will contain the certificates (`PEM`) for the [SSL](ssl.md) | +| /certs/certbot/ | The folder that will contain the certificates (`PEM`) processed by Certbot (e.g. Let's Encrypt). More info: [acme](acme.md). | +| /etc/haproxy/conf.d/ | The folder that will contain the [custom configuration](other.md) files. | +| /etc/haproxy/errors-custom/ | The folder that will contain the [custom error](other.md) html files. | ---- [Open source ByJG](http://opensource.byjg.com) diff --git a/examples/swarm/easyhaproxy.yml b/examples/swarm/easyhaproxy.yml index 3d088ef..a15260b 100644 --- a/examples/swarm/easyhaproxy.yml +++ b/examples/swarm/easyhaproxy.yml @@ -11,7 +11,7 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - ./certs:/certs/haproxy - - certs_letsencrypt:/certs/letsencrypt + - certs_certbot:/certs/certbot deploy: replicas: 1 environment: @@ -34,7 +34,7 @@ networks: external: true volumes: - certs_letsencrypt: + certs_certbot: # external: true # certs_haproxy: # external: true \ No newline at end of file diff --git a/examples/swarm/portainer.yml b/examples/swarm/portainer.yml index b438b58..d259988 100644 --- a/examples/swarm/portainer.yml +++ b/examples/swarm/portainer.yml @@ -13,13 +13,13 @@ services: replicas: 1 labels: # easyhaproxy.http.redirect_ssl: true - # easyhaproxy.http.letsencrypt: true + # easyhaproxy.http.certbot: true easyhaproxy.http.host: portainer.local easyhaproxy.http.port: 80 easyhaproxy.http.localport: 9000 volumes: - certs_letsencrypt: + certs_certbot: external: true # certs_haproxy: # external: true diff --git a/src/functions/__init__.py b/src/functions/__init__.py index 70f8ed8..1b3b1c0 100644 --- a/src/functions/__init__.py +++ b/src/functions/__init__.py @@ -27,6 +27,13 @@ class ContainerEnv: env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv( "EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy" + env_vars["logLevel"] = { + "easyhaproxy": os.getenv("EASYHAPROXY_LOG_LEVEL") if os.getenv( + "EASYHAPROXY_LOG_LEVEL") else Functions.DEBUG, + "haproxy": os.getenv("HAPROXY_LOG_LEVEL") if os.getenv("HAPROXY_LOG_LEVEL") else Functions.INFO, + "certbot": os.getenv("CERTBOT_LOG_LEVEL") if os.getenv("CERTBOT_LOG_LEVEL") else Functions.DEBUG, + } + env_vars["certbot"] = { "autoconfig": os.getenv("EASYHAPROXY_CERTBOT_AUTOCONFIG", ""), "email": os.getenv("EASYHAPROXY_CERTBOT_EMAIL", ""), @@ -72,7 +79,7 @@ class ContainerEnv: env_vars["certbot"]["eab_kid"] = os.environ['EASYHAPROXY_CERTBOT_EAB_KID'] = resp["eab_kid"] env_vars["certbot"]["eab_hmac_key"] = os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = resp["eab_hmac_key"] else: - os.environ["EASYHAPROXY_CERTBOT_EMAIL"] = "" + del os.environ["EASYHAPROXY_CERTBOT_EMAIL"] Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Could not obtain ZeroSSL credentials " + resp["error"]["type"]) os.environ['EASYHAPROXY_CERTBOT_SERVER'] = env_vars["certbot"]["server"] @@ -195,16 +202,17 @@ class DaemonizeHAProxy: self.thread = Process(target=self.__start, args=()) self.thread.start() - def get_haproxy_command(self, action): + def get_haproxy_command(self, action, pid_file="/run/haproxy.pid"): custom_config_files = "" if len(list(self.get_custom_config_files().keys())) != 0: custom_config_files = "-f %s" % (self.custom_config_folder) if action == "start": - return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (custom_config_files) + return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -S /var/run/haproxy.sock" % (custom_config_files, pid_file) else: - pid = "".join(Functions().run_bash(Functions.HAPROXY_LOG, "cat /run/haproxy.pid", log_output=False)) - return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p /run/haproxy.pid -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid) + return_code, output = Functions().run_bash(Functions.HAPROXY_LOG, "cat %s" % pid_file, log_output=False) + pid = "".join(output) + return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid_file, pid) def __prepare(self, command): source = Functions.HAPROXY_LOG diff --git a/src/processor/__init__.py b/src/processor/__init__.py index 5373b75..0d3f8a5 100644 --- a/src/processor/__init__.py +++ b/src/processor/__init__.py @@ -198,7 +198,7 @@ class Kubernetes(ProcessorInterface): self.cert_cache = {} super().__init__() - def _check_annotation(self, annotations, key, default = None): + def _check_annotation(self, annotations, key, default=None): if key not in annotations: return default return annotations[key] diff --git a/src/templates/bind.j2 b/src/templates/bind.j2 index e4f264e..d69de8f 100644 --- a/src/templates/bind.j2 +++ b/src/templates/bind.j2 @@ -1,5 +1,5 @@ {% if "ssl" in o %} - bind *:{{ o["port"] }} ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 + bind *:{{ o["port"] }} ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 {% elif "h2" in o and o["h2"] %} bind *:{{ o["port"] }} proto h2 option http-use-htx diff --git a/src/tests/expected/docker.txt b/src/tests/expected/docker.txt index 92c7430..91f928b 100644 --- a/src/tests/expected/docker.txt +++ b/src/tests/expected/docker.txt @@ -36,7 +36,7 @@ backend srv_stats server Local 127.0.0.1:1936 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 + bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local diff --git a/src/tests/expected/services-letsencrypt.txt b/src/tests/expected/services-letsencrypt.txt index ff7dd8c..8d5da5d 100644 --- a/src/tests/expected/services-letsencrypt.txt +++ b/src/tests/expected/services-letsencrypt.txt @@ -74,7 +74,7 @@ backend srv_test2_example_org_80 server srv-0 83d57d592e26:8080 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 + bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org diff --git a/src/tests/expected/services-redirect-ssl.txt b/src/tests/expected/services-redirect-ssl.txt index 01c9c09..ad841cc 100644 --- a/src/tests/expected/services-redirect-ssl.txt +++ b/src/tests/expected/services-redirect-ssl.txt @@ -49,7 +49,7 @@ backend srv_host1_local_80 server srv-0 5b69bc7fea1b:80 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 + bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_host2_local_443_1 hdr(host) -i host2.local diff --git a/src/tests/expected/services.txt b/src/tests/expected/services.txt index f6aaf6a..82f8a6c 100644 --- a/src/tests/expected/services.txt +++ b/src/tests/expected/services.txt @@ -67,7 +67,7 @@ backend srv_node-exporter_quantum_example_org_31337 server srv-0 my-stack_node-exporter:9100 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 + bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com.br } redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i somehost.com } diff --git a/src/tests/expected/static.txt b/src/tests/expected/static.txt index b643379..a5af1ec 100644 --- a/src/tests/expected/static.txt +++ b/src/tests/expected/static.txt @@ -74,7 +74,7 @@ backend srv_host2_com_br_80 server srv-0 other:3000 check weight 1 frontend http_in_443 - bind *:443 ssl crt /certs/letsencrypt/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 + bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1 mode http acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br diff --git a/src/tests/test_containerenv.py b/src/tests/test_containerenv.py index 9a3f3aa..30c2680 100644 --- a/src/tests/test_containerenv.py +++ b/src/tests/test_containerenv.py @@ -13,7 +13,7 @@ def test_container_env_empty(): "haproxy": Functions.INFO, "certbot": Functions.DEBUG, }, - "certbot": {"autoconfig": "", + "certbot": {"autoconfig": "", "eab_hmac_key": "", "eab_kid": "", "email": "", @@ -44,7 +44,7 @@ def test_container_env_customerrors(): "retry_count": 60} } == ContainerEnv.read() finally: - os.environ['HAPROXY_CUSTOMERRORS'] = '' + del os.environ['HAPROXY_CUSTOMERRORS'] def test_container_env_sslmode(): @@ -67,7 +67,7 @@ def test_container_env_sslmode(): "retry_count": 60} } == ContainerEnv.read() finally: - os.environ['EASYHAPROXY_SSL_MODE'] = '' + del os.environ['EASYHAPROXY_SSL_MODE'] def test_container_env_stats(): @@ -91,8 +91,8 @@ def test_container_env_stats(): "retry_count": 60} } == ContainerEnv.read() finally: - os.environ['HAPROXY_USERNAME'] = '' - os.environ['HAPROXY_STATS_PORT'] = '' + del os.environ['HAPROXY_USERNAME'] + del os.environ['HAPROXY_STATS_PORT'] def test_container_env_stats_password(): @@ -121,7 +121,7 @@ def test_container_env_stats_password(): "retry_count": 60} } == ContainerEnv.read() finally: - os.environ['HAPROXY_PASSWORD'] = '' + del os.environ['HAPROXY_PASSWORD'] def test_container_env_stats_password_2(): @@ -151,9 +151,9 @@ def test_container_env_stats_password_2(): "retry_count": 60} } == ContainerEnv.read() finally: - os.environ['HAPROXY_USERNAME'] = '' - os.environ['HAPROXY_STATS_PORT'] = '' - os.environ['HAPROXY_PASSWORD'] = '' + del os.environ['HAPROXY_USERNAME'] + del os.environ['HAPROXY_STATS_PORT'] + del os.environ['HAPROXY_PASSWORD'] def test_container_env_certbot_email(): @@ -178,7 +178,7 @@ def test_container_env_certbot_email(): } } == ContainerEnv.read() finally: - os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = '' + del os.environ['EASYHAPROXY_CERTBOT_EMAIL'] def test_container_env_certbot_full(): @@ -192,10 +192,6 @@ def test_container_env_certbot_full(): "customerrors": False, "ssl_mode": "default", "lookup_label": "easyhaproxy", - "letsencrypt": { - "email": "acme@example.org", - "server": True - }, "logLevel": { "easyhaproxy": Functions.DEBUG, "haproxy": Functions.INFO, @@ -211,7 +207,12 @@ def test_container_env_certbot_full(): } } == ContainerEnv.read() finally: - os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = '' + del os.environ['EASYHAPROXY_CERTBOT_EMAIL'] + del os.environ['EASYHAPROXY_CERTBOT_SERVER'] + del os.environ['EASYHAPROXY_CERTBOT_EAB_KID'] + del os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] + del os.environ['EASYHAPROXY_CERTBOT_RETRY_COUNT'] + def test_container_log_level(): os.environ['CERTBOT_LOG_LEVEL'] = Functions.TRACE @@ -231,10 +232,12 @@ def test_container_log_level(): "autoconfig": "", 'eab_hmac_key': "", 'eab_kid': "", - "email": "acme@example.org", + "email": "", "server": False, "retry_count": 60 } } == ContainerEnv.read() finally: - os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = '' + del os.environ['CERTBOT_LOG_LEVEL'] + del os.environ['EASYHAPROXY_LOG_LEVEL'] + del os.environ['HAPROXY_LOG_LEVEL'] diff --git a/src/tests/test_daemonize.py b/src/tests/test_daemonize.py index fb1ea08..e5d87c9 100644 --- a/src/tests/test_daemonize.py +++ b/src/tests/test_daemonize.py @@ -1,10 +1,7 @@ -import json -import pytest import os -import re -import random -import string -from functions import DaemonizeHAProxy + +from functions import DaemonizeHAProxy, Functions + def test_daemonize_haproxy(): daemon = DaemonizeHAProxy() @@ -38,7 +35,14 @@ def test_daemonize_haproxy_get_haproxy_command_start(): command = daemon.get_haproxy_command("start") assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures") + def test_daemonize_haproxy_get_haproxy_command_reload(): - daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') - command = daemon.get_haproxy_command("reload") - assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -x /var/run/haproxy.sock -sf " % (os.path.dirname(__file__) + "/fixtures") + tmp_pid_file = "/tmp/tmp_pid.txt" + Functions.save(tmp_pid_file, "10") + + try: + daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures') + command = daemon.get_haproxy_command("reload", tmp_pid_file) + assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p %s -x /var/run/haproxy.sock -sf %s" % (os.path.dirname(__file__) + "/fixtures", tmp_pid_file, 10) + finally: + os.remove(tmp_pid_file) diff --git a/src/tests/test_docker.py b/src/tests/test_docker.py index b6b95e1..e31063e 100644 --- a/src/tests/test_docker.py +++ b/src/tests/test_docker.py @@ -100,7 +100,7 @@ def test_processor_docker(): 'hostssl.local.pem': 'Some PEM Certificate' } finally: - os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = '' + del os.environ['EASYHAPROXY_CERTBOT_EMAIL'] container.stop() container2.stop() diff --git a/src/tests/test_functions.py b/src/tests/test_functions.py index e31a0ea..770cd55 100644 --- a/src/tests/test_functions.py +++ b/src/tests/test_functions.py @@ -13,15 +13,15 @@ def test_functions_check_local_level(): os.environ['CERTBOT_LOG_LEVEL'] = 'warn' assert Functions.skip_log('CERTBOT', Functions.INFO) == True - os.environ['CERTBOT_LOG_LEVEL'] = '' + del os.environ['CERTBOT_LOG_LEVEL'] os.environ['HAPROXY_LOG_LEVEL'] = 'warn' assert Functions.skip_log('HAPROXY', Functions.INFO) == True - os.environ['HAPROXY_LOG_LEVEL'] = '' + del os.environ['HAPROXY_LOG_LEVEL'] os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn' assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == True - os.environ['EASYHAPROXY_LOG_LEVEL'] = '' + del os.environ['EASYHAPROXY_LOG_LEVEL'] def test_function_load_and_save(): @@ -57,7 +57,7 @@ def test_functions_check_log_sanity(): assert len(Functions.debug_log) == 2 finally: - os.environ['EASYHAPROXY_LOG_LEVEL'] = '' + del os.environ['EASYHAPROXY_LOG_LEVEL'] Functions.debug_log = None