Merge branch 'master' into release-engineering

This commit is contained in:
Peter Eckersley
2015-12-11 17:53:02 -08:00
37 changed files with 587 additions and 85 deletions
+3 -3
View File
@@ -27,7 +27,7 @@ If ``letsencrypt`` is packaged for your OS, you can install it from there, and
run it by typing ``letsencrypt``. Because not all operating systems have
packages yet, we provide a temporary solution via the ``letsencrypt-auto``
wrapper script, which obtains some dependencies from your OS and puts others
in an python virtual environment::
in a python virtual environment::
user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt
user@webserver:~$ cd letsencrypt
@@ -128,7 +128,7 @@ launch. The client requires root access in order to write to
bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and
modify webserver configurations (if you use the ``apache`` or ``nginx``
plugins). If none of these apply to you, it is theoretically possible to run
without root privilegess, but for most users who want to avoid running an ACME
without root privileges, but for most users who want to avoid running an ACME
client as root, either `letsencrypt-nosudo
<https://github.com/diafygi/letsencrypt-nosudo>`_ or `simp_le
<https://github.com/kuba/simp_le>`_ are more appropriate choices.
@@ -163,5 +163,5 @@ Current Features
* Free and Open Source Software, made with Python.
.. _Freenode: https://freenode.net
.. _Freenode: https://webchat.freenode.net?channels=%23letsencrypt
.. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev
+1 -1
View File
@@ -194,7 +194,7 @@ class JSONDeSerializable(object):
:rtype: str
"""
return self.json_dumps(sort_keys=True, indent=4)
return self.json_dumps(sort_keys=True, indent=4, separators=(',', ': '))
@classmethod
def json_dump_default(cls, python_object):
+1 -4
View File
@@ -1,8 +1,6 @@
"""Tests for acme.jose.interfaces."""
import unittest
import six
class JSONDeSerializableTest(unittest.TestCase):
# pylint: disable=too-many-instance-attributes
@@ -92,9 +90,8 @@ class JSONDeSerializableTest(unittest.TestCase):
self.assertEqual('["foo1", "foo2"]', self.seq.json_dumps())
def test_json_dumps_pretty(self):
filler = ' ' if six.PY2 else ''
self.assertEqual(self.seq.json_dumps_pretty(),
'[\n "foo1",{0}\n "foo2"\n]'.format(filler))
'[\n "foo1",\n "foo2"\n]')
def test_json_dump_default(self):
from acme.jose.interfaces import JSONDeSerializable
+4 -2
View File
@@ -22,12 +22,14 @@ class Error(jose.JSONObjectWithFields, errors.Error):
('urn:acme:error:' + name, description) for name, description in (
('badCSR', 'The CSR is unacceptable (e.g., due to a short key)'),
('badNonce', 'The client sent an unacceptable anti-replay nonce'),
('connection', 'The server could not connect to the client for DV'),
('connection', 'The server could not connect to the client to '
'verify the domain'),
('dnssec', 'The server could not validate a DNSSEC signed domain'),
('malformed', 'The request message was malformed'),
('rateLimited', 'There were too many requests of a given type'),
('serverInternal', 'The server experienced an internal error'),
('tls', 'The server experienced a TLS error during DV'),
('tls', 'The server experienced a TLS error during domain '
'verification'),
('unauthorized', 'The client lacks sufficient authorization'),
('unknownHost', 'The server could not resolve a domain name'),
)
+1 -1
View File
@@ -4,7 +4,7 @@ from setuptools import setup
from setuptools import find_packages
version = '0.1.0.dev0'
version = '0.2.0.dev0'
install_requires = [
# load_pem_private/public_key (>=0.6)
+1 -1
View File
@@ -2,7 +2,7 @@
# Tested with:
# - Fedora 22, 23 (x64)
# - Centos 7 (x64: onD igitalOcean droplet)
# - Centos 7 (x64: on DigitalOcean droplet)
if type dnf 2>/dev/null
then
+11 -8
View File
@@ -173,10 +173,11 @@ Renewal
In order to renew certificates simply call the ``letsencrypt`` (or
letsencrypt-auto_) again, and use the same values when prompted. You
can automate it slightly by passing necessary flags on the CLI (see
`--help all`), or even further using the :ref:`config-file`. If you're
sure that UI doesn't prompt for any details you can add the command to
``crontab`` (make it less than every 90 days to avoid problems, say
every month).
`--help all`), or even further using the :ref:`config-file`. The
``--renew-by-default`` flag may be helpful for automating renewal. If
you're sure that UI doesn't prompt for any details you can add the
command to ``crontab`` (make it less than every 90 days to avoid
problems, say every month).
Please note that the CA will send notification emails to the address
you provide if you do not renew certificates that are about to expire.
@@ -223,21 +224,23 @@ The following files are available:
``cert.pem``
Server certificate only.
This is what Apache needs for `SSLCertificateFile
This is what Apache < 2.4.8 needs for `SSLCertificateFile
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile>`_.
``chain.pem``
All certificates that need to be served by the browser **excluding**
server certificate, i.e. root and intermediate certificates only.
This is what Apache needs for `SSLCertificateChainFile
This is what Apache < 2.4.8 needs for `SSLCertificateChainFile
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatechainfile>`_.
``fullchain.pem``
All certificates, **including** server certificate. This is
concatenation of ``chain.pem`` and ``cert.pem``.
This is what nginx needs for `ssl_certificate
This is what Apache >= 2.4.8 needs for `SSLCertificateFile
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatefile>`_,
and what nginx needs for `ssl_certificate
<http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate>`_.
@@ -286,7 +289,7 @@ get support on our `forums <https://community.letsencrypt.org>`_.
If you find a bug in the software, please do report it in our `issue
tracker
<https://github.com/letsencrypt/letsencrypt/issues>`_. Remember to
give us us as much information as possible:
give us as much information as possible:
- copy and paste exact command line used and the output (though mind
that the latter might include some personally identifiable
+4
View File
@@ -11,6 +11,10 @@ server = https://acme-staging.api.letsencrypt.org/directory
# Uncomment and update to register with the specified e-mail address
# email = foo@example.com
# Uncomment and update to generate certificates for the specified
# domains.
# domains = example.com, www.example.com
# Uncomment to use a text interface instead of ncurses
# text = True
@@ -1,2 +1,2 @@
Let's Encrypt includes the very latest Augeas lenses in order to ship bug fixes
to Apacche configuration handling bugs as quickly as possible
to Apache configuration handling bugs as quickly as possible
@@ -51,7 +51,7 @@ let sep_osp = Sep.opt_space
let sep_eq = del /[ \t]*=[ \t]*/ "="
let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/
let word = /[a-zA-Z][a-zA-Z0-9._-]*/
let word = /[a-z][a-z0-9._-]*/i
let comment = Util.comment
let eol = Util.doseol
@@ -59,13 +59,18 @@ let empty = Util.empty_dos
let indent = Util.indent
(* borrowed from shellvars.aug *)
let char_arg_dir = /([^\\ '"\t\r\n]|[^\\ '"\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_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/
let cdot = /\\\\./
let cl = /\\\\\n/
let dquot =
let no_dquot = /[^"\\\r\n]/
in /"/ . (no_dquot|cdot|cl)* . /"/
let dquot_msg =
let no_dquot = /([^ \t"\\\r\n]|[^"\\\r\n]+[^ \t"\\\r\n])/
in /"/ . (no_dquot|cdot|cl)*
let squot =
let no_squot = /[^'\\\r\n]/
in /'/ . (no_squot|cdot|cl)* . /'/
@@ -76,12 +81,24 @@ let comp = /[<>=]?=/
*****************************************************************)
let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ]
(* message argument starts with " but ends at EOL *)
let arg_dir_msg = [ label "arg" . store dquot_msg ]
let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ]
let arg_wl = [ label "arg" . store (char_arg_wl+|dquot|squot) ]
(* comma-separated wordlist as permitted in the SSLRequire directive *)
let arg_wordlist =
let wl_start = Util.del_str "{" in
let wl_end = Util.del_str "}" in
let wl_sep = del /[ \t]*,[ \t]*/ ", "
in [ label "wordlist" . wl_start . arg_wl . (wl_sep . arg_wl)* . wl_end ]
let argv (l:lens) = l . (sep_spc . l)*
let directive = [ indent . label "directive" . store word .
(sep_spc . argv arg_dir)? . eol ]
let directive =
(* arg_dir_msg may be the last or only argument *)
let dir_args = (argv (arg_dir|arg_wordlist) . (sep_spc . arg_dir_msg)?) | arg_dir_msg
in [ indent . label "directive" . store word . (sep_spc . dir_args)? . eol ]
let section (body:lens) =
(* opt_eol includes empty lines *)
@@ -91,7 +108,7 @@ let section (body:lens) =
indent . dels "</" in
let kword = key word in
let dword = del word "a" in
[ indent . dels "<" . square kword inner dword . del ">" ">" . eol ]
[ indent . dels "<" . square kword inner dword . del />[ \t\n\r]*/ ">\n" ]
let rec content = section (content|directive)
@@ -93,7 +93,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
add("enmod", default=constants.CLI_DEFAULTS["enmod"],
help="Path to the Apache 'a2enmod' binary.")
add("dismod", default=constants.CLI_DEFAULTS["dismod"],
help="Path to the Apache 'a2enmod' binary.")
help="Path to the Apache 'a2dismod' binary.")
add("le-vhost-ext", default=constants.CLI_DEFAULTS["le_vhost_ext"],
help="SSL vhost configuration extension.")
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
@@ -120,7 +120,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self.version = version
self.vhosts = None
self._enhance_func = {"redirect": self._enable_redirect,
"ensure-http-header": self._set_http_header}
"ensure-http-header": self._set_http_header}
@property
def mod_ssl_conf(self):
@@ -545,21 +545,43 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Check for Listen <port>
# Note: This could be made to also look for ip:443 combo
if not self.parser.find_dir("Listen", port):
logger.debug("No Listen %s directive found. Setting the "
"Apache Server to Listen on port %s", port, port)
if port == "443":
args = [port]
listens = [self.parser.get_arg(x).split()[0] for x in self.parser.find_dir("Listen")]
# In case no Listens are set (which really is a broken apache config)
if not listens:
listens = ["80"]
for listen in listens:
# For any listen statement, check if the machine also listens on Port 443.
# If not, add such a listen statement.
if len(listen.split(":")) == 1:
# Its listening to all interfaces
if port not in listens:
if port == "443":
args = [port]
else:
# Non-standard ports should specify https protocol
args = [port, "https"]
self.parser.add_dir_to_ifmodssl(
parser.get_aug_path(
self.parser.loc["listen"]), "Listen", args)
self.save_notes += "Added Listen %s directive to %s\n" % (
port, self.parser.loc["listen"])
listens.append(port)
else:
# Non-standard ports should specify https protocol
args = [port, "https"]
self.parser.add_dir_to_ifmodssl(
parser.get_aug_path(
self.parser.loc["listen"]), "Listen", args)
self.save_notes += "Added Listen %s directive to %s\n" % (
port, self.parser.loc["listen"])
# The Listen statement specifies an ip
_, ip = listen[::-1].split(":", 1)
ip = ip[::-1]
if "%s:%s" % (ip, port) not in listens:
if port == "443":
args = ["%s:%s" % (ip, port)]
else:
# Non-standard ports should specify https protocol
args = ["%s:%s" % (ip, port), "https"]
self.parser.add_dir_to_ifmodssl(
parser.get_aug_path(
self.parser.loc["listen"]), "Listen", args)
self.save_notes += "Added Listen %s:%s directive to %s\n" % (
ip, port, self.parser.loc["listen"])
listens.append("%s:%s" % (ip, port))
def make_addrs_sni_ready(self, addrs):
"""Checks to see if the server is ready for SNI challenges.
@@ -391,6 +391,39 @@ class TwoVhost80Test(util.ApacheTest):
self.assertEqual(mock_add_dir.call_count, 2)
def test_prepare_server_https_named_listen(self):
mock_find = mock.Mock()
mock_find.return_value = ["test1", "test2", "test3"]
mock_get = mock.Mock()
mock_get.side_effect = ["1.2.3.4:80", "[::1]:80", "1.1.1.1:443"]
mock_add_dir = mock.Mock()
mock_enable = mock.Mock()
self.config.parser.find_dir = mock_find
self.config.parser.get_arg = mock_get
self.config.parser.add_dir_to_ifmodssl = mock_add_dir
self.config.enable_mod = mock_enable
# Test Listen statements with specific ip listeed
self.config.prepare_server_https("443")
# Should only be 2 here, as the third interface already listens to the correct port
self.assertEqual(mock_add_dir.call_count, 2)
# Check argument to new Listen statements
self.assertEqual(mock_add_dir.call_args_list[0][0][2], ["1.2.3.4:443"])
self.assertEqual(mock_add_dir.call_args_list[1][0][2], ["[::1]:443"])
# Reset return lists and inputs
mock_add_dir.reset_mock()
mock_get.side_effect = ["1.2.3.4:80", "[::1]:80", "1.1.1.1:443"]
# Test
self.config.prepare_server_https("8080", temp=True)
self.assertEqual(mock_add_dir.call_count, 3)
self.assertEqual(mock_add_dir.call_args_list[0][0][2], ["1.2.3.4:8080", "https"])
self.assertEqual(mock_add_dir.call_args_list[1][0][2], ["[::1]:8080", "https"])
self.assertEqual(mock_add_dir.call_args_list[2][0][2], ["1.1.1.1:8080", "https"])
def test_make_vhost_ssl(self):
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
@@ -1,5 +1,3 @@
<VirtualHost 1.1.1.1>
ServerName invalid.net
</virtualHost>
+1 -1
View File
@@ -4,7 +4,7 @@ from setuptools import setup
from setuptools import find_packages
version = '0.1.0.dev0'
version = '0.2.0.dev0'
install_requires = [
'acme=={0}'.format(version),
+2 -2
View File
@@ -147,9 +147,9 @@ then
elif uname | grep -iq FreeBSD ; then
ExperimentalBootstrap "FreeBSD" freebsd.sh "$SUDO"
elif uname | grep -iq Darwin ; then
ExperimentalBootstrap "Mac OS X" mac.sh
ExperimentalBootstrap "Mac OS X" mac.sh # homebrew doesn't normally run as root
elif grep -iq "Amazon Linux" /etc/issue ; then
ExperimentalBootstrap "Amazon Linux" _rpm_common.sh
ExperimentalBootstrap "Amazon Linux" _rpm_common.sh "$SUDO"
else
echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!"
echo
+1 -1
View File
@@ -4,7 +4,7 @@ from setuptools import setup
from setuptools import find_packages
version = '0.1.0.dev0'
version = '0.2.0.dev0'
install_requires = [
'letsencrypt=={0}'.format(version),
+1 -1
View File
@@ -4,7 +4,7 @@ from setuptools import setup
from setuptools import find_packages
version = '0.1.0.dev0'
version = '0.2.0.dev0'
install_requires = [
'acme=={0}'.format(version),
-1
View File
@@ -1,5 +1,4 @@
"""Let's Encrypt client."""
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
# '0.1.0.dev0'
__version__ = '0.2.0.dev0'
+14 -2
View File
@@ -306,7 +306,7 @@ def _report_new_cert(cert_path, fullchain_path):
def _suggest_donate():
"Suggest a donation to support Let's Encrypt"
reporter_util = zope.component.getUtility(interfaces.IReporter)
msg = ("If like Let's Encrypt, please consider supporting our work by:\n\n"
msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n"
"Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n"
"Donating to EFF: https://eff.org/donate-le\n\n")
reporter_util.add_message(msg, reporter_util.LOW_PRIORITY)
@@ -696,6 +696,14 @@ class HelpfulArgumentParser(object):
parsed_args = self.parser.parse_args(self.args)
parsed_args.func = self.VERBS[self.verb]
# Do any post-parsing homework here
# argparse seemingly isn't flexible enough to give us this behaviour easily...
if parsed_args.staging:
if parsed_args.server not in (flag_default("server"), constants.STAGING_URI):
raise errors.Error("--server value conflicts with --staging")
parsed_args.server = constants.STAGING_URI
return parsed_args
@@ -855,7 +863,7 @@ def prepare_and_parse_args(plugins, args):
"email address. This is strongly discouraged, because in the "
"event of key loss or account compromise you will irrevocably "
"lose access to your account. You will also be unable to receive "
"notice about impending expiration of revocation of your "
"notice about impending expiration or revocation of your "
"certificates. Updates to the Subscriber Agreement will still "
"affect you, and will be effective 14 days after posting an "
"update to the web site.")
@@ -1037,6 +1045,10 @@ def _paths_parser(helpful):
help="Logs directory.")
add("paths", "--server", default=flag_default("server"),
help=config_help("server"))
# overwrites server, handled in HelpfulArgumentParser.parse_args()
add("testing", "--test-cert", "--staging", action='store_true', dest='staging',
help='Use the staging server to obtain test (invalid) certs; equivalent'
' to --server ' + constants.STAGING_URI)
def _plugins_parsing(helpful, plugins):
+3 -29
View File
@@ -1,13 +1,13 @@
"""Let's Encrypt user-supplied configuration."""
import os
import urlparse
import re
import zope.interface
from letsencrypt import constants
from letsencrypt import errors
from letsencrypt import interfaces
from letsencrypt import le_util
class NamespaceConfig(object):
@@ -123,31 +123,5 @@ def check_config_sanity(config):
# Domain checks
if config.namespace.domains is not None:
_check_config_domain_sanity(config.namespace.domains)
def _check_config_domain_sanity(domains):
"""Helper method for check_config_sanity which validates
domain flag values and errors out if the requirements are not met.
:param domains: List of domains
:type domains: `list` of `string`
:raises ConfigurationError: for invalid domains and cases where Let's
Encrypt currently will not issue certificates
"""
# Check if there's a wildcard domain
if any(d.startswith("*.") for d in domains):
raise errors.ConfigurationError(
"Wildcard domains are not supported")
# Punycode
if any("xn--" in d for d in domains):
raise errors.ConfigurationError(
"Punycode domains are not supported")
# FQDN checks from
# http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/
# Characters used, domain parts < 63 chars, tld > 1 < 64 chars
# first and last char is not "-"
fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,63}$")
if any(True for d in domains if not fqdn.match(d)):
raise errors.ConfigurationError("Requested domain is not a FQDN")
for domain in config.namespace.domains:
le_util.check_domain_sanity(domain)
+2 -1
View File
@@ -30,8 +30,9 @@ CLI_DEFAULTS = dict(
auth_chain_path="./chain.pem",
strict_permissions=False,
)
"""Defaults for CLI flags and `.IConfig` attributes."""
STAGING_URI = "https://acme-staging.api.letsencrypt.org/directory"
"""Defaults for CLI flags and `.IConfig` attributes."""
RENEWER_DEFAULTS = dict(
renewer_enabled="yes",
+20 -2
View File
@@ -4,6 +4,7 @@ import os
import zope.component
from letsencrypt import errors
from letsencrypt import interfaces
from letsencrypt import le_util
from letsencrypt.display import util as display_util
@@ -186,7 +187,8 @@ def choose_names(installer):
logger.debug("No installer, picking names manually")
return _choose_names_manually()
names = list(installer.get_all_names())
domains = list(installer.get_all_names())
names = get_valid_domains(domains)
if not names:
manual = util(interfaces.IDisplay).yesno(
@@ -207,6 +209,22 @@ def choose_names(installer):
else:
return []
def get_valid_domains(domains):
"""Helper method for choose_names that implements basic checks
on domain names
:param list domains: Domain names to validate
:return: List of valid domains
:rtype: list
"""
valid_domains = []
for domain in domains:
try:
le_util.check_domain_sanity(domain)
valid_domains.append(domain)
except errors.ConfigurationError:
continue
return valid_domains
def _filter_names(names):
"""Determine which names the user would like to select from a list.
@@ -245,7 +263,7 @@ def success_installation(domains):
"""
util(interfaces.IDisplay).notification(
"Congratulations! You have successfully enabled {0}!{1}{1}"
"Congratulations! You have successfully enabled {0}{1}{1}"
"You should test your configuration at:{1}{2}".format(
_gen_https_names(domains),
os.linesep,
+38
View File
@@ -10,6 +10,8 @@ import stat
import subprocess
import sys
import configargparse
from letsencrypt import errors
@@ -278,5 +280,41 @@ def add_deprecated_argument(add_argument, argument_name, nargs):
sys.stderr.write(
"Use of {0} is deprecated.\n".format(option_string))
configargparse.ACTION_TYPES_THAT_DONT_NEED_A_VALUE.add(ShowWarning)
add_argument(argument_name, action=ShowWarning,
help=argparse.SUPPRESS, nargs=nargs)
def check_domain_sanity(domain):
"""Method which validates domain value and errors out if
the requirements are not met.
:param domain: Domain to check
:type domains: `string`
:raises ConfigurationError: for invalid domains and cases where Let's
Encrypt currently will not issue certificates
"""
# Check if there's a wildcard domain
if domain.startswith("*."):
raise errors.ConfigurationError(
"Wildcard domains are not supported")
# Punycode
if "xn--" in domain:
raise errors.ConfigurationError(
"Punycode domains are not presently supported")
# Unicode
try:
domain.encode('ascii')
except UnicodeDecodeError:
raise errors.ConfigurationError(
"Internationalized domain names are not presently supported")
# FQDN checks from
# http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/
# Characters used, domain parts < 63 chars, tld > 1 < 64 chars
# first and last char is not "-"
fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,63}$")
if not fqdn.match(domain):
raise errors.ConfigurationError("Requested domain is not a FQDN")
+19
View File
@@ -15,6 +15,7 @@ from acme import jose
from letsencrypt import account
from letsencrypt import cli
from letsencrypt import configuration
from letsencrypt import constants
from letsencrypt import crypto_util
from letsencrypt import errors
from letsencrypt import le_util
@@ -343,6 +344,19 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
namespace = cli.prepare_and_parse_args(plugins, long_args)
self.assertEqual(namespace.domains, ['example.com', 'another.net'])
def test_parse_server(self):
plugins = disco.PluginsRegistry.find_all()
short_args = ['--server', 'example.com']
namespace = cli.prepare_and_parse_args(plugins, short_args)
self.assertEqual(namespace.server, 'example.com')
short_args = ['--staging']
namespace = cli.prepare_and_parse_args(plugins, short_args)
self.assertEqual(namespace.server, constants.STAGING_URI)
short_args = ['--staging', '--server', 'example.com']
self.assertRaises(errors.Error, cli.prepare_and_parse_args, plugins, short_args)
def test_parse_webroot(self):
plugins = disco.PluginsRegistry.find_all()
webroot_args = ['--webroot', '-w', '/var/www/example',
@@ -537,6 +551,11 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods
self.assertEqual(path, os.path.abspath(path))
self.assertEqual(contents, test_contents)
def test_agree_dev_preview_config(self):
with MockedVerb('run') as mocked_run:
self._call(['-c', test_util.vector_path('cli.ini')])
self.assertTrue(mocked_run.called)
class DetermineAccountTest(unittest.TestCase):
"""Tests for letsencrypt.cli._determine_account."""
+12
View File
@@ -1,3 +1,4 @@
# coding=utf-8
"""Test letsencrypt.display.ops."""
import os
import sys
@@ -385,6 +386,17 @@ class ChooseNamesTest(unittest.TestCase):
self.assertEqual(self._call(self.mock_install), [])
def test_get_valid_domains(self):
from letsencrypt.display.ops import get_valid_domains
all_valid = ["example.com", "second.example.com",
"also.example.com"]
all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN",
"uniçodé.com"]
two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"]
self.assertEqual(get_valid_domains(all_valid), all_valid)
self.assertEqual(get_valid_domains(all_invalid), [])
self.assertEqual(len(get_valid_domains(two_valid)), 2)
class SuccessInstallationTest(unittest.TestCase):
# pylint: disable=too-few-public-methods
+1
View File
@@ -0,0 +1 @@
agree-dev-preview = True
+1 -1
View File
@@ -4,7 +4,7 @@ from setuptools import setup
from setuptools import find_packages
version = '0.1.0.dev0'
version = '0.2.0.dev0'
install_requires = [
'setuptools', # pkg_resources
@@ -0,0 +1,52 @@
<VirtualHost *:443>
ServerAdmin webmaster@localhost
ServerAlias www.example.com
ServerName example.com
DocumentRoot /var/www/example.com/www/
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRS$
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/example.com/www>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
# This directive allows us to have apache2's default start page
# in /apache2-default/, but still have / go to the right place
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog /var/log/apache2/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog /var/log/apache2/access.log combined
ServerSignature On
Alias /apache_doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
</VirtualHost>
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# A hackish script to see if the client is behaving as expected
# with each of the "passing" conf files.
# TODO presently this requires interaction and human judgement to
# assess, but it should be automated
export EA=/etc/apache2/
TESTDIR="`dirname $0`"
LEROOT="`realpath \"$TESTDIR/../../\"`"
cd $TESTDIR/passing
function CleanupExit() {
echo control c, exiting tests...
if [ "$f" != "" ] ; then
sudo rm /etc/apache2/sites-{enabled,available}/"$f"
fi
exit 1
}
trap CleanupExit INT
for f in *.conf ; do
echo testing "$f"
sudo cp "$f" "$EA"/sites-available/
sudo ln -s "$EA/sites-available/$f" "$EA/sites-enabled/$f"
sudo "$LEROOT"/venv/bin/letsencrypt --apache certonly -t
sudo rm /etc/apache2/sites-{enabled,available}/"$f"
done
@@ -0,0 +1,37 @@
<VirtualHost *:80>
ServerAdmin denver@ossguy.com
ServerName c-beta.ossguy.com
Alias /robots.txt /home/denver/www/c-beta.ossguy.com/static/robots.txt
Alias /favicon.ico /home/denver/www/c-beta.ossguy.com/static/favicon.ico
AliasMatch /(.*\.css) /home/denver/www/c-beta.ossguy.com/static/$1
AliasMatch /(.*\.js) /home/denver/www/c-beta.ossguy.com/static/$1
AliasMatch /(.*\.png) /home/denver/www/c-beta.ossguy.com/static/$1
AliasMatch /(.*\.gif) /home/denver/www/c-beta.ossguy.com/static/$1
AliasMatch /(.*\.jpg) /home/denver/www/c-beta.ossguy.com/static/$1
WSGIScriptAlias / /home/denver/www/c-beta.ossguy.com/django.wsgi
WSGIDaemonProcess c-beta-ossguy user=www-data group=www-data home=/var/www processes=5 threads=10 maximum-requests=1000 umask=0007 display-name=c-beta-ossguy
WSGIProcessGroup c-beta-ossguy
WSGIApplicationGroup %{GLOBAL}
DocumentRoot /home/denver/www/c-beta.ossguy.com/static
<Directory /home/denver/www/c-beta.ossguy.com/static>
Options -Indexes +FollowSymLinks -MultiViews
Require all granted
AllowOverride None
</Directory>
<Directory /home/denver/www/c-beta.ossguy.com/static/source>
Options +Indexes +FollowSymLinks -MultiViews
Require all granted
AllowOverride None
</Directory>
# Custom log file locations
LogLevel warn
ErrorLog /tmp/error.log
CustomLog /tmp/access.log combined
</VirtualHost>
@@ -3,3 +3,5 @@ Modules required to parse these conf files:
ssl
rewrite
macro
wsgi
deflate
@@ -0,0 +1,116 @@
#
# Apache/PHP/Drupal settings:
#
# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl|svn-base)$|^(code-style\.pl|Entries.*|Repository|Root|Tag|Template|all-wcprops|entries|format)$">
Order allow,deny
</FilesMatch>
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Follow symbolic links in this directory.
Options +FollowSymLinks
# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php
# Force simple error message for requests for non-existent favicon.ico.
<Files favicon.ico>
# There is no end quote below, for compatibility with Apache 1.3.
ErrorDocument 404 "The requested file favicon.ico was not found.
</Files>
# Set the default handler.
DirectoryIndex index.php
# Override PHP settings. More in sites/default/settings.php
# but the following cannot be changed at runtime.
# PHP 4, Apache 1.
<IfModule mod_php4.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# PHP 4, Apache 2.
<IfModule sapi_apache2.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
php_value magic_quotes_gpc 0
php_value register_globals 0
php_value session.auto_start 0
php_value mbstring.http_input pass
php_value mbstring.http_output pass
php_value mbstring.encoding_translation 0
</IfModule>
# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
# Enable expirations.
ExpiresActive On
# Cache all files for 2 weeks after access (A).
ExpiresDefault A1209600
<FilesMatch \.php$>
# Do not allow PHP scripts to be cached unless they explicitly send cache
# headers themselves. Otherwise all scripts would have to overwrite the
# headers set by mod_expires if they want another caching behavior. This may
# fail if an error occurs early in the bootstrap process, and it may cause
# problems if a non-Drupal PHP file is installed in a subdirectory.
ExpiresActive Off
</FilesMatch>
</IfModule>
# Various rewrite rules.
<IfModule mod_rewrite.c>
RewriteEngine on
# If your site can be accessed both with and without the 'www.' prefix, you
# can use one of the following settings to redirect users to your preferred
# URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
#
# To redirect all users to access the site WITH the 'www.' prefix,
# (http://example.com/... will be redirected to http://www.example.com/...)
# adapt and uncomment the following:
# RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
# RewriteRule ^(.*)$ http://www.example.com/$1 [L,R=301]
#
# To redirect all users to access the site WITHOUT the 'www.' prefix,
# (http://www.example.com/... will be redirected to http://example.com/...)
# uncomment and adapt the following:
# RewriteCond %{HTTP_HOST} ^www\.example\.com$ [NC]
# RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]
# Modify the RewriteBase if you are using Drupal in a subdirectory or in a
# VirtualDocumentRoot and the rewrite rules are not working properly.
# For example if your site is at http://example.com/drupal uncomment and
# modify the following line:
# RewriteBase /drupal
#
# If your site is running in a VirtualDocumentRoot at http://example.com/,
# uncomment the following line:
# RewriteBase /
# Rewrite URLs of the form 'x' to the form 'index.php?q=x'.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
</IfModule>
# $Id$
@@ -0,0 +1,36 @@
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
ServerName www.example.com
ServerAlias example.com
SetOutputFilter DEFLATE
# Do not attempt to compress the following extensions
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|swf|flv|zip|gz|tar|mp3|mp4|m4v)$ no-gzip dont-vary
ServerAdmin webmaster@localhost
DocumentRoot /var/www/proof
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
@@ -0,0 +1,52 @@
<VirtualHost *:443>
ServerAdmin webmaster@localhost
ServerAlias www.example.com
ServerName example.com
DocumentRoot /var/www/example.com/www/
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRS$
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/example.com/www>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
# This directive allows us to have apache2's default start page
# in /apache2-default/, but still have / go to the right place
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog /var/log/apache2/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog /var/log/apache2/access.log combined
ServerSignature On
Alias /apache_doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
</VirtualHost>
@@ -0,0 +1 @@
SSLRequire %{SSL_CLIENT_S_DN_CN} in {"foo@bar.com", "bar@foo.com"}
@@ -0,0 +1,28 @@
<IfModule mod_ssl.c>
<VirtualHost *:443>
ServerAdmin info@somethingnewentertainment.com
ServerName somethingnewentertainment.com
DocumentRoot /var/www/html
ErrorLog /var/log/apache2/error.log
CustomLog /var/log/apache2/access.log combined
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EEC DH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRS A RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost> </IfModule>