commit
5d03751df1
60 changed files with 1303 additions and 614 deletions
23
README.md
23
README.md
|
|
@ -19,11 +19,23 @@ EasyHAProxy can detect and configure HAProxy automatically on the following plat
|
||||||
- Docker Swarm
|
- Docker Swarm
|
||||||
- Kubernetes
|
- Kubernetes
|
||||||
|
|
||||||
|
## Who is using?
|
||||||
|
|
||||||
|
EasyHAProxy is part of some projects:
|
||||||
|
- Dokku
|
||||||
|
- MicroK8s
|
||||||
|
|
||||||
|
See detailed instructions on how to install below.
|
||||||
|
|
||||||
|
## EasyHAProxy Mission
|
||||||
|
|
||||||
|
Easy to set up and low configuration to numerous features.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
EasyHAProxy will discover the services based on the Docker Tags of the containers running on a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features:
|
EasyHAProxy will discover the services based on the Docker Tags of the containers running on a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features:
|
||||||
|
|
||||||
- Use Letsencrypt with HAProxy.
|
- Support Automatic Certificate Management Environment (ACME) protocol compatible with Let's encrypt and others CA.
|
||||||
- Set your custom SSL certificates
|
- Set your custom SSL certificates
|
||||||
- Balance traffic between multiple replicas
|
- Balance traffic between multiple replicas
|
||||||
- Set SSL with three different levels of validations and according to the most recent definitions.
|
- Set SSL with three different levels of validations and according to the most recent definitions.
|
||||||
|
|
@ -34,7 +46,7 @@ EasyHAProxy will discover the services based on the Docker Tags of the container
|
||||||
|
|
||||||
Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file.
|
Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file.
|
||||||
|
|
||||||
## How Does It Works?
|
## How Does It Work?
|
||||||
|
|
||||||
You don't need to change your current infrastructure and don't need to learn the HAProxy configuration.
|
You don't need to change your current infrastructure and don't need to learn the HAProxy configuration.
|
||||||
|
|
||||||
|
|
@ -59,6 +71,13 @@ Or you can install using tools:
|
||||||
[](docs/microk8s.md)
|
[](docs/microk8s.md)
|
||||||
[](docs/dokku.md)
|
[](docs/dokku.md)
|
||||||
|
|
||||||
|
## Special Topics
|
||||||
|
|
||||||
|
If you already set up the EasyHAProxy, is time to go deeper:
|
||||||
|
|
||||||
|
- [Custom SSL](docs/ssl.md)
|
||||||
|
- [Automatic Certificate Issuing](docs/acme.md) (e.g. Letsencrypt)
|
||||||
|
|
||||||
## See EasyHAProxy in action
|
## See EasyHAProxy in action
|
||||||
|
|
||||||
Click on the image to see the videos (use HD for better visualization)
|
Click on the image to see the videos (use HD for better visualization)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:3.16
|
FROM alpine:3.18
|
||||||
|
|
||||||
ARG RELEASE_VERSION_ARG
|
ARG RELEASE_VERSION_ARG
|
||||||
|
|
||||||
|
|
@ -11,7 +11,6 @@ COPY src/ /scripts/
|
||||||
COPY build/assets /
|
COPY build/assets /
|
||||||
|
|
||||||
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml certbot openssl \
|
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml certbot openssl \
|
||||||
&& ln -s /usr/bin/python3 /usr/bin/python \
|
|
||||||
&& pip3 install --upgrade pip \
|
&& pip3 install --upgrade pip \
|
||||||
&& pip install -r requirements.txt \
|
&& pip install -r requirements.txt \
|
||||||
&& pytest -s -vv tests/ \
|
&& pytest -s -vv tests/ \
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ services:
|
||||||
image: byjg/easy-haproxy:4.3.1-rc1
|
image: byjg/easy-haproxy:4.3.1-rc1
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- certs_letsencrypt:/certs/letsencrypt
|
- certs_certbot:/certs/certbot
|
||||||
- certs_haproxy:/certs/haproxy
|
- certs_haproxy:/certs/haproxy
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
EASYHAPROXY_DISCOVER: docker
|
EASYHAPROXY_DISCOVER: docker
|
||||||
EASYHAPROXY_LABEL_PREFIX: easyhaproxy
|
EASYHAPROXY_LABEL_PREFIX: easyhaproxy
|
||||||
EASYHAPROXY_LETSENCRYPT_EMAIL: changeme@example.org
|
EASYHAPROXY_CERTBOT_EMAIL: changeme@example.org
|
||||||
EASYHAPROXY_SSL_MODE: "default"
|
EASYHAPROXY_SSL_MODE: "default"
|
||||||
HAPROXY_CUSTOMERRORS: "true"
|
HAPROXY_CUSTOMERRORS: "true"
|
||||||
HAPROXY_USERNAME: admin
|
HAPROXY_USERNAME: admin
|
||||||
|
|
@ -27,7 +27,7 @@ services:
|
||||||
- easyhaproxy
|
- easyhaproxy
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
certs_letsencrypt:
|
certs_certbot:
|
||||||
external: true
|
external: true
|
||||||
certs_haproxy:
|
certs_haproxy:
|
||||||
external: true
|
external: true
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
ASSETS_DIR="$(dirname "${BASH_SOURCE[0]}")"/../../build/assets/certs/haproxy
|
ASSETS_DIR="$(dirname "${BASH_SOURCE[0]}")"/../../build/assets/certs/haproxy
|
||||||
|
|
||||||
docker network create easyhaproxy
|
docker network create easyhaproxy
|
||||||
docker volume create certs_letsencrypt
|
docker volume create certs_certbot
|
||||||
docker volume create certs_haproxy
|
docker volume create certs_haproxy
|
||||||
|
|
||||||
docker run -d --rm --name easyhaproxy_install -v certs_haproxy:/certs alpine tail -f /dev/null
|
docker run -d --rm --name easyhaproxy_install -v certs_haproxy:/certs alpine tail -f /dev/null
|
||||||
|
|
|
||||||
99
docs/acme.md
Normal file
99
docs/acme.md
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
# SSL - Automatic Certificate Management Environment (ACME)
|
||||||
|
|
||||||
|
The Automatic Certificate Management Environment (ACME) protocol
|
||||||
|
allow automating interactions between certificate authorities and their users' servers,
|
||||||
|
allowing the automated deployment of public key infrastructure.
|
||||||
|
|
||||||
|
Most of the issuers offers Automatic Issuing free of cost.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
To enable the ACME protocol we need to enable Certbot in EasyHAProxy by setting up to 4 environment variables:
|
||||||
|
|
||||||
|
| Environment Variable | Required? | Description |
|
||||||
|
|----------------------------------|-----------|----------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| EASYHAPROXY_CERTBOT_EMAIL | YES | Your email in the certificate authority. |
|
||||||
|
| EASYHAPROXY_CERTBOT_AUTOCONFIG | - | Will use pre-sets for your Certificate Authority (CA). See table below. |
|
||||||
|
| EASYHAPROXY_CERTBOT_SERVER | - | The ACME Endpoint of your certificate authority. If you use AUTOCONFIG, it is set automatically. See table below. |
|
||||||
|
| EASYHAPROXY_CERTBOT_EAB_KID | - | External Account Binding (EAB) Key Identifier (KID) provided by your certificate authority. Some CA require it. See table below. |
|
||||||
|
| EASYHAPROXY_CERTBOT_EAB_HMAC_KEY | - | External Account Binding (EAB) HMAC Key provided by your certificate authority. Some CA require it. See table below. |
|
||||||
|
| EASYHAPROXY_CERTBOT_RETRY_COUNT | - | Wait 'n' requests before retrying issue invalid requests. Default 60. |
|
||||||
|
|
||||||
|
## Auto Config Certificate Authority (CA)
|
||||||
|
|
||||||
|
Here are detailed instructions per Certificate Authority (CA). If anyone is missing, please let's know.
|
||||||
|
|
||||||
|
| CA | Auto Config | Free? | Account Required? | EAB KID? | EAB HMAC Key? | More Info |
|
||||||
|
|----------------------|------------------|-------|--------------------|----------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| Let's Encrypt | - | Yes | No | No | No | - |
|
||||||
|
| Let's Encrypt (Test) | letsencrypt_test | Yes | No | No | No | - |
|
||||||
|
| ZeroSSL | zerossl | Yes | No | No | No | [Link](https://zerossl.com/documentation/acme/) |
|
||||||
|
| BuyPass | buypass | Yes | No | No | No | [Link](https://community.buypass.com/t/63d4ay/buypass-go-ssl-endpoints-updated-14-05-2020) |
|
||||||
|
| BuyPass (test) | buypass_test | Yes | No | No | No | [Link](https://community.buypass.com/t/63d4ay/buypass-go-ssl-endpoints-updated-14-05-2020) |
|
||||||
|
| Google | google | Yes | Yes | Yes | Yes | [Link](https://cloud.google.com/blog/products/identity-security/automate-public-certificate-lifecycle-management-via--acme-client-api) |
|
||||||
|
| Google Test | google_test | Yes | Yes | Yes | Yes | [Link](https://cloud.google.com/blog/products/identity-security/automate-public-certificate-lifecycle-management-via--acme-client-api) |
|
||||||
|
| SSLCOM RCA | sslcom_rca | Trial | EAB Keys by email. | Yes | Yes | [Link](https://www.ssl.com/blogs/sslcom-supports-acme-protocol-ssl-tls-certificate-automation/) |
|
||||||
|
| SSLCOM ECC | sslcom_ecc | Trial | EAB Keys by email. | Yes | Yes | [Link](https://www.ssl.com/blogs/sslcom-supports-acme-protocol-ssl-tls-certificate-automation/) |
|
||||||
|
| Digicert | - | No | Yes | Yes | Yes | [Link](https://docs.digicert.com/en/certcentral/certificate-tools/certificate-lifecycle-automation-guides/use-a-third-party-acme-client-for-host-automations.html) |
|
||||||
|
| Entrust | - | No | Yes | Yes | Yes | [Link](https://www.entrust.com/knowledgebase/ssl/how-to-use-acme-to-install-ssl-tls-certificates-in-entrust-certificate-services-apache) |
|
||||||
|
| Sectigo | - | No | Yes | Yes | Yes | [Link](https://www.sectigo.com/resource-library/sectigos-acme-automation) |
|
||||||
|
|
||||||
|
This configuration is global. After set up ACME properly, is necessary enable for each domain the certificate request.
|
||||||
|
|
||||||
|
To do that add the label: `easyhaproxy.<definition>.certbot=true`. See the method of installation you are using to learn how to set up properly.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
### Setting up EasyHAProxy
|
||||||
|
|
||||||
|
Run the EasyHAProxy container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run \
|
||||||
|
... \
|
||||||
|
-e EASYHAPROXY_CERTBOT_AUTOCONFIG=zerossl \
|
||||||
|
-e EASYHAPROXY_CERTBOT_EMAIL=john@doe.com \
|
||||||
|
-p 80:80 \
|
||||||
|
-p 443:443 \
|
||||||
|
-v /path/to/guest/certbot/certs:/certs/certbot \
|
||||||
|
... \
|
||||||
|
byjg/easy-haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- The `EASYHAPROXY_CERTBOT_AUTOCONFIG` is not required for Let's Encrypt. In this example, the certificate will be issued by ZeroSSL.
|
||||||
|
- If you don't setup `EASYHAPROXY_CERTBOT_EMAIL` environment variable, EasyHAProxy will fail silently and **will not request** a certificate.
|
||||||
|
- The ports 80 and 443 needs to accessible through the internet as [Let's Encrypt requirement](https://letsencrypt.org/docs/allow-port-80/)
|
||||||
|
|
||||||
|
In order to avoid several certificate issuing,
|
||||||
|
**It is required you to persist the container folder `/certs/certbot` outside the container.**
|
||||||
|
You cannot delete or change it contents.
|
||||||
|
If you do not persist, or change/delete the contents, Issue a certificate might not work properly and hit rate limit.
|
||||||
|
|
||||||
|
If you are using Let's Encrypt, be aware of it rate limits:
|
||||||
|
|
||||||
|
- https://letsencrypt.org/docs/duplicate-certificate-limit/
|
||||||
|
- https://letsencrypt.org/docs/rate-limits/
|
||||||
|
|
||||||
|
|
||||||
|
## Setting up your container to use the ACME CA
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run \
|
||||||
|
... \
|
||||||
|
--label easyhaproxy.express.port=80 \
|
||||||
|
--label easyhaproxy.express.localport=3000 \
|
||||||
|
--label easyhaproxy.express.host=example.org \
|
||||||
|
--label easyhaproxy.express.certbot=true \
|
||||||
|
... \
|
||||||
|
some/myimage
|
||||||
|
```
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
- Your container **must** listen to port 80. The CA will not issue the certificate if `easyhaproxy.<definition>.port` is in another port, and EasyHAProxy will fail silently.
|
||||||
|
- You cannot set port 443 for the container with the Letsencrypt because EasyHAProxy will create this port automatically once the certificate is issued.
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -2,19 +2,20 @@
|
||||||
|
|
||||||
## Container (Docker or Swarm) labels
|
## Container (Docker or Swarm) labels
|
||||||
|
|
||||||
| Tag | Description | Default | Example |
|
| Tag | Description | Default | Example |
|
||||||
|---------------------------------------|-------------------------------------------------------------------------------------------------------|----------------|--------------|
|
|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|--------------|------------------------------------------------------------|
|
||||||
| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | **required** | somehost.com OR host1.com,host2.com |
|
| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | **required** | somehost.com OR host1.com,host2.com |
|
||||||
| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. | http | http or tcp |
|
| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. | http | http or tcp |
|
||||||
| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. | 80 | 3000 |
|
| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. | 80 | 3000 |
|
||||||
| easyhaproxy.[definition].localport | (Optional) Port container is listening. | 80 | 8080 |
|
| easyhaproxy.[definition].localport | (Optional) Port container is listening. | 80 | 8080 |
|
||||||
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | *empty* | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
||||||
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `letsencrypt` is enabled. | *empty* | base64 cert + key |
|
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `certbot` is enabled. | *empty* | base64 cert + key |
|
||||||
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | false | true or false |
|
||||||
| easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
| easyhaproxy.[definition].ssl-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` | *empty* | ssl |
|
||||||
| easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with `sslcert` parameter. | false | true OR false |
|
| easyhaproxy.[definition].certbot | (Optional) Generate certificate with certbot. Do not use with `sslcert` parameter. More info [here](acme.md). | false | true OR false |
|
||||||
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | false | true OR false |
|
||||||
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `letsencrypt` parameters | false | true OR false |
|
| easyhaproxy.[definition].clone_to_ssl | (Optional) It copies the configuration to HTTPS(443) and disable SSL from the current config. **Do not use* this with `ssl` or `certbot` parameters | false | true OR false |
|
||||||
|
| easyhaproxy.[definition].balance | (Optional) HAProxy balance algorithm. See [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/1.8/configuration.html#4.2-balance) | roundrobin | roundrobin, source, uri, url_param, hdr, rdp-cookie, leastconn, first, static-rr, rdp-cookie, hdr_dom, map-based |
|
||||||
|
|
||||||
The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations.
|
The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
# Docker environment variables
|
|
||||||
|
|
||||||
| Environment Variable | Description | Default |
|
|
||||||
|---------------------------------|-------------------------------------------------------------------------------------------------|------------------|
|
|
||||||
| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** |
|
|
||||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` |
|
|
||||||
| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt | *empty* |
|
|
||||||
| EASYHAPROXY_LETSENCRYPT_SERVER | (Optional) Can be `staging` or 'schema://domain.tld'. If set, will try to connect to the Letsencrypt test server | *empty* |
|
|
||||||
| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default`|
|
|
||||||
| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. | 10 |
|
|
||||||
| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
|
||||||
| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
|
||||||
| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
|
||||||
| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` |
|
|
||||||
| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* |
|
|
||||||
| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` |
|
|
||||||
| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
|
|
@ -4,8 +4,12 @@
|
||||||
|
|
||||||
This method will use a docker standalone installation to discover the containers and configure the HAProxy.
|
This method will use a docker standalone installation to discover the containers and configure the HAProxy.
|
||||||
|
|
||||||
The only requirement is that containers and EasyHAProxy must be in the same docker network.
|
You cannot mix docker containers with swarm containers.
|
||||||
If not, EasyHAProxy will connect the container with the EasyHAProxy network.
|
|
||||||
|
The only request is that containers and EasyHAProxy must be in the same docker network.
|
||||||
|
If you don't add to your services the same network EasyHAProxy is connected to, EasyHAProxy will attach it network to your container.
|
||||||
|
|
||||||
|
Also, it is highly recommended you create a network external to EasyHAProxy.
|
||||||
|
|
||||||
e.g.:
|
e.g.:
|
||||||
|
|
||||||
|
|
@ -51,12 +55,11 @@ Please follow the [docker label configuration](container-labels.md) to see other
|
||||||
|
|
||||||
## Setup the EasyHAProxy container
|
## Setup the EasyHAProxy container
|
||||||
|
|
||||||
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [docker container environment](docker-environment.md)
|
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [environment variable guide](environment-variable.md)
|
||||||
|
|
||||||
|
## Setup certificates with ACME (e.g. Letsencrypt)
|
||||||
|
|
||||||
## Setup certificates with Letsencrypt
|
Follow [this link](acme.md)
|
||||||
|
|
||||||
Follow [this link](letsencrypt.md)
|
|
||||||
|
|
||||||
## Setup your own certificates
|
## Setup your own certificates
|
||||||
|
|
||||||
|
|
|
||||||
20
docs/environment-variable.md
Normal file
20
docs/environment-variable.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Docker environment variables
|
||||||
|
|
||||||
|
| Environment Variable | Description | Default |
|
||||||
|
|----------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
||||||
|
| EASYHAPROXY_DISCOVER | How the services will be discovered to create `haproxy.cfg`: `static`, `docker`, `swarm` or `kubernetes` | **required** |
|
||||||
|
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. | `easyhaproxy` |
|
||||||
|
| EASYHAPROXY_CERTBOT_* | (Optional) Enable Let's Encrypt or any other ACME certificate. See more: [acme](acme.md) | *empty* |
|
||||||
|
| EASYHAPROXY_SSL_MODE | (Optional) `strict` supports only the most recent TLS version; `default` good SSL integration with recent browsers; `loose` supports all old SSL protocols for old browsers (not recommended). | `default` |
|
||||||
|
| EASYHAPROXY_REFRESH_CONF | (Optional) Check for new containers/services every N seconds. | 10 |
|
||||||
|
| EASYHAPROXY_LOG_LEVEL | (Optional) The log level for EasyHAproxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
||||||
|
| CERTBOT_LOG_LEVEL | (Optional) The log level for Certbot messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
||||||
|
| HAPROXY_LOG_LEVEL | (Optional) The log level for HAProxy messages. Available: TRACE,DEBUG,INFO,WARN,ERROR,FATAL | DEBUG |
|
||||||
|
| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. | `admin` |
|
||||||
|
| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password | *empty* |
|
||||||
|
| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. If set to `false`, disable statistics | `1936` |
|
||||||
|
| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. | `false` |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -51,7 +51,7 @@ easyhaproxy:
|
||||||
certbot: DEBUG
|
certbot: DEBUG
|
||||||
easyhaproxy: DEBUG
|
easyhaproxy: DEBUG
|
||||||
haproxy: DEBUG
|
haproxy: DEBUG
|
||||||
letsencrypt:
|
certbot:
|
||||||
email: ""
|
email: ""
|
||||||
|
|
||||||
service:
|
service:
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ kubectl apply -f \
|
||||||
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/4.3.1-rc1/deploy/kubernetes/easyhaproxy-daemonset.yml
|
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/4.3.1-rc1/deploy/kubernetes/easyhaproxy-daemonset.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
If necessary, you can configure environment variables. To get a list of the variables, please follow the [docker container environment](docker-environment.md)
|
If necessary, you can configure environment variables. To get a list of the variables, please follow the [environment variable guide](environment-variable.md)
|
||||||
|
|
||||||
## Running containers
|
## Running containers
|
||||||
|
|
||||||
|
|
@ -71,7 +71,7 @@ Once the container is running, EasyHAProxy will detect automatically and start t
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
You don't need to expose any port in your container.
|
||||||
|
|
||||||
Caveats:
|
Notes:
|
||||||
|
|
||||||
- At this point, the implementation doesn't support all ingress properties or wildcard domains.
|
- At this point, the implementation doesn't support all ingress properties or wildcard domains.
|
||||||
- The ingress will publish the ports 80 and 443, plus 1936 if stats are enabled.
|
- The ingress will publish the ports 80 and 443, plus 1936 if stats are enabled.
|
||||||
|
|
@ -79,33 +79,35 @@ Caveats:
|
||||||
|
|
||||||
## Kubernetes annotations
|
## Kubernetes annotations
|
||||||
|
|
||||||
| annotation | Description | Default | Example |
|
| annotation | Description | Default | Example |
|
||||||
|-----------------------------|-----------------------------------------------------------------------------------------|--------------|--------------|
|
|----------------------------------|-------------------------------------------------------------------------------------|--------------|---------------------------------------|
|
||||||
| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress
|
| kubernetes.io/ingress.class | (required) Activate EasyHAProxy. | **required** | easyhaproxy-ingress |
|
||||||
| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false
|
| easyhaproxy.redirect_ssl | (optional) Boolean. Force redirect all endpoints to HTTPS. | false | true or false |
|
||||||
| easyhaproxy.letsencrypt | (optional) Boolean. It will request letsencrypt certificates for the ingresses domains. | false | true or false
|
| easyhaproxy.certbot | (optional) Boolean. It will request certbot certificates for the ingresses domains. | false | true or false |
|
||||||
| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"}
|
| easyhaproxy.redirect | (optional) JSON. Key pair with a domain and its destination. | *empty* | {"domain":"redirect_url"} |
|
||||||
| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp
|
| easyhaproxy.mode | (optional) Set the HTTP mode for that connection. | http | http or tcp |
|
||||||
| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp
|
| easyhaproxy.listen_port | (optional) Set the an additional port for that ingress | http | http or tcp |
|
||||||
|
|
||||||
**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration.
|
**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration.
|
||||||
|
|
||||||
## Letsencrypt
|
## Certbot / ACME / Letsencrypt
|
||||||
|
|
||||||
It is necessary add the annotation `easyhaproxy.letsencrypt` to the ingress configuration:
|
It is necessary add the annotation `easyhaproxy.certbot` to the ingress configuration:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||||
easyhaproxy.letsencrypt: 'true'
|
easyhaproxy.certbot: 'true'
|
||||||
name: example-ingress
|
name: example-ingress
|
||||||
namespace: example
|
namespace: example
|
||||||
spec:
|
spec:
|
||||||
....
|
....
|
||||||
```
|
```
|
||||||
|
|
||||||
|
More info [here](acme.md).
|
||||||
|
|
||||||
Make sure your cluster is accessible both through ports 80 and 443.
|
Make sure your cluster is accessible both through ports 80 and 443.
|
||||||
|
|
||||||
## Custom SSL Certificates
|
## Custom SSL Certificates
|
||||||
|
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# Letsencrypt
|
|
||||||
|
|
||||||
EasyHAProxy can issue a letsencrypt certificate. Follow the steps below:
|
|
||||||
|
|
||||||
Run the EasyHAProxy:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run \
|
|
||||||
-e EASYHAPROXY_LETSENCRYPT_EMAIL=john@doe.com
|
|
||||||
.... \
|
|
||||||
byjg/easy-haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
Run your container:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run \
|
|
||||||
--label easyhaproxy.express.port=80 \
|
|
||||||
--label easyhaproxy.express.localport=3000 \
|
|
||||||
--label easyhaproxy.express.host=example.org \
|
|
||||||
--label easyhaproxy.express.letsencrypt=true \
|
|
||||||
.... \
|
|
||||||
some/myimage
|
|
||||||
```
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- Your container **must** listen to port 80. Letsencrypt will not issue the certificate if `easyhaproxy.express.port` is in another port, and EasyHAProxy will fail silently.
|
|
||||||
- You cannot set port 443 for the container with the Letsencrypt because EasyHAProxy will create this port automatically once the certificate is issued.
|
|
||||||
- `EASYHAPROXY_LETSENCRYPT_EMAIL` environment variable is required to be set. If you don't set it up, EasyHAProxy **will not request** a certificate.
|
|
||||||
|
|
||||||
Be aware of Letsencrypt issue limits - https://letsencrypt.org/docs/duplicate-certificate-limit/ and https://letsencrypt.org/docs/rate-limits/
|
|
||||||
|
|
||||||
## Persist your Letsencrypt certificates
|
|
||||||
|
|
||||||
It is a good idea to store the letsencrypt certificate in persistent storage because of the limit on how many certificates can be issued for the same domain in a period.
|
|
||||||
|
|
||||||
To do this, map the folder `/certs/letsencrypt` to a docker volume.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker volume create certs_letsencrypt
|
|
||||||
|
|
||||||
docker run \
|
|
||||||
/* other parameters */
|
|
||||||
-v certs_letsencrypt:/certs/letsencrypt \
|
|
||||||
-d byjg/easy-haproxy
|
|
||||||
```
|
|
||||||
|
|
||||||
----
|
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
|
||||||
19
docs/ssl.md
19
docs/ssl.md
|
|
@ -1,12 +1,15 @@
|
||||||
# Setup custom certificates
|
# Setup custom certificates
|
||||||
|
|
||||||
You can use your certificates with EasyHAProxy.
|
You can use your own certificates with EasyHAProxy. You just need to let EasyHAProxy know that certificate.
|
||||||
|
|
||||||
There are two ways to do that.
|
There are two ways to do that.
|
||||||
|
|
||||||
|
- [Setup certificate as a label definition in docker container](#setup-certificate-as-a-label-definition-in-docker-container)
|
||||||
|
- [Map the certificate as a docker volume](#map-the-certificate-as-a-docker-volume)
|
||||||
|
|
||||||
## Setup certificate as a label definition in docker container
|
## Setup certificate as a label definition in docker container
|
||||||
|
|
||||||
### Create a single PEM from the certificate and key.
|
1. Create a single PEM from the certificate and key.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat example.com.crt example.com.key > single.pem
|
cat example.com.crt example.com.key > single.pem
|
||||||
|
|
@ -24,21 +27,21 @@ MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
```
|
```
|
||||||
|
|
||||||
### Convert the `single.pem` to BASE64 in a single line:
|
2. Convert the `single.pem` to BASE64 in a single line:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat single.pem | base64 -w0
|
cat single.pem | base64 -w0
|
||||||
```
|
```
|
||||||
|
|
||||||
### Map the BASE64 to your container
|
3. Define a label in yout container
|
||||||
|
|
||||||
Add the Base64 string you generated before to the label `easyhaproxy.[definition].sslcert`
|
Add the Base64 string you generated before to the label `easyhaproxy.[definition].sslcert`
|
||||||
|
|
||||||
## Map the certificate as a docker volume
|
## Map the certificate as a docker volume
|
||||||
|
|
||||||
EasyHAProxy stores the certificates inside the folder `/certs/haproxy`.
|
EasyHAProxy stores the certificates inside the container folder `/certs/haproxy`.
|
||||||
|
|
||||||
### Run EasyHAProxy with the volume for the certificates:
|
1. Run EasyHAProxy with the volume for the certificates:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker volume create certs_haproxy
|
docker volume create certs_haproxy
|
||||||
|
|
@ -49,7 +52,7 @@ docker run \
|
||||||
-d byjg/easy-haproxy
|
-d byjg/easy-haproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
### Create a single PEM from the certificate and the key.
|
2. Create a single PEM from the certificate and the key.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cat example.com.crt example.com.key > single.pem
|
cat example.com.crt example.com.key > single.pem
|
||||||
|
|
@ -67,7 +70,7 @@ MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
|
||||||
-----END PRIVATE KEY-----
|
-----END PRIVATE KEY-----
|
||||||
```
|
```
|
||||||
|
|
||||||
### Copy this certificate to EasyHAProxy volume
|
3. Copy this certificate to EasyHAProxy volume
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker cp single.pem easyhaproxy:/certs/haproxy
|
docker cp single.pem easyhaproxy:/certs/haproxy
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,10 @@ customerrors: true # Optional (default false)
|
||||||
|
|
||||||
ssl_mode: default
|
ssl_mode: default
|
||||||
|
|
||||||
letsencrypt: {
|
logLevel:
|
||||||
|
haproxy: INFO
|
||||||
|
|
||||||
|
certbot: {
|
||||||
"email": "acme@example.org"
|
"email": "acme@example.org"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -30,7 +33,7 @@ easymapping:
|
||||||
host1.com.br:
|
host1.com.br:
|
||||||
containers:
|
containers:
|
||||||
- container:5000
|
- container:5000
|
||||||
letsencrypt: true
|
certbot: true
|
||||||
redirect_ssl: true
|
redirect_ssl: true
|
||||||
host2.com.br:
|
host2.com.br:
|
||||||
containers:
|
containers:
|
||||||
|
|
@ -69,7 +72,7 @@ docker run -d \
|
||||||
byjg/easy-haproxy
|
byjg/easy-haproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
You can find other informations on [docker label configuration](container-labels.md) and [docker container environment](docker-environment.md)
|
You can find other informations on [docker label configuration](container-labels.md) and [environment variable guide](environment-variable.md)
|
||||||
|
|
||||||
## Yaml Definition
|
## Yaml Definition
|
||||||
|
|
||||||
|
|
@ -83,9 +86,18 @@ customerrors: true # Optional (default false)
|
||||||
|
|
||||||
ssl_mode: default # Optional
|
ssl_mode: default # Optional
|
||||||
|
|
||||||
letsencrypt: { # Optional. If you enable `letsencrypt` will need to setu0p this,
|
logLevel:
|
||||||
# otherwise the certificate will be issued
|
certbot: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||||
"email": "acme@example.org"
|
easyhaproxy: DEBUG # Optional (default: DEBUG). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||||
|
haproxy: INFO # Optional (default: INFO). Can be: TRACE,DEBUG,INFO,WARN,ERROR,FATAL
|
||||||
|
|
||||||
|
certbot:
|
||||||
|
email: "acme@example.org" # If email is defined enable ACME/Certbot
|
||||||
|
autoconfig: "" # If empty use letsencrypt, otherwise try to set the CA defined.
|
||||||
|
eab_hmac_key: "" # If required by the CA, set here.
|
||||||
|
eab_kid: "" # If required by the CA, set here.
|
||||||
|
server: False # If empty/False uses Letsencrypt, otherwise the CA Endpoint defined here
|
||||||
|
retry_count: 60 # If the certificate reaches the Rate Limit, try again after 'n' iterations.
|
||||||
}
|
}
|
||||||
|
|
||||||
easymapping:
|
easymapping:
|
||||||
|
|
@ -95,7 +107,7 @@ easymapping:
|
||||||
host1.com.br: # Hostname
|
host1.com.br: # Hostname
|
||||||
containers:
|
containers:
|
||||||
- container:5000 # Endpoints of the hostname above (ip, dns, container, etc)
|
- container:5000 # Endpoints of the hostname above (ip, dns, container, etc)
|
||||||
letsencrypt: true # Optional. it will request a letsencrypt certiticate
|
certbot: true # Optional. it will request a certbot certificate. Needs certbot.email set.
|
||||||
redirect_ssl: true # Optional. It will redirect this site to it SSL.
|
redirect_ssl: true # Optional. It will redirect this site to it SSL.
|
||||||
ssl: true # Optional. Inform this port will listen to SSL, instead of HTTP
|
ssl: true # Optional. Inform this port will listen to SSL, instead of HTTP
|
||||||
clone_to_ssl: true # Optional. Default False. You clone these hosts to its equivalent SSL.
|
clone_to_ssl: true # Optional. Default False. You clone these hosts to its equivalent SSL.
|
||||||
|
|
@ -103,7 +115,8 @@ easymapping:
|
||||||
www.host1.com.br: http://host1.com.br
|
www.host1.com.br: http://host1.com.br
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note**: The only way to pass SSL certificates is to map the certificates to EasyHAProxy as a docker volume. Refer to the [SSL documentation](ssl.md) to learn how to do it.
|
**Note**: The only way to pass SSL certificates in the static configuration file is to map the certificates
|
||||||
|
to EasyHAProxy as a docker volume. Refer to the [SSL documentation](ssl.md) to learn how to do it.
|
||||||
|
|
||||||
----
|
----
|
||||||
[Open source ByJG](http://opensource.byjg.com)
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,17 @@
|
||||||
This method will use a docker swarm installation to discover the containers and configure the HAProxy.
|
This method will use a docker swarm installation to discover the containers and configure the HAProxy.
|
||||||
The advantage of this method is that you can discover containers in other nodes from the cluster.
|
The advantage of this method is that you can discover containers in other nodes from the cluster.
|
||||||
|
|
||||||
The only requirement is that containers and EasyHAProxy must be in the same docker swarm network.
|
You cannot mix docker containers with swarm containers.
|
||||||
If not, EasyHAProxy will connect the service with the EasyHAProxy service network.
|
|
||||||
|
The only request is that containers and EasyHAProxy must be in the same docker swarm network.
|
||||||
|
If you don't add to your services the same network EasyHAProxy is connected to, EasyHAProxy will attach it network to your container.
|
||||||
|
|
||||||
|
Also, it is highly recommended you create a network external to EasyHAProxy.
|
||||||
|
|
||||||
e.g.:
|
e.g.:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker network create -d overlay easyhaproxy
|
docker network create -d overlay --attachable easyhaproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
And then deploy the EasyHAProxy stack:
|
And then deploy the EasyHAProxy stack:
|
||||||
|
|
@ -79,8 +83,6 @@ networks:
|
||||||
external: true
|
external: true
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: The services to be discovered **don't need** to be in the same network as EasyHAProxy is.
|
|
||||||
|
|
||||||
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container.
|
Once the container is running, EasyHAProxy will detect automatically and start to redirect all traffic from `example.org:80` to your container.
|
||||||
|
|
||||||
You don't need to expose any port in your container.
|
You don't need to expose any port in your container.
|
||||||
|
|
@ -89,7 +91,7 @@ Please follow the [docker label configuration](container-labels.md) to see other
|
||||||
|
|
||||||
## Setup the EasyHAProxy container
|
## Setup the EasyHAProxy container
|
||||||
|
|
||||||
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [docker container environment](docker-environment.md)
|
You can configure the behavior of the EasyHAProxy by setup specific environment variables. To get a list of the variables, please follow the [environment variable guide](environment-variable.md)
|
||||||
|
|
||||||
## More information
|
## More information
|
||||||
|
|
||||||
|
|
|
||||||
14
docs/volumes.md
Normal file
14
docs/volumes.md
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Volumes
|
||||||
|
|
||||||
|
You can map the following volumes:
|
||||||
|
|
||||||
|
| Volume | Description |
|
||||||
|
|-----------------------------|-------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| /etc/haproxy/static/ | The folder that will contain the [config.yml](static.md) file for static configuration |
|
||||||
|
| /certs/haproxy/ | The folder that will contain the certificates (`PEM`) for the [SSL](ssl.md) |
|
||||||
|
| /certs/certbot/ | The folder that will contain the certificates (`PEM`) processed by Certbot (e.g. Let's Encrypt). More info: [acme](acme.md). |
|
||||||
|
| /etc/haproxy/conf.d/ | The folder that will contain the [custom configuration](other.md) files. |
|
||||||
|
| /etc/haproxy/errors-custom/ | The folder that will contain the [custom error](other.md) html files. |
|
||||||
|
|
||||||
|
----
|
||||||
|
[Open source ByJG](http://opensource.byjg.com)
|
||||||
|
|
@ -8,7 +8,7 @@ services:
|
||||||
easyhaproxy.http.host: test.xpto.us
|
easyhaproxy.http.host: test.xpto.us
|
||||||
easyhaproxy.http.port: 80
|
easyhaproxy.http.port: 80
|
||||||
easyhaproxy.http.localport: 8080
|
easyhaproxy.http.localport: 8080
|
||||||
easyhaproxy.http.letsencrypt: true
|
easyhaproxy.http.certbot: true
|
||||||
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# docker volume create certs_letsencrypt
|
# docker volume create certs_certbot
|
||||||
# docker volume create certs_haproxy
|
# docker volume create certs_haproxy
|
||||||
# docker volume create portainer_data
|
# docker volume create portainer_data
|
||||||
# docker network create easyhaproxy
|
# docker network create easyhaproxy
|
||||||
|
|
@ -11,13 +11,13 @@ services:
|
||||||
image: byjg/easy-haproxy
|
image: byjg/easy-haproxy
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
- certs_letsencrypt:/certs/letsencrypt
|
- certs_certbot:/certs/certbot
|
||||||
# - certs_haproxy:/certs/haproxy
|
# - certs_haproxy:/certs/haproxy
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
EASYHAPROXY_DISCOVER: docker
|
EASYHAPROXY_DISCOVER: docker
|
||||||
EASYHAPROXY_LABEL_PREFIX: easyhaproxy
|
EASYHAPROXY_LABEL_PREFIX: easyhaproxy
|
||||||
EASYHAPROXY_LETSENCRYPT_EMAIL: changeme@example.org
|
EASYHAPROXY_CERTBOT_EMAIL: changeme@example.org
|
||||||
EASYHAPROXY_SSL_MODE: "default"
|
EASYHAPROXY_SSL_MODE: "default"
|
||||||
HAPROXY_CUSTOMERRORS: "true"
|
HAPROXY_CUSTOMERRORS: "true"
|
||||||
HAPROXY_USERNAME: admin
|
HAPROXY_USERNAME: admin
|
||||||
|
|
@ -32,18 +32,18 @@ services:
|
||||||
portainer:
|
portainer:
|
||||||
image: portainer/portainer-ce:latest
|
image: portainer/portainer-ce:latest
|
||||||
volumes:
|
volumes:
|
||||||
- portainer_data:/data portainer
|
- portainer_data:/data
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
labels:
|
labels:
|
||||||
easyhaproxy.http.redirect_ssl: true
|
easyhaproxy.http.redirect_ssl: true
|
||||||
easyhaproxy.http.letsencrypt: true
|
easyhaproxy.http.certbot: true
|
||||||
easyhaproxy.http.host: portainer.xpto.us
|
easyhaproxy.http.host: portainer.xpto.us
|
||||||
easyhaproxy.http.port: 80
|
easyhaproxy.http.port: 80
|
||||||
easyhaproxy.http.localport: 9000
|
easyhaproxy.http.localport: 9000
|
||||||
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
certs_letsencrypt:
|
certs_certbot:
|
||||||
external: true
|
external: true
|
||||||
certs_haproxy:
|
certs_haproxy:
|
||||||
external: true
|
external: true
|
||||||
|
|
|
||||||
82
examples/swarm/certs/host1.local.pem
Normal file
82
examples/swarm/certs/host1.local.pem
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFDTCCAvWgAwIBAgIURi+w1ZVgeedTlNIAwqQBMJv6dXswDQYJKoZIhvcNAQEL
|
||||||
|
BQAwFjEUMBIGA1UEAwwLaG9zdDEubG9jYWwwHhcNMjEwODEwMTg0OTA2WhcNMzEw
|
||||||
|
ODA4MTg0OTA2WjAWMRQwEgYDVQQDDAtob3N0MS5sb2NhbDCCAiIwDQYJKoZIhvcN
|
||||||
|
AQEBBQADggIPADCCAgoCggIBAMBDAhLAygJuaW6w6ffigzTAAGXpmEz0tIxn1k4Z
|
||||||
|
x5wN5rpv/qu0QMYz+Av2u1eOKEKZeaFRVpT0r93dX7IvbEZHt25GPiBvlLGqhjKR
|
||||||
|
PnSk/7U8XmsnttUAV7rVEK1UrdFw8/IwriQC+dhr0mnYfSDMkvBoMFpdhVNTrbAZ
|
||||||
|
1TB6rQjE7Ar0Mt8my96XJmwrcjK2Tj+E2rgPIUz1e5cekFYIDSBatmw+3+vr+T5x
|
||||||
|
FNFkJ2o30W5o8ZflCJJzrVaihqQics6ZKDgpf7iqXMFiwWIlhdQpGvx5Gf/KFTK9
|
||||||
|
UaOnRZz/X+2CebAFaTHR3k/PYppWTgBBBuRvlpCw+wdnkmteC0SQRF91QWVr7ejo
|
||||||
|
7KaOlGI5VtvMUsWvTeAZmpaymIaATETuOJaY0JU11OmLeD9DOj5E2SQ7qIX/pFcp
|
||||||
|
xpzG5j4c+MlgvxP2VAkNTeAXCaYiPBQH5ZZg0HE2WnB1KhLRFlHd4iHQD2GJ5yN/
|
||||||
|
6fCFBfZfKSeK8JauwxgWkra53OcDq/mKd+DA/dK+/ruG7tqwVgIa04HOplzM7LYR
|
||||||
|
GB0Irs9+lr5/PJbQZmU073Mdn6cXAg3p+6wvwFlDkS5v13gBDYNHtF62bc551edF
|
||||||
|
Z6kGzJ7wmGRo84aBP7MuRZeReLOrSS67a1wLdzZsMnP1TJ7x9Lfr9MKl2uDnQdnY
|
||||||
|
ex8DAgMBAAGjUzBRMB0GA1UdDgQWBBSQ/mtZd6h8en9YQVH6HO1PlWWiqzAfBgNV
|
||||||
|
HSMEGDAWgBSQ/mtZd6h8en9YQVH6HO1PlWWiqzAPBgNVHRMBAf8EBTADAQH/MA0G
|
||||||
|
CSqGSIb3DQEBCwUAA4ICAQChQYNuah3+mTpIBDYxGrjTJNuOTIMaWzMyi1tkf+L0
|
||||||
|
sEGwpbmAO2mWWQYF7WVLsi98PULh3adjt2jiud9VlaaC6gnwn5Zo1+Pilo9sNLLW
|
||||||
|
6ij0+rN4kwIm/pNqi+jDuu2cvAuHIwZWeh8bEe/5UCxo4ihmWFQN8eJ6TUKCphRC
|
||||||
|
6Eor/SSZZBQHgPl0BchzHOkwu7R3LCndRqxjhAoVb9yQOV+ZsmTeJXulwNzJ1uLt
|
||||||
|
T8OIgIiDpmBo7HSN2H0k3chx00AsjUyJ9mmAWPejFe/KXLRPcVZR17jhzgfIBEzs
|
||||||
|
M5WtWFm1aHDjVv6M6iteVm61E9T+k/M11ru1e2YwsxTDvb6x04mcrNu9soqddBbr
|
||||||
|
VfpluuoQ/hEAbXtFNPoTySpz0cwOwcHCowVOLmdKgvImszZiMyHHG8VGGmPh88n7
|
||||||
|
wVxb0gV0P4RMrcMLdeTdn55YQr1CqBr34eB6ol6AsbTm3VzBHRVmFNksl1o5JB5t
|
||||||
|
tXLgF/G8/rzJ/4m1PaVuxrB7DxUmIk8EPbSIVkvZvd7LBzKwQ6IfVaucewHfEajQ
|
||||||
|
VIiexSMiFc7lw3KnxjOHZjf6FM9VYg3No++GdC99s7LkIuJwAMLNqTQ7Hvhn7YvP
|
||||||
|
4FlSIgc6xj0YkGZEQlb5o/5nauEqQU0ABgw6jtI4NxrNLT6cp7CO4M0xIDEg/3YD
|
||||||
|
aA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDAQwISwMoCbmlu
|
||||||
|
sOn34oM0wABl6ZhM9LSMZ9ZOGcecDea6b/6rtEDGM/gL9rtXjihCmXmhUVaU9K/d
|
||||||
|
3V+yL2xGR7duRj4gb5SxqoYykT50pP+1PF5rJ7bVAFe61RCtVK3RcPPyMK4kAvnY
|
||||||
|
a9Jp2H0gzJLwaDBaXYVTU62wGdUweq0IxOwK9DLfJsvelyZsK3Iytk4/hNq4DyFM
|
||||||
|
9XuXHpBWCA0gWrZsPt/r6/k+cRTRZCdqN9FuaPGX5QiSc61WooakInLOmSg4KX+4
|
||||||
|
qlzBYsFiJYXUKRr8eRn/yhUyvVGjp0Wc/1/tgnmwBWkx0d5Pz2KaVk4AQQbkb5aQ
|
||||||
|
sPsHZ5JrXgtEkERfdUFla+3o6OymjpRiOVbbzFLFr03gGZqWspiGgExE7jiWmNCV
|
||||||
|
NdTpi3g/Qzo+RNkkO6iF/6RXKcacxuY+HPjJYL8T9lQJDU3gFwmmIjwUB+WWYNBx
|
||||||
|
NlpwdSoS0RZR3eIh0A9hiecjf+nwhQX2XyknivCWrsMYFpK2udznA6v5infgwP3S
|
||||||
|
vv67hu7asFYCGtOBzqZczOy2ERgdCK7Pfpa+fzyW0GZlNO9zHZ+nFwIN6fusL8BZ
|
||||||
|
Q5Eub9d4AQ2DR7Retm3OedXnRWepBsye8JhkaPOGgT+zLkWXkXizq0kuu2tcC3c2
|
||||||
|
bDJz9Uye8fS36/TCpdrg50HZ2HsfAwIDAQABAoICAQC/xZbZ0cctqagsqvaVNTEe
|
||||||
|
eq1q+hfaGvPEYQaYHIrIE+2i5XcnGcLKcKfodxDjAn8R/zgdOp6cMX0CVn/PohHk
|
||||||
|
AEDtE8+AVwwAM1FsOwgLHVGaGz8qrxBlYdQgHcpmueIu2PXbC8eHUBiaUOIuhaw5
|
||||||
|
/RRMDAC/Ai2ssfi7gOjvVE4oQxQW0QG1KGOOAUJn/uYHw2RFY2Uu1pimxO2kDO53
|
||||||
|
gcxmC1WOnyCHmHaiW/Uh7z6JamfSM4dXtTJZslyh37dhHKNbg9VkP7CQKA4hLzop
|
||||||
|
hbf5qY6rargONiny1HgMPxrmwKuUouJyOtN0yBtxjDCUNaXUBwiy7sNGS+H4vsyB
|
||||||
|
5P9HhIHStu+FZt3HG7EIqCndiaSKDS4jWaVQAbbo4nZ2Zs2BD+xDePRCRUqX7rM4
|
||||||
|
4XzPIRWWXmmWf/7Ig29Hbrp4a9LcOmQ2leCJtbaTFSN96OLUJ5E+hQ0ulCZgBVmQ
|
||||||
|
RCUYkJP4lOzbaKdzjxgHMrHzm45eUFf8LirOxi2uyxXHQmDNu4b3X18kt3PgUmUm
|
||||||
|
3dXpl3fqSyJa7SCV8ZNBrsrDq1E+thYtu91QbVSGxHd9HrNVe3XdLbOCdU9CuC69
|
||||||
|
Nglznaa7sZLqmyKejTfGsY7xrWdNcMPl4p4fcID/O4EpASZforpTeKNT0ZIfZZew
|
||||||
|
b0mAQeYZqQM8i/qMYN/uAQKCAQEA5qg1sRNMc6VdM/tRglasGYoxjgRC2OqADZgs
|
||||||
|
mAXMUJ3kErpyxt+eCimy8ibuYpzRTIQ8fBTWRkCtRZXJ7+KcLVtk9QZIoLbhyNwd
|
||||||
|
4IxEQZFuUljDbvSjTLSycsHvo65ibWIfTL7bgWlLGgGq/UOzfGsgH6S9wLp5G30G
|
||||||
|
8ELyjI5eTIYICrfTmVL+c45MRpEMKo+cvz8PysiaOFTn3cyswPVdYaeEEqMQjU8w
|
||||||
|
IGNsGZLytY7BABBcY0ldrtba/O+Fv/+RH7uUtzP7xpCIwFCx80ZzN+WRy9NvI63U
|
||||||
|
zq3yIBoW9GyApD2+PLaPNxf7QLTUChY1Zz/dYRltKOxv2Aa5gQKCAQEA1WLWNqp0
|
||||||
|
fhB/ZtfSEShxFMM89cjN6Aaz1WKL7uTBou9oSJnxjkhkaV76acnT/iqXtxMNgHi1
|
||||||
|
fImDpU3PvM0Y4Ud2T47oHc6P1BrZPN/GmXy/s6BAEdPwLe7J+4nTISHAdGmrh+a/
|
||||||
|
5pktu32g9lWqftxecFIVSLPWkxT0XKiMxp1ffkL+OavpMgMFZK41iKs3dNShKPog
|
||||||
|
L8GSPcP9x/yn78P2eK3N+PGjlA6pPzrANyWU7N0/bmHcB9TKP+udYWcjVhru7MYN
|
||||||
|
wNrE4kKdC8v8i7x7tDbvb79T+Fo6PIh53p0OsnZzA8UR0QNR+vDQufQuyaj8REC+
|
||||||
|
ZG8YyCKsvk8ygwKCAQA/fsSxB0f/eeErYx6wC536teEoYCHqxrsTgvWbr9TryFs1
|
||||||
|
kJ/yATLnR01cfb0X5mVzc9+WpMHLuxg31KEvaSlnDwa+sMkjfNSwz29mFhbgGeHN
|
||||||
|
x2OdUrj1b7TEBIEshN/RjrZhERUqDcs/0H+6kn2BXZgNPfOCb5LRL1zOnQ9aBAMP
|
||||||
|
e8IQ+UPFrGQheWWj81/vA3O57ekyAID7ytu9Yg+YWrMnI88mtj7jN45fDB+A9sPb
|
||||||
|
mP2mP9q+9j5U2A6WnHUsQnU30BKDUEsaAUWz80LZXmZvV8IH4x9wKfUwJBBIKAZz
|
||||||
|
qL7M97Y7zmGkX/Spfl30nOJ8lschaLd1EYlEZa2BAoIBAQCye25T4TV5MJFv0zuZ
|
||||||
|
MGuNg1Sc/O4Fkn2fEUOceWjhwUBH4cPjT/f1DwWDsNaJ9NRbxCr5931OArPDc5c8
|
||||||
|
A404+Y4jM5RBQkKZli94tHAod+jc9UBB6TUvJll59SlMwC9679wS21ZOKnfPKGCX
|
||||||
|
SsZGQEsZxf6ZhhsHgXJ3gl/lzUJPmPeOA5YVR+Od9/09KIFFTojSfoynhVCuKx49
|
||||||
|
xb4uVYn2HOJ4xJ0fPTghdCHMvrmXeeQRjvb88eaNmqVUEHHFFtgb4fklA5fE7RTx
|
||||||
|
BhliRDBwZ7bUkINK6yVk9n6BTns5mMvRLmgdnJpYvE7KC02LTbZb3I+j8C0ZUa+N
|
||||||
|
qy7DAoIBAAieribS7WUcl2aBlkm5+W7qNm/INm5zvnoSPo6V3wa5hs6f9+C/kbdF
|
||||||
|
87jQPA/YFe3uR2sAJ7slX5euZK8WmfpFmgzlu0sEz81MLQ/WypZtZytyVtWzB2Pu
|
||||||
|
XCW1tdSH9eI2BmhXgokHNTM48Nk/xOENrP/seXrIx5LK0hnDHZotu/z6+YSkB9hF
|
||||||
|
cm2fZygD1dMLX6liRimxyFY+dICJNB95JifTLWYnWeGddkwPtXUeGXE1olzvNkLD
|
||||||
|
zMzE09uhkx/lRJnteOBEZaf80OB/09Oi9b9/rxY59dwsH6GaxLoTfEKuPnvBVMNR
|
||||||
|
YkU14WzQKleFkiBJI9lVvnfgGnOlgg0=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
40
examples/swarm/easyhaproxy.yml
Normal file
40
examples/swarm/easyhaproxy.yml
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# To Install
|
||||||
|
# docker network create --driver overlay --attachable easyhaproxy
|
||||||
|
# docker stack deploy -c easyhaproxy.yml easyhaproxy
|
||||||
|
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
haproxy:
|
||||||
|
image: byjg/easy-haproxy:4.3.1-rc2
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ./certs:/certs/haproxy
|
||||||
|
- certs_certbot:/certs/certbot
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
environment:
|
||||||
|
EASYHAPROXY_DISCOVER: swarm
|
||||||
|
EASYHAPROXY_SSL_MODE: "loose"
|
||||||
|
EASYHAPROXY_LETSENCRYPT_EMAIL: changeme@example.org
|
||||||
|
HAPROXY_CUSTOMERRORS: "true"
|
||||||
|
HAPROXY_USERNAME: admin
|
||||||
|
HAPROXY_PASSWORD: password
|
||||||
|
HAPROXY_STATS_PORT: 1936
|
||||||
|
ports:
|
||||||
|
- "80:80/tcp"
|
||||||
|
- "443:443/tcp"
|
||||||
|
- "1936:1936/tcp"
|
||||||
|
networks:
|
||||||
|
- easyhaproxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
easyhaproxy:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
certs_certbot:
|
||||||
|
# external: true
|
||||||
|
# certs_haproxy:
|
||||||
|
# external: true
|
||||||
28
examples/swarm/portainer.yml
Normal file
28
examples/swarm/portainer.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# To install:
|
||||||
|
# docker stack deploy -c portainer.yml portainer
|
||||||
|
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
services:
|
||||||
|
portainer:
|
||||||
|
image: portainer/portainer-ce:latest
|
||||||
|
volumes:
|
||||||
|
- portainer_data:/data portainer
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
labels:
|
||||||
|
# easyhaproxy.http.redirect_ssl: true
|
||||||
|
# easyhaproxy.http.certbot: true
|
||||||
|
easyhaproxy.http.host: portainer.local
|
||||||
|
easyhaproxy.http.port: 80
|
||||||
|
easyhaproxy.http.localport: 9000
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
certs_certbot:
|
||||||
|
external: true
|
||||||
|
# certs_haproxy:
|
||||||
|
# external: true
|
||||||
|
portainer_data:
|
||||||
|
# external: true
|
||||||
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
# To install:
|
||||||
|
# docker stack deploy -c services.yml services
|
||||||
|
#
|
||||||
# To test:
|
# To test:
|
||||||
# curl -k -H "Host: host1.local" https://127.0.0.1/
|
# curl -k -H "Host: host1.local" https://127.0.0.1/
|
||||||
# curl -k -H "Host: host2.local" https://127.0.0.1/
|
# curl -k -H "Host: host2.local" https://127.0.0.1/
|
||||||
|
|
@ -18,25 +21,6 @@
|
||||||
version: "3"
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
haproxy:
|
|
||||||
image: byjg/easy-haproxy
|
|
||||||
volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
- ./host2.local.pem:/certs/haproxy/host2.local.pem
|
|
||||||
deploy:
|
|
||||||
replicas: 1
|
|
||||||
environment:
|
|
||||||
EASYHAPROXY_DISCOVER: swarm
|
|
||||||
EASYHAPROXY_SSL_MODE: "loose"
|
|
||||||
HAPROXY_CUSTOMERRORS: "true"
|
|
||||||
HAPROXY_USERNAME: admin
|
|
||||||
HAPROXY_PASSWORD: password
|
|
||||||
HAPROXY_STATS_PORT: 1936
|
|
||||||
ports:
|
|
||||||
- "80:80/tcp"
|
|
||||||
- "443:443/tcp"
|
|
||||||
- "1936:1936/tcp"
|
|
||||||
|
|
||||||
container:
|
container:
|
||||||
image: byjg/static-httpserver
|
image: byjg/static-httpserver
|
||||||
deploy:
|
deploy:
|
||||||
|
|
@ -69,7 +69,7 @@ spec:
|
||||||
value: {{ .Values.easyhaproxy.logLevel.haproxy }}
|
value: {{ .Values.easyhaproxy.logLevel.haproxy }}
|
||||||
- name: CERTBOT_LOG_LEVEL
|
- name: CERTBOT_LOG_LEVEL
|
||||||
value: {{ .Values.easyhaproxy.logLevel.certbot }}
|
value: {{ .Values.easyhaproxy.logLevel.certbot }}
|
||||||
{{- if .Values.easyhaproxy.letsencrypt.email }}
|
{{- if .Values.easyhaproxy.certbot.email }}
|
||||||
- name: EASYHAPROXY_LETSENCRYPT_EMAIL
|
- name: EASYHAPROXY_CERTBOT_EMAIL
|
||||||
value: {{ .Values.easyhaproxy.letsencrypt.email }}
|
value: {{ .Values.easyhaproxy.certbot.email }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ easyhaproxy:
|
||||||
certbot: DEBUG
|
certbot: DEBUG
|
||||||
easyhaproxy: DEBUG
|
easyhaproxy: DEBUG
|
||||||
haproxy: DEBUG
|
haproxy: DEBUG
|
||||||
letsencrypt:
|
certbot:
|
||||||
email: ""
|
email: ""
|
||||||
|
|
||||||
# Make sure to create this
|
# Make sure to create this
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import base64
|
import base64
|
||||||
import hashlib
|
|
||||||
from jinja2 import Environment, FileSystemLoader
|
|
||||||
import json
|
import json
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
|
||||||
class DockerLabelHandler:
|
class DockerLabelHandler:
|
||||||
def __init__(self, label):
|
def __init__(self, label):
|
||||||
|
self.__data = None
|
||||||
self.__label_base = label
|
self.__label_base = label
|
||||||
|
|
||||||
def get_lookup_label(self):
|
def get_lookup_label(self):
|
||||||
|
|
@ -18,19 +19,17 @@ class DockerLabelHandler:
|
||||||
|
|
||||||
return "{}.{}".format(self.__label_base, ".".join(key))
|
return "{}.{}".format(self.__label_base, ".".join(key))
|
||||||
|
|
||||||
|
def get(self, label, default_value=""):
|
||||||
def get(self, label, default_value = ""):
|
|
||||||
if self.has_label(label):
|
if self.has_label(label):
|
||||||
return self.__data[label]
|
return self.__data[label]
|
||||||
return default_value
|
return default_value
|
||||||
|
|
||||||
|
def get_bool(self, label, default_value=False):
|
||||||
def get_bool(self, label, default_value = False):
|
|
||||||
if self.has_label(label):
|
if self.has_label(label):
|
||||||
return self.__data[label].lower() in ["true", "1", "yes"]
|
return self.__data[label].lower() in ["true", "1", "yes"]
|
||||||
return default_value
|
return default_value
|
||||||
|
|
||||||
def get_json(self, label, default_value = {}):
|
def get_json(self, label, default_value={}):
|
||||||
if self.has_label(label):
|
if self.has_label(label):
|
||||||
return json.loads(self.__data[label])
|
return json.loads(self.__data[label])
|
||||||
return default_value
|
return default_value
|
||||||
|
|
@ -38,7 +37,6 @@ class DockerLabelHandler:
|
||||||
def set_data(self, data):
|
def set_data(self, data):
|
||||||
self.__data = data
|
self.__data = data
|
||||||
|
|
||||||
|
|
||||||
def has_label(self, label):
|
def has_label(self, label):
|
||||||
if label in self.__data:
|
if label in self.__data:
|
||||||
return True
|
return True
|
||||||
|
|
@ -49,14 +47,14 @@ class HaproxyConfigGenerator:
|
||||||
def __init__(self, mapping):
|
def __init__(self, mapping):
|
||||||
self.mapping = mapping
|
self.mapping = mapping
|
||||||
self.mapping.setdefault("ssl_mode", 'default')
|
self.mapping.setdefault("ssl_mode", 'default')
|
||||||
self.mapping.setdefault("letsencrypt", {"email": "", "staging": False})
|
self.mapping.setdefault("certbot", {"email": "", "server": False, "eab_kid": False, "eab_hmac_key": False})
|
||||||
self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower()
|
self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower()
|
||||||
self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy")
|
self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy")
|
||||||
self.letsencrypt_hosts = []
|
self.certbot_hosts = []
|
||||||
self.serving_hosts = []
|
self.serving_hosts = []
|
||||||
self.certs = {}
|
self.certs = {}
|
||||||
|
|
||||||
def generate(self, container_metadata = {}):
|
def generate(self, container_metadata={}):
|
||||||
self.mapping.setdefault("easymapping", [])
|
self.mapping.setdefault("easymapping", [])
|
||||||
|
|
||||||
if container_metadata != {}:
|
if container_metadata != {}:
|
||||||
|
|
@ -70,7 +68,6 @@ class HaproxyConfigGenerator:
|
||||||
template = env.get_template('haproxy.cfg.j2')
|
template = env.get_template('haproxy.cfg.j2')
|
||||||
return template.render(data=self.mapping)
|
return template.render(data=self.mapping)
|
||||||
|
|
||||||
|
|
||||||
def parse(self, container_metadata):
|
def parse(self, container_metadata):
|
||||||
easymapping = dict()
|
easymapping = dict()
|
||||||
|
|
||||||
|
|
@ -106,10 +103,10 @@ class HaproxyConfigGenerator:
|
||||||
"80"
|
"80"
|
||||||
)
|
)
|
||||||
|
|
||||||
letsencrypt = self.label.get_bool(
|
certbot = self.label.get_bool(
|
||||||
self.label.create([definition, "letsencrypt"]),
|
self.label.create([definition, "certbot"]),
|
||||||
False
|
False
|
||||||
) and self.mapping["letsencrypt"]["email"] != ""
|
) and self.mapping["certbot"]["email"] != ""
|
||||||
clone_to_ssl = self.label.get_bool(
|
clone_to_ssl = self.label.get_bool(
|
||||||
self.label.create([definition, "clone_to_ssl"])
|
self.label.create([definition, "clone_to_ssl"])
|
||||||
)
|
)
|
||||||
|
|
@ -117,7 +114,7 @@ class HaproxyConfigGenerator:
|
||||||
if port not in easymapping:
|
if port not in easymapping:
|
||||||
easymapping[port] = {
|
easymapping[port] = {
|
||||||
"mode": mode,
|
"mode": mode,
|
||||||
"health-check": "",
|
"ssl-check": "",
|
||||||
"port": port,
|
"port": port,
|
||||||
"hosts": dict(),
|
"hosts": dict(),
|
||||||
"redirect": dict(),
|
"redirect": dict(),
|
||||||
|
|
@ -129,8 +126,8 @@ class HaproxyConfigGenerator:
|
||||||
"80"
|
"80"
|
||||||
)
|
)
|
||||||
|
|
||||||
easymapping[port]["health-check"] = self.label.get(
|
easymapping[port]["ssl-check"] = self.label.get(
|
||||||
self.label.create([definition, "health-check"]),
|
self.label.create([definition, "ssl-check"]),
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -139,32 +136,36 @@ class HaproxyConfigGenerator:
|
||||||
self.serving_hosts.append("%s:%s" % (hostname, port))
|
self.serving_hosts.append("%s:%s" % (hostname, port))
|
||||||
easymapping[port]["hosts"].setdefault(hostname, {})
|
easymapping[port]["hosts"].setdefault(hostname, {})
|
||||||
easymapping[port]["hosts"][hostname].setdefault("containers", [])
|
easymapping[port]["hosts"][hostname].setdefault("containers", [])
|
||||||
easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False)
|
easymapping[port]["hosts"][hostname].setdefault("certbot", False)
|
||||||
easymapping[port]["hosts"][hostname]["containers"] += ["{}:{}".format(container, ct_port)]
|
easymapping[port]["hosts"][hostname]["containers"] += ["{}:{}".format(container, ct_port)]
|
||||||
easymapping[port]["hosts"][hostname]["letsencrypt"] = letsencrypt
|
easymapping[port]["hosts"][hostname]["certbot"] = certbot
|
||||||
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
easymapping[port]["hosts"][hostname]["redirect_ssl"] = self.label.get_bool(
|
||||||
self.label.create([definition, "redirect_ssl"])
|
self.label.create([definition, "redirect_ssl"])
|
||||||
)
|
)
|
||||||
|
easymapping[port]["hosts"][hostname]["balance"] = self.label.get(
|
||||||
|
self.label.create([definition, "balance"]),
|
||||||
|
"roundrobin"
|
||||||
|
)
|
||||||
|
|
||||||
easymapping[port]["redirect"] = self.label.get_json(
|
easymapping[port]["redirect"] = self.label.get_json(
|
||||||
self.label.create([definition, "redirect"])
|
self.label.create([definition, "redirect"])
|
||||||
)
|
)
|
||||||
|
|
||||||
if letsencrypt or clone_to_ssl:
|
if certbot or clone_to_ssl:
|
||||||
if "443" not in easymapping:
|
if "443" not in easymapping:
|
||||||
easymapping["443"] = {
|
easymapping["443"] = {
|
||||||
"mode": "http",
|
"mode": "http",
|
||||||
"health-check": "ssl",
|
"ssl-check": "ssl",
|
||||||
"port": "443",
|
"port": "443",
|
||||||
"hosts": dict(),
|
"hosts": dict(),
|
||||||
"redirect": dict(),
|
"redirect": dict(),
|
||||||
}
|
}
|
||||||
easymapping["443"]["hosts"][hostname] = dict(easymapping[port]["hosts"][hostname])
|
easymapping["443"]["hosts"][hostname] = dict(easymapping[port]["hosts"][hostname])
|
||||||
easymapping["443"]["hosts"][hostname]["letsencrypt"] = False
|
easymapping["443"]["hosts"][hostname]["certbot"] = False
|
||||||
easymapping["443"]["hosts"][hostname]["redirect_ssl"] = False
|
easymapping["443"]["hosts"][hostname]["redirect_ssl"] = False
|
||||||
easymapping["443"]["ssl"] = True
|
easymapping["443"]["ssl"] = True
|
||||||
self.letsencrypt_hosts.append(hostname) if letsencrypt and hostname not in self.letsencrypt_hosts else self.letsencrypt_hosts
|
self.certbot_hosts.append(
|
||||||
|
hostname) if certbot and hostname not in self.certbot_hosts else self.certbot_hosts
|
||||||
|
|
||||||
# handle SSL
|
# handle SSL
|
||||||
ssl_label = self.label.create([definition, "sslcert"])
|
ssl_label = self.label.create([definition, "sslcert"])
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,97 @@
|
||||||
from datetime import datetime
|
|
||||||
from multiprocessing import Process, Lock
|
|
||||||
import subprocess
|
|
||||||
import shlex
|
|
||||||
import time
|
|
||||||
import os
|
import os
|
||||||
import re
|
import shlex
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from multiprocessing import Process
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from OpenSSL import crypto
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerEnv:
|
||||||
|
@staticmethod
|
||||||
|
def read():
|
||||||
|
env_vars = {
|
||||||
|
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
|
||||||
|
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE").lower() if os.getenv("EASYHAPROXY_SSL_MODE") else '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"
|
||||||
|
|
||||||
|
env_vars["logLevel"] = {
|
||||||
|
"easyhaproxy": os.getenv("EASYHAPROXY_LOG_LEVEL") if os.getenv(
|
||||||
|
"EASYHAPROXY_LOG_LEVEL") else Functions.DEBUG,
|
||||||
|
"haproxy": os.getenv("HAPROXY_LOG_LEVEL") if os.getenv("HAPROXY_LOG_LEVEL") else Functions.INFO,
|
||||||
|
"certbot": os.getenv("CERTBOT_LOG_LEVEL") if os.getenv("CERTBOT_LOG_LEVEL") else Functions.DEBUG,
|
||||||
|
}
|
||||||
|
|
||||||
|
env_vars["certbot"] = {
|
||||||
|
"autoconfig": os.getenv("EASYHAPROXY_CERTBOT_AUTOCONFIG", ""),
|
||||||
|
"email": os.getenv("EASYHAPROXY_CERTBOT_EMAIL", ""),
|
||||||
|
"server": os.getenv("EASYHAPROXY_CERTBOT_SERVER", False),
|
||||||
|
"eab_kid": os.getenv("EASYHAPROXY_CERTBOT_EAB_KID", ""),
|
||||||
|
"eab_hmac_key": os.getenv("EASYHAPROXY_CERTBOT_EAB_HMAC_KEY", ""),
|
||||||
|
"retry_count": int(os.getenv("EASYHAPROXY_CERTBOT_RETRY_COUNT", 60)),
|
||||||
|
}
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] != "" and not env_vars["certbot"]["server"] and env_vars["certbot"]["email"] != "":
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "letsencrypt":
|
||||||
|
env_vars["certbot"]["server"] = "https://acme-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "letsencrypt_test":
|
||||||
|
env_vars["certbot"]["server"] = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "buypass":
|
||||||
|
env_vars["certbot"]["server"] = "https://api.buypass.com/acme/directory"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "buypass_test":
|
||||||
|
env_vars["certbot"]["server"] = "https://api.test4.buypass.no/acme/directory"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "sslcom_rca":
|
||||||
|
env_vars["certbot"]["server"] = "https://acme.ssl.com/sslcom-dv-rsa"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "sslcom_ecc":
|
||||||
|
env_vars["certbot"]["server"] = "https://acme.ssl.com/sslcom-dv-ecc"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "google":
|
||||||
|
env_vars["certbot"]["server"] = "https://dv.acme-v02.api.pki.goog/directory"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "google_test":
|
||||||
|
env_vars["certbot"]["server"] = "https://dv.acme-v02.test-api.pki.goog/directory"
|
||||||
|
|
||||||
|
if env_vars["certbot"]["autoconfig"] == "zerossl":
|
||||||
|
url = "https://api.zerossl.com/acme/eab-credentials-email"
|
||||||
|
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
||||||
|
data = "email=" + env_vars["certbot"]["email"]
|
||||||
|
resp = requests.post(url, headers=headers, data=data).json()
|
||||||
|
|
||||||
|
if resp["success"]:
|
||||||
|
env_vars["certbot"]["server"] = "https://acme.zerossl.com/v2/DV90"
|
||||||
|
env_vars["certbot"]["eab_kid"] = os.environ['EASYHAPROXY_CERTBOT_EAB_KID'] = resp["eab_kid"]
|
||||||
|
env_vars["certbot"]["eab_hmac_key"] = os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = resp["eab_hmac_key"]
|
||||||
|
else:
|
||||||
|
del os.environ["EASYHAPROXY_CERTBOT_EMAIL"]
|
||||||
|
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Could not obtain ZeroSSL credentials " + resp["error"]["type"])
|
||||||
|
|
||||||
|
os.environ['EASYHAPROXY_CERTBOT_SERVER'] = env_vars["certbot"]["server"]
|
||||||
|
|
||||||
|
return env_vars
|
||||||
|
|
||||||
|
|
||||||
class Functions:
|
class Functions:
|
||||||
HAPROXY_LOG="HAPROXY"
|
HAPROXY_LOG = "HAPROXY"
|
||||||
EASYHAPROXY_LOG="EASYHAPROXY"
|
EASYHAPROXY_LOG = "EASYHAPROXY"
|
||||||
CERTBOT_LOG="CERTBOT"
|
CERTBOT_LOG = "CERTBOT"
|
||||||
INIT_LOG="INIT"
|
INIT_LOG = "INIT"
|
||||||
|
|
||||||
TRACE = "TRACE"
|
TRACE = "TRACE"
|
||||||
DEBUG = "DEBUG"
|
DEBUG = "DEBUG"
|
||||||
|
|
@ -71,9 +151,9 @@ class Functions:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(command,
|
process = subprocess.Popen(command,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
|
||||||
output = []
|
output = []
|
||||||
|
|
||||||
|
|
@ -92,29 +172,29 @@ class Functions:
|
||||||
Functions.log(source, Functions.WARN, process.stderr.readlines())
|
Functions.log(source, Functions.WARN, process.stderr.readlines())
|
||||||
break
|
break
|
||||||
|
|
||||||
return output
|
return [return_code, output]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(source, Functions.ERROR, "%s" % (e))
|
Functions.log(source, Functions.ERROR, "%s" % e)
|
||||||
|
return [-99, e]
|
||||||
|
|
||||||
|
|
||||||
class Consts:
|
class Consts:
|
||||||
easyhaproxy_config = "/etc/haproxy/static/config.yml"
|
easyhaproxy_config = "/etc/haproxy/static/config.yml"
|
||||||
haproxy_config = "/etc/haproxy/haproxy.cfg"
|
haproxy_config = "/etc/haproxy/haproxy.cfg"
|
||||||
certs_letsencrypt = "/certs/letsencrypt"
|
custom_config_folder = "/etc/haproxy/conf.d"
|
||||||
|
certs_certbot = "/certs/certbot"
|
||||||
certs_haproxy = "/certs/haproxy"
|
certs_haproxy = "/certs/haproxy"
|
||||||
|
|
||||||
|
|
||||||
class DaemonizeHAProxy:
|
class DaemonizeHAProxy:
|
||||||
def __init__(self):
|
def __init__(self, custom_config_folder = None):
|
||||||
self.process = None
|
self.process = None
|
||||||
self.thread = None
|
self.thread = None
|
||||||
self.sleep_secs = None
|
self.sleep_secs = None
|
||||||
|
self.custom_config_folder = custom_config_folder if custom_config_folder is not None else Consts.custom_config_folder
|
||||||
|
|
||||||
def haproxy(self, action):
|
def haproxy(self, action):
|
||||||
if action == "start":
|
self.__prepare(self.get_haproxy_command(action))
|
||||||
self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock")
|
|
||||||
else:
|
|
||||||
pid = "".join(Functions().run_bash(Functions.HAPROXY_LOG, "cat /run/haproxy.pid", log_output=False))
|
|
||||||
self.__prepare("/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf %s" % (pid))
|
|
||||||
|
|
||||||
if self.process is None:
|
if self.process is None:
|
||||||
return
|
return
|
||||||
|
|
@ -122,6 +202,18 @@ class DaemonizeHAProxy:
|
||||||
self.thread = Process(target=self.__start, args=())
|
self.thread = Process(target=self.__start, args=())
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
|
def get_haproxy_command(self, action, pid_file="/run/haproxy.pid"):
|
||||||
|
custom_config_files = ""
|
||||||
|
if len(list(self.get_custom_config_files().keys())) != 0:
|
||||||
|
custom_config_files = "-f %s" % (self.custom_config_folder)
|
||||||
|
|
||||||
|
if action == "start":
|
||||||
|
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -S /var/run/haproxy.sock" % (custom_config_files, pid_file)
|
||||||
|
else:
|
||||||
|
return_code, output = Functions().run_bash(Functions.HAPROXY_LOG, "cat %s" % pid_file, log_output=False)
|
||||||
|
pid = "".join(output)
|
||||||
|
return "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg %s -p %s -x /var/run/haproxy.sock -sf %s" % (custom_config_files, pid_file, pid)
|
||||||
|
|
||||||
def __prepare(self, command):
|
def __prepare(self, command):
|
||||||
source = Functions.HAPROXY_LOG
|
source = Functions.HAPROXY_LOG
|
||||||
if not isinstance(command, (list, tuple)):
|
if not isinstance(command, (list, tuple)):
|
||||||
|
|
@ -129,15 +221,14 @@ class DaemonizeHAProxy:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.process = subprocess.Popen(command,
|
self.process = subprocess.Popen(command,
|
||||||
shell=False,
|
shell=False,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
bufsize=-1,
|
bufsize=-1,
|
||||||
universal_newlines=True)
|
universal_newlines=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(source, Functions.ERROR, "%s" % (e))
|
Functions.log(source, Functions.ERROR, "%s" % e)
|
||||||
|
|
||||||
|
|
||||||
def __start(self):
|
def __start(self):
|
||||||
source = Functions.HAPROXY_LOG
|
source = Functions.HAPROXY_LOG
|
||||||
|
|
@ -146,11 +237,11 @@ class DaemonizeHAProxy:
|
||||||
for line in iter(self.process.stdout.readline, b''):
|
for line in iter(self.process.stdout.readline, b''):
|
||||||
Functions.log(source, Functions.INFO, line)
|
Functions.log(source, Functions.INFO, line)
|
||||||
|
|
||||||
returncode = self.process.wait()
|
return_code = self.process.wait()
|
||||||
Functions.log(source, Functions.DEBUG, "Return code %s" % (returncode))
|
Functions.log(source, Functions.DEBUG, "Return code %s" % return_code)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(source, Functions.ERROR, "%s" % (e))
|
Functions.log(source, Functions.ERROR, "%s" % e)
|
||||||
|
|
||||||
def is_alive(self):
|
def is_alive(self):
|
||||||
return self.thread.is_alive()
|
return self.thread.is_alive()
|
||||||
|
|
@ -172,18 +263,51 @@ class DaemonizeHAProxy:
|
||||||
|
|
||||||
time.sleep(self.sleep_secs)
|
time.sleep(self.sleep_secs)
|
||||||
|
|
||||||
|
def get_custom_config_files(self):
|
||||||
|
if not os.path.exists(self.custom_config_folder):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
files = {}
|
||||||
|
for file in os.listdir(self.custom_config_folder):
|
||||||
|
if file.endswith(".cfg"):
|
||||||
|
files[os.path.join(self.custom_config_folder, file)] = os.path.getmtime(os.path.join(self.custom_config_folder, file))
|
||||||
|
return dict(sorted(files.items(), key=lambda t: t[0]))
|
||||||
|
|
||||||
|
|
||||||
class Certbot:
|
class Certbot:
|
||||||
def __init__(self, certs, email, test_server):
|
def __init__(self, certs):
|
||||||
self.certs = certs
|
env = ContainerEnv.read()
|
||||||
self.email = email
|
|
||||||
self.test_server = self.set_test_server(test_server)
|
|
||||||
|
|
||||||
def set_test_server(self, test_server):
|
self.certs = certs
|
||||||
if test_server.lower() == "staging":
|
self.email = env["certbot"]["email"]
|
||||||
|
self.acme_server = self.set_acme_server(env["certbot"]["server"])
|
||||||
|
self.eab_kid = self.set_eab_kid(env["certbot"]["eab_kid"])
|
||||||
|
self.eab_hmac_key = self.set_eab_hmac_key(env["certbot"]["eab_hmac_key"])
|
||||||
|
self.freeze_issue = {}
|
||||||
|
self.retry_count = env["certbot"]["retry_count"]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_acme_server(acme_server):
|
||||||
|
if not acme_server:
|
||||||
|
return ""
|
||||||
|
if acme_server.lower() == "staging":
|
||||||
return "--staging"
|
return "--staging"
|
||||||
elif test_server.lower().startswith("http"):
|
elif acme_server.lower().startswith("http"):
|
||||||
return "--server " + test_server
|
return "--server " + acme_server
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_eab_kid(eab_kid):
|
||||||
|
if eab_kid != "":
|
||||||
|
return "--eab-kid \"%s\"" % eab_kid
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_eab_hmac_key(eab_hmac_key):
|
||||||
|
if eab_hmac_key != "":
|
||||||
|
return "--eab-hmac-key \"%s\"" % eab_hmac_key
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
@ -194,23 +318,25 @@ class Certbot:
|
||||||
try:
|
try:
|
||||||
request_certs = []
|
request_certs = []
|
||||||
renew_certs = []
|
renew_certs = []
|
||||||
current_time = time.time()
|
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
filename = "%s/%s.pem" % (self.certs, host)
|
cert_status = self.get_certificate_status(host)
|
||||||
host_arg = '-d %s' % (host)
|
host_arg = '-d %s' % host
|
||||||
if not os.path.exists(filename):
|
if cert_status == "ok" or cert_status == "error":
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Request new certificate for %s" % (host))
|
continue
|
||||||
|
elif host in self.freeze_issue:
|
||||||
|
freeze_count = self.freeze_issue.pop(host, 0)
|
||||||
|
if freeze_count > 0:
|
||||||
|
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG,
|
||||||
|
"Waiting freezing period (%d) for %s due previous errors" % (freeze_count, host))
|
||||||
|
self.freeze_issue[host] = freeze_count-1
|
||||||
|
elif cert_status == "not_found" or cert_status == "expired":
|
||||||
|
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "[%s] Request new certificate for %s" % (cert_status, host))
|
||||||
request_certs.append(host_arg)
|
request_certs.append(host_arg)
|
||||||
else:
|
elif cert_status == "expiring":
|
||||||
creation_time = os.path.getctime(filename)
|
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "[%s] Renew certificate for %s" % (cert_status, host))
|
||||||
if (current_time - creation_time) // (24 * 3600) > 90:
|
renew_certs.append(host_arg)
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Request expired certificate for %s" % (host))
|
|
||||||
request_certs.append(host_arg)
|
|
||||||
if (current_time - creation_time) // (24 * 3600) >= 45:
|
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Renew certificate for %s" % (host))
|
|
||||||
renew_certs.append(host_arg)
|
|
||||||
|
|
||||||
certbot_certonly = ('/usr/bin/certbot certonly {test_server}'
|
certbot_certonly = ('/usr/bin/certbot certonly {acme_server}'
|
||||||
' --standalone'
|
' --standalone'
|
||||||
' --preferred-challenges http'
|
' --preferred-challenges http'
|
||||||
' --http-01-port 2080'
|
' --http-01-port 2080'
|
||||||
|
|
@ -219,39 +345,80 @@ class Certbot:
|
||||||
' --no-eff-email'
|
' --no-eff-email'
|
||||||
' --non-interactive'
|
' --non-interactive'
|
||||||
' --max-log-backups=0'
|
' --max-log-backups=0'
|
||||||
' {certs} --email {email}'.format(certs = ' '.join(request_certs),
|
' {eab_kid} {eab_hmac_key}'
|
||||||
email = self.email,
|
' {certs} --email {email}'.format(eab_kid=self.eab_kid,
|
||||||
test_server = self.test_server)
|
eab_hmac_key=self.eab_hmac_key,
|
||||||
)
|
certs=' '.join(request_certs),
|
||||||
|
email=self.email,
|
||||||
|
acme_server=self.acme_server)
|
||||||
|
)
|
||||||
|
|
||||||
ret_reload = False
|
ret_reload = False
|
||||||
|
return_code_issue = 0
|
||||||
|
return_code_renew = 0
|
||||||
if len(request_certs) > 0:
|
if len(request_certs) > 0:
|
||||||
Functions.run_bash(Functions.CERTBOT_LOG, certbot_certonly, return_result=False)
|
return_code_issue, output = Functions.run_bash(Functions.CERTBOT_LOG, certbot_certonly, return_result=False)
|
||||||
ret_reload = True
|
ret_reload = True
|
||||||
|
|
||||||
if len(renew_certs) > 0:
|
if len(renew_certs) > 0:
|
||||||
Functions.run_bash(Functions.CERTBOT_LOG, "/usb/bin/certbot renew", return_result=False)
|
return_code_renew, output = Functions.run_bash(Functions.CERTBOT_LOG, "/usr/bin/certbot renew", return_result=False)
|
||||||
ret_reload = True
|
ret_reload = True
|
||||||
|
|
||||||
if ret_reload:
|
if ret_reload:
|
||||||
self.find_live_certificates()
|
self.find_live_certificates()
|
||||||
|
|
||||||
|
if return_code_issue != 0:
|
||||||
|
self.find_missing_certificates(request_certs)
|
||||||
|
if return_code_renew != 0:
|
||||||
|
self.find_missing_certificates(renew_certs)
|
||||||
|
|
||||||
return ret_reload
|
return ret_reload
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "%s" % (e))
|
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "%s" % e)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def merge_certificate(self, cert, key, filename):
|
@staticmethod
|
||||||
|
def merge_certificate(cert, key, filename):
|
||||||
Functions.save(filename, cert + key)
|
Functions.save(filename, cert + key)
|
||||||
|
|
||||||
def find_live_certificates(self):
|
def find_live_certificates(self):
|
||||||
letsencrypt_certs = "/etc/letsencrypt/live/"
|
certbot_certs = "/etc/letsencrypt/live/"
|
||||||
if not os.path.exists(letsencrypt_certs):
|
if not os.path.exists(certbot_certs):
|
||||||
return
|
return
|
||||||
for item in os.listdir(letsencrypt_certs):
|
for item in os.listdir(certbot_certs):
|
||||||
path = os.path.join(letsencrypt_certs, item)
|
path = os.path.join(certbot_certs, item)
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
cert = Functions.load(os.path.join(path, "cert.pem"))
|
cert = Functions.load(os.path.join(path, "cert.pem"))
|
||||||
key = Functions.load(os.path.join(path, "privkey.pem"))
|
key = Functions.load(os.path.join(path, "privkey.pem"))
|
||||||
filename = "%s/%s.pem" % (self.certs, item)
|
filename = "%s/%s.pem" % (self.certs, item)
|
||||||
self.merge_certificate(cert, key, filename)
|
self.merge_certificate(cert, key, filename)
|
||||||
|
|
||||||
|
def get_certificate_status(self, host):
|
||||||
|
current_time = time.time()
|
||||||
|
filename = "%s/%s.pem" % (self.certs, host)
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
return "not_found"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(filename, 'rb') as file:
|
||||||
|
certificate_str = file.read()
|
||||||
|
certificate = crypto.load_certificate(crypto.FILETYPE_PEM, certificate_str)
|
||||||
|
expiration_after = datetime.strptime(certificate.get_notAfter().decode()[:-1], '%Y%m%d%H%M%S').timestamp()
|
||||||
|
if current_time >= expiration_after:
|
||||||
|
return "expired"
|
||||||
|
elif (expiration_after - current_time) // (24 * 3600) <= 15:
|
||||||
|
return "expiring"
|
||||||
|
except Exception as e:
|
||||||
|
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "Certificate %s error %s" % (host, e))
|
||||||
|
return "error"
|
||||||
|
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
def find_missing_certificates(self, hosts):
|
||||||
|
for host in hosts:
|
||||||
|
if host.startswith("-d "):
|
||||||
|
host = host[3:]
|
||||||
|
cert_status = self.get_certificate_status(host)
|
||||||
|
if cert_status != "ok":
|
||||||
|
self.freeze_issue[host] = self.retry_count
|
||||||
|
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Freeze issuing ssl for %s due failure. The certificate is %s" % (host, cert_status))
|
||||||
|
|
|
||||||
33
src/main.py
33
src/main.py
|
|
@ -1,28 +1,33 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from deepdiff import DeepDiff
|
||||||
|
|
||||||
from functions import Functions, DaemonizeHAProxy, Certbot, Consts
|
from functions import Functions, DaemonizeHAProxy, Certbot, Consts
|
||||||
from processor import ProcessorInterface
|
from processor import ProcessorInterface
|
||||||
import os
|
|
||||||
from deepdiff import DeepDiff
|
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
processor_obj = ProcessorInterface.factory(os.getenv("EASYHAPROXY_DISCOVER"))
|
processor_obj = ProcessorInterface.factory(os.getenv("EASYHAPROXY_DISCOVER"))
|
||||||
if processor_obj is None:
|
if processor_obj is None:
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
os.makedirs(Consts.certs_letsencrypt, exist_ok=True)
|
os.makedirs(Consts.certs_certbot, exist_ok=True)
|
||||||
os.makedirs(Consts.certs_haproxy, exist_ok=True)
|
os.makedirs(Consts.certs_haproxy, exist_ok=True)
|
||||||
|
|
||||||
processor_obj.save_config(Consts.haproxy_config)
|
processor_obj.save_config(Consts.haproxy_config)
|
||||||
processor_obj.save_certs(Consts.certs_haproxy)
|
processor_obj.save_certs(Consts.certs_haproxy)
|
||||||
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
|
certbot_certs_found = processor_obj.get_certbot_hosts()
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to run after save_config
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG,
|
||||||
|
'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to run after save_config
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE, 'Object Found: %s' % (processor_obj.get_parsed_object()))
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE, 'Object Found: %s' % (processor_obj.get_parsed_object()))
|
||||||
|
|
||||||
old_haproxy = None
|
old_haproxy = None
|
||||||
haproxy = DaemonizeHAProxy()
|
haproxy = DaemonizeHAProxy()
|
||||||
|
current_custom_config_files = haproxy.get_custom_config_files()
|
||||||
haproxy.haproxy("start")
|
haproxy.haproxy("start")
|
||||||
haproxy.sleep()
|
haproxy.sleep()
|
||||||
|
|
||||||
certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"), os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "").lower())
|
certbot = Certbot(Consts.certs_certbot)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
if old_haproxy is not None:
|
if old_haproxy is not None:
|
||||||
|
|
@ -31,27 +36,28 @@ def start():
|
||||||
try:
|
try:
|
||||||
old_parsed = processor_obj.get_parsed_object()
|
old_parsed = processor_obj.get_parsed_object()
|
||||||
processor_obj.refresh()
|
processor_obj.refresh()
|
||||||
if certbot.check_certificates(letsencrypt_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive():
|
if certbot.check_certificates(certbot_certs_found) or DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive() or DeepDiff(current_custom_config_files, haproxy.get_custom_config_files()) != {}:
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'New configuration found. Reloading...')
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'New configuration found. Reloading...')
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE, 'Object Found: %s' % (processor_obj.get_parsed_object()))
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.TRACE,
|
||||||
|
'Object Found: %s' % (processor_obj.get_parsed_object()))
|
||||||
processor_obj.save_config(Consts.haproxy_config)
|
processor_obj.save_config(Consts.haproxy_config)
|
||||||
processor_obj.save_certs(Consts.certs_haproxy)
|
processor_obj.save_certs(Consts.certs_haproxy)
|
||||||
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
|
certbot_certs_found = processor_obj.get_certbot_hosts()
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG,
|
||||||
|
'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
||||||
old_haproxy = haproxy
|
old_haproxy = haproxy
|
||||||
haproxy = DaemonizeHAProxy()
|
haproxy = DaemonizeHAProxy()
|
||||||
|
current_custom_config_files = haproxy.get_custom_config_files()
|
||||||
haproxy.haproxy("reload")
|
haproxy.haproxy("reload")
|
||||||
old_haproxy.terminate()
|
old_haproxy.terminate()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.FATAL, "Err: %s" % (e))
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.FATAL, "Err: %s" % e)
|
||||||
|
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Heartbeat')
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Heartbeat')
|
||||||
haproxy.sleep()
|
haproxy.sleep()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
Functions.run_bash(Functions.INIT_LOG, '/usr/sbin/haproxy -v')
|
Functions.run_bash(Functions.INIT_LOG, '/usr/sbin/haproxy -v')
|
||||||
|
|
||||||
|
|
@ -69,5 +75,6 @@ def main():
|
||||||
|
|
||||||
start()
|
start()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,28 @@
|
||||||
from easymapping import HaproxyConfigGenerator
|
|
||||||
from functions import Functions, Consts
|
|
||||||
import yaml
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import base64
|
import base64
|
||||||
import docker
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
|
import docker
|
||||||
|
import yaml
|
||||||
from kubernetes import client, config
|
from kubernetes import client, config
|
||||||
from kubernetes.client.rest import ApiException
|
from kubernetes.client.rest import ApiException
|
||||||
|
|
||||||
class ContainerEnv:
|
from easymapping import HaproxyConfigGenerator
|
||||||
@staticmethod
|
from functions import Functions, Consts, ContainerEnv
|
||||||
def read():
|
|
||||||
env_vars = {
|
|
||||||
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
|
|
||||||
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE").lower() if os.getenv("EASYHAPROXY_SSL_MODE") else '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"),
|
|
||||||
"server": os.getenv("EASYHAPROXY_LETSENCRYPT_SERVER", "false").lower() in ["true", "1", "yes"]
|
|
||||||
}
|
|
||||||
|
|
||||||
return env_vars
|
|
||||||
|
|
||||||
|
|
||||||
class ProcessorInterface:
|
class ProcessorInterface:
|
||||||
static_file = Consts.easyhaproxy_config
|
static_file = Consts.easyhaproxy_config
|
||||||
|
|
||||||
def __init__(self, filename = None):
|
def __init__(self, filename=None):
|
||||||
|
self.certbot_hosts = None
|
||||||
|
self.parsed_object = None
|
||||||
|
self.cfg = None
|
||||||
|
self.hosts = None
|
||||||
|
self.cfg = None
|
||||||
|
self.certbot_hosts = None
|
||||||
|
self.hosts = None
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
|
self.label = ContainerEnv.read()['lookup_label']
|
||||||
self.refresh()
|
self.refresh()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -53,11 +36,12 @@ class ProcessorInterface:
|
||||||
elif mode == "kubernetes":
|
elif mode == "kubernetes":
|
||||||
return Kubernetes()
|
return Kubernetes()
|
||||||
else:
|
else:
|
||||||
Functions.log("EASYHAPROXY", Functions.FATAL, "Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % (mode))
|
Functions.log("EASYHAPROXY", Functions.FATAL,
|
||||||
|
"Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % mode)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
self.letsencrypt_hosts = None
|
self.certbot_hosts = None
|
||||||
self.parsed_object = None
|
self.parsed_object = None
|
||||||
self.cfg = None
|
self.cfg = None
|
||||||
self.hosts = None
|
self.hosts = None
|
||||||
|
|
@ -65,14 +49,14 @@ class ProcessorInterface:
|
||||||
self.parse()
|
self.parse()
|
||||||
|
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
#Abstract
|
# Abstract
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
||||||
|
|
||||||
def get_letsencrypt_hosts(self):
|
def get_certbot_hosts(self):
|
||||||
return self.letsencrypt_hosts
|
return self.certbot_hosts
|
||||||
|
|
||||||
def get_hosts(self):
|
def get_hosts(self):
|
||||||
return self.hosts
|
return self.hosts
|
||||||
|
|
@ -80,7 +64,7 @@ class ProcessorInterface:
|
||||||
def get_parsed_object(self):
|
def get_parsed_object(self):
|
||||||
return self.parsed_object
|
return self.parsed_object
|
||||||
|
|
||||||
def get_certs(self, key = None):
|
def get_certs(self, key=None):
|
||||||
if key is None:
|
if key is None:
|
||||||
return self.cfg.certs
|
return self.cfg.certs
|
||||||
else:
|
else:
|
||||||
|
|
@ -88,7 +72,7 @@ class ProcessorInterface:
|
||||||
|
|
||||||
def get_haproxy_conf(self):
|
def get_haproxy_conf(self):
|
||||||
conf = self.cfg.generate(self.parsed_object)
|
conf = self.cfg.generate(self.parsed_object)
|
||||||
self.letsencrypt_hosts = self.cfg.letsencrypt_hosts
|
self.certbot_hosts = self.cfg.certbot_hosts
|
||||||
self.hosts = self.cfg.serving_hosts
|
self.hosts = self.cfg.serving_hosts
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
|
|
@ -101,6 +85,13 @@ class ProcessorInterface:
|
||||||
|
|
||||||
|
|
||||||
class Static(ProcessorInterface):
|
class Static(ProcessorInterface):
|
||||||
|
def __init__(self, filename=None):
|
||||||
|
self.parsed_object = None
|
||||||
|
self.static_content = None
|
||||||
|
self.static_content = None
|
||||||
|
self.cfg = None
|
||||||
|
super().__init__(filename)
|
||||||
|
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
self.parsed_object = {}
|
self.parsed_object = {}
|
||||||
self.static_content = None
|
self.static_content = None
|
||||||
|
|
@ -110,11 +101,11 @@ class Static(ProcessorInterface):
|
||||||
|
|
||||||
def get_hosts(self):
|
def get_hosts(self):
|
||||||
hosts = []
|
hosts = []
|
||||||
for object in self.get_parsed_object():
|
for obj in self.get_parsed_object():
|
||||||
if "hosts" not in object:
|
if "hosts" not in obj:
|
||||||
continue
|
continue
|
||||||
for host in object["hosts"].keys():
|
for host in obj["hosts"].keys():
|
||||||
hosts.append("%s:%s" % (host, object["port"]))
|
hosts.append("%s:%s" % (host, obj["port"]))
|
||||||
return hosts
|
return hosts
|
||||||
|
|
||||||
def parse(self):
|
def parse(self):
|
||||||
|
|
@ -123,18 +114,21 @@ class Static(ProcessorInterface):
|
||||||
|
|
||||||
|
|
||||||
class Docker(ProcessorInterface):
|
class Docker(ProcessorInterface):
|
||||||
def __init__(self, filename = None):
|
def __init__(self, filename=None):
|
||||||
|
self.parsed_object = None
|
||||||
self.client = docker.from_env()
|
self.client = docker.from_env()
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
try:
|
try:
|
||||||
ha_proxy_network_name = next(iter(self.client.containers.get(socket.gethostname()).attrs["NetworkSettings"]["Networks"]))
|
ha_proxy_network_name = next(
|
||||||
|
iter(self.client.containers.get(socket.gethostname()).attrs["NetworkSettings"]["Networks"]))
|
||||||
except:
|
except:
|
||||||
# HAProxy is not running in a container, get first container network
|
# HAProxy is not running in a container, get first container network
|
||||||
if len(self.client.containers.list()) == 0:
|
if len(self.client.containers.list()) == 0:
|
||||||
return
|
return
|
||||||
ha_proxy_network_name = next(iter(self.client.containers.get(self.client.containers.list()[0].name).attrs["NetworkSettings"]["Networks"]))
|
ha_proxy_network_name = next(iter(
|
||||||
|
self.client.containers.get(self.client.containers.list()[0].name).attrs["NetworkSettings"]["Networks"]))
|
||||||
|
|
||||||
ha_proxy_network = self.client.networks.get(ha_proxy_network_name)
|
ha_proxy_network = self.client.networks.get(ha_proxy_network_name)
|
||||||
|
|
||||||
|
|
@ -150,27 +144,42 @@ class Docker(ProcessorInterface):
|
||||||
|
|
||||||
|
|
||||||
class Swarm(ProcessorInterface):
|
class Swarm(ProcessorInterface):
|
||||||
def __init__(self, filename = None):
|
def __init__(self, filename=None):
|
||||||
|
self.parsed_object = None
|
||||||
self.client = docker.from_env()
|
self.client = docker.from_env()
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
ha_proxy_service_name = self.client.containers.get(socket.gethostname()).name.split('.')[0]
|
ha_proxy_service_name = self.client.containers.get(socket.gethostname()).name.split('.')[0]
|
||||||
|
ha_proxy_network_id = None
|
||||||
|
swarm_ingress_id = None
|
||||||
|
|
||||||
|
# Get the HAProxy network and the ingress network
|
||||||
for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]:
|
for endpoint in self.client.services.get(ha_proxy_service_name).attrs['Endpoint']["VirtualIPs"]:
|
||||||
ha_proxy_network_id = endpoint["NetworkID"]
|
network_name = self.client.networks.get(endpoint["NetworkID"]).name
|
||||||
if self.client.networks.get(ha_proxy_network_id).name != 'ingress':
|
if swarm_ingress_id is None and network_name == 'ingress':
|
||||||
|
swarm_ingress_id = endpoint["NetworkID"]
|
||||||
|
if ha_proxy_network_id is None and network_name != 'ingress':
|
||||||
|
ha_proxy_network_id = endpoint["NetworkID"]
|
||||||
|
if ha_proxy_network_id is not None and swarm_ingress_id is not None:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Check if the service is attached to the HAProxy network
|
||||||
self.parsed_object = {}
|
self.parsed_object = {}
|
||||||
for service in self.client.services.list():
|
for service in self.client.services.list():
|
||||||
|
if not any(self.label in key for key in service.attrs["Spec"]["Labels"]):
|
||||||
|
continue
|
||||||
|
|
||||||
ip_address = None
|
ip_address = None
|
||||||
network_list = []
|
network_list = []
|
||||||
for endpoint in service.attrs["Endpoint"]["VirtualIPs"]:
|
for endpoint in service.attrs["Endpoint"]["VirtualIPs"]:
|
||||||
if ha_proxy_network_id == endpoint["NetworkID"]:
|
if ha_proxy_network_id == endpoint["NetworkID"]:
|
||||||
ip_address = endpoint["Addr"].split("/")[0]
|
ip_address = endpoint["Addr"].split("/")[0]
|
||||||
break
|
break
|
||||||
network_list.append(endpoint["NetworkID"])
|
elif swarm_ingress_id != endpoint["NetworkID"]:
|
||||||
|
network_list.append(endpoint["NetworkID"])
|
||||||
|
|
||||||
|
# Attach the service to the HAProxy network
|
||||||
if ip_address is None:
|
if ip_address is None:
|
||||||
network_list.append(ha_proxy_network_id)
|
network_list.append(ha_proxy_network_id)
|
||||||
service.update(networks = network_list)
|
service.update(networks = network_list)
|
||||||
|
|
@ -180,7 +189,8 @@ class Swarm(ProcessorInterface):
|
||||||
|
|
||||||
|
|
||||||
class Kubernetes(ProcessorInterface):
|
class Kubernetes(ProcessorInterface):
|
||||||
def __init__(self, filename = None):
|
def __init__(self, filename=None):
|
||||||
|
self.parsed_object = None
|
||||||
config.load_incluster_config()
|
config.load_incluster_config()
|
||||||
config.verify_ssl = False
|
config.verify_ssl = False
|
||||||
self.api_instance = client.CoreV1Api()
|
self.api_instance = client.CoreV1Api()
|
||||||
|
|
@ -188,9 +198,9 @@ class Kubernetes(ProcessorInterface):
|
||||||
self.cert_cache = {}
|
self.cert_cache = {}
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
def _check_annotation(self, annotations, key):
|
def _check_annotation(self, annotations, key, default=None):
|
||||||
if key not in annotations:
|
if key not in annotations:
|
||||||
return None
|
return default
|
||||||
return annotations[key]
|
return annotations[key]
|
||||||
|
|
||||||
def inspect_network(self):
|
def inspect_network(self):
|
||||||
|
|
@ -206,18 +216,14 @@ class Kubernetes(ProcessorInterface):
|
||||||
|
|
||||||
ssl_hosts = []
|
ssl_hosts = []
|
||||||
|
|
||||||
letsencrypt = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.letsencrypt")
|
certbot = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.certbot")
|
||||||
redirect_ssl = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect_ssl")
|
redirect_ssl = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect_ssl")
|
||||||
redirect = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect")
|
redirect = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect")
|
||||||
mode = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.mode")
|
mode = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.mode")
|
||||||
listen_port = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.listen_port")
|
listen_port = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.listen_port", 80)
|
||||||
if listen_port is None:
|
|
||||||
listen_port = 80
|
|
||||||
|
|
||||||
data = {}
|
data = {"creation_timestamp": ingress.metadata.creation_timestamp.strftime("%x %X"),
|
||||||
data["creation_timestamp"] = ingress.metadata.creation_timestamp.strftime("%x %X")
|
"resource_version": ingress.metadata.resource_version, "namespace": ingress.metadata.namespace}
|
||||||
data["resource_version"] = ingress.metadata.resource_version
|
|
||||||
data["namespace"] = ingress.metadata.namespace
|
|
||||||
|
|
||||||
ingress_name = ingress.metadata.namespace
|
ingress_name = ingress.metadata.namespace
|
||||||
|
|
||||||
|
|
@ -232,32 +238,36 @@ class Kubernetes(ProcessorInterface):
|
||||||
self.cert_cache[tls.secret_name] = secret.data
|
self.cert_cache[tls.secret_name] = secret.data
|
||||||
Functions.save(
|
Functions.save(
|
||||||
"{0}/{1}.pem".format(Consts.certs_haproxy, tls.secret_name),
|
"{0}/{1}.pem".format(Consts.certs_haproxy, tls.secret_name),
|
||||||
base64.b64decode(secret.data["tls.crt"]).decode('ascii') + "\n" + base64.b64decode(secret.data["tls.key"]).decode('ascii')
|
base64.b64decode(secret.data["tls.crt"]).decode('ascii') + "\n" + base64.b64decode(
|
||||||
|
secret.data["tls.key"]).decode('ascii')
|
||||||
)
|
)
|
||||||
|
|
||||||
ssl_hosts.extend(tls.hosts)
|
ssl_hosts.extend(tls.hosts)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Functions.log("EASYHAPROXY", Functions.WARN, "Ingress %s - Get secret failed: '%s'" % (ingress_name, e))
|
Functions.log("EASYHAPROXY", Functions.WARN,
|
||||||
|
"Ingress %s - Get secret failed: '%s'" % (ingress_name, e))
|
||||||
|
|
||||||
Functions.log("EASYHAPROXY", Functions.TRACE, "Ingress %s - SSL Hosts found '%s'" % (ingress_name, ssl_hosts))
|
Functions.log("EASYHAPROXY", Functions.TRACE,
|
||||||
|
"Ingress %s - SSL Hosts found '%s'" % (ingress_name, ssl_hosts))
|
||||||
|
|
||||||
for rule in ingress.spec.rules:
|
for rule in ingress.spec.rules:
|
||||||
rule_data = {}
|
rule_data = {}
|
||||||
port_number = rule.http.paths[0].backend.service.port.number
|
port_number = rule.http.paths[0].backend.service.port.number
|
||||||
definition = "easyhaproxy.%s_%s" % (rule.host.replace(".", "-"), port_number)
|
definition = "easyhaproxy.%s_%s" % (rule.host.replace(".", "-"), port_number)
|
||||||
rule_data["%s.host" % (definition)] = rule.host
|
rule_data["%s.host" % definition] = rule.host
|
||||||
rule_data["%s.port" % (definition)] = listen_port
|
rule_data["%s.port" % definition] = listen_port
|
||||||
rule_data["%s.localport" % (definition)] = port_number
|
rule_data["%s.localport" % definition] = port_number
|
||||||
if rule.host in ssl_hosts:
|
if rule.host in ssl_hosts:
|
||||||
rule_data["%s.clone_to_ssl" % (definition)] = 'true'
|
rule_data["%s.clone_to_ssl" % definition] = 'true'
|
||||||
if redirect_ssl is not None:
|
if redirect_ssl is not None:
|
||||||
rule_data["%s.redirect_ssl" % (definition)] = redirect_ssl
|
rule_data["%s.redirect_ssl" % definition] = redirect_ssl
|
||||||
if letsencrypt is not None:
|
if certbot is not None:
|
||||||
rule_data["%s.letsencrypt" % (definition)] = letsencrypt
|
rule_data["%s.certbot" % definition] = certbot
|
||||||
if redirect is not None:
|
if redirect is not None:
|
||||||
rule_data["%s.redirect" % (definition)] = redirect
|
rule_data["%s.redirect" % definition] = redirect
|
||||||
if mode is not None:
|
if mode is not None:
|
||||||
rule_data["%s.mode" % (definition)] = mode
|
rule_data["%s.mode" % definition] = mode
|
||||||
|
rule_data["%s.balance" % definition] = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.balance", "roundrobin")
|
||||||
|
|
||||||
service_name = rule.http.paths[0].backend.service.name
|
service_name = rule.http.paths[0].backend.service.name
|
||||||
try:
|
try:
|
||||||
|
|
@ -265,13 +275,10 @@ class Kubernetes(ProcessorInterface):
|
||||||
cluster_ip = api_response.spec.cluster_ip
|
cluster_ip = api_response.spec.cluster_ip
|
||||||
except ApiException as e:
|
except ApiException as e:
|
||||||
cluster_ip = None
|
cluster_ip = None
|
||||||
Functions.log("EASYHAPROXY", Functions.WARN, "Ingress %s - Service %s - Failed: '%s'" % (ingress_name, service_name, e))
|
Functions.log("EASYHAPROXY", Functions.WARN,
|
||||||
|
"Ingress %s - Service %s - Failed: '%s'" % (ingress_name, service_name, e))
|
||||||
|
|
||||||
if cluster_ip is not None:
|
if cluster_ip is not None:
|
||||||
if cluster_ip not in self.parsed_object.keys():
|
if cluster_ip not in self.parsed_object.keys():
|
||||||
self.parsed_object[cluster_ip] = data
|
self.parsed_object[cluster_ip] = data
|
||||||
self.parsed_object[cluster_ip].update(rule_data)
|
self.parsed_object[cluster_ip].update(rule_data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,4 @@ pytest
|
||||||
docker
|
docker
|
||||||
kubernetes
|
kubernetes
|
||||||
deepdiff
|
deepdiff
|
||||||
|
pyopenssl
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{% if "ssl" in o %}
|
{% if "ssl" in o %}
|
||||||
bind *:{{ o["port"] }} ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:{{ o["port"] }} ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
{% elif "h2" in o and o["h2"] %}
|
{% elif "h2" in o and o["h2"] %}
|
||||||
bind *:{{ o["port"] }} proto h2
|
bind *:{{ o["port"] }} proto h2
|
||||||
option http-use-htx
|
option http-use-htx
|
||||||
|
|
|
||||||
|
|
@ -5,16 +5,16 @@
|
||||||
|
|
||||||
{%- for k in o["hosts"] %}
|
{%- for k in o["hosts"] %}
|
||||||
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
|
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
|
||||||
{% set letsencrypt = o["hosts"][k]["letsencrypt"] %}
|
{% set certbot = o["hosts"][k]["certbot"] %}
|
||||||
|
|
||||||
acl is_rule_{{ host }}_1 hdr(host) -i {{ k }}
|
acl is_rule_{{ host }}_1 hdr(host) -i {{ k }}
|
||||||
acl is_rule_{{ host }}_2 hdr(host) -i {{ k }}:{{ o["port"] }}
|
acl is_rule_{{ host }}_2 hdr(host) -i {{ k }}:{{ o["port"] }}
|
||||||
{% if letsencrypt %}
|
{% if certbot %}
|
||||||
acl is_letsencrypt_{{ host }} path_beg /.well-known/acme-challenge/
|
acl is_certbot_{{ 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
|
use_backend certbot_backend if is_certbot_{{ host }} is_rule_{{ host }}_1 OR is_certbot_{{ host }} is_rule_{{ host }}_2
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if o["hosts"][k]["redirect_ssl"] %}
|
{% 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
|
http-request redirect scheme https code 301 if {% if certbot %}!is_certbot_{{ host }} {% endif %}is_rule_{{ host }}_1 OR {% if certbot %}!is_certbot_{{ host }} {% endif %}is_rule_{{ host }}_2
|
||||||
{% else %}
|
{% else %}
|
||||||
use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2
|
use_backend srv_{{ host }} if is_rule_{{ host }}_1 OR is_rule_{{ host }}_2
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,18 @@
|
||||||
|
{% set log_definition = data["logLevel"] | default({}) %}
|
||||||
|
{% set log_level = log_definition["haproxy"] | default("INFO") | upper %}
|
||||||
|
{% if log_level == "TRACE" or log_level == "DEBUG" %}
|
||||||
|
{% set haproxy_log_level = "debug" %}
|
||||||
|
{% elif log_level == "INFO" %}
|
||||||
|
{% set haproxy_log_level = "info" %}
|
||||||
|
{% elif log_level == "WARN" %}
|
||||||
|
{% set haproxy_log_level = "warning" %}
|
||||||
|
{% elif log_level == "ERROR" %}
|
||||||
|
{% set haproxy_log_level = "err" %}
|
||||||
|
{% elif log_level == "FATAL" %}
|
||||||
|
{% set haproxy_log_level = "crit" %}
|
||||||
|
{% endif %}
|
||||||
global
|
global
|
||||||
log stdout format raw local0 info
|
log stdout format raw local0 {{ haproxy_log_level }}
|
||||||
maxconn 2000
|
maxconn 2000
|
||||||
{% if data["ssl_mode"] == "strict" %}
|
{% if data["ssl_mode"] == "strict" %}
|
||||||
{% include "ssl_strict.j2" %}
|
{% include "ssl_strict.j2" %}
|
||||||
|
|
@ -59,7 +72,7 @@ frontend {{ mode }}_in_{{ o["port"] }}
|
||||||
{% for k in o["hosts"] -%}
|
{% for k in o["hosts"] -%}
|
||||||
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
|
{% set host = k.replace(".", "_") + "_{0}".format(o["port"]) %}
|
||||||
backend srv_{{ host }}
|
backend srv_{{ host }}
|
||||||
balance roundrobin
|
balance {{ o["balance"] | default("roundrobin") }}
|
||||||
mode {{ mode }}
|
mode {{ mode }}
|
||||||
{% if mode == "http" %}
|
{% if mode == "http" %}
|
||||||
option forwardfor
|
option forwardfor
|
||||||
|
|
@ -67,15 +80,15 @@ backend srv_{{ host }}
|
||||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
{% elif mode == "tcp" %}
|
{% elif mode == "tcp" %}
|
||||||
option tcp-check
|
option tcp-check
|
||||||
tcp-check connect{{ " ssl" if o["health-check"] == "ssl" }}
|
tcp-check connect{{ " ssl" if o["ssl-check"] == "ssl" }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for c in o["hosts"][k]["containers"] %}
|
{% for c in o["hosts"][k]["containers"] %}
|
||||||
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["health-check"] == "ssl" }}
|
server srv-{{ loop.index0 }} {{ c }} check weight 1{{ " verify none" if o["ssl-check"] == "ssl" }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
||||||
|
|
||||||
import easymapping
|
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ backend srv_stats
|
||||||
server Local 127.0.0.1:1936
|
server Local 127.0.0.1:1936
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local
|
acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local
|
||||||
|
|
@ -84,8 +84,8 @@ frontend http_in_90
|
||||||
|
|
||||||
acl is_rule_host2_local_90_1 hdr(host) -i host2.local
|
acl is_rule_host2_local_90_1 hdr(host) -i host2.local
|
||||||
acl is_rule_host2_local_90_2 hdr(host) -i host2.local:90
|
acl is_rule_host2_local_90_2 hdr(host) -i host2.local:90
|
||||||
acl is_letsencrypt_host2_local_90 path_beg /.well-known/acme-challenge/
|
acl is_certbot_host2_local_90 path_beg /.well-known/acme-challenge/
|
||||||
use_backend letsencrypt_backend if is_letsencrypt_host2_local_90 is_rule_host2_local_90_1 OR is_letsencrypt_host2_local_90 is_rule_host2_local_90_2
|
use_backend certbot_backend if is_certbot_host2_local_90 is_rule_host2_local_90_1 OR is_certbot_host2_local_90 is_rule_host2_local_90_2
|
||||||
use_backend srv_host2_local_90 if is_rule_host2_local_90_1 OR is_rule_host2_local_90_2
|
use_backend srv_host2_local_90 if is_rule_host2_local_90_1 OR is_rule_host2_local_90_2
|
||||||
|
|
||||||
backend srv_host2_local_90
|
backend srv_host2_local_90
|
||||||
|
|
@ -96,6 +96,6 @@ backend srv_host2_local_90
|
||||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
server srv-0 test_processor_docker:9000 check weight 1
|
server srv-0 test_processor_docker:9000 check weight 1
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,6 @@ defaults
|
||||||
timeout server 10m
|
timeout server 10m
|
||||||
|
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,9 @@ frontend http_in_80
|
||||||
|
|
||||||
acl is_rule_test_example_org_80_1 hdr(host) -i test.example.org
|
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_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/
|
acl is_certbot_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
|
use_backend certbot_backend if is_certbot_test_example_org_80 is_rule_test_example_org_80_1 OR is_certbot_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
|
http-request redirect scheme https code 301 if !is_certbot_test_example_org_80 is_rule_test_example_org_80_1 OR !is_certbot_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_1 hdr(host) -i test2.example.org
|
||||||
acl is_rule_test2_example_org_80_2 hdr(host) -i test2.example.org:80
|
acl is_rule_test2_example_org_80_2 hdr(host) -i test2.example.org:80
|
||||||
|
|
@ -74,7 +74,7 @@ backend srv_test2_example_org_80
|
||||||
server srv-0 83d57d592e26:8080 check weight 1
|
server srv-0 83d57d592e26:8080 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org
|
acl is_rule_test_example_org_443_1 hdr(host) -i test.example.org
|
||||||
|
|
@ -90,6 +90,6 @@ backend srv_test_example_org_443
|
||||||
server srv-0 f5c645a0dfc6:80 check weight 1 verify none
|
server srv-0 f5c645a0dfc6:80 check weight 1 verify none
|
||||||
server srv-1 b63438410b6a:80 check weight 1 verify none
|
server srv-1 b63438410b6a:80 check weight 1 verify none
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,6 @@ backend srv_www_helloworld_com_19901
|
||||||
server srv-0 test_nginx.2.t5r94mjlced7m3t5orfjbowmm:80 check weight 1
|
server srv-0 test_nginx.2.t5r94mjlced7m3t5orfjbowmm:80 check weight 1
|
||||||
server srv-1 test_nginx.1.p552hqxkdx88narjrp5kouwb2:80 check weight 1
|
server srv-1 test_nginx.1.p552hqxkdx88narjrp5kouwb2:80 check weight 1
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,6 @@ backend srv_hello_com_19901
|
||||||
server srv-0 3e63154954b0:80 check weight 1
|
server srv-0 3e63154954b0:80 check weight 1
|
||||||
server srv-1 eb294c110eb1:80 check weight 1
|
server srv-1 eb294c110eb1:80 check weight 1
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ backend srv_host1_local_80
|
||||||
server srv-0 5b69bc7fea1b:80 check weight 1
|
server srv-0 5b69bc7fea1b:80 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_host2_local_443_1 hdr(host) -i host2.local
|
acl is_rule_host2_local_443_1 hdr(host) -i host2.local
|
||||||
|
|
@ -75,6 +75,6 @@ backend srv_host1_local_443
|
||||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
server srv-0 5b69bc7fea1b:8080 check weight 1
|
server srv-0 5b69bc7fea1b:8080 check weight 1
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,6 @@ backend srv_agent_quantum_local_31339
|
||||||
tcp-check connect ssl
|
tcp-check connect ssl
|
||||||
server srv-0 test_agent:9001 check weight 1 verify none
|
server srv-0 test_agent:9001 check weight 1 verify none
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ frontend http_in_31337
|
||||||
|
|
||||||
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_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_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/
|
acl is_certbot_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 certbot_backend if is_certbot_node-exporter_quantum_example_org_31337 is_rule_node-exporter_quantum_example_org_31337_1 OR is_certbot_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
|
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
|
backend srv_cadvisor_quantum_example_org_31337
|
||||||
|
|
@ -67,7 +67,7 @@ backend srv_node-exporter_quantum_example_org_31337
|
||||||
server srv-0 my-stack_node-exporter:9100 check weight 1
|
server srv-0 my-stack_node-exporter:9100 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
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.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 somehost.com }
|
||||||
|
|
@ -119,6 +119,6 @@ backend srv_www_somehost_com_br_80
|
||||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
server srv-0 some-service:80 check weight 1
|
server srv-0 some-service:80 check weight 1
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,6 @@ backend srv_stats
|
||||||
mode http
|
mode http
|
||||||
server Local 127.0.0.1:1936
|
server Local 127.0.0.1:1936
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,6 @@ defaults
|
||||||
timeout server 10m
|
timeout server 10m
|
||||||
|
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ frontend http_in_80
|
||||||
|
|
||||||
acl is_rule_host1_com_br_80_1 hdr(host) -i 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_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/
|
acl is_certbot_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 certbot_backend if is_certbot_host1_com_br_80 is_rule_host1_com_br_80_1 OR is_certbot_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
|
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_1 hdr(host) -i host2.com.br
|
||||||
|
|
@ -74,7 +74,7 @@ backend srv_host2_com_br_80
|
||||||
server srv-0 other:3000 check weight 1
|
server srv-0 other:3000 check weight 1
|
||||||
|
|
||||||
frontend http_in_443
|
frontend http_in_443
|
||||||
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
bind *:443 ssl crt /certs/certbot/ alpn h2,http/1.1 crt /certs/haproxy/ alpn h2,http/1.1
|
||||||
mode http
|
mode http
|
||||||
|
|
||||||
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
|
acl is_rule_host1_com_br_443_1 hdr(host) -i host1.com.br
|
||||||
|
|
@ -105,6 +105,6 @@ backend srv_host3_com_br_8080
|
||||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||||
server srv-0 domain:8181 check weight 1
|
server srv-0 domain:8181 check weight 1
|
||||||
|
|
||||||
backend letsencrypt_backend
|
backend certbot_backend
|
||||||
mode http
|
mode http
|
||||||
server certbot 127.0.0.1:2080
|
server certbot 127.0.0.1:2080
|
||||||
|
|
|
||||||
2
src/tests/fixtures/00_haproxy.cfg
vendored
Normal file
2
src/tests/fixtures/00_haproxy.cfg
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
global
|
||||||
|
maxconn 4000
|
||||||
2
src/tests/fixtures/10_haproxy.cfg
vendored
Normal file
2
src/tests/fixtures/10_haproxy.cfg
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
global
|
||||||
|
maxconn 5000
|
||||||
7
src/tests/fixtures/run_bash.sh
vendored
Executable file
7
src/tests/fixtures/run_bash.sh
vendored
Executable file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Just return the exit code
|
||||||
|
echo "Processing run_bash.sh"
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
exit "$1"
|
||||||
|
fi
|
||||||
2
src/tests/fixtures/services
vendored
2
src/tests/fixtures/services
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
|
{"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_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_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_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.certbot":"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"},
|
"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="}}
|
"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="}}
|
||||||
2
src/tests/fixtures/services-changed-label
vendored
2
src/tests/fixtures/services-changed-label
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
|
{"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_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_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_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.certbot":"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"},
|
"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="}}
|
"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
4
src/tests/fixtures/services-letsencrypt
vendored
|
|
@ -1,4 +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"},
|
{"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.certbot":"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"},
|
"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"},
|
"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.certbot":"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"}}
|
"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-tcp
vendored
2
src/tests/fixtures/services-tcp
vendored
|
|
@ -1,2 +1,2 @@
|
||||||
{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.health-check":"ssl"},
|
{"test_agent": {"easyhaproxy.agent.host":"agent.quantum.local","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"test", "easyhaproxy.agent.ssl-check":"ssl"},
|
||||||
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}}
|
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy:local","com.docker.stack.namespace":"test"}}
|
||||||
2
src/tests/fixtures/static.yml
vendored
2
src/tests/fixtures/static.yml
vendored
|
|
@ -11,7 +11,7 @@ easymapping:
|
||||||
host1.com.br:
|
host1.com.br:
|
||||||
containers:
|
containers:
|
||||||
- container:5000
|
- container:5000
|
||||||
letsencrypt: true
|
certbot: true
|
||||||
host2.com.br:
|
host2.com.br:
|
||||||
containers:
|
containers:
|
||||||
- other:3000
|
- other:3000
|
||||||
|
|
|
||||||
|
|
@ -1,118 +1,243 @@
|
||||||
import pytest
|
|
||||||
import os
|
import os
|
||||||
from processor import ContainerEnv
|
|
||||||
|
from functions import Functions, ContainerEnv
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_empty():
|
def test_container_env_empty():
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy"
|
"lookup_label": "easyhaproxy",
|
||||||
} == ContainerEnv.read()
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {"autoconfig": "",
|
||||||
|
"eab_hmac_key": "",
|
||||||
|
"eab_kid": "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60}
|
||||||
|
} == ContainerEnv.read()
|
||||||
|
|
||||||
# os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
# os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_customerrors():
|
def test_container_env_customerrors():
|
||||||
os.environ['HAPROXY_CUSTOMERRORS'] = 'true'
|
os.environ['HAPROXY_CUSTOMERRORS'] = 'true'
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": True,
|
"customerrors": True,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy"
|
"lookup_label": "easyhaproxy",
|
||||||
} == ContainerEnv.read()
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {"autoconfig": "",
|
||||||
|
"eab_hmac_key": "",
|
||||||
|
"eab_kid": "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_CUSTOMERRORS'] = ''
|
del os.environ['HAPROXY_CUSTOMERRORS']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_sslmode():
|
def test_container_env_sslmode():
|
||||||
os.environ['EASYHAPROXY_SSL_MODE'] = 'STRICT'
|
os.environ['EASYHAPROXY_SSL_MODE'] = 'STRICT'
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "strict",
|
"ssl_mode": "strict",
|
||||||
"lookup_label": "easyhaproxy"
|
"lookup_label": "easyhaproxy",
|
||||||
} == ContainerEnv.read()
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {"autoconfig": "",
|
||||||
|
"eab_hmac_key": "",
|
||||||
|
"eab_kid": "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_SSL_MODE'] = ''
|
del os.environ['EASYHAPROXY_SSL_MODE']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats():
|
def test_container_env_stats():
|
||||||
os.environ['HAPROXY_USERNAME'] = 'abc'
|
os.environ['HAPROXY_USERNAME'] = 'abc'
|
||||||
os.environ['HAPROXY_STATS_PORT'] = '2101'
|
os.environ['HAPROXY_STATS_PORT'] = '2101'
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
} == ContainerEnv.read()
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {"autoconfig": "",
|
||||||
|
"eab_hmac_key": "",
|
||||||
|
"eab_kid": "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_USERNAME'] = ''
|
del os.environ['HAPROXY_USERNAME']
|
||||||
os.environ['HAPROXY_STATS_PORT'] = ''
|
del os.environ['HAPROXY_STATS_PORT']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats_password():
|
def test_container_env_stats_password():
|
||||||
os.environ['HAPROXY_PASSWORD'] = 'xyz'
|
os.environ['HAPROXY_PASSWORD'] = 'xyz'
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
"stats": {
|
"stats": {
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "xyz",
|
"password": "xyz",
|
||||||
"port": "1936"
|
"port": "1936"
|
||||||
|
|
||||||
}
|
},
|
||||||
} == ContainerEnv.read()
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {"autoconfig": "",
|
||||||
|
"eab_hmac_key": "",
|
||||||
|
"eab_kid": "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_PASSWORD'] = ''
|
del os.environ['HAPROXY_PASSWORD']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats_password():
|
def test_container_env_stats_password_2():
|
||||||
os.environ['HAPROXY_USERNAME'] = 'abc'
|
os.environ['HAPROXY_USERNAME'] = 'abc'
|
||||||
os.environ['HAPROXY_STATS_PORT'] = '2101'
|
os.environ['HAPROXY_STATS_PORT'] = '2101'
|
||||||
os.environ['HAPROXY_PASSWORD'] = 'xyz'
|
os.environ['HAPROXY_PASSWORD'] = 'xyz'
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
"stats": {
|
"stats": {
|
||||||
"username": "abc",
|
"username": "abc",
|
||||||
"password": "xyz",
|
"password": "xyz",
|
||||||
"port": "2101"
|
"port": "2101"
|
||||||
|
},
|
||||||
}
|
"logLevel": {
|
||||||
} == ContainerEnv.read()
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {"autoconfig": "",
|
||||||
|
"eab_hmac_key": "",
|
||||||
|
"eab_kid": "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['HAPROXY_USERNAME'] = ''
|
del os.environ['HAPROXY_USERNAME']
|
||||||
os.environ['HAPROXY_STATS_PORT'] = ''
|
del os.environ['HAPROXY_STATS_PORT']
|
||||||
os.environ['HAPROXY_PASSWORD'] = ''
|
del os.environ['HAPROXY_PASSWORD']
|
||||||
|
|
||||||
|
|
||||||
def test_container_env_stats_password():
|
def test_container_env_certbot_email():
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'acme@example.org'
|
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = 'acme@example.org'
|
||||||
|
try:
|
||||||
|
assert {
|
||||||
|
"customerrors": False,
|
||||||
|
"ssl_mode": "default",
|
||||||
|
"lookup_label": "easyhaproxy",
|
||||||
|
"logLevel": {
|
||||||
|
"easyhaproxy": Functions.DEBUG,
|
||||||
|
"haproxy": Functions.INFO,
|
||||||
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {
|
||||||
|
"autoconfig": "",
|
||||||
|
'eab_hmac_key': "",
|
||||||
|
'eab_kid': "",
|
||||||
|
"email": "acme@example.org",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60
|
||||||
|
}
|
||||||
|
} == ContainerEnv.read()
|
||||||
|
finally:
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_EMAIL']
|
||||||
|
|
||||||
|
|
||||||
|
def test_container_env_certbot_full():
|
||||||
|
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = 'acme@example.org'
|
||||||
|
os.environ['EASYHAPROXY_CERTBOT_SERVER'] = 'schema://url/a'
|
||||||
|
os.environ['EASYHAPROXY_CERTBOT_EAB_KID'] = 'eab_kid'
|
||||||
|
os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY'] = 'eab_hmac_key'
|
||||||
|
os.environ['EASYHAPROXY_CERTBOT_RETRY_COUNT'] = "10"
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
"letsencrypt": {
|
"logLevel": {
|
||||||
"email": "acme@example.org",
|
"easyhaproxy": Functions.DEBUG,
|
||||||
"server": False
|
"haproxy": Functions.INFO,
|
||||||
}
|
"certbot": Functions.DEBUG,
|
||||||
|
},
|
||||||
|
"certbot": {
|
||||||
|
"autoconfig": "",
|
||||||
|
"email": "acme@example.org",
|
||||||
|
"server": "schema://url/a",
|
||||||
|
'eab_hmac_key': 'eab_hmac_key',
|
||||||
|
'eab_kid': 'eab_kid',
|
||||||
|
'retry_count': 10
|
||||||
|
}
|
||||||
} == ContainerEnv.read()
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = ''
|
del os.environ['EASYHAPROXY_CERTBOT_EMAIL']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_SERVER']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_EAB_KID']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_EAB_HMAC_KEY']
|
||||||
|
del os.environ['EASYHAPROXY_CERTBOT_RETRY_COUNT']
|
||||||
|
|
||||||
def test_container_env_letsencrypt():
|
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'acme@example.org'
|
def test_container_log_level():
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_SERVER'] = 'true'
|
os.environ['CERTBOT_LOG_LEVEL'] = Functions.TRACE
|
||||||
|
os.environ['EASYHAPROXY_LOG_LEVEL'] = Functions.ERROR
|
||||||
|
os.environ['HAPROXY_LOG_LEVEL'] = Functions.FATAL
|
||||||
try:
|
try:
|
||||||
assert {
|
assert {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"ssl_mode": "default",
|
"ssl_mode": "default",
|
||||||
"lookup_label": "easyhaproxy",
|
"lookup_label": "easyhaproxy",
|
||||||
"letsencrypt": {
|
"logLevel": {
|
||||||
"email": "acme@example.org",
|
"easyhaproxy": Functions.ERROR,
|
||||||
"server": True
|
"haproxy": Functions.FATAL,
|
||||||
}
|
"certbot": Functions.TRACE,
|
||||||
} == ContainerEnv.read()
|
},
|
||||||
|
"certbot": {
|
||||||
|
"autoconfig": "",
|
||||||
|
'eab_hmac_key': "",
|
||||||
|
'eab_kid': "",
|
||||||
|
"email": "",
|
||||||
|
"server": False,
|
||||||
|
"retry_count": 60
|
||||||
|
}
|
||||||
|
} == ContainerEnv.read()
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = ''
|
del os.environ['CERTBOT_LOG_LEVEL']
|
||||||
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
|
del os.environ['HAPROXY_LOG_LEVEL']
|
||||||
|
|
|
||||||
48
src/tests/test_daemonize.py
Normal file
48
src/tests/test_daemonize.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
from functions import DaemonizeHAProxy, Functions
|
||||||
|
|
||||||
|
|
||||||
|
def test_daemonize_haproxy():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
assert daemon is not None
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_check_config():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
filed = daemon.get_custom_config_files()
|
||||||
|
assert filed == {}
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_start():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
command = daemon.get_haproxy_command("start")
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /var/run/haproxy.sock"
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_reload():
|
||||||
|
daemon = DaemonizeHAProxy()
|
||||||
|
command = daemon.get_haproxy_command("reload")
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -x /var/run/haproxy.sock -sf "
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_check_config():
|
||||||
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
|
filed = daemon.get_custom_config_files()
|
||||||
|
assert filed == {
|
||||||
|
os.path.dirname(__file__) + "/fixtures/00_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/00_haproxy.cfg"),
|
||||||
|
os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg": os.path.getmtime(os.path.dirname(__file__) + "/fixtures/10_haproxy.cfg")
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_start():
|
||||||
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
|
command = daemon.get_haproxy_command("start")
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p /run/haproxy.pid -S /var/run/haproxy.sock" % (os.path.dirname(__file__) + "/fixtures")
|
||||||
|
|
||||||
|
|
||||||
|
def test_daemonize_haproxy_get_haproxy_command_reload():
|
||||||
|
tmp_pid_file = "/tmp/tmp_pid.txt"
|
||||||
|
Functions.save(tmp_pid_file, "10")
|
||||||
|
|
||||||
|
try:
|
||||||
|
daemon = DaemonizeHAProxy(os.path.abspath(os.path.dirname(__file__)) + '/fixtures')
|
||||||
|
command = daemon.get_haproxy_command("reload", tmp_pid_file)
|
||||||
|
assert command == "/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg -f %s -p %s -x /var/run/haproxy.sock -sf %s" % (os.path.dirname(__file__) + "/fixtures", tmp_pid_file, 10)
|
||||||
|
finally:
|
||||||
|
os.remove(tmp_pid_file)
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import pytest
|
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
|
import pytest
|
||||||
|
|
||||||
from functions import Functions
|
from functions import Functions
|
||||||
from processor import ProcessorInterface
|
from processor import ProcessorInterface
|
||||||
from processor import Docker
|
|
||||||
|
|
||||||
|
|
||||||
def _get_hydrated_object(parsed_objects, lookup_key):
|
def _get_hydrated_object(parsed_objects, lookup_key):
|
||||||
|
|
@ -17,7 +18,6 @@ def _get_hydrated_object(parsed_objects, lookup_key):
|
||||||
|
|
||||||
|
|
||||||
def _get_ip_host(parsed_objects, lookup_key):
|
def _get_ip_host(parsed_objects, lookup_key):
|
||||||
hydrated_object = {}
|
|
||||||
for key in parsed_objects:
|
for key in parsed_objects:
|
||||||
for keys in parsed_objects[key]:
|
for keys in parsed_objects[key]:
|
||||||
if lookup_key in keys:
|
if lookup_key in keys:
|
||||||
|
|
@ -46,7 +46,7 @@ def test_processor_docker():
|
||||||
"easyhaproxy.http2.port": "90",
|
"easyhaproxy.http2.port": "90",
|
||||||
"easyhaproxy.http2.localport": "9000",
|
"easyhaproxy.http2.localport": "9000",
|
||||||
"easyhaproxy.http2.host": "host2.local",
|
"easyhaproxy.http2.host": "host2.local",
|
||||||
"easyhaproxy.http2.letsencrypt": "true",
|
"easyhaproxy.http2.certbot": "true",
|
||||||
})
|
})
|
||||||
container2 = client.containers.run("byjg/static-httpserver",
|
container2 = client.containers.run("byjg/static-httpserver",
|
||||||
name="test2_processor_docker",
|
name="test2_processor_docker",
|
||||||
|
|
@ -62,10 +62,10 @@ def test_processor_docker():
|
||||||
try:
|
try:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = 'docker@example.org'
|
os.environ['EASYHAPROXY_CERTBOT_EMAIL'] = 'docker@example.org'
|
||||||
|
|
||||||
static = ProcessorInterface.factory("docker")
|
static = ProcessorInterface.factory("docker")
|
||||||
assert static.get_letsencrypt_hosts() is None
|
assert static.get_certbot_hosts() is None
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
'easyhaproxy.http.host': 'host1.local',
|
'easyhaproxy.http.host': 'host1.local',
|
||||||
|
|
@ -74,7 +74,7 @@ def test_processor_docker():
|
||||||
'easyhaproxy.http2.host': 'host2.local',
|
'easyhaproxy.http2.host': 'host2.local',
|
||||||
'easyhaproxy.http2.localport': '9000',
|
'easyhaproxy.http2.localport': '9000',
|
||||||
'easyhaproxy.http2.port': '90',
|
'easyhaproxy.http2.port': '90',
|
||||||
'easyhaproxy.http2.letsencrypt': 'true',
|
'easyhaproxy.http2.certbot': 'true',
|
||||||
} == _get_hydrated_object(static.get_parsed_object(), "easyhaproxy.http")
|
} == _get_hydrated_object(static.get_parsed_object(), "easyhaproxy.http")
|
||||||
assert {
|
assert {
|
||||||
'easyhaproxy.ssl.host': 'hostssl.local',
|
'easyhaproxy.ssl.host': 'hostssl.local',
|
||||||
|
|
@ -90,7 +90,7 @@ def test_processor_docker():
|
||||||
assert haproxy_cfg == Functions.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/docker.txt")).replace("test_processor_docker", _get_ip_host(
|
assert haproxy_cfg == Functions.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/docker.txt")).replace("test_processor_docker", _get_ip_host(
|
||||||
static.get_parsed_object(), "easyhaproxy.http")).replace("test2_processor_docker", _get_ip_host(static.get_parsed_object(), "easyhaproxy.ssl"))
|
static.get_parsed_object(), "easyhaproxy.http")).replace("test2_processor_docker", _get_ip_host(static.get_parsed_object(), "easyhaproxy.ssl"))
|
||||||
|
|
||||||
assert static.get_letsencrypt_hosts() == ['host2.local']
|
assert static.get_certbot_hosts() == ['host2.local']
|
||||||
assert static.get_hosts() == [
|
assert static.get_hosts() == [
|
||||||
'hostssl.local:443',
|
'hostssl.local:443',
|
||||||
'host1.local:80',
|
'host1.local:80',
|
||||||
|
|
@ -100,7 +100,7 @@ def test_processor_docker():
|
||||||
'hostssl.local.pem': 'Some PEM Certificate'
|
'hostssl.local.pem': 'Some PEM Certificate'
|
||||||
}
|
}
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_LETSENCRYPT_EMAIL'] = ''
|
del os.environ['EASYHAPROXY_CERTBOT_EMAIL']
|
||||||
container.stop()
|
container.stop()
|
||||||
container2.stop()
|
container2.stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import json
|
|
||||||
import pytest
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import random
|
import random
|
||||||
|
import re
|
||||||
import string
|
import string
|
||||||
|
|
||||||
from functions import Functions
|
from functions import Functions
|
||||||
|
|
||||||
|
|
||||||
def test_functions_check_local_level():
|
def test_functions_check_local_level():
|
||||||
assert Functions.skip_log('CERTBOT', Functions.INFO) == False
|
assert Functions.skip_log('CERTBOT', Functions.INFO) == False
|
||||||
assert Functions.skip_log('HAPOROXY', Functions.INFO) == False
|
assert Functions.skip_log('HAPOROXY', Functions.INFO) == False
|
||||||
|
|
@ -13,15 +13,16 @@ def test_functions_check_local_level():
|
||||||
|
|
||||||
os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
os.environ['CERTBOT_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('CERTBOT', Functions.INFO) == True
|
assert Functions.skip_log('CERTBOT', Functions.INFO) == True
|
||||||
os.environ['CERTBOT_LOG_LEVEL'] = ''
|
del os.environ['CERTBOT_LOG_LEVEL']
|
||||||
|
|
||||||
os.environ['HAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['HAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('HAPROXY', Functions.INFO) == True
|
assert Functions.skip_log('HAPROXY', Functions.INFO) == True
|
||||||
os.environ['HAPROXY_LOG_LEVEL'] = ''
|
del os.environ['HAPROXY_LOG_LEVEL']
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == True
|
assert Functions.skip_log('EASYHAPROXY', Functions.INFO) == True
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = ''
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
|
|
||||||
|
|
||||||
def test_function_load_and_save():
|
def test_function_load_and_save():
|
||||||
filename = '/tmp/x.txt'
|
filename = '/tmp/x.txt'
|
||||||
|
|
@ -34,6 +35,7 @@ def test_function_load_and_save():
|
||||||
finally:
|
finally:
|
||||||
os.unlink(filename)
|
os.unlink(filename)
|
||||||
|
|
||||||
|
|
||||||
def test_functions_check_log_sanity():
|
def test_functions_check_log_sanity():
|
||||||
print()
|
print()
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 1")
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 1")
|
||||||
|
|
@ -51,51 +53,104 @@ def test_functions_check_log_sanity():
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Test 3", Functions.debug_log[1])
|
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Test 3", Functions.debug_log[1])
|
||||||
|
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
os.environ['EASYHAPROXY_LOG_LEVEL'] = 'warn'
|
||||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 4") # Should not log to debug
|
Functions.log(Functions.EASYHAPROXY_LOG, Functions.INFO, "Test 4") # Should not log to debug
|
||||||
assert len(Functions.debug_log) == 2
|
assert len(Functions.debug_log) == 2
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
os.environ['EASYHAPROXY_LOG_LEVEL'] = ''
|
del os.environ['EASYHAPROXY_LOG_LEVEL']
|
||||||
Functions.debug_log = None
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_log_output():
|
def test_functions_run_bash_log_output():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
Functions.debug_log = []
|
||||||
try:
|
try:
|
||||||
result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 1'", log_output=True, return_result=False)
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 1'", log_output=True,
|
||||||
|
return_result=False)
|
||||||
|
assert return_code == 0
|
||||||
assert result == []
|
assert result == []
|
||||||
assert len(Functions.debug_log) == 1
|
assert len(Functions.debug_log) == 1
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: test run 1", Functions.debug_log[0])
|
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: test run 1", Functions.debug_log[0])
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_no_log_output():
|
def test_functions_run_bash_no_log_output():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
Functions.debug_log = []
|
||||||
try:
|
try:
|
||||||
result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 2'", log_output=False, return_result=False)
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 2'", log_output=False,
|
||||||
|
return_result=False)
|
||||||
|
assert return_code == 0
|
||||||
assert result == []
|
assert result == []
|
||||||
assert len(Functions.debug_log) == 0
|
assert len(Functions.debug_log) == 0
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_return():
|
def test_functions_run_bash_return():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
Functions.debug_log = []
|
||||||
try:
|
try:
|
||||||
result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 3'", log_output=False, return_result=True)
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 3'", log_output=False,
|
||||||
|
return_result=True)
|
||||||
|
assert return_code == 0
|
||||||
assert len(Functions.debug_log) == 0
|
assert len(Functions.debug_log) == 0
|
||||||
assert "".join(result) == 'test run 3'
|
assert "".join(result) == 'test run 3'
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
def test_functions_run_bash_log_and_return_output():
|
def test_functions_run_bash_log_and_return_output():
|
||||||
print()
|
print()
|
||||||
Functions.debug_log = []
|
Functions.debug_log = []
|
||||||
try:
|
try:
|
||||||
result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 4'", log_output=True, return_result=True)
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "echo 'test run 4'", log_output=True, return_result=True)
|
||||||
|
assert return_code == 0
|
||||||
assert "".join(result) == 'test run 4'
|
assert "".join(result) == 'test run 4'
|
||||||
assert len(Functions.debug_log) == 1
|
assert len(Functions.debug_log) == 1
|
||||||
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: test run 4", Functions.debug_log[0])
|
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: test run 4", Functions.debug_log[0])
|
||||||
finally:
|
finally:
|
||||||
Functions.debug_log = None
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
|
def test_functions_run_bash_ok():
|
||||||
|
print()
|
||||||
|
Functions.debug_log = []
|
||||||
|
try:
|
||||||
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "%s/fixtures/run_bash.sh" % os.path.dirname(__file__), log_output=True,
|
||||||
|
return_result=False)
|
||||||
|
assert return_code == 0
|
||||||
|
assert result == []
|
||||||
|
assert len(Functions.debug_log) == 1
|
||||||
|
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Processing run_bash.sh", Functions.debug_log[0])
|
||||||
|
finally:
|
||||||
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
|
def test_functions_run_bash_fail():
|
||||||
|
print()
|
||||||
|
Functions.debug_log = []
|
||||||
|
try:
|
||||||
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "%s/fixtures/run_bash.sh 15" % os.path.dirname(__file__), log_output=True,
|
||||||
|
return_result=False)
|
||||||
|
assert return_code == 15
|
||||||
|
assert result == []
|
||||||
|
assert len(Functions.debug_log) == 1
|
||||||
|
assert re.match("\[EASYHAPROXY\] .* \[INFO\]: Processing run_bash.sh", Functions.debug_log[0])
|
||||||
|
finally:
|
||||||
|
Functions.debug_log = None
|
||||||
|
|
||||||
|
|
||||||
|
def test_functions_run_command_not_found():
|
||||||
|
print()
|
||||||
|
Functions.debug_log = []
|
||||||
|
try:
|
||||||
|
return_code, result = Functions.run_bash(Functions.EASYHAPROXY_LOG, "no_command_here", log_output=True,
|
||||||
|
return_result=False)
|
||||||
|
assert return_code == -99
|
||||||
|
assert str(result) == "[Errno 2] No such file or directory: 'no_command_here'"
|
||||||
|
assert len(Functions.debug_log) == 1
|
||||||
|
assert re.match("\[EASYHAPROXY\] .* \[ERROR\]: \[Errno 2\] No such file or directory: 'no_command_here'", Functions.debug_log[0])
|
||||||
|
finally:
|
||||||
|
Functions.debug_log = None
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,17 @@
|
||||||
from .context import easymapping
|
|
||||||
import json
|
import json
|
||||||
import pytest
|
|
||||||
|
from easymapping import DockerLabelHandler
|
||||||
|
|
||||||
|
|
||||||
def test_label_generation():
|
def test_label_generation():
|
||||||
label = easymapping.DockerLabelHandler("foo")
|
label = DockerLabelHandler("foo")
|
||||||
|
|
||||||
assert label.create("bar") == "foo.bar"
|
assert label.create("bar") == "foo.bar"
|
||||||
assert label.create(["bar", "foobar"]) == "foo.bar.foobar"
|
assert label.create(["bar", "foobar"]) == "foo.bar.foobar"
|
||||||
|
|
||||||
|
|
||||||
def test_label_data():
|
def test_label_data():
|
||||||
label = easymapping.DockerLabelHandler("base")
|
label = DockerLabelHandler("base")
|
||||||
label.set_data(json.loads('{"base.definitions":"h2"}'))
|
label.set_data(json.loads('{"base.definitions":"h2"}'))
|
||||||
|
|
||||||
label_name = label.create("definitions")
|
label_name = label.create("definitions")
|
||||||
|
|
@ -20,7 +21,7 @@ def test_label_data():
|
||||||
|
|
||||||
|
|
||||||
def test_label_complex_key():
|
def test_label_complex_key():
|
||||||
label = easymapping.DockerLabelHandler("till")
|
label = DockerLabelHandler("till")
|
||||||
|
|
||||||
data = dict()
|
data = dict()
|
||||||
data["till.definitions"] = "h2"
|
data["till.definitions"] = "h2"
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import easymapping
|
|
||||||
import pytest
|
|
||||||
import os
|
|
||||||
import yaml
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
import easymapping
|
||||||
|
|
||||||
|
CERTS_FOLDER = "/tmp/certs"
|
||||||
|
CERT_FILE = "/tmp/certs/haproxy/www.somehost.com.br.pem"
|
||||||
|
CERTBOT_EMAIL = "some@email.com"
|
||||||
|
|
||||||
CERTS_FOLDER="/tmp/certs"
|
|
||||||
CERT_FILE="/tmp/certs/haproxy/www.somehost.com.br.pem"
|
|
||||||
LETSENCRYPT_EMAIL="some@email.com"
|
|
||||||
|
|
||||||
def load_fixture(file):
|
def load_fixture(file):
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
@ -33,15 +35,16 @@ def test_parser_doesnt_crash():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/no-services.txt", 'r') as expected_file:
|
with open(path + "/expected/no-services.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_finds_services():
|
def test_parser_finds_services():
|
||||||
line_list = load_fixture("services")
|
line_list = load_fixture("services")
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"letsencrypt": {
|
"certbot": {
|
||||||
"email": LETSENCRYPT_EMAIL
|
"email": CERTBOT_EMAIL
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"port": 0
|
"port": 0
|
||||||
|
|
@ -56,9 +59,10 @@ def test_parser_finds_services():
|
||||||
with open(path + "/expected/services.txt", 'r') as expected_file:
|
with open(path + "/expected/services.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
|
|
||||||
assert {"www.somehost.com.br.pem":"Some PEM Certificate"} == cfg.certs
|
assert {"www.somehost.com.br.pem": "Some PEM Certificate"} == cfg.certs
|
||||||
|
|
||||||
|
assert ['node-exporter.quantum.example.org'] == cfg.certbot_hosts
|
||||||
|
|
||||||
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
|
||||||
|
|
||||||
def test_parser_finds_services_changed_label():
|
def test_parser_finds_services_changed_label():
|
||||||
line_list = load_fixture("services-changed-label")
|
line_list = load_fixture("services-changed-label")
|
||||||
|
|
@ -66,8 +70,8 @@ def test_parser_finds_services_changed_label():
|
||||||
result = {
|
result = {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"lookup_label": "haproxy",
|
"lookup_label": "haproxy",
|
||||||
"letsencrypt": {
|
"certbot": {
|
||||||
"email": LETSENCRYPT_EMAIL
|
"email": CERTBOT_EMAIL
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"port": 0
|
"port": 0
|
||||||
|
|
@ -85,17 +89,18 @@ def test_parser_finds_services_changed_label():
|
||||||
with open(path + "/expected/services.txt", 'r') as expected_file:
|
with open(path + "/expected/services.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
|
|
||||||
assert {"www.somehost.com.br.pem":"Some PEM Certificate"} == cfg.certs
|
assert {"www.somehost.com.br.pem": "Some PEM Certificate"} == cfg.certs
|
||||||
|
|
||||||
|
assert ['node-exporter.quantum.example.org'] == cfg.certbot_hosts
|
||||||
|
|
||||||
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
|
||||||
|
|
||||||
def test_parser_finds_services_raw():
|
def test_parser_finds_services_raw():
|
||||||
line_list = load_fixture("services")
|
line_list = load_fixture("services")
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"letsencrypt": {
|
"certbot": {
|
||||||
"email": LETSENCRYPT_EMAIL
|
"email": CERTBOT_EMAIL
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"port": 0
|
"port": 0
|
||||||
|
|
@ -110,93 +115,99 @@ def test_parser_finds_services_raw():
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
"mode":"tcp",
|
"mode":"tcp",
|
||||||
"health-check":"",
|
"ssl-check":"",
|
||||||
"port":"31339",
|
"port":"31339",
|
||||||
"hosts":{
|
"hosts":{
|
||||||
"agent.quantum.example.org": {
|
"agent.quantum.example.org": {
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_agent:9001"
|
"my-stack_agent:9001"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect":{
|
"redirect": {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode":"http",
|
"mode":"http",
|
||||||
"health-check":"",
|
"ssl-check":"",
|
||||||
"port":"31337",
|
"port":"31337",
|
||||||
"hosts":{
|
"hosts":{
|
||||||
"cadvisor.quantum.example.org":{
|
"cadvisor.quantum.example.org":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_cadvisor:8080"
|
"my-stack_cadvisor:8080"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"node-exporter.quantum.example.org":{
|
"node-exporter.quantum.example.org":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_node-exporter:9100"
|
"my-stack_node-exporter:9100"
|
||||||
],
|
],
|
||||||
"letsencrypt": True,
|
"certbot": True,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect":{
|
"redirect": {
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode":"http",
|
"mode":"http",
|
||||||
"health-check":"",
|
"ssl-check":"",
|
||||||
"port":"443",
|
"port":"443",
|
||||||
"hosts":{
|
"hosts":{
|
||||||
"node-exporter.quantum.example.org": {
|
"node-exporter.quantum.example.org": {
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"my-stack_node-exporter:9100"
|
"my-stack_node-exporter:9100"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"www.somehost.com.br":{
|
"www.somehost.com.br":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"some-service:80"
|
"some-service:80"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect":{
|
"redirect": {
|
||||||
"somehost.com.br":"https://www.somehost.com.br",
|
"somehost.com.br": "https://www.somehost.com.br",
|
||||||
"somehost.com":"https://www.somehost.com.br",
|
"somehost.com": "https://www.somehost.com.br",
|
||||||
"www.somehost.com":"https://www.somehost.com.br",
|
"www.somehost.com": "https://www.somehost.com.br",
|
||||||
"byjg.ca":"https://www.somehost.com.br",
|
"byjg.ca": "https://www.somehost.com.br",
|
||||||
"www.byjg.ca":"https://www.somehost.com.br"
|
"www.byjg.ca": "https://www.somehost.com.br"
|
||||||
},
|
},
|
||||||
"ssl": True
|
"ssl": True
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode":"http",
|
"mode":"http",
|
||||||
"health-check":"",
|
"ssl-check":"",
|
||||||
"port":"80",
|
"port":"80",
|
||||||
"hosts":{
|
"hosts":{
|
||||||
"www.somehost.com.br":{
|
"www.somehost.com.br":{
|
||||||
|
"balance": "roundrobin",
|
||||||
"containers": [
|
"containers": [
|
||||||
"some-service:80"
|
"some-service:80"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redirect":{
|
"redirect": {
|
||||||
"somehost.com.br":"https://www.somehost.com.br",
|
"somehost.com.br": "https://www.somehost.com.br",
|
||||||
"somehost.com":"https://www.somehost.com.br",
|
"somehost.com": "https://www.somehost.com.br",
|
||||||
"www.somehost.com":"https://www.somehost.com.br",
|
"www.somehost.com": "https://www.somehost.com.br",
|
||||||
"byjg.ca":"https://www.somehost.com.br",
|
"byjg.ca": "https://www.somehost.com.br",
|
||||||
"www.byjg.ca":"https://www.somehost.com.br"
|
"www.byjg.ca": "https://www.somehost.com.br"
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -204,8 +215,7 @@ def test_parser_finds_services_raw():
|
||||||
processed = list(cfg.parse(line_list))
|
processed = list(cfg.parse(line_list))
|
||||||
|
|
||||||
assert parsed_object == processed
|
assert parsed_object == processed
|
||||||
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
assert ['node-exporter.quantum.example.org'] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_parser_static():
|
def test_parser_static():
|
||||||
|
|
@ -219,7 +229,8 @@ def test_parser_static():
|
||||||
|
|
||||||
with open(path + "/expected/static.txt", 'r') as expected_file:
|
with open(path + "/expected/static.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_static_raw():
|
def test_parser_static_raw():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
@ -241,7 +252,7 @@ def test_parser_static_raw():
|
||||||
"containers": [
|
"containers": [
|
||||||
"container:5000"
|
"container:5000"
|
||||||
],
|
],
|
||||||
"letsencrypt": True
|
"certbot": True
|
||||||
},
|
},
|
||||||
"host2.com.br": {
|
"host2.com.br": {
|
||||||
"containers": [
|
"containers": [
|
||||||
|
|
@ -280,7 +291,6 @@ def test_parser_static_raw():
|
||||||
assert expected == parsed
|
assert expected == parsed
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_parser_tcp():
|
def test_parser_tcp():
|
||||||
line_list = load_fixture("services-tcp")
|
line_list = load_fixture("services-tcp")
|
||||||
|
|
||||||
|
|
@ -299,7 +309,8 @@ def test_parser_tcp():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/services-tcp.txt", 'r') as expected_file:
|
with open(path + "/expected/services-tcp.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_multi_containers():
|
def test_parser_multi_containers():
|
||||||
line_list = load_fixture("services-multi-containers")
|
line_list = load_fixture("services-multi-containers")
|
||||||
|
|
@ -318,7 +329,7 @@ def test_parser_multi_containers():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/services-multi-containers.txt", 'r') as expected_file:
|
with open(path + "/expected/services-multi-containers.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_multiple_hosts():
|
def test_parser_multiple_hosts():
|
||||||
|
|
@ -340,7 +351,7 @@ def test_parser_multiple_hosts():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/services-multiple-hosts.txt", 'r') as expected_file:
|
with open(path + "/expected/services-multiple-hosts.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_redirect_ssl():
|
def test_parser_redirect_ssl():
|
||||||
|
|
@ -361,7 +372,7 @@ def test_parser_redirect_ssl():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/services-redirect-ssl.txt", 'r') as expected_file:
|
with open(path + "/expected/services-redirect-ssl.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_ssl_strict():
|
def test_parser_ssl_strict():
|
||||||
|
|
@ -382,7 +393,8 @@ def test_parser_ssl_strict():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/ssl-strict.txt", 'r') as expected_file:
|
with open(path + "/expected/ssl-strict.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_ssl_loose():
|
def test_parser_ssl_loose():
|
||||||
line_list = load_fixture("no-services")
|
line_list = load_fixture("no-services")
|
||||||
|
|
@ -399,7 +411,8 @@ def test_parser_ssl_loose():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/ssl-loose.txt", 'r') as expected_file:
|
with open(path + "/expected/ssl-loose.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_ssl_letsencrypt():
|
def test_parser_ssl_letsencrypt():
|
||||||
line_list = load_fixture("services-letsencrypt")
|
line_list = load_fixture("services-letsencrypt")
|
||||||
|
|
@ -409,8 +422,8 @@ def test_parser_ssl_letsencrypt():
|
||||||
"stats": {
|
"stats": {
|
||||||
"password": "password"
|
"password": "password"
|
||||||
},
|
},
|
||||||
"letsencrypt": {
|
"certbot": {
|
||||||
"email": LETSENCRYPT_EMAIL
|
"email": CERTBOT_EMAIL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,7 +434,7 @@ def test_parser_ssl_letsencrypt():
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
with open(path + "/expected/services-letsencrypt.txt", 'r') as expected_file:
|
with open(path + "/expected/services-letsencrypt.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
assert ["test.example.org"] == cfg.letsencrypt_hosts
|
assert ["test.example.org"] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
|
||||||
def test_parser_finds_services_clone_to_ssl_raw():
|
def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
|
|
@ -429,8 +442,8 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"customerrors": False,
|
"customerrors": False,
|
||||||
"letsencrypt": {
|
"certbot": {
|
||||||
"email": LETSENCRYPT_EMAIL
|
"email": CERTBOT_EMAIL
|
||||||
},
|
},
|
||||||
"stats": {
|
"stats": {
|
||||||
"port": 0
|
"port": 0
|
||||||
|
|
@ -444,50 +457,54 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
|
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
"health-check":"",
|
"ssl-check":"",
|
||||||
"hosts":{
|
"hosts":{
|
||||||
"host2.local":{
|
"host2.local":{
|
||||||
|
"balance":"roundrobin",
|
||||||
"containers":[
|
"containers":[
|
||||||
"10.152.183.215:8080"
|
"10.152.183.215:8080"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"valida.me":{
|
"valida.me":{
|
||||||
|
"balance":"roundrobin",
|
||||||
"containers":[
|
"containers":[
|
||||||
"10.152.183.62:8080"
|
"10.152.183.62:8080"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
},
|
},
|
||||||
"www.valida.me":{
|
"www.valida.me":{
|
||||||
|
"balance":"roundrobin",
|
||||||
"containers":[
|
"containers":[
|
||||||
"10.152.183.62:8080"
|
"10.152.183.62:8080"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mode":"http",
|
"mode": "http",
|
||||||
"port":"80",
|
"port": "80",
|
||||||
"redirect":{
|
"redirect": {
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"health-check":"ssl",
|
"ssl-check":"ssl",
|
||||||
"hosts":{
|
"hosts":{
|
||||||
"host2.local":{
|
"host2.local":{
|
||||||
|
"balance":"roundrobin",
|
||||||
"containers":[
|
"containers":[
|
||||||
"10.152.183.215:8080"
|
"10.152.183.215:8080"
|
||||||
],
|
],
|
||||||
"letsencrypt": False,
|
"certbot": False,
|
||||||
"redirect_ssl": False
|
"redirect_ssl": False
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mode":"http",
|
"mode": "http",
|
||||||
"port":"443",
|
"port": "443",
|
||||||
"redirect":{
|
"redirect": {
|
||||||
|
|
||||||
},
|
},
|
||||||
"ssl": True
|
"ssl": True
|
||||||
|
|
@ -496,12 +513,10 @@ def test_parser_finds_services_clone_to_ssl_raw():
|
||||||
processed = list(cfg.parse(line_list))
|
processed = list(cfg.parse(line_list))
|
||||||
|
|
||||||
assert parsed_object == processed
|
assert parsed_object == processed
|
||||||
assert [] == cfg.letsencrypt_hosts
|
assert [] == cfg.certbot_hosts
|
||||||
|
|
||||||
|
# test_parser_finds_services_raw()
|
||||||
|
# test_parser_tcp()
|
||||||
#test_parser_finds_services_raw()
|
# test_parser_multiple_hosts()
|
||||||
#test_parser_tcp()
|
# test_parser_ssl_certbot()
|
||||||
#test_parser_multiple_hosts()
|
# test_parser_finds_services()
|
||||||
#test_parser_ssl_letsencrypt()
|
|
||||||
#test_parser_finds_services()
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import pytest
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from functions import Functions
|
from functions import Functions
|
||||||
from processor import ProcessorInterface
|
from processor import ProcessorInterface
|
||||||
from processor import Static
|
|
||||||
|
|
||||||
def test_processor_static():
|
def test_processor_static():
|
||||||
ProcessorInterface.static_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./fixtures/static.yml")
|
ProcessorInterface.static_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "./fixtures/static.yml")
|
||||||
|
|
@ -10,44 +10,44 @@ def test_processor_static():
|
||||||
|
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
"hosts":{
|
"hosts": {
|
||||||
"host1.com.br":{
|
"host1.com.br": {
|
||||||
"containers":[
|
"containers": [
|
||||||
"container:5000"
|
"container:5000"
|
||||||
],
|
],
|
||||||
"letsencrypt": True
|
"certbot": True
|
||||||
},
|
},
|
||||||
"host2.com.br":{
|
"host2.com.br": {
|
||||||
"containers":[
|
"containers": [
|
||||||
"other:3000"
|
"other:3000"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"port":80,
|
"port": 80,
|
||||||
"redirect":{
|
"redirect": {
|
||||||
"www.host1.com.br":"http://host1.com.br"
|
"www.host1.com.br": "http://host1.com.br"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hosts":{
|
"hosts": {
|
||||||
"host1.com.br":{
|
"host1.com.br": {
|
||||||
"containers":[
|
"containers": [
|
||||||
"container:80"
|
"container:80"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"port":443,
|
"port": 443,
|
||||||
"ssl": True
|
"ssl": True
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hosts":{
|
"hosts": {
|
||||||
"host3.com.br":{
|
"host3.com.br": {
|
||||||
"containers":[
|
"containers": [
|
||||||
"domain:8181"
|
"domain:8181"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"port":8080
|
"port": 8080
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
hosts = [
|
hosts = [
|
||||||
|
|
@ -57,16 +57,17 @@ def test_processor_static():
|
||||||
'host3.com.br:8080'
|
'host3.com.br:8080'
|
||||||
]
|
]
|
||||||
|
|
||||||
assert static.get_letsencrypt_hosts() is None
|
assert static.get_certbot_hosts() is None
|
||||||
assert static.get_parsed_object() == parsed_object
|
assert static.get_parsed_object() == parsed_object
|
||||||
assert static.get_hosts() == hosts
|
assert static.get_hosts() == hosts
|
||||||
|
|
||||||
haproxy_cfg = static.get_haproxy_conf()
|
haproxy_cfg = static.get_haproxy_conf()
|
||||||
|
|
||||||
assert haproxy_cfg == Functions.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/static.txt"))
|
assert haproxy_cfg == Functions.load(
|
||||||
|
os.path.join(os.path.dirname(os.path.realpath(__file__)), "./expected/static.txt"))
|
||||||
|
|
||||||
# @todo: Static doesnt populate this fields
|
# @todo: Static doesnt populate this fields
|
||||||
assert static.get_letsencrypt_hosts() == []
|
assert static.get_certbot_hosts() == []
|
||||||
assert static.get_parsed_object() == parsed_object
|
assert static.get_parsed_object() == parsed_object
|
||||||
assert static.get_hosts() == hosts
|
assert static.get_hosts() == hosts
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue