Merge remote-tracking branch 'upstream/master' into multios_apache

This commit is contained in:
Joona Hoikkala
2015-12-21 23:53:08 +02:00
15 changed files with 423 additions and 84 deletions
+6 -1
View File
@@ -64,7 +64,7 @@ or for full help, type:
``letsencrypt-auto`` is the recommended method of running the Let's Encrypt
client beta releases on systems that don't have a packaged version. Debian,
Arch linux and FreeBSD now have native packages, so on those
Arch linux, FreeBSD, and OpenBSD now have native packages, so on those
systems you can just install ``letsencrypt`` (and perhaps
``letsencrypt-apache``). If you'd like to run the latest copy from Git, or
run your own locally modified copy of the client, follow the instructions in
@@ -351,6 +351,11 @@ Operating System Packages
* Port: ``cd /usr/ports/security/py-letsencrypt && make install clean``
* Package: ``pkg install py27-letsencrypt``
**OpenBSD**
* Port: ``cd /usr/ports/security/letsencrypt/client && make install clean``
* Package: ``pkg_add letsencrypt``
**Arch Linux**
.. code-block:: shell
@@ -59,7 +59,7 @@ 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])|\\\\"|\\\\'/
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])/
@@ -1,7 +1,6 @@
"""Apache Configuration based off of Augeas Configurator."""
# pylint: disable=too-many-lines
import filecmp
import itertools
import logging
import os
import re
@@ -26,6 +25,7 @@ from letsencrypt_apache import tls_sni_01
from letsencrypt_apache import obj
from letsencrypt_apache import parser
from collections import defaultdict
logger = logging.getLogger(__name__)
@@ -921,15 +921,33 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"redirection")
self._create_redirect_vhost(ssl_vhost)
else:
# Check if redirection already exists
self._verify_no_redirects(general_vh)
# Check if LetsEncrypt redirection already exists
self._verify_no_letsencrypt_redirect(general_vh)
# Note: if code flow gets here it means we didn't find the exact
# letsencrypt RewriteRule config for redirection. Finding
# another RewriteRule is likely to be fine in most or all cases,
# but redirect loops are possible in very obscure cases; see #1620
# for reasoning.
if self._is_rewrite_exists(general_vh):
logger.warn("Added an HTTP->HTTPS rewrite in addition to "
"other RewriteRules; you may wish to check for "
"overall consistency.")
# Add directives to server
# Note: These are not immediately searchable in sites-enabled
# even with save() and load()
self.parser.add_dir(general_vh.path, "RewriteEngine", "on")
self.parser.add_dir(general_vh.path, "RewriteRule",
if not self._is_rewrite_engine_on(general_vh):
self.parser.add_dir(general_vh.path, "RewriteEngine", "on")
if self.get_version() >= (2, 3, 9):
self.parser.add_dir(general_vh.path, "RewriteRule",
constants.REWRITE_HTTPS_ARGS_WITH_END)
else:
self.parser.add_dir(general_vh.path, "RewriteRule",
constants.REWRITE_HTTPS_ARGS)
self.save_notes += ("Redirecting host in %s to ssl vhost in %s\n" %
(general_vh.filep, ssl_vhost.filep))
self.save()
@@ -937,40 +955,67 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
logger.info("Redirecting vhost in %s to ssl vhost in %s",
general_vh.filep, ssl_vhost.filep)
def _verify_no_redirects(self, vhost):
"""Checks to see if existing redirect is in place.
def _verify_no_letsencrypt_redirect(self, vhost):
"""Checks to see if a redirect was already installed by letsencrypt.
Checks to see if virtualhost already contains a rewrite or redirect
returns boolean, integer
Checks to see if virtualhost already contains a rewrite rule that is
identical to Letsencrypt's redirection rewrite rule.
:param vhost: vhost to check
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
:raises errors.PluginEnhancementAlreadyPresent: When the exact
letsencrypt redirection WriteRule exists in virtual host.
errors.PluginError: When there exists directives that may hint
other redirection. (TODO: We should not throw a PluginError,
but that's for an other PR.)
"""
rewrite_path = self.parser.find_dir(
"RewriteRule", None, start=vhost.path)
redirect_path = self.parser.find_dir("Redirect", None, start=vhost.path)
"RewriteRule", None, start=vhost.path)
if redirect_path:
# "Existing Redirect directive for virtualhost"
raise errors.PluginError("Existing Redirect present on HTTP vhost.")
if rewrite_path:
# "No existing redirection for virtualhost"
if len(rewrite_path) != len(constants.REWRITE_HTTPS_ARGS):
raise errors.PluginError("Unknown Existing RewriteRule")
for match, arg in itertools.izip(
rewrite_path, constants.REWRITE_HTTPS_ARGS):
if self.aug.get(match) != arg:
raise errors.PluginError("Unknown Existing RewriteRule")
# There can be other RewriteRule directive lines in vhost config.
# rewrite_args_dict keys are directive ids and the corresponding value
# for each is a list of arguments to that directive.
rewrite_args_dict = defaultdict(list)
pat = r'.*(directive\[\d+\]).*'
for match in rewrite_path:
m = re.match(pat, match)
if m:
dir_id = m.group(1)
rewrite_args_dict[dir_id].append(match)
raise errors.PluginEnhancementAlreadyPresent(
"Let's Encrypt has already enabled redirection")
if rewrite_args_dict:
redirect_args = [constants.REWRITE_HTTPS_ARGS,
constants.REWRITE_HTTPS_ARGS_WITH_END]
for matches in rewrite_args_dict.values():
if [self.aug.get(x) for x in matches] in redirect_args:
raise errors.PluginEnhancementAlreadyPresent(
"Let's Encrypt has already enabled redirection")
def _is_rewrite_exists(self, vhost):
"""Checks if there exists a RewriteRule directive in vhost
:param vhost: vhost to check
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
:returns: True if a RewriteRule directive exists.
:rtype: bool
"""
rewrite_path = self.parser.find_dir(
"RewriteRule", None, start=vhost.path)
return bool(rewrite_path)
def _is_rewrite_engine_on(self, vhost):
"""Checks if a RewriteEngine directive is on
:param vhost: vhost to check
:type vhost: :class:`~letsencrypt_apache.obj.VirtualHost`
"""
rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on",
start=vhost.path)
if rewrite_engine_path:
return self.parser.get_arg(rewrite_engine_path[0])
return False
def _create_redirect_vhost(self, ssl_vhost):
"""Creates an http_vhost specifically to redirect for the ssl_vhost.
@@ -1007,6 +1052,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
if ssl_vhost.aliases:
serveralias = "ServerAlias " + " ".join(ssl_vhost.aliases)
rewrite_rule_args = []
if self.get_version() >= (2, 3, 9):
rewrite_rule_args = constants.REWRITE_HTTPS_ARGS_WITH_END
else:
rewrite_rule_args = constants.REWRITE_HTTPS_ARGS
return ("<VirtualHost %s>\n"
"%s \n"
"%s \n"
@@ -1020,7 +1072,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"</VirtualHost>\n"
% (" ".join(str(addr) for addr in self._get_proposed_addrs(ssl_vhost)),
servername, serveralias,
" ".join(constants.REWRITE_HTTPS_ARGS)))
" ".join(rewrite_rule_args)))
def _write_out_redirect(self, ssl_vhost, text):
# This is the default name
@@ -64,8 +64,12 @@ AUGEAS_LENS_DIR = pkg_resources.resource_filename(
REWRITE_HTTPS_ARGS = [
"^", "https://%{SERVER_NAME}%{REQUEST_URI}", "[L,QSA,R=permanent]"]
"""Apache rewrite rule arguments used for redirections to https vhost"""
"""Apache version<2.3.9 rewrite rule arguments used for redirections to https vhost"""
REWRITE_HTTPS_ARGS_WITH_END = [
"^", "https://%{SERVER_NAME}%{REQUEST_URI}", "[END,QSA,R=permanent]"]
"""Apache version >= 2.3.9 rewrite rule arguments used for redirections to
https vhost"""
HSTS_ARGS = ["always", "set", "Strict-Transport-Security",
"\"max-age=31536000; includeSubDomains\""]
@@ -676,6 +676,19 @@ class TwoVhost80Test(util.ApacheTest):
def test_supported_enhancements(self):
self.assertTrue(isinstance(self.config.supported_enhancements(), list))
@mock.patch("letsencrypt.le_util.exe_exists")
def test_enhance_unknown_vhost(self, mock_exe):
self.config.parser.modules.add("rewrite_module")
mock_exe.return_value = True
ssl_vh = obj.VirtualHost(
"fp", "ap", set([obj.Addr(("*", "443")), obj.Addr(("satoshi.com",))]),
True, False)
self.config.vhosts.append(ssl_vh)
self.assertRaises(
errors.PluginError,
self.config.enhance, "satoshi.com", "redirect")
def test_enhance_unknown_enhancement(self):
self.assertRaises(
errors.PluginError,
@@ -764,6 +777,8 @@ class TwoVhost80Test(util.ApacheTest):
def test_redirect_well_formed_http(self, mock_exe, _):
self.config.parser.update_runtime_variables = mock.Mock()
mock_exe.return_value = True
self.config.get_version = mock.Mock(return_value=(2, 2))
# This will create an ssl vhost for letsencrypt.demo
self.config.enhance("letsencrypt.demo", "redirect")
@@ -783,6 +798,55 @@ class TwoVhost80Test(util.ApacheTest):
self.assertTrue("rewrite_module" in self.config.parser.modules)
def test_rewrite_rule_exists(self):
# Skip the enable mod
self.config.parser.modules.add("rewrite_module")
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
self.config.parser.add_dir(
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
self.assertTrue(self.config._is_rewrite_exists(self.vh_truth[3])) # pylint: disable=protected-access
def test_rewrite_engine_exists(self):
# Skip the enable mod
self.config.parser.modules.add("rewrite_module")
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
self.config.parser.add_dir(
self.vh_truth[3].path, "RewriteEngine", "on")
self.assertTrue(self.config._is_rewrite_engine_on(self.vh_truth[3])) # pylint: disable=protected-access
@mock.patch("letsencrypt.le_util.run_script")
@mock.patch("letsencrypt.le_util.exe_exists")
def test_redirect_with_existing_rewrite(self, mock_exe, _):
self.config.parser.update_runtime_variables = mock.Mock()
mock_exe.return_value = True
self.config.get_version = mock.Mock(return_value=(2, 2))
# Create a preexisting rewrite rule
self.config.parser.add_dir(
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
self.config.save()
# This will create an ssl vhost for letsencrypt.demo
self.config.enhance("letsencrypt.demo", "redirect")
# These are not immediately available in find_dir even with save() and
# load(). They must be found in sites-available
rw_engine = self.config.parser.find_dir(
"RewriteEngine", "on", self.vh_truth[3].path)
rw_rule = self.config.parser.find_dir(
"RewriteRule", None, self.vh_truth[3].path)
self.assertEqual(len(rw_engine), 1)
# three args to rw_rule + 1 arg for the pre existing rewrite
self.assertEqual(len(rw_rule), 4)
self.assertTrue(rw_engine[0].startswith(self.vh_truth[3].path))
self.assertTrue(rw_rule[0].startswith(self.vh_truth[3].path))
self.assertTrue("rewrite_module" in self.config.parser.modules)
def test_redirect_with_conflict(self):
self.config.parser.modules.add("rewrite_module")
ssl_vh = obj.VirtualHost(
@@ -797,43 +861,16 @@ class TwoVhost80Test(util.ApacheTest):
def test_redirect_twice(self):
# Skip the enable mod
self.config.parser.modules.add("rewrite_module")
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
self.config.enhance("encryption-example.demo", "redirect")
self.assertRaises(
errors.PluginEnhancementAlreadyPresent,
self.config.enhance, "encryption-example.demo", "redirect")
def test_unknown_rewrite(self):
# Skip the enable mod
self.config.parser.modules.add("rewrite_module")
self.config.parser.add_dir(
self.vh_truth[3].path, "RewriteRule", ["Unknown"])
self.config.save()
self.assertRaises(
errors.PluginError,
self.config.enhance, "letsencrypt.demo", "redirect")
def test_unknown_rewrite2(self):
# Skip the enable mod
self.config.parser.modules.add("rewrite_module")
self.config.parser.add_dir(
self.vh_truth[3].path, "RewriteRule", ["Unknown", "2", "3"])
self.config.save()
self.assertRaises(
errors.PluginError,
self.config.enhance, "letsencrypt.demo", "redirect")
def test_unknown_redirect(self):
# Skip the enable mod
self.config.parser.modules.add("rewrite_module")
self.config.parser.add_dir(
self.vh_truth[3].path, "Redirect", ["Unknown"])
self.config.save()
self.assertRaises(
errors.PluginError,
self.config.enhance, "letsencrypt.demo", "redirect")
def test_create_own_redirect(self):
self.config.parser.modules.add("rewrite_module")
self.config.get_version = mock.Mock(return_value=(2, 3, 9))
# For full testing... give names...
self.vh_truth[1].name = "default.com"
self.vh_truth[1].aliases = set(["yes.default.com"])
@@ -841,6 +878,17 @@ class TwoVhost80Test(util.ApacheTest):
self.config._enable_redirect(self.vh_truth[1], "") # pylint: disable=protected-access
self.assertEqual(len(self.config.vhosts), 7)
def test_create_own_redirect_for_old_apache_version(self):
self.config.parser.modules.add("rewrite_module")
self.config.get_version = mock.Mock(return_value=(2, 2))
# For full testing... give names...
self.vh_truth[1].name = "default.com"
self.vh_truth[1].aliases = set(["yes.default.com"])
self.config._enable_redirect(self.vh_truth[1], "") # pylint: disable=protected-access
self.assertEqual(len(self.config.vhosts), 7)
def get_achalls(self):
"""Return testing achallenges."""
account_key = self.rsa512jwk
+1
View File
@@ -97,6 +97,7 @@ DeterminePythonVersion() {
export LE_PYTHON=${LE_PYTHON:-python}
else
echo "Cannot find any Pythons... please install one!"
exit 1
fi
PYVER=`$LE_PYTHON --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
+4 -3
View File
@@ -407,9 +407,10 @@ class Client(object):
logger.warning("No config is specified.")
raise errors.Error("No config available")
redirect = config.redirect
hsts = config.hsts
uir = config.uir # Upgrade Insecure Requests
supported = self.installer.supported_enhancements()
redirect = config.redirect if "redirect" in supported else False
hsts = config.hsts if "ensure-http-header" in supported else False
uir = config.uir if "ensure-http-header" in supported else False
if redirect is None:
redirect = enhancements.ask("redirect")
+3 -1
View File
@@ -181,7 +181,9 @@ def main(cli_args=sys.argv[1:]):
# RenewableCert object for this cert at all, which could
# dramatically improve performance for large deployments
# where autorenewal is widely turned off.
cert = storage.RenewableCert(renewal_file, cli_config)
cert = storage.RenewableCert(
os.path.join(cli_config.renewal_configs_dir, renewal_file),
cli_config)
except errors.CertStorageError:
# This indicates an invalid renewal configuration file, such
# as one missing a required parameter (in the future, perhaps
+1 -1
View File
@@ -260,7 +260,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
:returns: The path to the current version of the specified
member.
:rtype: str
:rtype: str or None
"""
if kind not in ALL_FOUR:
+17
View File
@@ -240,6 +240,7 @@ class ClientTest(unittest.TestCase):
mock_enhancements.ask.return_value = True
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = ["redirect"]
self.client.enhance_config(["foo.bar"], config)
installer.enhance.assert_called_once_with("foo.bar", "redirect", None)
@@ -255,6 +256,7 @@ class ClientTest(unittest.TestCase):
mock_enhancements.ask.return_value = True
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = ["redirect", "ensure-http-header"]
config = ConfigHelper(redirect=True, hsts=False, uir=False)
self.client.enhance_config(["foo.bar"], config)
@@ -273,6 +275,17 @@ class ClientTest(unittest.TestCase):
self.assertEqual(installer.save.call_count, 3)
self.assertEqual(installer.restart.call_count, 3)
@mock.patch("letsencrypt.client.enhancements")
def test_enhance_config_unsupported(self, mock_enhancements):
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = []
config = ConfigHelper(redirect=None, hsts=True, uir=True)
self.client.enhance_config(["foo.bar"], config)
installer.enhance.assert_not_called()
mock_enhancements.ask.assert_not_called()
def test_enhance_config_no_installer(self):
config = ConfigHelper(redirect=True, hsts=False, uir=False)
self.assertRaises(errors.Error,
@@ -285,6 +298,7 @@ class ClientTest(unittest.TestCase):
mock_enhancements.ask.return_value = True
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = ["redirect"]
installer.enhance.side_effect = errors.PluginError
config = ConfigHelper(redirect=True, hsts=False, uir=False)
@@ -301,6 +315,7 @@ class ClientTest(unittest.TestCase):
mock_enhancements.ask.return_value = True
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = ["redirect"]
installer.save.side_effect = errors.PluginError
config = ConfigHelper(redirect=True, hsts=False, uir=False)
@@ -317,6 +332,7 @@ class ClientTest(unittest.TestCase):
mock_enhancements.ask.return_value = True
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = ["redirect"]
installer.restart.side_effect = [errors.PluginError, None]
config = ConfigHelper(redirect=True, hsts=False, uir=False)
@@ -335,6 +351,7 @@ class ClientTest(unittest.TestCase):
mock_enhancements.ask.return_value = True
installer = mock.MagicMock()
self.client.installer = installer
installer.supported_enhancements.return_value = ["redirect"]
installer.restart.side_effect = errors.PluginError
installer.rollback_checkpoints.side_effect = errors.ReverterError
+2
View File
@@ -764,6 +764,8 @@ class RenewableCertTests(BaseRenewableCertTest):
def test_bad_config_file(self):
from letsencrypt import renewer
os.unlink(os.path.join(self.cli_config.renewal_configs_dir,
"example.org.conf"))
with open(os.path.join(self.cli_config.renewal_configs_dir,
"bad.conf"), "w") as f:
f.write("incomplete = configfile\n")
@@ -0,0 +1,21 @@
<VirtualHost *:80>
WSGIDaemonProcess _graphite processes=5 threads=5 display-name='%{GROUP}' inactivity-timeout=120 user=_graphite group=_graphite
WSGIProcessGroup _graphite
WSGIImportScript /usr/share/graphite-web/graphite.wsgi process-group=_graphite application-group=%{GLOBAL}
WSGIScriptAlias / /usr/share/graphite-web/graphite.wsgi
Alias /content/ /usr/share/graphite-web/static/
<Location "/content/">
SetHandler None
</Location>
ErrorLog ${APACHE_LOG_DIR}/graphite-web_error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/graphite-web_access.log combined
</VirtualHost>
+120
View File
@@ -0,0 +1,120 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
// This program can be used to perform RSA public key signatures given only
// the hash of the file to be signed as input.
// Sign with SHA1
#define HASH_SIZE 20
void usage() {
printf("half-sign <private key file> [binary hash file]\n");
printf("\n");
printf(" Computes and prints a binary RSA signature over data given the SHA1 hash of\n");
printf(" the data as input.\n");
printf("\n");
printf(" <private key file> should be PEM encoded.\n");
printf("\n");
printf(" The input SHA1 hash should be %d bytes in length. If no binary hash file is\n", HASH_SIZE);
printf(" specified, it will be read from stdin.\n");
exit(1);
}
void sign_hashed_data(EVP_PKEY *signing_key, unsigned char *md, size_t mdlen) {
// cribbed from the openssl EVP_PKEY_sign man page
EVP_PKEY_CTX *ctx;
unsigned char *sig;
size_t siglen;
/* NB: assumes signing_key, md and mdlen are already set up
* and that signing_key is an RSA private key
*/
ctx = EVP_PKEY_CTX_new(signing_key, NULL);
if ((!ctx)
|| (EVP_PKEY_sign_init(ctx) <= 0)
|| (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
|| (EVP_PKEY_CTX_set_signature_md(ctx, EVP_sha1()) <= 0)) {
fprintf(stderr, "Failure establishing ctx for signature\n");
exit(1);
}
/* Determine buffer length */
if (EVP_PKEY_sign(ctx, NULL, &siglen, md, mdlen) <= 0) {
fprintf(stderr, "Unable to determine buffer length for signature\n");
exit(1);
}
sig = OPENSSL_malloc(siglen);
if (!sig) {
fprintf(stderr, "Malloc failed\n");
exit(1);
}
if (EVP_PKEY_sign(ctx, sig, &siglen, md, mdlen) <= 0) {
fprintf(stderr, "Signature error\n");
exit(1);
}
/* Signature is siglen bytes written to buffer sig */
fwrite(sig, siglen, 1, stdout);
}
EVP_PKEY *read_private_key(char *filename) {
FILE *keyfile;
EVP_PKEY *privkey;
keyfile = fopen(filename, "r");
if (!keyfile) {
fprintf(stderr, "Failed to open private key.pem file %s\n", filename);
exit(1);
}
privkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL);
if (!privkey) {
fprintf(stderr, "Failed to read PEM private key from %s\n", filename);
exit(1);
}
if (EVP_PKEY_type(privkey->type) != EVP_PKEY_RSA) {
fprintf(stderr, "%s was a non-RSA key\n", filename);
exit(1);
}
return privkey;
}
int main(int argc, char *argv[]) {
FILE *input;
unsigned char *buffer;
int test;
EVP_PKEY *privkey;
if (argc > 3 || argc < 2)
usage();
if (argc < 3 || strcmp(argv[2],"-") == 0)
input = stdin;
else {
input = fopen(argv[2], "r");
if (!input) usage();
}
privkey = read_private_key(argv[1]);
buffer = malloc(HASH_SIZE);
if (!buffer) {
fprintf(stderr, "Argh, malloc failed\n");
exit(1);
}
if (fread(buffer, HASH_SIZE, 1, input) != 1) {
perror("half-sign: Failed to read SHA1 from input\n");
exit(1);
}
test = fgetc(input);
if (test != EOF && test != '\n') {
fprintf(stderr,"Error, more than %d bytes fed to half-sign\n", HASH_SIZE);
fprintf(stderr,"Last byte was :%d\n" , (int) test);
exit(1);
}
sign_hashed_data(privkey, buffer, HASH_SIZE);
return 0;
}
+82 -16
View File
@@ -1,13 +1,43 @@
#!/bin/sh -xe
#!/bin/bash -xe
# Release dev packages to PyPI
Usage() {
echo Usage:
echo "$0 [ --production ]"
exit 1
}
if [ "`dirname $0`" != "tools" ] ; then
echo Please run this script from the repo root
exit 1
fi
CheckVersion() {
# Args: <description of version type> <version number>
if ! echo "$2" | grep -q -e '[0-9]\+.[0-9]\+.[0-9]\+' ; then
echo "$1 doesn't look like 1.2.3"
exit 1
fi
}
if [ "$1" = "--production" ] ; then
version="$2"
CheckVersion Version "$version"
echo Releasing production version "$version"...
nextversion="$3"
CheckVersion "Next version" "$nextversion"
RELEASE_BRANCH="candidate-$version"
else
version=`grep "__version__" letsencrypt/__init__.py | cut -d\' -f2 | sed s/\.dev0//`
version="$version.dev$(date +%Y%m%d)1"
RELEASE_BRANCH="dev-release"
echo Releasing developer version "$version"...
fi
RELEASE_GPG_KEY=${RELEASE_GPG_KEY:-A2CFB51FA275A7286234E7B24D17C995CD9775F2}
# Needed to fix problems with git signatures and pinentry
export GPG_TTY=$(tty)
version="0.0.0.dev$(date +%Y%m%d)"
DEV_RELEASE_BRANCH="dev-release"
RELEASE_GPG_KEY=A2CFB51FA275A7286234E7B24D17C995CD9775F2
# port for a local Python Package Index (used in testing)
PORT=${PORT:-1234}
@@ -36,21 +66,29 @@ pip install -U wheel # setup.py bdist_wheel
# from current env when creating a child env
pip install -U virtualenv
root="$(mktemp -d -t le.$version.XXX)"
root_without_le="$version.$$"
root="./releases/le.$root_without_le"
echo "Cloning into fresh copy at $root" # clean repo = no artificats
git clone . $root
git rev-parse HEAD
cd $root
git branch -f "$DEV_RELEASE_BRANCH"
git checkout "$DEV_RELEASE_BRANCH"
if [ "$RELEASE_BRANCH" != "candidate-$version" ] ; then
git branch -f "$RELEASE_BRANCH"
fi
git checkout "$RELEASE_BRANCH"
for pkg_dir in $SUBPKGS
do
sed -i $x "s/^version.*/version = '$version'/" $pkg_dir/setup.py
done
sed -i "s/^__version.*/__version__ = '$version'/" letsencrypt/__init__.py
SetVersion() {
ver="$1"
for pkg_dir in $SUBPKGS
do
sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py
done
sed -i "s/^__version.*/__version__ = '$ver'/" letsencrypt/__init__.py
git add -p # interactive user input
git add -p $SUBPKGS # interactive user input
}
SetVersion "$version"
git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version"
git tag --local-user "$RELEASE_GPG_KEY" \
--sign --message "Release $version" "$tag"
@@ -68,7 +106,7 @@ do
echo "Signing ($pkg_dir)"
for x in dist/*.tar.gz dist/*.whl
do
gpg2 --detach-sign --armor --sign $x
gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign $x
done
cd -
@@ -97,16 +135,44 @@ pip install \
letsencrypt $SUBPKGS
# stop local PyPI
kill $!
cd ~-
# freeze before installing anything else, so that we know end-user KGS
# make sure "twine upload" doesn't catch "kgs"
if [ -d ../kgs ] ; then
echo Deleting old kgs...
rm -rf ../kgs
fi
mkdir ../kgs
kgs="../kgs/$version"
pip freeze | tee $kgs
pip install nose
nosetests letsencrypt $subpkgs_modules
for module in letsencrypt $subpkgs_modules ; do
echo testing $module
nosetests $module
done
deactivate
cd ..
echo Now in $PWD
name=${root_without_le%.*}
ext="${root_without_le##*.}"
rev="$(git rev-parse --short HEAD)"
echo tar cJvf $name.$rev.tar.xz $name.$rev
echo gpg -U $RELEASE_GPG_KEY --detach-sign --armor $name.$rev.tar.xz
cd ~-
echo "New root: $root"
echo "KGS is at $root/kgs"
echo "Test commands (in the letstest repo):"
echo 'python multitester.py targets.yaml $AWS_KEY $USERNAME scripts/test_leauto_upgrades.sh --alt_pip $YOUR_PIP_REPO --branch public-beta'
echo 'python multitester.py targets.yaml $AWK_KEY $USERNAME scripts/test_letsencrypt_auto_certonly_standalone.sh --branch candidate-0.1.1'
echo 'python multitester.py --saveinstances targets.yaml $AWS_KEY $USERNAME scripts/test_apache2.sh'
echo "In order to upload packages run the following command:"
echo twine upload "$root/dist.$version/*/*"
if [ "$RELEASE_BRANCH" = candidate-"$version" ] ; then
SetVersion "$nextversion".dev0
git diff
git commit -m "Bump version to $nextversion"
fi
+1 -1
View File
@@ -16,7 +16,7 @@ fi
cover () {
if [ "$1" = "letsencrypt" ]; then
min=97
min=98
elif [ "$1" = "acme" ]; then
min=100
elif [ "$1" = "letsencrypt_apache" ]; then