Merge pull request #24 from byjg/redirect-ssl
Add `redirect-ssl` and different `ssl_modes`
This commit is contained in:
commit
21a4a056cb
27 changed files with 491 additions and 81 deletions
|
|
@ -13,6 +13,7 @@ RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml supervisor docker ce
|
|||
&& pip3 install --upgrade pip \
|
||||
&& pip install -r requirements.txt \
|
||||
&& pytest -s tests/ \
|
||||
&& openssl dhparam -out /etc/haproxy/dhparam 2048
|
||||
&& openssl dhparam -out /etc/haproxy/dhparam 2048 \
|
||||
&& openssl dhparam -out /etc/haproxy/dhparam-1024 1024
|
||||
|
||||
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf" ]
|
||||
|
|
|
|||
91
README.md
91
README.md
|
|
@ -8,19 +8,26 @@
|
|||
|
||||
Service discovery for HAProxy.
|
||||
|
||||
This Docker image will create dynamically the `haproxy.cfg` based on the labels defined in docker containers or from
|
||||
This Docker image will dynamically create the `haproxy.cfg` based on the labels defined in docker containers or from
|
||||
a simple Yaml.
|
||||
|
||||
## Features
|
||||
|
||||
- Enable or disable Stats on port 1936 with custom password
|
||||
- Discover and setup haproxy from Docker Tag
|
||||
- Discover and setup haproxy redirect from Docker Tag
|
||||
- Setup HAProxy CFG from a Yaml file.
|
||||
EasyHAProxy will discover the services based on the Docker Tags of the running containers in a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features::
|
||||
- Use Letsencrypt with HAProxy.
|
||||
- Balance traffic between multiple replicas
|
||||
- Set SSL with three different levels of validations and according to the most recent definitions.
|
||||
- Include your SSL certificate.
|
||||
- Setup HAProxy to listen to TCP.
|
||||
- Add redirects.
|
||||
- Enable/disable Stats on port 1936 with a custom password.
|
||||
- Enable/disable custom errors.
|
||||
|
||||
Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
The Easy HAProxy will create the `haproxy.cfg` automatically based on the containers or from a YAML provided.
|
||||
The Easy HAProxy will automatically create the `haproxy.cfg` file based on the containers or a YAML provided.
|
||||
|
||||
The basic command line to run is:
|
||||
|
||||
|
|
@ -41,14 +48,15 @@ The environment variables will setup the HAProxy.
|
|||
| Environment Variable | Description |
|
||||
|-------------------------------|-------------------------------------------------------------------------------|
|
||||
| EASYHAPROXY_DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` |
|
||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search to match resources. Default: `easyhaproxy`. |
|
||||
| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request certificate to letsencrypt |
|
||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. Default: `easyhaproxy`. |
|
||||
| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt |
|
||||
| EASYHAPROXY_SSL_MODE | (Optional) `STRICT` supports only the most recent TLS version; `DEFAULT` good SSL integration with recent browsers; `LOOSE` supports all old SSL protocols for old browsers (not recommended). |
|
||||
| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. Default: `admin` |
|
||||
| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. |
|
||||
| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set, it will disable stats. |
|
||||
| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. Default: `1936` |
|
||||
| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. Default: false |
|
||||
|
||||
The environment variable `EASYHAPROXY_DISCOVER` will define where is located your containers (see below more details):
|
||||
The environment variable `EASYHAPROXY_DISCOVER` will define where is located your containers (see below for more details):
|
||||
|
||||
- docker
|
||||
- swarm
|
||||
|
|
@ -56,15 +64,15 @@ The environment variable `EASYHAPROXY_DISCOVER` will define where is located you
|
|||
|
||||
## Automatic Discover Services
|
||||
|
||||
Easy HAProxy can discover automatically the container services running in the same network of Docker or in a Docker Swarm cluster.
|
||||
Easy HAProxy can automatically discover the container services running in the same network of Docker or in a Docker Swarm cluster.
|
||||
|
||||
### EASYHAPROXY_DISCOVER: docker
|
||||
|
||||
This method will use a regular docker installation to discover the containers and configure the HAProxy.
|
||||
This method will use a standard docker installation to discover the containers and configure the HAProxy.
|
||||
|
||||
The only requirement is that containers and easy-haproxy must be in the same docker network.
|
||||
|
||||
The discover will occur every minute.
|
||||
The discovery will occur every minute.
|
||||
|
||||
e.g.:
|
||||
|
||||
|
|
@ -81,11 +89,11 @@ docker run --network easyhaproxy myimage
|
|||
This method requires a functional Docker Swarm Cluster. The system will search for the labels in all containers on all
|
||||
swarm nodes.
|
||||
|
||||
The discover will occur every minute.
|
||||
The discovery will occur every minute.
|
||||
|
||||
Important: easyhaproxy needs to be in the same network of the containers or otherwise will not access.
|
||||
|
||||
### Tags to be attached in the Docker Container (Swarm or Docker)
|
||||
### Docker Container (Swarm or Docker) tags:
|
||||
|
||||
| Tag | Description | Example |
|
||||
|---------------------------------------|---------------------------------------------------------------------------------------------------------|--------------|
|
||||
|
|
@ -93,14 +101,16 @@ Important: easyhaproxy needs to be in the same network of the containers or othe
|
|||
| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. (Defaults to http) | http |
|
||||
| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. (Defaults to 80) | 80 |
|
||||
| easyhaproxy.[definition].localport | (Optional) Port container is listening. (Defaults to 80) | 8080 |
|
||||
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to url redirect. | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
||||
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
||||
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if letsencrypt is enabled. | |
|
||||
| easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` (Defaults to "empty") | |
|
||||
| easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with sslcert | true OR yes OR false OR no |
|
||||
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | true |
|
||||
| easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` (Defaults to "empty") | ssl |
|
||||
| easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with `sslcert`. | true OR yes OR false OR no |
|
||||
| easyhaproxy.[definition].redirect-ssl | (Optional) Redirect all requests to https | true OR yes OR false OR no |
|
||||
|
||||
### Defining the labels in Docker Swarm
|
||||
|
||||
if you are deploying a stack in a Docker Swarm cluster set labels at the `deploy` level:
|
||||
if you are deploying a stack in a Docker Swarm cluster, set labels at the `deploy` level:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
|
|
@ -138,7 +148,7 @@ docker run \
|
|||
|
||||
### TLS passthrough
|
||||
|
||||
Used to pass on SSL-termination to a backend. Alternatively, you can enable health-check via SSL on the backend with the optional `health-check` label:
|
||||
Used to pass on SSL termination to a backend. Alternatively, you can enable health-check via SSL on the backend with the optional `health-check` label:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
|
|
@ -177,9 +187,11 @@ easymapping:
|
|||
containers:
|
||||
- container:5000
|
||||
letsencrypt: true
|
||||
redirect-ssl: true
|
||||
host2.com.br:
|
||||
containers:
|
||||
- other:3000
|
||||
ssl: false
|
||||
redirect:
|
||||
www.host1.com.br: http://host1.com.br
|
||||
|
||||
|
|
@ -189,6 +201,7 @@ easymapping:
|
|||
host1.com.br:
|
||||
containers:
|
||||
- container:80
|
||||
redirect-ssl: false
|
||||
|
||||
- port: 8080
|
||||
hosts:
|
||||
|
|
@ -231,15 +244,17 @@ Caveats:
|
|||
|
||||
- Your container **must** listen to the port 80. Besides no error, the certificate won't be issued if in a different port.
|
||||
- The port 2080 is reserved for the certbot and should not be exposed.
|
||||
- You cannot set the port 443 for the container with the Letsencrypt. EasyHAProxy will handle this automatically once the certificate is issued.
|
||||
- You cannot set the port 443 for the container with the Letsencrypt because EasyHAProxy will handle this automatically once the certificate is issued.
|
||||
- If you don't run the EasyHAProxy with the parameter `EASYHAPROXY_LETSENCRYPT_EMAIL` no certificate will be issued.
|
||||
- Be aware about the issue limits - https://letsencrypt.org/docs/rate-limits/
|
||||
- Be aware of Letsencrypt issue limits - https://letsencrypt.org/docs/duplicate-certificate-limit/ and https://letsencrypt.org/docs/rate-limits/
|
||||
|
||||
## Exposing Ports
|
||||
|
||||
- You need to expose at least the ports `80` and `443` when you run the `byjg/easy-haproxy` image.
|
||||
- If you enable the HAProxy statistics you must also expose the port defined in `HAPROXY_STATS_PORT` environment variable.
|
||||
- Every port defined in `easyhaproxy.[definitions].port` also should be enabel.
|
||||
You must expose some ports on the EasyHAProxy container and in the firewall. However, you don't need to expose the other container ports because EasyHAProxy will handle that.
|
||||
|
||||
- The ports `80` and `443`.
|
||||
- If you enable the HAProxy statistics, you must also expose the port defined in `HAPROXY_STATS_PORT` environment variable.
|
||||
- Every port defined in `easyhaproxy.[definitions].port` also should be exposed.
|
||||
|
||||
e.g.
|
||||
|
||||
|
|
@ -252,11 +267,10 @@ docker run \
|
|||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
Also, you need to expose these ports in the firewall.
|
||||
|
||||
## Mapping custom .cfg files
|
||||
|
||||
Map a folder containing valid HAProxy `.cfg` files to `/etc/haproxy/conf.d`. It will be concatenated to your HAProxy CFG.
|
||||
You can concatenate valid HAProxy `.cfg` files to the dynamically generated `haproxy.cfg` by mapping the folder `/etc/haproxy/conf.d`.
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
|
|
@ -265,11 +279,24 @@ docker run \
|
|||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
## Mapping SSL certificates volumes
|
||||
|
||||
EasyHAProxy stores the certificates inside the folder `/certs/haproxy` and `/certs/letsencrypt`.
|
||||
- If you want to preserve the letsencrypt certificates between reloads, map the folder `/certs/letsencrypt` to your volume.
|
||||
- If you want to provide your certificates as a file instead of a Base64 parameter, map the folder `/certs/haproxy` to your volume, and instead of use `easyhaproxy.[definition].sslcert`, use `easyhaproxy.[definition].ssl: true`
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-v /your/certs/letsencrypt:/certs/letsencrypt \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
## Handling SSL
|
||||
|
||||
You can attach a valid SSL certificate to the request.
|
||||
|
||||
1. First Create a single PEM file including CA.
|
||||
1. First, Create a single PEM file including CA.
|
||||
|
||||
```bash
|
||||
cat example.com.crt example.com.key > single.pem
|
||||
|
|
@ -298,7 +325,7 @@ cat single.pem | base64 -w0
|
|||
## Setting Custom Errors
|
||||
|
||||
If enabled, map the volume : `/etc/haproxy/errors-custom/` to your container and put a file named `ERROR_NUMBER.http`
|
||||
where ERROR_NUMBER is the http error code (e.g. 503.http)
|
||||
where ERROR_NUMBER is the HTTP error code (e.g., `503.http`)
|
||||
|
||||
## Build
|
||||
|
||||
|
|
@ -307,5 +334,11 @@ where ERROR_NUMBER is the http error code (e.g. 503.http)
|
|||
docker build -t byjg/easy-haproxy .
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
EasyHAProxy has some limitations when there is more than one easy-haproxy container running:
|
||||
- Replicas can be out-of-sync for a few seconds because each replica will discover the pods separately.
|
||||
- Each replica will request a Letsencrypt certificate and can fail because the letsencrypt challenge can be directed to the other replica.
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@ REQUEST_CERTS=""
|
|||
RENEW_CERTS=""
|
||||
|
||||
for domain in $(cat /scripts/letsencrypt_hosts.txt); do
|
||||
if [ ! -f "/etc/haproxy/certs/$domain.pem" ]; then
|
||||
if [ ! -f "/certs/letsencrypt/$domain.pem" ]; then
|
||||
REQUEST_CERTS="$REQUES_CERTS -d $domain"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ $(find "/etc/haproxy/certs/$domain.pem" -mtime +30 -print) ]]; then
|
||||
if [[ $(find "/certs/letsencrypt/$domain.pem" -mtime +30 -print) ]]; then
|
||||
RENEW_CERTS="$RENEW_CERTS -d $domain"
|
||||
fi
|
||||
done
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ for CERTIFICATE in `find /etc/letsencrypt/live/* -type d`; do
|
|||
CERTIFICATE=`basename $CERTIFICATE`
|
||||
|
||||
# Combine certificate and private key to single file
|
||||
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /etc/haproxy/certs/$CERTIFICATE.pem
|
||||
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /certs/letsencrypt/$CERTIFICATE.pem
|
||||
done
|
||||
|
||||
# It will be checked on haproxy-reload.sh
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ else
|
|||
touch ${CONTROL_FILE}
|
||||
|
||||
if [[ "$EASYHAPROXY_DISCOVER" == "docker" ]]; then
|
||||
CONTAINERS=$(docker ps -q)
|
||||
CONTAINERS=$(docker ps -q | sort | uniq)
|
||||
LABEL_PATH=".Config.Labels"
|
||||
|
||||
for container in ${CONTAINERS}; do
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ with open("/tmp/.docker_data", 'r') as content_file:
|
|||
line_list = content_file.readlines()
|
||||
|
||||
result = {
|
||||
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False
|
||||
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
|
||||
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default")
|
||||
}
|
||||
|
||||
if os.getenv("HAPROXY_PASSWORD"):
|
||||
|
|
@ -16,6 +17,11 @@ if os.getenv("HAPROXY_PASSWORD"):
|
|||
}
|
||||
|
||||
result["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv("EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
|
||||
if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")):
|
||||
result["letsencrypt"] = {
|
||||
"email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")
|
||||
}
|
||||
|
||||
|
||||
cfg = HaproxyConfigGenerator(result)
|
||||
print(cfg.generate(line_list))
|
||||
|
|
|
|||
|
|
@ -46,15 +46,18 @@ class DockerLabelHandler:
|
|||
|
||||
|
||||
class HaproxyConfigGenerator:
|
||||
def __init__(self, mapping, ssl_cert_folder="/etc/haproxy/certs/discover"):
|
||||
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_folder = ssl_cert_folder
|
||||
self.ssl_cert_haproxy = ssl_cert_folder + "/haproxy"
|
||||
self.ssl_cert_letsecncrypt = ssl_cert_folder + "/letsencrypt"
|
||||
self.letsencrypt_hosts = []
|
||||
self.letsencrypt_email = os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL", "")
|
||||
os.makedirs(self.ssl_cert_folder, exist_ok=True)
|
||||
|
||||
|
||||
os.makedirs(self.ssl_cert_haproxy, exist_ok=True)
|
||||
os.makedirs(self.ssl_cert_letsecncrypt, exist_ok=True)
|
||||
|
||||
def generate(self, line_list = []):
|
||||
self.mapping.setdefault("easymapping", [])
|
||||
|
||||
|
|
@ -113,7 +116,7 @@ class HaproxyConfigGenerator:
|
|||
letsencrypt = self.label.get_bool(
|
||||
self.label.create([definition, "letsencrypt"]),
|
||||
False
|
||||
) and self.letsencrypt_email != ""
|
||||
) and self.mapping["letsencrypt"]["email"] != ""
|
||||
|
||||
if port not in easymapping:
|
||||
easymapping[port] = {
|
||||
|
|
@ -142,6 +145,10 @@ class HaproxyConfigGenerator:
|
|||
easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False)
|
||||
easymapping[port]["hosts"][hostname]["containers"] += ["{}:{}".format(container, ct_port)]
|
||||
easymapping[port]["hosts"][hostname]["letsencrypt"] = letsencrypt
|
||||
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
||||
self.label.create([definition, "redirect_ssl"])
|
||||
)
|
||||
|
||||
easymapping[port]["redirect"] = self.label.get_json(
|
||||
self.label.create([definition, "redirect"])
|
||||
)
|
||||
|
|
@ -157,7 +164,8 @@ class HaproxyConfigGenerator:
|
|||
}
|
||||
easymapping["443"]["hosts"][hostname] = dict(easymapping[port]["hosts"][hostname])
|
||||
easymapping["443"]["hosts"][hostname]["letsencrypt"] = False
|
||||
easymapping["443"]["ssl_cert"] = "/etc/haproxy/certs"
|
||||
easymapping["443"]["hosts"][hostname]["redirect_ssl"] = False
|
||||
easymapping["443"]["ssl_cert"] = self.ssl_cert_letsecncrypt
|
||||
self.letsencrypt_hosts.append(hostname) if hostname not in self.letsencrypt_hosts else self.letsencrypt_hosts
|
||||
|
||||
|
||||
|
|
@ -165,12 +173,14 @@ class HaproxyConfigGenerator:
|
|||
ssl_label = self.label.create([definition, "sslcert"])
|
||||
if self.label.has_label(ssl_label):
|
||||
filename = "{}/{}.pem".format(
|
||||
self.ssl_cert_folder, d[host_label]
|
||||
self.ssl_cert_haproxy, d[host_label]
|
||||
)
|
||||
easymapping[port]["ssl_cert"] = filename
|
||||
with open(filename, 'wb') as file:
|
||||
file.write(
|
||||
base64.b64decode(d[ssl_label])
|
||||
)
|
||||
if self.label.get_bool(self.label.create([definition, "ssl"])):
|
||||
easymapping[port]["ssl_cert"] = self.ssl_cert_haproxy
|
||||
|
||||
return easymapping.values()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,19 @@
|
|||
# To test:
|
||||
# curl -k -H "Host: host1.local" https://127.0.0.1/
|
||||
# curl -k -H "Host: host2.local" https://127.0.0.1/
|
||||
#
|
||||
# curl -I -H Host:host1.local http://127.0.0.1
|
||||
# HTTP/1.1 301 Moved Permanently
|
||||
# content-length: 0
|
||||
# location: https://host1.local/
|
||||
#
|
||||
# or add to /etc/hosts
|
||||
# 127.0.0.1 host1.local
|
||||
# curl -I -H Host:host2.local http://127.0.0.1
|
||||
# HTTP/1.1 200 OK
|
||||
# accept-ranges: bytes
|
||||
# content-length: 3276
|
||||
# content-type: text/html; charset=utf-8
|
||||
# last-modified: Tue, 16 Aug 2022 15:48:05 GMT
|
||||
# date: Tue, 16 Aug 2022 16:36:35 GMT
|
||||
#
|
||||
# Test SSL:
|
||||
# openssl s_client -showcerts -connect 127.0.0.1:443 --servername host1.local
|
||||
|
|
@ -16,6 +27,7 @@ services:
|
|||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
EASYHAPROXY_DISCOVER: docker
|
||||
EASYHAPROXY_SSL_MODE: "loose"
|
||||
HAPROXY_CUSTOMERRORS: "true"
|
||||
HAPROXY_USERNAME: admin
|
||||
HAPROXY_PASSWORD: password
|
||||
|
|
@ -29,7 +41,7 @@ services:
|
|||
container:
|
||||
image: byjg/static-httpserver
|
||||
labels:
|
||||
easyhaproxy.http.redirect: host1.local--https://host1.local
|
||||
easyhaproxy.http.redirect_ssl: true
|
||||
easyhaproxy.http.host: host1.local
|
||||
easyhaproxy.http.port: 80
|
||||
|
||||
|
|
@ -41,8 +53,8 @@ services:
|
|||
container2:
|
||||
image: byjg/static-httpserver
|
||||
labels:
|
||||
easyhaproxy.http.redirect: host2.local--https://host2.local
|
||||
easyhaproxy.http.host: host2.local
|
||||
easyhaproxy.http.localport: 8080
|
||||
easyhaproxy.http.port: 80
|
||||
|
||||
easyhaproxy.https.port: 443
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ easymapping:
|
|||
www.host1.local: https://host1.local
|
||||
|
||||
- port: 443
|
||||
ssl_cert: /etc/certs/host1.local.pem
|
||||
ssl_cert: /certs/haproxy/host1.local.pem
|
||||
hosts:
|
||||
host1.local:
|
||||
containers:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ services:
|
|||
image: byjg/easy-haproxy
|
||||
volumes:
|
||||
- ./config.yml:/etc/haproxy/easyconfig.yml
|
||||
- ./host1.local.pem:/etc/certs/host1.local.pem
|
||||
- ./certs:/certs
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
environment:
|
||||
EASYHAPROXY_DISCOVER: static
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{% if "ssl_cert" in o %}
|
||||
bind *:{{ o["port"] }} ssl crt /etc/haproxy/certs/discover/ alpn http/1.1 crt /etc/haproxy/certs/ alpn http/1.1
|
||||
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
|
||||
|
|
|
|||
|
|
@ -13,5 +13,9 @@
|
|||
acl is_letsencrypt_{{ host }} path_beg /.well-known/acme-challenge/
|
||||
use_backend letsencrypt_backend if is_letsencrypt_{{ host }} is_rule_{{ host }}_1 OR is_letsencrypt_{{ host }} is_rule_{{ host }}_2
|
||||
{% endif %}
|
||||
{% if o["hosts"][k]["redirect_ssl"] %}
|
||||
http-request redirect scheme https code 301 if !is_letsencrypt_{{ host }} is_rule_{{ host }}_1 OR !is_letsencrypt_{{ host }} is_rule_{{ host }}_2
|
||||
{% else %}
|
||||
use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
global
|
||||
log stdout format raw local0 info
|
||||
maxconn 2000
|
||||
tune.ssl.default-dh-param 2048
|
||||
{% if data["ssl_mode"] == "strict" %}
|
||||
{% include "ssl_strict.j2" %}
|
||||
{% elif data["ssl_mode"] == "loose" %}
|
||||
{% include "ssl_loose.j2" %}
|
||||
{% else %}
|
||||
{% include "ssl_default.j2" %}
|
||||
{% endif %}
|
||||
|
||||
# intermediate configuration
|
||||
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-dh-param-file /etc/haproxy/dhparam
|
||||
|
||||
defaults
|
||||
log global
|
||||
|
|
|
|||
12
templates/ssl_default.j2
Normal file
12
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
templates/ssl_loose.j2
Normal file
10
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
templates/ssl_strict.j2
Normal file
7
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
|
||||
|
||||
97
tests/expected/services-letsencrypt.txt
Normal file
97
tests/expected/services-letsencrypt.txt
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
global
|
||||
log stdout format raw local0 info
|
||||
maxconn 2000
|
||||
tune.ssl.default-dh-param 2048
|
||||
|
||||
# intermediate configuration
|
||||
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-dh-param-file /etc/haproxy/dhparam
|
||||
|
||||
defaults
|
||||
log global
|
||||
|
||||
timeout connect 3s
|
||||
timeout client 10s
|
||||
timeout server 10m
|
||||
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
|
||||
# acl is_proxystats hdr(host) -i some.host.com
|
||||
# default_backend srv_stats
|
||||
# use_backend srv_stats if is_proxystats
|
||||
default_backend srv_stats
|
||||
|
||||
backend srv_stats
|
||||
mode http
|
||||
server Local 127.0.0.1:1936
|
||||
|
||||
frontend 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
|
||||
81
tests/expected/services-redirect-ssl.txt
Normal file
81
tests/expected/services-redirect-ssl.txt
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
global
|
||||
log stdout format raw local0 info
|
||||
maxconn 2000
|
||||
tune.ssl.default-dh-param 2048
|
||||
|
||||
# intermediate configuration
|
||||
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-dh-param-file /etc/haproxy/dhparam
|
||||
|
||||
defaults
|
||||
log global
|
||||
|
||||
timeout connect 3s
|
||||
timeout client 10s
|
||||
timeout server 10m
|
||||
|
||||
|
||||
frontend http_in_80
|
||||
bind *:80
|
||||
mode http
|
||||
|
||||
acl is_rule_host2_local_80_1 hdr(host) -i host2.local
|
||||
acl is_rule_host2_local_80_2 hdr(host) -i host2.local:80
|
||||
use_backend srv_host2_local_80 if is_rule_host2_local_80_1 OR is_rule_host2_local_80_2
|
||||
|
||||
acl is_rule_host1_local_80_1 hdr(host) -i host1.local
|
||||
acl is_rule_host1_local_80_2 hdr(host) -i host1.local:80
|
||||
http-request redirect scheme https code 301 if !is_letsencrypt_host1_local_80 is_rule_host1_local_80_1 OR !is_letsencrypt_host1_local_80 is_rule_host1_local_80_2
|
||||
|
||||
backend srv_host2_local_80
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 b18a88da403b:8080 check weight 1
|
||||
backend srv_host1_local_80
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 78a90f5c2d8a:80 check weight 1
|
||||
|
||||
frontend http_in_443
|
||||
bind *:443 ssl crt /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 b18a88da403b:8080 check weight 1
|
||||
backend srv_host1_local_443
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 78a90f5c2d8a:8080 check weight 1
|
||||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -66,7 +66,7 @@ backend srv_node-exporter_quantum_example_org_31337
|
|||
server srv-0 my-stack_node-exporter:9100 check weight 1
|
||||
|
||||
frontend http_in_443
|
||||
bind *:443 ssl crt /etc/haproxy/certs/discover/ alpn http/1.1 crt /etc/haproxy/certs/ alpn http/1.1
|
||||
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 }
|
||||
|
|
|
|||
25
tests/expected/ssl-loose.txt
Normal file
25
tests/expected/ssl-loose.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
global
|
||||
log stdout format raw local0 info
|
||||
maxconn 2000
|
||||
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA
|
||||
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-bind-options no-sslv3 no-tls-tickets
|
||||
|
||||
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA
|
||||
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-server-options no-sslv3 no-tls-tickets
|
||||
|
||||
ssl-dh-param-file /etc/haproxy/dhparam-1024
|
||||
|
||||
|
||||
defaults
|
||||
log global
|
||||
|
||||
timeout connect 3s
|
||||
timeout client 10s
|
||||
timeout server 10m
|
||||
|
||||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
22
tests/expected/ssl-strict.txt
Normal file
22
tests/expected/ssl-strict.txt
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
global
|
||||
log stdout format raw local0 info
|
||||
maxconn 2000
|
||||
# modern configuration
|
||||
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
|
||||
|
||||
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tlsv12 no-tls-tickets
|
||||
|
||||
|
||||
defaults
|
||||
log global
|
||||
|
||||
timeout connect 3s
|
||||
timeout client 10s
|
||||
timeout server 10m
|
||||
|
||||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -76,7 +76,7 @@ backend srv_host2_com_br_80
|
|||
server srv-0 other:3000 check weight 1
|
||||
|
||||
frontend http_in_443
|
||||
bind *:443 ssl crt /etc/haproxy/certs/discover/ alpn http/1.1 crt /etc/haproxy/certs/ alpn http/1.1
|
||||
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
|
||||
|
|
|
|||
4
tests/fixtures/services-letsencrypt
vendored
Normal file
4
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"}
|
||||
3
tests/fixtures/services-redirect-ssl
vendored
Normal file
3
tests/fixtures/services-redirect-ssl
vendored
Normal file
File diff suppressed because one or more lines are too long
2
tests/fixtures/static.yml
vendored
2
tests/fixtures/static.yml
vendored
|
|
@ -19,7 +19,7 @@ easymapping:
|
|||
www.host1.com.br: http://host1.com.br
|
||||
|
||||
- port: 443
|
||||
ssl_cert: /etc/haproxy/certs/mycert.pem
|
||||
ssl_cert: /certs/haproxy/mycert.pem
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import os
|
|||
import yaml
|
||||
|
||||
CERTS_FOLDER="/tmp/certs"
|
||||
CERT_FILE="/tmp/certs/www.somehost.com.br.pem"
|
||||
CERT_FILE="/tmp/certs/haproxy/www.somehost.com.br.pem"
|
||||
LETSENCRYPT_EMAIL="some@email.com"
|
||||
|
||||
def load_fixture(file):
|
||||
|
|
@ -35,14 +35,16 @@ def test_parser_finds_services():
|
|||
line_list = load_fixture("services")
|
||||
|
||||
result = {
|
||||
"customerrors": False
|
||||
"customerrors": False,
|
||||
"letsencrypt": {
|
||||
"email": LETSENCRYPT_EMAIL
|
||||
}
|
||||
}
|
||||
|
||||
if os.path.exists(CERT_FILE):
|
||||
os.remove(CERT_FILE)
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||
cfg.letsencrypt_email = LETSENCRYPT_EMAIL
|
||||
haproxy_config = cfg.generate(line_list)
|
||||
|
||||
assert len(haproxy_config) > 0
|
||||
|
|
@ -60,14 +62,16 @@ def test_parser_finds_services_changed_label():
|
|||
|
||||
result = {
|
||||
"customerrors": False,
|
||||
"lookup_label": "haproxy"
|
||||
"lookup_label": "haproxy",
|
||||
"letsencrypt": {
|
||||
"email": LETSENCRYPT_EMAIL
|
||||
}
|
||||
}
|
||||
|
||||
if os.path.exists(CERT_FILE):
|
||||
os.remove(CERT_FILE)
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||
cfg.letsencrypt_email = LETSENCRYPT_EMAIL
|
||||
haproxy_config = cfg.generate(line_list)
|
||||
|
||||
assert len(haproxy_config) > 0
|
||||
|
|
@ -84,14 +88,16 @@ def test_parser_finds_services_raw():
|
|||
line_list = load_fixture("services")
|
||||
|
||||
result = {
|
||||
"customerrors": False
|
||||
"customerrors": False,
|
||||
"letsencrypt": {
|
||||
"email": LETSENCRYPT_EMAIL
|
||||
}
|
||||
}
|
||||
|
||||
if os.path.exists(CERT_FILE):
|
||||
os.remove(CERT_FILE)
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||
cfg.letsencrypt_email = LETSENCRYPT_EMAIL
|
||||
|
||||
parsed_object = [
|
||||
{
|
||||
|
|
@ -103,7 +109,8 @@ def test_parser_finds_services_raw():
|
|||
"containers": [
|
||||
"my-stack_agent:9001"
|
||||
],
|
||||
"letsencrypt": False
|
||||
"letsencrypt": False,
|
||||
"redirect_ssl": False
|
||||
}
|
||||
},
|
||||
"redirect":{
|
||||
|
|
@ -119,18 +126,20 @@ def test_parser_finds_services_raw():
|
|||
"containers": [
|
||||
"my-stack_cadvisor:8080"
|
||||
],
|
||||
"letsencrypt": False
|
||||
"letsencrypt": False,
|
||||
"redirect_ssl": False
|
||||
},
|
||||
"node-exporter.quantum.example.org":{
|
||||
"containers": [
|
||||
"my-stack_node-exporter:9100"
|
||||
],
|
||||
"letsencrypt": True
|
||||
"letsencrypt": True,
|
||||
"redirect_ssl": False
|
||||
}
|
||||
},
|
||||
"redirect":{
|
||||
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"mode":"http",
|
||||
|
|
@ -141,13 +150,15 @@ def test_parser_finds_services_raw():
|
|||
"containers": [
|
||||
"my-stack_node-exporter:9100"
|
||||
],
|
||||
"letsencrypt": False
|
||||
"letsencrypt": False,
|
||||
"redirect_ssl": False
|
||||
},
|
||||
"www.somehost.com.br":{
|
||||
"containers": [
|
||||
"some-service:80"
|
||||
],
|
||||
"letsencrypt": False
|
||||
"letsencrypt": False,
|
||||
"redirect_ssl": False
|
||||
}
|
||||
},
|
||||
"redirect":{
|
||||
|
|
@ -168,7 +179,8 @@ def test_parser_finds_services_raw():
|
|||
"containers": [
|
||||
"some-service:80"
|
||||
],
|
||||
"letsencrypt": False
|
||||
"letsencrypt": False,
|
||||
"redirect_ssl": False
|
||||
}
|
||||
},
|
||||
"redirect":{
|
||||
|
|
@ -177,7 +189,7 @@ def test_parser_finds_services_raw():
|
|||
"www.somehost.com":"https://www.somehost.com.br",
|
||||
"byjg.ca":"https://www.somehost.com.br",
|
||||
"www.byjg.ca":"https://www.somehost.com.br"
|
||||
}
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
|
|
@ -235,7 +247,7 @@ def test_parser_static_raw():
|
|||
},
|
||||
{
|
||||
"port": 443,
|
||||
"ssl_cert": "/etc/haproxy/certs/mycert.pem",
|
||||
"ssl_cert": "/certs/haproxy/mycert.pem",
|
||||
"hosts": {
|
||||
"host1.com.br": {
|
||||
"containers": [
|
||||
|
|
@ -317,6 +329,81 @@ def test_parser_multiple_hosts():
|
|||
assert [] == cfg.letsencrypt_hosts
|
||||
|
||||
|
||||
def test_parser_redirect_ssl():
|
||||
line_list = load_fixture("services-redirect-ssl")
|
||||
|
||||
result = {
|
||||
"customerrors": False
|
||||
}
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||
haproxy_config = cfg.generate(line_list)
|
||||
|
||||
assert len(haproxy_config) > 0
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(path + "/expected/services-redirect-ssl.txt", 'r') as expected_file:
|
||||
assert expected_file.read() == haproxy_config
|
||||
assert [] == cfg.letsencrypt_hosts
|
||||
|
||||
|
||||
def test_parser_ssl_strict():
|
||||
line_list = load_fixture("no-services")
|
||||
|
||||
result = {
|
||||
"customerrors": False,
|
||||
"ssl_mode": "strict"
|
||||
}
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||
haproxy_config = cfg.generate(line_list)
|
||||
|
||||
assert len(haproxy_config) > 0
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(path + "/expected/ssl-strict.txt", 'r') as expected_file:
|
||||
assert expected_file.read() == haproxy_config
|
||||
assert [] == cfg.letsencrypt_hosts
|
||||
|
||||
def test_parser_ssl_loose():
|
||||
line_list = load_fixture("no-services")
|
||||
|
||||
result = {
|
||||
"customerrors": False,
|
||||
"ssl_mode": "loose"
|
||||
}
|
||||
|
||||
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||
haproxy_config = cfg.generate(line_list)
|
||||
|
||||
assert len(haproxy_config) > 0
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(path + "/expected/ssl-loose.txt", 'r') as expected_file:
|
||||
assert expected_file.read() == haproxy_config
|
||||
assert [] == cfg.letsencrypt_hosts
|
||||
|
||||
def test_parser_ssl_letsencrypt():
|
||||
line_list = load_fixture("services-letsencrypt")
|
||||
|
||||
result = {
|
||||
"customerrors": True,
|
||||
"stats": {
|
||||
"username": "admin",
|
||||
"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