diff --git a/Dockerfile b/Dockerfile index 87f765f..95acd84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml supervisor docker ce && pip3 install --upgrade pip \ && pip install -r requirements.txt \ && pytest -s tests/ \ - && openssl dhparam -out /etc/haproxy/dhparam 2048 + && openssl dhparam -out /etc/haproxy/dhparam 2048 \ + && openssl dhparam -out /etc/haproxy/dhparam-1024 1024 CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf" ] diff --git a/README.md b/README.md index c1156b8..2d040bc 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ The environment variables will setup the HAProxy. | EASYHAPROXY_DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` | | EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search to match resources. Default: `easyhaproxy`. | | EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request certificate to letsencrypt | +| EASYHAPROXY_SSL_MODE | (Optional) `STRICT` supports only the most recent TLS version; `DEFAULT` good SSL integration with recent browsers; `LOOSE` support all old SSL protocols for old browsers (not recommended). | | HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. Default: `admin` | | HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. | | HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. Default: `1936` | @@ -97,6 +98,7 @@ Important: easyhaproxy needs to be in the same network of the containers or othe | easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if letsencrypt is enabled. | | | easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` (Defaults to "empty") | | | easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with sslcert | true OR yes OR false OR no | +| easyhaproxy.[definition].redirect-ssl | (Optional) Redirect all requests to https | true OR yes OR false OR no | ### Defining the labels in Docker Swarm diff --git a/assets/scripts/swarm.py b/assets/scripts/swarm.py index 52fd829..fa00dc9 100644 --- a/assets/scripts/swarm.py +++ b/assets/scripts/swarm.py @@ -5,7 +5,8 @@ with open("/tmp/.docker_data", 'r') as content_file: line_list = content_file.readlines() result = { - "customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False + "customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False, + "ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default") } if os.getenv("HAPROXY_PASSWORD"): diff --git a/easymapping/__init__.py b/easymapping/__init__.py index 2c5777c..541528e 100644 --- a/easymapping/__init__.py +++ b/easymapping/__init__.py @@ -48,6 +48,8 @@ class DockerLabelHandler: class HaproxyConfigGenerator: def __init__(self, mapping, ssl_cert_folder="/etc/haproxy/certs/discover"): self.mapping = mapping + self.mapping.setdefault("ssl_mode", 'default') + self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower() self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy") self.ssl_cert_folder = ssl_cert_folder self.letsencrypt_hosts = [] @@ -142,6 +144,10 @@ class HaproxyConfigGenerator: easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False) easymapping[port]["hosts"][hostname]["containers"] += ["{}:{}".format(container, ct_port)] easymapping[port]["hosts"][hostname]["letsencrypt"] = letsencrypt + easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool( + self.label.create([definition, "redirect_ssl"]) + ) + easymapping[port]["redirect"] = self.label.get_json( self.label.create([definition, "redirect"]) ) diff --git a/examples/docker/docker-compose.yml b/examples/docker/docker-compose.yml index c57920f..542ba32 100644 --- a/examples/docker/docker-compose.yml +++ b/examples/docker/docker-compose.yml @@ -1,8 +1,19 @@ # 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/ # -# or add to /etc/hosts -# 127.0.0.1 host1.local +# curl -I -H Host:host2.local http://127.0.0.1 +# HTTP/1.1 200 OK +# accept-ranges: bytes +# content-length: 3276 +# content-type: text/html; charset=utf-8 +# last-modified: Tue, 16 Aug 2022 15:48:05 GMT +# date: Tue, 16 Aug 2022 16:36:35 GMT # # Test SSL: # openssl s_client -showcerts -connect 127.0.0.1:443 --servername host1.local @@ -16,6 +27,7 @@ services: - /var/run/docker.sock:/var/run/docker.sock environment: EASYHAPROXY_DISCOVER: docker + EASYHAPROXY_SSL_MODE: "loose" HAPROXY_CUSTOMERRORS: "true" HAPROXY_USERNAME: admin HAPROXY_PASSWORD: password @@ -29,7 +41,7 @@ services: container: image: byjg/static-httpserver labels: - easyhaproxy.http.redirect: host1.local--https://host1.local + easyhaproxy.http.redirect_ssl: true easyhaproxy.http.host: host1.local easyhaproxy.http.port: 80 @@ -41,8 +53,8 @@ services: container2: image: byjg/static-httpserver labels: - easyhaproxy.http.redirect: host2.local--https://host2.local easyhaproxy.http.host: host2.local + easyhaproxy.http.localport: 8080 easyhaproxy.http.port: 80 easyhaproxy.https.port: 443 diff --git a/templates/frontend-mode-http.j2 b/templates/frontend-mode-http.j2 index 738761a..e6392ad 100644 --- a/templates/frontend-mode-http.j2 +++ b/templates/frontend-mode-http.j2 @@ -13,5 +13,9 @@ acl is_letsencrypt_{{ host }} path_beg /.well-known/acme-challenge/ use_backend letsencrypt_backend if is_letsencrypt_{{ host }} is_rule_{{ host }}_1 OR is_letsencrypt_{{ host }} is_rule_{{ host }}_2 {% endif %} + {% if o["hosts"][k]["redirect_ssl"] %} + http-request redirect scheme https code 301 if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2 + {% else %} use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2 + {% endif %} {% endfor %} diff --git a/templates/haproxy.cfg.j2 b/templates/haproxy.cfg.j2 index 97c9892..c47c127 100644 --- a/templates/haproxy.cfg.j2 +++ b/templates/haproxy.cfg.j2 @@ -1,18 +1,14 @@ global log stdout format raw local0 info maxconn 2000 - tune.ssl.default-dh-param 2048 +{% if data["ssl_mode"] == "strict" %} +{% include "ssl_strict.j2" %} +{% elif data["ssl_mode"] == "loose" %} +{% include "ssl_loose.j2" %} +{% else %} +{% include "ssl_default.j2" %} +{% endif %} - # intermediate configuration - ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 - ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 - ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets - - ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 - ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 - ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets - - ssl-dh-param-file /etc/haproxy/dhparam defaults log global diff --git a/templates/ssl_default.j2 b/templates/ssl_default.j2 new file mode 100644 index 0000000..0357561 --- /dev/null +++ b/templates/ssl_default.j2 @@ -0,0 +1,12 @@ + tune.ssl.default-dh-param 2048 + + # intermediate configuration + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + + ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + + ssl-dh-param-file /etc/haproxy/dhparam diff --git a/templates/ssl_loose.j2 b/templates/ssl_loose.j2 new file mode 100644 index 0000000..0dafc55 --- /dev/null +++ b/templates/ssl_loose.j2 @@ -0,0 +1,10 @@ + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options no-sslv3 no-tls-tickets + + ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tls-tickets + + ssl-dh-param-file /etc/haproxy/dhparam-1024 + diff --git a/templates/ssl_strict.j2 b/templates/ssl_strict.j2 new file mode 100644 index 0000000..654d387 --- /dev/null +++ b/templates/ssl_strict.j2 @@ -0,0 +1,7 @@ + # modern configuration + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets + + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets + diff --git a/tests/expected/services-redirect-ssl.txt b/tests/expected/services-redirect-ssl.txt new file mode 100644 index 0000000..342c2bc --- /dev/null +++ b/tests/expected/services-redirect-ssl.txt @@ -0,0 +1,81 @@ +global + log stdout format raw local0 info + maxconn 2000 + tune.ssl.default-dh-param 2048 + + # intermediate configuration + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + + ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets + + ssl-dh-param-file /etc/haproxy/dhparam + +defaults + log global + + timeout connect 3s + timeout client 10s + timeout server 10m + + +frontend http_in_80 + bind *:80 + mode http + + acl is_rule_host2_local_80_1 hdr(host) -i host2.local + acl is_rule_host2_local_80_2 hdr(host) -i host2.local:80 + use_backend srv_host2_local_80 if is_rule_host2_local_80_1 OR is_rule_host2_local_80_2 + + acl is_rule_host1_local_80_1 hdr(host) -i host1.local + acl is_rule_host1_local_80_2 hdr(host) -i host1.local:80 + http-request redirect scheme https code 301 if is_rule_host1_local_80_1 OR is_rule_host1_local_80_2 + +backend srv_host2_local_80 + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + server srv-0 b18a88da403b:8080 check weight 1 +backend srv_host1_local_80 + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + server srv-0 78a90f5c2d8a:80 check weight 1 + +frontend http_in_443 + bind *:443 ssl crt /etc/haproxy/certs/discover/ alpn http/1.1 crt /etc/haproxy/certs/ alpn http/1.1 + mode http + + acl is_rule_host2_local_443_1 hdr(host) -i host2.local + acl is_rule_host2_local_443_2 hdr(host) -i host2.local:443 + use_backend srv_host2_local_443 if is_rule_host2_local_443_1 OR is_rule_host2_local_443_2 + + acl is_rule_host1_local_443_1 hdr(host) -i host1.local + acl is_rule_host1_local_443_2 hdr(host) -i host1.local:443 + use_backend srv_host1_local_443 if is_rule_host1_local_443_1 OR is_rule_host1_local_443_2 + +backend srv_host2_local_443 + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + server srv-0 b18a88da403b:8080 check weight 1 +backend srv_host1_local_443 + balance roundrobin + mode http + option forwardfor + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + server srv-0 78a90f5c2d8a:8080 check weight 1 + +backend letsencrypt_backend + mode http + server certbot 127.0.0.1:2080 \ No newline at end of file diff --git a/tests/expected/ssl-loose.txt b/tests/expected/ssl-loose.txt new file mode 100644 index 0000000..6236f95 --- /dev/null +++ b/tests/expected/ssl-loose.txt @@ -0,0 +1,25 @@ +global + log stdout format raw local0 info + maxconn 2000 + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options no-sslv3 no-tls-tickets + + ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tls-tickets + + ssl-dh-param-file /etc/haproxy/dhparam-1024 + + +defaults + log global + + timeout connect 3s + timeout client 10s + timeout server 10m + + +backend letsencrypt_backend + mode http + server certbot 127.0.0.1:2080 \ No newline at end of file diff --git a/tests/expected/ssl-strict.txt b/tests/expected/ssl-strict.txt new file mode 100644 index 0000000..7a43dc3 --- /dev/null +++ b/tests/expected/ssl-strict.txt @@ -0,0 +1,22 @@ +global + log stdout format raw local0 info + maxconn 2000 + # modern configuration + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets + + ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets + + +defaults + log global + + timeout connect 3s + timeout client 10s + timeout server 10m + + +backend letsencrypt_backend + mode http + server certbot 127.0.0.1:2080 \ No newline at end of file diff --git a/tests/fixtures/services-redirect-ssl b/tests/fixtures/services-redirect-ssl new file mode 100644 index 0000000..eb712e1 --- /dev/null +++ b/tests/fixtures/services-redirect-ssl @@ -0,0 +1,3 @@ +c9575b39472f={"com.docker.compose.config-hash":"fe82a528a911c9457aa399aa94dcb6fb268d11623fc255e9140e516a807d7f1e","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:21030a845136af91b314af638e6174ecb0d8efadca2f4bee21790a52c0080a44","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"} +b18a88da403b={"com.docker.compose.config-hash":"b283cf8404cab91cc6c0ab9761b53802cb8ef3d719d4123f08b133cecf2161e6","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:c4232396c715f3d568816c666e6d9b4a68ef6c36f6243b4007c4ee1d8335fd65","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"container2","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"host2.local","easyhaproxy.http.localport":"8080","easyhaproxy.http.port":"80","easyhaproxy.https.host":"host2.local","easyhaproxy.https.localport":"8080","easyhaproxy.https.port":"443","easyhaproxy.https.sslcert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxVENDQXBHZ0F3SUJBZ0lVSWQ1Yjl0OXVxSDc4ZzAyRXpiV0Y2RktWdzNnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pERUxNQWtHQTFVRUJoTUNRbEl4RnpBVkJnTlZCQWdNRGxKcGJ5QmtaU0JLWVc1bGFYSnZNUmN3RlFZRApWUVFIREE1U2FXOGdaR1VnU21GdVpXbHliekVOTUFzR0ExVUVDZ3dFUVVOTlJURVVNQklHQTFVRUF3d0xhRzl6CmRESXViRzlqWVd3d0hoY05Nakl3T0RFMU1EUXlOekExV2hjTk1qTXdPREUxTURReU56QTFXakJrTVFzd0NRWUQKVlFRR0V3SkNVakVYTUJVR0ExVUVDQXdPVW1sdklHUmxJRXBoYm1WcGNtOHhGekFWQmdOVkJBY01EbEpwYnlCawpaU0JLWVc1bGFYSnZNUTB3Q3dZRFZRUUtEQVJCUTAxRk1SUXdFZ1lEVlFRRERBdG9iM04wTWk1c2IyTmhiRENDCkFTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTVNLdnJPYWhhdkNYbnZTRjUxMzFocG8KNms2NUM1N2pnUlE4NEZhRGo1TWJKT1ZsWVFWRnRNRzBYT2s3YStoaDV2MWZlNHdIMFI3STZGRG8wVjlzUytzcwprbzVic0VsYzF4WWxnNUhidUtxODl2UlNLZzZFRGx6dHgzQktiaTkxMlBtdDV2RkdOSjE2emN3NzdEVXJRSVhvCjRJL2I0YTNwbUJpV2o0M05vVElybVNXSHRzR3d3T2ozaUR2U3dlcWRZWEpJcjNocEhINXU2cG9oakRvUXZxRHoKSzZNdThwNm1oQ1VLTnM3S0ZKbk5Jbk5HMjVvUVQ2TzBuNE9HdG1nUmpMV29wZEVuT2hNa0tzZklvSTFYdGxYQgpMQkR2N2h1SUNrM3Q1eXd0ZkNReU8wOWtYN2xGSWdkNXJuNytNandINVdOZXFiUUp4dWFxam9YUW5OWlVnVXNDCkF3RUFBYU5UTUZFd0hRWURWUjBPQkJZRUZOaE1CRzhxNmEraUsybkVDd1ZUbjZCOUVYWk9NQjhHQTFVZEl3UVkKTUJhQUZOaE1CRzhxNmEraUsybkVDd1ZUbjZCOUVYWk9NQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEUVlKS29aSQpodmNOQVFFTEJRQURnZ0VCQUptdWR2eDgrcDVpSVVzVDhmbS9mYlZNMERBNnFXQUxEWVVKblRuM2o2THE0dnBmClBGQytxMUxtdVdmQlFNeXFLckhyUDNlNDkzRWN0WG9pU0taTzZpTjVkVkpJdXIwMk9qR3VpQUVjc1l1WTFuTG4KczlwaWlJK1VFd3hINnV4MU5hSFVuenNXYXVvQnZSaHpqWHZPNlNBVlNaSllhOWRZNW1pelhrbER5RE51RzVVMApsWHY5ZWdNR0JzeTBkRzZlRlhrVTVDUGR4V1U1NDB5STJzQ3RTQWo3eitXUlVENWs3Z0o3dFZvWTMvL2pIUVpHCjVTVFRtbTV0OWtwSVpUV2twdHlKb3M5b1pKRllNSVhxVzJGYzZ0eUxacFJwMzFSNzh0RHM2RVRJa1RvRGMwUlIKano2NnRoNkhJK1psZ0lCUWh3MDkraFlBaEJEZTkrRG1kL1N6UVpjPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tQkVHSU4gUFJJVkFURSBLRVktLS0tLQpNSUlFdndJQkFEQU5CZ2txaGtpRzl3MEJBUUVGQUFTQ0JLa3dnZ1NsQWdFQUFvSUJBUURFaXI2em1vV3J3bDU3CjBoZWRkOVlhYU9wT3VRdWU0NEVVUE9CV2c0K1RHeVRsWldFRlJiVEJ0RnpwTzJ2b1llYjlYM3VNQjlFZXlPaFEKNk5GZmJFdnJMSktPVzdCSlhOY1dKWU9SMjdpcXZQYjBVaW9PaEE1YzdjZHdTbTR2ZGRqNXJlYnhSalNkZXMzTQpPK3cxSzBDRjZPQ1AyK0d0NlpnWWxvK056YUV5SzVrbGg3YkJzTURvOTRnNzBzSHFuV0Z5U0s5NGFSeCtidXFhCklZdzZFTDZnOHl1akx2S2Vwb1FsQ2piT3loU1p6U0p6UnR1YUVFK2p0SitEaHJab0VZeTFxS1hSSnpvVEpDckgKeUtDTlY3WlZ3U3dRNys0YmlBcE43ZWNzTFh3a01qdFBaRis1UlNJSGVhNSsvakk4QitWalhxbTBDY2JtcW82RgowSnpXVklGTEFnTUJBQUVDZ2dFQkFLY2VpdFZST1FRNWUvbXhSUjlDZksxc05IL0gzTmUzLzFQa0I2WElyRmFiCnFCM2V2RWF0Wk91b243QTZOS0VlVGpsMzdTZStwZFNWWk9VWGNxQy9CemJyYVpyZTMrRWhya3BJajcyQXBWK1kKMml3WmlXVmFhSlFnSTR1WjNtTkF3OFJhV0pzajVTMWE5SThMRE9pUTVJWjQ1Q212QUJEUEplTVNjdkpTdlJSWQplNU4wTDZzdHFTN1orSW95R1ZLVWZwMWlOTzBZeXl3T1VpU2tJUlhnc2N1UlhaR1lwaUdQb21zSitKczFlanpXCmp5U3RsWkpFcjRMMTI4NXJHUHJtSHFqVHdGZCtoRzgwV2M0MTc5eEwrV1JFNkhCRVVaU2l5OTVmZTZrY1BIWFgKQmdpVll0Y0ZLbWlCaTJkVGJ4bDRlOTR1dDIzOWkwSHRsSjFaSnRMaCtCRUNnWUVBOGEyOThLMnpYa0hvc3hoTgp0UnJIN1hmTVBUa0hERGQzcnhNMjFMVCtmSVhxaW5HVXA5TFlhRGNianVUUHM4ZTMzdUtNZDdSNlE0MHg4OXlXCklYTmthL1ZMMFBYVWVWNjdhQ1ZMTHFnWERMR3VkbHVKaW5IMFh2bUkwQ21CZWNGU01xSUZtUWxncUVSb0dHczMKVU1hYzBwODc2VDRYa0dRUUpkZjYyYkZwRTRVQ2dZRUEwREJIMFBEbE9wd1hjY0RnWHJNZmF5cjhIQWhJK0c1Ugp5V1EvLzlpaXJ0VTgzY2h3SVd3a2g1M2VMTE16TGdkcUpuUGlXeWFVVzVCcXptWXVEMjNuaHhjUTdQTmRJcU9PCkgxc0U2enFMTnNodjQ2dDVRS2xoMVE0cWpkN1VxdGdyU3JZNjNSWEpDTVdUd25OTWVETHRqOGdhS2Jqa3JHM1IKQk0yaWxkdDZVbzhDZ1lCWDdORFVsaTFTbG9IMVhsc3ZEMDQ3UzhGSGFNN3lsOTk0RjNKMFVtRGZwc3pjajFQNAo5cEY2NE1tcTQvM1l0MGxpMG1NdVRiL0pnYjN4cllnRkpYa2NlY0thaEVWSDNyb3B1cCt1bXNMQUFJaXJVTVFxClZTa0Z3SjBRdG5qL2RlRFV3UE51YU9YOGNkNjVPNUNGVjZ6SVI5eEJFREQ4ZkJzUDJaTE96bWVmRFFLQmdRREYKbTI0dlZ0aGQvMWNKZENnRCswVnhOWVhESGVJVlhMRm8xUzBpTFlDTkxuM3RqWmxSUUJLVVh6WkplM2F5MC9yZgpzTk5EN2FTWUhNWWtUenlkREpiYzFQb056eG15RFVpVFhwT1dxeVVFeE0vZmJCMVZVUEU1aDQ3QXhxZFoyb0dOCkV0ZGdqcE1aTG1DSUMyU2tHc0wrM05Kb2s4VUtIZHB1RXJtbVFJTWs1UUtCZ1FDZ0VXY1l0TFhDM1lEWU1GZEkKVWdjVGViRnFTczNtTFlndWIxeGVrVzNJWFIyeW9tNFY1ZlFUTGlGN1lmbjJkcERXNEljTVUwVUpGWVZzVWxoSwphR3RldDRWbTVObjgrTWdob3Q1eUFqcU85eUFVYXViN3dnaWZLSWU5OXRRS2Q4dVp5Q3ZKMGhodm1ERFNmeDRtCkIvVEVpRkFPOTl5RjQ5aVN4RVZTQVM2cHFRPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=","io.buildah.version":"1.21.0"} +78a90f5c2d8a={"com.docker.compose.config-hash":"e2e9424b6ef2adfa33f750e3894446c9200f24674f5cf601ad60a87430e14314","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:c4232396c715f3d568816c666e6d9b4a68ef6c36f6243b4007c4ee1d8335fd65","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"container","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"host1.local","easyhaproxy.http.port":"80","easyhaproxy.http.redirect_ssl":"true","easyhaproxy.https.host":"host1.local","easyhaproxy.https.localport":"8080","easyhaproxy.https.port":"443","easyhaproxy.https.sslcert":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZEVENDQXZXZ0F3SUJBZ0lVUmkrdzFaVmdlZWRUbE5JQXdxUUJNSnY2ZFhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0ZqRVVNQklHQTFVRUF3d0xhRzl6ZERFdWJHOWpZV3d3SGhjTk1qRXdPREV3TVRnME9UQTJXaGNOTXpFdwpPREE0TVRnME9UQTJXakFXTVJRd0VnWURWUVFEREF0b2IzTjBNUzVzYjJOaGJEQ0NBaUl3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFNQkRBaExBeWdKdWFXNnc2ZmZpZ3pUQUFHWHBtRXowdEl4bjFrNFoKeDV3TjVycHYvcXUwUU1ZeitBdjJ1MWVPS0VLWmVhRlJWcFQwcjkzZFg3SXZiRVpIdDI1R1BpQnZsTEdxaGpLUgpQblNrLzdVOFhtc250dFVBVjdyVkVLMVVyZEZ3OC9Jd3JpUUMrZGhyMG1uWWZTRE1rdkJvTUZwZGhWTlRyYkFaCjFUQjZyUWpFN0FyME10OG15OTZYSm13cmNqSzJUaitFMnJnUElVejFlNWNla0ZZSURTQmF0bXcrMyt2citUNXgKRk5Ga0oybzMwVzVvOFpmbENKSnpyVmFpaHFRaWNzNlpLRGdwZjdpcVhNRml3V0lsaGRRcEd2eDVHZi9LRlRLOQpVYU9uUlp6L1grMkNlYkFGYVRIUjNrL1BZcHBXVGdCQkJ1UnZscEN3K3dkbmttdGVDMFNRUkY5MVFXVnI3ZWpvCjdLYU9sR0k1VnR2TVVzV3ZUZUFabXBheW1JYUFURVR1T0phWTBKVTExT21MZUQ5RE9qNUUyU1E3cUlYL3BGY3AKeHB6RzVqNGMrTWxndnhQMlZBa05UZUFYQ2FZaVBCUUg1WlpnMEhFMlduQjFLaExSRmxIZDRpSFFEMkdKNXlOLwo2ZkNGQmZaZktTZUs4SmF1d3hnV2tyYTUzT2NEcS9tS2QrREEvZEsrL3J1Rzd0cXdWZ0lhMDRIT3Bsek03TFlSCkdCMElyczkrbHI1L1BKYlFabVUwNzNNZG42Y1hBZzNwKzZ3dndGbERrUzV2MTNnQkRZTkh0RjYyYmM1NTFlZEYKWjZrR3pKN3dtR1JvODRhQlA3TXVSWmVSZUxPclNTNjdhMXdMZHpac01uUDFUSjd4OUxmcjlNS2wydURuUWRuWQpleDhEQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QWZCZ05WCkhTTUVHREFXZ0JTUS9tdFpkNmg4ZW45WVFWSDZITzFQbFdXaXF6QVBCZ05WSFJNQkFmOEVCVEFEQVFIL01BMEcKQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUNoUVlOdWFoMyttVHBJQkRZeEdyalRKTnVPVElNYVd6TXlpMXRrZitMMApzRUd3cGJtQU8ybVdXUVlGN1dWTHNpOThQVUxoM2FkanQyaml1ZDlWbGFhQzZnbnduNVpvMStQaWxvOXNOTExXCjZpajArck40a3dJbS9wTnFpK2pEdXUyY3ZBdUhJd1pXZWg4YkVlLzVVQ3hvNGlobVdGUU44ZUo2VFVLQ3BoUkMKNkVvci9TU1paQlFIZ1BsMEJjaHpIT2t3dTdSM0xDbmRScXhqaEFvVmI5eVFPVitac21UZUpYdWx3TnpKMXVMdApUOE9JZ0lpRHBtQm83SFNOMkgwazNjaHgwMEFzalV5SjltbUFXUGVqRmUvS1hMUlBjVlpSMTdqaHpnZklCRXpzCk01V3RXRm0xYUhEalZ2Nk02aXRlVm02MUU5VCtrL00xMXJ1MWUyWXdzeFREdmI2eDA0bWNyTnU5c29xZGRCYnIKVmZwbHV1b1EvaEVBYlh0Rk5Qb1R5U3B6MGN3T3djSENvd1ZPTG1kS2d2SW1zelppTXlISEc4VkdHbVBoODhuNwp3VnhiMGdWMFA0Uk1yY01MZGVUZG41NVlRcjFDcUJyMzRlQjZvbDZBc2JUbTNWekJIUlZtRk5rc2wxbzVKQjV0CnRYTGdGL0c4L3J6Si80bTFQYVZ1eHJCN0R4VW1JazhFUGJTSVZrdlp2ZDdMQnpLd1E2SWZWYXVjZXdIZkVhalEKVklpZXhTTWlGYzdsdzNLbnhqT0haamY2Rk05VllnM05vKytHZEM5OXM3TGtJdUp3QU1MTnFUUTdIdmhuN1l2UAo0RmxTSWdjNnhqMFlrR1pFUWxiNW8vNW5hdUVxUVUwQUJndzZqdEk0TnhyTkxUNmNwN0NPNE0weElERWcvM1lECmFBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQotLS0tLUJFR0lOIFBSSVZBVEUgS0VZLS0tLS0KTUlJSlF3SUJBREFOQmdrcWhraUc5dzBCQVFFRkFBU0NDUzB3Z2drcEFnRUFBb0lDQVFEQVF3SVN3TW9DYm1sdQpzT24zNG9NMHdBQmw2WmhNOUxTTVo5Wk9HY2VjRGVhNmIvNnJ0RURHTS9nTDlydFhqaWhDbVhtaFVWYVU5Sy9kCjNWK3lMMnhHUjdkdVJqNGdiNVN4cW9ZeWtUNTBwUCsxUEY1cko3YlZBRmU2MVJDdFZLM1JjUFB5TUs0a0F2blkKYTlKcDJIMGd6Skx3YURCYVhZVlRVNjJ3R2RVd2VxMEl4T3dLOURMZkpzdmVseVpzSzNJeXRrNC9oTnE0RHlGTQo5WHVYSHBCV0NBMGdXclpzUHQvcjYvaytjUlRSWkNkcU45RnVhUEdYNVFpU2M2MVdvb2FrSW5MT21TZzRLWCs0CnFsekJZc0ZpSllYVUtScjhlUm4veWhVeXZWR2pwMFdjLzEvdGdubXdCV2t4MGQ1UHoyS2FWazRBUVFia2I1YVEKc1BzSFo1SnJYZ3RFa0VSZmRVRmxhKzNvNk95bWpwUmlPVmJiekZMRnIwM2dHWnFXc3BpR2dFeEU3amlXbU5DVgpOZFRwaTNnL1F6bytSTmtrTzZpRi82UlhLY2FjeHVZK0hQakpZTDhUOWxRSkRVM2dGd21tSWp3VUIrV1dZTkJ4Ck5scHdkU29TMFJaUjNlSWgwQTloaWVjamYrbndoUVgyWHlrbml2Q1dyc01ZRnBLMnVkem5BNnY1aW5mZ3dQM1MKdnY2N2h1N2FzRllDR3RPQnpxWmN6T3kyRVJnZENLN1BmcGErZnp5VzBHWmxOTzl6SForbkZ3SU42ZnVzTDhCWgpRNUV1YjlkNEFRMkRSN1JldG0zT2VkWG5SV2VwQnN5ZThKaGthUE9HZ1QrekxrV1hrWGl6cTBrdXUydGNDM2MyCmJESno5VXllOGZTMzYvVENwZHJnNTBIWjJIc2ZBd0lEQVFBQkFvSUNBUUMveFpiWjBjY3RxYWdzcXZhVk5URWUKZXExcStoZmFHdlBFWVFhWUhJcklFKzJpNVhjbkdjTEtjS2ZvZHhEakFuOFIvemdkT3A2Y01YMENWbi9Qb2hIawpBRUR0RTgrQVZ3d0FNMUZzT3dnTEhWR2FHejhxcnhCbFlkUWdIY3BtdWVJdTJQWGJDOGVIVUJpYVVPSXVoYXc1Ci9SUk1EQUMvQWkyc3NmaTdnT2p2VkU0b1F4UVcwUUcxS0dPT0FVSm4vdVlIdzJSRlkyVXUxcGlteE8ya0RPNTMKZ2N4bUMxV09ueUNIbUhhaVcvVWg3ejZKYW1mU000ZFh0VEpac2x5aDM3ZGhIS05iZzlWa1A3Q1FLQTRoTHpvcApoYmY1cVk2cmFyZ09OaW55MUhnTVB4cm13S3VVb3VKeU90TjB5QnR4akRDVU5hWFVCd2l5N3NOR1MrSDR2c3lCCjVQOUhoSUhTdHUrRlp0M0hHN0VJcUNuZGlhU0tEUzRqV2FWUUFiYm80bloyWnMyQkQreERlUFJDUlVxWDdyTTQKNFh6UElSV1dYbW1XZi83SWcyOUhicnA0YTlMY09tUTJsZUNKdGJhVEZTTjk2T0xVSjVFK2hRMHVsQ1pnQlZtUQpSQ1VZa0pQNGxPemJhS2R6anhnSE1ySHptNDVlVUZmOExpck94aTJ1eXhYSFFtRE51NGIzWDE4a3QzUGdVbVVtCjNkWHBsM2ZxU3lKYTdTQ1Y4Wk5CcnNyRHExRSt0aFl0dTkxUWJWU0d4SGQ5SHJOVmUzWGRMYk9DZFU5Q3VDNjkKTmdsem5hYTdzWkxxbXlLZWpUZkdzWTd4cldkTmNNUGw0cDRmY0lEL080RXBBU1pmb3JwVGVLTlQwWklmWlpldwpiMG1BUWVZWnFRTThpL3FNWU4vdUFRS0NBUUVBNXFnMXNSTk1jNlZkTS90UmdsYXNHWW94amdSQzJPcUFEWmdzCm1BWE1VSjNrRXJweXh0K2VDaW15OGlidVlwelJUSVE4ZkJUV1JrQ3RSWlhKNytLY0xWdGs5UVpJb0xiaHlOd2QKNEl4RVFaRnVVbGpEYnZTalRMU3ljc0h2bzY1aWJXSWZUTDdiZ1dsTEdnR3EvVU96ZkdzZ0g2Uzl3THA1RzMwRwo4RUx5akk1ZVRJWUlDcmZUbVZMK2M0NU1ScEVNS28rY3Z6OFB5c2lhT0ZUbjNjeXN3UFZkWWFlRUVxTVFqVTh3CklHTnNHWkx5dFk3QkFCQmNZMGxkcnRiYS9PK0Z2LytSSDd1VXR6UDd4cENJd0ZDeDgwWnpOK1dSeTlOdkk2M1UKenEzeUlCb1c5R3lBcEQyK1BMYVBOeGY3UUxUVUNoWTFaei9kWVJsdEtPeHYyQWE1Z1FLQ0FRRUExV0xXTnFwMApmaEIvWnRmU0VTaHhGTU04OWNqTjZBYXoxV0tMN3VUQm91OW9TSm54amtoa2FWNzZhY25UL2lxWHR4TU5nSGkxCmZJbURwVTNQdk0wWTRVZDJUNDdvSGM2UDFCclpQTi9HbVh5L3M2QkFFZFB3TGU3Sis0blRJU0hBZEdtcmgrYS8KNXBrdHUzMmc5bFdxZnR4ZWNGSVZTTFBXa3hUMFhLaU14cDFmZmtMK09hdnBNZ01GWks0MWlLczNkTlNoS1BvZwpMOEdTUGNQOXgveW43OFAyZUszTitQR2psQTZwUHpyQU55V1U3TjAvYm1IY0I5VEtQK3VkWVdjalZocnU3TVlOCndOckU0a0tkQzh2OGk3eDd0RGJ2Yjc5VCtGbzZQSWg1M3AwT3NuWnpBOFVSMFFOUit2RFF1ZlF1eWFqOFJFQysKWkc4WXlDS3N2azh5Z3dLQ0FRQS9mc1N4QjBmL2VlRXJZeDZ3QzUzNnRlRW9ZQ0hxeHJzVGd2V2JyOVRyeUZzMQprSi95QVRMblIwMWNmYjBYNW1WemM5K1dwTUhMdXhnMzFLRXZhU2xuRHdhK3NNa2pmTlN3ejI5bUZoYmdHZUhOCngyT2RVcmoxYjdURUJJRXNoTi9SanJaaEVSVXFEY3MvMEgrNmtuMkJYWmdOUGZPQ2I1TFJMMXpPblE5YUJBTVAKZThJUStVUEZyR1FoZVdXajgxL3ZBM081N2VreUFJRDd5dHU5WWcrWVdyTW5JODhtdGo3ak40NWZEQitBOXNQYgptUDJtUDlxKzlqNVUyQTZXbkhVc1FuVTMwQktEVUVzYUFVV3o4MExaWG1adlY4SUg0eDl3S2ZVd0pCQklLQVp6CnFMN005N1k3em1Ha1gvU3BmbDMwbk9KOGxzY2hhTGQxRVlsRVphMkJBb0lCQVFDeWUyNVQ0VFY1TUpGdjB6dVoKTUd1TmcxU2MvTzRGa24yZkVVT2NlV2pod1VCSDRjUGpUL2YxRHdXRHNOYUo5TlJieENyNTkzMU9BclBEYzVjOApBNDA0K1k0ak01UkJRa0tabGk5NHRIQW9kK2pjOVVCQjZUVXZKbGw1OVNsTXdDOTY3OXdTMjFaT0tuZlBLR0NYClNzWkdRRXNaeGY2Wmhoc0hnWEozZ2wvbHpVSlBtUGVPQTVZVlIrT2Q5LzA5S0lGRlRvalNmb3luaFZDdUt4NDkKeGI0dVZZbjJIT0o0eEowZlBUZ2hkQ0hNdnJtWGVlUVJqdmI4OGVhTm1xVlVFSEhGRnRnYjRma2xBNWZFN1JUeApCaGxpUkRCd1o3YlVrSU5LNnlWazluNkJUbnM1bU12UkxtZ2RuSnBZdkU3S0MwMkxUYlpiM0krajhDMFpVYStOCnF5N0RBb0lCQUFpZXJpYlM3V1VjbDJhQmxrbTUrVzdxTm0vSU5tNXp2bm9TUG82VjN3YTVoczZmOStDL2tiZEYKODdqUVBBL1lGZTN1UjJzQUo3c2xYNWV1Wks4V21mcEZtZ3psdTBzRXo4MU1MUS9XeXBadFp5dHlWdFd6QjJQdQpYQ1cxdGRTSDllSTJCbWhYZ29rSE5UTTQ4TmsveE9FTnJQL3NlWHJJeDVMSzBobkRIWm90dS96NitZU2tCOWhGCmNtMmZaeWdEMWRNTFg2bGlSaW14eUZZK2RJQ0pOQjk1SmlmVExXWW5XZUdkZGt3UHRYVWVHWEUxb2x6dk5rTEQKek16RTA5dWhreC9sUkpudGVPQkVaYWY4ME9CLzA5T2k5YjkvcnhZNTlkd3NINkdheExvVGZFS3VQbnZCVk1OUgpZa1UxNFd6UUtsZUZraUJKSTlsVnZuZmdHbk9sZ2cwPQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t","io.buildah.version":"1.21.0"} \ No newline at end of file diff --git a/tests/test_parser.py b/tests/test_parser.py index efbb359..3d597ff 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -103,7 +103,8 @@ def test_parser_finds_services_raw(): "containers": [ "my-stack_agent:9001" ], - "letsencrypt": False + "letsencrypt": False, + "redirect_ssl": False } }, "redirect":{ @@ -119,18 +120,20 @@ def test_parser_finds_services_raw(): "containers": [ "my-stack_cadvisor:8080" ], - "letsencrypt": False + "letsencrypt": False, + "redirect_ssl": False }, "node-exporter.quantum.example.org":{ "containers": [ "my-stack_node-exporter:9100" ], - "letsencrypt": True + "letsencrypt": True, + "redirect_ssl": False } }, "redirect":{ - } + }, }, { "mode":"http", @@ -141,13 +144,15 @@ def test_parser_finds_services_raw(): "containers": [ "my-stack_node-exporter:9100" ], - "letsencrypt": False + "letsencrypt": False, + "redirect_ssl": False }, "www.somehost.com.br":{ "containers": [ "some-service:80" ], - "letsencrypt": False + "letsencrypt": False, + "redirect_ssl": False } }, "redirect":{ @@ -168,7 +173,8 @@ def test_parser_finds_services_raw(): "containers": [ "some-service:80" ], - "letsencrypt": False + "letsencrypt": False, + "redirect_ssl": False } }, "redirect":{ @@ -177,7 +183,7 @@ def test_parser_finds_services_raw(): "www.somehost.com":"https://www.somehost.com.br", "byjg.ca":"https://www.somehost.com.br", "www.byjg.ca":"https://www.somehost.com.br" - } + }, } ] @@ -317,6 +323,58 @@ def test_parser_multiple_hosts(): assert [] == cfg.letsencrypt_hosts +def test_parser_redirect_ssl(): + line_list = load_fixture("services-redirect-ssl") + + result = { + "customerrors": False + } + + cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + haproxy_config = cfg.generate(line_list) + + assert len(haproxy_config) > 0 + path = os.path.dirname(os.path.realpath(__file__)) + with open(path + "/expected/services-redirect-ssl.txt", 'r') as expected_file: + assert expected_file.read() == haproxy_config + assert [] == cfg.letsencrypt_hosts + + +def test_parser_ssl_strict(): + line_list = load_fixture("no-services") + + result = { + "customerrors": False, + "ssl_mode": "strict" + } + + cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + haproxy_config = cfg.generate(line_list) + + assert len(haproxy_config) > 0 + path = os.path.dirname(os.path.realpath(__file__)) + with open(path + "/expected/ssl-strict.txt", 'r') as expected_file: + assert expected_file.read() == haproxy_config + assert [] == cfg.letsencrypt_hosts + +def test_parser_ssl_loose(): + line_list = load_fixture("no-services") + + result = { + "customerrors": False, + "ssl_mode": "loose" + } + + cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER) + haproxy_config = cfg.generate(line_list) + + assert len(haproxy_config) > 0 + path = os.path.dirname(os.path.realpath(__file__)) + with open(path + "/expected/ssl-loose.txt", 'r') as expected_file: + assert expected_file.read() == haproxy_config + assert [] == cfg.letsencrypt_hosts + + #test_parser_finds_services_raw() #test_parser_tcp() #test_parser_multiple_hosts()