Merge remote-tracking branch 'github/letsencrypt/master' into acme-pylint

This commit is contained in:
Jakub Warmuz 2016-01-10 11:17:03 +00:00
commit 1182dcf0c2
No known key found for this signature in database
GPG key ID: 2A7BAD3A489B52EA
9 changed files with 153 additions and 31 deletions

View file

@ -22,11 +22,17 @@ env:
matrix:
- TOXENV=py26 BOULDER_INTEGRATION=1
- TOXENV=py27 BOULDER_INTEGRATION=1
- TOXENV=py33
- TOXENV=py34
- TOXENV=lint
- TOXENV=cover
# Disabled for now due to requiring sudo -> causing more boulder integration
# DNS timeouts :(
# - TOXENV=apacheconftest
matrix:
include:
- env: TOXENV=py35
python: 3.5
# Only build pushes to the master branch, PRs, and branches beginning with

View file

@ -66,6 +66,7 @@ setup(
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Security',
],

View file

@ -32,28 +32,42 @@ if apt-cache show python-virtualenv > /dev/null 2>&1; then
virtualenv="$virtualenv python-virtualenv"
fi
augeas_pkg=libaugeas0
augeas_pkg="libaugeas0 augeas-lenses"
AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2`
AddBackportRepo() {
# ARGS:
BACKPORT_NAME="$1"
BACKPORT_SOURCELINE="$2"
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then
/bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..."
sleep 1s
/bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..."
sleep 1s
/bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..."
sleep 1s
if echo $BACKPORT_NAME | grep -q wheezy ; then
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
fi
echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/"$BACKPORT_NAME".list
apt-get update
apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg
augeas_pkg=
fi
fi
}
if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then
if lsb_release -a | grep -q wheezy ; then
if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q wheezy-backports ; then
# This can theoretically error if sources.list.d is empty, but in that case we don't care.
if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q wheezy-backports ; then
/bin/echo -n "Installing augeas from wheezy-backports in 3 seconds..."
sleep 1s
/bin/echo -ne "\e[0K\rInstalling augeas from wheezy-backports in 2 seconds..."
sleep 1s
/bin/echo -e "\e[0K\rInstalling augeas from wheezy-backports in 1 second ..."
sleep 1s
/bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")'
echo deb http://http.debian.net/debian wheezy-backports main >> /etc/apt/sources.list.d/wheezy-backports.list
apt-get update
fi
fi
apt-get install -y --no-install-recommends -t wheezy-backports libaugeas0
augeas_pkg=
AddBackportRepo wheezy-backports "deb http://http.debian.net/debian wheezy-backports main"
elif lsb_release -a | grep -q precise ; then
# XXX add ARM case
AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse"
else
echo "No libaugeas0 version is available that's new enough to run the"
echo "Let's Encrypt apache plugin..."

View file

@ -139,9 +139,20 @@ Would obtain a single certificate for all of those names, using the
``/var/www/example`` webroot directory for the first two, and
``/var/www/eg`` for the second two.
The webroot plugin works by creating a temporary file for each of your requested
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's
Encrypt validation server makes HTTP requests to validate that the DNS for each
requested domain resolves to the server running letsencrypt. An example request
made to your web server would look like:
::
66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
Note that to use the webroot plugin, your server must be configured to serve
files from hidden directories.
Manual
------
@ -237,7 +248,9 @@ The following files are available:
server certificate, i.e. root and intermediate certificates only.
This is what Apache < 2.4.8 needs for `SSLCertificateChainFile
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatechainfile>`_.
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatechainfile>`_,
and what nginx >= 1.3.7 needs for `ssl_trusted_certificate
<http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_trusted_certificate>`_.
``fullchain.pem``
All certificates, **including** server certificate. This is

View file

@ -35,6 +35,7 @@ class ApacheParser(object):
# https://httpd.apache.org/docs/2.4/mod/core.html#define
# https://httpd.apache.org/docs/2.4/mod/core.html#ifdefine
# This only handles invocation parameters and Define directives!
self.parser_paths = {}
self.variables = {}
if version >= (2, 4):
self.update_runtime_variables()
@ -471,16 +472,63 @@ class ApacheParser(object):
:param str filepath: Apache config file path
"""
use_new, remove_old = self._check_path_actions(filepath)
# Test if augeas included file for Httpd.lens
# Note: This works for augeas globs, ie. *.conf
inc_test = self.aug.match(
"/augeas/load/Httpd/incl [. ='%s']" % filepath)
if not inc_test:
# Load up files
# This doesn't seem to work on TravisCI
# self.aug.add_transform("Httpd.lns", [filepath])
self._add_httpd_transform(filepath)
self.aug.load()
if use_new:
inc_test = self.aug.match(
"/augeas/load/Httpd/incl [. ='%s']" % filepath)
if not inc_test:
# Load up files
# This doesn't seem to work on TravisCI
# self.aug.add_transform("Httpd.lns", [filepath])
if remove_old:
self._remove_httpd_transform(filepath)
self._add_httpd_transform(filepath)
self.aug.load()
def _check_path_actions(self, filepath):
"""Determine actions to take with a new augeas path
This helper function will return a tuple that defines
if we should try to append the new filepath to augeas
parser paths, and / or remove the old one with more
narrow matching.
:param str filepath: filepath to check the actions for
"""
try:
new_file_match = os.path.basename(filepath)
existing_matches = self.parser_paths[os.path.dirname(filepath)]
if "*" in existing_matches:
use_new = False
else:
use_new = True
if new_file_match == "*":
remove_old = True
else:
remove_old = False
except KeyError:
use_new = True
remove_old = False
return use_new, remove_old
def _remove_httpd_transform(self, filepath):
"""Remove path from Augeas transform
:param str filepath: filepath to remove
"""
remove_basenames = self.parser_paths[os.path.dirname(filepath)]
remove_dirname = os.path.dirname(filepath)
for name in remove_basenames:
remove_path = remove_dirname + "/" + name
remove_inc = self.aug.match(
"/augeas/load/Httpd/incl [. ='%s']" % remove_path)
self.aug.remove(remove_inc[0])
self.parser_paths.pop(remove_dirname)
def _add_httpd_transform(self, incl):
"""Add a transform to Augeas.
@ -502,6 +550,13 @@ class ApacheParser(object):
# Augeas uses base 1 indexing... insert at beginning...
self.aug.set("/augeas/load/Httpd/lens", "Httpd.lns")
self.aug.set("/augeas/load/Httpd/incl", incl)
# Add included path to paths dictionary
try:
self.parser_paths[os.path.dirname(incl)].append(
os.path.basename(incl))
except KeyError:
self.parser_paths[os.path.dirname(incl)] = [
os.path.basename(incl)]
def standardize_excl(self):
"""Standardize the excl arguments for the Httpd lens in Augeas.

View file

@ -49,7 +49,8 @@ if [ "$1" = --debian-modules ] ; then
sudo apt-get install -y libapache2-mod-wsgi
sudo apt-get install -y libapache2-mod-macro
for mod in ssl rewrite macro wsgi deflate userdir version mime ; do
for mod in ssl rewrite macro wsgi deflate userdir version mime setenvif ; do
echo -n enabling $mod
sudo a2enmod $mod
done
fi

View file

@ -36,7 +36,7 @@ class BasicParserTest(util.ParserTest):
"""
file_path = os.path.join(
self.config_path, "sites-available", "letsencrypt.conf")
self.config_path, "not-parsed-by-default", "letsencrypt.conf")
self.parser._parse_file(file_path) # pylint: disable=protected-access

View file

@ -122,7 +122,7 @@ class NginxConfigurator(common.Plugin):
# Entry point in main.py for installing cert
def deploy_cert(self, domain, cert_path, key_path,
chain_path, fullchain_path):
chain_path=None, fullchain_path=None):
# pylint: disable=unused-argument
"""Deploys certificate to specified virtual host.
@ -136,7 +136,15 @@ class NginxConfigurator(common.Plugin):
.. note:: This doesn't save the config files!
:raises errors.PluginError: When unable to deploy certificate due to
a lack of directives or configuration
"""
if not fullchain_path:
raise errors.PluginError(
"The nginx plugin currently requires --fullchain-path to "
"install a cert.")
vhost = self.choose_vhost(domain)
cert_directives = [['ssl_certificate', fullchain_path],
['ssl_certificate_key', key_path]]
@ -150,6 +158,12 @@ class NginxConfigurator(common.Plugin):
['ssl_stapling', 'on'],
['ssl_stapling_verify', 'on']]
if len(stapling_directives) != 0 and not chain_path:
raise errors.PluginError(
"--chain-path is required to enable "
"Online Certificate Status Protocol (OCSP) stapling "
"on nginx >= 1.3.7.")
try:
self.parser.add_server_directives(vhost.filep, vhost.names,
cert_directives, replace=True)
@ -168,7 +182,7 @@ class NginxConfigurator(common.Plugin):
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
(vhost.filep,
", ".join(str(addr) for addr in vhost.addrs)))
self.save_notes += "\tssl_certificate %s\n" % cert_path
self.save_notes += "\tssl_certificate %s\n" % fullchain_path
self.save_notes += "\tssl_certificate_key %s\n" % key_path
#######################

View file

@ -159,6 +159,24 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertTrue(util.contains_at_depth(generated_conf,
['ssl_trusted_certificate', 'example/chain.pem'], 2))
def test_deploy_cert_stapling_requires_chain_path(self):
self.config.version = (1, 3, 7)
self.assertRaises(errors.PluginError, self.config.deploy_cert,
"www.example.com",
"example/cert.pem",
"example/key.pem",
None,
"example/fullchain.pem")
def test_deploy_cert_requires_fullchain_path(self):
self.config.version = (1, 3, 1)
self.assertRaises(errors.PluginError, self.config.deploy_cert,
"www.example.com",
"example/cert.pem",
"example/key.pem",
"example/chain.pem",
None)
def test_deploy_cert(self):
server_conf = self.config.parser.abs_path('server.conf')
nginx_conf = self.config.parser.abs_path('nginx.conf')