1
0
Fork 0

Refactoring to Python

This commit is contained in:
Joao M 2022-08-24 01:00:26 +00:00
parent cd5035602a
commit 7d959f892a
43 changed files with 454 additions and 34 deletions

5
src/banner.txt Normal file
View file

@ -0,0 +1,5 @@
" _ "
" ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ "
"/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |"
"\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |"
" |__/ |_| |__/ "

178
src/easymapping/__init__.py Normal file
View file

@ -0,0 +1,178 @@
import base64
import hashlib
from jinja2 import Environment, FileSystemLoader
import json
import os
import re
class DockerLabelHandler:
def __init__(self, label):
self.__label_base = label
def get_lookup_label(self):
return self.__label_base
def create(self, key):
if isinstance(key, str):
return "{}.{}".format(self.__label_base, key)
return "{}.{}".format(self.__label_base, ".".join(key))
def get(self, label, default_value = ""):
if self.has_label(label):
return self.__data[label]
return default_value
def get_bool(self, label, default_value = False):
if self.has_label(label):
return self.__data[label].lower() in ["True", "true", "1", "yes"]
return default_value
def get_json(self, label, default_value = {}):
if self.has_label(label):
return json.loads(self.__data[label])
return default_value
def set_data(self, data):
self.__data = data
def has_label(self, label):
if label in self.__data:
return True
return False
class HaproxyConfigGenerator:
def __init__(self, mapping, ssl_cert_folder="/certs"):
self.mapping = mapping
self.mapping.setdefault("ssl_mode", 'default')
self.mapping.setdefault("letsencrypt", {"email": ""})
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_haproxy = ssl_cert_folder + "/haproxy"
self.ssl_cert_letsecncrypt = ssl_cert_folder + "/letsencrypt"
self.letsencrypt_hosts = []
self.certs = {}
def generate(self, container_metadata = None):
self.mapping.setdefault("easymapping", [])
if container_metadata is not None:
self.mapping["easymapping"] = self.parse(container_metadata)
file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)
env.trim_blocks = True
env.lstrip_blocks = True
env.rstrip_blocks = True
template = env.get_template('haproxy.cfg.j2')
return template.render(data=self.mapping)
def parse(self, container_metadata):
easymapping = dict()
for container in container_metadata:
d = container_metadata[container]
# Extract the definitions dynamically
definitions = {}
r = re.compile(self.label.get_lookup_label() + r"\.(.*)\..*")
for key in d.keys():
if r.match(key):
definitions[r.search(key).group(1)] = 1
if len(definitions.keys()) == 0:
continue
self.label.set_data(d)
# Parse each definition found.
for definition in definitions.keys():
mode = self.label.get(
self.label.create([definition, "mode"]),
"http"
)
# TODO: we can ignore "host" in TCP, but it would break the template
host_label = self.label.create([definition, "host"])
if not self.label.has_label(host_label):
continue
port = self.label.get(
self.label.create([definition, "port"]),
"80"
)
letsencrypt = self.label.get_bool(
self.label.create([definition, "letsencrypt"]),
False
) and self.mapping["letsencrypt"]["email"] != ""
if port not in easymapping:
easymapping[port] = {
"mode": mode,
"health-check": "",
"port": port,
"hosts": dict(),
"redirect": dict(),
}
# TODO: this could use `EXPOSE` from `Dockerfile`?
ct_port = self.label.get(
self.label.create([definition, "localport"]),
"80"
)
easymapping[port]["health-check"] = self.label.get(
self.label.create([definition, "health-check"]),
""
)
for hostname in d[host_label].split(","):
hostname = hostname.strip()
easymapping[port]["hosts"].setdefault(hostname, {})
easymapping[port]["hosts"][hostname].setdefault("containers", [])
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"])
)
if letsencrypt:
if "443" not in easymapping:
easymapping["443"] = {
"mode": "http",
"health-check": "ssl",
"port": "443",
"hosts": dict(),
"redirect": dict(),
}
easymapping["443"]["hosts"][hostname] = dict(easymapping[port]["hosts"][hostname])
easymapping["443"]["hosts"][hostname]["letsencrypt"] = False
easymapping["443"]["hosts"][hostname]["redirect_ssl"] = False
easymapping["443"]["ssl"] = True
self.letsencrypt_hosts.append(hostname) if hostname not in self.letsencrypt_hosts else self.letsencrypt_hosts
# handle SSL
ssl_label = self.label.create([definition, "sslcert"])
if self.label.has_label(ssl_label):
filename = "{}/{}.pem".format(
self.ssl_cert_haproxy, d[host_label]
)
easymapping[port]["ssl"] = True
self.certs[filename] = base64.b64decode(d[ssl_label])
if self.label.get_bool(self.label.create([definition, "ssl"])):
easymapping[port]["ssl"] = True
return easymapping.values()

58
src/functions/__init__.py Normal file
View file

@ -0,0 +1,58 @@
from datetime import datetime
import subprocess
import shlex
import numpy as np
class Functions:
@staticmethod
def load(filename):
with open(filename, 'r') as content_file:
return content_file.read()
@staticmethod
def save(filename, contents):
with open(filename, 'w') as file:
file.write(contents)
@staticmethod
def log(source, level, message):
if message is None or message == "":
return
if not isinstance(message, (list, tuple, np.ndarray)):
message = [message]
for line in message:
print("[%s] %s [%s]: %s" % (source, datetime.now().strftime("%x %X"), level, line.rstrip()))
@staticmethod
def run_bash(source, command, log_output=True, return_result=True):
if not isinstance(command, (list, tuple, np.ndarray)):
command = shlex.split(command)
try:
process = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
output = []
while True:
line = process.stdout.readline().rstrip()
output.append(line) if return_result else None
Functions.log(source, "info", line) if log_output else None
Functions.log(source, "error", process.stderr.readline())
return_code = process.poll()
if return_code is not None:
lines = []
for line in process.stdout.readlines():
output.append(line.rstrip()) if return_result else None
lines.append(line.rstrip())
Functions.log(source, "info", lines) if log_output else None
Functions.log(source, "error", process.stderr.readlines())
break
return output
except Exception as e:
Functions.log(source, 'error', "%s" % (e))

49
src/main.py Normal file
View file

@ -0,0 +1,49 @@
from functions import Functions
from processor import ProcessorInterface
import os
import time
from threading import Thread
easyhaproxy_config = "/etc/haproxy/easyconfig.yml"
haproxy_config = "/etc/haproxy/haproxy.cfg"
certs_letsencrypt = "/certs/letsencrypt"
certs_haproxy = "/certs/haproxy"
def start():
processor_obj = ProcessorInterface.factory(os.getenv("EASYHAPROXY_DISCOVER"))
if processor_obj is None:
exit(1)
os.makedirs(certs_letsencrypt, exist_ok=True)
os.makedirs(certs_haproxy, exist_ok=True)
Functions.save(haproxy_config, processor_obj.get_haproxy_conf())
for cert in processor_obj.get_certs():
Functions.save(certs_haproxy, processor_obj.get_certs(cert))
#configs = Functions.run_bash('HAPROXY', 'ls /etc/haproxy/conf.d/*.cfg', log_output=False)
x = Thread(target=Functions.run_bash, args=("HAPROXY", "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock", True, False))
x.start()
while True:
time.sleep(10)
def main():
Functions.run_bash('INIT', '/usr/sbin/haproxy -v')
Functions.log('INIT', 'info', " _ ")
Functions.log('INIT', 'info', " ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ ")
Functions.log('INIT', 'info', "/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |")
Functions.log('INIT', 'info', "\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |")
Functions.log('INIT', 'info', " |__/ |_| |__/ ")
Functions.log('INIT', 'INFO', os.getenv("RELEASE_VERSION"))
Functions.log('INIT', 'INFO', "")
Functions.log('INIT', 'INFO', 'Environment:')
for name, value in os.environ.items():
if "HAPROXY" in name:
print("- {0}: {1}".format(name, value))
if __name__ == '__main__':
main()

170
src/processor/__init__.py Normal file
View file

@ -0,0 +1,170 @@
from easymapping import HaproxyConfigGenerator
from functions import Functions
import yaml
import sys
import os
import json
from kubernetes import client, config
from kubernetes.client.rest import ApiException
class ContainerEnv:
@staticmethod
def read():
env_vars = {
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default")
}
if os.getenv("HAPROXY_PASSWORD"):
env_vars["stats"] = {
"username": os.getenv("HAPROXY_USERNAME") if os.getenv("HAPROXY_USERNAME") else "admin",
"password": os.getenv("HAPROXY_PASSWORD"),
"port": os.getenv("HAPROXY_STATS_PORT") if os.getenv("HAPROXY_STATS_PORT") else "1936",
}
env_vars["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv("EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")):
env_vars["letsencrypt"] = {
"email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")
}
return env_vars
class ProcessorInterface:
def __init__(self, filename = None):
self.filename = filename
self.letsencrypt_hosts = None
self.parsed_object = None
self.cfg = None
self.inspect_network()
self.parse()
@staticmethod
def factory(mode):
if mode == "static":
return Static("/etc/haproxy/easyconfig.yml")
elif mode == "docker":
return Docker()
elif mode == "swarm":
return Swarm()
elif mode == "kubernetes":
return Swarm()
else:
Functions.log("FACTORY", "error", "Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got " + mode)
return None
def inspect_network(self):
#Abstract
pass
def parse(self):
# Abstract
pass
def get_letsencrypt_hosts(self):
return self.letsencrypt_hosts
def get_parsed_object(self):
return self.parsed_object
def get_certs(self, key = None):
if key is None:
return self.cfg.certs
else:
return None if key not in self.cfg.certs else self.cfg.certs[key]
def get_haproxy_conf(self):
conf = self.cfg.generate(self.parsed_object)
self.letsencrypt_hosts = self.cfg.letsencrypt_hosts
return conf
class Static(ProcessorInterface):
def inspect_network(self):
self.parsed_object = {}
def parse(self):
static_content = yaml.load(Functions.load(self.filename), Loader=yaml.FullLoader)
self.cfg = HaproxyConfigGenerator(static_content)
class Docker(ProcessorInterface):
def inspect_network(self):
docker = "/usr/bin/docker"
containers_list = Functions.run_bash('DOCKER_PROCESSOR', docker + " ps -q", log_output=False)
containers_list.sort()
containers = list(set(containers_list))
self.parsed_object = {}
for container in containers:
self.parsed_object[container] = json.loads(''.join(Functions.run_bash('DOCKER_PROCESSOR', docker + ' inspect --format "{{ json .Config.Labels }}" ' + container, log_output=False)))
def parse(self):
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
class Swarm(ProcessorInterface):
def inspect_network(self):
docker = "/usr/bin/docker"
node_list = ' '.join(Functions.run_bash('SWARM_PROCESSOR', docker + ' node ls -q', False))
containers_list_raw = Functions.run_bash('SWARM_PROCESSOR', docker + ' node ps ' + node_list + ' --format "{{ .Name }}" --filter desired-state=running', log_output=False)
containers_list = []
for container in containers_list_raw:
containers_list.append(container.split('.')[0])
containers_list.sort()
containers = list(set(containers_list))
self.parsed_object = {}
for container in containers:
self.parsed_object[container] = json.loads(''.join(Functions.run_bash('SWARM_PROCESSOR', docker + ' service inspect --format "{{ json .Spec.Labels }}" ' + container, log_output=False)))
def parse(self):
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
class Kubernetes(ProcessorInterface):
def inspect_network(self):
config.load_incluster_config()
api_instance = client.CoreV1Api()
v1 = client.NetworkingV1Api()
ret = v1.list_ingress_for_all_namespaces(watch=False)
self.parsed_object = {}
for i in ret.items:
if i.metadata.annotations['kubernetes.io/ingress.class'] != "easyhaproxy-ingress":
continue
data = {}
#ingress_name = i.metadata.name
data["creation_timestamp"] = i.metadata.creation_timestamp.strftime("%x %X")
data["resource_version"] = i.metadata.resource_version
data["namespace"] = i.metadata.namespace
for rule in i.spec.rules:
rule_data = {}
port_number = rule.http.paths[0].backend.service.port.number
definition = rule.host.replace(".", "-")
rule_data["easyhaproxy.%s_%s.host" % (definition, port_number)] = rule.host
rule_data["easyhaproxy.%s_%s.port" % (definition, port_number)] = "80"
rule_data["easyhaproxy.%s_%s.localport" % (definition, port_number)] = port_number
service_name = rule.http.paths[0].backend.service.name
try:
api_response = api_instance.read_namespaced_service(service_name, i.metadata.namespace)
cluster_ip = api_response.spec.cluster_ip
except ApiException as e:
cluster_ip = None
# print("Exception when calling CoreV1Api->read_namespaced_service: %s\n" % e)
if cluster_ip is not None:
if cluster_ip not in self.parsed_object.keys():
self.parsed_object[cluster_ip] = data
self.parsed_object[cluster_ip].update(rule_data)
def parse(self):
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())

6
src/requirements.txt Normal file
View file

@ -0,0 +1,6 @@
pyyaml
docker
jinja2
pytest
kubernetes
numpy

8
src/templates/bind.j2 Normal file
View file

@ -0,0 +1,8 @@
{% if "ssl" in o %}
bind *:{{ o["port"] }} ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
{% elif "h2" in o and o["h2"] %}
bind *:{{ o["port"] }} proto h2
option http-use-htx
{% else %}
bind *:{{ o["port"] }}
{% endif %}

View file

@ -0,0 +1,21 @@
mode http
{% for k in o["redirect"] %}
redirect prefix {{ o["redirect"][k] }} code 301 if { hdr(host) -i {{ k }} }
{% endfor %}
{%- for k in o["hosts"] %}
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
{% set letsencrypt = o["hosts"][k]["letsencrypt"] %}
acl is_rule_{{ host }}_1 hdr(host) -i {{ k }}
acl is_rule_{{ host }}_2 hdr(host) -i {{ k }}:{{ o["port"] }}
{% if letsencrypt %}
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 {% if letsencrypt %}!is_letsencrypt_{{ host }} {% endif %}is_rule_{{ host }}_1 OR {% if letsencrypt %}!is_letsencrypt_{{ host }} {% endif %}is_rule_{{ host }}_2
{% else %}
use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2
{% endif %}
{% endfor %}

View file

@ -0,0 +1,6 @@
mode tcp
option tcplog
log global
{% set backend = (o["hosts"]|first) %}
default_backend srv_{{ backend.replace(".", "_") + "_{0}".format(o["port"]) }}

View file

@ -0,0 +1,80 @@
global
log stdout format raw local0 info
maxconn 2000
{% if data["ssl_mode"] == "strict" %}
{% include "ssl_strict.j2" %}
{% elif data["ssl_mode"] == "loose" %}
{% include "ssl_loose.j2" %}
{% else %}
{% include "ssl_default.j2" %}
{% endif %}
defaults
log global
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
{% if data["customerrors"] %}
errorfile 400 /etc/haproxy/errors-custom/400.http
errorfile 403 /etc/haproxy/errors-custom/403.http
errorfile 408 /etc/haproxy/errors-custom/408.http
errorfile 500 /etc/haproxy/errors-custom/500.http
errorfile 502 /etc/haproxy/errors-custom/502.http
errorfile 503 /etc/haproxy/errors-custom/503.http
errorfile 504 /etc/haproxy/errors-custom/504.http
{% endif %}
{% set data_stats = data["stats"] | default({}) %}
{% if data_stats["port"] | default(1936) | int > 0 %}
frontend stats
bind *:{{ data_stats["port"] | default(1936) }}
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
{% if data_stats["password"] | default("") != "" %}
stats auth {{ data_stats["username"] | default("admin") }}:{{ data_stats["password"] }}
{% endif %}
default_backend srv_stats
backend srv_stats
mode http
server Local 127.0.0.1:{{ data_stats["port"] | default(1936) }}
{% endif %}
{% for o in data["easymapping"] -%}
{% set mode = o["mode"] or "http" %}
frontend {{ mode }}_in_{{ o["port"] }}
{% include "bind.j2" %}
{% if mode == "http" %}
{% include "frontend-mode-http.j2" %}
{% else %}
{% include "frontend-mode-tcp.j2" %}
{% endif %}
{% for k in o["hosts"] -%}
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
backend srv_{{ host }}
balance roundrobin
mode {{ mode }}
{% if mode == "http" %}
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
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" }}
{% endif %}
{% for c in o["hosts"][k]["containers"] %}
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["health-check"] == "ssl" }}
{% endfor %}
{% endfor %}
{% endfor %}
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -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

View file

@ -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

View file

@ -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

0
src/tests/__init__.py Normal file
View file

5
src/tests/context.py Normal file
View file

@ -0,0 +1,5 @@
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
import easymapping

View file

@ -0,0 +1,28 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,95 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
errorfile 400 /etc/haproxy/errors-custom/400.http
errorfile 403 /etc/haproxy/errors-custom/403.http
errorfile 408 /etc/haproxy/errors-custom/408.http
errorfile 500 /etc/haproxy/errors-custom/500.http
errorfile 502 /etc/haproxy/errors-custom/502.http
errorfile 503 /etc/haproxy/errors-custom/503.http
errorfile 504 /etc/haproxy/errors-custom/504.http
frontend stats
bind *:1936
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
stats auth admin:password
default_backend srv_stats
backend srv_stats
mode http
server Local 127.0.0.1:1936
frontend http_in_80
bind *:80
mode http
acl is_rule_test_example_org_80_1 hdr(host) -i test.example.org
acl is_rule_test_example_org_80_2 hdr(host) -i test.example.org:80
acl is_letsencrypt_test_example_org_80 path_beg /.well-known/acme-challenge/
use_backend letsencrypt_backend if is_letsencrypt_test_example_org_80 is_rule_test_example_org_80_1 OR is_letsencrypt_test_example_org_80 is_rule_test_example_org_80_2
http-request redirect scheme https code 301 if !is_letsencrypt_test_example_org_80 is_rule_test_example_org_80_1 OR !is_letsencrypt_test_example_org_80 is_rule_test_example_org_80_2
acl is_rule_test2_example_org_80_1 hdr(host) -i test2.example.org
acl is_rule_test2_example_org_80_2 hdr(host) -i test2.example.org:80
use_backend srv_test2_example_org_80 if is_rule_test2_example_org_80_1 OR is_rule_test2_example_org_80_2
backend srv_test_example_org_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 f5c645a0dfc6:80 check weight 1
server srv-1 b63438410b6a:80 check weight 1
backend srv_test2_example_org_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 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
mode http
acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org
acl is_rule_test_example_org_443_2 hdr(host) -i test.example.org:443
use_backend srv_test_example_org_443 if is_rule_test_example_org_443_1 OR is_rule_test_example_org_443_2
backend srv_test_example_org_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 f5c645a0dfc6:80 check weight 1 verify none
server srv-1 b63438410b6a:80 check weight 1 verify none
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,45 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
frontend http_in_19901
bind *:19901
mode http
acl is_rule_www_helloworld_com_19901_1 hdr(host) -i www.helloworld.com
acl is_rule_www_helloworld_com_19901_2 hdr(host) -i www.helloworld.com:19901
use_backend srv_www_helloworld_com_19901 if is_rule_www_helloworld_com_19901_1 OR is_rule_www_helloworld_com_19901_2
backend srv_www_helloworld_com_19901
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 test_nginx.2.t5r94mjlced7m3t5orfjbowmm:80 check weight 1
server srv-1 test_nginx.1.p552hqxkdx88narjrp5kouwb2:80 check weight 1
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,78 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
errorfile 400 /etc/haproxy/errors-custom/400.http
errorfile 403 /etc/haproxy/errors-custom/403.http
errorfile 408 /etc/haproxy/errors-custom/408.http
errorfile 500 /etc/haproxy/errors-custom/500.http
errorfile 502 /etc/haproxy/errors-custom/502.http
errorfile 503 /etc/haproxy/errors-custom/503.http
errorfile 504 /etc/haproxy/errors-custom/504.http
frontend stats
bind *:1937
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
stats auth joe:s3cr3t
default_backend srv_stats
backend srv_stats
mode http
server Local 127.0.0.1:1937
frontend http_in_19901
bind *:19901
mode http
redirect prefix www.google.com code 301 if { hdr(host) -i google.helloworld.com }
acl is_rule_hello_com_19901_1 hdr(host) -i hello.com
acl is_rule_hello_com_19901_2 hdr(host) -i hello.com:19901
use_backend srv_hello_com_19901 if is_rule_hello_com_19901_1 OR is_rule_hello_com_19901_2
acl is_rule_www_helloworld_com_19901_1 hdr(host) -i www.helloworld.com
acl is_rule_www_helloworld_com_19901_2 hdr(host) -i www.helloworld.com:19901
use_backend srv_www_helloworld_com_19901 if is_rule_www_helloworld_com_19901_1 OR is_rule_www_helloworld_com_19901_2
backend srv_hello_com_19901
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 3e63154954b0:80 check weight 1
server srv-1 eb294c110eb1:80 check weight 1
backend srv_www_helloworld_com_19901
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 3e63154954b0:80 check weight 1
server srv-1 eb294c110eb1:80 check weight 1
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,80 @@
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
option httplog
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
http-request redirect scheme https code 301 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 3571640c480a:80 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 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
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 3571640c480a: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 5b69bc7fea1b:8080 check weight 1
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,42 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
frontend tcp_in_31339
bind *:31339
mode tcp
option tcplog
log global
default_backend srv_agent_quantum_local_31339
backend srv_agent_quantum_local_31339
balance roundrobin
mode tcp
option tcp-check
tcp-check connect ssl
server srv-0 test_agent:9001 check weight 1 verify none
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,124 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
frontend tcp_in_31339
bind *:31339
mode tcp
option tcplog
log global
default_backend srv_agent_quantum_example_org_31339
backend srv_agent_quantum_example_org_31339
balance roundrobin
mode tcp
option tcp-check
tcp-check connect
server srv-0 my-stack_agent:9001 check weight 1
frontend http_in_31337
bind *:31337
mode http
acl is_rule_cadvisor_quantum_example_org_31337_1 hdr(host) -i cadvisor.quantum.example.org
acl is_rule_cadvisor_quantum_example_org_31337_2 hdr(host) -i cadvisor.quantum.example.org:31337
use_backend srv_cadvisor_quantum_example_org_31337 if is_rule_cadvisor_quantum_example_org_31337_1 OR is_rule_cadvisor_quantum_example_org_31337_2
acl is_rule_node-exporter_quantum_example_org_31337_1 hdr(host) -i node-exporter.quantum.example.org
acl is_rule_node-exporter_quantum_example_org_31337_2 hdr(host) -i node-exporter.quantum.example.org:31337
acl is_letsencrypt_node-exporter_quantum_example_org_31337 path_beg /.well-known/acme-challenge/
use_backend letsencrypt_backend if is_letsencrypt_node-exporter_quantum_example_org_31337 is_rule_node-exporter_quantum_example_org_31337_1 OR is_letsencrypt_node-exporter_quantum_example_org_31337 is_rule_node-exporter_quantum_example_org_31337_2
use_backend srv_node-exporter_quantum_example_org_31337 if is_rule_node-exporter_quantum_example_org_31337_1 OR is_rule_node-exporter_quantum_example_org_31337_2
backend srv_cadvisor_quantum_example_org_31337
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 my-stack_cadvisor:8080 check weight 1
backend srv_node-exporter_quantum_example_org_31337
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 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
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 }
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i www.somehost.com }
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i byjg.ca }
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i www.byjg.ca }
acl is_rule_node-exporter_quantum_example_org_443_1 hdr(host) -i node-exporter.quantum.example.org
acl is_rule_node-exporter_quantum_example_org_443_2 hdr(host) -i node-exporter.quantum.example.org:443
use_backend srv_node-exporter_quantum_example_org_443 if is_rule_node-exporter_quantum_example_org_443_1 OR is_rule_node-exporter_quantum_example_org_443_2
acl is_rule_www_somehost_com_br_443_1 hdr(host) -i www.somehost.com.br
acl is_rule_www_somehost_com_br_443_2 hdr(host) -i www.somehost.com.br:443
use_backend srv_www_somehost_com_br_443 if is_rule_www_somehost_com_br_443_1 OR is_rule_www_somehost_com_br_443_2
backend srv_node-exporter_quantum_example_org_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 my-stack_node-exporter:9100 check weight 1
backend srv_www_somehost_com_br_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 some-service:80 check weight 1
frontend http_in_80
bind *:80
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 }
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i www.somehost.com }
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i byjg.ca }
redirect prefix https://www.somehost.com.br code 301 if { hdr(host) -i www.byjg.ca }
acl is_rule_www_somehost_com_br_80_1 hdr(host) -i www.somehost.com.br
acl is_rule_www_somehost_com_br_80_2 hdr(host) -i www.somehost.com.br:80
use_backend srv_www_somehost_com_br_80 if is_rule_www_somehost_com_br_80_1 OR is_rule_www_somehost_com_br_80_2
backend srv_www_somehost_com_br_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 some-service:80 check weight 1
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,38 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
frontend stats
bind *:1936
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
default_backend srv_stats
backend srv_stats
mode http
server Local 127.0.0.1:1936
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,23 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

View file

@ -0,0 +1,110 @@
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
option httplog
timeout connect 3s
timeout client 10s
timeout server 10m
errorfile 400 /etc/haproxy/errors-custom/400.http
errorfile 403 /etc/haproxy/errors-custom/403.http
errorfile 408 /etc/haproxy/errors-custom/408.http
errorfile 500 /etc/haproxy/errors-custom/500.http
errorfile 502 /etc/haproxy/errors-custom/502.http
errorfile 503 /etc/haproxy/errors-custom/503.http
errorfile 504 /etc/haproxy/errors-custom/504.http
frontend stats
bind *:1936
mode http
stats enable
stats hide-version
stats realm Haproxy\ Statistics
stats uri /
stats auth admin:test123
default_backend srv_stats
backend srv_stats
mode http
server Local 127.0.0.1:1936
frontend http_in_80
bind *:80
mode http
redirect prefix http://host1.com.br code 301 if { hdr(host) -i www.host1.com.br }
acl is_rule_host1_com_br_80_1 hdr(host) -i host1.com.br
acl is_rule_host1_com_br_80_2 hdr(host) -i host1.com.br:80
acl is_letsencrypt_host1_com_br_80 path_beg /.well-known/acme-challenge/
use_backend letsencrypt_backend if is_letsencrypt_host1_com_br_80 is_rule_host1_com_br_80_1 OR is_letsencrypt_host1_com_br_80 is_rule_host1_com_br_80_2
use_backend srv_host1_com_br_80 if is_rule_host1_com_br_80_1 OR is_rule_host1_com_br_80_2
acl is_rule_host2_com_br_80_1 hdr(host) -i host2.com.br
acl is_rule_host2_com_br_80_2 hdr(host) -i host2.com.br:80
use_backend srv_host2_com_br_80 if is_rule_host2_com_br_80_1 OR is_rule_host2_com_br_80_2
backend srv_host1_com_br_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 container:5000 check weight 1
backend srv_host2_com_br_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 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
mode http
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
acl is_rule_host1_com_br_443_2 hdr(host) -i host1.com.br:443
use_backend srv_host1_com_br_443 if is_rule_host1_com_br_443_1 OR is_rule_host1_com_br_443_2
backend srv_host1_com_br_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 container:80 check weight 1
frontend http_in_8080
bind *:8080
mode http
acl is_rule_host3_com_br_8080_1 hdr(host) -i host3.com.br
acl is_rule_host3_com_br_8080_2 hdr(host) -i host3.com.br:8080
use_backend srv_host3_com_br_8080 if is_rule_host3_com_br_8080_1 OR is_rule_host3_com_br_8080_2
backend srv_host3_com_br_8080
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 domain:8181 check weight 1
backend letsencrypt_backend
mode http
server certbot 127.0.0.1:2080

5
src/tests/fixtures/no-services vendored Normal file
View file

@ -0,0 +1,5 @@
swarm-prom_caddy={"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"}
swarm-prom_cadvisor={"com.docker.stack.image":"google/cadvisor","com.docker.stack.namespace":"swarm-prom"}
swarm-prom_dockerd-exporter={"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"}
swarm-prom_unsee={"com.docker.stack.image":"cloudflare/unsee:v0.8.0","com.docker.stack.namespace":"swarm-prom"}
test_proxy={"com.docker.stack.image":"byjg/easy-haproxy","com.docker.stack.namespace":"test"}

6
src/tests/fixtures/services vendored Normal file
View file

@ -0,0 +1,6 @@
portainer-agent_agent={"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"}
my-stack_agent={"easyhaproxy.agent.host":"agent.quantum.example.org","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":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_cadvisor={"easyhaproxy.cadvisor.host":"cadvisor.quantum.example.org","easyhaproxy.cadvisor.localport":"8080","easyhaproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_node-exporter={"easyhaproxy.exp.host":"node-exporter.quantum.example.org","easyhaproxy.exp.localport":"9100","easyhaproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","easyhaproxy.exp.letsencrypt":"true"}
my-stack_reverse-proxy={"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
some-service={"easyhaproxy.http.port":"80","easyhaproxy.http.host":"www.somehost.com.br","easyhaproxy.http.localport":"80","easyhaproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.port":"443","easyhaproxy.https.host":"www.somehost.com.br","easyhaproxy.https.localport":"80","easyhaproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}

View file

@ -0,0 +1,6 @@
portainer-agent_agent={"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"}
my-stack_agent={"haproxy.agent.host":"agent.quantum.example.org","haproxy.agent.localport":"9001","haproxy.agent.mode":"tcp","haproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_cadvisor={"haproxy.cadvisor.host":"cadvisor.quantum.example.org","haproxy.cadvisor.localport":"8080","haproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
my-stack_node-exporter={"haproxy.exp.host":"node-exporter.quantum.example.org","haproxy.exp.localport":"9100","haproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","haproxy.exp.letsencrypt":"yes"}
my-stack_reverse-proxy={"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"}
some-service={"haproxy.http.port":"80","haproxy.http.host":"www.somehost.com.br","haproxy.http.localport":"80","haproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.port":"443","haproxy.https.host":"www.somehost.com.br","haproxy.https.localport":"80","haproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}

View file

@ -0,0 +1,4 @@
f5c645a0dfc6={"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"}
bbd4d1854155={"com.docker.compose.config-hash":"3dc790bf2bea944359c75a40c45655bd868f1d85beb599d1ca797e8ea2c95ee4","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:0fd95b1512c207048ab3fcc74032354f38143fbb8235ac2a47da903c98a58205","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.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"}
b63438410b6a={"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"}
83d57d592e26={"com.docker.compose.config-hash":"8c5871144f1e8a3aeca037207c02f011ab2c6e6c311a3773602b63541762dab5","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-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"static","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test2.example.org","easyhaproxy.http.localport":"8080","easyhaproxy.http.port":"80","io.buildah.version":"1.21.0"}

View file

@ -0,0 +1,2 @@
test_nginx.2.t5r94mjlced7m3t5orfjbowmm={"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}
test_nginx.1.p552hqxkdx88narjrp5kouwb2={"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}

View file

@ -0,0 +1,3 @@
db79d3a910f4={"com.docker.compose.config-hash":"5bde40f52451521ad201e70de1291397376a0498a7c955624a609da3b60e7e8e","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:ea39067705590557dd0cd951664a10970ceefcb725a3c1f43690d6d6d4ed5fce","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.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"}
3e63154954b0={"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}
eb294c110eb1={"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}

File diff suppressed because one or more lines are too long

2
src/tests/fixtures/services-tcp vendored Normal file
View file

@ -0,0 +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_proxy={"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}

31
src/tests/fixtures/static.yml vendored Normal file
View file

@ -0,0 +1,31 @@
stats:
username: admin
password: test123
port: 1936 # Optional (default 1936)
customerrors: true # Optional (default false)
easymapping:
- port: 80
hosts:
host1.com.br:
containers:
- container:5000
letsencrypt: true
host2.com.br:
containers:
- other:3000
redirect:
www.host1.com.br: http://host1.com.br
- port: 443
ssl: True
hosts:
host1.com.br:
containers:
- container:80
- port: 8080
hosts:
host3.com.br:
containers: [ "domain:8181" ]

32
src/tests/test_labels.py Normal file
View file

@ -0,0 +1,32 @@
from .context import easymapping
import json
import pytest
def test_label_generation():
label = easymapping.DockerLabelHandler("foo")
assert label.create("bar") == "foo.bar"
assert label.create(["bar", "foobar"]) == "foo.bar.foobar"
def test_label_data():
label = easymapping.DockerLabelHandler("base")
label.set_data(json.loads('{"base.definitions":"h2"}'))
label_name = label.create("definitions")
assert label_name == "base.definitions"
assert label.has_label(label_name)
assert label.get(label_name) == "h2"
def test_label_complex_key():
label = easymapping.DockerLabelHandler("till")
data = dict()
data["till.definitions"] = "h2"
data["till.host.h2"] = "fqdn.example.org"
data["till.mode.h2"] = "tcp"
label.set_data(json.loads(json.dumps(data)))
assert label.get(label.create(["host", "h2"])) == "fqdn.example.org"
assert label.get(label.create(["mode", "h2"])) == "tcp"

433
src/tests/test_parser.py Normal file
View file

@ -0,0 +1,433 @@
import easymapping
import pytest
import os
import yaml
CERTS_FOLDER="/tmp/certs"
CERT_FILE="/tmp/certs/haproxy/www.somehost.com.br.pem"
LETSENCRYPT_EMAIL="some@email.com"
def load_fixture(file):
path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/fixtures/" + file, 'r') as content_file:
line_list = content_file.readlines()
return line_list
def test_parser_doesnt_crash():
line_list = load_fixture("no-services")
result = {
"customerrors": False,
"stats": {
"port": "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/no-services.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
assert [] == cfg.letsencrypt_hosts
def test_parser_finds_services():
line_list = load_fixture("services")
result = {
"customerrors": False,
"letsencrypt": {
"email": LETSENCRYPT_EMAIL
},
"stats": {
"port": 0
}
}
if os.path.exists(CERT_FILE):
os.remove(CERT_FILE)
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.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
with open(CERT_FILE, 'r') as expected_file:
assert expected_file.read() == "Some PEM Certificate"
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
def test_parser_finds_services_changed_label():
line_list = load_fixture("services-changed-label")
result = {
"customerrors": False,
"lookup_label": "haproxy",
"letsencrypt": {
"email": LETSENCRYPT_EMAIL
},
"stats": {
"port": 0
}
}
if os.path.exists(CERT_FILE):
os.remove(CERT_FILE)
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.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
with open(CERT_FILE, 'r') as expected_file:
assert expected_file.read() == "Some PEM Certificate"
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
def test_parser_finds_services_raw():
line_list = load_fixture("services")
result = {
"customerrors": False,
"letsencrypt": {
"email": LETSENCRYPT_EMAIL
},
"stats": {
"port": 0
}
}
if os.path.exists(CERT_FILE):
os.remove(CERT_FILE)
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
parsed_object = [
{
"mode":"tcp",
"health-check":"",
"port":"31339",
"hosts":{
"agent.quantum.example.org": {
"containers": [
"my-stack_agent:9001"
],
"letsencrypt": False,
"redirect_ssl": False
}
},
"redirect":{
}
},
{
"mode":"http",
"health-check":"",
"port":"31337",
"hosts":{
"cadvisor.quantum.example.org":{
"containers": [
"my-stack_cadvisor:8080"
],
"letsencrypt": False,
"redirect_ssl": False
},
"node-exporter.quantum.example.org":{
"containers": [
"my-stack_node-exporter:9100"
],
"letsencrypt": True,
"redirect_ssl": False
}
},
"redirect":{
},
},
{
"mode":"http",
"health-check":"",
"port":"443",
"hosts":{
"node-exporter.quantum.example.org": {
"containers": [
"my-stack_node-exporter:9100"
],
"letsencrypt": False,
"redirect_ssl": False
},
"www.somehost.com.br":{
"containers": [
"some-service:80"
],
"letsencrypt": False,
"redirect_ssl": False
}
},
"redirect":{
"somehost.com.br":"https://www.somehost.com.br",
"somehost.com":"https://www.somehost.com.br",
"www.somehost.com":"https://www.somehost.com.br",
"byjg.ca":"https://www.somehost.com.br",
"www.byjg.ca":"https://www.somehost.com.br"
},
"ssl": True
},
{
"mode":"http",
"health-check":"",
"port":"80",
"hosts":{
"www.somehost.com.br":{
"containers": [
"some-service:80"
],
"letsencrypt": False,
"redirect_ssl": False
}
},
"redirect":{
"somehost.com.br":"https://www.somehost.com.br",
"somehost.com":"https://www.somehost.com.br",
"www.somehost.com":"https://www.somehost.com.br",
"byjg.ca":"https://www.somehost.com.br",
"www.byjg.ca":"https://www.somehost.com.br"
},
}
]
processed = list(cfg.parse(line_list))
assert parsed_object == processed
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
def test_parser_static():
path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/fixtures/static.yml", 'r') as content_file:
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
cfg = easymapping.HaproxyConfigGenerator(parsed, CERTS_FOLDER)
haproxy_config = cfg.generate()
assert len(haproxy_config) > 0
with open(path + "/expected/static.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
assert [] == cfg.letsencrypt_hosts
def test_parser_static_raw():
path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/fixtures/static.yml", 'r') as content_file:
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
expected = {
"stats": {
"username": "admin",
"password": "test123",
"port": 1936
},
"customerrors": True,
"easymapping": [
{
"port": 80,
"hosts": {
"host1.com.br": {
"containers": [
"container:5000"
],
"letsencrypt": True
},
"host2.com.br": {
"containers": [
"other:3000"
]
}
},
"redirect": {
"www.host1.com.br": "http://host1.com.br"
}
},
{
"port": 443,
"ssl": True,
"hosts": {
"host1.com.br": {
"containers": [
"container:80"
]
}
}
},
{
"port": 8080,
"hosts": {
"host3.com.br": {
"containers": [
"domain:8181"
]
}
}
}
]
}
assert expected == parsed
def test_parser_tcp():
line_list = load_fixture("services-tcp")
result = {
"customerrors": False,
"stats": {
"port": 0
}
}
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
haproxy_config = cfg.generate(line_list)
# print(haproxy_config)
assert len(haproxy_config) > 0
path = os.path.dirname(os.path.realpath(__file__))
with open(path + "/expected/services-tcp.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
assert [] == cfg.letsencrypt_hosts
def test_parser_multi_containers():
line_list = load_fixture("services-multi-containers")
result = {
"customerrors": False,
"stats": {
"port": 0
}
}
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-multi-containers.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
assert [] == cfg.letsencrypt_hosts
def test_parser_multiple_hosts():
line_list = load_fixture("services-multiple-hosts")
result = {
"customerrors": True,
"stats": {
"username": "joe",
"password": "s3cr3t",
"port": "1937"
}
}
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-multiple-hosts.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
assert [] == cfg.letsencrypt_hosts
def test_parser_redirect_ssl():
line_list = load_fixture("services-redirect-ssl")
result = {
"customerrors": False,
"ssl_mode": "loose",
"stats": {
"port": 0
}
}
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",
"stats": {
"port": 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/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
def test_parser_ssl_letsencrypt():
line_list = load_fixture("services-letsencrypt")
result = {
"customerrors": True,
"stats": {
"password": "password"
},
"letsencrypt": {
"email": LETSENCRYPT_EMAIL
}
}
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-letsencrypt.txt", 'r') as expected_file:
assert expected_file.read() == haproxy_config
assert ["test.example.org"] == cfg.letsencrypt_hosts
#test_parser_finds_services_raw()
#test_parser_tcp()
#test_parser_multiple_hosts()
#test_parser_ssl_letsencrypt()