Compare commits
33 Commits
travis-tes
...
test-http0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4401d8086 | ||
|
|
cfe5854aa3 | ||
|
|
b1df26a4dc | ||
|
|
d7c03e136b | ||
|
|
265afaf7a0 | ||
|
|
cd2188a681 | ||
|
|
ffe8870b9b | ||
|
|
0cc1e3d4bf | ||
|
|
77493d6e1a | ||
|
|
3100dca880 | ||
|
|
b0fa063ef3 | ||
|
|
874a4c6773 | ||
|
|
9741f78afe | ||
|
|
5210588a29 | ||
|
|
7fa4a7a5b9 | ||
|
|
9ada7711f6 | ||
|
|
c36bca765d | ||
|
|
dc60852355 | ||
|
|
b11c685339 | ||
|
|
88d4a7da55 | ||
|
|
34217cf36e | ||
|
|
91d18d1234 | ||
|
|
f3395d487c | ||
|
|
787b0c358f | ||
|
|
6575ed955c | ||
|
|
b574bdf145 | ||
|
|
4c1bcd1bcd | ||
|
|
5d517a1f0d | ||
|
|
1afec8a8fd | ||
|
|
c9759bd1b6 | ||
|
|
557815bf94 | ||
|
|
9209d1eeca | ||
|
|
9c02b83773 |
49
.travis.yml
49
.travis.yml
@@ -16,46 +16,6 @@ matrix:
|
||||
env: TOXENV=py27_install BOULDER_INTEGRATION=1
|
||||
sudo: required
|
||||
services: docker
|
||||
- python: "2.7"
|
||||
env: TOXENV=cover FYI="this also tests py27"
|
||||
- sudo: required
|
||||
env: TOXENV=nginx_compat
|
||||
services: docker
|
||||
before_install:
|
||||
addons:
|
||||
- python: "2.7"
|
||||
env: TOXENV=lint
|
||||
- python: "2.6"
|
||||
env: TOXENV=py26
|
||||
sudo: required
|
||||
services: docker
|
||||
- python: "2.7"
|
||||
env: TOXENV=py27-oldest
|
||||
sudo: required
|
||||
services: docker
|
||||
- python: "3.3"
|
||||
env: TOXENV=py33
|
||||
sudo: required
|
||||
services: docker
|
||||
- python: "3.6"
|
||||
env: TOXENV=py36
|
||||
sudo: required
|
||||
services: docker
|
||||
- sudo: required
|
||||
env: TOXENV=apache_compat
|
||||
services: docker
|
||||
before_install:
|
||||
addons:
|
||||
- sudo: required
|
||||
env: TOXENV=le_auto_trusty
|
||||
services: docker
|
||||
before_install:
|
||||
addons:
|
||||
- python: "2.7"
|
||||
env: TOXENV=apacheconftest
|
||||
sudo: required
|
||||
- python: "2.7"
|
||||
env: TOXENV=nginxroundtrip
|
||||
|
||||
|
||||
# Only build pushes to the master branch, PRs, and branches beginning with
|
||||
@@ -97,12 +57,3 @@ script:
|
||||
- '[ -z "${BOULDER_INTEGRATION+x}" ] || (travis_retry tests/boulder-fetch.sh && tests/tox-boulder-integration.sh)'
|
||||
|
||||
after_success: '[ "$TOXENV" == "cover" ] && coveralls'
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
irc:
|
||||
channels:
|
||||
- secure: "SGWZl3ownKx9xKVV2VnGt7DqkTmutJ89oJV9tjKhSs84kLijU6EYdPnllqISpfHMTxXflNZuxtGo0wTDYHXBuZL47w1O32W6nzuXdra5zC+i4sYQwYULUsyfOv9gJX8zWAULiK0Z3r0oho45U+FR5ZN6TPCidi8/eGU+EEPwaAw="
|
||||
on_success: never
|
||||
on_failure: always
|
||||
use_notice: true
|
||||
|
||||
@@ -26,6 +26,7 @@ from certbot_nginx import constants
|
||||
from certbot_nginx import nginxparser
|
||||
from certbot_nginx import parser
|
||||
from certbot_nginx import tls_sni_01
|
||||
from certbot_nginx import http_01
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -208,7 +209,8 @@ class NginxConfigurator(common.Installer):
|
||||
|
||||
:param str target_name: domain name
|
||||
:param bool create_if_no_match: If we should create a new vhost from default
|
||||
when there is no match found
|
||||
when there is no match found. If we can't choose a default, raise a
|
||||
MisconfigurationError.
|
||||
|
||||
:returns: ssl vhost associated with name
|
||||
:rtype: :class:`~certbot_nginx.obj.VirtualHost`
|
||||
@@ -366,7 +368,7 @@ class NginxConfigurator(common.Installer):
|
||||
return sorted(matches, key=lambda x: x['rank'])
|
||||
|
||||
|
||||
def choose_redirect_vhost(self, target_name, port):
|
||||
def choose_redirect_vhost(self, target_name, port, create_if_no_match=False):
|
||||
"""Chooses a single virtual host for redirect enhancement.
|
||||
|
||||
Chooses the vhost most closely matching target_name that is
|
||||
@@ -380,12 +382,19 @@ class NginxConfigurator(common.Installer):
|
||||
|
||||
:param str target_name: domain name
|
||||
:param str port: port number
|
||||
:param bool create_if_no_match: If we should create a new vhost from default
|
||||
when there is no match found. If we can't choose a default, raise a
|
||||
MisconfigurationError.
|
||||
|
||||
:returns: vhost associated with name
|
||||
:rtype: :class:`~certbot_nginx.obj.VirtualHost`
|
||||
|
||||
"""
|
||||
matches = self._get_redirect_ranked_matches(target_name, port)
|
||||
return self._select_best_name_match(matches)
|
||||
vhost = self._select_best_name_match(matches)
|
||||
if not vhost and create_if_no_match:
|
||||
vhost = self._vhost_from_duplicated_default(target_name)
|
||||
return vhost
|
||||
|
||||
def _get_redirect_ranked_matches(self, target_name, port):
|
||||
"""Gets a ranked list of plaintextish port-listening vhosts matching target_name
|
||||
@@ -394,7 +403,7 @@ class NginxConfigurator(common.Installer):
|
||||
Rank by how well these match target_name.
|
||||
|
||||
:param str target_name: The name to match
|
||||
:param str port: port number
|
||||
:param str port: port number as a string
|
||||
:returns: list of dicts containing the vhost, the matching name, and
|
||||
the numerical rank
|
||||
:rtype: list
|
||||
@@ -840,7 +849,7 @@ class NginxConfigurator(common.Installer):
|
||||
###########################################################################
|
||||
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
|
||||
"""Return list of challenge preferences."""
|
||||
return [challenges.TLSSNI01]
|
||||
return [challenges.TLSSNI01, challenges.HTTP01]
|
||||
|
||||
# Entry point in main.py for performing challenges
|
||||
def perform(self, achalls):
|
||||
@@ -853,15 +862,20 @@ class NginxConfigurator(common.Installer):
|
||||
"""
|
||||
self._chall_out += len(achalls)
|
||||
responses = [None] * len(achalls)
|
||||
chall_doer = tls_sni_01.NginxTlsSni01(self)
|
||||
sni_doer = tls_sni_01.NginxTlsSni01(self)
|
||||
http_doer = http_01.NginxHttp01(self)
|
||||
|
||||
for i, achall in enumerate(achalls):
|
||||
# Currently also have chall_doer hold associated index of the
|
||||
# challenge. This helps to put all of the responses back together
|
||||
# when they are all complete.
|
||||
chall_doer.add_chall(achall, i)
|
||||
if isinstance(achall.chall, challenges.HTTP01):
|
||||
http_doer.add_chall(achall, i)
|
||||
else: # tls-sni-01
|
||||
sni_doer.add_chall(achall, i)
|
||||
|
||||
sni_response = chall_doer.perform()
|
||||
sni_response = sni_doer.perform()
|
||||
http_response = http_doer.perform()
|
||||
# Must restart in order to activate the challenges.
|
||||
# Handled here because we may be able to load up other challenge types
|
||||
self.restart()
|
||||
@@ -869,8 +883,9 @@ class NginxConfigurator(common.Installer):
|
||||
# Go through all of the challenges and assign them to the proper place
|
||||
# in the responses return value. All responses must be in the same order
|
||||
# as the original challenges.
|
||||
for i, resp in enumerate(sni_response):
|
||||
responses[chall_doer.indices[i]] = resp
|
||||
for chall_response, chall_doer in ((sni_response, sni_doer), (http_response, http_doer)):
|
||||
for i, resp in enumerate(chall_response):
|
||||
responses[chall_doer.indices[i]] = resp
|
||||
|
||||
return responses
|
||||
|
||||
|
||||
106
certbot-nginx/certbot_nginx/http_01.py
Normal file
106
certbot-nginx/certbot_nginx/http_01.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""A class that performs HTTP-01 challenges for Nginx"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from acme import challenges
|
||||
|
||||
from certbot.plugins import common
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NginxHttp01(common.ChallengePerformer):
|
||||
"""HTTP-01 authenticator for Nginx
|
||||
|
||||
:ivar configurator: NginxConfigurator object
|
||||
:type configurator: :class:`~nginx.configurator.NginxConfigurator`
|
||||
|
||||
:ivar list achalls: Annotated
|
||||
class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
|
||||
challenges
|
||||
|
||||
:param list indices: Meant to hold indices of challenges in a
|
||||
larger array. NginxHttp01 is capable of solving many challenges
|
||||
at once which causes an indexing issue within NginxConfigurator
|
||||
who must return all responses in order. Imagine NginxConfigurator
|
||||
maintaining state about where all of the http-01 Challenges,
|
||||
TLS-SNI-01 Challenges belong in the response array. This is an
|
||||
optional utility.
|
||||
|
||||
"""
|
||||
|
||||
def perform(self):
|
||||
"""Perform a challenge on Nginx.
|
||||
|
||||
:returns: list of :class:`certbot.acme.challenges.HTTP01Response`
|
||||
:rtype: list
|
||||
|
||||
"""
|
||||
if not self.achalls:
|
||||
return []
|
||||
|
||||
responses = [x.response(x.account_key) for x in self.achalls]
|
||||
|
||||
# Set up the configuration
|
||||
self._mod_config()
|
||||
|
||||
# Save reversible changes
|
||||
self.configurator.save("HTTP Challenge", True)
|
||||
|
||||
return responses
|
||||
|
||||
def _add_bucket_directive(self):
|
||||
"""Modifies Nginx config to include server_names_hash_bucket_size directive."""
|
||||
root = self.configurator.parser.config_root
|
||||
|
||||
bucket_directive = ['\n', 'server_names_hash_bucket_size', ' ', '128']
|
||||
|
||||
main = self.configurator.parser.parsed[root]
|
||||
for line in main:
|
||||
if line[0] == ['http']:
|
||||
body = line[1]
|
||||
found_bucket = False
|
||||
posn = 0
|
||||
for inner_line in body:
|
||||
if inner_line[0] == bucket_directive[1]:
|
||||
if int(inner_line[1]) < int(bucket_directive[3]):
|
||||
body[posn] = bucket_directive
|
||||
found_bucket = True
|
||||
posn += 1
|
||||
if not found_bucket:
|
||||
body.insert(0, bucket_directive)
|
||||
break
|
||||
|
||||
def _mod_config(self):
|
||||
"""Modifies Nginx config to handle challenges.
|
||||
|
||||
"""
|
||||
self._add_bucket_directive()
|
||||
|
||||
for achall in self.achalls:
|
||||
self._mod_server_block(achall)
|
||||
|
||||
def _get_validation_path(self, achall):
|
||||
return os.sep + os.path.join(challenges.HTTP01.URI_ROOT_PATH, achall.chall.encode("token"))
|
||||
|
||||
def _mod_server_block(self, achall):
|
||||
"""Modifies a server block to respond to a challenge.
|
||||
|
||||
:param achall: Annotated HTTP-01 challenge
|
||||
:type achall:
|
||||
:class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge`
|
||||
|
||||
"""
|
||||
vhost = self.configurator.choose_redirect_vhost(achall.domain,
|
||||
'%i' % self.configurator.config.http01_port, create_if_no_match=True)
|
||||
validation = achall.validation(achall.account_key)
|
||||
validation_path = self._get_validation_path(achall)
|
||||
|
||||
location_directive = [[['location', ' ', '=', ' ', validation_path],
|
||||
[['default_type', ' ', 'text/plain'],
|
||||
['return', ' ', '200', ' ', validation]]]]
|
||||
|
||||
self.configurator.parser.add_server_directives(vhost,
|
||||
location_directive, replace=False)
|
||||
@@ -524,7 +524,7 @@ def _is_ssl_on_directive(entry):
|
||||
def _add_directives(directives, replace, block):
|
||||
"""Adds or replaces directives in a config block.
|
||||
|
||||
When replace=False, it's an error to try and add a directive that already
|
||||
When replace=False, it's an error to try and add a nonrepeatable directive that already
|
||||
exists in the config block with a conflicting value.
|
||||
|
||||
When replace=True and a directive with the same name already exists in the
|
||||
@@ -545,7 +545,7 @@ def _add_directives(directives, replace, block):
|
||||
|
||||
|
||||
INCLUDE = 'include'
|
||||
REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE])
|
||||
REPEATABLE_DIRECTIVES = set(['server_name', 'listen', INCLUDE, 'location'])
|
||||
COMMENT = ' managed by Certbot'
|
||||
COMMENT_BLOCK = [' ', '#', COMMENT]
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
||||
errors.PluginError, self.config.enhance, 'myhost', 'unknown_enhancement')
|
||||
|
||||
def test_get_chall_pref(self):
|
||||
self.assertEqual([challenges.TLSSNI01],
|
||||
self.assertEqual([challenges.TLSSNI01, challenges.HTTP01],
|
||||
self.config.get_chall_pref('myhost'))
|
||||
|
||||
def test_save(self):
|
||||
@@ -291,9 +291,11 @@ class NginxConfiguratorTest(util.NginxTest):
|
||||
parsed_migration_conf[0])
|
||||
|
||||
@mock.patch("certbot_nginx.configurator.tls_sni_01.NginxTlsSni01.perform")
|
||||
@mock.patch("certbot_nginx.configurator.http_01.NginxHttp01.perform")
|
||||
@mock.patch("certbot_nginx.configurator.NginxConfigurator.restart")
|
||||
@mock.patch("certbot_nginx.configurator.NginxConfigurator.revert_challenge_config")
|
||||
def test_perform_and_cleanup(self, mock_revert, mock_restart, mock_perform):
|
||||
def test_perform_and_cleanup(self, mock_revert, mock_restart, mock_http_perform,
|
||||
mock_tls_perform):
|
||||
# Only tests functionality specific to configurator.perform
|
||||
# Note: As more challenges are offered this will have to be expanded
|
||||
achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
@@ -304,7 +306,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
||||
), domain="localhost", account_key=self.rsa512jwk)
|
||||
achall2 = achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=messages.ChallengeBody(
|
||||
chall=challenges.TLSSNI01(token=b"m8TdO1qik4JVFtgPPurJmg"),
|
||||
chall=challenges.HTTP01(token=b"m8TdO1qik4JVFtgPPurJmg"),
|
||||
uri="https://ca.org/chall1_uri",
|
||||
status=messages.Status("pending"),
|
||||
), domain="example.com", account_key=self.rsa512jwk)
|
||||
@@ -314,10 +316,12 @@ class NginxConfiguratorTest(util.NginxTest):
|
||||
achall2.response(self.rsa512jwk),
|
||||
]
|
||||
|
||||
mock_perform.return_value = expected
|
||||
mock_tls_perform.return_value = expected[:1]
|
||||
mock_http_perform.return_value = expected[1:]
|
||||
responses = self.config.perform([achall1, achall2])
|
||||
|
||||
self.assertEqual(mock_perform.call_count, 1)
|
||||
self.assertEqual(mock_tls_perform.call_count, 1)
|
||||
self.assertEqual(mock_http_perform.call_count, 1)
|
||||
self.assertEqual(responses, expected)
|
||||
|
||||
self.config.cleanup([achall1, achall2])
|
||||
|
||||
113
certbot-nginx/certbot_nginx/tests/http_01_test.py
Normal file
113
certbot-nginx/certbot_nginx/tests/http_01_test.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""Tests for certbot_nginx.http_01"""
|
||||
import unittest
|
||||
import shutil
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from acme import challenges
|
||||
|
||||
from certbot import achallenges
|
||||
|
||||
from certbot.plugins import common_test
|
||||
from certbot.tests import acme_util
|
||||
|
||||
from certbot_nginx.tests import util
|
||||
|
||||
|
||||
class HttpPerformTest(util.NginxTest):
|
||||
"""Test the NginxHttp01 challenge."""
|
||||
|
||||
account_key = common_test.AUTH_KEY
|
||||
achalls = [
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.HTTP01(token=b"kNdwjwOeX0I_A8DXt9Msmg"), "pending"),
|
||||
domain="www.example.com", account_key=account_key),
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.HTTP01(
|
||||
token=b"\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y"
|
||||
b"\x80\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945"
|
||||
), "pending"),
|
||||
domain="ipv6.com", account_key=account_key),
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.HTTP01(
|
||||
token=b"\x8c\x8a\xbf_-f\\cw\xee\xd6\xf8/\xa5\xe3\xfd"
|
||||
b"\xeb9\xf1\xf5\xb9\xefVM\xc9w\xa4u\x9c\xe1\x87\xb4"
|
||||
), "pending"),
|
||||
domain="www.example.org", account_key=account_key),
|
||||
achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.chall_to_challb(
|
||||
challenges.HTTP01(token=b"kNdwjxOeX0I_A8DXt9Msmg"), "pending"),
|
||||
domain="migration.com", account_key=account_key),
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(HttpPerformTest, self).setUp()
|
||||
|
||||
config = util.get_nginx_configurator(
|
||||
self.config_path, self.config_dir, self.work_dir, self.logs_dir)
|
||||
|
||||
from certbot_nginx import http_01
|
||||
self.http01 = http_01.NginxHttp01(config)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.temp_dir)
|
||||
shutil.rmtree(self.config_dir)
|
||||
shutil.rmtree(self.work_dir)
|
||||
|
||||
def test_perform0(self):
|
||||
responses = self.http01.perform()
|
||||
self.assertEqual([], responses)
|
||||
|
||||
@mock.patch("certbot_nginx.configurator.NginxConfigurator.save")
|
||||
def test_perform1(self, mock_save):
|
||||
self.http01.add_chall(self.achalls[0])
|
||||
response = self.achalls[0].response(self.account_key)
|
||||
|
||||
responses = self.http01.perform()
|
||||
|
||||
self.assertEqual([response], responses)
|
||||
self.assertEqual(mock_save.call_count, 1)
|
||||
|
||||
def test_perform2(self):
|
||||
acme_responses = []
|
||||
for achall in self.achalls:
|
||||
self.http01.add_chall(achall)
|
||||
acme_responses.append(achall.response(self.account_key))
|
||||
|
||||
sni_responses = self.http01.perform()
|
||||
|
||||
self.assertEqual(len(sni_responses), 4)
|
||||
for i in six.moves.range(4):
|
||||
self.assertEqual(sni_responses[i], acme_responses[i])
|
||||
|
||||
def test_mod_config(self):
|
||||
self.http01.add_chall(self.achalls[0])
|
||||
self.http01.add_chall(self.achalls[2])
|
||||
|
||||
self.http01._mod_config() # pylint: disable=protected-access
|
||||
|
||||
self.http01.configurator.save()
|
||||
|
||||
self.http01.configurator.parser.load()
|
||||
|
||||
# vhosts = self.http01.configurator.parser.get_vhosts()
|
||||
|
||||
# for vhost in vhosts:
|
||||
# pass
|
||||
# if the name matches
|
||||
# check that the location block is in there and is correct
|
||||
|
||||
# if vhost.addrs == set(v_addr1):
|
||||
# response = self.achalls[0].response(self.account_key)
|
||||
# else:
|
||||
# response = self.achalls[2].response(self.account_key)
|
||||
# self.assertEqual(vhost.addrs, set(v_addr2_print))
|
||||
# self.assertEqual(vhost.names, set([response.z_domain.decode('ascii')]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
@@ -64,6 +64,7 @@ def get_nginx_configurator(
|
||||
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
|
||||
server="https://acme-server.org:443/new",
|
||||
tls_sni_01_port=5001,
|
||||
http01_port=80
|
||||
),
|
||||
name="nginx",
|
||||
version=version)
|
||||
|
||||
@@ -49,10 +49,10 @@ http {
|
||||
|
||||
server {
|
||||
# IPv4.
|
||||
listen 8081;
|
||||
listen 5002;
|
||||
# IPv6.
|
||||
listen [::]:8081 default ipv6only=on;
|
||||
server_name nginx.wtf;
|
||||
listen [::]:5002 default ipv6only=on;
|
||||
server_name nginx.wtf nginx2.wtf;
|
||||
|
||||
root $root/webroot;
|
||||
|
||||
|
||||
@@ -22,13 +22,20 @@ certbot_test_nginx () {
|
||||
"$@"
|
||||
}
|
||||
|
||||
certbot_test_nginx --domains nginx.wtf run
|
||||
echo | openssl s_client -connect localhost:5001 \
|
||||
| openssl x509 -out $root/nginx.pem
|
||||
diff -q $root/nginx.pem $root/conf/live/nginx.wtf/cert.pem
|
||||
test_deployment_and_rollback() {
|
||||
# Arguments: certname
|
||||
echo | openssl s_client -connect localhost:5001 \
|
||||
| openssl x509 -out $root/nginx.pem
|
||||
diff -q $root/nginx.pem "$root/conf/live/$1/cert.pem"
|
||||
|
||||
certbot_test_nginx rollback --checkpoints 9001
|
||||
diff -q <(echo "$original") $nginx_conf
|
||||
certbot_test_nginx rollback --checkpoints 9001
|
||||
diff -q <(echo "$original") $nginx_conf
|
||||
}
|
||||
|
||||
certbot_test_nginx --domains nginx.wtf run
|
||||
test_deployment_and_rollback nginx.wtf
|
||||
certbot_test_nginx --domains nginx2.wtf --preferred-challenges http
|
||||
test_deployment_and_rollback nginx2.wtf
|
||||
|
||||
# note: not reached if anything above fails, hence "killall" at the
|
||||
# top
|
||||
|
||||
@@ -166,269 +166,6 @@ CheckRenewHook() {
|
||||
CheckSavedRenewHook $1
|
||||
}
|
||||
|
||||
# Cleanup coverage data
|
||||
coverage erase
|
||||
|
||||
# test for regressions of #4719
|
||||
get_num_tmp_files() {
|
||||
ls -1 /tmp | wc -l
|
||||
}
|
||||
num_tmp_files=$(get_num_tmp_files)
|
||||
common --csr / && echo expected error && exit 1 || true
|
||||
common --help
|
||||
common --help all
|
||||
common --version
|
||||
if [ $(get_num_tmp_files) -ne $num_tmp_files ]; then
|
||||
echo "New files or directories created in /tmp!"
|
||||
exit 1
|
||||
fi
|
||||
CreateDirHooks
|
||||
|
||||
common register
|
||||
for dir in $renewal_hooks_dirs; do
|
||||
if [ ! -d "$dir" ]; then
|
||||
echo "Hook directory not created by Certbot!" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
common register --update-registration --email example@example.org
|
||||
|
||||
common plugins --init --prepare | grep webroot
|
||||
|
||||
# We start a server listening on the port for the
|
||||
# unrequested challenge to prevent regressions in #3601.
|
||||
python ./tests/run_http_server.py $http_01_port &
|
||||
python_server_pid=$!
|
||||
|
||||
certname="le1.wtf"
|
||||
common --domains le1.wtf --preferred-challenges tls-sni-01 auth \
|
||||
--cert-name $certname \
|
||||
--pre-hook 'echo wtf.pre >> "$HOOK_TEST"' \
|
||||
--post-hook 'echo wtf.post >> "$HOOK_TEST"'\
|
||||
--deploy-hook 'echo deploy >> "$HOOK_TEST"'
|
||||
kill $python_server_pid
|
||||
CheckDeployHook $certname
|
||||
|
||||
python ./tests/run_http_server.py $tls_sni_01_port &
|
||||
python_server_pid=$!
|
||||
certname="le2.wtf"
|
||||
common --domains le2.wtf --preferred-challenges http-01 run \
|
||||
--cert-name $certname \
|
||||
--pre-hook 'echo wtf.pre >> "$HOOK_TEST"' \
|
||||
--post-hook 'echo wtf.post >> "$HOOK_TEST"'\
|
||||
--deploy-hook 'echo deploy >> "$HOOK_TEST"'
|
||||
kill $python_server_pid
|
||||
CheckDeployHook $certname
|
||||
|
||||
certname="le.wtf"
|
||||
common certonly -a manual -d le.wtf --rsa-key-size 4096 --cert-name $certname \
|
||||
--manual-auth-hook ./tests/manual-http-auth.sh \
|
||||
--manual-cleanup-hook ./tests/manual-http-cleanup.sh \
|
||||
--pre-hook 'echo wtf2.pre >> "$HOOK_TEST"' \
|
||||
--post-hook 'echo wtf2.post >> "$HOOK_TEST"' \
|
||||
--renew-hook 'echo deploy >> "$HOOK_TEST"'
|
||||
CheckRenewHook $certname
|
||||
|
||||
certname="dns.le.wtf"
|
||||
common -a manual -d dns.le.wtf --preferred-challenges dns,tls-sni run \
|
||||
--cert-name $certname \
|
||||
--manual-auth-hook ./tests/manual-dns-auth.sh \
|
||||
--pre-hook 'echo wtf2.pre >> "$HOOK_TEST"' \
|
||||
--post-hook 'echo wtf2.post >> "$HOOK_TEST"' \
|
||||
--renew-hook 'echo deploy >> "$HOOK_TEST"'
|
||||
CheckRenewHook $certname
|
||||
|
||||
common certonly --cert-name newname -d newname.le.wtf
|
||||
|
||||
export CSR_PATH="${root}/csr.der" KEY_PATH="${root}/key.pem" \
|
||||
OPENSSL_CNF=examples/openssl.cnf
|
||||
./examples/generate-csr.sh le3.wtf
|
||||
common auth --csr "$CSR_PATH" \
|
||||
--cert-path "${root}/csr/cert.pem" \
|
||||
--chain-path "${root}/csr/chain.pem"
|
||||
openssl x509 -in "${root}/csr/cert.pem" -text
|
||||
openssl x509 -in "${root}/csr/chain.pem" -text
|
||||
|
||||
common --domains le3.wtf install \
|
||||
--cert-path "${root}/csr/cert.pem" \
|
||||
--key-path "${root}/csr/key.pem"
|
||||
|
||||
CheckCertCount() {
|
||||
CERTCOUNT=`ls "${root}/conf/archive/$1/cert"* | wc -l`
|
||||
if [ "$CERTCOUNT" -ne "$2" ] ; then
|
||||
echo Wrong cert count, not "$2" `ls "${root}/conf/archive/$1/"*`
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
CheckCertCount "le.wtf" 1
|
||||
# This won't renew (because it's not time yet)
|
||||
common_no_force_renew renew
|
||||
CheckCertCount "le.wtf" 1
|
||||
if [ -s "$HOOK_DIRS_TEST" ]; then
|
||||
echo "Directory hooks were executed for non-renewal!" >&2;
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -rf "$renewal_hooks_root"
|
||||
# renew using HTTP manual auth hooks
|
||||
common renew --cert-name le.wtf --authenticator manual
|
||||
CheckCertCount "le.wtf" 2
|
||||
|
||||
# test renewal with no executables in hook directories
|
||||
for hook_dir in $renewal_hooks_dirs; do
|
||||
touch "$hook_dir/file"
|
||||
mkdir "$hook_dir/dir"
|
||||
done
|
||||
# renew using DNS manual auth hooks
|
||||
common renew --cert-name dns.le.wtf --authenticator manual
|
||||
CheckCertCount "dns.le.wtf" 2
|
||||
|
||||
# test with disabled directory hooks
|
||||
rm -rf "$renewal_hooks_root"
|
||||
CreateDirHooks
|
||||
# This will renew because the expiry is less than 10 years from now
|
||||
sed -i "4arenew_before_expiry = 4 years" "$root/conf/renewal/le.wtf.conf"
|
||||
common_no_force_renew renew --rsa-key-size 2048 --no-directory-hooks
|
||||
CheckCertCount "le.wtf" 3
|
||||
if [ -s "$HOOK_DIRS_TEST" ]; then
|
||||
echo "Directory hooks were executed with --no-directory-hooks!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# The 4096 bit setting should persist to the first renewal, but be overridden in the second
|
||||
|
||||
size1=`wc -c ${root}/conf/archive/le.wtf/privkey1.pem | cut -d" " -f1`
|
||||
size2=`wc -c ${root}/conf/archive/le.wtf/privkey2.pem | cut -d" " -f1`
|
||||
size3=`wc -c ${root}/conf/archive/le.wtf/privkey3.pem | cut -d" " -f1`
|
||||
# 4096 bit PEM keys are about ~3270 bytes, 2048 ones are about 1700 bytes
|
||||
if [ "$size1" -lt 3000 ] || [ "$size2" -lt 3000 ] || [ "$size3" -gt 1800 ] ; then
|
||||
echo key sizes violate assumptions:
|
||||
ls -l "${root}/conf/archive/le.wtf/privkey"*
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --renew-by-default is used, so renewal should occur
|
||||
[ -f "$HOOK_TEST" ] && rm -f "$HOOK_TEST"
|
||||
common renew
|
||||
CheckCertCount "le.wtf" 4
|
||||
CheckHooks
|
||||
CheckDirHooks 5
|
||||
|
||||
# test with overlapping directory hooks on the command line
|
||||
common renew --cert-name le2.wtf \
|
||||
--pre-hook "$renewal_dir_pre_hook" \
|
||||
--deploy-hook "$renewal_dir_deploy_hook" \
|
||||
--post-hook "$renewal_dir_post_hook"
|
||||
CheckDirHooks 1
|
||||
|
||||
# test with overlapping directory hooks in the renewal conf files
|
||||
common renew --cert-name le2.wtf
|
||||
CheckDirHooks 1
|
||||
|
||||
# ECDSA
|
||||
openssl ecparam -genkey -name secp384r1 -out "${root}/privkey-p384.pem"
|
||||
SAN="DNS:ecdsa.le.wtf" openssl req -new -sha256 \
|
||||
-config "${OPENSSL_CNF:-openssl.cnf}" \
|
||||
-key "${root}/privkey-p384.pem" \
|
||||
-subj "/" \
|
||||
-reqexts san \
|
||||
-outform der \
|
||||
-out "${root}/csr-p384.der"
|
||||
common auth --csr "${root}/csr-p384.der" \
|
||||
--cert-path "${root}/csr/cert-p384.pem" \
|
||||
--chain-path "${root}/csr/chain-p384.pem"
|
||||
openssl x509 -in "${root}/csr/cert-p384.pem" -text | grep 'ASN1 OID: secp384r1'
|
||||
|
||||
# OCSP Must Staple
|
||||
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" --delete-after-revoke
|
||||
# revoke renewed
|
||||
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"
|
||||
|
||||
# Get new certs to test revoke with a reason, by account and by cert key
|
||||
common --domains le1.wtf
|
||||
common revoke --cert-path "$root/conf/live/le1.wtf/cert.pem" \
|
||||
--reason cessationOfOperation
|
||||
common --domains le2.wtf
|
||||
common revoke --cert-path "$root/conf/live/le2.wtf/cert.pem" \
|
||||
--key-path "$root/conf/live/le2.wtf/privkey.pem" \
|
||||
--reason keyCompromise
|
||||
|
||||
common unregister
|
||||
|
||||
out=$(common certificates)
|
||||
subdomains="le dns.le newname.le must-staple.le"
|
||||
for subdomain in $subdomains; do
|
||||
domain="$subdomain.wtf"
|
||||
if ! echo $out | grep "$domain"; then
|
||||
echo "$domain not in certificates output!"
|
||||
exit 1;
|
||||
fi
|
||||
done
|
||||
|
||||
# Testing that revocation also deletes by default
|
||||
subdomains="le1 le2"
|
||||
for subdomain in $subdomains; do
|
||||
domain="$subdomain.wtf"
|
||||
if echo $out | grep "$domain"; then
|
||||
echo "Revoked $domain in certificates output! Should not be!"
|
||||
exit 1;
|
||||
fi
|
||||
done
|
||||
|
||||
# Test that revocation raises correct error if --cert-name and --cert-path don't match
|
||||
common --domains le1.wtf
|
||||
common --domains le2.wtf
|
||||
out=$(common revoke --cert-path "$root/conf/live/le1.wtf/fullchain.pem" --cert-name "le2.wtf" 2>&1) || true
|
||||
if ! echo $out | grep "or both must point to the same certificate lineages."; then
|
||||
echo "Non-interactive revoking with mismatched --cert-name and --cert-path "
|
||||
echo "did not raise the correct error!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Revoking by matching --cert-name and --cert-path deletes
|
||||
common --domains le1.wtf
|
||||
common revoke --cert-path "$root/conf/live/le1.wtf/fullchain.pem" --cert-name "le1.wtf"
|
||||
out=$(common certificates)
|
||||
if echo $out | grep "le1.wtf"; then
|
||||
echo "Cert le1.wtf should've been deleted! Was revoked via matching --cert-path & --cert-name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test that revocation doesn't delete if multiple lineages share an archive dir
|
||||
common --domains le1.wtf
|
||||
common --domains le2.wtf
|
||||
sed -i "s|^archive_dir = .*$|archive_dir = $root/conf/archive/le1.wtf|" "$root/conf/renewal/le2.wtf.conf"
|
||||
#common update_symlinks # not needed, but a bit more context for what this test is about
|
||||
out=$(common revoke --cert-path "$root/conf/live/le1.wtf/cert.pem")
|
||||
if ! echo $out | grep "Not deleting revoked certs due to overlapping archive dirs"; then
|
||||
echo "Deleted a cert that had an overlapping archive dir with another lineage!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cert_name="must-staple.le.wtf"
|
||||
common delete --cert-name $cert_name
|
||||
archive="$root/conf/archive/$cert_name"
|
||||
conf="$root/conf/renewal/$cert_name.conf"
|
||||
live="$root/conf/live/$cert_name"
|
||||
for path in $archive $conf $live; do
|
||||
if [ -e $path ]; then
|
||||
echo "Lineage not properly deleted!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Most CI systems set this variable to true.
|
||||
# If the tests are running as part of CI, Nginx should be available.
|
||||
|
||||
Reference in New Issue
Block a user