Merge branch 'release/0.1.1'

This commit is contained in:
Maarten de Waard 2016-11-01 11:13:29 +01:00
commit 7c51c9896c
10 changed files with 176 additions and 28 deletions

4
.gitignore vendored
View File

@ -19,6 +19,7 @@ pip-log.txt
# Unit test / coverage reports
.coverage
.tox
htmlcov
# Translations
*.mo
@ -39,8 +40,9 @@ Scripts
# LE HAProxy dev stuff
working
.vagrant
.vagrant*
certbot.log
# tmp files:
*.swp

View File

@ -3,12 +3,37 @@ HAProxy plugin for Certbot
.. contents:: Table of Contents
About
-----
This is a certbot plugin for using certbot in combination with a HAProxy setup.
Its advantage over using the standalone certbot is that it automatically places
certificates in the correct directory and restarts HAProxy afterwards. It should
also enable you to very easily do automatic certificate renewal.
Furthermore, you can configure HAProxy to handle Boulder's authentication using
the HAProxy authenticator of this plugin.
It was created for use with `Greenhost`_'s share hosting environment and can be
useful to you in the following cases:
- If you use HAProxy and have several domains for which you want to enable Let's
Encrypt certificates
- If you yourself have a shared hosting platform that uses HAProxy to redirect
to your client's websites
- Actually any case in which you want to automatically restart HAProxy after you
request a new certificate.
.. _Greenhost: https://greenhost.net
Please read the installation instructions on how to configure HAProxy.
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.
take additional steps during the installation. Thus, the requirements are:
- 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
@ -421,3 +446,25 @@ run:
Since pip is part of ``python-setuptools``, you need to have it installed before
you can update.
Making a `.deb` debian package
------------------------------
Requirements:
- python stdeb: pip install --upgrade stdeb
- dh clean: apt-get install dh-make
Run the following commands in your vagrant machine:
```
apt-file update
python setup.py sdist
# py2dsc has a problem with vbox mounted folders
mv dist/certbot-haproxy-<version>.tar.gz ~
cd ~
py2dsc certbot-haproxy-<version>.tar.gz
cd deb_dist/certbot-haproxy-<version>
# NOTE: Not signed, no signed changes (with -uc and -us)
# NOTE: Add the package to the ghtools repo
dpkg-buildpackage -rfakeroot -uc -us
```

View File

@ -26,6 +26,8 @@
Be sure to replace `$USER` with the user that will be running the lehaproxy
installer.
"""
from builtins import str
from past.builtins import basestring
import logging
import os
import glob
@ -134,7 +136,7 @@ class HAProxyInstaller(common.Plugin):
" returns letsencrypt certificates. Defaults to the value"
" 'h2ppy h2cker fake CA' that is used by the local boulder."
),
type=unicode,
type=str,
default=u'Let\'s Encrypt Authority X3'
)
add(
@ -464,7 +466,7 @@ class HAProxyInstaller(common.Plugin):
# Write all new files and changes:
for filepath, contents in \
self.new_crt_files.items() + self.crt_files.items():
list(self.new_crt_files.items()) + list(self.crt_files.items()):
# Make sure directory of filepath exists
path = os.path.dirname(os.path.abspath(filepath))
@ -523,7 +525,7 @@ class HAProxyInstaller(common.Plugin):
contents = pem.read()
if self._cert_key_check(contents, filepath):
yield (filepath, filepath, self.conf("haproxy-config"))
except IOError, err:
except IOError as err:
logger.error(
"Can't access \"%s\", reason:\n %s",
filepath,
@ -544,7 +546,7 @@ class HAProxyInstaller(common.Plugin):
except TypeError:
logger.warn("Could not read certificate, wrong type (not PEM)")
# Documentation says it raises "Error"
except Exception, err: # pylint: disable=broad-except
except Exception as err: # pylint: disable=broad-except
logger.error("Unexpected error! %s", err)
if issuer == self.conf('haproxy-ca-common-name') and key.check():

View File

@ -1,4 +1,5 @@
"""Certbot HAProxy Tests"""
from __future__ import print_function
import unittest
@ -6,11 +7,8 @@ def load_tests(loader, tests, pattern=None):
"""Find all python files in the tests folder"""
if pattern is None:
pattern = 'test_*.py'
print "loader: ", loader
print("loader: ", loader)
suite = loader.discover('certbot_haproxy/tests', pattern=pattern)
suite.addTests(tests)
return suite
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,36 @@
import unittest
import mock
import os
from certbot_haproxy.authenticator import HAProxyAuthenticator
from acme import challenges
class TestAuthenticator(unittest.TestCase):
test_domain = 'le.wtf'
"""Test the relevant functions of the certbot_haproxy installer"""
def setUp(self):
mock_le_config = mock.MagicMock(
# TODO: Don't know what we need here
)
self.authenticator = HAProxyAuthenticator(
config=mock_le_config, name="authenticator")
def test_more_info(self):
info = self.authenticator.more_info()
self.assertIsInstance(info, str)
@mock.patch("certbot_haproxy.authenticator.logger")
@mock.patch("certbot.util.logger")
def test_add_parser_arguments(self, util_logger, certbot_logger):
"""Weak test taken from apache plugin tests"""
self.authenticator.add_parser_arguments(mock.MagicMock())
self.assertEqual(certbot_logger.error.call_count, 0)
self.assertEqual(util_logger.error.call_count, 0)
def test_supported_challenges(self):
chal = self.authenticator.supported_challenges
self.assertIsInstance(chal, list)
self.assertTrue(challenges.HTTP01 in chal)

View File

@ -1,4 +1,5 @@
"""Test installer functions"""
from past.builtins import basestring
import unittest
import mock
import os
@ -15,6 +16,9 @@ def _conf(self, var):
@mock.patch("certbot_haproxy.installer.HAProxyInstaller.conf", new=_conf)
class TestInstaller(unittest.TestCase):
test_domain = 'le.wtf'
"""Test the relevant functions of the certbot_haproxy installer"""
def setUp(self):
@ -72,7 +76,7 @@ class TestInstaller(unittest.TestCase):
from OpenSSL import crypto
self.installer.new_crt_files = {}
self.installer._fall_back_cert()
key = self.installer.new_crt_files.keys()[0]
key = list(self.installer.new_crt_files.keys())[0]
cert = self.installer.new_crt_files[key]
self.assertIsInstance(key, str)
self.assertIsInstance(cert, str)
@ -83,7 +87,6 @@ class TestInstaller(unittest.TestCase):
def test_deploy_cert_save(self):
"""Deploy and save a certificate and rollback after that"""
# Variables for test:
domain = 'le.wtf'
crt_dir = os.path.join(self.temp_dir, self.test_dir, "deploy_test")
base = os.path.join(self.temp_dir, self.test_dir, "deploy_cert")
key_path = os.path.join(base, "privkey.pem")
@ -99,37 +102,70 @@ class TestInstaller(unittest.TestCase):
self.assertRaises(
errors.PluginError,
self.installer.deploy_cert,
domain, 'no-cert', 'no-key')
self.test_domain, 'no-cert', 'no-key')
# Arguments for several tests
all_args = [
(domain, cert_path, key_path),
(domain, cert_path, key_path, chain_path),
(domain, None, key_path, None, fullchain_path),
(self.test_domain, cert_path, key_path),
(self.test_domain, cert_path, key_path, chain_path),
(self.test_domain, None, key_path, None, fullchain_path),
]
# Run deploy and save with all types of args
for args in all_args:
# Deploy with only key and cert
self.installer.deploy_cert(*args)
try:
self.installer.view_config_changes()
except ReverterError:
self.fail("Reverter failed")
except PluginError:
self.fail("Reverter failed with PluginError")
self.installer.save()
# Check if le.wtf.pem is created
pem = os.path.join(crt_dir, domain) + self.installer.crt_postfix
pem = os.path.join(crt_dir, self.test_domain) \
+ self.installer.crt_postfix
self.assertTrue(os.path.isfile(pem))
# Roll back pem creation
self.installer.rollback_checkpoints()
# Check if file was removed again
self.assertFalse(os.path.isfile(pem))
# Try to revert:
try:
self.installer.recovery_routine()
except PluginError:
self.fail("Recovery routine didn't work")
# fail without key
self.assertRaises(
errors.PluginError,
self.installer.deploy_cert,
self.test_domain, cert_path, None)
# Run twice (should update instead of create)
args = (self.test_domain, cert_path, key_path)
self.installer.deploy_cert(*args)
self.installer.save()
self.installer.deploy_cert(*args)
self.installer.save()
def test_enhancement(self):
""" Currently no enhancements are supported, we should see that """
self.assertRaises(
errors.PluginError,
self.installer.enhance,
self.test_domain,
"non-existent-enhancement")
@mock.patch("certbot_haproxy.installer.logger")
@mock.patch("certbot.util.logger")
def test_config_test(self, util_logger, certbot_logger):
"""Test config_test function with a faulty and a valid cfg file"""
# Check with current config file
self.installer.config_test()
self.assertEqual(certbot_logger.error.call_count, 0)
self.assertEqual(util_logger.error.call_count, 0)
# Check with bad config file
self.installer.config.haproxy_config = os.path.join(
self.temp_dir, self.test_dir, "haproxy_bad.cfg")
@ -145,3 +181,26 @@ class TestInstaller(unittest.TestCase):
errors.MisconfigurationError,
self.installer.config_test
)
def test_more_info(self):
ret = self.installer.more_info()
self.assertIsInstance(ret, basestring)
@mock.patch('certbot.util.exe_exists', return_value=False)
def test_failed_service_command(self, mock_exe_exists):
""" Fail on service manager command """
self.assertRaises(errors.NoInstallationError, self.installer.prepare)
mock_exe_exists.assert_called_once()
@mock.patch('subprocess.check_output',
return_value='not-really-a-version-number')
def test_no_version_number(self, mock_check_output):
""" Fail on version command """
self.assertRaises(errors.NoInstallationError, self.installer.prepare)
@mock.patch('subprocess.check_output',
return_value='HA-Proxy version 1.4.8 2014/10/31')
def test_wrong_version_number(self, mock_check_output):
""" Supply a too low version number for HAproxy """
self.assertRaises(errors.NotSupportedError, self.installer.prepare)
mock_check_output.assert_called_once()

View File

@ -1,6 +1,7 @@
"""
Utility functions.
"""
from builtins import object
from OpenSSL import crypto
import socket

View File

@ -69,6 +69,6 @@ index 7a8d348..746dee8 100644
{
- "module": "/usr/local/lib/libpkcs11-proxy.so",
+ "module": "/usr/lib/softhsm/libsofthsm.so",
"tokenLabel": "token_label",
"tokenLabel": "intermediate",
"pin": "5678",
"privateKeyLabel": "key_label"
"privateKeyLabel": "intermediate_key"

View File

@ -5,7 +5,9 @@ dpkg-reconfigure -f noninteractive tzdata
export DEBIAN_FRONTEND="noninteractive"
# Install go 1.5
wget -q https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz
if [ ! -f go1.5.linux-amd64.tar.gz ]; then
wget -q https://storage.googleapis.com/golang/go1.5.linux-amd64.tar.gz
fi
tar -C /usr/local -xzf go1.5.linux-amd64.tar.gz
# Set GOROOT and GOPATH so that GO knows where it is and where it can install
@ -118,7 +120,7 @@ server {
}
EOF
ln -s /etc/nginx/sites-available/wfe /etc/nginx/sites-enabled/wfe
ln -fs /etc/nginx/sites-available/wfe /etc/nginx/sites-enabled/wfe
rm -rfv /etc/nginx/sites-enabled/default
systemctl restart nginx

View File

@ -3,7 +3,7 @@ import sys
from setuptools import setup
from setuptools import find_packages
own_version = '0.1.1'
certbot_version = '0.8.1'
# Please update tox.ini when modifying dependency version requirements
@ -15,6 +15,7 @@ install_requires = [
'setuptools>=1.0',
'zope.component',
'zope.interface',
'future',
]
if sys.version_info < (2, 7):
@ -42,7 +43,7 @@ haproxy_installer = 'certbot_haproxy.installer:HAProxyInstaller'
setup(
name='certbot-haproxy',
version=certbot_version,
version=own_version,
description="HAProxy plugin for Certbot",
long_description=long_description,
url='https://code.greenhost.net/open/certbot-haproxy',