diff --git a/.travis.yml b/.travis.yml index 96e28b1b0..8dde06ceb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,8 +60,7 @@ addons: - rsyslog install: "travis_retry pip install tox coveralls" -before_script: '[ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/boulder-start.sh' -script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || (source .tox/$TOXENV/bin/activate && ./tests/boulder-integration.sh))' +script: 'travis_retry tox && ([ "xxx$BOULDER_INTEGRATION" = "xxx" ] || ./tests/travis-integration.sh)' after_success: '[ "$TOXENV" == "cover" ] && coveralls' diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 976d7ab12..336e6c4e5 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -228,6 +228,9 @@ class HTTP01Response(KeyAuthorizationChallengeResponse): """ + WHITESPACE_CUTSET = "\n\r\t " + """Whitespace characters which should be ignored at the end of the body.""" + def simple_verify(self, chall, domain, account_public_key, port=None): """Simple verify. @@ -273,10 +276,11 @@ class HTTP01Response(KeyAuthorizationChallengeResponse): found_ct, chall.CONTENT_TYPE) return False - if self.key_authorization != http_response.text: + challenge_response = http_response.text.rstrip(self.WHITESPACE_CUTSET) + if self.key_authorization != challenge_response: logger.debug("Key authorization from response (%r) doesn't match " "HTTP response (%r)", self.key_authorization, - http_response.text) + challenge_response) return False return True diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index c4f3d6c61..7cf387ece 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -126,6 +126,16 @@ class HTTP01ResponseTest(unittest.TestCase): self.assertFalse(self.response.simple_verify( self.chall, "local", KEY.public_key())) + @mock.patch("acme.challenges.requests.get") + def test_simple_verify_whitespace_validation(self, mock_get): + from acme.challenges import HTTP01Response + mock_get.return_value = mock.MagicMock( + text=(self.chall.validation(KEY) + + HTTP01Response.WHITESPACE_CUTSET), headers=self.good_headers) + self.assertTrue(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + mock_get.assert_called_once_with(self.chall.uri("local")) + @mock.patch("acme.challenges.requests.get") def test_simple_verify_bad_content_type(self, mock_get): mock_get().text = self.chall.token diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index 9f670da6e..5aca13cd4 100755 --- a/bootstrap/_rpm_common.sh +++ b/bootstrap/_rpm_common.sh @@ -1,8 +1,8 @@ #!/bin/sh # Tested with: -# - Fedora 22 (x64) -# - Centos 7 (x64: on AWS EC2 t2.micro, DigitalOcean droplet) +# - Fedora 22, 23 (x64) +# - Centos 7 (x64: onD igitalOcean droplet) if type yum 2>/dev/null then @@ -15,17 +15,33 @@ else exit 1 fi +# Some distros and older versions of current distros use a "python27" +# instead of "python" naming convention. Try both conventions. +if ! $tool install -y \ + python \ + python-devel \ + python-virtualenv +then + if ! $tool install -y \ + python27 \ + python27-devel \ + python27-virtualenv + then + echo "Could not install Python dependencies. Aborting bootstrap!" + exit 1 + fi +fi + # "git-core" seems to be an alias for "git" in CentOS 7 (yum search fails) -# Amazon Linux 2015.03 needs python27-virtualenv rather than python-virtualenv -$tool install -y \ - git-core \ - python \ - python-devel \ - python27-virtualenv \ - python-virtualenv \ - gcc \ - dialog \ - augeas-libs \ - openssl-devel \ - libffi-devel \ - ca-certificates \ +if ! $tool install -y \ + git-core \ + gcc \ + dialog \ + augeas-libs \ + openssl-devel \ + libffi-devel \ + ca-certificates +then + echo "Could not install additional dependencies. Aborting bootstrap!" + exit 1 +fi diff --git a/bootstrap/_suse_common.sh b/bootstrap/_suse_common.sh index 4b41bac36..46f9d693b 100755 --- a/bootstrap/_suse_common.sh +++ b/bootstrap/_suse_common.sh @@ -1,6 +1,6 @@ #!/bin/sh -# SLE12 dont have python-virtualenv +# SLE12 don't have python-virtualenv zypper -nq in -l git-core \ python \ diff --git a/docs/ciphers.rst b/docs/ciphers.rst index 12c403d09..49c0824a3 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -105,7 +105,7 @@ https://wiki.mozilla.org/Security/Server_Side_TLS and the version implemented by the Let's Encrypt client will be the version that was most current as of the release date of each client -version. Mozilla offers three seperate sets of cryptographic options, +version. Mozilla offers three separate sets of cryptographic options, which trade off security and compatibility differently. These are referred to as as the "Modern", "Intermediate", and "Old" configurations (in order from most secure to least secure, and least-backwards compatible diff --git a/docs/using.rst b/docs/using.rst index 45db2bbad..3f04fc5fa 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -42,14 +42,24 @@ To install and run the client you just need to type: ./letsencrypt-auto +.. note:: On RedHat/CentOS 6 you will need to enable the EPEL_ + repository before install. + +.. _EPEL: http://fedoraproject.org/wiki/EPEL + Throughout the documentation, whenever you see references to ``letsencrypt`` script/binary, you can substitute in -``letsencrypt-auto``. For example, to get the help you would type: +``letsencrypt-auto``. For example, to get basic help you would type: .. code-block:: shell ./letsencrypt-auto --help +or for full help, type: + +.. code-block:: shell + + ./letsencrypt-auto --help all Running with Docker ------------------- @@ -94,6 +104,13 @@ Operating System Packages * Port: ``cd /usr/ports/security/py-letsencrypt && make install clean`` * Package: ``pkg install py27-letsencrypt`` +**Arch Linux** + +.. code-block:: shell + + sudo pacman -S letsencrypt letsencrypt-nginx letsencrypt-apache \ + letshelp-letsencrypt + **Other Operating Systems** Unfortunately, this is an ongoing effort. If you'd like to package @@ -128,32 +145,79 @@ SSL certificates! Plugins ======= -Officially supported plugins: +=========== = = =============================================================== +Plugin A I Notes +=========== = = =============================================================== +apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on + Debian-based distributions with ``libaugeas0`` 1.0+. +standalone_ Y N Uses a "standalone" webserver to obtain a cert. +webroot_ Y N Obtains a cert using an already running webserver. +manual_ Y N Helps you obtain a cert by giving you instructions to perform + domain validation yourself. +nginx_ Y Y Very experimental and not included in letsencrypt-auto_. +=========== = = =============================================================== -========== = = ================================================================ -Plugin A I Notes and status -========== = = ================================================================ -standalone Y N Very stable. Uses port 80 (force by - ``--standalone-supported-challenges http-01``) or 443 - (force by ``--standalone-supported-challenges tls-sni-01``). -apache Y Y Alpha. Automates Apache installation, works fairly well but on - Debian-based distributions only for now. -webroot Y N Works with already running webserver, by writing necessary files - to the disk (``--webroot-path`` should be pointed to your - ``public_html``). Currently, when multiple domains are specified - (`-d`), they must all use the same web root path. -manual Y N Hidden from standard UI, use with ``-a manual``. Requires to - copy and paste commands into a new terminal session. Allows to - run client on machine different than target webserver, e.g. your - laptop. -nginx Y Y Very experimental. Not included in letsencrypt-auto_. -========== = = ================================================================ +Apache +------ -Third party plugins are listed at -https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If -that's not enough, you can always :ref:`write your own plugin -`. +If you're running Apache 2.4 on a Debian-based OS with version 1.0+ of +the ``libaugeas0`` package available, you can use the Apache plugin. +This automates both obtaining and installing certs on an Apache +webserver. To specify this plugin on the command line, simply include +``--apache``. +Standalone +---------- + +To obtain a cert using a "standalone" webserver, you can use the +standalone plugin by including ``certonly`` and ``--standalone`` +on the command line. This plugin needs to bind to port 80 or 443 in +order to perform domain validation, so you may need to stop your +existing webserver. To control which port the plugin uses, include +one of the options shown below on the command line. + + * ``--standalone-supported-challenges http-01`` to use port 80 + * ``--standalone-supported-challenges tls-sni-01`` to use port 443 + +Webroot +------- + +If you're running a webserver that you don't want to stop to use +standalone, you can use the webroot plugin to obtain a cert by +including ``certonly`` and ``-a webroot`` on the command line. In +addition, you'll need to specify ``--webroot-path`` with the root +directory of the files served by your webserver. For example, +``--webroot-path /var/www/html`` or +``--webroot-path /usr/share/nginx/html`` are two common webroot paths. +If multiple domains are specified, they must all use the same path. +Additionally, your server must be configured to serve files from +hidden directories. + +Manual +------ + +If you'd like to obtain a cert running ``letsencrypt`` on a machine +other than your target webserver or perform the steps for domain +validation yourself, you can use the manual plugin. While hidden from +the UI, you can use the plugin to obtain a cert by specifying +``certonly`` and ``-a manual`` on the command line. This requires you +to copy and paste commands into another terminal session. + +Nginx +----- + +In the future, if you're running Nginx you can use this plugin to +automatically obtain and install your certificate. The Nginx plugin +is still experimental, however, and is not installed with +letsencrypt-auto_. If installed, you can select this plugin on the +command line by including ``--nginx``. + +Third party plugins +------------------- + +These plugins are listed at +https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If you're +interested, you can also :ref:`write your own plugin `. Renewal ======= diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index f6286dccc..189af25e0 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -188,12 +188,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # cert_key... can all be parsed appropriately self.prepare_server_https("443") - path = {} - - path["cert_path"] = self.parser.find_dir( - "SSLCertificateFile", None, vhost.path) - path["cert_key"] = self.parser.find_dir( - "SSLCertificateKeyFile", None, vhost.path) + path = {"cert_path": self.parser.find_dir("SSLCertificateFile", None, vhost.path), + "cert_key": self.parser.find_dir("SSLCertificateKeyFile", None, vhost.path)} # Only include if a certificate chain is specified if chain_path is not None: diff --git a/letsencrypt-auto b/letsencrypt-auto index b3e380f9d..a3009fe52 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -85,6 +85,8 @@ ExperimentalBootstrap() { DeterminePythonVersion() { if command -v python2.7 > /dev/null ; then export LE_PYTHON=${LE_PYTHON:-python2.7} + elif command -v python27 > /dev/null ; then + export LE_PYTHON=${LE_PYTHON:-python27} elif command -v python2 > /dev/null ; then export LE_PYTHON=${LE_PYTHON:-python2} elif command -v python > /dev/null ; then @@ -135,7 +137,7 @@ then elif uname | grep -iq Darwin ; then ExperimentalBootstrap "Mac OS X" mac.sh elif grep -iq "Amazon Linux" /etc/issue ; then - ExperimentalBootstrap "Amazon Linux" amazon_linux.sh + ExperimentalBootstrap "Amazon Linux" _rpm_common.sh else echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py index 43070cf03..b635ee539 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py +++ b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py @@ -34,6 +34,8 @@ def create_le_config(parent_dir): os.mkdir(config["work_dir"]) os.mkdir(config["logs_dir"]) + config["domains"] = None + return argparse.Namespace(**config) # pylint: disable=star-args diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/letsencrypt-nginx/letsencrypt_nginx/configurator.py index d97cf7397..29445a9d4 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py +++ b/letsencrypt-nginx/letsencrypt_nginx/configurator.py @@ -93,7 +93,7 @@ class NginxConfigurator(common.Plugin): # These will be set in the prepare function self.parser = None self.version = version - self._enhance_func = {} # TODO: Support at least redirects + self._enhance_func = {"redirect": self._enable_redirect} # Set up reverter self.reverter = reverter.Reverter(self.config) @@ -344,7 +344,7 @@ class NginxConfigurator(common.Plugin): ################################## def supported_enhancements(self): # pylint: disable=no-self-use """Returns currently supported enhancements.""" - return [] + return ['redirect'] def enhance(self, domain, enhancement, options=None): """Enhance configuration. @@ -366,6 +366,26 @@ class NginxConfigurator(common.Plugin): except errors.PluginError: logger.warn("Failed %s for %s", enhancement, domain) + def _enable_redirect(self, vhost, unused_options): + """Redirect all equivalent HTTP traffic to ssl_vhost. + + Add rewrite directive to non https traffic + + .. note:: This function saves the configuration + + :param vhost: Destination of traffic, an ssl enabled vhost + :type vhost: :class:`~letsencrypt_nginx.obj.VirtualHost` + + :param unused_options: Not currently used + :type unused_options: Not Available + """ + redirect_block = [[['if', '($scheme != "https")'], + [['return', '301 https://$host$request_uri']] + ]] + self.parser.add_server_directives(vhost.filep, vhost.names, + redirect_block) + logger.info("Redirecting all traffic to ssl in %s", vhost.filep) + ###################################### # Nginx server management (IInstaller) ###################################### diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/letsencrypt-nginx/letsencrypt_nginx/parser.py index fb79703dc..e5c6be0e6 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/parser.py +++ b/letsencrypt-nginx/letsencrypt_nginx/parser.py @@ -257,6 +257,8 @@ class NginxParser(object): ..note :: If replace is True, this raises a misconfiguration error if the directive does not already exist. + ..note :: If replace is False nothing gets added if an identical + block exists already. ..todo :: Doesn't match server blocks whose server_name directives are split across multiple conf files. @@ -411,7 +413,7 @@ def _regex_match(target_name, name): return True else: return False - except re.error: + except re.error: # pragma: no cover # perl-compatible regexes are sometimes not recognized by python return False @@ -448,10 +450,9 @@ def _parse_server(server): :rtype: dict """ - parsed_server = {} - parsed_server['addrs'] = set() - parsed_server['ssl'] = False - parsed_server['names'] = set() + parsed_server = {'addrs': set(), + 'ssl': False, + 'names': set()} for directive in server: if directive[0] == 'listen': @@ -480,7 +481,9 @@ def _add_directives(block, directives, replace=False): if not replace: # We insert new directives at the top of the block, mostly # to work around https://trac.nginx.org/nginx/ticket/810 - block.insert(0, directive) + # Only add directive if its not already in the block + if directive not in block: + block.insert(0, directive) else: changed = False if len(directive) == 0: diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py index 913c5de27..ff720ea85 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py @@ -51,11 +51,11 @@ class NginxConfiguratorTest(util.NginxTest): "example.*", "www.example.org", "myhost"])) def test_supported_enhancements(self): - self.assertEqual([], self.config.supported_enhancements()) + self.assertEqual(['redirect'], self.config.supported_enhancements()) def test_enhance(self): self.assertRaises( - errors.PluginError, self.config.enhance, 'myhost', 'redirect') + errors.PluginError, self.config.enhance, 'myhost', 'unknown_enhancement') def test_get_chall_pref(self): self.assertEqual([challenges.TLSSNI01], diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py index b28640d7f..2d6156429 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py +++ b/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py @@ -133,11 +133,11 @@ class NginxParserTest(util.NginxTest): self.assertEqual(1, len(re.findall(ssl_re, dump))) server_conf = nparser.abs_path('server.conf') - nparser.add_server_directives(server_conf, - set(['alias', 'another.alias', - 'somename']), + names = set(['alias', 'another.alias', 'somename']) + nparser.add_server_directives(server_conf, names, [['foo', 'bar'], ['ssl_certificate', '/etc/ssl/cert2.pem']]) + nparser.add_server_directives(server_conf, names, [['foo', 'bar']]) self.assertEqual(nparser.parsed[server_conf], [['ssl_certificate', '/etc/ssl/cert2.pem'], ['foo', 'bar'], diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index ce419b393..34551c97f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -69,6 +69,7 @@ USAGE = SHORT_USAGE + """Choice of server plugins for obtaining and installing c %s --standalone Run a standalone webserver for authentication %s + --webroot Place files in a server's webroot folder for authentication OR use different servers to obtain (authenticate) the cert and then install it: @@ -80,7 +81,7 @@ More detailed help: the available topics are: all, automation, paths, security, testing, or any of the subcommands or - plugins (certonly, install, nginx, apache, standalone, etc) + plugins (certonly, install, nginx, apache, standalone, webroot, etc) """ @@ -139,10 +140,8 @@ def _determine_account(args, config): elif len(accounts) == 1: acc = accounts[0] else: # no account registered yet - if args.email is None: + if args.email is None and not args.register_unsafely_without_email: args.email = display_ops.get_email() - if not args.email: # get_email might return "" - args.email = None def _tos_cb(regr): if args.tos: @@ -380,7 +379,7 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): raise errors.PluginSelectionError(msg) -def choose_configurator_plugins(args, config, plugins, verb): +def choose_configurator_plugins(args, config, plugins, verb): # pylint: disable=too-many-branches """ Figure out which configurator we're going to use @@ -408,6 +407,10 @@ def choose_configurator_plugins(args, config, plugins, verb): req_auth = set_configurator(req_auth, "apache") if args.standalone: req_auth = set_configurator(req_auth, "standalone") + if args.webroot: + req_auth = set_configurator(req_auth, "webroot") + if args.manual: + req_auth = set_configurator(req_auth, "manual") logger.debug("Requested authenticator %s and installer %s", req_auth, req_inst) # Try to meet the user's request and/or ask them to pick plugins @@ -842,6 +845,16 @@ def prepare_and_parse_args(plugins, args): helpful.add( None, "-t", "--text", dest="text_mode", action="store_true", help="Use the text output instead of the curses UI.") + helpful.add( + None, "--register-unsafely-without-email", action="store_true", + help="Specifying this flag enables registering an account with no " + "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 " + "certificates. Updates to the Subscriber Agreement will still " + "affect you, and will be effective N days after posting an " + "update to the web site.") helpful.add(None, "-m", "--email", help=config_help("email")) # positional arg shadows --domains, instead of appending, and # --domains is useful, because it can be stored in config @@ -850,7 +863,7 @@ def prepare_and_parse_args(plugins, args): helpful.add(None, "-d", "--domains", dest="domains", metavar="DOMAIN", action="append", help="Domain names to apply. For multiple domains you can use " - "multiple -d flags or enter a comma separated list of domains" + "multiple -d flags or enter a comma separated list of domains " "as a parameter.") helpful.add( None, "--duplicate", dest="duplicate", action="store_true", @@ -893,8 +906,9 @@ def prepare_and_parse_args(plugins, args): "testing", "--tls-sni-01-port", type=int, default=flag_default("tls_sni_01_port"), help=config_help("tls_sni_01_port")) - helpful.add("testing", "--http-01-port", dest="http01_port", type=int, - help=config_help("http01_port")) + helpful.add( + "testing", "--http-01-port", type=int, dest="http01_port", + default=flag_default("http01_port"), help=config_help("http01_port")) helpful.add_group( "security", description="Security parameters & server settings") @@ -1045,6 +1059,10 @@ def _plugins_parsing(helpful, plugins): help="Obtain and install certs using Nginx") helpful.add("plugins", "--standalone", action="store_true", help='Obtain certs using a "standalone" webserver.') + helpful.add("plugins", "--manual", action="store_true", + help='Provide laborious manual instructions for obtaining a cert') + helpful.add("plugins", "--webroot", action="store_true", + help='Obtain certs by placing files in a webroot directory.') # things should not be reorder past/pre this comment: # plugins_group should be displayed in --help before plugin diff --git a/letsencrypt/client.py b/letsencrypt/client.py index be6fc7a22..e229d3c8d 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -100,6 +100,11 @@ def register(config, account_storage, tos_cb=None): if account_storage.find_all(): logger.info("There are already existing accounts for %s", config.server) if config.email is None: + if not config.register_unsafely_without_email: + msg = ("No email was provided and " + "--register-unsafely-without-email was not present.") + logger.warn(msg) + raise errors.Error(msg) logger.warn("Registering without email!") # Each new registration shall use a fresh new key @@ -110,7 +115,7 @@ def register(config, account_storage, tos_cb=None): backend=default_backend()))) acme = acme_from_config_key(config, key) # TODO: add phone? - regr = acme.register(messages.NewRegistration.from_data(email=config.email)) + regr = perform_registration(acme, config) if regr.terms_of_service is not None: if tos_cb is not None and not tos_cb(regr): @@ -126,6 +131,30 @@ def register(config, account_storage, tos_cb=None): return acc, acme +def perform_registration(acme, config): + """ + Actually register new account, trying repeatedly if there are email + problems + + :param .IConfig config: Client configuration. + :param acme.client.Client client: ACME client object. + + :returns: Registration Resource. + :rtype: `acme.messages.RegistrationResource` + + :raises .UnexpectedUpdate: + """ + try: + return acme.register(messages.NewRegistration.from_data(email=config.email)) + except messages.Error, e: + err = repr(e) + if "MX record" in err or "Validation of contact mailto" in err: + config.namespace.email = display_ops.get_email(more=True, invalid=True) + return perform_registration(acme, config) + else: + raise + + class Client(object): """ACME protocol client. @@ -455,7 +484,7 @@ class Client(object): except: # TODO: suggest letshelp-letsencypt here reporter.add_message( - "An error occured and we failed to restore your config and " + "An error occurred and we failed to restore your config and " "restart your server. Please submit a bug report to " "https://github.com/letsencrypt/letsencrypt", reporter.HIGH_PRIORITY) diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index 4955655f3..a2a54d2d0 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -5,8 +5,6 @@ import re import zope.interface -from acme import challenges - from letsencrypt import constants from letsencrypt import errors from letsencrypt import interfaces @@ -80,13 +78,6 @@ class NamespaceConfig(object): return os.path.join( self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR) - @property - def http01_port(self): # pylint: disable=missing-docstring - if self.namespace.http01_port is not None: - return self.namespace.http01_port - else: - return challenges.HTTP01Response.PORT - class RenewerConfiguration(object): """Configuration wrapper for renewer.""" @@ -155,8 +146,8 @@ def _check_config_domain_sanity(domains): "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 < 7 chars + # 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}(? $GOPATH/bin/goose && \ + chmod +x $GOPATH/bin/goose +./test/create_db.sh +# listenbuddy is needed for ./start.py +go get github.com/jsha/listenbuddy +cd - + diff --git a/tests/boulder-start.sh b/tests/boulder-start.sh index 47c1b6278..acf8f0bbf 100755 --- a/tests/boulder-start.sh +++ b/tests/boulder-start.sh @@ -1,43 +1,9 @@ #!/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 { - #OS X doesn't support version sorting; emulate with sed - if [ `uname` == 'Darwin' ]; then - [ "$1" = "`echo -e \"$1\n$2\" | sed 's/\b\([0-9]\)\b/0\1/g' \ - | sort | sed 's/\b0\([0-9]\)/\1/g' | head -n1`" ] - else - [ "$1" = "`echo -e "$1\n$2" | sort -V | head -n1`" ] - fi -} - -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/... +./tests/boulder-fetch.sh + cd $GOPATH/src/github.com/letsencrypt/boulder -# goose is needed for ./test/create_db.sh -wget https://github.com/jsha/boulder-tools/raw/master/goose.gz && \ - mkdir $GOPATH/bin && \ - zcat goose.gz > $GOPATH/bin/goose && \ - chmod +x $GOPATH/bin/goose -./test/create_db.sh -# listenbuddy is needed for ./start.py -go get github.com/jsha/listenbuddy -./start.py & -# Hopefully start.py bootstraps before integration test is started... +./start.py diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index dbc473728..71d745d93 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -23,7 +23,7 @@ letsencrypt_test () { --no-redirect \ --agree-dev-preview \ --agree-tos \ - --email "" \ + --register-unsafely-without-email \ --renew-by-default \ --debug \ -vvvvvvv \ diff --git a/tests/travis-integration.sh b/tests/travis-integration.sh new file mode 100755 index 000000000..3b507bb86 --- /dev/null +++ b/tests/travis-integration.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +set -o errexit + +./tests/boulder-fetch.sh + +source .tox/$TOXENV/bin/activate + +export LETSENCRYPT_PATH=`pwd` + +cd $GOPATH/src/github.com/letsencrypt/boulder/ + +# boulder's integration-test.py has code that knows to start and wait for the +# boulder processes to start reliably and then will run the letsencrypt +# boulder-interation.sh on its own. The --letsencrypt flag says to run only the +# letsencrypt tests (instead of any other client tests it might run). We're +# going to want to define a more robust interaction point between the boulder +# and letsencrypt tests, but that will be better built off of this. +python test/integration-test.py --letsencrypt diff --git a/tools/dev-release.sh b/tools/dev-release.sh index 6bbc6ced4..bd86bff44 100755 --- a/tools/dev-release.sh +++ b/tools/dev-release.sh @@ -23,7 +23,7 @@ mv "dist.$version" "dist.$version.$(date +%s).bak" || true git tag --delete "$tag" || true tmpvenv=$(mktemp -d) -virtualenv --no-site-packages $tmpvenv +virtualenv --no-site-packages -p python2 $tmpvenv . $tmpvenv/bin/activate # update setuptools/pip just like in other places in the repo pip install -U setuptools @@ -49,7 +49,7 @@ done sed -i "s/^__version.*/__version__ = '$version'/" letsencrypt/__init__.py git add -p # interactive user input -git -c commit.gpgsign=true commit -m "Release $version" +git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version" git tag --local-user "$RELEASE_GPG_KEY" \ --sign --message "Release $version" "$tag" diff --git a/tox.ini b/tox.ini index 9988d8d1f..d1fafe20f 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ # acme and letsencrypt are not yet on pypi, so when Tox invokes # "install *.zip", it will not find deps skipsdist = true -envlist = py26,py27,py33,py34,cover,lint +envlist = py26,py27,py33,py34,py35,cover,lint # nosetest -v => more verbose output, allows to detect busy waiting # loops, especially on Travis @@ -41,6 +41,11 @@ commands = pip install -e acme[testing] nosetests -v acme +[testenv:py35] +commands = + pip install -e acme[testing] + nosetests -v acme + [testenv:cover] basepython = python2.7 commands =