Some controls improvements for certbot.
This commit is contained in:
parent
c86d4a02e9
commit
22cac26555
6 changed files with 70 additions and 31 deletions
51
README.md
51
README.md
|
|
@ -9,7 +9,7 @@
|
||||||
Service discovery for HAProxy.
|
Service discovery for HAProxy.
|
||||||
|
|
||||||
This Docker image will create dynamically the `haproxy.cfg` based on the labels defined in docker containers or from
|
This Docker image will create dynamically the `haproxy.cfg` based on the labels defined in docker containers or from
|
||||||
a simple Yaml instead docker
|
a simple Yaml.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
|
@ -38,14 +38,15 @@ The mapping to `/var/run/docker.sock` is necessary to discover the docker contai
|
||||||
|
|
||||||
The environment variables will setup the HAProxy.
|
The environment variables will setup the HAProxy.
|
||||||
|
|
||||||
| Environment Variable | Description |
|
| Environment Variable | Description |
|
||||||
|--------------------------|-------------------------------------------------------------------------------|
|
|-------------------------------|-------------------------------------------------------------------------------|
|
||||||
| EASYHAPROXY_DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` |
|
| EASYHAPROXY_DISCOVER | How `haproxy.cfg` will be created: `static`, `docker` or `swarm` |
|
||||||
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search to match resources. Default: `easyhaproxy`. |
|
| EASYHAPROXY_LABEL_PREFIX | (Optional) The key will search to match resources. Default: `easyhaproxy`. |
|
||||||
| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. Default: `admin` |
|
[ EASYHAPROXY_LETSENCRYPT_EMAIL | (Optional) The email will be used to request certificate to letsencrypt |
|
||||||
| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. |
|
| HAPROXY_USERNAME | (Optional) The HAProxy username to the statistics. Default: `admin` |
|
||||||
| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. Default: `1936` |
|
| HAPROXY_PASSWORD | The HAProxy password to the statistics. If not set disable stats. |
|
||||||
| HAPROXY_CUSTOMERRORS. | (Optional) If HAProxy will use custom HTML errors. true/false. Default: false |
|
| HAPROXY_STATS_PORT | (Optional) The HAProxy port to the statistics. Default: `1936` |
|
||||||
|
| HAPROXY_CUSTOMERRORS. | (Optional) If HAProxy will use custom HTML errors. true/false. Default: false |
|
||||||
|
|
||||||
The environment variable `EASYHAPROXY_DISCOVER` will define where is located your containers (see below more details):
|
The environment variable `EASYHAPROXY_DISCOVER` will define where is located your containers (see below more details):
|
||||||
|
|
||||||
|
|
@ -202,6 +203,38 @@ Running:
|
||||||
docker run -v /my/config.yml:/etc/haproxy/easyconfig.yml .... byjg/easyhaproxy
|
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
|
||||||
|
- You cannot set the port 443 for the container with the Letsencrypt. 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.
|
||||||
|
|
||||||
|
|
||||||
## Mapping custom .cfg files
|
## Mapping custom .cfg files
|
||||||
|
|
||||||
Map a folder containing valid HAProxy `.cfg` files to `/etc/haproxy/conf.d`. It will be concatenated to your HAProxy CFG.
|
Map a folder containing valid HAProxy `.cfg` files to `/etc/haproxy/conf.d`. It will be concatenated to your HAProxy CFG.
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,12 @@ if [ -n "$REQUEST_CERTS" ]; then
|
||||||
--non-interactive \
|
--non-interactive \
|
||||||
--max-log-backups=0 \
|
--max-log-backups=0 \
|
||||||
--post-hook "/scripts/certbot_to_haproxy.sh" \
|
--post-hook "/scripts/certbot_to_haproxy.sh" \
|
||||||
$REQUEST_CERTS --email info@xpto.us
|
$REQUEST_CERTS --email $EASYHAPROXY_LETSENCRYPT_EMAIL
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$RENEW_CERTS" ]; then
|
if [ -n "$RENEW_CERTS" ]; then
|
||||||
certbot renew --post-hook "/scripts/certbot_to_haproxy.sh"
|
certbot renew --post-hook "/scripts/certbot_to_haproxy.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Release semaphore
|
||||||
rm /tmp/certbot-lock
|
rm /tmp/certbot-lock
|
||||||
|
|
@ -8,4 +8,5 @@ for CERTIFICATE in `find /etc/letsencrypt/live/* -type d`; do
|
||||||
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /etc/haproxy/certs/$CERTIFICATE.pem
|
cat /etc/letsencrypt/live/$CERTIFICATE/fullchain.pem /etc/letsencrypt/live/$CERTIFICATE/privkey.pem > /etc/haproxy/certs/$CERTIFICATE.pem
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# It will be checked on haproxy-reload.sh
|
||||||
touch /tmp/force-reload
|
touch /tmp/force-reload
|
||||||
|
|
@ -11,7 +11,7 @@ def main():
|
||||||
headers, payload = childutils.listener.wait()
|
headers, payload = childutils.listener.wait()
|
||||||
childutils.listener.ok()
|
childutils.listener.ok()
|
||||||
events = ['PROCESS_STATE_FATAL', 'PROCESS_STATE_EXITED', 'PROCESS_STATE_STOPPED']
|
events = ['PROCESS_STATE_FATAL', 'PROCESS_STATE_EXITED', 'PROCESS_STATE_STOPPED']
|
||||||
if not (headers['eventname'] in events):
|
if headers['eventname'] not in events:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print(headers)
|
print(headers)
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ class HaproxyConfigGenerator:
|
||||||
self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy")
|
self.label = DockerLabelHandler(mapping['lookup_label'] if 'lookup_label' in mapping else "easyhaproxy")
|
||||||
self.ssl_cert_folder = ssl_cert_folder
|
self.ssl_cert_folder = ssl_cert_folder
|
||||||
self.letsencrypt_hosts = []
|
self.letsencrypt_hosts = []
|
||||||
|
self.letsencrypt_email = os.getenv("EASYHAPROXY_LETSENCRYPT_EMAIL", "")
|
||||||
os.makedirs(self.ssl_cert_folder, exist_ok=True)
|
os.makedirs(self.ssl_cert_folder, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -108,7 +109,7 @@ class HaproxyConfigGenerator:
|
||||||
letsencrypt = self.label.get_bool(
|
letsencrypt = self.label.get_bool(
|
||||||
self.label.create([definition, "letsencrypt"]),
|
self.label.create([definition, "letsencrypt"]),
|
||||||
False
|
False
|
||||||
)
|
) and self.letsencrypt_email != ""
|
||||||
self.letsencrypt_hosts.append(d[host_label]) if letsencrypt and d[host_label] not in self.letsencrypt_hosts else self.letsencrypt_hosts
|
self.letsencrypt_hosts.append(d[host_label]) if letsencrypt and d[host_label] not in self.letsencrypt_hosts else self.letsencrypt_hosts
|
||||||
|
|
||||||
if port not in easymapping:
|
if port not in easymapping:
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import pytest
|
||||||
import os
|
import os
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
CERTS_FOLDER="/tmp/certs"
|
||||||
|
CERT_FILE="/tmp/certs/www.somehost.com.br.pem"
|
||||||
|
LETSENCRYPT_EMAIL="some@email.com"
|
||||||
|
|
||||||
def load_fixture(file):
|
def load_fixture(file):
|
||||||
path = os.path.dirname(os.path.realpath(__file__))
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
@ -19,7 +22,7 @@ def test_parser_doesnt_crash():
|
||||||
"customerrors": False
|
"customerrors": False
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(result, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||||
haproxy_config = cfg.generate(line_list)
|
haproxy_config = cfg.generate(line_list)
|
||||||
|
|
||||||
assert len(haproxy_config) > 0
|
assert len(haproxy_config) > 0
|
||||||
|
|
@ -35,11 +38,11 @@ def test_parser_finds_services():
|
||||||
"customerrors": False
|
"customerrors": False
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_file = "/tmp/certs/www.somehost.com.br.pem"
|
if os.path.exists(CERT_FILE):
|
||||||
if os.path.exists(cert_file):
|
os.remove(CERT_FILE)
|
||||||
os.remove(cert_file)
|
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(result, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||||
|
cfg.letsencrypt_email = LETSENCRYPT_EMAIL
|
||||||
haproxy_config = cfg.generate(line_list)
|
haproxy_config = cfg.generate(line_list)
|
||||||
|
|
||||||
assert len(haproxy_config) > 0
|
assert len(haproxy_config) > 0
|
||||||
|
|
@ -47,7 +50,7 @@ def test_parser_finds_services():
|
||||||
with open(path + "/expected/services.txt", 'r') as expected_file:
|
with open(path + "/expected/services.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
|
|
||||||
with open(cert_file, 'r') as expected_file:
|
with open(CERT_FILE, 'r') as expected_file:
|
||||||
assert expected_file.read() == "Some PEM Certificate"
|
assert expected_file.read() == "Some PEM Certificate"
|
||||||
|
|
||||||
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
||||||
|
|
@ -60,11 +63,11 @@ def test_parser_finds_services_changed_label():
|
||||||
"lookup_label": "haproxy"
|
"lookup_label": "haproxy"
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_file = "/tmp/certs/www.somehost.com.br.pem"
|
if os.path.exists(CERT_FILE):
|
||||||
if os.path.exists(cert_file):
|
os.remove(CERT_FILE)
|
||||||
os.remove(cert_file)
|
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(result, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||||
|
cfg.letsencrypt_email = LETSENCRYPT_EMAIL
|
||||||
haproxy_config = cfg.generate(line_list)
|
haproxy_config = cfg.generate(line_list)
|
||||||
|
|
||||||
assert len(haproxy_config) > 0
|
assert len(haproxy_config) > 0
|
||||||
|
|
@ -72,7 +75,7 @@ def test_parser_finds_services_changed_label():
|
||||||
with open(path + "/expected/services.txt", 'r') as expected_file:
|
with open(path + "/expected/services.txt", 'r') as expected_file:
|
||||||
assert expected_file.read() == haproxy_config
|
assert expected_file.read() == haproxy_config
|
||||||
|
|
||||||
with open(cert_file, 'r') as expected_file:
|
with open(CERT_FILE, 'r') as expected_file:
|
||||||
assert expected_file.read() == "Some PEM Certificate"
|
assert expected_file.read() == "Some PEM Certificate"
|
||||||
|
|
||||||
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
assert ['node-exporter.quantum.example.org'] == cfg.letsencrypt_hosts
|
||||||
|
|
@ -84,11 +87,11 @@ def test_parser_finds_services_raw():
|
||||||
"customerrors": False
|
"customerrors": False
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_file = "/tmp/certs/www.somehost.com.br.pem"
|
if os.path.exists(CERT_FILE):
|
||||||
if os.path.exists(cert_file):
|
os.remove(CERT_FILE)
|
||||||
os.remove(cert_file)
|
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(result, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||||
|
cfg.letsencrypt_email = LETSENCRYPT_EMAIL
|
||||||
|
|
||||||
parsed_object = [
|
parsed_object = [
|
||||||
{
|
{
|
||||||
|
|
@ -154,7 +157,7 @@ def test_parser_finds_services_raw():
|
||||||
"byjg.ca":"https://www.somehost.com.br",
|
"byjg.ca":"https://www.somehost.com.br",
|
||||||
"www.byjg.ca":"https://www.somehost.com.br"
|
"www.byjg.ca":"https://www.somehost.com.br"
|
||||||
},
|
},
|
||||||
"ssl_cert":"/tmp/certs/www.somehost.com.br.pem"
|
"ssl_cert":CERT_FILE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mode":"http",
|
"mode":"http",
|
||||||
|
|
@ -190,7 +193,7 @@ def test_parser_static():
|
||||||
with open(path + "/fixtures/static.yml", 'r') as content_file:
|
with open(path + "/fixtures/static.yml", 'r') as content_file:
|
||||||
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
|
parsed = yaml.load(content_file.read(), Loader=yaml.FullLoader)
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(parsed, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(parsed, CERTS_FOLDER)
|
||||||
haproxy_config = cfg.generate()
|
haproxy_config = cfg.generate()
|
||||||
assert len(haproxy_config) > 0
|
assert len(haproxy_config) > 0
|
||||||
|
|
||||||
|
|
@ -265,7 +268,7 @@ def test_parser_tcp():
|
||||||
"customerrors": False
|
"customerrors": False
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(result, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||||
haproxy_config = cfg.generate(line_list)
|
haproxy_config = cfg.generate(line_list)
|
||||||
# print(haproxy_config)
|
# print(haproxy_config)
|
||||||
|
|
||||||
|
|
@ -282,7 +285,7 @@ def test_parser_multi_containers():
|
||||||
"customerrors": False
|
"customerrors": False
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg = easymapping.HaproxyConfigGenerator(result, "/tmp/certs")
|
cfg = easymapping.HaproxyConfigGenerator(result, CERTS_FOLDER)
|
||||||
haproxy_config = cfg.generate(line_list)
|
haproxy_config = cfg.generate(line_list)
|
||||||
|
|
||||||
assert len(haproxy_config) > 0
|
assert len(haproxy_config) > 0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue