commit
af1fb73c13
116 changed files with 3339 additions and 815 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
__pycache__
|
||||
.pytest_cache
|
||||
*.pyc
|
||||
.*
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
|
|
@ -6,6 +6,6 @@ updates:
|
|||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "pip"
|
||||
directory: "/"
|
||||
directory: "/src"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
|
|
|||
21
.github/workflows/build.yml
vendored
21
.github/workflows/build.yml
vendored
|
|
@ -26,9 +26,14 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- run: |
|
||||
- name: Install requirements
|
||||
run: |
|
||||
cd src/
|
||||
pip install -r requirements.txt
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd src/
|
||||
pytest -s tests/ -vv
|
||||
|
||||
Build:
|
||||
|
|
@ -87,7 +92,7 @@ jobs:
|
|||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
file: build/Dockerfile
|
||||
build-args: |
|
||||
RELEASE_VERSION_ARG="${{ join(steps.tags.outputs.result, ',') }}"
|
||||
platforms: linux/amd64,linux/arm64
|
||||
|
|
@ -102,6 +107,16 @@ jobs:
|
|||
# chmod +x $HOME/.docker/cli-plugins/docker-pushrm
|
||||
# docker pushrm ${{ env.IMAGE_NAME }}
|
||||
|
||||
Helm:
|
||||
runs-on: 'ubuntu-latest'
|
||||
needs: Build
|
||||
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
DOC_GITHUB_TOKEN: '${{ secrets.DOC_TOKEN }}'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: curl https://opensource.byjg.com/add-helm.sh | bash /dev/stdin helm easyhaproxy
|
||||
|
||||
Documentation:
|
||||
runs-on: 'ubuntu-latest'
|
||||
needs: Build
|
||||
|
|
@ -110,4 +125,4 @@ jobs:
|
|||
DOC_GITHUB_TOKEN: '${{ secrets.DOC_TOKEN }}'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: curl https://opensource.byjg.com/add-doc.sh | bash /dev/stdin devops docker-easy-haproxy
|
||||
- run: curl https://opensource.byjg.com/add-doc.sh | bash /dev/stdin devops docker-easy-haproxy docs
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -4,4 +4,5 @@ venv
|
|||
.docker_data
|
||||
__pycache__
|
||||
.pytest_cache
|
||||
*.pyc
|
||||
*.pyc
|
||||
.env
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ tasks:
|
|||
- command: |
|
||||
virtualenv -p /usr/bin/python3 venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
pip install -r src/requirements.txt
|
||||
|
|
|
|||
26
.travis.yml
26
.travis.yml
|
|
@ -1,26 +0,0 @@
|
|||
language: python
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
if: (type IN (pull_request))
|
||||
install:
|
||||
- pip install -r requirements.txt
|
||||
script:
|
||||
- pytest -s tests/
|
||||
|
||||
- stage: build docker
|
||||
if: (branch = master) AND (NOT (type IN (pull_request)))
|
||||
install:
|
||||
- docker pull byjg/k8s-ci
|
||||
script:
|
||||
- docker run --privileged -v /tmp/z:/var/lib/containers -it --rm -v $PWD:/work -w /work -e DOCKER_USERNAME=$DOCKER_USERNAME -e DOCKER_PASSWORD=$DOCKER_PASSWORD -e DOCKER_REGISTRY=$DOCKER_REGISTRY byjg/k8s-ci /work/build-multiarch.sh
|
||||
|
||||
- stage: documentation
|
||||
if: (branch = master) AND (NOT (type IN (pull_request)))
|
||||
install: skip
|
||||
script: "curl https://opensource.byjg.com/add-doc.sh | bash /dev/stdin devops docker-easy-haproxy"
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
|
@ -10,9 +10,9 @@
|
|||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"cwd": "${workspaceFolder}/src",
|
||||
"env": {
|
||||
"PYTHONPATH": "${cwd}"
|
||||
"PYTHONPATH": "${cwd}/src"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -2,7 +2,7 @@ VERSION := $(shell git rev-parse --short HEAD)
|
|||
|
||||
.PHONY: build
|
||||
build:
|
||||
docker build -t byjg/easy-haproxy --build-arg RELEASE_VERSION_ARG="$(VERSION)" -t byjg/easy-haproxy:local .
|
||||
docker build -t byjg/easy-haproxy --build-arg RELEASE_VERSION_ARG="$(VERSION)" -t byjg/easy-haproxy:local -f build/Dockerfile .
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
|
|
|
|||
378
README.md
378
README.md
|
|
@ -1,4 +1,4 @@
|
|||
# Easy HAProxy
|
||||
# EasyHAProxy
|
||||
|
||||
[](http://opensource.byjg.com)
|
||||
[](https://github.com/byjg/docker-easy-haproxy/actions/workflows/build.yml)
|
||||
|
|
@ -6,378 +6,48 @@
|
|||
[](https://opensource.byjg.com/opensource/licensing.html)
|
||||
[](https://github.com/byjg/docker-easy-haproxy/releases/)
|
||||
|
||||
Service discovery for HAProxy.
|
||||

|
||||
|
||||
This Docker image will dynamically create the `haproxy.cfg` based on the labels defined in docker containers or from
|
||||
a simple Yaml.
|
||||
## Service discovery for HAProxy
|
||||
|
||||
The main objective of EasyHAProxy is dynamically create the `haproxy.cfg` based on the labels defined in docker containers.
|
||||
|
||||
EasyHAProxy can detect and configure automatically HAProxy on the folowing platforms:
|
||||
|
||||
- Docker
|
||||
- Docker Swarm
|
||||
- Kubernetes
|
||||
|
||||
## Features
|
||||
|
||||
EasyHAProxy will discover the services based on the Docker Tags of the running containers in a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features::
|
||||
EasyHAProxy will discover the services based on the Docker Tags of the running containers in a Docker host or Docker Swarm cluster and dynamically set up the `haproxy.cfg`. Below, EasyHAProxy main features:
|
||||
|
||||
- Use Letsencrypt with HAProxy.
|
||||
- Set your custom SSL certificates
|
||||
- Balance traffic between multiple replicas
|
||||
- Set SSL with three different levels of validations and according to the most recent definitions.
|
||||
- Include your SSL certificate.
|
||||
- Setup HAProxy to listen to TCP.
|
||||
- Add redirects.
|
||||
- Enable/disable Stats on port 1936 with a custom password.
|
||||
- Enable/disable custom errors.
|
||||
|
||||
Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file.
|
||||
Also, it is possible to set up HAProxy from a simple Yaml file instead of creating `haproxy.cfg` file.
|
||||
|
||||
## Basic Usage
|
||||
## How It Works?
|
||||
|
||||
The Easy HAProxy will automatically create the `haproxy.cfg` file based on the containers or a YAML provided.
|
||||
You don't need to change your current infrastructure and don't need to learn the HAProxy configuration.
|
||||
|
||||
The basic command line to run is:
|
||||
You need run the EasyHAProxy container, add some labels to your existing container and EasyHAProxy will
|
||||
automatically detect them and setup HAProxy for you.
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name easy-haproxy-container \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-e EASYHAPROXY_DISCOVER="swarm|docker|static" \
|
||||
# + Environment Variables \
|
||||
# + ports mapped to the host \
|
||||
byjg/easy-haproxy
|
||||
```
|
||||
## Detailed Instructions
|
||||
|
||||
The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels;
|
||||
For detailed instructions on how to use EasyHAProxy follow the instructions for the platform you want to use:
|
||||
|
||||
The environment variables will setup the HAProxy.
|
||||
| Kubernetes | Docker Swarm | Docker | Static
|
||||
|:----------:|:------------:|:------:|:-------:
|
||||
| [](docs/kubernetes.md) | [](docs/swarm.md) | [](docs/docker.md) | [](docs/static.md)
|
||||
|
||||
| Environment Variable | Description |
|
||||
|-------------------------------|---------------------------------------------------------------------------------------------------------------|
|
||||
| EASYHAPROXY_DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` |
|
||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search for matching resources. Default: `easyhaproxy`. |
|
||||
| EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request the certificate to Letsencrypt |
|
||||
| EASYHAPROXY_SSL_MODE | (Optional) `STRICT` supports only the most recent TLS version; `DEFAULT` good SSL integration with recent browsers; `LOOSE` supports all old SSL protocols for old browsers (not recommended). |
|
||||
| EASYHAPROXY_REFRESH_CONF | (Optional) Check configuration every N seconds. Default: 10 |
|
||||
| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. Default: `admin` |
|
||||
| HAPROXY_PASSWORD | (Optional) The HAProxy password to the statistics. If not set, statistics will be available with no password |
|
||||
| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. Default: `1936`. If set to `false`, disable statistics |
|
||||
| HAPROXY_CUSTOMERRORS | (Optional) If HAProxy will use custom HTML errors. true/false. Default: `false` |
|
||||
|
||||
The environment variable `EASYHAPROXY_DISCOVER` will define where is located your containers (see below for more details):
|
||||
|
||||
- docker
|
||||
- swarm
|
||||
- static
|
||||
|
||||
## Automatic Discover Services
|
||||
|
||||
Easy HAProxy can automatically discover the container services running in the same network of Docker or in a Docker Swarm cluster.
|
||||
|
||||
### EASYHAPROXY_DISCOVER: docker
|
||||
|
||||
This method will use a standard docker installation to discover the containers and configure the HAProxy.
|
||||
|
||||
The only requirement is that containers and easy-haproxy must be in the same docker network.
|
||||
|
||||
The discovery will occur every minute.
|
||||
|
||||
e.g.:
|
||||
|
||||
```bash
|
||||
docker create networkd easyhaproxy
|
||||
|
||||
docker run --network easyhaproxy byjg/easyhaproxy
|
||||
|
||||
docker run --network easyhaproxy myimage
|
||||
```
|
||||
|
||||
or, if the container is already created you can join it using the command:
|
||||
|
||||
```
|
||||
docker network connect easyhaproxy mycontainer
|
||||
```
|
||||
|
||||
### EASYHAPROXY_DISCOVER: swarm
|
||||
|
||||
This method requires a functional Docker Swarm Cluster. The system will search for the labels in all containers on all
|
||||
swarm nodes.
|
||||
|
||||
The discovery will occur every minute.
|
||||
|
||||
Important: easyhaproxy needs to be in the same network of the containers or otherwise will not access.
|
||||
|
||||
### Docker Container (Swarm or Docker) tags:
|
||||
|
||||
| Tag | Description | Example |
|
||||
|---------------------------------------|---------------------------------------------------------------------------------------------------------|--------------|
|
||||
| easyhaproxy.[definition].host | Host(s) HAProxy is listening. More than one host use comma as delimiter | somehost.com OR host1.com,host2.com |
|
||||
| easyhaproxy.[definition].mode | (Optional) Is this `http` or `tcp` mode in HAProxy. (Defaults to http) | http |
|
||||
| easyhaproxy.[definition].port | (Optional) Port HAProxy will listen for the host. (Defaults to 80) | 80 |
|
||||
| easyhaproxy.[definition].localport | (Optional) Port container is listening. (Defaults to 80) | 8080 |
|
||||
| easyhaproxy.[definition].redirect | (Optional) JSON containing key/value pair from host/to URL redirect. | {"foo.com":"https://bla.com", "bar.com":"https://bar.org"} |
|
||||
| easyhaproxy.[definition].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if letsencrypt is enabled. | |
|
||||
| easyhaproxy.[definition].ssl | (Optional) If `true` you need to provide certificate as a file. See below. Do not use with `sslcert`. | true |
|
||||
| easyhaproxy.[definition].health-check | (Optional) `ssl`, enable health check via SSL in `mode tcp` (Defaults to "empty") | ssl |
|
||||
| easyhaproxy.[definition].letsencrypt | (Optional) Generate certificate with letsencrypt. Do not use with `sslcert`. | true OR yes OR false OR no |
|
||||
| easyhaproxy.[definition].redirect_ssl | (Optional) Redirect all requests to https | true OR yes OR false OR no |
|
||||
|
||||
### Defining the labels in Docker Swarm
|
||||
|
||||
if you are deploying a stack in a Docker Swarm cluster, set labels at the `deploy` level:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
foo:
|
||||
deploy:
|
||||
labels:
|
||||
easyhaproxy.my.host: "www.example.org"
|
||||
easyhaproxy.my.localport: 8080
|
||||
...
|
||||
```
|
||||
|
||||
### Single Definition
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.webapi.port=80\
|
||||
-l easyhaproxy.webapi.host=byjg.com.br \
|
||||
....
|
||||
```
|
||||
|
||||
### Multiples Definitions on the same container
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.express.port=80 \
|
||||
-l easyhaproxy.express.localport=3000 \
|
||||
-l easyhaproxy.express.host=express.byjg.com.br \
|
||||
|
||||
-l easyhaproxy.admin.port=80 \
|
||||
-l easyhaproxy.admin.localport=3001 \
|
||||
-l easyhaproxy.admin.host=admin.byjg.com.br \
|
||||
.... \
|
||||
some/myimage
|
||||
```
|
||||
|
||||
### Multiples hosts on the same container
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.express.port=80 \
|
||||
-l easyhaproxy.express.localport=3000 \
|
||||
-l easyhaproxy.express.host=express.byjg.com.br,admin.byjg.com.br \
|
||||
.... \
|
||||
some/myimage
|
||||
```
|
||||
|
||||
If you are using docker-compose you can use this way:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
mycontainer:
|
||||
image: some/myimage
|
||||
labels:
|
||||
easyhaproxy.express.port: 80
|
||||
easyhaproxy.express.localport: 3000
|
||||
easyhaproxy.express.host: >-
|
||||
express.byjg.com.br,
|
||||
admin.byjg.com.br
|
||||
```
|
||||
|
||||
### TLS passthrough
|
||||
|
||||
Used to pass on SSL termination to a backend. Alternatively, you can enable health-check via SSL on the backend with the optional `health-check` label:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.example.mode=tcp \
|
||||
-l easyhaproxy.example.health-check=ssl \
|
||||
-l easyhaproxy.example.port=443
|
||||
.... \
|
||||
some/tcp-service
|
||||
```
|
||||
|
||||
### Redirect Example
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.[definition].redirect='{"www.byjg.com.br":"http://byjg.com.br","byjg.com":"http://byjg.com.br"}'
|
||||
```
|
||||
|
||||
## EASYHAPROXY_DISCOVER: static
|
||||
|
||||
This method expects a YAML file to setup the `haproxy.cfg`
|
||||
|
||||
Create a YAML file and map to `/etc/haproxy/easyconfig.yml`
|
||||
|
||||
```yaml
|
||||
stats:
|
||||
username: admin
|
||||
password: password
|
||||
port: 1936 # Optional (default 1936)
|
||||
|
||||
customerrors: true # Optional (default false)
|
||||
|
||||
ssl_mode: default
|
||||
|
||||
letsencrypt: {
|
||||
"email": "acme@example.org"
|
||||
}
|
||||
|
||||
easymapping:
|
||||
- port: 80
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
- container:5000
|
||||
letsencrypt: true
|
||||
redirect_ssl: true
|
||||
host2.com.br:
|
||||
containers:
|
||||
- other:3000
|
||||
redirect:
|
||||
www.host1.com.br: http://host1.com.br
|
||||
|
||||
- port: 443
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
- container:80
|
||||
redirect_ssl: false
|
||||
ssl: true
|
||||
|
||||
- port: 8080
|
||||
hosts:
|
||||
host3.com.br:
|
||||
containers:
|
||||
- domain:8181
|
||||
```
|
||||
|
||||
Running:
|
||||
|
||||
```bash
|
||||
docker run -v /my/config.yml:/etc/haproxy/easyconfig.yml .... byjg/easyhaproxy
|
||||
```
|
||||
|
||||
## Letsencrypt
|
||||
|
||||
This HAProxy can issue a letsencrypt certificate. The command is as below:
|
||||
|
||||
Run the EasyHAProxy:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-e EASYHAPROXY_LETSENCRYPT_EMAIL=john@doe.com
|
||||
.... \
|
||||
byjg/easy-haproxy
|
||||
```
|
||||
|
||||
Run your container:
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.express.port=80 \
|
||||
-l easyhaproxy.express.localport=3000 \
|
||||
-l easyhaproxy.express.host=example.org \
|
||||
-l easyhaproxy.express.letsencrypt=true \
|
||||
.... \
|
||||
some/myimage
|
||||
```
|
||||
|
||||
Caveats:
|
||||
|
||||
- Your container **must** listen to the port 80. Besides no error, the certificate won't be issued if in a different port.
|
||||
- The port 2080 is reserved for the certbot and should not be exposed.
|
||||
- You cannot set the port 443 for the container with the Letsencrypt because EasyHAProxy will handle this automatically once the certificate is issued.
|
||||
- If you don't run the EasyHAProxy with the parameter `EASYHAPROXY_LETSENCRYPT_EMAIL` no certificate will be issued.
|
||||
- Be aware of Letsencrypt issue limits - https://letsencrypt.org/docs/duplicate-certificate-limit/ and https://letsencrypt.org/docs/rate-limits/
|
||||
|
||||
## Exposing Ports
|
||||
|
||||
You must expose some ports on the EasyHAProxy container and in the firewall. However, you don't need to expose the other container ports because EasyHAProxy will handle that.
|
||||
|
||||
- The ports `80` and `443`.
|
||||
- If you enable the HAProxy statistics, you must also expose the port defined in `HAPROXY_STATS_PORT` environment variable (default 1936). Be aware that statististics are enabled by default with no password.
|
||||
- Every port defined in `easyhaproxy.[definitions].port` also should be exposed.
|
||||
|
||||
e.g.
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
-p 1936:1936 \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
|
||||
## Mapping custom .cfg files
|
||||
|
||||
You can concatenate valid HAProxy `.cfg` files to the dynamically generated `haproxy.cfg` by mapping the folder `/etc/haproxy/conf.d`.
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-v /your/local/conf.d:/etc/haproxy/conf.d \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
## Mapping SSL certificates volumes
|
||||
|
||||
EasyHAProxy stores the certificates inside the folder `/certs/haproxy` and `/certs/letsencrypt`.
|
||||
- If you want to preserve the letsencrypt certificates between reloads, map the folder `/certs/letsencrypt` to your volume.
|
||||
- If you want to provide your certificates as a file instead of a Base64 parameter, map the folder `/certs/haproxy` to your volume, and instead of use `easyhaproxy.[definition].sslcert`, use `easyhaproxy.[definition].ssl: true`
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-v /your/certs/letsencrypt:/certs/letsencrypt \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
## Handling SSL
|
||||
|
||||
You can attach a valid SSL certificate to the request.
|
||||
|
||||
1. First, Create a single PEM file including CA.
|
||||
|
||||
```bash
|
||||
cat example.com.crt example.com.key > single.pem
|
||||
|
||||
cat single.pem
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+
|
||||
U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
|
||||
3j4wz8/I5fdsk090j4s5KA==
|
||||
-----END PRIVATE KEY-----
|
||||
```
|
||||
|
||||
2. Convert it to BASE64 in a single line:
|
||||
|
||||
```bash
|
||||
cat single.pem | base64 -w0
|
||||
```
|
||||
|
||||
3. Use this string to define the label `easyhaproxy.[definition].sslcert`
|
||||
|
||||
## Setting Custom Errors
|
||||
|
||||
If enabled, map the volume : `/etc/haproxy/errors-custom/` to your container and put a file named `ERROR_NUMBER.http`
|
||||
where ERROR_NUMBER is the HTTP error code (e.g., `503.http`)
|
||||
|
||||
## Build
|
||||
|
||||
|
||||
```bash
|
||||
docker build -t byjg/easy-haproxy .
|
||||
```
|
||||
|
||||
## Limitations
|
||||
|
||||
EasyHAProxy has some limitations when there is more than one easy-haproxy container running:
|
||||
- Replicas can be out-of-sync for a few seconds because each replica will discover the pods separately.
|
||||
- Each replica will request a Letsencrypt certificate and can fail because the letsencrypt challenge can be directed to the other replica.
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
|
|
|
|||
66
_config.yml
66
_config.yml
|
|
@ -1,66 +0,0 @@
|
|||
name: docker-easy-haproxy
|
||||
|
||||
project:
|
||||
version: 1.0.0
|
||||
download_url: https://github.com/byjg/docker-easy-haproxy/releases
|
||||
|
||||
license:
|
||||
software: MIT
|
||||
software_url: https://opensource.org/licenses/MIT
|
||||
|
||||
docs: MIT
|
||||
docs_url: https://opensource.org/licenses/MIT
|
||||
|
||||
git_edit_address: https://github.com/byjg/docker-easy-haproxy/blob/master/
|
||||
|
||||
links:
|
||||
header:
|
||||
- title: GitHub
|
||||
url: https://github.com/byjg/docker-easy-haproxy
|
||||
- title: ByJG
|
||||
url: https://opensource.byjg.com/
|
||||
footer:
|
||||
- title: GitHub
|
||||
url: https://github.com/byjg/docker-easy-haproxy
|
||||
- title: Issues
|
||||
url: https://github.com/byjg/docker-easy-haproxy/issues
|
||||
|
||||
ui:
|
||||
header:
|
||||
color1: "#080331"
|
||||
color2: "#0033cc"
|
||||
trianglify: true
|
||||
|
||||
social:
|
||||
github:
|
||||
user: byjg
|
||||
repo: docker-easy-haproxy
|
||||
twitter:
|
||||
enabled: false
|
||||
via:
|
||||
hash: opensourcebyjg
|
||||
account:
|
||||
facebook:
|
||||
enabled: true
|
||||
url: https://opensource.byjg.com/
|
||||
profileUrl:
|
||||
|
||||
author:
|
||||
twitter: byjg
|
||||
|
||||
twitter:
|
||||
card: summary
|
||||
username: byjg
|
||||
|
||||
logo: https://opensource.byjg.com/images/logo_byjg.png
|
||||
|
||||
analytics:
|
||||
google: UA-130014324-1
|
||||
|
||||
plugins:
|
||||
- jekyll-seo-tag
|
||||
|
||||
# Build settings
|
||||
markdown: kramdown
|
||||
remote_theme: byjg/jekyll-docs-theme
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
_
|
||||
___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _
|
||||
/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |
|
||||
\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |
|
||||
|__/ |_| |__/
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source /scripts/functions.sh
|
||||
|
||||
if [ ! -f /scripts/letsencrypt_hosts.txt ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Semaphore
|
||||
if [ -f /tmp/certbot-lock ]; then
|
||||
log "notice" "CERTBOT_JOB" "Another process is running"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
touch /tmp/certbot-lock
|
||||
|
||||
mkdir -p /var/log/letsencrypt
|
||||
ln -sf /dev/stdout /var/log/letsencrypt/letsencrypt.log
|
||||
|
||||
REQUEST_CERTS=""
|
||||
RENEW_CERTS=""
|
||||
|
||||
for domain in $(cat /scripts/letsencrypt_hosts.txt); do
|
||||
if [ ! -f "/certs/letsencrypt/$domain.pem" ]; then
|
||||
REQUEST_CERTS="$REQUES_CERTS -d $domain"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ $(find "/certs/letsencrypt/$domain.pem" -mtime +30 -print) ]]; then
|
||||
RENEW_CERTS="$RENEW_CERTS -d $domain"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$REQUEST_CERTS" ]; then
|
||||
log "info" "CERTBOT_JOB" "Requesting certificates for $REQUEST_CERTS"
|
||||
certbot certonly \
|
||||
--standalone \
|
||||
--preferred-challenges http \
|
||||
--http-01-port 2080 \
|
||||
--agree-tos \
|
||||
--issuance-timeout 90 \
|
||||
--no-eff-email \
|
||||
--non-interactive \
|
||||
--max-log-backups=0 \
|
||||
--post-hook "/scripts/certbot_to_haproxy.sh" \
|
||||
$REQUEST_CERTS --email $EASYHAPROXY_LETSENCRYPT_EMAIL
|
||||
fi
|
||||
|
||||
if [ -n "$RENEW_CERTS" ]; then
|
||||
log "info" "CERTBOT_JOB" "Resquesting renew certificated fort $RENEW_CERTS"
|
||||
certbot renew --post-hook "/scripts/certbot_to_haproxy.sh"
|
||||
fi
|
||||
|
||||
# Release semaphore
|
||||
rm /tmp/certbot-lock
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
source /scripts/functions.sh
|
||||
|
||||
# Loop through all Let's Encrypt certificates
|
||||
for CERTIFICATE in `find /etc/letsencrypt/live/* -type d`; do
|
||||
CERTIFICATE=`basename $CERTIFICATE`
|
||||
|
||||
# Combine certificate and private key to single file
|
||||
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /certs/letsencrypt/$CERTIFICATE.pem
|
||||
done
|
||||
|
||||
# It will be checked on haproxy-reload.sh
|
||||
touch /tmp/force-reload
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
function log() {
|
||||
# ARGS:
|
||||
# - loglevel
|
||||
# - app
|
||||
# - message
|
||||
echo [$2] $(date +"$EASYHAPROXY_DATEFORMAT") [$1]: $3
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source /scripts/functions.sh
|
||||
|
||||
cd /scripts
|
||||
|
||||
RELOAD="true"
|
||||
|
||||
if [[ "static|docker|swarm" != *"$EASYHAPROXY_DISCOVER"* ]];then
|
||||
log "error" "CONF_CHECK" "EASYHAPROXY_DISCOVER should be 'static', 'docker', or 'swarm'. I got '$EASYHAPROXY_DISCOVER' instead."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$EASYHAPROXY_DISCOVER" == "static" ]]; then
|
||||
CONTROL_FILE="/etc/haproxy/haproxy.cfg"
|
||||
touch ${CONTROL_FILE}
|
||||
cp ${CONTROL_FILE} ${CONTROL_FILE}.old
|
||||
python3 static.py /etc/haproxy/easyconfig.yml > ${CONTROL_FILE}
|
||||
else
|
||||
CONTROL_FILE="/tmp/.docker_data"
|
||||
touch ${CONTROL_FILE}
|
||||
mv ${CONTROL_FILE} ${CONTROL_FILE}.old
|
||||
touch ${CONTROL_FILE}
|
||||
|
||||
if [[ "$EASYHAPROXY_DISCOVER" == "docker" ]]; then
|
||||
CONTAINERS=$(docker ps -q | sort | uniq)
|
||||
LABEL_PATH=".Config.Labels"
|
||||
|
||||
for container in ${CONTAINERS}; do
|
||||
docker inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE}
|
||||
done
|
||||
else
|
||||
CONTAINERS=$(docker node ps $(docker node ls -q) --format "{{ .Name }}" --filter desired-state=running | cut -d. -f1 | sort | uniq)
|
||||
LABEL_PATH=".Spec.Labels"
|
||||
|
||||
for container in ${CONTAINERS}; do
|
||||
docker service inspect --format "{{ json $LABEL_PATH }}" ${container} | xargs -I % echo ${container}=% >> ${CONTROL_FILE}
|
||||
done
|
||||
fi
|
||||
|
||||
if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then
|
||||
RELOAD="false"
|
||||
else
|
||||
python3 swarm.py > /etc/haproxy/haproxy.cfg
|
||||
log "info" "CONF_CHECK" "New configuration found"
|
||||
fi
|
||||
fi
|
||||
|
||||
if cmp -s ${CONTROL_FILE} ${CONTROL_FILE}.old ; then
|
||||
RELOAD="false"
|
||||
fi
|
||||
|
||||
if [[ ! -z "$1" ]]; then
|
||||
log "info" "CONF_CHECK" "Initial configuration. Skip certbot."
|
||||
else
|
||||
/scripts/certbot.sh
|
||||
fi
|
||||
|
||||
# If Certbot reloads successfully will create the file /tmp/force-reload
|
||||
if [ -f /tmp/force-reload ]; then
|
||||
log "info" "CONF_CHECK" "New certificates found..."
|
||||
RELOAD="true"
|
||||
rm /tmp/force-reload
|
||||
fi
|
||||
|
||||
if [[ ! -z "$1" ]]; then
|
||||
log "info" "CONF_CHECK" "Starting haproxy..."
|
||||
/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg $(ls /etc/haproxy/conf.d/*.cfg 2>/dev/null | xargs -I{} echo -f {}) -p /run/haproxy.pid -S /var/run/haproxy.sock &
|
||||
|
||||
elif [[ "$RELOAD" == "true" ]]; then
|
||||
log "info" "CONF_CHECK" "Reloading..."
|
||||
/usr/sbin/haproxy -W -f /etc/haproxy/haproxy.cfg $(ls /etc/haproxy/conf.d/*.cfg 2>/dev/null | xargs -I{} echo -f {}) -p /run/haproxy.pid -x /var/run/haproxy.sock -sf $(cat /run/haproxy.pid) &
|
||||
fi
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
source /scripts/functions.sh
|
||||
|
||||
/usr/sbin/haproxy -v
|
||||
|
||||
if [ -z "$EASYHAPROXY_DATEFORMAT" ]; then
|
||||
export EASYHAPROXY_DATEFORMAT="%Y-%m-%d %H:%M:%S %Z"
|
||||
fi
|
||||
|
||||
if [ -z "$EASYHAPROXY_REFRESH_CONF" ]; then
|
||||
export EASYHAPROXY_REFRESH_CONF=10
|
||||
fi
|
||||
|
||||
|
||||
cat banner.txt
|
||||
echo Release: $RELEASE_VERSION
|
||||
echo
|
||||
echo "Environment"
|
||||
env | sort | grep 'HAPROXY' | xargs -I{} echo " - {} "
|
||||
echo
|
||||
|
||||
/scripts/haproxy-reload.sh initial
|
||||
|
||||
while true; do
|
||||
sleep $EASYHAPROXY_REFRESH_CONF
|
||||
log "info" "CONF_CHECK" "Heartbeat."
|
||||
/scripts/haproxy-reload.sh
|
||||
done
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
import yaml
|
||||
import sys
|
||||
import os
|
||||
|
||||
from easymapping import HaproxyConfigGenerator
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("You need to pass the easyconfig.yml path")
|
||||
exit(1)
|
||||
|
||||
|
||||
with open(sys.argv[1], 'r') as content_file:
|
||||
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
|
||||
|
||||
cfg = HaproxyConfigGenerator(parsed)
|
||||
print(cfg.generate())
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(path + "/letsencrypt_hosts.txt", 'w') as fp:
|
||||
fp.write('\n'.join(cfg.letsencrypt_hosts))
|
||||
|
||||
exit(0)
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import os
|
||||
from easymapping import HaproxyConfigGenerator
|
||||
|
||||
with open("/tmp/.docker_data", 'r') as content_file:
|
||||
line_list = content_file.readlines()
|
||||
|
||||
result = {
|
||||
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
|
||||
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE", "default")
|
||||
}
|
||||
|
||||
if os.getenv("HAPROXY_PASSWORD"):
|
||||
result["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",
|
||||
}
|
||||
|
||||
result["lookup_label"] = os.getenv("EASYHAPROXY_LABEL_PREFIX") if os.getenv("EASYHAPROXY_LABEL_PREFIX") else "easyhaproxy"
|
||||
if (os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")):
|
||||
result["letsencrypt"] = {
|
||||
"email": os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL")
|
||||
}
|
||||
|
||||
|
||||
cfg = HaproxyConfigGenerator(result)
|
||||
print(cfg.generate(line_list))
|
||||
|
||||
path = os.path.dirname(os.path.realpath(__file__))
|
||||
with open(path + "/letsencrypt_hosts.txt", 'w') as fp:
|
||||
fp.write('\n'.join(cfg.letsencrypt_hosts))
|
||||
# print(jsonStr)
|
||||
|
||||
|
||||
|
|
@ -5,9 +5,9 @@ set -e
|
|||
# Start k8s-ci before run this command
|
||||
# docker run --privileged -v /tmp/z:/var/lib/containers -it --rm -v $PWD:/work -w /work byjg/k8s-ci
|
||||
|
||||
if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_PASSWORD" ] || [ -z "$DOCKER_REGISTRY" ]
|
||||
if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_PASSWORD" ] || [ -z "$DOCKER_REGISTRY" ] || [ -z "$VERSIONS" ]
|
||||
then
|
||||
echo You need to setup \$DOCKER_USERNAME, \$DOCKER_PASSWORD and \$DOCKER_REGISTRY before run this command.
|
||||
echo You need to setup \$DOCKER_USERNAME, \$DOCKER_PASSWORD, \$DOCKER_REGISTRY and \$VERSIONS before run this command.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
@ -15,19 +15,17 @@ buildah login --username $DOCKER_USERNAME --password $DOCKER_PASSWORD $DOCKER_RE
|
|||
|
||||
podman run --rm --events-backend=file --cgroup-manager=cgroupfs --privileged docker://multiarch/qemu-user-static --reset -p yes
|
||||
|
||||
VERSIONS="latest $TRAVIS_TAG"
|
||||
for VERSION in $VERSIONS
|
||||
do
|
||||
DOCKERFILE=Dockerfile
|
||||
DOCKERFILE=build/Dockerfile
|
||||
|
||||
buildah manifest create byjg/easy-haproxy:$VERSION
|
||||
|
||||
buildah bud --arch arm64 --os linux --iidfile /tmp/iid-arm64 -f $DOCKERFILE -t byjg/easy-haproxy:$VERSION-arm64 .
|
||||
buildah bud --arch amd64 --os linux --iidfile /tmp/iid-amd64 -f $DOCKERFILE -t byjg/easy-haproxy:$VERSION-amd64 .
|
||||
buildah bud --arch arm64 --os linux --iidfile /tmp/iid-arm64 -f $DOCKERFILE --build-arg=RELEASE_VERSION_ARG="$VERSION-manual" -t byjg/easy-haproxy:$VERSION-arm64 .
|
||||
buildah bud --arch amd64 --os linux --iidfile /tmp/iid-amd64 -f $DOCKERFILE --build-arg=RELEASE_VERSION_ARG="$VERSION-manual" -t byjg/easy-haproxy:$VERSION-amd64 .
|
||||
|
||||
buildah manifest add byjg/easy-haproxy:$VERSION --arch arm64 --os linux --variant v8 $(cat /tmp/iid-arm64)
|
||||
buildah manifest add byjg/easy-haproxy:$VERSION --arch amd64 --os linux --os=linux $(cat /tmp/iid-amd64)
|
||||
|
||||
buildah manifest push --all --format v2s2 byjg/easy-haproxy:$VERSION docker://byjg/easy-haproxy:$VERSION
|
||||
done
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,19 @@ FROM alpine:3.16
|
|||
ARG RELEASE_VERSION_ARG
|
||||
|
||||
ENV RELEASE_VERSION=$RELEASE_VERSION_ARG
|
||||
ENV TZ="Etc/UTC"
|
||||
|
||||
WORKDIR /scripts
|
||||
|
||||
COPY requirements.txt /scripts
|
||||
COPY templates /scripts/templates/
|
||||
COPY easymapping /scripts/easymapping/
|
||||
COPY tests/ /scripts/tests/
|
||||
COPY assets /
|
||||
COPY src/ /scripts/
|
||||
COPY build/assets /
|
||||
|
||||
RUN apk add --no-cache haproxy bash python3 py3-pip py-yaml docker 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 \
|
||||
&& pip install -r requirements.txt \
|
||||
&& pytest -s tests/ \
|
||||
&& pytest -s -vv tests/ \
|
||||
&& openssl dhparam -out /etc/haproxy/dhparam 2048 \
|
||||
&& openssl dhparam -out /etc/haproxy/dhparam-1024 1024
|
||||
|
||||
CMD ["/bin/bash", "-c", "/scripts/haproxy.sh" ]
|
||||
CMD ["/usr/bin/python", "-u", "/scripts/main.py" ]
|
||||
|
|
@ -2,7 +2,7 @@ version: "3"
|
|||
|
||||
services:
|
||||
easyhaproxy:
|
||||
image: byjg/easy-haproxy:4.1.0
|
||||
image: byjg/easy-haproxy:master
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- certs_letsencrypt:/certs/letsencrypt
|
||||
|
|
@ -23,10 +23,15 @@ services:
|
|||
- "443:443/tcp"
|
||||
- "1936:1936/tcp"
|
||||
|
||||
networks:
|
||||
- easyhaproxy
|
||||
|
||||
volumes:
|
||||
certs_letsencrypt:
|
||||
external: true
|
||||
certs_haproxy:
|
||||
external: true
|
||||
|
||||
networks:
|
||||
easyhaproxy:
|
||||
driver: bridge
|
||||
external: true
|
||||
29
deploy/docker/install.sh
Executable file
29
deploy/docker/install.sh
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
|
||||
ASSETS_DIR="$(dirname "${BASH_SOURCE[0]}")"/../../build/assets/certs/haproxy
|
||||
|
||||
docker network create easyhaproxy
|
||||
docker volume create certs_letsencrypt
|
||||
docker volume create certs_haproxy
|
||||
|
||||
docker run -d --rm --name easyhaproxy_install -v certs_haproxy:/certs alpine tail -f /dev/null
|
||||
docker cp $ASSETS_DIR/.place_holder_cert.pem easyhaproxy_install:/certs/.place_holder_cert.pem
|
||||
docker stop easyhaproxy_install
|
||||
|
||||
echo
|
||||
echo
|
||||
echo make sure to add to all of your containers:
|
||||
echo
|
||||
echo docker-compose
|
||||
echo ==============
|
||||
echo "networks:"
|
||||
echo " default:"
|
||||
echo " name: easyhaproxy"
|
||||
echo " external: true"
|
||||
|
||||
echo
|
||||
echo
|
||||
echo docker run
|
||||
echo ==============
|
||||
echo docker run ... --network easyhaproxy ... your_image:tag
|
||||
echo
|
||||
208
deploy/kubernetes/easyhaproxy-clusterip.yml
Normal file
208
deploy/kubernetes/easyhaproxy-clusterip.yml
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
---
|
||||
# Source: easyhaproxy/templates/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
---
|
||||
# Source: easyhaproxy/templates/clusterrole.yaml
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
# - configmaps
|
||||
# - endpoints
|
||||
# - nodes
|
||||
- pods
|
||||
- services
|
||||
- namespaces
|
||||
# - events
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "extensions"
|
||||
- "networking.k8s.io"
|
||||
resources:
|
||||
- ingresses
|
||||
# - ingresses/status
|
||||
# - ingressclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
# - apiGroups:
|
||||
# - "extensions"
|
||||
# - "networking.k8s.io"
|
||||
# resources:
|
||||
# - ingresses/status
|
||||
# verbs:
|
||||
# - update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
# - watch
|
||||
# - create
|
||||
# - patch
|
||||
# - update
|
||||
# - apiGroups:
|
||||
# - "discovery.k8s.io"
|
||||
# resources:
|
||||
# - endpointslices
|
||||
# verbs:
|
||||
# - get
|
||||
# - list
|
||||
# - watch
|
||||
---
|
||||
# Source: easyhaproxy/templates/clusterrolebinding.yaml
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: ingress-easyhaproxy
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
---
|
||||
# Source: easyhaproxy/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
annotations:
|
||||
{}
|
||||
spec:
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
|
||||
- name: https
|
||||
port: 443
|
||||
|
||||
- name: stats
|
||||
port: 1936
|
||||
|
||||
|
||||
|
||||
selector:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
---
|
||||
# Source: easyhaproxy/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: easyhaproxy/node
|
||||
operator: In
|
||||
values:
|
||||
- master
|
||||
serviceAccountName: ingress-easyhaproxy
|
||||
securityContext:
|
||||
{}
|
||||
containers:
|
||||
- name: easyhaproxy
|
||||
securityContext:
|
||||
{}
|
||||
image: "byjg/easy-haproxy:master"
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
|
||||
- name: https
|
||||
containerPort: 443
|
||||
|
||||
- name: stats
|
||||
containerPort: 1936
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
env:
|
||||
- name: EASYHAPROXY_DISCOVER
|
||||
value: kubernetes
|
||||
- name: HAPROXY_USERNAME
|
||||
value: admin
|
||||
- name: HAPROXY_PASSWORD
|
||||
value: password
|
||||
- name: EASYHAPROXY_REFRESH_CONF
|
||||
value: "10"
|
||||
- name: HAPROXY_CUSTOMERRORS
|
||||
value: "true"
|
||||
- name: EASYHAPROXY_SSL_MODE
|
||||
value: loose
|
||||
- name: EASYHAPROXY_LOG_LEVEL
|
||||
value: DEBUG
|
||||
- name: HAPROXY_LOG_LEVEL
|
||||
value: DEBUG
|
||||
- name: CERTBOT_LOG_LEVEL
|
||||
value: DEBUG
|
||||
175
deploy/kubernetes/easyhaproxy-daemonset.yml
Normal file
175
deploy/kubernetes/easyhaproxy-daemonset.yml
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
---
|
||||
# Source: easyhaproxy/templates/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
---
|
||||
# Source: easyhaproxy/templates/clusterrole.yaml
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
# - configmaps
|
||||
# - endpoints
|
||||
# - nodes
|
||||
- pods
|
||||
- services
|
||||
- namespaces
|
||||
# - events
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "extensions"
|
||||
- "networking.k8s.io"
|
||||
resources:
|
||||
- ingresses
|
||||
# - ingresses/status
|
||||
# - ingressclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
# - apiGroups:
|
||||
# - "extensions"
|
||||
# - "networking.k8s.io"
|
||||
# resources:
|
||||
# - ingresses/status
|
||||
# verbs:
|
||||
# - update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
# - watch
|
||||
# - create
|
||||
# - patch
|
||||
# - update
|
||||
# - apiGroups:
|
||||
# - "discovery.k8s.io"
|
||||
# resources:
|
||||
# - endpointslices
|
||||
# verbs:
|
||||
# - get
|
||||
# - list
|
||||
# - watch
|
||||
---
|
||||
# Source: easyhaproxy/templates/clusterrolebinding.yaml
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: ingress-easyhaproxy
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
---
|
||||
# Source: easyhaproxy/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: easyhaproxy/node
|
||||
operator: In
|
||||
values:
|
||||
- master
|
||||
serviceAccountName: ingress-easyhaproxy
|
||||
securityContext:
|
||||
{}
|
||||
containers:
|
||||
- name: easyhaproxy
|
||||
securityContext:
|
||||
{}
|
||||
image: "byjg/easy-haproxy:master"
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
hostPort: 80
|
||||
- name: https
|
||||
containerPort: 443
|
||||
hostPort: 443
|
||||
- name: stats
|
||||
containerPort: 1936
|
||||
hostPort: 1936
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
env:
|
||||
- name: EASYHAPROXY_DISCOVER
|
||||
value: kubernetes
|
||||
- name: HAPROXY_USERNAME
|
||||
value: admin
|
||||
- name: HAPROXY_PASSWORD
|
||||
value: password
|
||||
- name: EASYHAPROXY_REFRESH_CONF
|
||||
value: "10"
|
||||
- name: HAPROXY_CUSTOMERRORS
|
||||
value: "true"
|
||||
- name: EASYHAPROXY_SSL_MODE
|
||||
value: loose
|
||||
- name: EASYHAPROXY_LOG_LEVEL
|
||||
value: DEBUG
|
||||
- name: HAPROXY_LOG_LEVEL
|
||||
value: DEBUG
|
||||
- name: CERTBOT_LOG_LEVEL
|
||||
value: DEBUG
|
||||
208
deploy/kubernetes/easyhaproxy-nodeport.yml
Normal file
208
deploy/kubernetes/easyhaproxy-nodeport.yml
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
---
|
||||
# Source: easyhaproxy/templates/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
---
|
||||
# Source: easyhaproxy/templates/clusterrole.yaml
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
# - configmaps
|
||||
# - endpoints
|
||||
# - nodes
|
||||
- pods
|
||||
- services
|
||||
- namespaces
|
||||
# - events
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "extensions"
|
||||
- "networking.k8s.io"
|
||||
resources:
|
||||
- ingresses
|
||||
# - ingresses/status
|
||||
# - ingressclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
# - apiGroups:
|
||||
# - "extensions"
|
||||
# - "networking.k8s.io"
|
||||
# resources:
|
||||
# - ingresses/status
|
||||
# verbs:
|
||||
# - update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
# - watch
|
||||
# - create
|
||||
# - patch
|
||||
# - update
|
||||
# - apiGroups:
|
||||
# - "discovery.k8s.io"
|
||||
# resources:
|
||||
# - endpointslices
|
||||
# verbs:
|
||||
# - get
|
||||
# - list
|
||||
# - watch
|
||||
---
|
||||
# Source: easyhaproxy/templates/clusterrolebinding.yaml
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: ingress-easyhaproxy
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
---
|
||||
# Source: easyhaproxy/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
annotations:
|
||||
{}
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
nodePort: 31080
|
||||
- name: https
|
||||
port: 443
|
||||
nodePort: 31443
|
||||
- name: stats
|
||||
port: 1936
|
||||
nodePort: 31936
|
||||
|
||||
|
||||
selector:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
---
|
||||
# Source: easyhaproxy/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: ingress-easyhaproxy
|
||||
namespace: easyhaproxy
|
||||
labels:
|
||||
helm.sh/chart: easyhaproxy-0.1.3
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
app.kubernetes.io/version: "master"
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: easyhaproxy
|
||||
app.kubernetes.io/instance: ingress
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: easyhaproxy/node
|
||||
operator: In
|
||||
values:
|
||||
- master
|
||||
serviceAccountName: ingress-easyhaproxy
|
||||
securityContext:
|
||||
{}
|
||||
containers:
|
||||
- name: easyhaproxy
|
||||
securityContext:
|
||||
{}
|
||||
image: "byjg/easy-haproxy:master"
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
|
||||
- name: https
|
||||
containerPort: 443
|
||||
|
||||
- name: stats
|
||||
containerPort: 1936
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
env:
|
||||
- name: EASYHAPROXY_DISCOVER
|
||||
value: kubernetes
|
||||
- name: HAPROXY_USERNAME
|
||||
value: admin
|
||||
- name: HAPROXY_PASSWORD
|
||||
value: password
|
||||
- name: EASYHAPROXY_REFRESH_CONF
|
||||
value: "10"
|
||||
- name: HAPROXY_CUSTOMERRORS
|
||||
value: "true"
|
||||
- name: EASYHAPROXY_SSL_MODE
|
||||
value: loose
|
||||
- name: EASYHAPROXY_LOG_LEVEL
|
||||
value: DEBUG
|
||||
- name: HAPROXY_LOG_LEVEL
|
||||
value: DEBUG
|
||||
- name: CERTBOT_LOG_LEVEL
|
||||
value: DEBUG
|
||||
97
docs/container-labels.md
Normal file
97
docs/container-labels.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# Container Labels
|
||||
|
||||
## Container (Docker or Swarm) labels
|
||||
|
||||
| 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].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].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].sslcert | (Optional) Cert PEM Base64 encoded. Do not use this if `letsencrypt` 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].health-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].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 |
|
||||
|
||||
The `definition` is a string that will group all configurations togethers. Different `definition` will create different configurations.
|
||||
|
||||
The container can have more than one defintion.
|
||||
|
||||
## Configuations
|
||||
|
||||
### Single Definition
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.webapi.port=80\
|
||||
-l easyhaproxy.webapi.host=byjg.com.br \
|
||||
....
|
||||
```
|
||||
|
||||
### Multiples Definitions on the same container
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.express.port=80 \
|
||||
-l easyhaproxy.express.localport=3000 \
|
||||
-l easyhaproxy.express.host=express.byjg.com.br \
|
||||
|
||||
-l easyhaproxy.admin.port=80 \
|
||||
-l easyhaproxy.admin.localport=3001 \
|
||||
-l easyhaproxy.admin.host=admin.byjg.com.br \
|
||||
.... \
|
||||
some/myimage
|
||||
```
|
||||
|
||||
### Multiples hosts on the same container
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.express.port=80 \
|
||||
-l easyhaproxy.express.localport=3000 \
|
||||
-l easyhaproxy.express.host=express.byjg.com.br,admin.byjg.com.br \
|
||||
.... \
|
||||
some/myimage
|
||||
```
|
||||
|
||||
If you are using docker-compose you can use this way:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
mycontainer:
|
||||
image: some/myimage
|
||||
labels:
|
||||
easyhaproxy.express.port: 80
|
||||
easyhaproxy.express.localport: 3000
|
||||
easyhaproxy.express.host: >-
|
||||
express.byjg.com.br,
|
||||
admin.byjg.com.br
|
||||
```
|
||||
|
||||
### TCP Mode
|
||||
|
||||
Set `easyhaproxy.[definition].mode=tcp` if your application uses TCP protocol instead of HTTP.
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.example.mode=tcp \
|
||||
-l easyhaproxy.example.port=3306
|
||||
-l easyhaproxy.example.localport=3306
|
||||
.... \
|
||||
some/tcp-service
|
||||
```
|
||||
|
||||
### Redirect Domains
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.[definition].redirect='{"www.byjg.com.br":"http://byjg.com.br","byjg.com":"http://byjg.com.br"}'
|
||||
```
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
21
docs/docker-environment.md
Normal file
21
docs/docker-environment.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# 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_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)
|
||||
65
docs/docker.md
Normal file
65
docs/docker.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Docker
|
||||
|
||||
## Setup Docker EasyHAProxy
|
||||
|
||||
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 in order to HAProxy be able direct the traffic to the containers.
|
||||
|
||||
e.g.:
|
||||
|
||||
```bash
|
||||
docker create network easyhaproxy
|
||||
```
|
||||
|
||||
And then run the EasyHAProxy
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name easy-haproxy-container \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-e EASYHAPROXY_DISCOVER="docker" \
|
||||
# + Environment Variables \
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
-p 1936:1936 \
|
||||
--network easyhaproxy
|
||||
byjg/easy-haproxy
|
||||
```
|
||||
|
||||
The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels;
|
||||
|
||||
## Running containers
|
||||
|
||||
To make your containers "discoverable" by EasyHAProxy that is minimum configuration you need:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-e easyhaproxy.http.host=example.org \
|
||||
-e easyhaproxy.http.port=80 \
|
||||
-e easyhaproxy.http.localport=8080 \
|
||||
--network easyhaproxy
|
||||
my/image:tag
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
There a list of other parameters you can to configure your container. Please follow the [docker label configuration](container-labels.md)
|
||||
|
||||
## 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)
|
||||
|
||||
|
||||
## Setup certificates with Letsencrypt
|
||||
|
||||
Follow [this link](letsencrypt.md)
|
||||
|
||||
## Setup your own certificates
|
||||
|
||||
Follow [this link](ssl.md)
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
BIN
docs/icons.xcf
Normal file
BIN
docs/icons.xcf
Normal file
Binary file not shown.
198
docs/kubernetes.md
Normal file
198
docs/kubernetes.md
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
# Kubernetes
|
||||
|
||||
## Setup Kubernetes EasyHAProxy
|
||||
|
||||
EasyHAProxy query all ingress definitions with the annotation `kubernetes.io/ingress.class: easyhaproxy-ingress`.Once find the annotation, it will immediatelly setup HAProxy and start to serve it.
|
||||
|
||||
There are three installation modes:
|
||||
|
||||
- DaemonSet: It will expose the ports 80, 443 and 1936
|
||||
- NodePort: It will expose the ports 31080, 31443 and 31936
|
||||
- ClusterIP it will node expose any port. The HAProxy will be accessible only inside the cluster.
|
||||
|
||||
To install the daemonset in your cluster follow these steps:
|
||||
|
||||
### 1) Identify the node where your EasyHAProxy container will run.
|
||||
|
||||
Doesn't matter if you choose DaemonSet or ClusterIP, EasyHAProxy will be limited to a single node. To understand that see [limitations](limitations.md) page.
|
||||
|
||||
```bash
|
||||
$ kubectl get nodes
|
||||
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
node-01 Ready <none> 561d v1.21.13-3
|
||||
node-02 Ready <none> 561d v1.21.13-3
|
||||
```
|
||||
|
||||
Add the EasyHAProxy label to the node
|
||||
|
||||
```bash
|
||||
kubectl label nodes node-01 "easyhaproxy/node=master"
|
||||
```
|
||||
|
||||
### 2) Install EasyHAProxy
|
||||
|
||||
There are two ways to install EasyHAProxy in a Kubernetes cluster. You can use Kubernetes Manifest or Helm 3.
|
||||
|
||||
#### 2.1.) Using Kubernetes Manifest
|
||||
|
||||
```bash
|
||||
kubectl create namespace easyhaproxy
|
||||
|
||||
kubectl apply -f \
|
||||
https://raw.githubusercontent.com/byjg/docker-easy-haproxy/kubernetes/deploy/kubernetes/easyhaproxy-daemonset.yml
|
||||
```
|
||||
|
||||
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)
|
||||
|
||||
#### 2.2) Using HELM 3
|
||||
|
||||
Minimal configuration
|
||||
|
||||
```bash
|
||||
helm repo add byjg https://opensource.byjg.com/helm
|
||||
helm repo update byjg
|
||||
kubectl create namespace easyhaproxy
|
||||
|
||||
helm upgrade --install ingress byjg/easyhaproxy \
|
||||
--namespace easyhaproxy \
|
||||
--set resources.requests.cpu=100m \
|
||||
--set resources.requests.memory=128Mi
|
||||
```
|
||||
|
||||
Customizing Helm Values:
|
||||
|
||||
```yaml
|
||||
easyhaproxy:
|
||||
stats:
|
||||
username: admin
|
||||
password: password
|
||||
refresh: "10"
|
||||
customErrors: "true"
|
||||
sslMode: loose
|
||||
logLevel:
|
||||
certbot: DEBUG
|
||||
easyhaproxy: DEBUG
|
||||
haproxy: DEBUG
|
||||
|
||||
service:
|
||||
create: false # If false, it will create a Daemonset with hostPort. The easiest.
|
||||
type: ClusterIP # or NodePort
|
||||
annotations: {}
|
||||
|
||||
binding:
|
||||
ports:
|
||||
http: 80
|
||||
https: 443
|
||||
stats: 1936
|
||||
additionalPorts: []
|
||||
|
||||
# Make sure to create this
|
||||
masterNode:
|
||||
label: easyhaproxy/node
|
||||
values:
|
||||
- master
|
||||
```
|
||||
|
||||
## Running containers
|
||||
|
||||
The only requirement is that you have an ingress properly setup and with the annotation `kubernetes.io/ingress.class: easyhaproxy-ingress`.
|
||||
|
||||
e.g.
|
||||
|
||||
```yaml
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||
name: example-ingress
|
||||
namespace: example
|
||||
spec:
|
||||
rules:
|
||||
- host: example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: example-service
|
||||
port:
|
||||
number: 8080
|
||||
pathType: ImplementationSpecific
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
Caveats:
|
||||
|
||||
- At this point, the implementation don't support all ingress properties nor wildcard domains.
|
||||
- The ingress will publish externally only the ports 80 and 443, plus 1936 if stats is enable.
|
||||
- EasyHAProxy will read all `spec.rules[].host` spec, however it will parse only the first path `spec.rules[].http.paths[0].port.number` for each rule, and ignore the other paths.
|
||||
|
||||
## Kubernetes annotations
|
||||
|
||||
| annotation | Description | Default | Example |
|
||||
|-----------------------------|-----------------------------------------------------------------------------------------|--------------|--------------|
|
||||
| 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.letsencrypt | (optional) Boolean. It will request letsencript certificates for the ingresses domains. | false | true or false
|
||||
| easyhaproxy.redirect | (optional) Json. Specific a domain and its destination. | *empty* | {"domain":"redirect_url"}
|
||||
| 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
|
||||
|
||||
**Important**: The annotations are per ingress and applied to all hosts in that ingress configuration.
|
||||
|
||||
## Letsencrypt
|
||||
|
||||
It is necessary add the annotation `easyhaproxy.letsencrypt` to the ingress configuration:
|
||||
|
||||
```yaml
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||
easyhaproxy.letsencrypt: 'true'
|
||||
name: example-ingress
|
||||
namespace: example
|
||||
spec:
|
||||
....
|
||||
```
|
||||
|
||||
Make sure your cluster is accessible both through ports 80 and 443.
|
||||
|
||||
## Custom SSL Certificates
|
||||
|
||||
You need to create a secret with your certificate and key, and associate them in your ingress.
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: host2-tls
|
||||
namespace: default
|
||||
data:
|
||||
tls.crt: base64 of your certificate
|
||||
tls.key: base64 of your certificate private key
|
||||
type: kubernetes.io/tls
|
||||
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||
name: tls-example
|
||||
namespace: default
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- host2.local
|
||||
secretName: host2-tls
|
||||
rules:
|
||||
...
|
||||
```
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
52
docs/letsencrypt.md
Normal file
52
docs/letsencrypt.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# Letsencrypt
|
||||
|
||||
EasyHAProxy can issue a letsencrypt certificate. The command is as below:
|
||||
|
||||
Run the EasyHAProxy:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-e EASYHAPROXY_LETSENCRYPT_EMAIL=john@doe.com
|
||||
.... \
|
||||
byjg/easy-haproxy
|
||||
```
|
||||
|
||||
Run your container:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
-l easyhaproxy.express.port=80 \
|
||||
-l easyhaproxy.express.localport=3000 \
|
||||
-l easyhaproxy.express.host=example.org \
|
||||
-l easyhaproxy.express.letsencrypt=true \
|
||||
.... \
|
||||
some/myimage
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- Your container **must** listen to the port 80. Besides no error, the certificate won't be issued if in a different port.
|
||||
- You cannot set the port 443 for the container with the Letsencrypt because EasyHAProxy will handle this automatically once the certificate is issued.
|
||||
- You have to setup the `EASYHAPROXY_LETSENCRYPT_EMAIL` environment variable on EasyHAProxy. If you don't setup, 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 a persistent storage, even you knowing you can issue again in case your lost the certificate.
|
||||
|
||||
However, there is a limit in how many certificates can be issue for the same domain in a period of time.
|
||||
|
||||
To avoid 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)
|
||||
16
docs/limitations.md
Normal file
16
docs/limitations.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Limitations
|
||||
|
||||
EasyHAProxy currently expects to work in a single replica.
|
||||
|
||||
If more than one replica is running, EasyHAProxy will continue to work, however each replica will discover
|
||||
the services independently.
|
||||
|
||||
It means replicas can be out-of-sync for a few seconds because each replica will discover the pods
|
||||
separately.
|
||||
|
||||
For Letsencrypt this is worse because each replica will a Letsencrypt certificate and can fail because the
|
||||
letsencrypt challenge can be directed to the other replica. Also, you can hit the certificate issue limit.
|
||||
So if you intend to run multiple replicas **do not** activate letsencrypt.
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
BIN
docs/logo.xcf
Normal file
BIN
docs/logo.xcf
Normal file
Binary file not shown.
39
docs/other.md
Normal file
39
docs/other.md
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Other configurations
|
||||
|
||||
## Exposing Ports
|
||||
|
||||
You must expose some ports on the EasyHAProxy container and in the firewall. However, you don't need to expose the other container ports because EasyHAProxy will handle that.
|
||||
|
||||
- The ports `80` and `443`.
|
||||
- If you enable the HAProxy statistics, you must also expose the port defined in `HAPROXY_STATS_PORT` environment variable (default 1936). Be aware that statististics are enabled by default with no password.
|
||||
- Every port defined in `easyhaproxy.[definitions].port` also should be exposed.
|
||||
|
||||
e.g.
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
-p 1936:1936 \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
## Mapping custom .cfg files
|
||||
|
||||
You can concatenate valid HAProxy `.cfg` files to the dynamically generated `haproxy.cfg` by mapping the folder `/etc/haproxy/conf.d`.
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-v /your/local/conf.d:/etc/haproxy/conf.d \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
## Setting Custom Errors
|
||||
|
||||
If enabled, map the volume : `/etc/haproxy/errors-custom/` to your container and put a file named `ERROR_NUMBER.http`
|
||||
where ERROR_NUMBER is the HTTP error code (e.g., `503.http`)
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
75
docs/ssl.md
Normal file
75
docs/ssl.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# Setup your own certificates
|
||||
|
||||
You can use your certificates with EasyHAProxy.
|
||||
|
||||
There is two ways to do that.
|
||||
|
||||
## Setup certificate as a label definition in docker container
|
||||
|
||||
1. First, Create a single PEM from the certificate and the key.
|
||||
|
||||
```bash
|
||||
cat example.com.crt example.com.key > single.pem
|
||||
|
||||
cat single.pem
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+
|
||||
U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
|
||||
3j4wz8/I5fdsk090j4s5KA==
|
||||
-----END PRIVATE KEY-----
|
||||
```
|
||||
|
||||
2. Convert the `single.pem` to BASE64 in a single line:
|
||||
|
||||
```bash
|
||||
cat single.pem | base64 -w0
|
||||
```
|
||||
|
||||
3. Use this string to define the label `easyhaproxy.[definition].sslcert`
|
||||
|
||||
## Map the certificate as docker volume
|
||||
|
||||
EasyHAProxy stores the certificates inside the folder `/certs/haproxy`.
|
||||
|
||||
1. Run EasyHAProxy with the volume for the certificates:
|
||||
|
||||
```bash
|
||||
docker volume create certs_haproxy
|
||||
|
||||
docker run \
|
||||
/* other parameters */
|
||||
-v certs_haproxy:/certs/haproxy \
|
||||
-d byjg/easy-haproxy
|
||||
```
|
||||
|
||||
2. Create a single PEM from the certificate and the key.
|
||||
|
||||
```bash
|
||||
cat example.com.crt example.com.key > single.pem
|
||||
|
||||
cat single.pem
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC5ZheHqmBnEJP+
|
||||
U9r1gxYWKLzdqrMrcxtQN6M1hIH9n0peuJeIrybdcV7sMbStMXI=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEojCCA4qgAwIBAgIUegW2BimwuL4RzRZ2WYkHA6U5nkAwDQYJKoZIhvcNAQEL
|
||||
3j4wz8/I5fdsk090j4s5KA==
|
||||
-----END PRIVATE KEY-----
|
||||
```
|
||||
|
||||
3. Copy this certificate to EasyHAProxy volume
|
||||
|
||||
```bash
|
||||
docker cp single.pem easyhaproxy:/certs/haproxy
|
||||
```
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
109
docs/static.md
Normal file
109
docs/static.md
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
# Docker
|
||||
|
||||
## Setup Docker EasyHAProxy
|
||||
|
||||
This method will use a static configuration, simpler and easier than HAProxy to create the `haproxy.cfg`
|
||||
|
||||
You can use this configuration to setup external servers not related to docker or kubernetes.
|
||||
|
||||
Another advantage is that EasyHAProxy will monitor for changes in this file and automatically reconfigure HAProxy and changes are detected.
|
||||
|
||||
First, Create a YAML:
|
||||
|
||||
```yaml
|
||||
stats:
|
||||
username: admin
|
||||
password: password
|
||||
port: 1936 # Optional (default 1936)
|
||||
|
||||
customerrors: true # Optional (default false)
|
||||
|
||||
ssl_mode: default
|
||||
|
||||
letsencrypt: {
|
||||
"email": "acme@example.org"
|
||||
}
|
||||
|
||||
easymapping:
|
||||
- port: 80
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
- container:5000
|
||||
letsencrypt: true
|
||||
redirect_ssl: true
|
||||
host2.com.br:
|
||||
containers:
|
||||
- other:3000
|
||||
redirect:
|
||||
www.host1.com.br: http://host1.com.br
|
||||
|
||||
- port: 443
|
||||
hosts:
|
||||
host1.com.br:
|
||||
containers:
|
||||
- container:80
|
||||
redirect_ssl: false
|
||||
ssl: true
|
||||
|
||||
- port: 8080
|
||||
hosts:
|
||||
host3.com.br:
|
||||
containers:
|
||||
- domain:8181
|
||||
```
|
||||
|
||||
Then map this file to `/etc/haproxy/easyconfig.yml` in your EasyHAProxy container as:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name easy-haproxy-container \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v /my/config.yml:/etc/haproxy/easyconfig.yml
|
||||
-e EASYHAPROXY_DISCOVER="static" \
|
||||
# + Environment Variables \
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
-p 1936:1936 \
|
||||
--network easyhaproxy
|
||||
byjg/easy-haproxy
|
||||
```
|
||||
|
||||
You can find other informations on [docker label configuration](container-labels.md) and [docker container environment](docker-environment.md)
|
||||
|
||||
## Yaml Definition
|
||||
|
||||
```yaml
|
||||
stats:
|
||||
username: admin # Optional (default "admin")
|
||||
password: password # If stats or stats.password is omitted, stats will be public with no password
|
||||
port: 1936 # Optional (default 1936)
|
||||
|
||||
customerrors: true # Optional (default false)
|
||||
|
||||
ssl_mode: default # Optional
|
||||
|
||||
letsencrypt: { # Optional. If you enable `letsencrypt` will need to setu0p this,
|
||||
# otherwise the certificate will be issued
|
||||
"email": "acme@example.org"
|
||||
}
|
||||
|
||||
easymapping:
|
||||
- port: 80 # Listen port
|
||||
mode: http # Optional. Default `http`. Can be http or tcp
|
||||
hosts:
|
||||
host1.com.br: # Hostname
|
||||
containers:
|
||||
- container:5000 # Endpoints of the hostname above (ip, dns, container, etc)
|
||||
letsencrypt: true # Optional. it will request a letsencrypt certiticate
|
||||
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
|
||||
clone_to_ssl: true # Optional. Default False. You clone these hosts to it equivalent SSL.
|
||||
redirect:
|
||||
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.
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
96
docs/swarm.md
Normal file
96
docs/swarm.md
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
# Swarm
|
||||
|
||||
## Setup Docker EasyHAProxy
|
||||
|
||||
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 container in other nodes from cluster.
|
||||
|
||||
The only requirement is that containers and EasyHAProxy must be in the same docker swarm network in order to HAProxy be able direct the traffic to the containers.
|
||||
|
||||
e.g.:
|
||||
|
||||
```bash
|
||||
docker create network easyhaproxy
|
||||
```
|
||||
|
||||
And then deploy the EasyHAProxy stack:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
haproxy:
|
||||
image: byjg/easy-haproxy
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
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"
|
||||
networks:
|
||||
- easyhaproxy
|
||||
|
||||
networks:
|
||||
easyhaproxy:
|
||||
external: true
|
||||
```
|
||||
|
||||
and then:
|
||||
|
||||
```bash
|
||||
docker stack deploy --compose-file docker-compose.yml easyhaproxy
|
||||
```
|
||||
|
||||
The mapping to `/var/run/docker.sock` is necessary to discover the docker containers and get the labels;
|
||||
|
||||
**Do not** add more than one replica for EasyHAProxy. To understand that see [limitations](limitations.md) page.
|
||||
|
||||
## Running containers
|
||||
|
||||
To make your containers "discoverable" by EasyHAProxy that is minimum configuration you need:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
container:
|
||||
image: my/image:tag
|
||||
deploy:
|
||||
replicas: 1
|
||||
labels:
|
||||
easyhaproxy.http.host: host1.local
|
||||
easyhaproxy.http.port: 80
|
||||
easyhaproxy.http.localport: 8080
|
||||
networks:
|
||||
- easyhaproxy
|
||||
|
||||
networks:
|
||||
easyhaproxy:
|
||||
external: true
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
There a list of other parameters you can to configure your container. Please follow the [docker label configuration](container-labels.md)
|
||||
|
||||
## 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)
|
||||
|
||||
## More information
|
||||
|
||||
You can refer the [Docker Documentation](docker.md) to get other detailed instructions.
|
||||
|
||||
----
|
||||
[Open source ByJG](http://opensource.byjg.com)
|
||||
BIN
easyhaproxy_docker.png
Normal file
BIN
easyhaproxy_docker.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
BIN
easyhaproxy_kubernetes.png
Normal file
BIN
easyhaproxy_kubernetes.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
easyhaproxy_logo.png
Normal file
BIN
easyhaproxy_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
easyhaproxy_static.png
Normal file
BIN
easyhaproxy_static.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
easyhaproxy_swarm.png
Normal file
BIN
easyhaproxy_swarm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
80
examples/kubernetes/service.yml
Normal file
80
examples/kubernetes/service.yml
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||
name: container-example
|
||||
namespace: default
|
||||
spec:
|
||||
rules:
|
||||
- host: example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: container-example
|
||||
port:
|
||||
number: 8080
|
||||
pathType: ImplementationSpecific
|
||||
- host: www.example.org
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: container-example
|
||||
port:
|
||||
number: 8080
|
||||
pathType: ImplementationSpecific
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: container-example
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
selector:
|
||||
app: container-example
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: container-example
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: container-example
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: container-example
|
||||
spec:
|
||||
containers:
|
||||
- name: container-example
|
||||
image: byjg/static-httpserver
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: '0.05'
|
||||
memory: '20Mi'
|
||||
requests:
|
||||
cpu: '0.05'
|
||||
memory: '20Mi'
|
||||
env:
|
||||
- name: TITLE
|
||||
value: "My Host Example"
|
||||
86
examples/kubernetes/service_tls.yml
Normal file
86
examples/kubernetes/service_tls.yml
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: easyhaproxy-ingress
|
||||
name: tls-example
|
||||
namespace: default
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- host2.local
|
||||
secretName: host2-tls
|
||||
rules:
|
||||
- host: host2.local
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: tls-example
|
||||
port:
|
||||
number: 8080
|
||||
pathType: ImplementationSpecific
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: host2-tls
|
||||
namespace: default
|
||||
data:
|
||||
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxVENDQXBHZ0F3SUJBZ0lVSWQ1Yjl0OXVxSDc4ZzAyRXpiV0Y2RktWdzNnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pERUxNQWtHQTFVRUJoTUNRbEl4RnpBVkJnTlZCQWdNRGxKcGJ5QmtaU0JLWVc1bGFYSnZNUmN3RlFZRApWUVFIREE1U2FXOGdaR1VnU21GdVpXbHliekVOTUFzR0ExVUVDZ3dFUVVOTlJURVVNQklHQTFVRUF3d0xhRzl6CmRESXViRzlqWVd3d0hoY05Nakl3T0RFMU1EUXlOekExV2hjTk1qTXdPREUxTURReU56QTFXakJrTVFzd0NRWUQKVlFRR0V3SkNVakVYTUJVR0ExVUVDQXdPVW1sdklHUmxJRXBoYm1WcGNtOHhGekFWQmdOVkJBY01EbEpwYnlCawpaU0JLWVc1bGFYSnZNUTB3Q3dZRFZRUUtEQVJCUTAxRk1SUXdFZ1lEVlFRRERBdG9iM04wTWk1c2IyTmhiRENDCkFTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTVNLdnJPYWhhdkNYbnZTRjUxMzFocG8KNms2NUM1N2pnUlE4NEZhRGo1TWJKT1ZsWVFWRnRNRzBYT2s3YStoaDV2MWZlNHdIMFI3STZGRG8wVjlzUytzcwprbzVic0VsYzF4WWxnNUhidUtxODl2UlNLZzZFRGx6dHgzQktiaTkxMlBtdDV2RkdOSjE2emN3NzdEVXJRSVhvCjRJL2I0YTNwbUJpV2o0M05vVElybVNXSHRzR3d3T2ozaUR2U3dlcWRZWEpJcjNocEhINXU2cG9oakRvUXZxRHoKSzZNdThwNm1oQ1VLTnM3S0ZKbk5Jbk5HMjVvUVQ2TzBuNE9HdG1nUmpMV29wZEVuT2hNa0tzZklvSTFYdGxYQgpMQkR2N2h1SUNrM3Q1eXd0ZkNReU8wOWtYN2xGSWdkNXJuNytNandINVdOZXFiUUp4dWFxam9YUW5OWlVnVXNDCkF3RUFBYU5UTUZFd0hRWURWUjBPQkJZRUZOaE1CRzhxNmEraUsybkVDd1ZUbjZCOUVYWk9NQjhHQTFVZEl3UVkKTUJhQUZOaE1CRzhxNmEraUsybkVDd1ZUbjZCOUVYWk9NQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEUVlKS29aSQpodmNOQVFFTEJRQURnZ0VCQUptdWR2eDgrcDVpSVVzVDhmbS9mYlZNMERBNnFXQUxEWVVKblRuM2o2THE0dnBmClBGQytxMUxtdVdmQlFNeXFLckhyUDNlNDkzRWN0WG9pU0taTzZpTjVkVkpJdXIwMk9qR3VpQUVjc1l1WTFuTG4KczlwaWlJK1VFd3hINnV4MU5hSFVuenNXYXVvQnZSaHpqWHZPNlNBVlNaSllhOWRZNW1pelhrbER5RE51RzVVMApsWHY5ZWdNR0JzeTBkRzZlRlhrVTVDUGR4V1U1NDB5STJzQ3RTQWo3eitXUlVENWs3Z0o3dFZvWTMvL2pIUVpHCjVTVFRtbTV0OWtwSVpUV2twdHlKb3M5b1pKRllNSVhxVzJGYzZ0eUxacFJwMzFSNzh0RHM2RVRJa1RvRGMwUlIKano2NnRoNkhJK1psZ0lCUWh3MDkraFlBaEJEZTkrRG1kL1N6UVpjPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t
|
||||
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktrd2dnU2xBZ0VBQW9JQkFRREVpcjZ6bW9XcndsNTcKMGhlZGQ5WWFhT3BPdVF1ZTQ0RVVQT0JXZzQrVEd5VGxaV0VGUmJUQnRGenBPMnZvWWViOVgzdU1COUVleU9oUQo2TkZmYkV2ckxKS09XN0JKWE5jV0pZT1IyN2lxdlBiMFVpb09oQTVjN2Nkd1NtNHZkZGo1cmVieFJqU2RlczNNCk8rdzFLMENGNk9DUDIrR3Q2WmdZbG8rTnphRXlLNWtsaDdiQnNNRG85NGc3MHNIcW5XRnlTSzk0YVJ4K2J1cWEKSVl3NkVMNmc4eXVqTHZLZXBvUWxDamJPeWhTWnpTSnpSdHVhRUUranRKK0RoclpvRVl5MXFLWFJKem9USkNySAp5S0NOVjdaVndTd1E3KzRiaUFwTjdlY3NMWHdrTWp0UFpGKzVSU0lIZWE1Ky9qSThCK1ZqWHFtMENjYm1xbzZGCjBKeldWSUZMQWdNQkFBRUNnZ0VCQUtjZWl0VlJPUVE1ZS9teFJSOUNmSzFzTkgvSDNOZTMvMVBrQjZYSXJGYWIKcUIzZXZFYXRaT3VvbjdBNk5LRWVUamwzN1NlK3BkU1ZaT1VYY3FDL0J6YnJhWnJlMytFaHJrcElqNzJBcFYrWQoyaXdaaVdWYWFKUWdJNHVaM21OQXc4UmFXSnNqNVMxYTlJOExET2lRNUlaNDVDbXZBQkRQSmVNU2N2SlN2UlJZCmU1TjBMNnN0cVM3WitJb3lHVktVZnAxaU5PMFl5eXdPVWlTa0lSWGdzY3VSWFpHWXBpR1BvbXNKK0pzMWVqelcKanlTdGxaSkVyNEwxMjg1ckdQcm1IcWpUd0ZkK2hHODBXYzQxNzl4TCtXUkU2SEJFVVpTaXk5NWZlNmtjUEhYWApCZ2lWWXRjRkttaUJpMmRUYnhsNGU5NHV0MjM5aTBIdGxKMVpKdExoK0JFQ2dZRUE4YTI5OEsyelhrSG9zeGhOCnRSckg3WGZNUFRrSEREZDNyeE0yMUxUK2ZJWHFpbkdVcDlMWWFEY2JqdVRQczhlMzN1S01kN1I2UTQweDg5eVcKSVhOa2EvVkwwUFhVZVY2N2FDVkxMcWdYRExHdWRsdUppbkgwWHZtSTBDbUJlY0ZTTXFJRm1RbGdxRVJvR0dzMwpVTWFjMHA4NzZUNFhrR1FRSmRmNjJiRnBFNFVDZ1lFQTBEQkgwUERsT3B3WGNjRGdYck1mYXlyOEhBaEkrRzVSCnlXUS8vOWlpcnRVODNjaHdJV3draDUzZUxMTXpMZ2RxSm5QaVd5YVVXNUJxem1ZdUQyM25oeGNRN1BOZElxT08KSDFzRTZ6cUxOc2h2NDZ0NVFLbGgxUTRxamQ3VXF0Z3JTclk2M1JYSkNNV1R3bk5NZURMdGo4Z2FLYmprckczUgpCTTJpbGR0NlVvOENnWUJYN05EVWxpMVNsb0gxWGxzdkQwNDdTOEZIYU03eWw5OTRGM0owVW1EZnBzemNqMVA0CjlwRjY0TW1xNC8zWXQwbGkwbU11VGIvSmdiM3hyWWdGSlhrY2VjS2FoRVZIM3JvcHVwK3Vtc0xBQUlpclVNUXEKVlNrRndKMFF0bmovZGVEVXdQTnVhT1g4Y2Q2NU81Q0ZWNnpJUjl4QkVERDhmQnNQMlpMT3ptZWZEUUtCZ1FERgptMjR2VnRoZC8xY0pkQ2dEKzBWeE5ZWERIZUlWWExGbzFTMGlMWUNOTG4zdGpabFJRQktVWHpaSmUzYXkwL3JmCnNOTkQ3YVNZSE1Za1R6eWRESmJjMVBvTnp4bXlEVWlUWHBPV3F5VUV4TS9mYkIxVlVQRTVoNDdBeHFkWjJvR04KRXRkZ2pwTVpMbUNJQzJTa0dzTCszTkpvazhVS0hkcHVFcm1tUUlNazVRS0JnUUNnRVdjWXRMWEMzWURZTUZkSQpVZ2NUZWJGcVNzM21MWWd1YjF4ZWtXM0lYUjJ5b200VjVmUVRMaUY3WWZuMmRwRFc0SWNNVTBVSkZZVnNVbGhLCmFHdGV0NFZtNU5uOCtNZ2hvdDV5QWpxTzl5QVVhdWI3d2dpZktJZTk5dFFLZDh1WnlDdkowaGh2bUREU2Z4NG0KQi9URWlGQU85OXlGNDlpU3hFVlNBUzZwcVE9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t
|
||||
type: kubernetes.io/tls
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tls-example
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
selector:
|
||||
app: tls-example
|
||||
type: ClusterIP
|
||||
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: tls-example
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
revisionHistoryLimit: 10
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
type: RollingUpdate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: tls-example
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: tls-example
|
||||
spec:
|
||||
containers:
|
||||
- name: tls-example
|
||||
image: byjg/static-httpserver
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: '0.05'
|
||||
memory: '20Mi'
|
||||
requests:
|
||||
cpu: '0.05'
|
||||
memory: '20Mi'
|
||||
env:
|
||||
- name: TITLE
|
||||
value: "My Host Example"
|
||||
67
examples/swarm/docker-compose.yml
Normal file
67
examples/swarm/docker-compose.yml
Normal file
File diff suppressed because one or more lines are too long
50
examples/swarm/host2.local.pem
Normal file
50
examples/swarm/host2.local.pem
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDqTCCApGgAwIBAgIUId5b9t9uqH78g02EzbWF6FKVw3gwDQYJKoZIhvcNAQEL
|
||||
BQAwZDELMAkGA1UEBhMCQlIxFzAVBgNVBAgMDlJpbyBkZSBKYW5laXJvMRcwFQYD
|
||||
VQQHDA5SaW8gZGUgSmFuZWlybzENMAsGA1UECgwEQUNNRTEUMBIGA1UEAwwLaG9z
|
||||
dDIubG9jYWwwHhcNMjIwODE1MDQyNzA1WhcNMjMwODE1MDQyNzA1WjBkMQswCQYD
|
||||
VQQGEwJCUjEXMBUGA1UECAwOUmlvIGRlIEphbmVpcm8xFzAVBgNVBAcMDlJpbyBk
|
||||
ZSBKYW5laXJvMQ0wCwYDVQQKDARBQ01FMRQwEgYDVQQDDAtob3N0Mi5sb2NhbDCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMSKvrOahavCXnvSF5131hpo
|
||||
6k65C57jgRQ84FaDj5MbJOVlYQVFtMG0XOk7a+hh5v1fe4wH0R7I6FDo0V9sS+ss
|
||||
ko5bsElc1xYlg5HbuKq89vRSKg6EDlztx3BKbi912Pmt5vFGNJ16zcw77DUrQIXo
|
||||
4I/b4a3pmBiWj43NoTIrmSWHtsGwwOj3iDvSweqdYXJIr3hpHH5u6pohjDoQvqDz
|
||||
K6Mu8p6mhCUKNs7KFJnNInNG25oQT6O0n4OGtmgRjLWopdEnOhMkKsfIoI1XtlXB
|
||||
LBDv7huICk3t5ywtfCQyO09kX7lFIgd5rn7+MjwH5WNeqbQJxuaqjoXQnNZUgUsC
|
||||
AwEAAaNTMFEwHQYDVR0OBBYEFNhMBG8q6a+iK2nECwVTn6B9EXZOMB8GA1UdIwQY
|
||||
MBaAFNhMBG8q6a+iK2nECwVTn6B9EXZOMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI
|
||||
hvcNAQELBQADggEBAJmudvx8+p5iIUsT8fm/fbVM0DA6qWALDYUJnTn3j6Lq4vpf
|
||||
PFC+q1LmuWfBQMyqKrHrP3e493EctXoiSKZO6iN5dVJIur02OjGuiAEcsYuY1nLn
|
||||
s9piiI+UEwxH6ux1NaHUnzsWauoBvRhzjXvO6SAVSZJYa9dY5mizXklDyDNuG5U0
|
||||
lXv9egMGBsy0dG6eFXkU5CPdxWU540yI2sCtSAj7z+WRUD5k7gJ7tVoY3//jHQZG
|
||||
5STTmm5t9kpIZTWkptyJos9oZJFYMIXqW2Fc6tyLZpRp31R78tDs6ETIkToDc0RR
|
||||
jz66th6HI+ZlgIBQhw09+hYAhBDe9+Dmd/SzQZc=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDEir6zmoWrwl57
|
||||
0hedd9YaaOpOuQue44EUPOBWg4+TGyTlZWEFRbTBtFzpO2voYeb9X3uMB9EeyOhQ
|
||||
6NFfbEvrLJKOW7BJXNcWJYOR27iqvPb0UioOhA5c7cdwSm4vddj5rebxRjSdes3M
|
||||
O+w1K0CF6OCP2+Gt6ZgYlo+NzaEyK5klh7bBsMDo94g70sHqnWFySK94aRx+buqa
|
||||
IYw6EL6g8yujLvKepoQlCjbOyhSZzSJzRtuaEE+jtJ+DhrZoEYy1qKXRJzoTJCrH
|
||||
yKCNV7ZVwSwQ7+4biApN7ecsLXwkMjtPZF+5RSIHea5+/jI8B+VjXqm0Ccbmqo6F
|
||||
0JzWVIFLAgMBAAECggEBAKceitVROQQ5e/mxRR9CfK1sNH/H3Ne3/1PkB6XIrFab
|
||||
qB3evEatZOuon7A6NKEeTjl37Se+pdSVZOUXcqC/BzbraZre3+EhrkpIj72ApV+Y
|
||||
2iwZiWVaaJQgI4uZ3mNAw8RaWJsj5S1a9I8LDOiQ5IZ45CmvABDPJeMScvJSvRRY
|
||||
e5N0L6stqS7Z+IoyGVKUfp1iNO0YyywOUiSkIRXgscuRXZGYpiGPomsJ+Js1ejzW
|
||||
jyStlZJEr4L1285rGPrmHqjTwFd+hG80Wc4179xL+WRE6HBEUZSiy95fe6kcPHXX
|
||||
BgiVYtcFKmiBi2dTbxl4e94ut239i0HtlJ1ZJtLh+BECgYEA8a298K2zXkHosxhN
|
||||
tRrH7XfMPTkHDDd3rxM21LT+fIXqinGUp9LYaDcbjuTPs8e33uKMd7R6Q40x89yW
|
||||
IXNka/VL0PXUeV67aCVLLqgXDLGudluJinH0XvmI0CmBecFSMqIFmQlgqERoGGs3
|
||||
UMac0p876T4XkGQQJdf62bFpE4UCgYEA0DBH0PDlOpwXccDgXrMfayr8HAhI+G5R
|
||||
yWQ//9iirtU83chwIWwkh53eLLMzLgdqJnPiWyaUW5BqzmYuD23nhxcQ7PNdIqOO
|
||||
H1sE6zqLNshv46t5QKlh1Q4qjd7UqtgrSrY63RXJCMWTwnNMeDLtj8gaKbjkrG3R
|
||||
BM2ildt6Uo8CgYBX7NDUli1SloH1XlsvD047S8FHaM7yl994F3J0UmDfpszcj1P4
|
||||
9pF64Mmq4/3Yt0li0mMuTb/Jgb3xrYgFJXkcecKahEVH3ropup+umsLAAIirUMQq
|
||||
VSkFwJ0Qtnj/deDUwPNuaOX8cd65O5CFV6zIR9xBEDD8fBsP2ZLOzmefDQKBgQDF
|
||||
m24vVthd/1cJdCgD+0VxNYXDHeIVXLFo1S0iLYCNLn3tjZlRQBKUXzZJe3ay0/rf
|
||||
sNND7aSYHMYkTzydDJbc1PoNzxmyDUiTXpOWqyUExM/fbB1VUPE5h47AxqdZ2oGN
|
||||
EtdgjpMZLmCIC2SkGsL+3NJok8UKHdpuErmmQIMk5QKBgQCgEWcYtLXC3YDYMFdI
|
||||
UgcTebFqSs3mLYgub1xekW3IXR2yom4V5fQTLiF7Yfn2dpDW4IcMU0UJFYVsUlhK
|
||||
aGtet4Vm5Nn8+Mghot5yAjqO9yAUaub7wgifKIe99tQKd8uZyCvJ0hhvmDDSfx4m
|
||||
B/TEiFAO99yF49iSxEVSAS6pqQ==
|
||||
-----END PRIVATE KEY-----
|
||||
23
helm/easyhaproxy/.helmignore
Normal file
23
helm/easyhaproxy/.helmignore
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
24
helm/easyhaproxy/Chart.yaml
Normal file
24
helm/easyhaproxy/Chart.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
apiVersion: v2
|
||||
name: easyhaproxy
|
||||
description: "EasyHAProxy - A service discovery backed on HAProxy"
|
||||
|
||||
# A chart can be either an 'application' or a 'library' chart.
|
||||
#
|
||||
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||
# to be deployed.
|
||||
#
|
||||
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||
type: application
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.3
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
# follow Semantic Versioning. They should reflect the version the application is using.
|
||||
# It is recommended to use it with quotes.
|
||||
appVersion: "master"
|
||||
62
helm/easyhaproxy/templates/_helpers.tpl
Normal file
62
helm/easyhaproxy/templates/_helpers.tpl
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{{/*
|
||||
Expand the name of the chart.
|
||||
*/}}
|
||||
{{- define "easyhaproxy.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create a default fully qualified app name.
|
||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
||||
If release name contains chart name it will be used as a full name.
|
||||
*/}}
|
||||
{{- define "easyhaproxy.fullname" -}}
|
||||
{{- if .Values.fullnameOverride }}
|
||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
||||
{{- if contains $name .Release.Name }}
|
||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
{{- else }}
|
||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create chart name and version as used by the chart label.
|
||||
*/}}
|
||||
{{- define "easyhaproxy.chart" -}}
|
||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "easyhaproxy.labels" -}}
|
||||
helm.sh/chart: {{ include "easyhaproxy.chart" . }}
|
||||
{{ include "easyhaproxy.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "easyhaproxy.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "easyhaproxy.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
{{- define "easyhaproxy.serviceAccountName" -}}
|
||||
{{- if .Values.serviceAccount.create }}
|
||||
{{- default (include "easyhaproxy.fullname" .) .Values.serviceAccount.name }}
|
||||
{{- else }}
|
||||
{{- default "default" .Values.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
67
helm/easyhaproxy/templates/clusterrole.yaml
Normal file
67
helm/easyhaproxy/templates/clusterrole.yaml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{{- if .Values.serviceAccount.create -}}
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: {{ include "easyhaproxy.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "easyhaproxy.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
# - configmaps
|
||||
# - endpoints
|
||||
# - nodes
|
||||
- pods
|
||||
- services
|
||||
- namespaces
|
||||
# - events
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- "extensions"
|
||||
- "networking.k8s.io"
|
||||
resources:
|
||||
- ingresses
|
||||
# - ingresses/status
|
||||
# - ingressclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
# - apiGroups:
|
||||
# - "extensions"
|
||||
# - "networking.k8s.io"
|
||||
# resources:
|
||||
# - ingresses/status
|
||||
# verbs:
|
||||
# - update
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- secrets
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
# - watch
|
||||
# - create
|
||||
# - patch
|
||||
# - update
|
||||
# - apiGroups:
|
||||
# - "discovery.k8s.io"
|
||||
# resources:
|
||||
# - endpointslices
|
||||
# verbs:
|
||||
# - get
|
||||
# - list
|
||||
# - watch
|
||||
{{- end }}
|
||||
22
helm/easyhaproxy/templates/clusterrolebinding.yaml
Normal file
22
helm/easyhaproxy/templates/clusterrolebinding.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{{- if .Values.serviceAccount.create -}}
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: {{ include "easyhaproxy.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "easyhaproxy.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ include "easyhaproxy.fullname" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "easyhaproxy.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
71
helm/easyhaproxy/templates/deployment.yaml
Normal file
71
helm/easyhaproxy/templates/deployment.yaml
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
apiVersion: apps/v1
|
||||
kind: {{ ternary "Deployment" "DaemonSet" .Values.service.create }}
|
||||
metadata:
|
||||
name: {{ include "easyhaproxy.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "easyhaproxy.labels" . | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "easyhaproxy.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "easyhaproxy.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
affinity:
|
||||
nodeAffinity:
|
||||
requiredDuringSchedulingIgnoredDuringExecution:
|
||||
nodeSelectorTerms:
|
||||
- matchExpressions:
|
||||
- key: {{ .Values.masterNode.label }}
|
||||
operator: In
|
||||
values:
|
||||
{{- toYaml .Values.masterNode.values | nindent 18 }}
|
||||
serviceAccountName: {{ include "easyhaproxy.serviceAccountName" . }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
{{ if not .Values.service.create }}hostPort: {{ .Values.binding.ports.http }}{{ end }}
|
||||
- name: https
|
||||
containerPort: 443
|
||||
{{ if not .Values.service.create }}hostPort: {{ .Values.binding.ports.https }}{{ end }}
|
||||
- name: stats
|
||||
containerPort: 1936
|
||||
{{ if not .Values.service.create }}hostPort: {{ .Values.binding.ports.stats }}{{ end }}
|
||||
{{- range $port := .Values.binding.additionalPorts }}
|
||||
- name: extra{{ $port }}
|
||||
containerPort: {{ $port }}
|
||||
{{ if not $.Values.service.create }}hostPort: {{ $port }}{{ end }}
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
env:
|
||||
- name: EASYHAPROXY_DISCOVER
|
||||
value: kubernetes
|
||||
- name: HAPROXY_USERNAME
|
||||
value: {{ .Values.easyhaproxy.stats.username }}
|
||||
- name: HAPROXY_PASSWORD
|
||||
value: {{ .Values.easyhaproxy.stats.password }}
|
||||
- name: EASYHAPROXY_REFRESH_CONF
|
||||
value: {{ .Values.easyhaproxy.refresh | quote }}
|
||||
- name: HAPROXY_CUSTOMERRORS
|
||||
value: {{ .Values.easyhaproxy.customErrors | quote}}
|
||||
- name: EASYHAPROXY_SSL_MODE
|
||||
value: {{ .Values.easyhaproxy.sslMode }}
|
||||
- name: EASYHAPROXY_LOG_LEVEL
|
||||
value: {{ .Values.easyhaproxy.logLevel.easyhaproxy }}
|
||||
- name: HAPROXY_LOG_LEVEL
|
||||
value: {{ .Values.easyhaproxy.logLevel.haproxy }}
|
||||
- name: CERTBOT_LOG_LEVEL
|
||||
value: {{ .Values.easyhaproxy.logLevel.certbot }}
|
||||
34
helm/easyhaproxy/templates/service.yaml
Normal file
34
helm/easyhaproxy/templates/service.yaml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{{ if .Values.service.create }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
metadata:
|
||||
name: {{ include "easyhaproxy.fullname" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "easyhaproxy.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
{{- toYaml .Values.service.annotations | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
{{ if eq .Values.service.type "NodePort" }}nodePort: {{ .Values.binding.ports.http }}{{ end }}
|
||||
- name: https
|
||||
port: 443
|
||||
{{ if eq .Values.service.type "NodePort" }}nodePort: {{ .Values.binding.ports.https }}{{ end }}
|
||||
- name: stats
|
||||
port: 1936
|
||||
{{ if eq .Values.service.type "NodePort" }}nodePort: {{ .Values.binding.ports.stats }}{{ end }}
|
||||
{{- range $port := .Values.binding.additionalPorts }}
|
||||
- name: extra{{ $port }}
|
||||
port: {{ $port }}
|
||||
{{ if eq $.Values.service.type "NodePort" }}nodePort: {{ $port }}{{ end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
selector:
|
||||
{{- include "easyhaproxy.selectorLabels" . | nindent 4 }}
|
||||
{{ end }}
|
||||
13
helm/easyhaproxy/templates/serviceaccount.yaml
Normal file
13
helm/easyhaproxy/templates/serviceaccount.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{{- if .Values.serviceAccount.create -}}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "easyhaproxy.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "easyhaproxy.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
19
helm/easyhaproxy/values.schema.json
Normal file
19
helm/easyhaproxy/values.schema.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"service": {
|
||||
"type": "object",
|
||||
"required": [],
|
||||
"properties": {
|
||||
"create": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["ClusterIP", "NodePort"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
helm/easyhaproxy/values.yaml
Normal file
77
helm/easyhaproxy/values.yaml
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
# Default values for easyhaproxy.
|
||||
# This is a YAML-formatted file.
|
||||
# Declare variables to be passed into your templates.
|
||||
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: byjg/easy-haproxy
|
||||
pullPolicy: Always # IfNotPresent
|
||||
# Overrides the image tag whose default is the chart appVersion.
|
||||
tag: ""
|
||||
|
||||
imagePullSecrets: []
|
||||
nameOverride: ""
|
||||
fullnameOverride: ""
|
||||
|
||||
service:
|
||||
create: false # If false, it will create a Daemonset with hostPort. The easiest.
|
||||
type: ClusterIP # or NodePort
|
||||
annotations: {}
|
||||
|
||||
binding:
|
||||
ports:
|
||||
http: 80
|
||||
https: 443
|
||||
stats: 1936
|
||||
additionalPorts: []
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
annotations: {}
|
||||
name: ""
|
||||
|
||||
podAnnotations: {}
|
||||
|
||||
podSecurityContext: {}
|
||||
# fsGroup: 2000
|
||||
|
||||
securityContext: {}
|
||||
# capabilities:
|
||||
# drop:
|
||||
# - ALL
|
||||
# readOnlyRootFilesystem: true
|
||||
# runAsNonRoot: true
|
||||
# runAsUser: 1000
|
||||
|
||||
resources: {}
|
||||
# requests:
|
||||
# cpu: "100m"
|
||||
# memory: "128Mi"
|
||||
# limits:
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
|
||||
affinity: {}
|
||||
|
||||
easyhaproxy:
|
||||
stats:
|
||||
username: admin
|
||||
password: password
|
||||
refresh: "10"
|
||||
customErrors: "true"
|
||||
sslMode: loose
|
||||
logLevel:
|
||||
certbot: DEBUG
|
||||
easyhaproxy: DEBUG
|
||||
haproxy: DEBUG
|
||||
|
||||
# Make sure to create this
|
||||
masterNode:
|
||||
label: easyhaproxy/node
|
||||
values:
|
||||
- master
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
pyyaml
|
||||
docker
|
||||
jinja2
|
||||
pytest
|
||||
|
|
@ -27,7 +27,7 @@ class DockerLabelHandler:
|
|||
|
||||
def get_bool(self, label, default_value = False):
|
||||
if self.has_label(label):
|
||||
return self.__data[label].lower() in ["True", "true", "1", "yes"]
|
||||
return self.__data[label].lower() in ["true", "1", "yes"]
|
||||
return default_value
|
||||
|
||||
def get_json(self, label, default_value = {}):
|
||||
|
|
@ -46,24 +46,21 @@ class DockerLabelHandler:
|
|||
|
||||
|
||||
class HaproxyConfigGenerator:
|
||||
def __init__(self, mapping, ssl_cert_folder="/certs"):
|
||||
def __init__(self, mapping):
|
||||
self.mapping = mapping
|
||||
self.mapping.setdefault("ssl_mode", 'default')
|
||||
self.mapping.setdefault("letsencrypt", {"email": ""})
|
||||
self.mapping["ssl_mode"] = self.mapping["ssl_mode"].lower()
|
||||
self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy")
|
||||
self.ssl_cert_haproxy = ssl_cert_folder + "/haproxy"
|
||||
self.ssl_cert_letsecncrypt = ssl_cert_folder + "/letsencrypt"
|
||||
self.letsencrypt_hosts = []
|
||||
os.makedirs(self.ssl_cert_haproxy, exist_ok=True)
|
||||
os.makedirs(self.ssl_cert_letsecncrypt, exist_ok=True)
|
||||
self.serving_hosts = []
|
||||
self.certs = {}
|
||||
|
||||
def generate(self, line_list = []):
|
||||
def generate(self, container_metadata = {}):
|
||||
self.mapping.setdefault("easymapping", [])
|
||||
|
||||
# static?
|
||||
if len(line_list) > 0:
|
||||
self.mapping["easymapping"] = self.parse(line_list)
|
||||
if container_metadata != {}:
|
||||
self.mapping["easymapping"] = self.parse(container_metadata)
|
||||
|
||||
file_loader = FileSystemLoader('templates')
|
||||
env = Environment(loader=file_loader)
|
||||
|
|
@ -74,15 +71,11 @@ class HaproxyConfigGenerator:
|
|||
return template.render(data=self.mapping)
|
||||
|
||||
|
||||
def parse(self, line_list):
|
||||
def parse(self, container_metadata):
|
||||
easymapping = dict()
|
||||
|
||||
for line in line_list:
|
||||
line = line.strip()
|
||||
i = line.find("=")
|
||||
container = line[:i]
|
||||
json_str = line[i+1:]
|
||||
d = json.loads(json_str)
|
||||
for container in container_metadata:
|
||||
d = container_metadata[container]
|
||||
|
||||
# Extract the definitions dynamically
|
||||
definitions = {}
|
||||
|
|
@ -97,7 +90,7 @@ class HaproxyConfigGenerator:
|
|||
self.label.set_data(d)
|
||||
|
||||
# Parse each definition found.
|
||||
for definition in definitions.keys():
|
||||
for definition in sorted(definitions.keys()):
|
||||
mode = self.label.get(
|
||||
self.label.create([definition, "mode"]),
|
||||
"http"
|
||||
|
|
@ -117,6 +110,9 @@ class HaproxyConfigGenerator:
|
|||
self.label.create([definition, "letsencrypt"]),
|
||||
False
|
||||
) and self.mapping["letsencrypt"]["email"] != ""
|
||||
clone_to_ssl = self.label.get_bool(
|
||||
self.label.create([definition, "clone_to_ssl"])
|
||||
)
|
||||
|
||||
if port not in easymapping:
|
||||
easymapping[port] = {
|
||||
|
|
@ -138,8 +134,9 @@ class HaproxyConfigGenerator:
|
|||
""
|
||||
)
|
||||
|
||||
for hostname in d[host_label].split(","):
|
||||
for hostname in sorted(d[host_label].split(",")):
|
||||
hostname = hostname.strip()
|
||||
self.serving_hosts.append("%s:%s" % (hostname, port))
|
||||
easymapping[port]["hosts"].setdefault(hostname, {})
|
||||
easymapping[port]["hosts"][hostname].setdefault("containers", [])
|
||||
easymapping[port]["hosts"][hostname].setdefault("letsencrypt", False)
|
||||
|
|
@ -153,7 +150,7 @@ class HaproxyConfigGenerator:
|
|||
self.label.create([definition, "redirect"])
|
||||
)
|
||||
|
||||
if letsencrypt:
|
||||
if letsencrypt or clone_to_ssl:
|
||||
if "443" not in easymapping:
|
||||
easymapping["443"] = {
|
||||
"mode": "http",
|
||||
|
|
@ -166,21 +163,17 @@ class HaproxyConfigGenerator:
|
|||
easymapping["443"]["hosts"][hostname]["letsencrypt"] = False
|
||||
easymapping["443"]["hosts"][hostname]["redirect_ssl"] = False
|
||||
easymapping["443"]["ssl"] = True
|
||||
self.letsencrypt_hosts.append(hostname) if hostname not in self.letsencrypt_hosts else self.letsencrypt_hosts
|
||||
self.letsencrypt_hosts.append(hostname) if letsencrypt and hostname not in self.letsencrypt_hosts else self.letsencrypt_hosts
|
||||
|
||||
|
||||
# handle SSL
|
||||
ssl_label = self.label.create([definition, "sslcert"])
|
||||
if self.label.has_label(ssl_label):
|
||||
filename = "{}/{}.pem".format(
|
||||
self.ssl_cert_haproxy, d[host_label]
|
||||
)
|
||||
easymapping[port]["ssl"] = True
|
||||
with open(filename, 'wb') as file:
|
||||
file.write(
|
||||
base64.b64decode(d[ssl_label])
|
||||
)
|
||||
filename = "{}.pem".format(d[host_label])
|
||||
easymapping[port]["ssl"] = True if not clone_to_ssl else False
|
||||
self.certs[filename] = base64.b64decode(d[ssl_label]).decode('ascii')
|
||||
|
||||
if self.label.get_bool(self.label.create([definition, "ssl"])):
|
||||
easymapping[port]["ssl"] = True
|
||||
easymapping[port]["ssl"] = True if not clone_to_ssl else False
|
||||
|
||||
return easymapping.values()
|
||||
232
src/functions/__init__.py
Normal file
232
src/functions/__init__.py
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
from datetime import datetime
|
||||
from multiprocessing import Process, Lock
|
||||
import subprocess
|
||||
import shlex
|
||||
import time
|
||||
import os
|
||||
|
||||
class Functions:
|
||||
HAPROXY_LOG="HAPROXY"
|
||||
EASYHAPROXY_LOG="EASYHAPROXY"
|
||||
CERTBOT_LOG="CERTBOT"
|
||||
INIT_LOG="INIT"
|
||||
|
||||
TRACE = "TRACE"
|
||||
DEBUG = "DEBUG"
|
||||
INFO = "INFO"
|
||||
WARN = "WARN"
|
||||
ERROR = "ERROR"
|
||||
FATAL = "FATAL"
|
||||
|
||||
debug_log = None
|
||||
|
||||
@staticmethod
|
||||
def skip_log(source, log_level_str):
|
||||
level = os.getenv("%s_LOG_LEVEL" % (source.upper()), "").upper()
|
||||
level_importance = {
|
||||
Functions.TRACE: 0,
|
||||
Functions.DEBUG: 1,
|
||||
Functions.INFO: 2,
|
||||
Functions.WARN: 3,
|
||||
Functions.ERROR: 4,
|
||||
Functions.FATAL: 5
|
||||
}
|
||||
level_required = 1 if level not in level_importance else level_importance[level]
|
||||
level_asked = 1 if log_level_str.upper() not in level_importance else level_importance[log_level_str.upper()]
|
||||
return level_asked < level_required
|
||||
|
||||
@staticmethod
|
||||
def load(filename):
|
||||
with open(filename, 'r') as content_file:
|
||||
return content_file.read()
|
||||
|
||||
@staticmethod
|
||||
def save(filename, contents):
|
||||
with open(filename, 'w') as file:
|
||||
file.write(contents)
|
||||
|
||||
@staticmethod
|
||||
def log(source, level, message):
|
||||
if message is None or message == "":
|
||||
return
|
||||
|
||||
if Functions.skip_log(source, level):
|
||||
return
|
||||
|
||||
if not isinstance(message, (list, tuple)):
|
||||
message = [message]
|
||||
|
||||
for line in message:
|
||||
log = "[%s] %s [%s]: %s" % (source, datetime.now().strftime("%x %X"), level, line.rstrip())
|
||||
print(log)
|
||||
if Functions.debug_log is not None:
|
||||
Functions.debug_log.append(log)
|
||||
|
||||
@staticmethod
|
||||
def run_bash(source, command, log_output=True, return_result=True):
|
||||
if not isinstance(command, (list, tuple)):
|
||||
command = shlex.split(command)
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
|
||||
output = []
|
||||
|
||||
while True:
|
||||
line = process.stdout.readline().rstrip()
|
||||
output.append(line) if return_result else None
|
||||
Functions.log(source, Functions.INFO, line) if log_output else None
|
||||
Functions.log(source, Functions.WARN, process.stderr.readline())
|
||||
return_code = process.poll()
|
||||
if return_code is not None:
|
||||
lines = []
|
||||
for line in process.stdout.readlines():
|
||||
output.append(line.rstrip()) if return_result else None
|
||||
lines.append(line.rstrip())
|
||||
Functions.log(source, Functions.INFO, lines) if log_output else None
|
||||
Functions.log(source, Functions.WARN, process.stderr.readlines())
|
||||
break
|
||||
|
||||
return output
|
||||
except Exception as e:
|
||||
Functions.log(source, Functions.ERROR, "%s" % (e))
|
||||
|
||||
|
||||
class Consts:
|
||||
easyhaproxy_config = "/etc/haproxy/easyconfig.yml"
|
||||
haproxy_config = "/etc/haproxy/haproxy.cfg"
|
||||
certs_letsencrypt = "/certs/letsencrypt"
|
||||
certs_haproxy = "/certs/haproxy"
|
||||
|
||||
class DaemonizeHAProxy:
|
||||
def __init__(self):
|
||||
self.process = None
|
||||
self.thread = None
|
||||
|
||||
def haproxy(self, action):
|
||||
if action == "start":
|
||||
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:
|
||||
return
|
||||
|
||||
self.thread = Process(target=self.__start, args=())
|
||||
self.thread.start()
|
||||
|
||||
def __prepare(self, command):
|
||||
source = Functions.HAPROXY_LOG
|
||||
if not isinstance(command, (list, tuple)):
|
||||
command = shlex.split(command)
|
||||
|
||||
try:
|
||||
self.process = subprocess.Popen(command,
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
bufsize=-1,
|
||||
universal_newlines=True)
|
||||
|
||||
except Exception as e:
|
||||
Functions.log(source, Functions.ERROR, "%s" % (e))
|
||||
|
||||
|
||||
def __start(self):
|
||||
source = Functions.HAPROXY_LOG
|
||||
try:
|
||||
with self.process.stdout:
|
||||
for line in iter(self.process.stdout.readline, b''):
|
||||
Functions.log(source, Functions.INFO, line)
|
||||
|
||||
returncode = self.process.wait()
|
||||
Functions.log(source, Functions.DEBUG, "Return code %s" % (returncode))
|
||||
|
||||
except Exception as e:
|
||||
Functions.log(source, Functions.ERROR, "%s" % (e))
|
||||
|
||||
def is_alive(self):
|
||||
return self.thread.is_alive()
|
||||
|
||||
def kill(self):
|
||||
self.process.kill()
|
||||
self.thread.kill()
|
||||
|
||||
def terminate(self):
|
||||
self.process.terminate()
|
||||
self.thread.terminate()
|
||||
|
||||
|
||||
class Certbot:
|
||||
def __init__(self, certs, email):
|
||||
self.certs = certs
|
||||
self.email = email
|
||||
|
||||
def check_certificates(self, hosts):
|
||||
if self.email == "" or len(hosts) == 0:
|
||||
return False
|
||||
|
||||
try:
|
||||
request_certs = []
|
||||
renew_certs = []
|
||||
current_time = time.time()
|
||||
for host in hosts:
|
||||
filename = "%s/%s.pem" % (self.certs, host)
|
||||
host_arg = '-d %s' % (host)
|
||||
if not os.path.exists(filename):
|
||||
Functions.log(Functions.CERTBOT_LOG, Functions.DEBUG, "Request new certificate for %s" % (host))
|
||||
request_certs.append(host_arg)
|
||||
else:
|
||||
creation_time = os.path.getctime(filename)
|
||||
if (current_time - creation_time) // (24 * 3600) > 90:
|
||||
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 '
|
||||
' --standalone'
|
||||
' --preferred-challenges http'
|
||||
' --http-01-port 2080'
|
||||
' --agree-tos'
|
||||
' --issuance-timeout 90'
|
||||
' --no-eff-email'
|
||||
' --non-interactive'
|
||||
' --max-log-backups=0'
|
||||
' %s --email %s' % (' '.join(request_certs), self.email)
|
||||
)
|
||||
|
||||
ret_reload = False
|
||||
if len(request_certs) > 0:
|
||||
Functions.run_bash(Functions.CERTBOT_LOG, certbot_certonly, return_result=False)
|
||||
ret_reload = True
|
||||
|
||||
if len(renew_certs) > 0:
|
||||
Functions.run_bash(Functions.CERTBOT_LOG, "/usb/bin/certbot renew", return_result=False)
|
||||
ret_reload = True
|
||||
|
||||
if ret_reload:
|
||||
self.find_live_certificates()
|
||||
|
||||
return ret_reload
|
||||
except Exception as e:
|
||||
Functions.log(Functions.CERTBOT_LOG, Functions.ERROR, "%s" % (e))
|
||||
return False
|
||||
|
||||
def merge_certificate(self, cert, key, filename):
|
||||
Functions.save(filename, cert + key)
|
||||
|
||||
def find_live_certificates(self):
|
||||
letsencrypt_certs = "/etc/letsencrypt/live/"
|
||||
for item in os.listdir(letsencrypt_certs):
|
||||
path = os.path.join(letsencrypt_certs, item)
|
||||
if os.path.isdir(path):
|
||||
cert = Functions.load(os.path.join(path, "fullchain.pem"))
|
||||
key = Functions.load(os.path.join(path, "privkey.pem"))
|
||||
filename = "%s/%s.pem" % (self.certs, item)
|
||||
self.merge_certificate(cert, key, filename)
|
||||
73
src/main.py
Normal file
73
src/main.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
from functions import Functions, DaemonizeHAProxy, Certbot, Consts
|
||||
from processor import ProcessorInterface
|
||||
import os
|
||||
import time
|
||||
from deepdiff import DeepDiff
|
||||
|
||||
def start():
|
||||
processor_obj = ProcessorInterface.factory(os.getenv("EASYHAPROXY_DISCOVER"))
|
||||
if processor_obj is None:
|
||||
exit(1)
|
||||
|
||||
os.makedirs(Consts.certs_letsencrypt, exist_ok=True)
|
||||
os.makedirs(Consts.certs_haproxy, exist_ok=True)
|
||||
|
||||
processor_obj.save_config(Consts.haproxy_config)
|
||||
processor_obj.save_certs(Consts.certs_haproxy)
|
||||
letsencrypt_certs_found = processor_obj.get_letsencrypt_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.TRACE, 'Object Found: %s' % (processor_obj.get_parsed_object()))
|
||||
|
||||
old_haproxy = None
|
||||
haproxy = DaemonizeHAProxy()
|
||||
haproxy.haproxy("start")
|
||||
|
||||
certbot = Certbot(Consts.certs_letsencrypt, os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL"))
|
||||
|
||||
while True:
|
||||
time.sleep(10)
|
||||
if old_haproxy is not None:
|
||||
old_haproxy.kill()
|
||||
old_haproxy = None
|
||||
try:
|
||||
old_parsed = processor_obj.get_parsed_object()
|
||||
processor_obj.refresh()
|
||||
if DeepDiff(old_parsed, processor_obj.get_parsed_object()) != {} or not haproxy.is_alive():
|
||||
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()))
|
||||
processor_obj.save_config(Consts.haproxy_config)
|
||||
processor_obj.save_certs(Consts.certs_haproxy)
|
||||
letsencrypt_certs_found = processor_obj.get_letsencrypt_hosts()
|
||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Found hosts: %s' % ", ".join(processor_obj.get_hosts())) # Needs to after save_config
|
||||
old_haproxy = haproxy
|
||||
haproxy = DaemonizeHAProxy()
|
||||
haproxy.haproxy("reload")
|
||||
old_haproxy.terminate()
|
||||
|
||||
certbot.check_certificates(letsencrypt_certs_found)
|
||||
except Exception as e:
|
||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.FATAL, "Err: %s" % (e))
|
||||
Functions.log(Functions.EASYHAPROXY_LOG, Functions.DEBUG, 'Heartbeat')
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
Functions.run_bash(Functions.INIT_LOG, '/usr/sbin/haproxy -v')
|
||||
|
||||
Functions.log(Functions.INIT_LOG, Functions.INFO, " _ ")
|
||||
Functions.log(Functions.INIT_LOG, Functions.INFO, " ___ __ _ ____ _ ___| |_ __ _ _ __ _ _ _____ ___ _ ")
|
||||
Functions.log(Functions.INIT_LOG, Functions.INFO, "/ -_) _` (_-< || |___| ' \/ _` | '_ \ '_/ _ \ \ / || |")
|
||||
Functions.log(Functions.INIT_LOG, Functions.INFO, "\___\__,_/__/\_, | |_||_\__,_| .__/_| \___/_\_\\_, |")
|
||||
Functions.log(Functions.INIT_LOG, Functions.INFO, " |__/ |_| |__/ ")
|
||||
|
||||
Functions.log(Functions.INIT_LOG, Functions.INFO, "Release: %s" % (os.getenv("RELEASE_VERSION")))
|
||||
Functions.log(Functions.INIT_LOG, Functions.DEBUG, 'Environment:')
|
||||
for name, value in os.environ.items():
|
||||
if "HAPROXY" in name:
|
||||
Functions.log(Functions.INIT_LOG, Functions.DEBUG, "- {0}: {1}".format(name, value))
|
||||
|
||||
start()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
236
src/processor/__init__.py
Normal file
236
src/processor/__init__.py
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
from easymapping import HaproxyConfigGenerator
|
||||
from functions import Functions, Consts
|
||||
import yaml
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import base64
|
||||
import docker
|
||||
from kubernetes import client, config
|
||||
from kubernetes.client.rest import ApiException
|
||||
|
||||
class ContainerEnv:
|
||||
@staticmethod
|
||||
def read():
|
||||
env_vars = {
|
||||
"customerrors": True if os.getenv("HAPROXY_CUSTOMERRORS") == "true" else False,
|
||||
"ssl_mode": os.getenv("EASYHAPROXY_SSL_MODE").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")
|
||||
}
|
||||
|
||||
return env_vars
|
||||
|
||||
|
||||
class ProcessorInterface:
|
||||
static_file = "/etc/haproxy/easyconfig.yml"
|
||||
|
||||
def __init__(self, filename = None):
|
||||
self.filename = filename
|
||||
self.refresh()
|
||||
|
||||
@staticmethod
|
||||
def factory(mode):
|
||||
if mode == "static":
|
||||
return Static(ProcessorInterface.static_file)
|
||||
elif mode == "docker":
|
||||
return Docker()
|
||||
elif mode == "swarm":
|
||||
return Swarm()
|
||||
elif mode == "kubernetes":
|
||||
return Kubernetes()
|
||||
else:
|
||||
Functions.log("EASYHAPROXY", Functions.FATAL, "Expected mode to be 'static', 'docker', 'swarm' or 'kubernetes'. I got '%s'" % (mode))
|
||||
return None
|
||||
|
||||
def refresh(self):
|
||||
self.letsencrypt_hosts = None
|
||||
self.parsed_object = None
|
||||
self.cfg = None
|
||||
self.hosts = None
|
||||
self.inspect_network()
|
||||
self.parse()
|
||||
|
||||
def inspect_network(self):
|
||||
#Abstract
|
||||
pass
|
||||
|
||||
def parse(self):
|
||||
self.cfg = HaproxyConfigGenerator(ContainerEnv.read())
|
||||
|
||||
def get_letsencrypt_hosts(self):
|
||||
return self.letsencrypt_hosts
|
||||
|
||||
def get_hosts(self):
|
||||
return self.hosts
|
||||
|
||||
def get_parsed_object(self):
|
||||
return self.parsed_object
|
||||
|
||||
def get_certs(self, key = None):
|
||||
if key is None:
|
||||
return self.cfg.certs
|
||||
else:
|
||||
return None if key not in self.cfg.certs else self.cfg.certs[key]
|
||||
|
||||
def get_haproxy_conf(self):
|
||||
conf = self.cfg.generate(self.parsed_object)
|
||||
self.letsencrypt_hosts = self.cfg.letsencrypt_hosts
|
||||
self.hosts = self.cfg.serving_hosts
|
||||
return conf
|
||||
|
||||
def save_config(self, filename):
|
||||
Functions.save(filename, self.get_haproxy_conf())
|
||||
|
||||
def save_certs(self, path):
|
||||
for cert in self.get_certs():
|
||||
Functions.save("{0}/{1}".format(path, cert), self.get_certs(cert))
|
||||
|
||||
|
||||
class Static(ProcessorInterface):
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
self.static_content = None
|
||||
|
||||
def get_parsed_object(self):
|
||||
return self.static_content["easymapping"] if "easymapping" in self.static_content else []
|
||||
|
||||
def get_hosts(self):
|
||||
hosts = []
|
||||
for object in self.get_parsed_object():
|
||||
for host in object["hosts"].keys():
|
||||
hosts.append("%s:%s" % (host, object["port"]))
|
||||
return hosts
|
||||
|
||||
def parse(self):
|
||||
self.static_content = yaml.load(Functions.load(self.filename), Loader=yaml.FullLoader)
|
||||
self.cfg = HaproxyConfigGenerator(self.static_content)
|
||||
|
||||
|
||||
class Docker(ProcessorInterface):
|
||||
def __init__(self, filename = None):
|
||||
self.client = docker.from_env()
|
||||
super().__init__()
|
||||
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
for container in self.client.containers.list():
|
||||
self.parsed_object[container.name] = container.labels
|
||||
|
||||
|
||||
class Swarm(ProcessorInterface):
|
||||
def __init__(self, filename = None):
|
||||
self.client = docker.from_env()
|
||||
super().__init__()
|
||||
|
||||
def inspect_network(self):
|
||||
self.parsed_object = {}
|
||||
for container in self.client.services.list():
|
||||
self.parsed_object[container.attrs["Spec"]["Name"]] = container.attrs["Spec"]["Labels"]
|
||||
|
||||
|
||||
class Kubernetes(ProcessorInterface):
|
||||
def __init__(self, filename = None):
|
||||
config.load_incluster_config()
|
||||
config.verify_ssl = False
|
||||
self.api_instance = client.CoreV1Api()
|
||||
self.v1 = client.NetworkingV1Api()
|
||||
self.cert_cache = {}
|
||||
super().__init__()
|
||||
|
||||
def _check_annotation(self, annotations, key):
|
||||
if key not in annotations:
|
||||
return None
|
||||
return annotations[key]
|
||||
|
||||
def inspect_network(self):
|
||||
|
||||
ret = self.v1.list_ingress_for_all_namespaces(watch=False)
|
||||
|
||||
self.parsed_object = {}
|
||||
for ingress in ret.items:
|
||||
if ingress.metadata.annotations['kubernetes.io/ingress.class'] != "easyhaproxy-ingress":
|
||||
continue
|
||||
|
||||
ssl_hosts = []
|
||||
|
||||
letsencrypt = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.letsencrypt")
|
||||
redirect_ssl = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect_ssl")
|
||||
redirect = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.redirect")
|
||||
mode = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.mode")
|
||||
listen_port = self._check_annotation(ingress.metadata.annotations, "easyhaproxy.listen_port")
|
||||
if listen_port is None:
|
||||
listen_port = 80
|
||||
|
||||
data = {}
|
||||
data["creation_timestamp"] = ingress.metadata.creation_timestamp.strftime("%x %X")
|
||||
data["resource_version"] = ingress.metadata.resource_version
|
||||
data["namespace"] = ingress.metadata.namespace
|
||||
|
||||
ingress_name = ingress.metadata.namespace
|
||||
|
||||
if ingress.spec.tls is not None:
|
||||
for tls in ingress.spec.tls:
|
||||
try:
|
||||
secret = self.api_instance.read_namespaced_secret(tls.secret_name, ingress.metadata.namespace)
|
||||
if "tls.crt" not in secret.data or "tls.key" not in secret.data:
|
||||
continue
|
||||
|
||||
if tls.secret_name not in self.cert_cache or self.cert_cache[tls.secret_name] != secret.data:
|
||||
self.cert_cache[tls.secret_name] = secret.data
|
||||
Functions.save(
|
||||
"{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')
|
||||
)
|
||||
|
||||
ssl_hosts.extend(tls.hosts)
|
||||
except Exception as 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))
|
||||
|
||||
for rule in ingress.spec.rules:
|
||||
rule_data = {}
|
||||
port_number = rule.http.paths[0].backend.service.port.number
|
||||
definition = "easyhaproxy.%s_%s" % (rule.host.replace(".", "-"), port_number)
|
||||
rule_data["%s.host" % (definition)] = rule.host
|
||||
rule_data["%s.port" % (definition)] = listen_port
|
||||
rule_data["%s.localport" % (definition)] = port_number
|
||||
if rule.host in ssl_hosts:
|
||||
rule_data["%s.clone_to_ssl" % (definition)] = 'true'
|
||||
if redirect_ssl is not None:
|
||||
rule_data["%s.redirect_ssl" % (definition)] = redirect_ssl
|
||||
if letsencrypt is not None:
|
||||
rule_data["%s.letsencrypt" % (definition)] = letsencrypt
|
||||
if redirect is not None:
|
||||
rule_data["%s.redirect" % (definition)] = redirect
|
||||
if mode is not None:
|
||||
rule_data["%s.mode" % (definition)] = mode
|
||||
|
||||
service_name = rule.http.paths[0].backend.service.name
|
||||
try:
|
||||
api_response = self.api_instance.read_namespaced_service(service_name, ingress.metadata.namespace)
|
||||
cluster_ip = api_response.spec.cluster_ip
|
||||
except ApiException as e:
|
||||
cluster_ip = None
|
||||
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 not in self.parsed_object.keys():
|
||||
self.parsed_object[cluster_ip] = data
|
||||
self.parsed_object[cluster_ip].update(rule_data)
|
||||
|
||||
|
||||
|
||||
|
||||
7
src/requirements.txt
Normal file
7
src/requirements.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pyyaml
|
||||
docker
|
||||
jinja2
|
||||
pytest
|
||||
docker
|
||||
kubernetes
|
||||
deepdiff
|
||||
|
|
@ -78,3 +78,4 @@ backend srv_{{ host }}
|
|||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
|
||||
101
src/tests/expected/docker.txt
Normal file
101
src/tests/expected/docker.txt
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
global
|
||||
log stdout format raw local0 info
|
||||
maxconn 2000
|
||||
tune.ssl.default-dh-param 2048
|
||||
|
||||
# intermediate configuration
|
||||
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
|
||||
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
|
||||
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
|
||||
|
||||
ssl-dh-param-file /etc/haproxy/dhparam
|
||||
|
||||
defaults
|
||||
log global
|
||||
option httplog
|
||||
|
||||
timeout connect 3s
|
||||
timeout client 10s
|
||||
timeout server 10m
|
||||
|
||||
frontend stats
|
||||
bind *:1936
|
||||
mode http
|
||||
stats enable
|
||||
stats hide-version
|
||||
stats realm Haproxy\ Statistics
|
||||
stats uri /
|
||||
default_backend srv_stats
|
||||
|
||||
backend srv_stats
|
||||
mode http
|
||||
server Local 127.0.0.1:1936
|
||||
|
||||
frontend http_in_443
|
||||
bind *:443 ssl crt /certs/letsencrypt/ alpn http/1.1 crt /certs/haproxy/ alpn http/1.1
|
||||
mode http
|
||||
|
||||
acl is_rule_hostssl_local_443_1 hdr(host) -i hostssl.local
|
||||
acl is_rule_hostssl_local_443_2 hdr(host) -i hostssl.local:443
|
||||
use_backend srv_hostssl_local_443 if is_rule_hostssl_local_443_1 OR is_rule_hostssl_local_443_2
|
||||
|
||||
acl is_rule_host2_local_443_1 hdr(host) -i host2.local
|
||||
acl is_rule_host2_local_443_2 hdr(host) -i host2.local:443
|
||||
use_backend srv_host2_local_443 if is_rule_host2_local_443_1 OR is_rule_host2_local_443_2
|
||||
|
||||
backend srv_hostssl_local_443
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 test2_processor_docker:8080 check weight 1
|
||||
backend srv_host2_local_443
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 test_processor_docker:9000 check weight 1
|
||||
|
||||
frontend http_in_80
|
||||
bind *:80
|
||||
mode http
|
||||
|
||||
acl is_rule_host1_local_80_1 hdr(host) -i host1.local
|
||||
acl is_rule_host1_local_80_2 hdr(host) -i host1.local:80
|
||||
use_backend srv_host1_local_80 if is_rule_host1_local_80_1 OR is_rule_host1_local_80_2
|
||||
|
||||
backend srv_host1_local_80
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 test_processor_docker:8080 check weight 1
|
||||
|
||||
frontend http_in_90
|
||||
bind *:90
|
||||
mode http
|
||||
|
||||
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_letsencrypt_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 srv_host2_local_90 if is_rule_host2_local_90_1 OR is_rule_host2_local_90_2
|
||||
|
||||
backend srv_host2_local_90
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
http-request set-header X-Forwarded-Port %[dst_port]
|
||||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 test_processor_docker:9000 check weight 1
|
||||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -25,4 +25,4 @@ defaults
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -92,4 +92,4 @@ backend srv_test_example_org_443
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -42,4 +42,4 @@ backend srv_www_helloworld_com_19901
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -48,15 +48,15 @@ frontend http_in_19901
|
|||
mode http
|
||||
redirect prefix www.google.com code 301 if { hdr(host) -i google.helloworld.com }
|
||||
|
||||
acl is_rule_hello_com_19901_1 hdr(host) -i hello.com
|
||||
acl is_rule_hello_com_19901_2 hdr(host) -i hello.com:19901
|
||||
use_backend srv_hello_com_19901 if is_rule_hello_com_19901_1 OR is_rule_hello_com_19901_2
|
||||
|
||||
acl is_rule_www_helloworld_com_19901_1 hdr(host) -i www.helloworld.com
|
||||
acl is_rule_www_helloworld_com_19901_2 hdr(host) -i www.helloworld.com:19901
|
||||
use_backend srv_www_helloworld_com_19901 if is_rule_www_helloworld_com_19901_1 OR is_rule_www_helloworld_com_19901_2
|
||||
|
||||
backend srv_hello_com_19901
|
||||
acl is_rule_hello_com_19901_1 hdr(host) -i hello.com
|
||||
acl is_rule_hello_com_19901_2 hdr(host) -i hello.com:19901
|
||||
use_backend srv_hello_com_19901 if is_rule_hello_com_19901_1 OR is_rule_hello_com_19901_2
|
||||
|
||||
backend srv_www_helloworld_com_19901
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
|
|
@ -64,7 +64,7 @@ backend srv_hello_com_19901
|
|||
http-request add-header X-Forwarded-Proto https if { ssl_fc }
|
||||
server srv-0 3e63154954b0:80 check weight 1
|
||||
server srv-1 eb294c110eb1:80 check weight 1
|
||||
backend srv_www_helloworld_com_19901
|
||||
backend srv_hello_com_19901
|
||||
balance roundrobin
|
||||
mode http
|
||||
option forwardfor
|
||||
|
|
@ -75,4 +75,4 @@ backend srv_www_helloworld_com_19901
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -77,4 +77,4 @@ backend srv_host1_local_443
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -39,4 +39,4 @@ backend srv_agent_quantum_local_31339
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -121,4 +121,4 @@ backend srv_www_somehost_com_br_80
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -35,4 +35,4 @@ backend srv_stats
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -20,4 +20,4 @@ defaults
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
|
|
@ -107,4 +107,4 @@ backend srv_host3_com_br_8080
|
|||
|
||||
backend letsencrypt_backend
|
||||
mode http
|
||||
server certbot 127.0.0.1:2080
|
||||
server certbot 127.0.0.1:2080
|
||||
5
src/tests/fixtures/no-services
vendored
Normal file
5
src/tests/fixtures/no-services
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{"swarm-prom_caddy": {"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"},
|
||||
"swarm-prom_cadvisor": {"com.docker.stack.image":"google/cadvisor","com.docker.stack.namespace":"swarm-prom"},
|
||||
"swarm-prom_dockerd-exporter": {"com.docker.stack.image":"stefanprodan/caddy","com.docker.stack.namespace":"swarm-prom"},
|
||||
"swarm-prom_unsee": {"com.docker.stack.image":"cloudflare/unsee:v0.8.0","com.docker.stack.namespace":"swarm-prom"},
|
||||
"test_proxy": {"com.docker.stack.image":"byjg/easy-haproxy","com.docker.stack.namespace":"test"}}
|
||||
6
src/tests/fixtures/services
vendored
Normal file
6
src/tests/fixtures/services
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
|
||||
"my-stack_agent": {"easyhaproxy.agent.host":"agent.quantum.example.org","easyhaproxy.agent.localport":"9001","easyhaproxy.agent.mode":"tcp","easyhaproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_cadvisor": {"easyhaproxy.cadvisor.host":"cadvisor.quantum.example.org","easyhaproxy.cadvisor.localport":"8080","easyhaproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_node-exporter": {"easyhaproxy.exp.host":"node-exporter.quantum.example.org","easyhaproxy.exp.localport":"9100","easyhaproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","easyhaproxy.exp.letsencrypt":"true"},
|
||||
"my-stack_reverse-proxy": {"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"some-service": {"easyhaproxy.http.port":"80","easyhaproxy.http.host":"www.somehost.com.br","easyhaproxy.http.localport":"80","easyhaproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.port":"443","easyhaproxy.https.host":"www.somehost.com.br","easyhaproxy.https.localport":"80","easyhaproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","easyhaproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}}
|
||||
6
src/tests/fixtures/services-changed-label
vendored
Normal file
6
src/tests/fixtures/services-changed-label
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{"portainer-agent_agent": {"com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"portainer-agent"},
|
||||
"my-stack_agent": {"haproxy.agent.host":"agent.quantum.example.org","haproxy.agent.localport":"9001","haproxy.agent.mode":"tcp","haproxy.agent.port":"31339","com.docker.stack.image":"portainer/agent:1.5.1","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_cadvisor": {"haproxy.cadvisor.host":"cadvisor.quantum.example.org","haproxy.cadvisor.localport":"8080","haproxy.cadvisor.port":"31337","com.docker.stack.image":"gcr.io/google-containers/cadvisor:v0.34.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"my-stack_node-exporter": {"haproxy.exp.host":"node-exporter.quantum.example.org","haproxy.exp.localport":"9100","haproxy.exp.port":"31337","com.docker.stack.image":"stefanprodan/swarmprom-node-exporter:v0.16.0","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring","haproxy.exp.letsencrypt":"yes"},
|
||||
"my-stack_reverse-proxy": {"com.docker.stack.image":"quay.io/pngmbh/easy-haproxy:tcp-mode","com.docker.stack.namespace":"my-stack","com.planetary-quantum":"monitoring"},
|
||||
"some-service": {"haproxy.http.port":"80","haproxy.http.host":"www.somehost.com.br","haproxy.http.localport":"80","haproxy.http.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.port":"443","haproxy.https.host":"www.somehost.com.br","haproxy.https.localport":"80","haproxy.https.redirect":"{\"somehost.com.br\":\"https://www.somehost.com.br\",\"somehost.com\":\"https://www.somehost.com.br\",\"www.somehost.com\":\"https://www.somehost.com.br\",\"byjg.ca\":\"https://www.somehost.com.br\",\"www.byjg.ca\":\"https://www.somehost.com.br\"}","haproxy.https.sslcert":"U29tZSBQRU0gQ2VydGlmaWNhdGU="}}
|
||||
2
src/tests/fixtures/services-clone-to-ssl
vendored
Normal file
2
src/tests/fixtures/services-clone-to-ssl
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{"10.152.183.62": {"creation_timestamp": "08/24/22 02:59:44", "resource_version": "72517156", "namespace": "parking", "easyhaproxy.valida-me_8080.host": "valida.me", "easyhaproxy.valida-me_8080.port": "80", "easyhaproxy.valida-me_8080.localport": 8080, "easyhaproxy.valida-me_8080.redirect": "{\"www.valida.me\": \"https://valida.me\"}", "easyhaproxy.www-valida-me_8080.host": "www.valida.me", "easyhaproxy.www-valida-me_8080.port": "80", "easyhaproxy.www-valida-me_8080.localport": 8080, "easyhaproxy.www-valida-me_8080.redirect": "{\"www.valida.me\": \"https://valida.me\"}"},
|
||||
"10.152.183.215": {"creation_timestamp": "08/26/22 03:06:01", "resource_version": "72522999", "namespace": "default", "easyhaproxy.host2-local_8080.host": "host2.local", "easyhaproxy.host2-local_8080.port": "80", "easyhaproxy.host2-local_8080.localport": 8080, "easyhaproxy.host2-local_8080.clone_to_ssl": "true"}}
|
||||
4
src/tests/fixtures/services-letsencrypt
vendored
Normal file
4
src/tests/fixtures/services-letsencrypt
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
{"f5c645a0dfc6": {"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"},
|
||||
"bbd4d1854155": {"com.docker.compose.config-hash":"3dc790bf2bea944359c75a40c45655bd868f1d85beb599d1ca797e8ea2c95ee4","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:0fd95b1512c207048ab3fcc74032354f38143fbb8235ac2a47da903c98a58205","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"},
|
||||
"b63438410b6a": {"com.docker.compose.config-hash":"b95ebc27d0e61caa418cdfa632e05a656da9bbc3ea0d4603651971015f10a1f0","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test.example.org","easyhaproxy.http.letsencrypt":"true","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"80","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}","easyhaproxy.http.redirect_ssl":"true"},
|
||||
"83d57d592e26": {"com.docker.compose.config-hash":"8c5871144f1e8a3aeca037207c02f011ab2c6e6c311a3773602b63541762dab5","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:c4232396c715f3d568816c666e6d9b4a68ef6c36f6243b4007c4ee1d8335fd65","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-test.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"static","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"test2.example.org","easyhaproxy.http.localport":"8080","easyhaproxy.http.port":"80","io.buildah.version":"1.21.0"}}
|
||||
2
src/tests/fixtures/services-multi-containers
vendored
Normal file
2
src/tests/fixtures/services-multi-containers
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{"test_nginx.2.t5r94mjlced7m3t5orfjbowmm": {"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"},
|
||||
"test_nginx.1.p552hqxkdx88narjrp5kouwb2": {"easyhaproxy.http.host":"www.helloworld.com","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","com.docker.stack.image":"stenote/nginx-hostname","com.docker.stack.namespace":"test"}}
|
||||
3
src/tests/fixtures/services-multiple-hosts
vendored
Normal file
3
src/tests/fixtures/services-multiple-hosts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{"db79d3a910f4": {"com.docker.compose.config-hash":"5bde40f52451521ad201e70de1291397376a0498a7c955624a609da3b60e7e8e","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:ea39067705590557dd0cd951664a10970ceefcb725a3c1f43690d6d6d4ed5fce","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"haproxy","com.docker.compose.version":"2.8.0"},
|
||||
"3e63154954b0": {"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"1","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"},
|
||||
"eb294c110eb1": {"com.docker.compose.config-hash":"4e0cbdd8372c6779863799e5021ed8178f74b55bd8e070abcdffaf87eb7baa36","com.docker.compose.container-number":"2","com.docker.compose.depends_on":"","com.docker.compose.image":"sha256:bea3509d6fdc8d7f9ec95563a5a226dc977ee74fb3e980e0de70e892c2d38dde","com.docker.compose.oneoff":"False","com.docker.compose.project":"docker","com.docker.compose.project.config_files":"/workspace/docker-easy-haproxy/examples/docker/docker-compose-multi-containers.yml","com.docker.compose.project.working_dir":"/workspace/docker-easy-haproxy/examples/docker","com.docker.compose.service":"nginx","com.docker.compose.version":"2.8.0","easyhaproxy.http.host":"hello.com\n, www.helloworld.com\n","easyhaproxy.http.localport":"80","easyhaproxy.http.port":"19901","easyhaproxy.http.redirect":"{\"google.helloworld.com\": \"www.google.com\"}"}}
|
||||
3
src/tests/fixtures/services-redirect-ssl
vendored
Normal file
3
src/tests/fixtures/services-redirect-ssl
vendored
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue