Merge branch 'master' into fix-rebootstrap

This commit is contained in:
Brad Warren 2018-01-08 18:24:26 -08:00
commit 7b7cb152aa
8 changed files with 143 additions and 20 deletions

View file

@ -1220,6 +1220,18 @@ def _create_subparsers(helpful):
key=constants.REVOCATION_REASONS.get)),
action=_EncodeReasonAction, default=flag_default("reason"),
help="Specify reason for revoking certificate. (default: unspecified)")
helpful.add("revoke",
"--delete-after-revoke", action="store_true",
default=flag_default("delete_after_revoke"),
help="Delete certificates after revoking them.")
helpful.add("revoke",
"--no-delete-after-revoke", action="store_false",
dest="delete_after_revoke",
default=flag_default("delete_after_revoke"),
help="Do not delete certificates after revoking them. This "
"option should be used with caution because the 'renew' "
"subcommand will attempt to renew undeleted revoked "
"certificates.")
helpful.add("rollback",
"--checkpoints", type=int, metavar="N",
default=flag_default("rollback_checkpoints"),

View file

@ -71,6 +71,7 @@ CLI_DEFAULTS = dict(
user_agent_comment=None,
csr=None,
reason=0,
delete_after_revoke=None,
rollback_checkpoints=1,
init=False,
prepare=False,

View file

@ -536,9 +536,11 @@ def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-b
display = zope.component.getUtility(interfaces.IDisplay)
reporter_util = zope.component.getUtility(interfaces.IReporter)
msg = ("Would you like to delete the cert(s) you just revoked?")
attempt_deletion = display.yesno(msg, yes_label="Yes (recommended)", no_label="No",
force_interactive=True, default=True)
attempt_deletion = config.delete_after_revoke
if attempt_deletion is None:
msg = ("Would you like to delete the cert(s) you just revoked?")
attempt_deletion = display.yesno(msg, yes_label="Yes (recommended)", no_label="No",
force_interactive=True, default=True)
if not attempt_deletion:
reporter_util.add_message("Not deleting revoked certs.", reporter_util.LOW_PRIORITY)

View file

@ -164,6 +164,8 @@ class ParseTest(unittest.TestCase): # pylint: disable=too-many-public-methods
self.assertTrue("--cert-path" in out)
self.assertTrue("--key-path" in out)
self.assertTrue("--reason" in out)
self.assertTrue("--delete-after-revoke" in out)
self.assertTrue("--no-delete-after-revoke" in out)
out = self._help_output(['-h', 'config_changes'])
self.assertTrue("--cert-path" not in out)
@ -412,6 +414,18 @@ class ParseTest(unittest.TestCase): # pylint: disable=too-many-public-methods
def test_no_directory_hooks_unset(self):
self.assertTrue(self.parse([]).directory_hooks)
def test_delete_after_revoke(self):
namespace = self.parse(["--delete-after-revoke"])
self.assertTrue(namespace.delete_after_revoke)
def test_delete_after_revoke_default(self):
namespace = self.parse([])
self.assertEqual(namespace.delete_after_revoke, None)
def test_no_delete_after_revoke(self):
namespace = self.parse(["--no-delete-after-revoke"])
self.assertFalse(namespace.delete_after_revoke)
class DefaultTest(unittest.TestCase):
"""Tests for certbot.cli._Default."""

View file

@ -298,25 +298,29 @@ class RevokeTest(test_util.TempDirTestCase):
self._call()
self.assertFalse(mock_delete.called)
class DeleteIfAppropriateTest(unittest.TestCase):
class DeleteIfAppropriateTest(test_util.ConfigTestCase):
"""Tests for certbot.main._delete_if_appropriate """
def setUp(self):
self.config = mock.Mock()
self.config.namespace = mock.Mock()
self.config.namespace.noninteractive_mode = False
def _call(self, mock_config):
from certbot.main import _delete_if_appropriate
_delete_if_appropriate(mock_config)
@mock.patch('certbot.cert_manager.delete')
def _test_delete_opt_out_common(self, mock_get_utility):
with mock.patch('certbot.cert_manager.delete') as mock_delete:
self._call(self.config)
mock_delete.assert_not_called()
self.assertTrue(mock_get_utility().add_message.called)
@test_util.patch_get_utility()
def test_delete_opt_out(self, mock_get_utility, mock_delete):
def test_delete_flag_opt_out(self, mock_get_utility):
self.config.delete_after_revoke = False
self._test_delete_opt_out_common(mock_get_utility)
@test_util.patch_get_utility()
def test_delete_prompt_opt_out(self, mock_get_utility):
util_mock = mock_get_utility()
util_mock.yesno.return_value = False
self._call(self.config)
mock_delete.assert_not_called()
self._test_delete_opt_out_common(mock_get_utility)
# pylint: disable=too-many-arguments
@mock.patch('certbot.storage.renewal_file_for_certname')
@ -397,6 +401,28 @@ class DeleteIfAppropriateTest(unittest.TestCase):
self._call(config)
self.assertEqual(mock_delete.call_count, 1)
# pylint: disable=too-many-arguments
@mock.patch('certbot.storage.renewal_file_for_certname')
@mock.patch('certbot.cert_manager.match_and_check_overlaps')
@mock.patch('certbot.storage.full_archive_path')
@mock.patch('certbot.cert_manager.cert_path_to_lineage')
@mock.patch('certbot.cert_manager.delete')
@test_util.patch_get_utility()
def test_opt_in_deletion(self, mock_get_utility, mock_delete,
mock_cert_path_to_lineage, mock_full_archive_dir,
mock_match_and_check_overlaps, mock_renewal_file_for_certname):
# pylint: disable = unused-argument
config = self.config
config.namespace.delete_after_revoke = True
config.cert_path = "/some/reasonable/path"
config.certname = ""
mock_cert_path_to_lineage.return_value = "example.com"
mock_full_archive_dir.return_value = ""
mock_match_and_check_overlaps.return_value = ""
self._call(config)
self.assertEqual(mock_delete.call_count, 1)
self.assertFalse(mock_get_utility().yesno.called)
# pylint: disable=too-many-arguments
@mock.patch('certbot.storage.renewal_file_for_certname')
@mock.patch('certbot.cert_manager.match_and_check_overlaps')

View file

@ -345,9 +345,14 @@ common auth --must-staple --domains "must-staple.le.wtf"
openssl x509 -in "${root}/conf/live/must-staple.le.wtf/cert.pem" -text | grep '1.3.6.1.5.5.7.1.24'
# revoke by account key
common revoke --cert-path "$root/conf/live/le.wtf/cert.pem"
common revoke --cert-path "$root/conf/live/le.wtf/cert.pem" --delete-after-revoke
# revoke renewed
common revoke --cert-path "$root/conf/live/le1.wtf/cert.pem"
common revoke --cert-path "$root/conf/live/le1.wtf/cert.pem" --no-delete-after-revoke
if [ ! -d "$root/conf/live/le1.wtf" ]; then
echo "cert deleted when --no-delete-after-revoke was used!"
exit 1
fi
common delete --cert-name le1.wtf
# revoke by cert key
common revoke --cert-path "$root/conf/live/le2.wtf/cert.pem" \
--key-path "$root/conf/live/le2.wtf/privkey.pem"

View file

@ -15,19 +15,56 @@ if ! command -v git ; then
exit 1
fi
fi
BRANCH=`git rev-parse --abbrev-ref HEAD`
# 0.5.0 is the oldest version of letsencrypt-auto that can be used because it's
# the first version that pins package versions, properly supports
# --no-self-upgrade, and works with newer versions of pip.
git checkout -f v0.5.0
git checkout -f v0.5.0 letsencrypt-auto
if ! ./letsencrypt-auto -v --debug --version --no-self-upgrade 2>&1 | grep 0.5.0 ; then
echo initial installation appeared to fail
exit 1
fi
git checkout -f "$BRANCH"
EXPECTED_VERSION=$(grep -m1 LE_AUTO_VERSION letsencrypt-auto | cut -d\" -f2)
if ! ./letsencrypt-auto -v --debug --version --no-self-upgrade 2>&1 | grep $EXPECTED_VERSION ; then
# Now that python and openssl have been installed, we can set up a fake server
# to provide a new version of letsencrypt-auto. First, we start the server and
# directory to be served.
MY_TEMP_DIR=$(mktemp -d)
PORT_FILE="$MY_TEMP_DIR/port"
SERVER_PATH=$(tools/readlink.py tools/simple_http_server.py)
cd "$MY_TEMP_DIR"
"$SERVER_PATH" 0 > $PORT_FILE &
SERVER_PID=$!
trap 'kill "$SERVER_PID" && rm -rf "$MY_TEMP_DIR"' EXIT
cd ~-
# Then, we set up the files to be served.
FAKE_VERSION_NUM="99.99.99"
echo "{\"releases\": {\"$FAKE_VERSION_NUM\": null}}" > "$MY_TEMP_DIR/json"
LE_AUTO_SOURCE_DIR="$MY_TEMP_DIR/v$FAKE_VERSION_NUM"
NEW_LE_AUTO_PATH="$LE_AUTO_SOURCE_DIR/letsencrypt-auto"
mkdir "$LE_AUTO_SOURCE_DIR"
cp letsencrypt-auto-source/letsencrypt-auto "$LE_AUTO_SOURCE_DIR/letsencrypt-auto"
SIGNING_KEY="letsencrypt-auto-source/tests/signing.key"
openssl dgst -sha256 -sign "$SIGNING_KEY" -out "$NEW_LE_AUTO_PATH.sig" "$NEW_LE_AUTO_PATH"
# Next, we wait for the server to start and get the port number.
sleep 5s
SERVER_PORT=$(sed -n 's/.*port \([0-9]\+\).*/\1/p' "$PORT_FILE")
# Finally, we set the necessary certbot-auto environment variables.
export LE_AUTO_DIR_TEMPLATE="http://localhost:$SERVER_PORT/%s/"
export LE_AUTO_JSON_URL="http://localhost:$SERVER_PORT/json"
export LE_AUTO_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsMoSzLYQ7E1sdSOkwelg
tzKIh2qi3bpXuYtcfFC0XrvWig071NwIj+dZiT0OLZ2hPispEH0B7ISuuWg1ll7G
hFW0VdbxL6JdGzS2ShNWkX9hE9z+j8VqwDPOBn3ZHm03qwpYkBDwQib3KqOdYbTT
uUtJmmGcuk3a9Aq/sCT6DdfmTSdP5asdQYwIcaQreDrOosaS84DTWI3IU+UYJVgl
LsIVPBuy9IcgHidUQ96hJnoPsDCWsHwX62495QKEarauyKQrJzFes0EY95orDM47
Z5o/NDiQB11m91yNB0MmPYY9QSbnOA9j7IaaC97AwRLuwXY+/R2ablTcxurWou68
iQIDAQAB
-----END PUBLIC KEY-----
"
if ! ./letsencrypt-auto -v --debug --version || ! diff letsencrypt-auto letsencrypt-auto-source/letsencrypt-auto ; then
echo upgrade appeared to fail
exit 1
fi

26
tools/simple_http_server.py Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env python
"""A version of Python 2.x's SimpleHTTPServer that flushes its output."""
from BaseHTTPServer import HTTPServer
from SimpleHTTPServer import SimpleHTTPRequestHandler
import sys
def serve_forever(port=0):
"""Spins up an HTTP server on all interfaces and the given port.
A message is printed to stdout specifying the address and port being used
by the server.
:param int port: port number to use.
"""
server = HTTPServer(('', port), SimpleHTTPRequestHandler)
print 'Serving HTTP on {0} port {1} ...'.format(*server.server_address)
sys.stdout.flush()
server.serve_forever()
if __name__ == '__main__':
kwargs = {}
if len(sys.argv) > 1:
kwargs['port'] = int(sys.argv[1])
serve_forever(**kwargs)