diff --git a/README.md b/README.md index 0a74e27677936743139f455be14e06f484be519a..f51838989a4da525739d3b4cc4322725a8cea4c8 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,62 @@ # haproxy HAproxy for IndieHosters + +# Warning: still experimental, use at your own risk + +## What is it? + +This repository contains one of the most advanced and libre HAproxy for docker setup that we are aware of. +It ships with: + - container discovery though docker API (like [smartstack](http://nerds.airbnb.com/smartstack-service-discovery-cloud/)) based on Env var `HOST` (Based on [docker-gen](https://github.com/jwilder/docker-gen)) + - [ocsp](https://en.wikipedia.org/wiki/Online_Certificate_Status_Protocol) + - [letsencrypt](https://letsencrypt.org/) + +Once you managed to make this running, each time you add a container with the environment variable `HOST` it will: + - generate a valid certificate (if the dns is pointing correctly) + - serves the OCSP response + - serves your web container with https + +## Prerequistes + + - docker + - docker-compose + +## Get started + +``` +git clone https://github.com/indiehosters/haproxy.git +cd haproxy +docker-compose up -d +``` + +The first time, you might need to run letsencrypt-watch in an interactive way to accept TOS and register your account. +Use the following to do so: +``` +docker-compose run letsencrypt-watch +``` + +And profit! + +``` +docker run -e HOST=example.org nginx +``` + +## Contributing + +If you have any issue (something not working, missing doc), please do report an issue here! Thanks + +This system is used in production at [IndieHosters](https://indiehosters.net/) so it is maintained. If you use it, please tell us, and we'll be really happy to update this README! + +You can help us by: + - starring this project + - sending us a thanks email + - reporting bugs + - writing documentation/blog on how you got up and running in 5mins + - writing more documentation + - sending us cake :) We loove cake! + +## TODO + + - renewal :) + - docker-swarm compatibility + - other backend (etcd/consul...) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..c797f9d7adabaa8a2aedd56fbf7542a780dad60d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +discovery: + image: jwilder/docker-gen + volumes: + - "/var/run/docker.sock:/tmp/docker.sock:ro" + - "./templates:/etc/docker-gen/templates" + - "./haproxy:/etc/haproxy" + command: "-watch /etc/docker-gen/templates/haproxy.cfg.tmpl /etc/haproxy/haproxy.cfg" +haproxy: + image: indiehosters/haproxy + volumes: + - "./haproxy:/etc/haproxy" + - "/dev/log:/dev/log" + links: + - letsencrypt + ports: + - "80:80" + - "443:443" +letsencrypt: + image: nginx + volumes: + - "./acme-challenge:/usr/share/nginx/html/.well-known/acme-challenge:ro" +letsencrypt-watch: + image: indiehosters/letsencrypt + volumes: + - "./haproxy:/etc/haproxy" + - "./acme-challenge:/html-root/.well-known/acme-challenge/" + - "./letsencrypt:/etc/letsencrypt" +ocsp: + image: indiehosters/ocsp + volumes: + - "./haproxy:/etc/haproxy" diff --git a/letsencrypt/cli.ini b/letsencrypt/cli.ini new file mode 100644 index 0000000000000000000000000000000000000000..c5783675f51d653ae39dc028b4e4bf2dfb84e7fa --- /dev/null +++ b/letsencrypt/cli.ini @@ -0,0 +1,8 @@ +# Always use the staging/testing server +# server = https://acme-staging.api.letsencrypt.org/directory + +# Uncomment and update to register with the specified e-mail address +# email = support@indie.host + +authenticator = webroot +webroot-path = /html-root diff --git a/templates/haproxy.cfg.tmpl b/templates/haproxy.cfg.tmpl new file mode 100644 index 0000000000000000000000000000000000000000..71652884ee95873bdfa703e173a0f8be9477d45f --- /dev/null +++ b/templates/haproxy.cfg.tmpl @@ -0,0 +1,47 @@ +global + log /dev/log local0 info + log /dev/log local0 notice + maxconn 4096 + tune.ssl.default-dh-param 2048 + ssl-default-bind-ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA + +defaults + log global + mode http + option forwardfor + option httpclose + option httplog + option dontlognull + retries 3 + timeout connect 5000 + timeout client 50000 + timeout server 50000 + +frontend http-in + bind *:80 +{{ range $host, $container := groupBy $ "Env.AUTOCONFIG_HOST" }} + redirect location https://{{ $host }}/mail/config-v1.1.xml code 301 if { hdr_beg(host) -i autoconfig } +{{end}} + redirect scheme https code 301 + +frontend https-in +mode http + bind *:443 ssl no-sslv3 crt /etc/haproxy/certs + reqadd X-Forwarded-Proto:\ https + rspadd Strict-Transport-Security:\ max-age=15768000 + + use_backend letsencrypt if { path_beg /.well-known/acme } +{{ range $host, $container := groupBy $ "Env.HOST" }} + use_backend {{ $host}} if { hdr(host) -i {{ $host }} www.{{ $host }} } +{{end}} + +{{ $containers := whereExist $ "Env.HOST" }} +{{ range $container := $containers }} +backend {{ $container.Env.HOST }} + cookie SERVERID insert nocache indirect + server Server {{ $container.IP }}:80 cookie Server +{{end}} + +backend letsencrypt + cookie SERVERID insert nocache indirect + server Server letsencrypt:80 cookie Server