mirror of
https://github.com/certbot/certbot.git
synced 2026-07-28 08:45:22 +02:00
Merge branch 'master' into pebble-no-docker
# Conflicts: # certbot-ci/certbot_integration_tests/utils/acme_server.py
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
||||
# Configuration for https://github.com/marketplace/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 180
|
||||
daysUntilStale: 365
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
### Added
|
||||
|
||||
*
|
||||
* Turn off session tickets for nginx plugin by default
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
"""Module to setup an ACME CA server environment able to run multiple tests in parallel"""
|
||||
from __future__ import print_function
|
||||
import json
|
||||
import tempfile
|
||||
import time
|
||||
import os
|
||||
@@ -10,9 +11,8 @@ import sys
|
||||
from os.path import join
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
from certbot_integration_tests.utils import misc, pebble_artifacts
|
||||
from certbot_integration_tests.utils import misc, proxy, pebble_artifacts
|
||||
from certbot_integration_tests.utils.constants import *
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ class ACMEServer(object):
|
||||
def start(self):
|
||||
"""Start the test stack"""
|
||||
if self._proxy:
|
||||
self._processes.extend(_prepare_traefik_proxy(self._workspace, self.acme_xdist))
|
||||
self._processes.extend(_prepare_http_proxy(self.acme_xdist))
|
||||
if self._acme_type == 'pebble':
|
||||
self._processes.extend(_prepare_pebble_server(self._workspace, self.acme_xdist))
|
||||
if self._acme_type == 'boulder':
|
||||
@@ -163,62 +163,16 @@ def _prepare_boulder_server(workspace, acme_xdist):
|
||||
return [process]
|
||||
|
||||
|
||||
def _prepare_traefik_proxy(workspace, acme_xdist):
|
||||
"""Configure and launch Traefik, the HTTP reverse proxy"""
|
||||
print('=> Starting traefik instance deployment...')
|
||||
instance_path = join(workspace, 'traefik')
|
||||
traefik_subnet = '10.33.33'
|
||||
try:
|
||||
os.mkdir(instance_path)
|
||||
def _prepare_http_proxy(acme_xdist):
|
||||
"""Configure and launch an HTTP proxy"""
|
||||
print('=> Configuring the HTTP proxy...')
|
||||
mapping = {r'.+\.{0}\.wtf'.format(node): 'http://127.0.0.1:{0}'.format(port)
|
||||
for node, port in acme_xdist['http_port'].items()}
|
||||
command = [sys.executable, proxy.__file__, str(HTTP_01_PORT), json.dumps(mapping)]
|
||||
process = _launch_process(command)
|
||||
print('=> Finished configuring the HTTP proxy.')
|
||||
|
||||
with open(join(instance_path, 'docker-compose.yml'), 'w') as file_h:
|
||||
file_h.write('''\
|
||||
version: '3'
|
||||
services:
|
||||
traefik:
|
||||
image: traefik
|
||||
command: --api --rest
|
||||
ports:
|
||||
- {http_01_port}:80
|
||||
- {traefik_api_port}:8080
|
||||
networks:
|
||||
traefiknet:
|
||||
ipv4_address: {traefik_subnet}.2
|
||||
networks:
|
||||
traefiknet:
|
||||
ipam:
|
||||
config:
|
||||
- subnet: {traefik_subnet}.0/24
|
||||
'''.format(traefik_subnet=traefik_subnet,
|
||||
traefik_api_port=TRAEFIK_API_PORT,
|
||||
http_01_port=HTTP_01_PORT))
|
||||
|
||||
process = _launch_process(['docker-compose', 'up', '--force-recreate'], cwd=instance_path)
|
||||
|
||||
misc.check_until_timeout('http://localhost:{0}/api'.format(TRAEFIK_API_PORT))
|
||||
config = {
|
||||
'backends': {
|
||||
node: {
|
||||
'servers': {node: {'url': 'http://{0}.1:{1}'.format(traefik_subnet, port)}}
|
||||
} for node, port in acme_xdist['http_port'].items()
|
||||
},
|
||||
'frontends': {
|
||||
node: {
|
||||
'backend': node, 'passHostHeader': True,
|
||||
'routes': {node: {'rule': 'HostRegexp: {{subdomain:.+}}.{0}.wtf'.format(node)}}
|
||||
} for node in acme_xdist['http_port'].keys()
|
||||
}
|
||||
}
|
||||
response = requests.put('http://localhost:{0}/api/providers/rest'.format(TRAEFIK_API_PORT),
|
||||
data=json.dumps(config))
|
||||
response.raise_for_status()
|
||||
|
||||
print('=> Finished traefik instance deployment.')
|
||||
|
||||
return [process]
|
||||
except BaseException:
|
||||
print('Error while setting up traefik instance.')
|
||||
raise
|
||||
return [process]
|
||||
|
||||
|
||||
def _launch_process(command, cwd=os.getcwd(), env=None):
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
HTTP_01_PORT = 5002
|
||||
TLS_ALPN_01_PORT = 5001
|
||||
CHALLTESTSRV_PORT = 8055
|
||||
TRAEFIK_API_PORT = 8056
|
||||
BOULDER_V1_DIRECTORY_URL = 'http://localhost:4000/directory'
|
||||
BOULDER_V2_DIRECTORY_URL = 'http://localhost:4001/directory'
|
||||
PEBBLE_DIRECTORY_URL = 'https://localhost:14000/dir'
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import sys
|
||||
import re
|
||||
|
||||
import requests
|
||||
from six.moves import BaseHTTPServer
|
||||
|
||||
from certbot_integration_tests.utils.misc import GracefulTCPServer
|
||||
|
||||
|
||||
def _create_proxy(mapping):
|
||||
class ProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
headers = {key.lower(): value for key, value in self.headers.items()}
|
||||
backend = [backend for pattern, backend in mapping.items()
|
||||
if re.match(pattern, headers['host'])][0]
|
||||
response = requests.get(backend + self.path, headers=headers)
|
||||
|
||||
self.send_response(response.status_code)
|
||||
for key, value in response.headers.items():
|
||||
self.send_header(key, value)
|
||||
self.end_headers()
|
||||
self.wfile.write(response.content)
|
||||
|
||||
return ProxyHandler
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
http_port = int(sys.argv[1])
|
||||
port_mapping = json.loads(sys.argv[2])
|
||||
httpd = GracefulTCPServer(('', http_port), _create_proxy(port_mapping))
|
||||
try:
|
||||
httpd.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
@@ -3,3 +3,4 @@ include README.rst
|
||||
recursive-include docs *
|
||||
recursive-include certbot_nginx/tests/testdata *
|
||||
include certbot_nginx/options-ssl-nginx.conf
|
||||
include certbot_nginx/options-ssl-nginx-old.conf
|
||||
|
||||
@@ -6,6 +6,8 @@ import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import pkg_resources
|
||||
|
||||
import OpenSSL
|
||||
import zope.interface
|
||||
|
||||
@@ -120,6 +122,14 @@ class NginxConfigurator(common.Installer):
|
||||
|
||||
self.reverter.recovery_routine()
|
||||
|
||||
@property
|
||||
def mod_ssl_conf_src(self):
|
||||
"""Full absolute path to SSL configuration file source."""
|
||||
config_filename = "options-ssl-nginx.conf"
|
||||
if self.version < (1, 5, 9):
|
||||
config_filename = "options-ssl-nginx-old.conf"
|
||||
return pkg_resources.resource_filename("certbot_nginx", config_filename)
|
||||
|
||||
@property
|
||||
def mod_ssl_conf(self):
|
||||
"""Full absolute path to SSL configuration file."""
|
||||
@@ -130,6 +140,11 @@ class NginxConfigurator(common.Installer):
|
||||
"""Full absolute path to digest of updated SSL configuration file."""
|
||||
return os.path.join(self.config.config_dir, constants.UPDATED_MOD_SSL_CONF_DIGEST)
|
||||
|
||||
def install_ssl_options_conf(self, options_ssl, options_ssl_digest):
|
||||
"""Copy Certbot's SSL options file into the system's config dir if required."""
|
||||
return common.install_version_controlled_file(options_ssl, options_ssl_digest,
|
||||
self.mod_ssl_conf_src, constants.ALL_SSL_OPTIONS_HASHES)
|
||||
|
||||
# This is called in determine_authenticator and determine_installer
|
||||
def prepare(self):
|
||||
"""Prepare the authenticator/installer.
|
||||
@@ -148,14 +163,14 @@ class NginxConfigurator(common.Installer):
|
||||
|
||||
self.parser = parser.NginxParser(self.conf('server-root'))
|
||||
|
||||
install_ssl_options_conf(self.mod_ssl_conf, self.updated_mod_ssl_conf_digest)
|
||||
|
||||
self.install_ssl_dhparams()
|
||||
|
||||
# Set Version
|
||||
if self.version is None:
|
||||
self.version = self.get_version()
|
||||
|
||||
self.install_ssl_options_conf(self.mod_ssl_conf, self.updated_mod_ssl_conf_digest)
|
||||
|
||||
self.install_ssl_dhparams()
|
||||
|
||||
# Prevent two Nginx plugins from modifying a config at once
|
||||
try:
|
||||
util.lock_dir_until_exit(self.conf('server-root'))
|
||||
@@ -1131,12 +1146,6 @@ def nginx_restart(nginx_ctl, nginx_conf):
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def install_ssl_options_conf(options_ssl, options_ssl_digest):
|
||||
"""Copy Certbot's SSL options file into the system's config dir if required."""
|
||||
return common.install_version_controlled_file(options_ssl, options_ssl_digest,
|
||||
constants.MOD_SSL_CONF_SRC, constants.ALL_SSL_OPTIONS_HASHES)
|
||||
|
||||
|
||||
def _determine_default_server_root():
|
||||
if os.environ.get("CERTBOT_DOCS") == "1":
|
||||
default_server_root = "%s or %s" % (constants.LINUX_SERVER_ROOT,
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
"""nginx plugin constants."""
|
||||
import platform
|
||||
|
||||
import pkg_resources
|
||||
|
||||
FREEBSD_DARWIN_SERVER_ROOT = "/usr/local/etc/nginx"
|
||||
LINUX_SERVER_ROOT = "/etc/nginx"
|
||||
|
||||
@@ -21,14 +19,13 @@ CLI_DEFAULTS = dict(
|
||||
MOD_SSL_CONF_DEST = "options-ssl-nginx.conf"
|
||||
"""Name of the mod_ssl config file as saved in `IConfig.config_dir`."""
|
||||
|
||||
MOD_SSL_CONF_SRC = pkg_resources.resource_filename(
|
||||
"certbot_nginx", "options-ssl-nginx.conf")
|
||||
"""Path to the nginx mod_ssl config file found in the Certbot
|
||||
distribution."""
|
||||
|
||||
UPDATED_MOD_SSL_CONF_DIGEST = ".updated-options-ssl-nginx-conf-digest.txt"
|
||||
"""Name of the hash of the updated or informed mod_ssl_conf as saved in `IConfig.config_dir`."""
|
||||
|
||||
SSL_OPTIONS_HASHES_NEW = [
|
||||
'63e2bddebb174a05c9d8a7cf2adf72f7af04349ba59a1a925fe447f73b2f1abf',
|
||||
]
|
||||
"""SHA256 hashes of the contents of versions of MOD_SSL_CONF_SRC for nginx >= 1.5.9"""
|
||||
|
||||
ALL_SSL_OPTIONS_HASHES = [
|
||||
'0f81093a1465e3d4eaa8b0c14e77b2a2e93568b0fc1351c2b87893a95f0de87c',
|
||||
@@ -37,7 +34,7 @@ ALL_SSL_OPTIONS_HASHES = [
|
||||
'7f95624dd95cf5afc708b9f967ee83a24b8025dc7c8d9df2b556bbc64256b3ff',
|
||||
'394732f2bbe3e5e637c3fb5c6e980a1f1b90b01e2e8d6b7cff41dde16e2a756d',
|
||||
'4b16fec2bcbcd8a2f3296d886f17f9953ffdcc0af54582452ca1e52f5f776f16',
|
||||
]
|
||||
] + SSL_OPTIONS_HASHES_NEW
|
||||
"""SHA256 hashes of the contents of all versions of MOD_SSL_CONF_SRC"""
|
||||
|
||||
def os_constant(key):
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# This file contains important security parameters. If you modify this file
|
||||
# manually, Certbot will be unable to automatically provide future security
|
||||
# updates. Instead, Certbot will print and log an error message with a path to
|
||||
# the up-to-date file that you will need to refer to when manually updating
|
||||
# this file.
|
||||
|
||||
ssl_session_cache shared:le_nginx_SSL:1m;
|
||||
ssl_session_timeout 1440m;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
ssl_session_cache shared:le_nginx_SSL:1m;
|
||||
ssl_session_timeout 1440m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
@@ -13,7 +13,6 @@ from certbot import errors
|
||||
from certbot.compat import os
|
||||
from certbot.tests import util as certbot_test_util
|
||||
|
||||
from certbot_nginx import constants
|
||||
from certbot_nginx import obj
|
||||
from certbot_nginx import parser
|
||||
from certbot_nginx.configurator import _redirect_block_for_domain
|
||||
@@ -883,12 +882,11 @@ class InstallSslOptionsConfTest(util.NginxTest):
|
||||
self.config_path, self.config_dir, self.work_dir, self.logs_dir)
|
||||
|
||||
def _call(self):
|
||||
from certbot_nginx.configurator import install_ssl_options_conf
|
||||
install_ssl_options_conf(self.config.mod_ssl_conf, self.config.updated_mod_ssl_conf_digest)
|
||||
self.config.install_ssl_options_conf(self.config.mod_ssl_conf,
|
||||
self.config.updated_mod_ssl_conf_digest)
|
||||
|
||||
def _current_ssl_options_hash(self):
|
||||
from certbot_nginx.constants import MOD_SSL_CONF_SRC
|
||||
return crypto_util.sha256sum(MOD_SSL_CONF_SRC)
|
||||
return crypto_util.sha256sum(self.config.mod_ssl_conf_src)
|
||||
|
||||
def _assert_current_file(self):
|
||||
self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
|
||||
@@ -908,13 +906,33 @@ class InstallSslOptionsConfTest(util.NginxTest):
|
||||
self._call()
|
||||
self._assert_current_file()
|
||||
|
||||
def _mock_hash_except_ssl_conf_src(self, fake_hash):
|
||||
# Write a bad file in place so that update tests fail if no update occurs.
|
||||
# We're going to pretend this file (the currently installed conf file)
|
||||
# actually hashes to `fake_hash` for the update tests.
|
||||
with open(self.config.mod_ssl_conf, "w") as f:
|
||||
f.write("bogus")
|
||||
sha256 = crypto_util.sha256sum
|
||||
def _hash(filename):
|
||||
return sha256(filename) if filename == self.config.mod_ssl_conf_src else fake_hash
|
||||
return _hash
|
||||
|
||||
def test_prev_file_updates_to_current(self):
|
||||
from certbot_nginx.constants import ALL_SSL_OPTIONS_HASHES
|
||||
with mock.patch('certbot.crypto_util.sha256sum') as mock_sha256:
|
||||
mock_sha256.return_value = ALL_SSL_OPTIONS_HASHES[0]
|
||||
with mock.patch('certbot.crypto_util.sha256sum',
|
||||
new=self._mock_hash_except_ssl_conf_src(ALL_SSL_OPTIONS_HASHES[0])):
|
||||
self._call()
|
||||
self._assert_current_file()
|
||||
|
||||
def test_prev_file_updates_to_current_old_nginx(self):
|
||||
from certbot_nginx.constants import ALL_SSL_OPTIONS_HASHES, SSL_OPTIONS_HASHES_NEW
|
||||
self.config.version = (1, 5, 8)
|
||||
with mock.patch('certbot.crypto_util.sha256sum',
|
||||
new=self._mock_hash_except_ssl_conf_src(ALL_SSL_OPTIONS_HASHES[0])):
|
||||
self._call()
|
||||
self._assert_current_file()
|
||||
self.assertTrue(self._current_ssl_options_hash() not in SSL_OPTIONS_HASHES_NEW)
|
||||
|
||||
def test_manually_modified_current_file_does_not_update(self):
|
||||
with open(self.config.mod_ssl_conf, "a") as mod_ssl_conf:
|
||||
mod_ssl_conf.write("a new line for the wrong hash\n")
|
||||
@@ -922,7 +940,7 @@ class InstallSslOptionsConfTest(util.NginxTest):
|
||||
self._call()
|
||||
self.assertFalse(mock_logger.warning.called)
|
||||
self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
|
||||
self.assertEqual(crypto_util.sha256sum(constants.MOD_SSL_CONF_SRC),
|
||||
self.assertEqual(crypto_util.sha256sum(self.config.mod_ssl_conf_src),
|
||||
self._current_ssl_options_hash())
|
||||
self.assertNotEqual(crypto_util.sha256sum(self.config.mod_ssl_conf),
|
||||
self._current_ssl_options_hash())
|
||||
@@ -937,7 +955,7 @@ class InstallSslOptionsConfTest(util.NginxTest):
|
||||
self.assertEqual(mock_logger.warning.call_args[0][0],
|
||||
"%s has been manually modified; updated file "
|
||||
"saved to %s. We recommend updating %s for security purposes.")
|
||||
self.assertEqual(crypto_util.sha256sum(constants.MOD_SSL_CONF_SRC),
|
||||
self.assertEqual(crypto_util.sha256sum(self.config.mod_ssl_conf_src),
|
||||
self._current_ssl_options_hash())
|
||||
# only print warning once
|
||||
with mock.patch("certbot.plugins.common.logger") as mock_logger:
|
||||
@@ -950,6 +968,16 @@ class InstallSslOptionsConfTest(util.NginxTest):
|
||||
"Constants.ALL_SSL_OPTIONS_HASHES must be appended"
|
||||
" with the sha256 hash of self.config.mod_ssl_conf when it is updated.")
|
||||
|
||||
def test_old_nginx_version_uses_old_config(self):
|
||||
self.config.version = (1, 5, 8)
|
||||
self.assertEqual(os.path.basename(self.config.mod_ssl_conf_src),
|
||||
"options-ssl-nginx-old.conf")
|
||||
self._call()
|
||||
self._assert_current_file()
|
||||
self.config.version = (1, 5, 9)
|
||||
self.assertEqual(os.path.basename(self.config.mod_ssl_conf_src),
|
||||
"options-ssl-nginx.conf")
|
||||
|
||||
|
||||
class DetermineDefaultServerRootTest(certbot_test_util.ConfigTestCase):
|
||||
"""Tests for certbot_nginx.configurator._determine_default_server_root."""
|
||||
|
||||
Reference in New Issue
Block a user