Merge remote-tracking branch 'letsencrypt/master'

This commit is contained in:
Amjad Mashaal
2016-02-28 22:13:07 +02:00
19 changed files with 569 additions and 94 deletions
+15 -8
View File
@@ -1,6 +1,7 @@
"""ACME client API.""" """ACME client API."""
import collections import collections
import datetime import datetime
from email.utils import parsedate_tz
import heapq import heapq
import logging import logging
import time import time
@@ -11,7 +12,6 @@ from six.moves import http_client # pylint: disable=import-error
import OpenSSL import OpenSSL
import requests import requests
import sys import sys
import werkzeug
from acme import errors from acme import errors
from acme import jose from acme import jose
@@ -248,6 +248,9 @@ class Client(object): # pylint: disable=too-many-instance-attributes
def retry_after(cls, response, default): def retry_after(cls, response, default):
"""Compute next `poll` time based on response ``Retry-After`` header. """Compute next `poll` time based on response ``Retry-After`` header.
Handles integers and various datestring formats per
https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37
:param requests.Response response: Response from `poll`. :param requests.Response response: Response from `poll`.
:param int default: Default value (in seconds), used when :param int default: Default value (in seconds), used when
``Retry-After`` header is not present or invalid. ``Retry-After`` header is not present or invalid.
@@ -260,12 +263,16 @@ class Client(object): # pylint: disable=too-many-instance-attributes
try: try:
seconds = int(retry_after) seconds = int(retry_after)
except ValueError: except ValueError:
# pylint: disable=no-member # The RFC 2822 parser handles all of RFC 2616's cases in modern
decoded = werkzeug.parse_date(retry_after) # RFC1123 # environments (primarily HTTP 1.1+ but also py27+)
if decoded is None: when = parsedate_tz(retry_after)
seconds = default if when is not None:
else: try:
return decoded tz_secs = datetime.timedelta(when[-1] if when[-1] else 0)
return datetime.datetime(*when[:7]) - tz_secs
except (ValueError, OverflowError):
pass
seconds = default
return datetime.datetime.now() + datetime.timedelta(seconds=seconds) return datetime.datetime.now() + datetime.timedelta(seconds=seconds)
@@ -357,7 +364,7 @@ class Client(object): # pylint: disable=too-many-instance-attributes
attempts = collections.defaultdict(int) attempts = collections.defaultdict(int)
exhausted = set() exhausted = set()
# priority queue with datetime (based on Retry-After) as key, # priority queue with datetime.datetime (based on Retry-After) as key,
# and original Authorization Resource as value # and original Authorization Resource as value
waiting = [(datetime.datetime.now(), authzr) for authzr in authzrs] waiting = [(datetime.datetime.now(), authzr) for authzr in authzrs]
# mapping between original Authorization Resource and the most # mapping between original Authorization Resource and the most
+11
View File
@@ -222,6 +222,17 @@ class ClientTest(unittest.TestCase):
datetime.datetime(2015, 3, 27, 0, 0, 10), datetime.datetime(2015, 3, 27, 0, 0, 10),
self.client.retry_after(response=self.response, default=10)) self.client.retry_after(response=self.response, default=10))
@mock.patch('acme.client.datetime')
def test_retry_after_overflow(self, dt_mock):
dt_mock.datetime.now.return_value = datetime.datetime(2015, 3, 27)
dt_mock.timedelta = datetime.timedelta
dt_mock.datetime.side_effect = datetime.datetime
self.response.headers['Retry-After'] = "Tue, 116 Feb 2016 11:50:00 MST"
self.assertEqual(
datetime.datetime(2015, 3, 27, 0, 0, 10),
self.client.retry_after(response=self.response, default=10))
@mock.patch('acme.client.datetime') @mock.patch('acme.client.datetime')
def test_retry_after_seconds(self, dt_mock): def test_retry_after_seconds(self, dt_mock):
dt_mock.datetime.now.return_value = datetime.datetime(2015, 3, 27) dt_mock.datetime.now.return_value = datetime.datetime(2015, 3, 27)
-1
View File
@@ -20,7 +20,6 @@ install_requires = [
'requests', 'requests',
'setuptools', # pkg_resources 'setuptools', # pkg_resources
'six', 'six',
'werkzeug',
] ]
# env markers in extras_require cause problems with older pip: #517 # env markers in extras_require cause problems with older pip: #517
+6
View File
@@ -429,6 +429,12 @@ If you don't want to use the Apache plugin, you can omit the
Packages for Debian Jessie are coming in the next few weeks. Packages for Debian Jessie are coming in the next few weeks.
**Fedora**
.. code-block:: shell
sudo dnf install letsencrypt
**Gentoo** **Gentoo**
The official Let's Encrypt client is available in Gentoo Portage. If you The official Let's Encrypt client is available in Gentoo Portage. If you
@@ -45,9 +45,8 @@ autoload xfm
let dels (s:string) = del s s let dels (s:string) = del s s
(* deal with continuation lines *) (* deal with continuation lines *)
let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)/ " " let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)/ " "
let sep_osp = del /([ \t]*|[ \t]*\\\\\r?\n[ \t]*)/ ""
let sep_osp = Sep.opt_space
let sep_eq = del /[ \t]*=[ \t]*/ "=" let sep_eq = del /[ \t]*=[ \t]*/ "="
let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/ let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/
@@ -60,7 +59,7 @@ let indent = Util.indent
(* borrowed from shellvars.aug *) (* borrowed from shellvars.aug *)
let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'/ let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'/
let char_arg_sec = /[^ '"\t\r\n>]|\\\\"|\\\\'/ let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'/
let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/ let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/
let cdot = /\\\\./ let cdot = /\\\\./
@@ -342,6 +342,21 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self.assoc[target_name] = vhost self.assoc[target_name] = vhost
return vhost return vhost
def included_in_wildcard(self, names, target_name):
"""Helper function to see if alias is covered by wildcard"""
target_name = target_name.split(".")[::-1]
wildcards = [domain.split(".")[1:] for domain in names if domain.startswith("*")]
for wildcard in wildcards:
if len(wildcard) > len(target_name):
continue
for idx, segment in enumerate(wildcard[::-1]):
if segment != target_name[idx]:
break
else:
# https://docs.python.org/2/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
return True
return False
def _find_best_vhost(self, target_name): def _find_best_vhost(self, target_name):
"""Finds the best vhost for a target_name. """Finds the best vhost for a target_name.
@@ -351,16 +366,21 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:returns: VHost or None :returns: VHost or None
""" """
# Points 4 - Servername SSL # Points 6 - Servername SSL
# Points 3 - Address name with SSL # Points 5 - Wildcard SSL
# Points 2 - Servername no SSL # Points 4 - Address name with SSL
# Points 3 - Servername no SSL
# Points 2 - Wildcard no SSL
# Points 1 - Address name with no SSL # Points 1 - Address name with no SSL
best_candidate = None best_candidate = None
best_points = 0 best_points = 0
for vhost in self.vhosts: for vhost in self.vhosts:
if vhost.modmacro is True: if vhost.modmacro is True:
continue continue
if target_name in vhost.get_names(): names = vhost.get_names()
if target_name in names:
points = 3
elif self.included_in_wildcard(names, target_name):
points = 2 points = 2
elif any(addr.get_addr() == target_name for addr in vhost.addrs): elif any(addr.get_addr() == target_name for addr in vhost.addrs):
points = 1 points = 1
@@ -370,7 +390,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
continue # pragma: no cover continue # pragma: no cover
if vhost.ssl: if vhost.ssl:
points += 2 points += 3
if points > best_points: if points > best_points:
best_points = points best_points = points
@@ -1440,7 +1460,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
""" """
self.config_test() self.config_test()
logger.debug(self.reverter.view_config_changes(for_logging=True))
self._reload() self._reload()
def _reload(self): def _reload(self):
@@ -0,0 +1,284 @@
NameVirtualHost 0.0.0.0:7080
NameVirtualHost [00000:000:000:000::0]:7080
NameVirtualHost 0.0.0.0:7080
NameVirtualHost 127.0.0.1:7080
NameVirtualHost 0.0.0.0:7081
NameVirtualHost [0000:000:000:000::2]:7081
NameVirtualHost 0.0.0.0:7081
NameVirtualHost 127.0.0.1:7081
ServerName "example.com"
ServerAdmin "srv@example.com"
DocumentRoot /tmp
<IfModule mod_logio.c>
LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" plesklog
</IfModule>
<IfModule !mod_logio.c>
LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" plesklog
</IfModule>
TraceEnable off
ServerTokens ProductOnly
<Directory "/var/www/vhosts">
AllowOverride "All"
Options SymLinksIfOwnerMatch
Order allow,deny
Allow from all
<IfModule sapi_apache2.c>
php_admin_flag engine off
</IfModule>
<IfModule mod_php5.c>
php_admin_flag engine off
</IfModule>
</Directory>
<Directory "/usr/lib/mailman">
AllowOverride "All"
Options SymLinksIfOwnerMatch
Order allow,deny
Allow from all
<IfModule sapi_apache2.c>
php_admin_flag engine off
</IfModule>
<IfModule mod_php5.c>
php_admin_flag engine off
</IfModule>
</Directory>
<IfModule mod_headers.c>
Header add X-Powered-By PleskLin
</IfModule>
<IfModule mod_security2.c>
SecRuleEngine DetectionOnly
SecRequestBodyAccess On
SecRequestBodyLimit 134217728
SecResponseBodyAccess Off
SecResponseBodyLimit 524288
SecAuditEngine On
SecAuditLog "/var/log/modsec_audit.log"
SecAuditLogType serial
</IfModule>
#Include "/etc/httpd/conf/plesk.conf.d/ip_default/*.conf"
<VirtualHost \
0.0.0.0:7080 \
[00000:000:000:0000::2]:7080 \
0.0.0.0:7080 \
127.0.0.1:7080 \
>
ServerName "default"
UseCanonicalName Off
DocumentRoot /tmp
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
<IfModule mod_ssl.c>
SSLEngine off
</IfModule>
<Directory "/var/www/vhosts/default/cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/vhosts/default/htdocs">
<IfModule sapi_apache2.c>
php_admin_flag engine on
</IfModule>
<IfModule mod_php5.c>
php_admin_flag engine on
</IfModule>
</Directory>
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost \
0.0.0.0:7081 \
127.0.0.1:7081 \
>
ServerName "default-0_0_0_0"
UseCanonicalName Off
DocumentRoot /tmp
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
<Directory "/var/www/vhosts/default/cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/vhosts/default/htdocs">
<IfModule sapi_apache2.c>
php_admin_flag engine on
</IfModule>
<IfModule mod_php5.c>
php_admin_flag engine on
</IfModule>
</Directory>
</VirtualHost>
<VirtualHost \
[00000:000:000:000::2]:7081 \
127.0.0.1:7081 \
>
ServerName "default-0000_000_000_00000__2"
UseCanonicalName Off
DocumentRoot /tmp
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
<Directory "/var/www/vhosts/default/cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/vhosts/default/htdocs">
<IfModule sapi_apache2.c>
php_admin_flag engine on
</IfModule>
<IfModule mod_php5.c>
php_admin_flag engine on
</IfModule>
</Directory>
</VirtualHost>
<VirtualHost \
0.0.0.0:7081 \
127.0.0.1:7081 \
>
ServerName "default-0_0_0_0"
UseCanonicalName Off
DocumentRoot /tmp
ScriptAlias "/cgi-bin/" "/var/www/vhosts/default/cgi-bin"
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
#SSLCACertificateFile "/usr/local/psa/var/certificates/cert-nLy6Z1"
<Directory "/var/www/vhosts/default/cgi-bin">
AllowOverride None
Options None
Order allow,deny
Allow from all
</Directory>
<Directory "/var/www/vhosts/default/htdocs">
<IfModule sapi_apache2.c>
php_admin_flag engine on
</IfModule>
<IfModule mod_php5.c>
php_admin_flag engine on
</IfModule>
</Directory>
</VirtualHost>
</IfModule>
<VirtualHost \
0.0.0.0:7080 \
[0000:000:000:000::2]:7080 \
0.0.0.0:7080 \
127.0.0.1:7080 \
>
DocumentRoot /tmp
ServerName lists
ServerAlias lists.*
UseCanonicalName Off
ScriptAlias "/mailman/" "/usr/lib/mailman/cgi-bin/"
Alias "/icons/" "/var/www/icons/"
Alias "/pipermail/" "/var/lib/mailman/archives/public/"
<IfModule mod_ssl.c>
SSLEngine off
</IfModule>
<Directory "/var/lib/mailman/archives/">
Options FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost \
0.0.0.0:7081 \
[00000:000:000:0000::2]:7081 \
0.0.0.0:7081 \
127.0.0.1:7081 \
>
DocumentRoot /tmp
ServerName lists
ServerAlias lists.*
UseCanonicalName Off
ScriptAlias "/mailman/" "/usr/lib/mailman/cgi-bin/"
Alias "/icons/" "/var/www/icons/"
Alias "/pipermail/" "/var/lib/mailman/archives/public/"
SSLEngine on
SSLVerifyClient none
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
<Directory "/var/lib/mailman/archives/">
Options FollowSymLinks
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
</IfModule>
<IfModule mod_rpaf.c>
RPAFproxy_ips 0.0.0.0 [00000:000:000:00000::2] 0.0.0.0
</IfModule>
<IfModule mod_rpaf-2.0.c>
RPAFproxy_ips 0.0.0.0 [0000:000:000:0000::2] 0.0.0.0
</IfModule>
<IfModule mod_remoteip.c>
RemoteIPInternalProxy 0.0.0.0 [0000:000:000:0000::2] 0.0.0.0
RemoteIPHeader X-Forwarded-For
</IfModule>
@@ -85,7 +85,7 @@ class TwoVhost80Test(util.ApacheTest):
mock_getutility.notification = mock.MagicMock(return_value=True) mock_getutility.notification = mock.MagicMock(return_value=True)
names = self.config.get_all_names() names = self.config.get_all_names()
self.assertEqual(names, set( self.assertEqual(names, set(
["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17"])) ["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"]))
@mock.patch("zope.component.getUtility") @mock.patch("zope.component.getUtility")
@mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr") @mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr")
@@ -103,7 +103,7 @@ class TwoVhost80Test(util.ApacheTest):
self.config.vhosts.append(vhost) self.config.vhosts.append(vhost)
names = self.config.get_all_names() names = self.config.get_all_names()
self.assertEqual(len(names), 5) self.assertEqual(len(names), 6)
self.assertTrue("zombo.com" in names) self.assertTrue("zombo.com" in names)
self.assertTrue("google.com" in names) self.assertTrue("google.com" in names)
self.assertTrue("letsencrypt.demo" in names) self.assertTrue("letsencrypt.demo" in names)
@@ -124,7 +124,7 @@ class TwoVhost80Test(util.ApacheTest):
""" """
vhs = self.config.get_virtual_hosts() vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 6) self.assertEqual(len(vhs), 7)
found = 0 found = 0
for vhost in vhs: for vhost in vhs:
@@ -135,7 +135,7 @@ class TwoVhost80Test(util.ApacheTest):
else: else:
raise Exception("Missed: %s" % vhost) # pragma: no cover raise Exception("Missed: %s" % vhost) # pragma: no cover
self.assertEqual(found, 6) self.assertEqual(found, 7)
# Handle case of non-debian layout get_virtual_hosts # Handle case of non-debian layout get_virtual_hosts
with mock.patch( with mock.patch(
@@ -143,7 +143,7 @@ class TwoVhost80Test(util.ApacheTest):
) as mock_conf: ) as mock_conf:
mock_conf.return_value = False mock_conf.return_value = False
vhs = self.config.get_virtual_hosts() vhs = self.config.get_virtual_hosts()
self.assertEqual(len(vhs), 6) self.assertEqual(len(vhs), 7)
@mock.patch("letsencrypt_apache.display_ops.select_vhost") @mock.patch("letsencrypt_apache.display_ops.select_vhost")
def test_choose_vhost_none_avail(self, mock_select): def test_choose_vhost_none_avail(self, mock_select):
@@ -186,6 +186,20 @@ class TwoVhost80Test(util.ApacheTest):
self.assertRaises( self.assertRaises(
errors.PluginError, self.config.choose_vhost, "none.com") errors.PluginError, self.config.choose_vhost, "none.com")
def test_choosevhost_select_vhost_with_wildcard(self):
chosen_vhost = self.config.choose_vhost("blue.purple.com", temp=True)
self.assertEqual(self.vh_truth[6], chosen_vhost)
def test_findbest_continues_on_short_domain(self):
# pylint: disable=protected-access
chosen_vhost = self.config._find_best_vhost("purple.com")
self.assertEqual(None, chosen_vhost)
def test_findbest_continues_on_long_domain(self):
# pylint: disable=protected-access
chosen_vhost = self.config._find_best_vhost("green.red.purple.com")
self.assertEqual(None, chosen_vhost)
def test_find_best_vhost(self): def test_find_best_vhost(self):
# pylint: disable=protected-access # pylint: disable=protected-access
self.assertEqual( self.assertEqual(
@@ -211,6 +225,7 @@ class TwoVhost80Test(util.ApacheTest):
self.config.vhosts = [ self.config.vhosts = [
vh for vh in self.config.vhosts vh for vh in self.config.vhosts
if vh.name not in ["letsencrypt.demo", "encryption-example.demo"] if vh.name not in ["letsencrypt.demo", "encryption-example.demo"]
and "*.blue.purple.com" not in vh.aliases
] ]
self.assertEqual( self.assertEqual(
@@ -218,7 +233,7 @@ class TwoVhost80Test(util.ApacheTest):
def test_non_default_vhosts(self): def test_non_default_vhosts(self):
# pylint: disable=protected-access # pylint: disable=protected-access
self.assertEqual(len(self.config._non_default_vhosts()), 4) self.assertEqual(len(self.config._non_default_vhosts()), 5)
def test_is_site_enabled(self): def test_is_site_enabled(self):
"""Test if site is enabled. """Test if site is enabled.
@@ -524,7 +539,7 @@ class TwoVhost80Test(util.ApacheTest):
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]), self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
self.config.is_name_vhost(ssl_vhost)) self.config.is_name_vhost(ssl_vhost))
self.assertEqual(len(self.config.vhosts), 7) self.assertEqual(len(self.config.vhosts), 8)
def test_clean_vhost_ssl(self): def test_clean_vhost_ssl(self):
# pylint: disable=protected-access # pylint: disable=protected-access
@@ -942,7 +957,7 @@ class TwoVhost80Test(util.ApacheTest):
# pylint: disable=protected-access # pylint: disable=protected-access
self.config._enable_redirect(self.vh_truth[1], "") self.config._enable_redirect(self.vh_truth[1], "")
self.assertEqual(len(self.config.vhosts), 7) self.assertEqual(len(self.config.vhosts), 8)
def test_create_own_redirect_for_old_apache_version(self): def test_create_own_redirect_for_old_apache_version(self):
self.config.parser.modules.add("rewrite_module") self.config.parser.modules.add("rewrite_module")
@@ -953,7 +968,7 @@ class TwoVhost80Test(util.ApacheTest):
# pylint: disable=protected-access # pylint: disable=protected-access
self.config._enable_redirect(self.vh_truth[1], "") self.config._enable_redirect(self.vh_truth[1], "")
self.assertEqual(len(self.config.vhosts), 7) self.assertEqual(len(self.config.vhosts), 8)
def test_sift_line(self): def test_sift_line(self):
# pylint: disable=protected-access # pylint: disable=protected-access
@@ -0,0 +1,13 @@
<VirtualHost *:80>
ServerName ip-172-30-0-17
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ServerAlias *.blue.purple.com
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
@@ -150,7 +150,12 @@ def get_vh_truth(temp_dir, config_name):
os.path.join(prefix, "default-ssl-port-only.conf"), os.path.join(prefix, "default-ssl-port-only.conf"),
os.path.join(aug_pre, ("default-ssl-port-only.conf/" os.path.join(aug_pre, ("default-ssl-port-only.conf/"
"IfModule/VirtualHost")), "IfModule/VirtualHost")),
set([obj.Addr.fromstring("_default_:443")]), True, False) set([obj.Addr.fromstring("_default_:443")]), True, False),
obj.VirtualHost(
os.path.join(prefix, "wildcard.conf"),
os.path.join(aug_pre, "wildcard.conf/VirtualHost"),
set([obj.Addr.fromstring("*:80")]), False, False,
"ip-172-30-0-17", aliases=["*.blue.purple.com"])
] ]
return vh_truth return vh_truth
@@ -108,7 +108,7 @@ class ApacheTlsSni01(common.TLSSNI01):
self.configurator.reverter.register_file_creation( self.configurator.reverter.register_file_creation(
True, self.challenge_conf) True, self.challenge_conf)
logger.debug("writing a config file with text: %s", config_text) logger.debug("writing a config file with text:\n %s", config_text)
with open(self.challenge_conf, "w") as new_conf: with open(self.challenge_conf, "w") as new_conf:
new_conf.write(config_text) new_conf.write(config_text)
@@ -144,6 +144,8 @@ class ApacheTlsSni01(common.TLSSNI01):
if len(self.configurator.parser.find_dir( if len(self.configurator.parser.find_dir(
parser.case_i("Include"), self.challenge_conf)) == 0: parser.case_i("Include"), self.challenge_conf)) == 0:
# print "Including challenge virtual host(s)" # print "Including challenge virtual host(s)"
logger.debug("Adding Include %s to %s",
self.challenge_conf, parser.get_aug_path(main_config))
self.configurator.parser.add_dir( self.configurator.parser.add_dir(
parser.get_aug_path(main_config), parser.get_aug_path(main_config),
"Include", self.challenge_conf) "Include", self.challenge_conf)
+1 -1
View File
@@ -17,7 +17,7 @@ RUN apt-get update && \
apt-get clean apt-get clean
RUN pip install nose RUN pip install nose
RUN mkdir -p /home/lea/letsencrypt/letsencrypt RUN mkdir -p /home/lea/letsencrypt
# Install fake testing CA: # Install fake testing CA:
COPY ./tests/certs/ca/my-root-ca.crt.pem /usr/local/share/ca-certificates/ COPY ./tests/certs/ca/my-root-ca.crt.pem /usr/local/share/ca-certificates/
+39 -30
View File
@@ -363,8 +363,8 @@ BootstrapMac() {
brew install dialog brew install dialog
fi fi
if ! hash pip 2>/dev/null; then if [ -z "$(brew list --versions python)" ]; then
echo "pip not installed.\nInstalling python from Homebrew..." echo "python not installed.\nInstalling python from Homebrew..."
brew install python brew install python
fi fi
@@ -423,9 +423,10 @@ TempDir() {
if [ "$NO_SELF_UPGRADE" = 1 ]; then if [ "$1" = "--le-auto-phase2" ]; then
# Phase 2: Create venv, install LE, and run. # Phase 2: Create venv, install LE, and run.
shift 1 # the --le-auto-phase2 arg
if [ -f "$VENV_BIN/letsencrypt" ]; then if [ -f "$VENV_BIN/letsencrypt" ]; then
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2)
else else
@@ -620,10 +621,6 @@ traceback2==1.4.0
# sha256: IogqDkGMKE4fcYqCKzsCKUTVPS2QjhaQsxmp0-ssBXk # sha256: IogqDkGMKE4fcYqCKzsCKUTVPS2QjhaQsxmp0-ssBXk
unittest2==1.1.0 unittest2==1.1.0
# sha256: aUkbUwUVfDxuDwSnAZhNaud_1yn8HJrNJQd_HfOFMms
# sha256: 619wCpv8lkILBVY1r5AC02YuQ9gMP_0x8iTCW8DV9GI
Werkzeug==0.11.3
# sha256: KCwRK1XdjjyGmjVx-GdnwVCrEoSprOK97CJsWSrK-Bo # sha256: KCwRK1XdjjyGmjVx-GdnwVCrEoSprOK97CJsWSrK-Bo
zope.component==4.2.2 zope.component==4.2.2
@@ -1675,10 +1672,11 @@ else
exit 0 exit 0
fi fi
echo "Checking for new version..." if [ "$NO_SELF_UPGRADE" != 1 ]; then
TEMP_DIR=$(TempDir) echo "Checking for new version..."
# --------------------------------------------------------------------------- TEMP_DIR=$(TempDir)
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" # ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
"""Do downloading and JSON parsing without additional dependencies. :: """Do downloading and JSON parsing without additional dependencies. ::
# Print latest released version of LE to stdout: # Print latest released version of LE to stdout:
@@ -1807,25 +1805,36 @@ if __name__ == '__main__':
exit(main()) exit(main())
UNLIKELY_EOF UNLIKELY_EOF
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
DeterminePythonVersion DeterminePythonVersion
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
# Now we drop into Python so we don't have to install even more # Now we drop into Python so we don't have to install even more
# dependencies (curl, etc.), for better flow control, and for the option of # dependencies (curl, etc.), for better flow control, and for the option of
# future Windows compatibility. # future Windows compatibility.
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
# Install new copy of letsencrypt-auto. This preserves permissions and # Install new copy of letsencrypt-auto.
# ownership from the old copy. # TODO: Deal with quotes in pathnames.
# TODO: Deal with quotes in pathnames. echo "Replacing letsencrypt-auto..."
echo "Replacing letsencrypt-auto..." # Clone permissions with cp. chmod and chown don't have a --reference
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
# TODO: Clean up temp dir safely, even if it has quotes in its path. $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
rm -rf "$TEMP_DIR" echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
fi # should upgrade $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
"$0" --no-self-upgrade "$@" # Using mv rather than cp leaves the old file descriptor pointing to the
# original copy so the shell can continue to read it unmolested. mv across
# filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the
# cp is unlikely to fail (esp. under sudo) if the rm doesn't.
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
# TODO: Clean up temp dir safely, even if it has quotes in its path.
rm -rf "$TEMP_DIR"
fi # A newer version is available.
fi # Self-upgrading is allowed.
"$0" --le-auto-phase2 "$@"
fi fi
@@ -171,9 +171,10 @@ TempDir() {
if [ "$NO_SELF_UPGRADE" = 1 ]; then if [ "$1" = "--le-auto-phase2" ]; then
# Phase 2: Create venv, install LE, and run. # Phase 2: Create venv, install LE, and run.
shift 1 # the --le-auto-phase2 arg
if [ -f "$VENV_BIN/letsencrypt" ]; then if [ -f "$VENV_BIN/letsencrypt" ]; then
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2)
else else
@@ -235,31 +236,43 @@ else
exit 0 exit 0
fi fi
echo "Checking for new version..." if [ "$NO_SELF_UPGRADE" != 1 ]; then
TEMP_DIR=$(TempDir) echo "Checking for new version..."
# --------------------------------------------------------------------------- TEMP_DIR=$(TempDir)
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" # ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
{{ fetch.py }} {{ fetch.py }}
UNLIKELY_EOF UNLIKELY_EOF
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
DeterminePythonVersion DeterminePythonVersion
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
# Now we drop into Python so we don't have to install even more # Now we drop into Python so we don't have to install even more
# dependencies (curl, etc.), for better flow control, and for the option of # dependencies (curl, etc.), for better flow control, and for the option of
# future Windows compatibility. # future Windows compatibility.
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
# Install new copy of letsencrypt-auto. This preserves permissions and # Install new copy of letsencrypt-auto.
# ownership from the old copy. # TODO: Deal with quotes in pathnames.
# TODO: Deal with quotes in pathnames. echo "Replacing letsencrypt-auto..."
echo "Replacing letsencrypt-auto..." # Clone permissions with cp. chmod and chown don't have a --reference
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD:
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
# TODO: Clean up temp dir safely, even if it has quotes in its path. $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone"
rm -rf "$TEMP_DIR" echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
fi # should upgrade $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone"
"$0" --no-self-upgrade "$@" # Using mv rather than cp leaves the old file descriptor pointing to the
# original copy so the shell can continue to read it unmolested. mv across
# filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the
# cp is unlikely to fail (esp. under sudo) if the rm doesn't.
echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
$SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0"
# TODO: Clean up temp dir safely, even if it has quotes in its path.
rm -rf "$TEMP_DIR"
fi # A newer version is available.
fi # Self-upgrading is allowed.
"$0" --le-auto-phase2 "$@"
fi fi
@@ -14,8 +14,8 @@ BootstrapMac() {
brew install dialog brew install dialog
fi fi
if ! hash pip 2>/dev/null; then if [ -z "$(brew list --versions python)" ]; then
echo "pip not installed.\nInstalling python from Homebrew..." echo "python not installed.\nInstalling python from Homebrew..."
brew install python brew install python
fi fi
@@ -172,10 +172,6 @@ traceback2==1.4.0
# sha256: IogqDkGMKE4fcYqCKzsCKUTVPS2QjhaQsxmp0-ssBXk # sha256: IogqDkGMKE4fcYqCKzsCKUTVPS2QjhaQsxmp0-ssBXk
unittest2==1.1.0 unittest2==1.1.0
# sha256: aUkbUwUVfDxuDwSnAZhNaud_1yn8HJrNJQd_HfOFMms
# sha256: 619wCpv8lkILBVY1r5AC02YuQ9gMP_0x8iTCW8DV9GI
Werkzeug==0.11.3
# sha256: KCwRK1XdjjyGmjVx-GdnwVCrEoSprOK97CJsWSrK-Bo # sha256: KCwRK1XdjjyGmjVx-GdnwVCrEoSprOK97CJsWSrK-Bo
zope.component==4.2.2 zope.component==4.2.2
+4 -1
View File
@@ -872,7 +872,10 @@ def _restore_webroot_config(config, renewalparams):
setattr(config.namespace, "webroot_map", renewalparams["webroot_map"]) setattr(config.namespace, "webroot_map", renewalparams["webroot_map"])
elif "webroot_path" in renewalparams: elif "webroot_path" in renewalparams:
logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path") logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path")
setattr(config.namespace, "webroot_path", renewalparams["webroot_path"]) wp = renewalparams["webroot_path"]
if isinstance(wp, str): # prior to 0.1.0, webroot_path was a string
wp = [wp]
setattr(config.namespace, "webroot_path", wp)
def _reconstitute(config, full_path): def _reconstitute(config, full_path):
+21 -2
View File
@@ -20,6 +20,7 @@ from letsencrypt import constants
from letsencrypt import crypto_util from letsencrypt import crypto_util
from letsencrypt import errors from letsencrypt import errors
from letsencrypt import le_util from letsencrypt import le_util
from letsencrypt import storage
from letsencrypt.plugins import disco from letsencrypt.plugins import disco
from letsencrypt.plugins import manual from letsencrypt.plugins import manual
@@ -630,8 +631,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
print "Logs:" print "Logs:"
print lf.read() print lf.read()
def test_renew_verb(self):
with open(test_util.vector_path('sample-renewal.conf')) as src: def _make_test_renewal_conf(self, testfile):
with open(test_util.vector_path(testfile)) as src:
# put the correct path for cert.pem, chain.pem etc in the renewal conf # put the correct path for cert.pem, chain.pem etc in the renewal conf
renewal_conf = src.read().replace("MAGICDIR", test_util.vector_path()) renewal_conf = src.read().replace("MAGICDIR", test_util.vector_path())
rd = os.path.join(self.config_dir, "renewal") rd = os.path.join(self.config_dir, "renewal")
@@ -640,9 +642,26 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
rc = os.path.join(rd, "sample-renewal.conf") rc = os.path.join(rd, "sample-renewal.conf")
with open(rc, "w") as dest: with open(rc, "w") as dest:
dest.write(renewal_conf) dest.write(renewal_conf)
return rc
def test_renew_verb(self):
self._make_test_renewal_conf('sample-renewal.conf')
args = ["renew", "--dry-run", "-tvv"] args = ["renew", "--dry-run", "-tvv"]
self._test_renewal_common(True, [], args=args, renew=True) self._test_renewal_common(True, [], args=args, renew=True)
@mock.patch("letsencrypt.cli._set_by_cli")
def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
mock_set_by_cli.return_value = False
rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf')
args = mock.MagicMock(account=None, email=None, webroot_path=None)
config = configuration.NamespaceConfig(args)
lineage = storage.RenewableCert(rc_path,
configuration.RenewerConfiguration(config))
renewalparams = lineage.configuration["renewalparams"]
# pylint: disable=protected-access
cli._restore_webroot_config(config, renewalparams)
self.assertEqual(config.webroot_path, ["/var/www/"])
def test_renew_verb_empty_config(self): def test_renew_verb_empty_config(self):
rd = os.path.join(self.config_dir, 'renewal') rd = os.path.join(self.config_dir, 'renewal')
if not os.path.exists(rd): if not os.path.exists(rd):
+75
View File
@@ -0,0 +1,75 @@
cert = MAGICDIR/live/sample-renewal/cert.pem
privkey = MAGICDIR/live/sample-renewal/privkey.pem
chain = MAGICDIR/live/sample-renewal/chain.pem
fullchain = MAGICDIR/live/sample-renewal/fullchain.pem
renew_before_expiry = 1 year
# Options and defaults used in the renewal process
[renewalparams]
no_self_upgrade = False
apache_enmod = a2enmod
no_verify_ssl = False
ifaces = None
apache_dismod = a2dismod
register_unsafely_without_email = False
apache_handle_modules = True
uir = None
installer = none
nginx_ctl = nginx
config_dir = MAGICDIR
text_mode = False
func = <function obtain_cert at 0x7f093a163c08>
staging = True
prepare = False
work_dir = /var/lib/letsencrypt
tos = False
init = False
http01_port = 80
duplicate = False
noninteractive_mode = True
key_path = None
nginx = False
nginx_server_root = /etc/nginx
fullchain_path = /home/ubuntu/letsencrypt/chain.pem
email = None
csr = None
agree_dev_preview = None
redirect = None
verb = certonly
verbose_count = -3
config_file = None
renew_by_default = False
hsts = False
apache_handle_sites = True
authenticator = webroot
domains = isnot.org,
rsa_key_size = 2048
apache_challenge_location = /etc/apache2
checkpoints = 1
manual_test_mode = False
apache = False
cert_path = /home/ubuntu/letsencrypt/cert.pem
webroot_path = /var/www/
reinstall = False
expand = False
strict_permissions = False
apache_server_root = /etc/apache2
account = None
dry_run = False
manual_public_ip_logging_ok = False
chain_path = /home/ubuntu/letsencrypt/chain.pem
break_my_certs = False
standalone = True
manual = False
server = https://acme-staging.api.letsencrypt.org/directory
standalone_supported_challenges = "tls-sni-01,http-01"
webroot = True
os_packages_only = False
apache_init_script = None
user_agent = None
apache_le_vhost_ext = -le-ssl.conf
debug = False
tls_sni_01_port = 443
logs_dir = /var/log/letsencrypt
apache_vhost_root = /etc/apache2/sites-available
configurator = None