diff --git a/README.rst b/README.rst index df357a6..2c603eb 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,277 @@ HAProxy plugin for Certbot ========================== -Getting started (development) +Installing: Requirements +------------------------ + +Currently this plugin has been tested on Debian Jessie, but it will most likely +work on Ubuntu 14.04+ too. If you are running Debian Wheezy, you may need to +take additional steps during the installation. + + - Debian Jessie (or higher) or Ubuntu Trusty (or higher). + - Python 2.7 (2.6 is supported by certbot and our goal is to be compatible but + it has not been tested yet). + - HAProxy 1.5+ (we will configure SNI, which is not strictly required) + - Certbot 0.8+ + +Installing: Getting started +--------------------------- + +The installation below assumes you are running Debian Jessie but it should be +almost entirely the same process on Ubuntu. + +First add the backports repo for Jessie to your apt sources. + +.. note: This will not work for Ubuntu, you will need to use another source, + check which version comes with your version of Ubuntu, if it is a version + below 0.8, you need to find a back port PPA or download certbot from source. + +``` +echo "deb http://ftp.debian.org/debian jessie-backports main" >> \ + /etc/apt/sources.list.d/jessie-backports.list +``` + +Now update, upgrade and install some requirements: + - **Some utilities:** sudo tcpdump ufw git curl wget + - **OpenSSL and CA certificates:** openssl ca-certificates + - **Build dependencies:** build-essential libffi-dev libssl-dev python-dev + - **Python and related:** python python-setuptools + - **HAProxy:** haproxy + - pip + +``` +apt-get update +apt-get upgrade -y +apt-get install -y \ + sudo tcpdump ufw git curl wget \ + openssl ca-certificates \ + build-essential libffi-dev libssl-dev python-dev \ + python python-setuptools \ + haproxy + +easy_install pip +pip install --upgrade setuptools +``` + +We also installed a simple firewall above, but it is not yet configured, let's +do that now: + +``` +ufw allow ssh +ufw allow http +ufw allow https +ufw default deny incoming +ufw --force enable +``` + +.. warning: You probably want a little more protection for a production proxy + than just this simple firewall, but it's out of the scope of this readme. + +Now set a hostname. + +``` +echo "[INSERT YOUR HOSTNAME HERE]" > /etc/hostname +hostname -F /etc/hostname +``` + +If you want to run Certbot in an unprivileged mode, keep reading, otherwise, +skip to the installation of Certbot. + +Certbot normally requires access to the ``/etc/`` directory, which is owned by +root and therefore, Certbot needs to run as root. However, we don't like it +when processes run as root, most especially when they are opening ports on a +public network interface.. + +In order to let Certbot run as an unprivileged user, we will: + - Create a ``certbot`` user with a home directory on the system so the + automatic renewal of certificates can be run by this user. + - Tell Certbot that the working directories are located in ``certbot``'s home + directory. + - Optionally: add your own user account to the Cerbot user's group so you can + run Certbot manually. + - Allow HAProxy to access the certificates that are generated by Certbot. + +Lastly, to do automatic renewal of certificates, we will create a systemd timer +and a service to start at every boot and every 12 hours, at a random time off +the day, in order to not collectively DDOS Let's Encrypts service. + +``` +useradd -s /bin/bash -m -d /opt/certbot certbot +usermod -a -G certbot haproxy # Allow HAProxy access to the certbot certs +mkdir -p /opt/certbot/logs +mkdir -p /opt/certbot/config +mkdir -p /opt/certbot/.config/letsencrypt +``` + +If you need to use Certbot from your user account, or if you have a daemon +running on your proxy server, that configures domains on your proxy, e.g.: in a +web hosting environment - you can add those users to the ``certbot`` group. + +``` +usermod -a -G certbot [ADD YOUR USER HERE] +``` + +You will also need to tell your user what the working directory of your Cerbot +setup is (/opt/certbot/). Certbot allows you to create a configuration file +with default settings in the users' home dir: +``$HOME/.config/letsencrypt/cli.ini``. + +Besides the working directory. + +``` +mkdir -p $HOME/.config/letsencrypt +cat < $HOME/.config/letsencrypt/cli.ini +work-dir=/opt/certbot/ +logs-dir=/opt/certbot/logs/ +config-dir=/opt/certbot/config +EOF +``` + +Next time you run Certbot, it will use our new working directory. + +Now we haven't done one very essential thing yet, install ``certbot-haproxy``. +Since our plugin is in an alpha stage, we did not package it yet. You will need +to get it from our Gitlab server. + +``` +git clone https://code.greenhost.net/open/certbot-haproxy.git +cd ./certbot-haproxy/ +sudo pip install ./ +``` + +Let's Encrypt's CA server will try to contact your proxy on port 80, which is +most likely in use for your and/or your customers' websites. So we have +configured our plugin to open port ``8000`` to verify control over the domain +instead. Therefore we need to forward verification requests on port 80 to port +8000 internally. + +The sample below contains all that is required for a working load-balancing +HAProxy setup that also forwards these verification requests. But it is +probably not "copy-paste compatible" with your setup. So you need to piece +together a configuration that works for you. + +``` +cat < /etc/haproxy/haproxy.cfg +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + stats socket /run/haproxy/admin.sock mode 660 level admin + stats timeout 30s + user haproxy + group haproxy + daemon + + # Default ciphers to use on SSL-enabled listening sockets. + # Cipher suites chosen by following logic: + # - Bits of security 128>256 (weighing performance vs added security) + # - Key exchange: EECDH>DHE (faster first) + # - Mode: GCM>CBC (streaming cipher over block cipher) + # - Ephemeral: All use ephemeral key exchanges + # - Explicitly disable weak ciphers and SSLv3 + ssl-default-bind-ciphers AES128+AESGCM+EECDH:AES128+EECDH:AES128+AESGCM+DHE:AES128+EDH:AES256+AESGCM+EECDH:AES256+EECDH:AES256+AESGCM+EDH:AES256+EDH:!SHA:!MD5:!RC4:!DES:!DSS + ssl-default-bind-options no-sslv3 + +defaults + log global + mode http + option httplog + option dontlognull + timeout connect 5000 + timeout client 50000 + timeout server 50000 + errorfile 400 /etc/haproxy/errors/400.http + errorfile 403 /etc/haproxy/errors/403.http + errorfile 408 /etc/haproxy/errors/408.http + errorfile 500 /etc/haproxy/errors/500.http + errorfile 502 /etc/haproxy/errors/502.http + errorfile 503 /etc/haproxy/errors/503.http + errorfile 504 /etc/haproxy/errors/504.http + +frontend http-in + # Listen on port 80 + bind *:80 + mode http + # Listen on port 443 + # Uncomment after running certbot for the first time, a certificate + # needs to be installed *before* HAProxy will be able to start when this + # directive is not commented. + # + ## bind *:443 ssl crt /opt/cerbot/haproxy_fullchains + + # Forward Cerbot verification requests to the certbot-haproxy plugin + acl is_certbot path_beg -i /.well-known/acme-challenge + use_backend certbot if is_certbot + + backend certbot + log global + mode http + server certbot 127.0.0.1:8000 + + # If redirection from port 80 to 443 is to be forced, uncomment the next + # line. Keep in mind that the bind *:443 line should be uncommented and a + # certificate should be present for all domains + # redirect scheme https if !{ ssl_fc } + + # You can also configure separate domains to force a redirect from port 80 + # to 443 like this: + # redirect scheme https if !{ ssl_fc } and [PUT YOUR DOMAIN NAME HERE] + + # The default backend is a cluster of 4 Apache servers that you need to + # host. + default_backend nodes + + backend nodes + log global + mode http + option tcplog + balance roundrobin + option forwardfor + option http-server-close + option httpclose + http-request set-header X-Forwarded-Port %[dst_port] + http-request add-header X-Forwarded-Proto https if { ssl_fc } + option httpchk HEAD / HTTP/1.1\r\nHost:localhost + server node2 hn222.greenhost.nl:80 check + server node1 hn227.greenhost.nl:80 check +EOF + +systemctl restart haproxy +``` + +Now you can try to run Certbot with the plugin as the Authenticator and +Installer, if you already have websites configured in your HAProxy setup, you +may try to install a certificate now. + +``` +certbot run +``` + +If you want your ``certbot`` to always use our Installer and Authenticator, you +can add this to your configuration file: + +``` +cat <> $HOME/.config/letsencrypt/cli.ini +authenticator certbot-haproxy:haproxy-authenticator +installer certbot-haproxy:haproxy-installer +EOF +``` + +If you need to run in unattended mode, there are a bunch of arguments you need +to set in order for Certbot to generate a certificate for you. + + - ``--domain [DOMAIN NAME]`` The domain name you want SSL to be enabled for. + - ``--agree-tos`` Tell Certbot you agree with its `TOS`_ + - ``--email [EMAIL ADDRESS]`` An e-mail address where issues with certificates + can be sent to, as well as changes in the `TOS`_. Or you could supply + ``--register-unsafely-without-email`` but this is not recommended. + +.. _TOS: https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf + +After you run certbot successfully once, there will be 2 certificate files in +the certificate directory. + +Development: Getting started ----------------------------- In order to run tests against the Let's Encrypt API we will run a Boulder @@ -11,8 +281,8 @@ components and dependencies from cluttering up your computer there is also a client Virtual Box instance. Both of these machines can be setup and started by running the `dev_start.sh` script. -Running locally without sudo ----------------------------- +Development: Running locally without sudo +----------------------------------------- You can't run certbot without root privileges because it needs to access `/etc/letsencrypt`, however you can tell it not to use `/etc/` and use some diff --git a/certbot_haproxy/constants.py b/certbot_haproxy/constants.py index 2259590..e310ecd 100644 --- a/certbot_haproxy/constants.py +++ b/certbot_haproxy/constants.py @@ -73,7 +73,7 @@ CLI_DEFAULTS_DEBIAN_BASED_SYSTEMD_OS = dict( conftest_cmd=['/usr/sbin/haproxy', '-c', '-f'], haproxy_config='/etc/haproxy/haproxy.cfg', # Needs to be writeable by the user that will run certbot - crt_directory='/etc/ssl/crt/', + crt_directory='/opt/cerbot/haproxy_fullchains', ) CLI_DEFAULTS_DEBIAN_BASED_PRE_SYSTEMD_OS = dict( @@ -84,7 +84,7 @@ CLI_DEFAULTS_DEBIAN_BASED_PRE_SYSTEMD_OS = dict( conftest_cmd=['/usr/sbin/haproxy', '-c', '-f'], haproxy_config='/etc/haproxy/haproxy.cfg', # Needs to be writeable by the user that will run certbot - crt_directory='/etc/ssl/crt/', + crt_directory='/opt/cerbot/haproxy_fullchains', ) CLI_DEFAULTS = { diff --git a/certbot_haproxy/installer.py b/certbot_haproxy/installer.py index bc2c8cd..9495c3a 100644 --- a/certbot_haproxy/installer.py +++ b/certbot_haproxy/installer.py @@ -31,8 +31,8 @@ import os import glob import subprocess import re -from OpenSSL import crypto from distutils.version import StrictVersion +from OpenSSL import crypto import zope.component import zope.interface @@ -81,6 +81,8 @@ class HAProxyInstaller(common.Plugin): #: Dict of supported enhancement functions: self._enhance_func = {} + print self.get_all_certs_keys() + @classmethod def add_parser_arguments(cls, add): """ diff --git a/provisioning_client.sh b/provisioning_client.sh index 29eed7b..82e7beb 100644 --- a/provisioning_client.sh +++ b/provisioning_client.sh @@ -2,7 +2,8 @@ echo "$PROJECT_TZ" > /etc/timezone dpkg-reconfigure -f noninteractive tzdata export DEBIAN_FRONTEND="noninteractive" -echo "deb http://ftp.debian.org/debian jessie-backports main" >> /etc/apt/sources.list +echo "deb http://ftp.debian.org/debian jessie-backports main" >> \ + /etc/apt/sources.list.d/jessie-backports.list apt-get update apt-get upgrade -y apt-get install -y \ @@ -12,9 +13,9 @@ apt-get install -y \ apt-get install -y -t jessie-backports certbot +easy_install pip pip install --upgrade setuptools -easy_install pip pip install virtualenv ufw allow ssh @@ -81,32 +82,28 @@ EOF # TODO: Does this even work with the `chroot` directive? usermod -a -G vagrant haproxy -mkdir /etc/ssl/crt +mkdir -p /opt/cerbot/haproxy_fullchains cat < /etc/haproxy/haproxy.cfg global - log /dev/log local0 - log /dev/log local1 notice - chroot /var/lib/haproxy - stats socket /run/haproxy/admin.sock mode 660 level admin - stats timeout 30s - user haproxy - group haproxy - daemon + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + stats socket /run/haproxy/admin.sock mode 660 level admin + stats timeout 30s + user haproxy + group haproxy + daemon - # Default SSL material locations - # ca-base /etc/ssl/certs - # crt-base /etc/ssl/private - - # Default ciphers to use on SSL-enabled listening sockets. - # Cipher suites chosen by following logic: - # - Bits of security 128>256 (weighing performance vs added security) - # - Key exchange: EECDH>DHE (faster first) - # - Mode: GCM>CBC (streaming cipher over block cipher) - # - Ephemeral: All use ephemeral key exchanges - # - Explicitly disable weak ciphers and SSLv3 - ssl-default-bind-ciphers AES128+AESGCM+EECDH:AES128+EECDH:AES128+AESGCM+DHE:AES128+EDH:AES256+AESGCM+EECDH:AES256+EECDH:AES256+AESGCM+EDH:AES256+EDH:!SHA:!MD5:!RC4:!DES:!DSS - ssl-default-bind-options no-sslv3 + # Default ciphers to use on SSL-enabled listening sockets. + # Cipher suites chosen by following logic: + # - Bits of security 128>256 (weighing performance vs added security) + # - Key exchange: EECDH>DHE (faster first) + # - Mode: GCM>CBC (streaming cipher over block cipher) + # - Ephemeral: All use ephemeral key exchanges + # - Explicitly disable weak ciphers and SSLv3 + ssl-default-bind-ciphers AES128+AESGCM+EECDH:AES128+EECDH:AES128+AESGCM+DHE:AES128+EDH:AES256+AESGCM+EECDH:AES256+EECDH:AES256+AESGCM+EDH:AES256+EDH:!SHA:!MD5:!RC4:!DES:!DSS + ssl-default-bind-options no-sslv3 defaults log global @@ -125,27 +122,38 @@ defaults errorfile 504 /etc/haproxy/errors/504.http frontend http-in + # Listen on port 80 bind *:80 mode http - # LE HAProxy installer should combine certs and place them here.. - # Uncomment when ready.. Needs ACL to work per site. - # bind *:443 ssl crt /etc/ssl/crt + # Listen on port 443 + # Uncomment after running certbot for the first time, a certificate + # needs to be installed *before* HAProxy will be able to start when this + # directive is not commented. + # + ## bind *:443 ssl crt /opt/cerbot/haproxy_fullchains + # Forward Cerbot verification requests to the certbot-haproxy plugin acl is_certbot path_beg -i /.well-known/acme-challenge use_backend certbot if is_certbot - # IF redirect is to be used, uncomment the next line - # redirect scheme https if !{ ssl_fc } and testsite.nl - default_backend nodes - - acl is_cerbot path_beg -i /.well-known/acme-challenge - use_backend certbot if is_cerbot - backend certbot log global mode http server certbot 127.0.0.1:8000 + # If redirection from port 80 to 443 is to be forced, uncomment the next + # line. Keep in mind that the bind *:443 line should be uncommented and a + # certificate should be present for all domains + # redirect scheme https if !{ ssl_fc } + + # You can also configure separate domains to force a redirect from port 80 + # to 443 like this: + # redirect scheme https if !{ ssl_fc } and [PUT YOUR DOMAIN NAME HERE] + + # The default backend is a cluster of 4 Apache servers that you need to + # host. + default_backend nodes + backend nodes log global mode http @@ -162,6 +170,7 @@ frontend http-in server node3 127.0.0.1:8080 check server node4 127.0.0.1:8080 check EOF + cat < /etc/apache2/sites-enabled/000-default.conf ServerName testsite.nl