diff --git a/Dockerfile-dev b/Dockerfile-dev index 835b3a7cc..2fe1a818d 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -32,7 +32,7 @@ RUN /opt/letsencrypt/src/ubuntu.sh && \ # the above is not likely to change, so by putting it further up the # Dockerfile we make sure we cache as much as possible -COPY setup.py README.rst CHANGES.rst MANIFEST.in requirements.txt EULA linter_plugin.py tox.cover.sh tox.ini /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in requirements.txt EULA linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/ # all above files are necessary for setup.py, however, package source # code directory has to be copied separately to a subdirectory... @@ -46,6 +46,8 @@ COPY letsencrypt /opt/letsencrypt/src/letsencrypt/ COPY acme /opt/letsencrypt/src/acme/ COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ +COPY letshelp-letsencrypt /opt/letsencrypt/src/letshelp-letsencrypt/ +COPY letsencrypt-compatibility-test /opt/letsencrypt/src/letsencrypt-compatibility-test/ COPY tests /opt/letsencrypt/src/tests/ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ @@ -55,6 +57,8 @@ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ -e /opt/letsencrypt/src \ -e /opt/letsencrypt/src/letsencrypt-apache \ -e /opt/letsencrypt/src/letsencrypt-nginx \ + -e /opt/letsencrypt/src/letshelp-letsencrypt \ + -e /opt/letsencrypt/src/letsencrypt-compatibility-test \ -e /opt/letsencrypt/src[dev,docs,testing] # install in editable mode (-e) to save space: it's not possible to diff --git a/bootstrap/mac.sh b/bootstrap/mac.sh index a48afe11e..6779188a7 100755 --- a/bootstrap/mac.sh +++ b/bootstrap/mac.sh @@ -1,2 +1,8 @@ #!/bin/sh +if ! hash brew 2>/dev/null; then + echo "Homebrew Not Installed\nDownloading..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +fi + brew install augeas +brew install dialog diff --git a/docs/contributing.rst b/docs/contributing.rst index 00ac509ab..c6443e3b2 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -68,17 +68,16 @@ The following tools are there to help you: Integration ~~~~~~~~~~~ -First, install `Go`_ 1.5 (pick a value for GOPATH and put $GOPATH/bin in your -PATH), libtool-ltdl, mariadb-server and rabbitmq-server and then start -Boulder_, an ACME CA server:: +First, install `Go`_ 1.5, libtool-ltdl, mariadb-server and +rabbitmq-server and then start Boulder_, an ACME CA server:: ./tests/boulder-start.sh -The script will download, compile and run the executable; please be patient - -it will take some time... Once its ready, you will see ``Server running, -listening on 127.0.0.1:4000...``. Add the ``venv/bin/`` subdirectory of your -letsencrypt repo to your path, and add an ``/etc/hosts`` entry pointing -``le.wtf`` to 127.0.0.1. You may now run (in a separate terminal):: +The script will download, compile and run the executable; please be +patient - it will take some time... Once its ready, you will see +``Server running, listening on 127.0.0.1:4000...``. Add an +``/etc/hosts`` entry pointing ``le.wtf`` to 127.0.0.1. You may now +run (in a separate terminal):: ./tests/boulder-integration.sh && echo OK || echo FAIL @@ -130,9 +129,8 @@ Docker OSX users will probably find it easiest to set up a Docker container for development. Let's Encrypt comes with a Dockerfile (``Dockerfile-dev``) -for doing so. To use Docker on OSX, install boot2docker using the -instructions at https://docs.docker.com/installation/mac/ and start it -from the command line (``boot2docker init``). +for doing so. To use Docker on OSX, install and setup docker-machine using the +instructions at https://docs.docker.com/installation/mac/. To build the development Docker image:: diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index d7dc3c422..0a3643064 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -241,6 +241,10 @@ class ApacheParser(object): Directives should be in the form of a case insensitive regex currently .. todo:: arg should probably be a list + .. todo:: arg search currently only supports direct matching. It does + not handle the case of variables or quoted arguments. This should + be adapted to use a generic search for the directive and then do a + case-insensitive self.get_arg filter Note: Augeas is inherently case sensitive while Apache is case insensitive. Augeas 1.0 allows case insensitive regexes like @@ -315,6 +319,14 @@ class ApacheParser(object): """ value = self.aug.get(match) + + # No need to strip quotes for variables, as apache2ctl already does this + # but we do need to strip quotes for all normal arguments. + + # Note: normal argument may be a quoted variable + # e.g. strip now, not later + value = value.strip("'\"") + variables = ApacheParser.arg_var_interpreter.findall(value) for var in variables: @@ -390,10 +402,15 @@ class ApacheParser(object): # logger.error("Error: Invalid regexp characters in %s", arg) # return [] + # Remove beginning and ending quotes + arg = arg.strip("'\"") + # Standardize the include argument based on server root if not arg.startswith("/"): # Normpath will condense ../ arg = os.path.normpath(os.path.join(self.root, arg)) + else: + arg = os.path.normpath(arg) # Attempts to add a transform to the file if one does not already exist if os.path.isdir(arg): diff --git a/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py b/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py index e7bd03cc5..7099c388f 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py @@ -32,6 +32,7 @@ class ComplexParserTest(util.ParserTest): "COMPLEX": "", "tls_port": "1234", "fnmatch_filename": "test_fnmatch.conf", + "tls_port_str": "1234" } ) @@ -49,6 +50,12 @@ class ComplexParserTest(util.ParserTest): self.assertEqual(len(matches), 1) self.assertEqual(self.parser.get_arg(matches[0]), "1234") + def test_basic_variable_parsing_quotes(self): + matches = self.parser.find_dir("TestVariablePortStr") + + self.assertEqual(len(matches), 1) + self.assertEqual(self.parser.get_arg(matches[0]), "1234") + def test_invalid_variable_parsing(self): del self.parser.variables["tls_port"] @@ -89,6 +96,7 @@ class ComplexParserTest(util.ParserTest): else: self.assertFalse(self.parser.find_dir("FNMATCH_DIRECTIVE")) + # NOTE: Only run one test per function otherwise you will have inf recursion def test_include(self): self.verify_fnmatch("test_fnmatch.?onf") @@ -98,6 +106,15 @@ class ComplexParserTest(util.ParserTest): def test_include_fullpath(self): self.verify_fnmatch(os.path.join(self.config_path, "test_fnmatch.conf")) + def test_include_fullpath_trailing_slash(self): + self.verify_fnmatch(self.config_path + "//") + + def test_include_single_quotes(self): + self.verify_fnmatch("'" + self.config_path + "'") + + def test_include_double_quotes(self): + self.verify_fnmatch('"' + self.config_path + '"') + def test_include_variable(self): self.verify_fnmatch("../complex_parsing/${fnmatch_filename}") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf index 26bf47263..14cf95f9e 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf @@ -46,6 +46,8 @@ IncludeOptional sites-enabled/*.conf Define COMPLEX Define tls_port 1234 +Define tls_port_str "1234" + Define fnmatch_filename test_fnmatch.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf index a38191837..1a9edff74 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf @@ -1,4 +1,5 @@ TestVariablePort ${tls_port} +TestVariablePortStr "${tls_port_str}" LoadModule status_module modules/mod_status.so diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py index 43070cf03..6181da16b 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py +++ b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py @@ -39,7 +39,7 @@ def create_le_config(parent_dir): def extract_configs(configs, parent_dir): """Extracts configs to a new dir under parent_dir and returns it""" - config_dir = os.path.join(parent_dir, "configs") + config_dir = os.path.join(parent_dir, "renewal") if os.path.isdir(configs): shutil.copytree(configs, config_dir, symlinks=True) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py b/letsencrypt-nginx/letsencrypt_nginx/tests/util.py index 9c8c6a5dd..363944490 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/util.py @@ -4,9 +4,12 @@ import pkg_resources import unittest import mock +import zope.component from acme import jose +from letsencrypt import configuration + from letsencrypt.tests import test_util from letsencrypt.plugins import common @@ -55,11 +58,17 @@ def get_nginx_configurator( backup_dir=backups, temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"), in_progress_dir=os.path.join(backups, "IN_PROGRESS"), + server="https://acme-server.org:443/new", dvsni_port=5001, ), name="nginx", version=version) config.prepare() + + # Provide general config utility. + nsconfig = configuration.NamespaceConfig(config.config) + zope.component.provideUtility(nsconfig) + return config diff --git a/letsencrypt/account.py b/letsencrypt/account.py index e705b1484..8bee22102 100644 --- a/letsencrypt/account.py +++ b/letsencrypt/account.py @@ -129,8 +129,9 @@ class AccountFileStorage(interfaces.AccountStorage): """ def __init__(self, config): - le_util.make_or_verify_dir(config.accounts_dir, 0o700, os.geteuid()) self.config = config + le_util.make_or_verify_dir(config.accounts_dir, 0o700, os.geteuid(), + self.config.strict_permissions) def _account_dir_path(self, account_id): return os.path.join(self.config.accounts_dir, account_id) @@ -186,7 +187,8 @@ class AccountFileStorage(interfaces.AccountStorage): def save(self, account): account_dir_path = self._account_dir_path(account.id) - le_util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid()) + le_util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(), + self.config.strict_permissions) try: with open(self._regr_path(account_dir_path), "w") as regr_file: regr_file.write(account.regr.json_dumps()) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 6e2849466..cd34708b9 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -701,6 +701,10 @@ def create_parser(plugins, args): "security", "-r", "--redirect", action="store_true", help="Automatically redirect all HTTP traffic to HTTPS for the newly " "authenticated vhost.") + helpful.add( + "security", "--strict-permissions", action="store_true", + help="Require that all configuration files are owned by the current " + "user; only needed if your config is somewhere unsafe like /tmp/") _paths_parser(helpful) # _plugins_parsing should be the last thing to act upon the main @@ -884,14 +888,17 @@ def _handle_exception(exc_type, exc_value, trace, args): if issubclass(exc_type, errors.Error): sys.exit(exc_value) - elif args is None: - sys.exit( - "An unexpected error occurred. Please see the logfile '{0}' " - "for more details.".format(logfile)) else: - sys.exit( - "An unexpected error occurred. Please see the logfiles in {0} " - "for more details.".format(args.logs_dir)) + # Tell the user a bit about what happened, without overwhelming + # them with a full traceback + msg = ("An unexpected error occurred.\n" + + traceback.format_exception_only(exc_type, exc_value)[0] + + "Please see the ") + if args is None: + msg += "logfile '{0}' for more details.".format(logfile) + else: + msg += "logfiles in {0} for more details.".format(args.logs_dir) + sys.exit(msg) else: sys.exit("".join( traceback.format_exception(exc_type, exc_value, trace))) @@ -906,15 +913,18 @@ def main(cli_args=sys.argv[1:]): parser, tweaked_cli_args = create_parser(plugins, cli_args) args = parser.parse_args(tweaked_cli_args) config = configuration.NamespaceConfig(args) + zope.component.provideUtility(config) # Setup logging ASAP, otherwise "No handlers could be found for # logger ..." TODO: this should be done before plugins discovery for directory in config.config_dir, config.work_dir: le_util.make_or_verify_dir( - directory, constants.CONFIG_DIRS_MODE, os.geteuid()) + directory, constants.CONFIG_DIRS_MODE, os.geteuid(), + "--strict-permissions" in cli_args) # TODO: logs might contain sensitive data such as contents of the # private key! #525 - le_util.make_or_verify_dir(args.logs_dir, 0o700, os.geteuid()) + le_util.make_or_verify_dir( + args.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args) _setup_logging(args) # do not log `args`, as it contains sensitive data (e.g. revoke --key)! diff --git a/letsencrypt/client.py b/letsencrypt/client.py index e9107e156..84ce9b7b2 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -313,7 +313,8 @@ class Client(object): """ for path in cert_path, chain_path: le_util.make_or_verify_dir( - os.path.dirname(path), 0o755, os.geteuid()) + os.path.dirname(path), 0o755, os.geteuid(), + self.config.strict_permissions) # try finally close cert_chain_abspath = None diff --git a/letsencrypt/constants.py b/letsencrypt/constants.py index 0d00f2d75..6c67ce445 100644 --- a/letsencrypt/constants.py +++ b/letsencrypt/constants.py @@ -88,7 +88,7 @@ LIVE_DIR = "live" TEMP_CHECKPOINT_DIR = "temp_checkpoint" """Temporary checkpoint directory (relative to `IConfig.work_dir`).""" -RENEWAL_CONFIGS_DIR = "configs" +RENEWAL_CONFIGS_DIR = "renewal" """Renewal configs directory, relative to `IConfig.config_dir`.""" RENEWER_CONFIG_FILENAME = "renewer.conf" diff --git a/letsencrypt/crypto_util.py b/letsencrypt/crypto_util.py index 71628677e..79cd24ed6 100644 --- a/letsencrypt/crypto_util.py +++ b/letsencrypt/crypto_util.py @@ -9,11 +9,13 @@ import logging import os import OpenSSL +import zope.component from acme import crypto_util as acme_crypto_util from acme import jose from letsencrypt import errors +from letsencrypt import interfaces from letsencrypt import le_util @@ -45,8 +47,10 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): logger.exception(err) raise err + config = zope.component.getUtility(interfaces.IConfig) # Save file - le_util.make_or_verify_dir(key_dir, 0o700, os.geteuid()) + le_util.make_or_verify_dir(key_dir, 0o700, os.geteuid(), + config.strict_permissions) key_f, key_path = le_util.unique_file( os.path.join(key_dir, keyname), 0o600) key_f.write(key_pem) @@ -73,8 +77,10 @@ def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"): """ csr_pem, csr_der = make_csr(privkey.pem, names) + config = zope.component.getUtility(interfaces.IConfig) # Save CSR - le_util.make_or_verify_dir(path, 0o755, os.geteuid()) + le_util.make_or_verify_dir(path, 0o755, os.geteuid(), + config.strict_permissions) csr_f, csr_filename = le_util.unique_file( os.path.join(path, csrname), 0o644) csr_f.write(csr_pem) diff --git a/letsencrypt/le_util.py b/letsencrypt/le_util.py index 194a80201..ffc7da190 100644 --- a/letsencrypt/le_util.py +++ b/letsencrypt/le_util.py @@ -70,7 +70,7 @@ def exe_exists(exe): return False -def make_or_verify_dir(directory, mode=0o755, uid=0): +def make_or_verify_dir(directory, mode=0o755, uid=0, strict=False): """Make sure directory exists with proper permissions. :param str directory: Path to a directory. @@ -89,9 +89,10 @@ def make_or_verify_dir(directory, mode=0o755, uid=0): os.makedirs(directory, mode) except OSError as exception: if exception.errno == errno.EEXIST: - if not check_permissions(directory, mode, uid): + if strict and not check_permissions(directory, mode, uid): raise errors.Error( - "%s exists, this client can't access it" % directory) + "%s exists, but it should be owned by user %d with" + "permissions %s" % (directory, uid, oct(mode))) else: raise diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 2d7c3e1e4..6b9359db1 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -46,12 +46,9 @@ class ManualAuthenticatorTest(unittest.TestCase): self.assertEqual([], self.auth.perform([])) @mock.patch("letsencrypt.plugins.manual.sys.stdout") - @mock.patch("letsencrypt.plugins.manual.os.urandom") @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") @mock.patch("__builtin__.raw_input") - def test_perform(self, mock_raw_input, mock_verify, mock_urandom, - mock_stdout): - mock_urandom.side_effect = nonrandom_urandom + def test_perform(self, mock_raw_input, mock_verify, mock_stdout): mock_verify.return_value = True resp = challenges.SimpleHTTPResponse(tls=False) @@ -61,27 +58,7 @@ class ManualAuthenticatorTest(unittest.TestCase): self.achalls[0].challb.chall, "foo.com", KEY.public_key(), 4430) message = mock_stdout.write.mock_calls[0][1][0] - self.assertEqual(message, """\ -Make sure your web server displays the following content at -http://foo.com/.well-known/acme-challenge/ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ before continuing: - -{"header": {"alg": "RS256", "jwk": {"e": "AQAB", "kty": "RSA", "n": "rHVztFHtH92ucFJD_N_HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3C5Q"}}, "payload": "eyJ0bHMiOiBmYWxzZSwgInRva2VuIjogIlpYWmhSM2htUVVSek5uQlRVbUl5VEVGMk9VbGFaakUzUkhRemFuVjRSMG9yVUVOME9USjNjaXR2UVEiLCAidHlwZSI6ICJzaW1wbGVIdHRwIn0", "signature": "jFPJFC-2eRyBw7Sl0wyEBhsdvRZtKk8hc6HykEPAiofZlIwdIu76u2xHqMVZWSZdpxwMNUnnawTEAqgMWFydMA"} - -Content-Type header MUST be set to application/jose+json. - -If you don\'t have HTTP server configured, you can run the following -command on the target server (as root): - -mkdir -p /tmp/letsencrypt/public_html/.well-known/acme-challenge -cd /tmp/letsencrypt/public_html -echo -n \'{"header": {"alg": "RS256", "jwk": {"e": "AQAB", "kty": "RSA", "n": "rHVztFHtH92ucFJD_N_HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3C5Q"}}, "payload": "eyJ0bHMiOiBmYWxzZSwgInRva2VuIjogIlpYWmhSM2htUVVSek5uQlRVbUl5VEVGMk9VbGFaakUzUkhRemFuVjRSMG9yVUVOME9USjNjaXR2UVEiLCAidHlwZSI6ICJzaW1wbGVIdHRwIn0", "signature": "jFPJFC-2eRyBw7Sl0wyEBhsdvRZtKk8hc6HykEPAiofZlIwdIu76u2xHqMVZWSZdpxwMNUnnawTEAqgMWFydMA"}\' > .well-known/acme-challenge/ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ -# run only once per server: -$(command -v python2 || command -v python2.7 || command -v python2.6) -c \\ -"import BaseHTTPServer, SimpleHTTPServer; \\ -SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map = {\'\': \'application/jose+json\'}; \\ -s = BaseHTTPServer.HTTPServer((\'\', 4430), SimpleHTTPServer.SimpleHTTPRequestHandler); \\ -s.serve_forever()" \n""") - #self.assertTrue(validation in message) + self.assertTrue(self.achalls[0].chall.encode("token") in message) mock_verify.return_value = False self.assertEqual([None], self.auth.perform(self.achalls)) @@ -130,10 +107,5 @@ s.serve_forever()" \n""") mock_killpg.assert_called_once_with(1234, signal.SIGTERM) -def nonrandom_urandom(num_bytes): - """Returns a string of length num_bytes""" - return "x" * num_bytes - - if __name__ == "__main__": unittest.main() # pragma: no cover diff --git a/letsencrypt/renewer.py b/letsencrypt/renewer.py index 5f73a7dad..1c9cddc95 100644 --- a/letsencrypt/renewer.py +++ b/letsencrypt/renewer.py @@ -70,6 +70,7 @@ def renew(cert, old_version): # was an int, not a str) config.rsa_key_size = int(config.rsa_key_size) config.dvsni_port = int(config.dvsni_port) + zope.component.provideUtility(config) try: authenticator = plugins[renewalparams["authenticator"]] except KeyError: diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index 8eed59156..d5114ae71 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -31,7 +31,8 @@ class Reverter(object): self.config = config le_util.make_or_verify_dir( - config.backup_dir, constants.CONFIG_DIRS_MODE, os.geteuid()) + config.backup_dir, constants.CONFIG_DIRS_MODE, os.geteuid(), + self.config.strict_permissions) def revert_temporary_config(self): """Reload users original configuration files after a temporary save. @@ -180,7 +181,8 @@ class Reverter(object): """ le_util.make_or_verify_dir( - cp_dir, constants.CONFIG_DIRS_MODE, os.geteuid()) + cp_dir, constants.CONFIG_DIRS_MODE, os.geteuid(), + self.config.strict_permissions) op_fd, existing_filepaths = self._read_and_append( os.path.join(cp_dir, "FILEPATHS")) @@ -393,7 +395,8 @@ class Reverter(object): cp_dir = self.config.in_progress_dir le_util.make_or_verify_dir( - cp_dir, constants.CONFIG_DIRS_MODE, os.geteuid()) + cp_dir, constants.CONFIG_DIRS_MODE, os.geteuid(), + self.config.strict_permissions) return cp_dir diff --git a/letsencrypt/revoker.py b/letsencrypt/revoker.py index e8b154012..32c6f003d 100644 --- a/letsencrypt/revoker.py +++ b/letsencrypt/revoker.py @@ -54,7 +54,8 @@ class Revoker(object): self.config = config self.no_confirm = no_confirm - le_util.make_or_verify_dir(config.cert_key_backup, 0o700, os.geteuid()) + le_util.make_or_verify_dir(config.cert_key_backup, 0o700, os.geteuid(), + self.config.strict_permissions) # TODO: Find a better solution for this... self.list_path = os.path.join(config.cert_key_backup, "LIST") @@ -287,12 +288,12 @@ class Revoker(object): :class:`letsencrypt.revoker.Cert` """ - list_path2 = tempfile.mktemp(".tmp", "LIST") + newfile_handle, list_path2 = tempfile.mkstemp(".tmp", "LIST") idx = 0 with open(self.list_path, "rb") as orgfile: csvreader = csv.reader(orgfile) - with open(list_path2, "wb") as newfile: + with os.fdopen(newfile_handle, "wb") as newfile: csvwriter = csv.writer(newfile) for row in csvreader: @@ -333,7 +334,8 @@ class Revoker(object): """ list_path = os.path.join(config.cert_key_backup, "LIST") - le_util.make_or_verify_dir(config.cert_key_backup, 0o700, os.geteuid()) + le_util.make_or_verify_dir(config.cert_key_backup, 0o700, os.geteuid(), + config.strict_permissions) cls._catalog_files( config.cert_key_backup, cert_path, key_path, list_path) diff --git a/letsencrypt/tests/le_util_test.py b/letsencrypt/tests/le_util_test.py index 98b7eb803..ed976f72d 100644 --- a/letsencrypt/tests/le_util_test.py +++ b/letsencrypt/tests/le_util_test.py @@ -92,7 +92,7 @@ class MakeOrVerifyDirTest(unittest.TestCase): def _call(self, directory, mode): from letsencrypt.le_util import make_or_verify_dir - return make_or_verify_dir(directory, mode, self.uid) + return make_or_verify_dir(directory, mode, self.uid, strict=True) def test_creates_dir_when_missing(self): path = os.path.join(self.root_path, "bar") diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index 15872423c..e67631605 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -48,13 +48,13 @@ class BaseRenewableCertTest(unittest.TestCase): # TODO: maybe provide RenewerConfiguration.make_dirs? os.makedirs(os.path.join(self.tempdir, "live", "example.org")) os.makedirs(os.path.join(self.tempdir, "archive", "example.org")) - os.makedirs(os.path.join(self.tempdir, "configs")) + os.makedirs(os.path.join(self.tempdir, "renewal")) config = configobj.ConfigObj() for kind in ALL_FOUR: config[kind] = os.path.join(self.tempdir, "live", "example.org", kind + ".pem") - config.filename = os.path.join(self.tempdir, "configs", + config.filename = os.path.join(self.tempdir, "renewal", "example.org.conf") self.config = config diff --git a/tests/boulder-integration.sh b/tests/boulder-integration.sh index 67cc4c5e9..ed877d136 100755 --- a/tests/boulder-integration.sh +++ b/tests/boulder-integration.sh @@ -11,6 +11,8 @@ . ./tests/integration/_common.sh export PATH="/usr/sbin:$PATH" # /usr/sbin/nginx +export GOPATH="${GOPATH:-/tmp/go}" +export PATH="$GOPATH/bin:$PATH" common() { letsencrypt_test \ diff --git a/tests/boulder-start.sh b/tests/boulder-start.sh index e17716b54..7ce7dcba4 100755 --- a/tests/boulder-start.sh +++ b/tests/boulder-start.sh @@ -1,14 +1,36 @@ -#!/bin/sh -xe +#!/bin/bash # Download and run Boulder instance for integration testing + +# ugh, go version output is like: +# go version go1.4.2 linux/amd64 +GOVER=`go version | cut -d" " -f3 | cut -do -f2` + +# version comparison +function verlte { + [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ] +} + +if ! verlte 1.5 "$GOVER" ; then + echo "We require go version 1.5 or later; you have... $GOVER" + exit 1 +fi + +set -xe + export GOPATH="${GOPATH:-/tmp/go}" +export PATH="$GOPATH/bin:$PATH" # `/...` avoids `no buildable Go source files` errors, for more info # see `go help packages` go get -d github.com/letsencrypt/boulder/... cd $GOPATH/src/github.com/letsencrypt/boulder # goose is needed for ./test/create_db.sh -go get bitbucket.org/liamstask/goose/cmd/goose +if ! go get bitbucket.org/liamstask/goose/cmd/goose ; then + echo Problems installing goose... perhaps rm -rf \$GOPATH \("$GOPATH"\) + echo and try again... + exit 1 +fi ./test/create_db.sh ./start.py & # Hopefully start.py bootstraps before integration test is started... diff --git a/tox.cover.sh b/tox.cover.sh index 6f8a5697b..edfd9b81a 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -16,13 +16,13 @@ fi cover () { if [ "$1" = "letsencrypt" ]; then - min=96 + min=97 elif [ "$1" = "acme" ]; then min=100 elif [ "$1" = "letsencrypt_apache" ]; then min=100 elif [ "$1" = "letsencrypt_nginx" ]; then - min=96 + min=97 elif [ "$1" = "letshelp_letsencrypt" ]; then min=100 else