Refactoring to Python
This commit is contained in:
parent
cd5035602a
commit
7d959f892a
43 changed files with 454 additions and 34 deletions
5
src/banner.txt
Normal file
5
src/banner.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
" _ "
|
||||
" ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ "
|
||||
"/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |"
|
||||
"\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |"
|
||||
" |__/ |_| |__/ "
|
||||
178
src/easymapping/__init__.py
Normal file
178
src/easymapping/__init__.py
Normal 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
58
src/functions/__init__.py
Normal 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
49
src/main.py
Normal 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
170
src/processor/__init__.py
Normal 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
6
src/requirements.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
pyyaml
|
||||
docker
|
||||
jinja2
|
||||
pytest
|
||||
kubernetes
|
||||
numpy
|
||||
8
src/templates/bind.j2
Normal file
8
src/templates/bind.j2
Normal 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 %}
|
||||
21
src/templates/frontend-mode-http.j2
Normal file
21
src/templates/frontend-mode-http.j2
Normal 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 %}
|
||||
6
src/templates/frontend-mode-tcp.j2
Normal file
6
src/templates/frontend-mode-tcp.j2
Normal 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"]) }}
|
||||
|
||||
80
src/templates/haproxy.cfg.j2
Normal file
80
src/templates/haproxy.cfg.j2
Normal 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
|
||||
12
src/templates/ssl_default.j2
Normal file
12
src/templates/ssl_default.j2
Normal 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
|
||||
10
src/templates/ssl_loose.j2
Normal file
10
src/templates/ssl_loose.j2
Normal 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
|
||||
|
||||
7
src/templates/ssl_strict.j2
Normal file
7
src/templates/ssl_strict.j2
Normal 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
0
src/tests/__init__.py
Normal file
5
src/tests/context.py
Normal file
5
src/tests/context.py
Normal 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
|
||||
28
src/tests/expected/no-services.txt
Normal file
28
src/tests/expected/no-services.txt
Normal 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
|
||||
95
src/tests/expected/services-letsencrypt.txt
Normal file
95
src/tests/expected/services-letsencrypt.txt
Normal 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
|
||||
45
src/tests/expected/services-multi-containers.txt
Normal file
45
src/tests/expected/services-multi-containers.txt
Normal 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
|
||||
78
src/tests/expected/services-multiple-hosts.txt
Normal file
78
src/tests/expected/services-multiple-hosts.txt
Normal 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
|
||||
80
src/tests/expected/services-redirect-ssl.txt
Normal file
80
src/tests/expected/services-redirect-ssl.txt
Normal 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
|
||||
42
src/tests/expected/services-tcp.txt
Normal file
42
src/tests/expected/services-tcp.txt
Normal 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
|
||||
124
src/tests/expected/services.txt
Normal file
124
src/tests/expected/services.txt
Normal 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
|
||||
38
src/tests/expected/ssl-loose.txt
Normal file
38
src/tests/expected/ssl-loose.txt
Normal 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
|
||||
23
src/tests/expected/ssl-strict.txt
Normal file
23
src/tests/expected/ssl-strict.txt
Normal 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
|
||||
110
src/tests/expected/static.txt
Normal file
110
src/tests/expected/static.txt
Normal 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
5
src/tests/fixtures/no-services
vendored
Normal 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
6
src/tests/fixtures/services
vendored
Normal 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="}
|
||||
6
src/tests/fixtures/services-changed-label
vendored
Normal file
6
src/tests/fixtures/services-changed-label
vendored
Normal 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="}
|
||||
4
src/tests/fixtures/services-letsencrypt
vendored
Normal file
4
src/tests/fixtures/services-letsencrypt
vendored
Normal 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"}
|
||||
2
src/tests/fixtures/services-multi-containers
vendored
Normal file
2
src/tests/fixtures/services-multi-containers
vendored
Normal 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"}
|
||||
3
src/tests/fixtures/services-multiple-hosts
vendored
Normal file
3
src/tests/fixtures/services-multiple-hosts
vendored
Normal 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\"}"}
|
||||
3
src/tests/fixtures/services-redirect-ssl
vendored
Normal file
3
src/tests/fixtures/services-redirect-ssl
vendored
Normal file
File diff suppressed because one or more lines are too long
2
src/tests/fixtures/services-tcp
vendored
Normal file
2
src/tests/fixtures/services-tcp
vendored
Normal 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
31
src/tests/fixtures/static.yml
vendored
Normal 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
32
src/tests/test_labels.py
Normal 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
433
src/tests/test_parser.py
Normal 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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue