Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'certbot-nginx/tests')
-rwxr-xr-xcertbot-nginx/tests/boulder-integration.conf.sh31
-rwxr-xr-xcertbot-nginx/tests/boulder-integration.sh69
-rw-r--r--certbot-nginx/tests/configurator_test.py1098
-rw-r--r--certbot-nginx/tests/display_ops_test.py42
-rw-r--r--certbot-nginx/tests/http_01_test.py143
-rw-r--r--certbot-nginx/tests/nginxparser_test.py443
-rw-r--r--certbot-nginx/tests/obj_test.py228
-rw-r--r--certbot-nginx/tests/parser_obj_test.py255
-rw-r--r--certbot-nginx/tests/parser_test.py487
-rw-r--r--certbot-nginx/tests/test_util.py130
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/broken.conf12
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/comment_in_file.conf1
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/edge_cases.conf27
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/foo.conf25
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/mime.types0
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/minimalistic_comments.conf12
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/multiline_quotes.conf16
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/nginx.conf122
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/server.conf1
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/default10
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/example.com6
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/globalssl.com9
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/headers.com4
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6.com5
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com7
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/migration.com19
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/sites-enabled/sslon.com6
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params25
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf108
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win102
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types79
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.116
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules13
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules75
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf95
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params4
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params14
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default112
l---------certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default1
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params15
-rw-r--r--certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf125
41 files changed, 3912 insertions, 80 deletions
diff --git a/certbot-nginx/tests/boulder-integration.conf.sh b/certbot-nginx/tests/boulder-integration.conf.sh
index 470eab28e..35cedf5ed 100755
--- a/certbot-nginx/tests/boulder-integration.conf.sh
+++ b/certbot-nginx/tests/boulder-integration.conf.sh
@@ -3,16 +3,22 @@
# https://www.exratione.com/2014/03/running-nginx-as-a-non-root-user/
# https://github.com/exratione/non-root-nginx/blob/9a77f62e5d5cb9c9026fd62eece76b9514011019/nginx.conf
+# USAGE: ./boulder-integration.conf.sh /path/to/root cert.key cert.pem >> nginx.conf
+
+ROOT=$1
+CERT_KEY_PATH=$2
+CERT_PATH=$3
+
cat <<EOF
# This error log will be written regardless of server scope error_log
# definitions, so we have to set this here in the main scope.
#
# Even doing this, Nginx will still try to create the default error file, and
# log a non-fatal error when it fails. After that things will work, however.
-error_log $root/error.log;
+error_log $ROOT/error.log;
# The pidfile will be written to /var/run unless this is set.
-pid $root/nginx.pid;
+pid $ROOT/nginx.pid;
worker_processes 1;
@@ -23,12 +29,12 @@ events {
http {
# Set an array of temp, cache and log file options that will otherwise default to
# restricted locations accessible only to root.
- client_body_temp_path $root/client_body;
- fastcgi_temp_path $root/fastcgi_temp;
- proxy_temp_path $root/proxy_temp;
- #scgi_temp_path $root/scgi_temp;
- #uwsgi_temp_path $root/uwsgi_temp;
- access_log $root/error.log;
+ client_body_temp_path $ROOT/client_body;
+ fastcgi_temp_path $ROOT/fastcgi_temp;
+ proxy_temp_path $ROOT/proxy_temp;
+ #scgi_temp_path $ROOT/scgi_temp;
+ #uwsgi_temp_path $ROOT/uwsgi_temp;
+ access_log $ROOT/error.log;
# This should be turned off in a Virtualbox VM, as it can cause some
# interesting issues with data corruption in delivered files.
@@ -53,9 +59,9 @@ http {
listen 5002 $default_server;
# IPv6.
listen [::]:5002 $default_server;
- server_name nginx.wtf nginx-tls.wtf nginx2.wtf;
+ server_name nginx.wtf nginx2.wtf;
- root $root/webroot;
+ root $ROOT/webroot;
location / {
# First attempt to serve request as file, then as directory, then fall
@@ -69,7 +75,7 @@ http {
listen [::]:5002;
server_name nginx3.wtf;
- root $root/webroot;
+ root $ROOT/webroot;
location /.well-known/ {
return 404;
@@ -93,6 +99,9 @@ http {
return 301 https://\$host\$request_uri;
}
server_name nginx6.wtf nginx7.wtf;
+
+ ssl_certificate ${CERT_PATH};
+ ssl_certificate_key ${CERT_KEY_PATH};
}
}
EOF
diff --git a/certbot-nginx/tests/boulder-integration.sh b/certbot-nginx/tests/boulder-integration.sh
deleted file mode 100755
index 03425734d..000000000
--- a/certbot-nginx/tests/boulder-integration.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/bash -xe
-# prerequisite: apt-get install --no-install-recommends nginx-light openssl
-
-. ./tests/integration/_common.sh
-
-export PATH="/usr/sbin:$PATH" # /usr/sbin/nginx
-nginx_root="$root/nginx"
-mkdir $nginx_root
-
-reload_nginx () {
- original=$(root="$nginx_root" ./certbot-nginx/tests/boulder-integration.conf.sh)
- nginx_conf="$nginx_root/nginx.conf"
- echo "$original" > $nginx_conf
-
- killall nginx || true
- nginx -c $nginx_root/nginx.conf
-}
-
-certbot_test_nginx () {
- certbot_test \
- --authenticator nginx \
- --installer nginx \
- --nginx-server-root $nginx_root \
- "$@"
-}
-
-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
-}
-
-export default_server="default_server"
-nginx -v
-reload_nginx
-certbot_test_nginx --domains nginx.wtf run
-test_deployment_and_rollback nginx.wtf
-certbot_test_nginx --domains nginx-tls.wtf run --preferred-challenges tls-sni
-test_deployment_and_rollback nginx-tls.wtf
-certbot_test_nginx --domains nginx2.wtf --preferred-challenges http
-test_deployment_and_rollback nginx2.wtf
-# Overlapping location block and server-block-level return 301
-certbot_test_nginx --domains nginx3.wtf --preferred-challenges http
-test_deployment_and_rollback nginx3.wtf
-# No matching server block; default_server exists
-certbot_test_nginx --domains nginx4.wtf --preferred-challenges http
-test_deployment_and_rollback nginx4.wtf
-# No matching server block; default_server does not exist
-export default_server=""
-reload_nginx
-if nginx -c $nginx_root/nginx.conf -T 2>/dev/null | grep "default_server"; then
- echo "Failed to remove default_server"
- exit 1
-fi
-certbot_test_nginx --domains nginx5.wtf --preferred-challenges http
-test_deployment_and_rollback nginx5.wtf
-# Mutiple domains, mix of matching and not
-certbot_test_nginx --domains nginx6.wtf,nginx7.wtf --preferred-challenges http
-test_deployment_and_rollback nginx6.wtf
-
-# note: not reached if anything above fails, hence "killall" at the
-# top
-nginx -c $nginx_root/nginx.conf -s stop
-
-coverage report --fail-under 75 --include 'certbot-nginx/*' --show-missing
diff --git a/certbot-nginx/tests/configurator_test.py b/certbot-nginx/tests/configurator_test.py
new file mode 100644
index 000000000..ef5593395
--- /dev/null
+++ b/certbot-nginx/tests/configurator_test.py
@@ -0,0 +1,1098 @@
+"""Test for certbot_nginx._internal.configurator."""
+import unittest
+
+import mock
+import OpenSSL
+
+from acme import challenges
+from acme import messages
+from certbot import achallenges
+from certbot import crypto_util
+from certbot import errors
+from certbot.compat import os
+from certbot.tests import util as certbot_test_util
+from certbot_nginx._internal import obj
+from certbot_nginx._internal import parser
+from certbot_nginx._internal.configurator import _redirect_block_for_domain
+from certbot_nginx._internal.nginxparser import UnspacedList
+import test_util as util
+
+
+class NginxConfiguratorTest(util.NginxTest):
+ """Test a semi complex vhost configuration."""
+
+
+ def setUp(self):
+ super(NginxConfiguratorTest, self).setUp()
+
+ self.config = self.get_nginx_configurator(
+ self.config_path, self.config_dir, self.work_dir, self.logs_dir)
+
+ @mock.patch("certbot_nginx._internal.configurator.util.exe_exists")
+ def test_prepare_no_install(self, mock_exe_exists):
+ mock_exe_exists.return_value = False
+ self.assertRaises(
+ errors.NoInstallationError, self.config.prepare)
+
+ def test_prepare(self):
+ self.assertEqual((1, 6, 2), self.config.version)
+ self.assertEqual(11, len(self.config.parser.parsed))
+
+ @mock.patch("certbot_nginx._internal.configurator.util.exe_exists")
+ @mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
+ def test_prepare_initializes_version(self, mock_popen, mock_exe_exists):
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["nginx version: nginx/1.6.2",
+ "built by clang 6.0 (clang-600.0.56)"
+ " (based on LLVM 3.5svn)",
+ "TLS SNI support enabled",
+ "configure arguments: --prefix=/usr/local/Cellar/"
+ "nginx/1.6.2 --with-http_ssl_module"]))
+
+ mock_exe_exists.return_value = True
+
+ self.config.version = None
+ self.config.config_test = mock.Mock()
+ self.config.prepare()
+ self.assertEqual((1, 6, 2), self.config.version)
+
+ def test_prepare_locked(self):
+ server_root = self.config.conf("server-root")
+
+ from certbot import util as certbot_util
+ certbot_util._LOCKS[server_root].release() # pylint: disable=protected-access
+
+ self.config.config_test = mock.Mock()
+ certbot_test_util.lock_and_call(self._test_prepare_locked, server_root)
+
+ @mock.patch("certbot_nginx._internal.configurator.util.exe_exists")
+ def _test_prepare_locked(self, unused_exe_exists):
+ try:
+ self.config.prepare()
+ except errors.PluginError as err:
+ err_msg = str(err)
+ self.assertTrue("lock" in err_msg)
+ self.assertTrue(self.config.conf("server-root") in err_msg)
+ else: # pragma: no cover
+ self.fail("Exception wasn't raised!")
+
+ @mock.patch("certbot_nginx._internal.configurator.socket.gethostbyaddr")
+ def test_get_all_names(self, mock_gethostbyaddr):
+ mock_gethostbyaddr.return_value = ('155.225.50.69.nephoscale.net', [], [])
+ names = self.config.get_all_names()
+ self.assertEqual(names, {
+ "155.225.50.69.nephoscale.net", "www.example.org", "another.alias",
+ "migration.com", "summer.com", "geese.com", "sslon.com",
+ "globalssl.com", "globalsslsetssl.com", "ipv6.com", "ipv6ssl.com",
+ "headers.com"})
+
+ def test_supported_enhancements(self):
+ self.assertEqual(['redirect', 'ensure-http-header', 'staple-ocsp'],
+ self.config.supported_enhancements())
+
+ def test_enhance(self):
+ self.assertRaises(
+ errors.PluginError, self.config.enhance, 'myhost', 'unknown_enhancement')
+
+ def test_get_chall_pref(self):
+ self.assertEqual([challenges.HTTP01],
+ self.config.get_chall_pref('myhost'))
+
+ def test_save(self):
+ filep = self.config.parser.abs_path('sites-enabled/example.com')
+ mock_vhost = obj.VirtualHost(filep,
+ None, None, None,
+ set(['.example.com', 'example.*']),
+ None, [0])
+ self.config.parser.add_server_directives(
+ mock_vhost,
+ [['listen', ' ', '5001', ' ', 'ssl']])
+ self.config.save()
+
+ # pylint: disable=protected-access
+ parsed = self.config.parser._parse_files(filep, override=True)
+ self.assertEqual([[['server'],
+ [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+ ['listen', '5001', 'ssl'],
+ ['#', parser.COMMENT]]]],
+ parsed[0])
+
+ def test_choose_vhosts_alias(self):
+ self._test_choose_vhosts_common('alias', 'server_conf')
+
+ def test_choose_vhosts_example_com(self):
+ self._test_choose_vhosts_common('example.com', 'example_conf')
+
+ def test_choose_vhosts_localhost(self):
+ self._test_choose_vhosts_common('localhost', 'localhost_conf')
+
+ def test_choose_vhosts_example_com_uk_test(self):
+ self._test_choose_vhosts_common('example.com.uk.test', 'example_conf')
+
+ def test_choose_vhosts_www_example_com(self):
+ self._test_choose_vhosts_common('www.example.com', 'example_conf')
+
+ def test_choose_vhosts_test_www_example_com(self):
+ self._test_choose_vhosts_common('test.www.example.com', 'foo_conf')
+
+ def test_choose_vhosts_abc_www_foo_com(self):
+ self._test_choose_vhosts_common('abc.www.foo.com', 'foo_conf')
+
+ def test_choose_vhosts_www_bar_co_uk(self):
+ self._test_choose_vhosts_common('www.bar.co.uk', 'localhost_conf')
+
+ def test_choose_vhosts_ipv6_com(self):
+ self._test_choose_vhosts_common('ipv6.com', 'ipv6_conf')
+
+ def _test_choose_vhosts_common(self, name, conf):
+ conf_names = {'localhost_conf': set(['localhost', r'~^(www\.)?(example|bar)\.']),
+ 'server_conf': set(['somename', 'another.alias', 'alias']),
+ 'example_conf': set(['.example.com', 'example.*']),
+ 'foo_conf': set(['*.www.foo.com', '*.www.example.com']),
+ 'ipv6_conf': set(['ipv6.com'])}
+
+ conf_path = {'localhost': "etc_nginx/nginx.conf",
+ 'alias': "etc_nginx/nginx.conf",
+ 'example.com': "etc_nginx/sites-enabled/example.com",
+ 'example.com.uk.test': "etc_nginx/sites-enabled/example.com",
+ 'www.example.com': "etc_nginx/sites-enabled/example.com",
+ 'test.www.example.com': "etc_nginx/foo.conf",
+ 'abc.www.foo.com': "etc_nginx/foo.conf",
+ 'www.bar.co.uk': "etc_nginx/nginx.conf",
+ 'ipv6.com': "etc_nginx/sites-enabled/ipv6.com"}
+ conf_path = {key: os.path.normpath(value) for key, value in conf_path.items()}
+
+ vhost = self.config.choose_vhosts(name)[0]
+ path = os.path.relpath(vhost.filep, self.temp_dir)
+
+ self.assertEqual(conf_names[conf], vhost.names)
+ self.assertEqual(conf_path[name], path)
+ # IPv6 specific checks
+ if name == "ipv6.com":
+ self.assertTrue(vhost.ipv6_enabled())
+ # Make sure that we have SSL enabled also for IPv6 addr
+ self.assertTrue(
+ any([True for x in vhost.addrs if x.ssl and x.ipv6]))
+
+ def test_choose_vhosts_bad(self):
+ bad_results = ['www.foo.com', 'example', 't.www.bar.co',
+ '69.255.225.155']
+
+ for name in bad_results:
+ self.assertRaises(errors.MisconfigurationError,
+ self.config.choose_vhosts, name)
+
+ def test_ipv6only(self):
+ # ipv6_info: (ipv6_active, ipv6only_present)
+ self.assertEqual((True, False), self.config.ipv6_info("80"))
+ # Port 443 has ipv6only=on because of ipv6ssl.com vhost
+ self.assertEqual((True, True), self.config.ipv6_info("443"))
+
+ def test_ipv6only_detection(self):
+ self.config.version = (1, 3, 1)
+
+ self.config.deploy_cert(
+ "ipv6.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+
+ for addr in self.config.choose_vhosts("ipv6.com")[0].addrs:
+ self.assertFalse(addr.ipv6only)
+
+ def test_more_info(self):
+ self.assertTrue('nginx.conf' in self.config.more_info())
+
+ def test_deploy_cert_requires_fullchain_path(self):
+ self.config.version = (1, 3, 1)
+ self.assertRaises(errors.PluginError, self.config.deploy_cert,
+ "www.example.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ None)
+
+ @mock.patch('certbot_nginx._internal.parser.NginxParser.update_or_add_server_directives')
+ def test_deploy_cert_raise_on_add_error(self, mock_update_or_add_server_directives):
+ mock_update_or_add_server_directives.side_effect = errors.MisconfigurationError()
+ self.assertRaises(
+ errors.PluginError,
+ self.config.deploy_cert,
+ "migration.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+
+ def test_deploy_cert(self):
+ server_conf = self.config.parser.abs_path('server.conf')
+ nginx_conf = self.config.parser.abs_path('nginx.conf')
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ self.config.version = (1, 3, 1)
+
+ # Get the default SSL vhost
+ self.config.deploy_cert(
+ "www.example.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+ self.config.deploy_cert(
+ "another.alias",
+ "/etc/nginx/cert.pem",
+ "/etc/nginx/key.pem",
+ "/etc/nginx/chain.pem",
+ "/etc/nginx/fullchain.pem")
+ self.config.save()
+
+ self.config.parser.load()
+
+ parsed_example_conf = util.filter_comments(self.config.parser.parsed[example_conf])
+ parsed_server_conf = util.filter_comments(self.config.parser.parsed[server_conf])
+ parsed_nginx_conf = util.filter_comments(self.config.parser.parsed[nginx_conf])
+
+ self.assertEqual([[['server'],
+ [
+ ['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+
+ ['listen', '5001', 'ssl'],
+ ['ssl_certificate', 'example/fullchain.pem'],
+ ['ssl_certificate_key', 'example/key.pem'],
+ ['include', self.config.mod_ssl_conf],
+ ['ssl_dhparam', self.config.ssl_dhparams],
+ ]]],
+ parsed_example_conf)
+ self.assertEqual([['server_name', 'somename', 'alias', 'another.alias']],
+ parsed_server_conf)
+ self.assertTrue(util.contains_at_depth(
+ parsed_nginx_conf,
+ [['server'],
+ [
+ ['listen', '8000'],
+ ['listen', 'somename:8080'],
+ ['include', 'server.conf'],
+ [['location', '/'],
+ [['root', 'html'],
+ ['index', 'index.html', 'index.htm']]],
+ ['listen', '5001', 'ssl'],
+ ['ssl_certificate', '/etc/nginx/fullchain.pem'],
+ ['ssl_certificate_key', '/etc/nginx/key.pem'],
+ ['include', self.config.mod_ssl_conf],
+ ['ssl_dhparam', self.config.ssl_dhparams],
+ ]],
+ 2))
+
+ def test_deploy_cert_add_explicit_listen(self):
+ migration_conf = self.config.parser.abs_path('sites-enabled/migration.com')
+ self.config.deploy_cert(
+ "summer.com",
+ "summer/cert.pem",
+ "summer/key.pem",
+ "summer/chain.pem",
+ "summer/fullchain.pem")
+ self.config.save()
+ self.config.parser.load()
+ parsed_migration_conf = util.filter_comments(self.config.parser.parsed[migration_conf])
+ self.assertEqual([['server'],
+ [
+ ['server_name', 'migration.com'],
+ ['server_name', 'summer.com'],
+
+ ['listen', '80'],
+ ['listen', '5001', 'ssl'],
+ ['ssl_certificate', 'summer/fullchain.pem'],
+ ['ssl_certificate_key', 'summer/key.pem'],
+ ['include', self.config.mod_ssl_conf],
+ ['ssl_dhparam', self.config.ssl_dhparams],
+ ]],
+ parsed_migration_conf[0])
+
+ @mock.patch("certbot_nginx._internal.configurator.http_01.NginxHttp01.perform")
+ @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.restart")
+ @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.revert_challenge_config")
+ def test_perform_and_cleanup(self, mock_revert, mock_restart, mock_http_perform):
+ # Only tests functionality specific to configurator.perform
+ # Note: As more challenges are offered this will have to be expanded
+ achall = achallenges.KeyAuthorizationAnnotatedChallenge(
+ challb=messages.ChallengeBody(
+ chall=challenges.HTTP01(token=b"m8TdO1qik4JVFtgPPurJmg"),
+ uri="https://ca.org/chall1_uri",
+ status=messages.Status("pending"),
+ ), domain="example.com", account_key=self.rsa512jwk)
+
+ expected = [
+ achall.response(self.rsa512jwk),
+ ]
+
+ mock_http_perform.return_value = expected[:]
+ responses = self.config.perform([achall])
+
+ self.assertEqual(mock_http_perform.call_count, 1)
+ self.assertEqual(responses, expected)
+
+ self.config.cleanup([achall])
+ self.assertEqual(0, self.config._chall_out) # pylint: disable=protected-access
+ self.assertEqual(mock_revert.call_count, 1)
+ self.assertEqual(mock_restart.call_count, 2)
+
+ @mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
+ def test_get_version(self, mock_popen):
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["nginx version: nginx/1.4.2",
+ "built by clang 6.0 (clang-600.0.56)"
+ " (based on LLVM 3.5svn)",
+ "TLS SNI support enabled",
+ "configure arguments: --prefix=/usr/local/Cellar/"
+ "nginx/1.6.2 --with-http_ssl_module"]))
+ self.assertEqual(self.config.get_version(), (1, 4, 2))
+
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["nginx version: nginx/0.9",
+ "built by clang 6.0 (clang-600.0.56)"
+ " (based on LLVM 3.5svn)",
+ "TLS SNI support enabled",
+ "configure arguments: --with-http_ssl_module"]))
+ self.assertEqual(self.config.get_version(), (0, 9))
+
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["blah 0.0.1",
+ "built by clang 6.0 (clang-600.0.56)"
+ " (based on LLVM 3.5svn)",
+ "TLS SNI support enabled",
+ "configure arguments: --with-http_ssl_module"]))
+ self.assertRaises(errors.PluginError, self.config.get_version)
+
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["nginx version: nginx/1.4.2",
+ "TLS SNI support enabled"]))
+ self.assertRaises(errors.PluginError, self.config.get_version)
+
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["nginx version: nginx/1.4.2",
+ "built by clang 6.0 (clang-600.0.56)"
+ " (based on LLVM 3.5svn)",
+ "configure arguments: --with-http_ssl_module"]))
+ self.assertRaises(errors.PluginError, self.config.get_version)
+
+ mock_popen().communicate.return_value = (
+ "", "\n".join(["nginx version: nginx/0.8.1",
+ "built by clang 6.0 (clang-600.0.56)"
+ " (based on LLVM 3.5svn)",
+ "TLS SNI support enabled",
+ "configure arguments: --with-http_ssl_module"]))
+ self.assertRaises(errors.NotSupportedError, self.config.get_version)
+
+ mock_popen.side_effect = OSError("Can't find program")
+ self.assertRaises(errors.PluginError, self.config.get_version)
+
+ @mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
+ def test_get_openssl_version(self, mock_popen):
+ # pylint: disable=protected-access
+ mock_popen().communicate.return_value = (
+ "", """
+ nginx version: nginx/1.15.5
+ built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
+ built with OpenSSL 1.0.2g 1 Mar 2016
+ TLS SNI support enabled
+ configure arguments:
+ """)
+ self.assertEqual(self.config._get_openssl_version(), "1.0.2g")
+
+ mock_popen().communicate.return_value = (
+ "", """
+ nginx version: nginx/1.15.5
+ built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
+ built with OpenSSL 1.0.2-beta1 1 Mar 2016
+ TLS SNI support enabled
+ configure arguments:
+ """)
+ self.assertEqual(self.config._get_openssl_version(), "1.0.2-beta1")
+
+ mock_popen().communicate.return_value = (
+ "", """
+ nginx version: nginx/1.15.5
+ built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
+ built with OpenSSL 1.0.2 1 Mar 2016
+ TLS SNI support enabled
+ configure arguments:
+ """)
+ self.assertEqual(self.config._get_openssl_version(), "1.0.2")
+
+ mock_popen().communicate.return_value = (
+ "", """
+ nginx version: nginx/1.15.5
+ built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
+ built with OpenSSL 1.0.2g 1 Mar 2016 (running with OpenSSL 1.0.2a 1 Mar 2016)
+ TLS SNI support enabled
+ configure arguments:
+ """)
+ self.assertEqual(self.config._get_openssl_version(), "1.0.2a")
+
+ mock_popen().communicate.return_value = (
+ "", """
+ nginx version: nginx/1.15.5
+ built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
+ built with LibreSSL 2.2.2
+ TLS SNI support enabled
+ configure arguments:
+ """)
+ self.assertEqual(self.config._get_openssl_version(), "")
+
+ mock_popen().communicate.return_value = (
+ "", """
+ nginx version: nginx/1.15.5
+ built by gcc 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
+ TLS SNI support enabled
+ configure arguments:
+ """)
+ self.assertEqual(self.config._get_openssl_version(), "")
+
+ @mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
+ def test_nginx_restart(self, mock_popen):
+ mocked = mock_popen()
+ mocked.communicate.return_value = ('', '')
+ mocked.returncode = 0
+ self.config.restart()
+
+ @mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
+ def test_nginx_restart_fail(self, mock_popen):
+ mocked = mock_popen()
+ mocked.communicate.return_value = ('', '')
+ mocked.returncode = 1
+ self.assertRaises(errors.MisconfigurationError, self.config.restart)
+
+ @mock.patch("certbot_nginx._internal.configurator.subprocess.Popen")
+ def test_no_nginx_start(self, mock_popen):
+ mock_popen.side_effect = OSError("Can't find program")
+ self.assertRaises(errors.MisconfigurationError, self.config.restart)
+
+ @mock.patch("certbot.util.run_script")
+ def test_config_test_bad_process(self, mock_run_script):
+ mock_run_script.side_effect = errors.SubprocessError
+ self.assertRaises(errors.MisconfigurationError, self.config.config_test)
+
+ @mock.patch("certbot.util.run_script")
+ def test_config_test(self, _):
+ self.config.config_test()
+
+ @mock.patch("certbot.reverter.Reverter.recovery_routine")
+ def test_recovery_routine_throws_error_from_reverter(self, mock_recovery_routine):
+ mock_recovery_routine.side_effect = errors.ReverterError("foo")
+ self.assertRaises(errors.PluginError, self.config.recovery_routine)
+
+ @mock.patch("certbot.reverter.Reverter.rollback_checkpoints")
+ def test_rollback_checkpoints_throws_error_from_reverter(self, mock_rollback_checkpoints):
+ mock_rollback_checkpoints.side_effect = errors.ReverterError("foo")
+ self.assertRaises(errors.PluginError, self.config.rollback_checkpoints)
+
+ @mock.patch("certbot.reverter.Reverter.revert_temporary_config")
+ def test_revert_challenge_config_throws_error_from_reverter(self, mock_revert_temporary_config):
+ mock_revert_temporary_config.side_effect = errors.ReverterError("foo")
+ self.assertRaises(errors.PluginError, self.config.revert_challenge_config)
+
+ @mock.patch("certbot.reverter.Reverter.add_to_checkpoint")
+ def test_save_throws_error_from_reverter(self, mock_add_to_checkpoint):
+ mock_add_to_checkpoint.side_effect = errors.ReverterError("foo")
+ self.assertRaises(errors.PluginError, self.config.save)
+
+ def test_get_snakeoil_paths(self):
+ # pylint: disable=protected-access
+ cert, key = self.config._get_snakeoil_paths()
+ self.assertTrue(os.path.exists(cert))
+ self.assertTrue(os.path.exists(key))
+ with open(cert) as cert_file:
+ OpenSSL.crypto.load_certificate(
+ OpenSSL.crypto.FILETYPE_PEM, cert_file.read())
+ with open(key) as key_file:
+ OpenSSL.crypto.load_privatekey(
+ OpenSSL.crypto.FILETYPE_PEM, key_file.read())
+
+ def test_redirect_enhance(self):
+ # Test that we successfully add a redirect when there is
+ # a listen directive
+ expected = UnspacedList(_redirect_block_for_domain("www.example.com"))[0]
+
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ self.config.enhance("www.example.com", "redirect")
+
+ generated_conf = self.config.parser.parsed[example_conf]
+ self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
+
+ # Test that we successfully add a redirect when there is
+ # no listen directive
+ migration_conf = self.config.parser.abs_path('sites-enabled/migration.com')
+ self.config.enhance("migration.com", "redirect")
+
+ expected = UnspacedList(_redirect_block_for_domain("migration.com"))[0]
+
+ generated_conf = self.config.parser.parsed[migration_conf]
+ self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
+
+ def test_split_for_redirect(self):
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ self.config.deploy_cert(
+ "example.org",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+ self.config.enhance("www.example.com", "redirect")
+ generated_conf = self.config.parser.parsed[example_conf]
+ self.assertEqual(
+ [[['server'], [
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'], [],
+ ['listen', '5001', 'ssl'], ['#', ' managed by Certbot'],
+ ['ssl_certificate', 'example/fullchain.pem'], ['#', ' managed by Certbot'],
+ ['ssl_certificate_key', 'example/key.pem'], ['#', ' managed by Certbot'],
+ ['include', self.config.mod_ssl_conf], ['#', ' managed by Certbot'],
+ ['ssl_dhparam', self.config.ssl_dhparams], ['#', ' managed by Certbot'],
+ [], []]],
+ [['server'], [
+ [['if', '($host', '=', 'www.example.com)'], [
+ ['return', '301', 'https://$host$request_uri']]],
+ ['#', ' managed by Certbot'], [],
+ ['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+ ['return', '404'], ['#', ' managed by Certbot'], [], [], []]]],
+ generated_conf)
+
+ def test_split_for_headers(self):
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ self.config.deploy_cert(
+ "example.org",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+ self.config.enhance("www.example.com", "ensure-http-header", "Strict-Transport-Security")
+ generated_conf = self.config.parser.parsed[example_conf]
+ self.assertEqual(
+ [[['server'], [
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'], [],
+ ['listen', '5001', 'ssl'], ['#', ' managed by Certbot'],
+ ['ssl_certificate', 'example/fullchain.pem'], ['#', ' managed by Certbot'],
+ ['ssl_certificate_key', 'example/key.pem'], ['#', ' managed by Certbot'],
+ ['include', self.config.mod_ssl_conf], ['#', ' managed by Certbot'],
+ ['ssl_dhparam', self.config.ssl_dhparams], ['#', ' managed by Certbot'],
+ [], [],
+ ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always'],
+ ['#', ' managed by Certbot'],
+ [], []]],
+ [['server'], [
+ ['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+ [], [], []]]],
+ generated_conf)
+
+ def test_http_header_hsts(self):
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ self.config.enhance("www.example.com", "ensure-http-header",
+ "Strict-Transport-Security")
+ expected = ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always']
+ generated_conf = self.config.parser.parsed[example_conf]
+ self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
+
+ def test_multiple_headers_hsts(self):
+ headers_conf = self.config.parser.abs_path('sites-enabled/headers.com')
+ self.config.enhance("headers.com", "ensure-http-header",
+ "Strict-Transport-Security")
+ expected = ['add_header', 'Strict-Transport-Security', '"max-age=31536000"', 'always']
+ generated_conf = self.config.parser.parsed[headers_conf]
+ self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
+
+ def test_http_header_hsts_twice(self):
+ self.config.enhance("www.example.com", "ensure-http-header",
+ "Strict-Transport-Security")
+ self.assertRaises(
+ errors.PluginEnhancementAlreadyPresent,
+ self.config.enhance, "www.example.com",
+ "ensure-http-header", "Strict-Transport-Security")
+
+
+ @mock.patch('certbot_nginx._internal.obj.VirtualHost.contains_list')
+ def test_certbot_redirect_exists(self, mock_contains_list):
+ # Test that we add no redirect statement if there is already a
+ # redirect in the block that is managed by certbot
+ # Has a certbot redirect
+ mock_contains_list.return_value = True
+ with mock.patch("certbot_nginx._internal.configurator.logger") as mock_logger:
+ self.config.enhance("www.example.com", "redirect")
+ self.assertEqual(mock_logger.info.call_args[0][0],
+ "Traffic on port %s already redirecting to ssl in %s")
+
+ def test_redirect_dont_enhance(self):
+ # Test that we don't accidentally add redirect to ssl-only block
+ with mock.patch("certbot_nginx._internal.configurator.logger") as mock_logger:
+ self.config.enhance("geese.com", "redirect")
+ self.assertEqual(mock_logger.info.call_args[0][0],
+ 'No matching insecure server blocks listening on port %s found.')
+
+ def test_double_redirect(self):
+ # Test that we add one redirect for each domain
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ self.config.enhance("example.com", "redirect")
+ self.config.enhance("example.org", "redirect")
+
+ expected1 = UnspacedList(_redirect_block_for_domain("example.com"))[0]
+ expected2 = UnspacedList(_redirect_block_for_domain("example.org"))[0]
+
+ generated_conf = self.config.parser.parsed[example_conf]
+ self.assertTrue(util.contains_at_depth(generated_conf, expected1, 2))
+ self.assertTrue(util.contains_at_depth(generated_conf, expected2, 2))
+
+ def test_staple_ocsp_bad_version(self):
+ self.config.version = (1, 3, 1)
+ self.assertRaises(errors.PluginError, self.config.enhance,
+ "www.example.com", "staple-ocsp", "chain_path")
+
+ def test_staple_ocsp_no_chain_path(self):
+ self.assertRaises(errors.PluginError, self.config.enhance,
+ "www.example.com", "staple-ocsp", None)
+
+ def test_staple_ocsp_internal_error(self):
+ self.config.enhance("www.example.com", "staple-ocsp", "chain_path")
+ # error is raised because the server block has conflicting directives
+ self.assertRaises(errors.PluginError, self.config.enhance,
+ "www.example.com", "staple-ocsp", "different_path")
+
+ def test_staple_ocsp(self):
+ chain_path = "example/chain.pem"
+ self.config.enhance("www.example.com", "staple-ocsp", chain_path)
+
+ example_conf = self.config.parser.abs_path('sites-enabled/example.com')
+ generated_conf = self.config.parser.parsed[example_conf]
+
+ self.assertTrue(util.contains_at_depth(
+ generated_conf,
+ ['ssl_trusted_certificate', 'example/chain.pem'], 2))
+ self.assertTrue(util.contains_at_depth(
+ generated_conf, ['ssl_stapling', 'on'], 2))
+ self.assertTrue(util.contains_at_depth(
+ generated_conf, ['ssl_stapling_verify', 'on'], 2))
+
+ def test_deploy_no_match_default_set(self):
+ default_conf = self.config.parser.abs_path('sites-enabled/default')
+ foo_conf = self.config.parser.abs_path('foo.conf')
+ del self.config.parser.parsed[foo_conf][2][1][0][1][0] # remove default_server
+ self.config.version = (1, 3, 1)
+
+ self.config.deploy_cert(
+ "www.nomatch.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+ self.config.save()
+
+ self.config.parser.load()
+
+ parsed_default_conf = util.filter_comments(self.config.parser.parsed[default_conf])
+
+ self.assertEqual([[['server'],
+ [['listen', 'myhost', 'default_server'],
+ ['listen', 'otherhost', 'default_server'],
+ ['server_name', '"www.example.org"'],
+ [['location', '/'],
+ [['root', 'html'],
+ ['index', 'index.html', 'index.htm']]]]],
+ [['server'],
+ [['listen', 'myhost'],
+ ['listen', 'otherhost'],
+ ['server_name', 'www.nomatch.com'],
+ [['location', '/'],
+ [['root', 'html'],
+ ['index', 'index.html', 'index.htm']]],
+ ['listen', '5001', 'ssl'],
+ ['ssl_certificate', 'example/fullchain.pem'],
+ ['ssl_certificate_key', 'example/key.pem'],
+ ['include', self.config.mod_ssl_conf],
+ ['ssl_dhparam', self.config.ssl_dhparams]]]],
+ parsed_default_conf)
+
+ self.config.deploy_cert(
+ "nomatch.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+ self.config.save()
+
+ self.config.parser.load()
+
+ parsed_default_conf = util.filter_comments(self.config.parser.parsed[default_conf])
+
+ self.assertTrue(util.contains_at_depth(parsed_default_conf, "nomatch.com", 3))
+
+ def test_deploy_no_match_default_set_multi_level_path(self):
+ default_conf = self.config.parser.abs_path('sites-enabled/default')
+ foo_conf = self.config.parser.abs_path('foo.conf')
+ del self.config.parser.parsed[default_conf][0][1][0]
+ del self.config.parser.parsed[default_conf][0][1][0]
+ self.config.version = (1, 3, 1)
+
+ self.config.deploy_cert(
+ "www.nomatch.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+ self.config.save()
+
+ self.config.parser.load()
+
+ parsed_foo_conf = util.filter_comments(self.config.parser.parsed[foo_conf])
+
+ self.assertEqual([['server'],
+ [['listen', '*:80', 'ssl'],
+ ['server_name', 'www.nomatch.com'],
+ ['root', '/home/ubuntu/sites/foo/'],
+ [['location', '/status'], [[['types'], [['image/jpeg', 'jpg']]]]],
+ [['location', '~', 'case_sensitive\\.php$'], [['index', 'index.php'],
+ ['root', '/var/root']]],
+ [['location', '~*', 'case_insensitive\\.php$'], []],
+ [['location', '=', 'exact_match\\.php$'], []],
+ [['location', '^~', 'ignore_regex\\.php$'], []],
+ ['ssl_certificate', 'example/fullchain.pem'],
+ ['ssl_certificate_key', 'example/key.pem']]],
+ parsed_foo_conf[1][1][1])
+
+ def test_deploy_no_match_no_default_set(self):
+ default_conf = self.config.parser.abs_path('sites-enabled/default')
+ foo_conf = self.config.parser.abs_path('foo.conf')
+ del self.config.parser.parsed[default_conf][0][1][0]
+ del self.config.parser.parsed[default_conf][0][1][0]
+ del self.config.parser.parsed[foo_conf][2][1][0][1][0]
+ self.config.version = (1, 3, 1)
+
+ self.assertRaises(errors.MisconfigurationError, self.config.deploy_cert,
+ "www.nomatch.com", "example/cert.pem", "example/key.pem",
+ "example/chain.pem", "example/fullchain.pem")
+
+ def test_deploy_no_match_fail_multiple_defaults(self):
+ self.config.version = (1, 3, 1)
+ self.assertRaises(errors.MisconfigurationError, self.config.deploy_cert,
+ "www.nomatch.com", "example/cert.pem", "example/key.pem",
+ "example/chain.pem", "example/fullchain.pem")
+
+ def test_deploy_no_match_multiple_defaults_ok(self):
+ foo_conf = self.config.parser.abs_path('foo.conf')
+ self.config.parser.parsed[foo_conf][2][1][0][1][0][1] = '*:5001'
+ self.config.version = (1, 3, 1)
+ self.config.deploy_cert("www.nomatch.com", "example/cert.pem", "example/key.pem",
+ "example/chain.pem", "example/fullchain.pem")
+
+ def test_deploy_no_match_add_redirect(self):
+ default_conf = self.config.parser.abs_path('sites-enabled/default')
+ foo_conf = self.config.parser.abs_path('foo.conf')
+ del self.config.parser.parsed[foo_conf][2][1][0][1][0] # remove default_server
+ self.config.version = (1, 3, 1)
+
+ self.config.deploy_cert(
+ "www.nomatch.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+
+ self.config.deploy_cert(
+ "nomatch.com",
+ "example/cert.pem",
+ "example/key.pem",
+ "example/chain.pem",
+ "example/fullchain.pem")
+
+ self.config.enhance("www.nomatch.com", "redirect")
+
+ self.config.save()
+
+ self.config.parser.load()
+
+ expected = UnspacedList(_redirect_block_for_domain("www.nomatch.com"))[0]
+
+ generated_conf = self.config.parser.parsed[default_conf]
+ self.assertTrue(util.contains_at_depth(generated_conf, expected, 2))
+
+ @mock.patch('certbot.reverter.logger')
+ @mock.patch('certbot_nginx._internal.parser.NginxParser.load')
+ def test_parser_reload_after_config_changes(self, mock_parser_load, unused_mock_logger):
+ self.config.recovery_routine()
+ self.config.revert_challenge_config()
+ self.config.rollback_checkpoints()
+ self.assertTrue(mock_parser_load.call_count == 3)
+
+ def test_choose_vhosts_wildcard(self):
+ # pylint: disable=protected-access
+ mock_path = "certbot_nginx._internal.display_ops.select_vhost_multiple"
+ with mock.patch(mock_path) as mock_select_vhs:
+ vhost = [x for x in self.config.parser.get_vhosts()
+ if 'summer.com' in x.names][0]
+ mock_select_vhs.return_value = [vhost]
+ vhs = self.config._choose_vhosts_wildcard("*.com",
+ prefer_ssl=True)
+ # Check that the dialog was called with migration.com
+ self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
+
+ # And the actual returned values
+ self.assertEqual(len(vhs), 1)
+ self.assertEqual(vhs[0], vhost)
+
+ def test_choose_vhosts_wildcard_redirect(self):
+ # pylint: disable=protected-access
+ mock_path = "certbot_nginx._internal.display_ops.select_vhost_multiple"
+ with mock.patch(mock_path) as mock_select_vhs:
+ vhost = [x for x in self.config.parser.get_vhosts()
+ if 'summer.com' in x.names][0]
+ mock_select_vhs.return_value = [vhost]
+ vhs = self.config._choose_vhosts_wildcard("*.com",
+ prefer_ssl=False)
+ # Check that the dialog was called with migration.com
+ self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
+
+ # And the actual returned values
+ self.assertEqual(len(vhs), 1)
+ self.assertEqual(vhs[0], vhost)
+
+ def test_deploy_cert_wildcard(self):
+ # pylint: disable=protected-access
+ mock_choose_vhosts = mock.MagicMock()
+ vhost = [x for x in self.config.parser.get_vhosts()
+ if 'geese.com' in x.names][0]
+ mock_choose_vhosts.return_value = [vhost]
+ self.config._choose_vhosts_wildcard = mock_choose_vhosts
+ mock_d = "certbot_nginx._internal.configurator.NginxConfigurator._deploy_cert"
+ with mock.patch(mock_d) as mock_dep:
+ self.config.deploy_cert("*.com", "/tmp/path",
+ "/tmp/path", "/tmp/path", "/tmp/path")
+ self.assertTrue(mock_dep.called)
+ self.assertEqual(len(mock_dep.call_args_list), 1)
+ self.assertEqual(vhost, mock_dep.call_args_list[0][0][0])
+
+ @mock.patch("certbot_nginx._internal.display_ops.select_vhost_multiple")
+ def test_deploy_cert_wildcard_no_vhosts(self, mock_dialog):
+ # pylint: disable=protected-access
+ mock_dialog.return_value = []
+ self.assertRaises(errors.PluginError,
+ self.config.deploy_cert,
+ "*.wild.cat", "/tmp/path", "/tmp/path",
+ "/tmp/path", "/tmp/path")
+
+ @mock.patch("certbot_nginx._internal.display_ops.select_vhost_multiple")
+ def test_enhance_wildcard_ocsp_after_install(self, mock_dialog):
+ # pylint: disable=protected-access
+ vhost = [x for x in self.config.parser.get_vhosts()
+ if 'geese.com' in x.names][0]
+ self.config._wildcard_vhosts["*.com"] = [vhost]
+ self.config.enhance("*.com", "staple-ocsp", "example/chain.pem")
+ self.assertFalse(mock_dialog.called)
+
+ @mock.patch("certbot_nginx._internal.display_ops.select_vhost_multiple")
+ def test_enhance_wildcard_redirect_or_ocsp_no_install(self, mock_dialog):
+ vhost = [x for x in self.config.parser.get_vhosts()
+ if 'summer.com' in x.names][0]
+ mock_dialog.return_value = [vhost]
+ self.config.enhance("*.com", "staple-ocsp", "example/chain.pem")
+ self.assertTrue(mock_dialog.called)
+
+ @mock.patch("certbot_nginx._internal.display_ops.select_vhost_multiple")
+ def test_enhance_wildcard_double_redirect(self, mock_dialog):
+ # pylint: disable=protected-access
+ vhost = [x for x in self.config.parser.get_vhosts()
+ if 'summer.com' in x.names][0]
+ self.config._wildcard_redirect_vhosts["*.com"] = [vhost]
+ self.config.enhance("*.com", "redirect")
+ self.assertFalse(mock_dialog.called)
+
+ def test_choose_vhosts_wildcard_no_ssl_filter_port(self):
+ # pylint: disable=protected-access
+ mock_path = "certbot_nginx._internal.display_ops.select_vhost_multiple"
+ with mock.patch(mock_path) as mock_select_vhs:
+ mock_select_vhs.return_value = []
+ self.config._choose_vhosts_wildcard("*.com",
+ prefer_ssl=False,
+ no_ssl_filter_port='80')
+ # Check that the dialog was called with only port 80 vhosts
+ self.assertEqual(len(mock_select_vhs.call_args[0][0]), 5)
+
+
+class InstallSslOptionsConfTest(util.NginxTest):
+ """Test that the options-ssl-nginx.conf file is installed and updated properly."""
+
+ def setUp(self):
+ super(InstallSslOptionsConfTest, self).setUp()
+
+ self.config = self.get_nginx_configurator(
+ self.config_path, self.config_dir, self.work_dir, self.logs_dir)
+
+ def _call(self):
+ self.config.install_ssl_options_conf(self.config.mod_ssl_conf,
+ self.config.updated_mod_ssl_conf_digest)
+
+ def _current_ssl_options_hash(self):
+ 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))
+ self.assertEqual(crypto_util.sha256sum(self.config.mod_ssl_conf),
+ self._current_ssl_options_hash())
+
+ def test_no_file(self):
+ # prepare should have placed a file there
+ self._assert_current_file()
+ os.remove(self.config.mod_ssl_conf)
+ self.assertFalse(os.path.isfile(self.config.mod_ssl_conf))
+ self._call()
+ self._assert_current_file()
+
+ def test_current_file(self):
+ self._assert_current_file()
+ 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._internal.constants import ALL_SSL_OPTIONS_HASHES
+ 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._internal.constants import ALL_SSL_OPTIONS_HASHES
+ 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()
+
+ 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")
+ with mock.patch("certbot.plugins.common.logger") as mock_logger:
+ self._call()
+ self.assertFalse(mock_logger.warning.called)
+ self.assertTrue(os.path.isfile(self.config.mod_ssl_conf))
+ 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())
+
+ def test_manually_modified_past_file_warns(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")
+ with open(self.config.updated_mod_ssl_conf_digest, "w") as f:
+ f.write("hashofanoldversion")
+ with mock.patch("certbot.plugins.common.logger") as mock_logger:
+ self._call()
+ 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(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:
+ self._call()
+ self.assertFalse(mock_logger.warning.called)
+
+ def test_current_file_hash_in_all_hashes(self):
+ from certbot_nginx._internal.constants import ALL_SSL_OPTIONS_HASHES
+ self.assertTrue(self._current_ssl_options_hash() in ALL_SSL_OPTIONS_HASHES,
+ "Constants.ALL_SSL_OPTIONS_HASHES must be appended"
+ " with the sha256 hash of self.config.mod_ssl_conf when it is updated.")
+
+ def test_ssl_config_files_hash_in_all_hashes(self):
+ """
+ It is really critical that all TLS Nginx config files have their SHA256 hash registered in
+ constants.ALL_SSL_OPTIONS_HASHES. Otherwise Certbot will mistakenly assume that the config
+ file has been manually edited by the user, and will refuse to update it.
+ This test ensures that all necessary hashes are present.
+ """
+ from certbot_nginx._internal.constants import ALL_SSL_OPTIONS_HASHES
+ import pkg_resources
+ all_files = [
+ pkg_resources.resource_filename("certbot_nginx",
+ os.path.join("_internal", "tls_configs", x))
+ for x in ("options-ssl-nginx.conf",
+ "options-ssl-nginx-old.conf",
+ "options-ssl-nginx-tls12-only.conf")
+ ]
+ self.assertTrue(all_files)
+ for one_file in all_files:
+ file_hash = crypto_util.sha256sum(one_file)
+ self.assertTrue(file_hash in ALL_SSL_OPTIONS_HASHES,
+ "Constants.ALL_SSL_OPTIONS_HASHES must be appended with the sha256 "
+ "hash of {0} when it is updated.".format(one_file))
+
+ def test_nginx_version_uses_correct_config(self):
+ self.config.version = (1, 5, 8)
+ self.config.openssl_version = "1.0.2g" # shouldn't matter
+ 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.config.openssl_version = "1.0.2l"
+ self.assertEqual(os.path.basename(self.config.mod_ssl_conf_src),
+ "options-ssl-nginx-tls12-only.conf")
+ self._call()
+ self._assert_current_file()
+ self.config.version = (1, 13, 0)
+ self.assertEqual(os.path.basename(self.config.mod_ssl_conf_src),
+ "options-ssl-nginx.conf")
+ self._call()
+ self._assert_current_file()
+ self.config.version = (1, 13, 0)
+ self.config.openssl_version = "1.0.2k"
+ self.assertEqual(os.path.basename(self.config.mod_ssl_conf_src),
+ "options-ssl-nginx-tls13-session-tix-on.conf")
+
+
+class DetermineDefaultServerRootTest(certbot_test_util.ConfigTestCase):
+ """Tests for certbot_nginx._internal.configurator._determine_default_server_root."""
+
+ def _call(self):
+ from certbot_nginx._internal.configurator import _determine_default_server_root
+ return _determine_default_server_root()
+
+ @mock.patch.dict(os.environ, {"CERTBOT_DOCS": "1"})
+ def test_docs_value(self):
+ self._test(expect_both_values=True)
+
+ @mock.patch.dict(os.environ, {})
+ def test_real_values(self):
+ self._test(expect_both_values=False)
+
+ def _test(self, expect_both_values):
+ server_root = self._call()
+
+ if expect_both_values:
+ self.assertIn("/usr/local/etc/nginx", server_root)
+ self.assertIn("/etc/nginx", server_root)
+ else:
+ self.assertTrue(server_root in ("/etc/nginx", "/usr/local/etc/nginx"))
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/display_ops_test.py b/certbot-nginx/tests/display_ops_test.py
new file mode 100644
index 000000000..377255441
--- /dev/null
+++ b/certbot-nginx/tests/display_ops_test.py
@@ -0,0 +1,42 @@
+"""Test certbot_nginx._internal.display_ops."""
+import unittest
+
+from certbot.display import util as display_util
+from certbot.tests import util as certbot_util
+from certbot_nginx._internal import parser
+from certbot_nginx._internal.display_ops import select_vhost_multiple
+import test_util as util
+
+
+class SelectVhostMultiTest(util.NginxTest):
+ """Tests for certbot_nginx._internal.display_ops.select_vhost_multiple."""
+
+ def setUp(self):
+ super(SelectVhostMultiTest, self).setUp()
+ nparser = parser.NginxParser(self.config_path)
+ self.vhosts = nparser.get_vhosts()
+
+ def test_select_no_input(self):
+ self.assertFalse(select_vhost_multiple([]))
+
+ @certbot_util.patch_get_utility()
+ def test_select_correct(self, mock_util):
+ mock_util().checklist.return_value = (
+ display_util.OK, [self.vhosts[3].display_repr(),
+ self.vhosts[2].display_repr()])
+ vhs = select_vhost_multiple([self.vhosts[3],
+ self.vhosts[2],
+ self.vhosts[1]])
+ self.assertTrue(self.vhosts[2] in vhs)
+ self.assertTrue(self.vhosts[3] in vhs)
+ self.assertFalse(self.vhosts[1] in vhs)
+
+ @certbot_util.patch_get_utility()
+ def test_select_cancel(self, mock_util):
+ mock_util().checklist.return_value = (display_util.CANCEL, "whatever")
+ vhs = select_vhost_multiple([self.vhosts[2], self.vhosts[3]])
+ self.assertFalse(vhs)
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/http_01_test.py b/certbot-nginx/tests/http_01_test.py
new file mode 100644
index 000000000..6418a8841
--- /dev/null
+++ b/certbot-nginx/tests/http_01_test.py
@@ -0,0 +1,143 @@
+"""Tests for certbot_nginx._internal.http_01"""
+import unittest
+
+import josepy as jose
+import mock
+import six
+
+from acme import challenges
+from certbot import achallenges
+from certbot.tests import acme_util
+from certbot.tests import util as test_util
+from certbot_nginx._internal.obj import Addr
+import test_util as util
+
+AUTH_KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem"))
+
+
+class HttpPerformTest(util.NginxTest):
+ """Test the NginxHttp01 challenge."""
+
+ account_key = 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 = self.get_nginx_configurator(
+ self.config_path, self.config_dir, self.work_dir, self.logs_dir)
+
+ from certbot_nginx._internal import http_01
+ self.http01 = http_01.NginxHttp01(config)
+
+ def test_perform0(self):
+ responses = self.http01.perform()
+ self.assertEqual([], responses)
+
+ @mock.patch("certbot_nginx._internal.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))
+
+ http_responses = self.http01.perform()
+
+ self.assertEqual(len(http_responses), 4)
+ for i in six.moves.range(4):
+ self.assertEqual(http_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')]))
+
+ @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info")
+ def test_default_listen_addresses_no_memoization(self, ipv6_info):
+ # pylint: disable=protected-access
+ ipv6_info.return_value = (True, True)
+ self.http01._default_listen_addresses()
+ self.assertEqual(ipv6_info.call_count, 1)
+ ipv6_info.return_value = (False, False)
+ self.http01._default_listen_addresses()
+ self.assertEqual(ipv6_info.call_count, 2)
+
+ @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info")
+ def test_default_listen_addresses_t_t(self, ipv6_info):
+ # pylint: disable=protected-access
+ ipv6_info.return_value = (True, True)
+ addrs = self.http01._default_listen_addresses()
+ http_addr = Addr.fromstring("80")
+ http_ipv6_addr = Addr.fromstring("[::]:80")
+ self.assertEqual(addrs, [http_addr, http_ipv6_addr])
+
+ @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info")
+ def test_default_listen_addresses_t_f(self, ipv6_info):
+ # pylint: disable=protected-access
+ ipv6_info.return_value = (True, False)
+ addrs = self.http01._default_listen_addresses()
+ http_addr = Addr.fromstring("80")
+ http_ipv6_addr = Addr.fromstring("[::]:80 ipv6only=on")
+ self.assertEqual(addrs, [http_addr, http_ipv6_addr])
+
+ @mock.patch("certbot_nginx._internal.configurator.NginxConfigurator.ipv6_info")
+ def test_default_listen_addresses_f_f(self, ipv6_info):
+ # pylint: disable=protected-access
+ ipv6_info.return_value = (False, False)
+ addrs = self.http01._default_listen_addresses()
+ http_addr = Addr.fromstring("80")
+ self.assertEqual(addrs, [http_addr])
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/nginxparser_test.py b/certbot-nginx/tests/nginxparser_test.py
new file mode 100644
index 000000000..a5212078f
--- /dev/null
+++ b/certbot-nginx/tests/nginxparser_test.py
@@ -0,0 +1,443 @@
+"""Test for certbot_nginx._internal.nginxparser."""
+import copy
+import operator
+import tempfile
+import unittest
+
+from pyparsing import ParseException
+
+from certbot_nginx._internal.nginxparser import dump
+from certbot_nginx._internal.nginxparser import dumps
+from certbot_nginx._internal.nginxparser import load
+from certbot_nginx._internal.nginxparser import loads
+from certbot_nginx._internal.nginxparser import RawNginxParser
+from certbot_nginx._internal.nginxparser import UnspacedList
+import test_util as util
+
+FIRST = operator.itemgetter(0)
+
+
+class TestRawNginxParser(unittest.TestCase):
+ """Test the raw low-level Nginx config parser."""
+
+ def test_assignments(self):
+ parsed = RawNginxParser.assignment.parseString('root /test;').asList()
+ self.assertEqual(parsed, ['root', ' ', '/test'])
+ parsed = RawNginxParser.assignment.parseString('root /test;foo bar;').asList()
+ self.assertEqual(parsed, ['root', ' ', '/test'], ['foo', ' ', 'bar'])
+
+ def test_blocks(self):
+ parsed = RawNginxParser.block.parseString('foo {}').asList()
+ self.assertEqual(parsed, [['foo', ' '], []])
+ parsed = RawNginxParser.block.parseString('location /foo{}').asList()
+ self.assertEqual(parsed, [['location', ' ', '/foo'], []])
+ parsed = RawNginxParser.block.parseString('foo { bar foo ; }').asList()
+ self.assertEqual(parsed, [['foo', ' '], [[' ', 'bar', ' ', 'foo', ' '], ' ']])
+
+ def test_nested_blocks(self):
+ parsed = RawNginxParser.block.parseString('foo { bar {} }').asList()
+ block, content = parsed
+ self.assertEqual(FIRST(content), [[' ', 'bar', ' '], []])
+ self.assertEqual(FIRST(block), 'foo')
+
+ def test_dump_as_string(self):
+ dumped = dumps(UnspacedList([
+ ['user', ' ', 'www-data'],
+ [['\n', 'server', ' '], [
+ ['\n ', 'listen', ' ', '80'],
+ ['\n ', 'server_name', ' ', 'foo.com'],
+ ['\n ', 'root', ' ', '/home/ubuntu/sites/foo/'],
+ [['\n\n ', 'location', ' ', '/status', ' '], [
+ ['\n ', 'check_status', ''],
+ [['\n\n ', 'types', ' '],
+ [['\n ', 'image/jpeg', ' ', 'jpg']]],
+ ]]
+ ]]]))
+
+ self.assertEqual(dumped.split('\n'),
+ 'user www-data;\n'
+ 'server {\n'
+ ' listen 80;\n'
+ ' server_name foo.com;\n'
+ ' root /home/ubuntu/sites/foo/;\n'
+ '\n'
+ ' location /status {\n'
+ ' check_status;\n'
+ '\n'
+ ' types {\n'
+ ' image/jpeg jpg;}}}'.split('\n'))
+
+ def test_parse_from_file(self):
+ with open(util.get_data_filename('foo.conf')) as handle:
+ parsed = util.filter_comments(load(handle))
+ self.assertEqual(
+ parsed,
+ [['user', 'www-data'],
+ [['http'],
+ [[['server'], [
+ ['listen', '*:80', 'default_server', 'ssl'],
+ ['server_name', '*.www.foo.com', '*.www.example.com'],
+ ['root', '/home/ubuntu/sites/foo/'],
+ [['location', '/status'], [
+ [['types'], [['image/jpeg', 'jpg']]],
+ ]],
+ [['location', '~', r'case_sensitive\.php$'], [
+ ['index', 'index.php'],
+ ['root', '/var/root'],
+ ]],
+ [['location', '~*', r'case_insensitive\.php$'], []],
+ [['location', '=', r'exact_match\.php$'], []],
+ [['location', '^~', r'ignore_regex\.php$'], []]
+ ]]]]]
+ )
+
+ def test_parse_from_file2(self):
+ with open(util.get_data_filename('edge_cases.conf')) as handle:
+ parsed = util.filter_comments(load(handle))
+ self.assertEqual(
+ parsed,
+ [[['server'], [['server_name', 'simple']]],
+ [['server'],
+ [['server_name', 'with.if'],
+ [['location', '~', '^/services/.+$'],
+ [[['if', '($request_filename', '~*', '\\.(ttf|woff)$)'],
+ [['add_header', 'Access-Control-Allow-Origin', '"*"']]]]]]],
+ [['server'],
+ [['server_name', 'with.complicated.headers'],
+ [['location', '~*', '\\.(?:gif|jpe?g|png)$'],
+ [['add_header', 'Pragma', 'public'],
+ ['add_header',
+ 'Cache-Control', '\'public, must-revalidate, proxy-revalidate\'',
+ '"test,;{}"', 'foo'],
+ ['blah', '"hello;world"'],
+ ['try_files', '$uri', '@rewrites']]]]]])
+
+ def test_parse_from_file3(self):
+ with open(util.get_data_filename('multiline_quotes.conf')) as handle:
+ parsed = util.filter_comments(load(handle))
+ self.assertEqual(
+ parsed,
+ [[['http'],
+ [[['server'],
+ [['listen', '*:443'],
+ [['location', '/'],
+ [['body_filter_by_lua',
+ '\'ngx.ctx.buffered = (ngx.ctx.buffered or "")'
+ ' .. string.sub(ngx.arg[1], 1, 1000)\n'
+ ' '
+ 'if ngx.arg[2] then\n'
+ ' '
+ 'ngx.var.resp_body = ngx.ctx.buffered\n'
+ ' end\'']]]]]]]])
+
+ def test_abort_on_parse_failure(self):
+ with open(util.get_data_filename('broken.conf')) as handle:
+ self.assertRaises(ParseException, load, handle)
+
+ def test_dump_as_file(self):
+ with open(util.get_data_filename('nginx.conf')) as handle:
+ parsed = load(handle)
+ parsed[-1][-1].append(UnspacedList([['server'],
+ [['listen', ' ', '443', ' ', 'ssl'],
+ ['server_name', ' ', 'localhost'],
+ ['ssl_certificate', ' ', 'cert.pem'],
+ ['ssl_certificate_key', ' ', 'cert.key'],
+ ['ssl_session_cache', ' ', 'shared:SSL:1m'],
+ ['ssl_session_timeout', ' ', '5m'],
+ ['ssl_ciphers', ' ', 'HIGH:!aNULL:!MD5'],
+ [['location', ' ', '/'],
+ [['root', ' ', 'html'],
+ ['index', ' ', 'index.html', ' ', 'index.htm']]]]]))
+
+ with tempfile.TemporaryFile(mode='w+t') as f:
+ dump(parsed, f)
+ f.seek(0)
+ parsed_new = load(f)
+ self.assertEqual(parsed, parsed_new)
+
+ def test_comments(self):
+ with open(util.get_data_filename('minimalistic_comments.conf')) as handle:
+ parsed = load(handle)
+
+ with tempfile.TemporaryFile(mode='w+t') as f:
+ dump(parsed, f)
+ f.seek(0)
+ parsed_new = load(f)
+
+ self.assertEqual(parsed, parsed_new)
+ self.assertEqual(parsed_new, [
+ ['#', " Use bar.conf when it's a full moon!"],
+ ['include', 'foo.conf'],
+ ['#', ' Kilroy was here'],
+ ['check_status'],
+ [['server'],
+ [['#', ''],
+ ['#', " Don't forget to open up your firewall!"],
+ ['#', ''],
+ ['listen', '1234'],
+ ['#', ' listen 80;']]],
+ ])
+
+ def test_issue_518(self):
+ parsed = loads('if ($http_accept ~* "webp") { set $webp "true"; }')
+
+ self.assertEqual(parsed, [
+ [['if', '($http_accept', '~*', '"webp")'],
+ [['set', '$webp', '"true"']]]
+ ])
+
+ def test_comment_in_block(self):
+ parsed = loads("""http {
+ # server{
+ }""")
+
+ self.assertEqual(parsed, [
+ [['http'],
+ [['#', ' server{']]]
+ ])
+
+ def test_access_log(self):
+ # see issue #3798
+ parsed = loads('access_log syslog:server=unix:/dev/log,facility=auth,'
+ 'tag=nginx_post,severity=info custom;')
+
+ self.assertEqual(parsed, [
+ ['access_log',
+ 'syslog:server=unix:/dev/log,facility=auth,tag=nginx_post,severity=info',
+ 'custom']
+ ])
+
+ def test_add_header(self):
+ # see issue #3798
+ parsed = loads('add_header Cache-Control no-cache,no-store,must-revalidate,max-age=0;')
+
+ self.assertEqual(parsed, [
+ ['add_header', 'Cache-Control', 'no-cache,no-store,must-revalidate,max-age=0']
+ ])
+
+ def test_map_then_assignment_in_block(self):
+ # see issue #3798
+ test_str = """http {
+ map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+ "~Opera Mini" 1;
+ *.example.com 1;
+ }
+ one;
+ }"""
+ parsed = loads(test_str)
+ self.assertEqual(parsed, [
+ [['http'], [
+ [['map', '$http_upgrade', '$connection_upgrade'], [
+ ['default', 'upgrade'],
+ ["''", 'close'],
+ ['"~Opera Mini"', '1'],
+ ['*.example.com', '1']
+ ]],
+ ['one']
+ ]]
+ ])
+
+ def test_variable_name(self):
+ parsed = loads('try_files /typo3temp/tx_ncstaticfilecache/'
+ '$host${request_uri}index.html @nocache;')
+
+ self.assertEqual(parsed, [
+ ['try_files',
+ '/typo3temp/tx_ncstaticfilecache/$host${request_uri}index.html',
+ '@nocache']
+ ])
+
+ def test_weird_blocks(self):
+ test = r"""
+ if ($http_user_agent ~ MSIE) {
+ rewrite ^(.*)$ /msie/$1 break;
+ }
+
+ if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
+ set $id $1;
+ }
+
+ if ($request_method = POST) {
+ return 405;
+ }
+
+ if ($request_method) {
+ return 403;
+ }
+
+ if ($args ~ post=140){
+ rewrite ^ http://example.com/;
+ }
+
+ location ~ ^/users/(.+\.(?:gif|jpe?g|png))$ {
+ alias /data/w3/images/$1;
+ }
+
+ proxy_set_header X-Origin-URI ${scheme}://${http_host}/$request_uri;
+ """
+ parsed = loads(test)
+ self.assertEqual(parsed, [[['if', '($http_user_agent', '~', 'MSIE)'],
+ [['rewrite', '^(.*)$', '/msie/$1', 'break']]],
+ [['if', '($http_cookie', '~*', '"id=([^;]+)(?:;|$)")'], [['set', '$id', '$1']]],
+ [['if', '($request_method', '=', 'POST)'], [['return', '405']]],
+ [['if', '($request_method)'],
+ [['return', '403']]], [['if', '($args', '~', 'post=140)'],
+ [['rewrite', '^', 'http://example.com/']]],
+ [['location', '~', '^/users/(.+\\.(?:gif|jpe?g|png))$'],
+ [['alias', '/data/w3/images/$1']]],
+ ['proxy_set_header', 'X-Origin-URI', '${scheme}://${http_host}/$request_uri']]
+ )
+
+ def test_edge_cases(self):
+ # quotes
+ parsed = loads(r'"hello\""; # blah "heh heh"')
+ self.assertEqual(parsed, [['"hello\\""'], ['#', ' blah "heh heh"']])
+
+ # if with comment
+ parsed = loads("""if ($http_cookie ~* "id=([^;]+)(?:;|$)") { # blah )
+ }""")
+ self.assertEqual(parsed, [[['if', '($http_cookie', '~*', '"id=([^;]+)(?:;|$)")'],
+ [['#', ' blah )']]]])
+
+ # end paren
+ test = """
+ one"test";
+ ("two");
+ "test")red;
+ "test")"blue";
+ "test")"three;
+ (one"test")one;
+ one";
+ one"test;
+ one"test"one;
+ """
+ parsed = loads(test)
+ self.assertEqual(parsed, [
+ ['one"test"'],
+ ['("two")'],
+ ['"test")red'],
+ ['"test")"blue"'],
+ ['"test")"three'],
+ ['(one"test")one'],
+ ['one"'],
+ ['one"test'],
+ ['one"test"one']
+ ])
+ self.assertRaises(ParseException, loads, r'"test"one;') # fails
+ self.assertRaises(ParseException, loads, r'"test;') # fails
+
+ # newlines
+ test = """
+ server_name foo.example.com bar.example.com \
+ baz.example.com qux.example.com;
+ server_name foo.example.com bar.example.com
+ baz.example.com qux.example.com;
+ """
+ parsed = loads(test)
+ self.assertEqual(parsed, [
+ ['server_name', 'foo.example.com', 'bar.example.com',
+ 'baz.example.com', 'qux.example.com'],
+ ['server_name', 'foo.example.com', 'bar.example.com',
+ 'baz.example.com', 'qux.example.com']
+ ])
+
+ # variable weirdness
+ parsed = loads("directive $var ${var} $ ${};")
+ self.assertEqual(parsed, [['directive', '$var', '${var}', '$', '${}']])
+ self.assertRaises(ParseException, loads, "server {server_name test.com};")
+ self.assertEqual(loads("blag${dfgdfg};"), [['blag${dfgdfg}']])
+ self.assertRaises(ParseException, loads, "blag${dfgdf{g};")
+
+
+class TestUnspacedList(unittest.TestCase):
+ """Test the UnspacedList data structure"""
+ def setUp(self):
+ self.a = ["\n ", "things", " ", "quirk"]
+ self.b = ["y", " "]
+ self.l = self.a[:]
+ self.l2 = self.b[:]
+ self.ul = UnspacedList(self.l)
+ self.ul2 = UnspacedList(self.l2)
+
+ def test_construction(self):
+ self.assertEqual(self.ul, ["things", "quirk"])
+ self.assertEqual(self.ul2, ["y"])
+
+ def test_append(self):
+ ul3 = copy.deepcopy(self.ul)
+ ul3.append("wise")
+ self.assertEqual(ul3, ["things", "quirk", "wise"])
+ self.assertEqual(ul3.spaced, self.a + ["wise"])
+
+ def test_add(self):
+ ul3 = self.ul + self.ul2
+ self.assertEqual(ul3, ["things", "quirk", "y"])
+ self.assertEqual(ul3.spaced, self.a + self.b)
+ self.assertEqual(self.ul.spaced, self.a)
+ ul3 = self.ul + self.l2
+ self.assertEqual(ul3, ["things", "quirk", "y"])
+ self.assertEqual(ul3.spaced, self.a + self.b)
+
+ def test_extend(self):
+ ul3 = copy.deepcopy(self.ul)
+ ul3.extend(self.ul2)
+ self.assertEqual(ul3, ["things", "quirk", "y"])
+ self.assertEqual(ul3.spaced, self.a + self.b)
+ self.assertEqual(self.ul.spaced, self.a)
+
+ def test_set(self):
+ ul3 = copy.deepcopy(self.ul)
+ ul3[0] = "zither"
+ l = ["\n ", "zather", "zest"]
+ ul3[1] = UnspacedList(l)
+ self.assertEqual(ul3, ["zither", ["zather", "zest"]])
+ self.assertEqual(ul3.spaced, [self.a[0], "zither", " ", l])
+
+ def test_get(self):
+ self.assertRaises(IndexError, self.ul2.__getitem__, 2)
+ self.assertRaises(IndexError, self.ul2.__getitem__, -3)
+
+ def test_insert(self):
+ x = UnspacedList(
+ [['\n ', 'listen', ' ', '69.50.225.155:9000'],
+ ['\n ', 'listen', ' ', '127.0.0.1'],
+ ['\n ', 'server_name', ' ', '.example.com'],
+ ['\n ', 'server_name', ' ', 'example.*'], '\n',
+ ['listen', ' ', '5001', ' ', 'ssl']])
+ x.insert(5, "FROGZ")
+ self.assertEqual(x,
+ [['listen', '69.50.225.155:9000'], ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'], ['server_name', 'example.*'],
+ ['listen', '5001', 'ssl'], 'FROGZ'])
+ self.assertEqual(x.spaced,
+ [['\n ', 'listen', ' ', '69.50.225.155:9000'],
+ ['\n ', 'listen', ' ', '127.0.0.1'],
+ ['\n ', 'server_name', ' ', '.example.com'],
+ ['\n ', 'server_name', ' ', 'example.*'], '\n',
+ ['listen', ' ', '5001', ' ', 'ssl'],
+ 'FROGZ'])
+
+ def test_rawlists(self):
+ ul3 = copy.deepcopy(self.ul)
+ ul3.insert(0, "some")
+ ul3.append("why")
+ ul3.extend(["did", "whether"])
+ del ul3[2]
+ self.assertEqual(ul3, ["some", "things", "why", "did", "whether"])
+
+ def test_is_dirty(self):
+ self.assertEqual(False, self.ul2.is_dirty())
+ ul3 = UnspacedList([])
+ ul3.append(self.ul)
+ self.assertEqual(False, self.ul.is_dirty())
+ self.assertEqual(True, ul3.is_dirty())
+ ul4 = UnspacedList([[1], [2, 3, 4]])
+ self.assertEqual(False, ul4.is_dirty())
+ ul4[1][2] = 5
+ self.assertEqual(True, ul4.is_dirty())
+
+
+if __name__ == '__main__':
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/obj_test.py b/certbot-nginx/tests/obj_test.py
new file mode 100644
index 000000000..db808229f
--- /dev/null
+++ b/certbot-nginx/tests/obj_test.py
@@ -0,0 +1,228 @@
+"""Test the helper objects in certbot_nginx._internal.obj."""
+import itertools
+import unittest
+
+
+class AddrTest(unittest.TestCase):
+ """Test the Addr class."""
+ def setUp(self):
+ from certbot_nginx._internal.obj import Addr
+ self.addr1 = Addr.fromstring("192.168.1.1")
+ self.addr2 = Addr.fromstring("192.168.1.1:* ssl")
+ self.addr3 = Addr.fromstring("192.168.1.1:80")
+ self.addr4 = Addr.fromstring("*:80 default_server ssl")
+ self.addr5 = Addr.fromstring("myhost")
+ self.addr6 = Addr.fromstring("80 default_server spdy")
+ self.addr7 = Addr.fromstring("unix:/var/run/nginx.sock")
+ self.addr8 = Addr.fromstring("*:80 default ssl")
+
+ def test_fromstring(self):
+ self.assertEqual(self.addr1.get_addr(), "192.168.1.1")
+ self.assertEqual(self.addr1.get_port(), "")
+ self.assertFalse(self.addr1.ssl)
+ self.assertFalse(self.addr1.default)
+
+ self.assertEqual(self.addr2.get_addr(), "192.168.1.1")
+ self.assertEqual(self.addr2.get_port(), "*")
+ self.assertTrue(self.addr2.ssl)
+ self.assertFalse(self.addr2.default)
+
+ self.assertEqual(self.addr3.get_addr(), "192.168.1.1")
+ self.assertEqual(self.addr3.get_port(), "80")
+ self.assertFalse(self.addr3.ssl)
+ self.assertFalse(self.addr3.default)
+
+ self.assertEqual(self.addr4.get_addr(), "*")
+ self.assertEqual(self.addr4.get_port(), "80")
+ self.assertTrue(self.addr4.ssl)
+ self.assertTrue(self.addr4.default)
+
+ self.assertEqual(self.addr5.get_addr(), "myhost")
+ self.assertEqual(self.addr5.get_port(), "")
+ self.assertFalse(self.addr5.ssl)
+ self.assertFalse(self.addr5.default)
+
+ self.assertEqual(self.addr6.get_addr(), "")
+ self.assertEqual(self.addr6.get_port(), "80")
+ self.assertFalse(self.addr6.ssl)
+ self.assertTrue(self.addr6.default)
+
+ self.assertTrue(self.addr8.default)
+
+ self.assertEqual(None, self.addr7)
+
+ def test_str(self):
+ self.assertEqual(str(self.addr1), "192.168.1.1")
+ self.assertEqual(str(self.addr2), "192.168.1.1:* ssl")
+ self.assertEqual(str(self.addr3), "192.168.1.1:80")
+ self.assertEqual(str(self.addr4), "*:80 default_server ssl")
+ self.assertEqual(str(self.addr5), "myhost")
+ self.assertEqual(str(self.addr6), "80 default_server")
+ self.assertEqual(str(self.addr8), "*:80 default_server ssl")
+
+ def test_to_string(self):
+ self.assertEqual(self.addr1.to_string(), "192.168.1.1")
+ self.assertEqual(self.addr2.to_string(), "192.168.1.1:* ssl")
+ self.assertEqual(self.addr3.to_string(), "192.168.1.1:80")
+ self.assertEqual(self.addr4.to_string(), "*:80 default_server ssl")
+ self.assertEqual(self.addr4.to_string(include_default=False), "*:80 ssl")
+ self.assertEqual(self.addr5.to_string(), "myhost")
+ self.assertEqual(self.addr6.to_string(), "80 default_server")
+ self.assertEqual(self.addr6.to_string(include_default=False), "80")
+
+ def test_eq(self):
+ from certbot_nginx._internal.obj import Addr
+ new_addr1 = Addr.fromstring("192.168.1.1 spdy")
+ self.assertEqual(self.addr1, new_addr1)
+ self.assertNotEqual(self.addr1, self.addr2)
+ self.assertFalse(self.addr1 == 3333)
+
+ def test_equivalent_any_addresses(self):
+ from certbot_nginx._internal.obj import Addr
+ any_addresses = ("0.0.0.0:80 default_server ssl",
+ "80 default_server ssl",
+ "*:80 default_server ssl",
+ "80 default ssl")
+ for first, second in itertools.combinations(any_addresses, 2):
+ self.assertEqual(Addr.fromstring(first), Addr.fromstring(second))
+
+ # Also, make sure ports are checked.
+ self.assertNotEqual(Addr.fromstring(any_addresses[0]),
+ Addr.fromstring("0.0.0.0:443 default_server ssl"))
+
+ # And they aren't equivalent to a specified address.
+ for any_address in any_addresses:
+ self.assertNotEqual(
+ Addr.fromstring("192.168.1.2:80 default_server ssl"),
+ Addr.fromstring(any_address))
+
+ def test_set_inclusion(self):
+ from certbot_nginx._internal.obj import Addr
+ set_a = set([self.addr1, self.addr2])
+ addr1b = Addr.fromstring("192.168.1.1")
+ addr2b = Addr.fromstring("192.168.1.1:* ssl")
+ set_b = set([addr1b, addr2b])
+
+ self.assertEqual(set_a, set_b)
+
+
+class VirtualHostTest(unittest.TestCase):
+ """Test the VirtualHost class."""
+ def setUp(self):
+ from certbot_nginx._internal.obj import VirtualHost
+ from certbot_nginx._internal.obj import Addr
+ raw1 = [
+ ['listen', '69.50.225.155:9000'],
+ [['if', '($scheme', '!=', '"https") '],
+ [['return', '301', 'https://$host$request_uri']]
+ ],
+ ['#', ' managed by Certbot']
+ ]
+ self.vhost1 = VirtualHost(
+ "filep",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), raw1, [])
+ raw2 = [
+ ['listen', '69.50.225.155:9000'],
+ [['if', '($scheme', '!=', '"https") '],
+ [['return', '301', 'https://$host$request_uri']]
+ ]
+ ]
+ self.vhost2 = VirtualHost(
+ "filep",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), raw2, [])
+ raw3 = [
+ ['listen', '69.50.225.155:9000'],
+ ['rewrite', '^(.*)$', '$scheme://www.domain.com$1', 'permanent']
+ ]
+ self.vhost3 = VirtualHost(
+ "filep",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), raw3, [])
+ raw4 = [
+ ['listen', '69.50.225.155:9000'],
+ ['server_name', 'return.com']
+ ]
+ self.vhost4 = VirtualHost(
+ "filp",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), raw4, [])
+ raw_has_hsts = [
+ ['listen', '69.50.225.155:9000'],
+ ['server_name', 'return.com'],
+ ['add_header', 'always', 'set', 'Strict-Transport-Security', '\"max-age=31536000\"'],
+ ]
+ self.vhost_has_hsts = VirtualHost(
+ "filep",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), raw_has_hsts, [])
+
+ def test_eq(self):
+ from certbot_nginx._internal.obj import Addr
+ from certbot_nginx._internal.obj import VirtualHost
+ vhost1b = VirtualHost(
+ "filep",
+ set([Addr.fromstring("localhost blah")]), False, False,
+ set(['localhost']), [], [])
+
+ self.assertEqual(vhost1b, self.vhost1)
+ self.assertEqual(str(vhost1b), str(self.vhost1))
+ self.assertFalse(vhost1b == 1234)
+
+ def test_str(self):
+ stringified = '\n'.join(['file: filep', 'addrs: localhost',
+ "names: ['localhost']", 'ssl: False',
+ 'enabled: False'])
+ self.assertEqual(stringified, str(self.vhost1))
+
+ def test_has_header(self):
+ self.assertTrue(self.vhost_has_hsts.has_header('Strict-Transport-Security'))
+ self.assertFalse(self.vhost_has_hsts.has_header('Bogus-Header'))
+ self.assertFalse(self.vhost1.has_header('Strict-Transport-Security'))
+ self.assertFalse(self.vhost1.has_header('Bogus-Header'))
+
+ def test_contains_list(self):
+ from certbot_nginx._internal.obj import VirtualHost
+ from certbot_nginx._internal.obj import Addr
+ from certbot_nginx._internal.configurator import _test_block_from_block
+ test_block = [
+ ['\n ', 'return', ' ', '301', ' ', 'https://$host$request_uri'],
+ ['\n']
+ ]
+ test_needle = _test_block_from_block(test_block)
+ test_haystack = [['listen', '80'], ['root', '/var/www/html'],
+ ['index', 'index.html index.htm index.nginx-debian.html'],
+ ['server_name', 'two.functorkitten.xyz'], ['listen', '443 ssl'],
+ ['#', ' managed by Certbot'],
+ ['ssl_certificate', '/etc/letsencrypt/live/two.functorkitten.xyz/fullchain.pem'],
+ ['#', ' managed by Certbot'],
+ ['ssl_certificate_key', '/etc/letsencrypt/live/two.functorkitten.xyz/privkey.pem'],
+ ['#', ' managed by Certbot'],
+ ['return', '301', 'https://$host$request_uri'],
+ ['#', ' managed by Certbot'], []]
+ vhost_haystack = VirtualHost(
+ "filp",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), test_haystack, [])
+ test_bad_haystack = [['listen', '80'], ['root', '/var/www/html'],
+ ['index', 'index.html index.htm index.nginx-debian.html'],
+ ['server_name', 'two.functorkitten.xyz'], ['listen', '443 ssl'],
+ ['#', ' managed by Certbot'],
+ ['ssl_certificate', '/etc/letsencrypt/live/two.functorkitten.xyz/fullchain.pem'],
+ ['#', ' managed by Certbot'],
+ ['ssl_certificate_key', '/etc/letsencrypt/live/two.functorkitten.xyz/privkey.pem'],
+ ['#', ' managed by Certbot'],
+ [['if', '($scheme', '!=', '"https")'],
+ [['return', '302', 'https://$host$request_uri']]
+ ],
+ ['#', ' managed by Certbot'], []]
+ vhost_bad_haystack = VirtualHost(
+ "filp",
+ set([Addr.fromstring("localhost")]), False, False,
+ set(['localhost']), test_bad_haystack, [])
+ self.assertTrue(vhost_haystack.contains_list(test_needle))
+ self.assertFalse(vhost_bad_haystack.contains_list(test_needle))
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/parser_obj_test.py b/certbot-nginx/tests/parser_obj_test.py
new file mode 100644
index 000000000..132f83771
--- /dev/null
+++ b/certbot-nginx/tests/parser_obj_test.py
@@ -0,0 +1,255 @@
+""" Tests for functions and classes in parser_obj.py """
+
+import unittest
+
+import mock
+
+from certbot_nginx._internal.parser_obj import COMMENT_BLOCK
+from certbot_nginx._internal.parser_obj import parse_raw
+
+
+class CommentHelpersTest(unittest.TestCase):
+ def test_is_comment(self):
+ from certbot_nginx._internal.parser_obj import _is_comment
+ self.assertTrue(_is_comment(parse_raw(['#'])))
+ self.assertTrue(_is_comment(parse_raw(['#', ' literally anything else'])))
+ self.assertFalse(_is_comment(parse_raw(['not', 'even', 'a', 'comment'])))
+
+ def test_is_certbot_comment(self):
+ from certbot_nginx._internal.parser_obj import _is_certbot_comment
+ self.assertTrue(_is_certbot_comment(
+ parse_raw(COMMENT_BLOCK)))
+ self.assertFalse(_is_certbot_comment(
+ parse_raw(['#', ' not a certbot comment'])))
+ self.assertFalse(_is_certbot_comment(
+ parse_raw(['#', ' managed by Certbot', ' also not a certbot comment'])))
+ self.assertFalse(_is_certbot_comment(
+ parse_raw(['not', 'even', 'a', 'comment'])))
+
+ def test_certbot_comment(self):
+ from certbot_nginx._internal.parser_obj import _certbot_comment, _is_certbot_comment
+ comment = _certbot_comment(None)
+ self.assertTrue(_is_certbot_comment(comment))
+ self.assertEqual(comment.dump(), COMMENT_BLOCK)
+ self.assertEqual(comment.dump(True), [' '] + COMMENT_BLOCK)
+ self.assertEqual(_certbot_comment(None, 2).dump(True),
+ [' '] + COMMENT_BLOCK)
+
+class ParsingHooksTest(unittest.TestCase):
+ def test_is_sentence(self):
+ from certbot_nginx._internal.parser_obj import Sentence
+ self.assertFalse(Sentence.should_parse([]))
+ self.assertTrue(Sentence.should_parse(['']))
+ self.assertTrue(Sentence.should_parse(['word']))
+ self.assertTrue(Sentence.should_parse(['two', 'words']))
+ self.assertFalse(Sentence.should_parse([[]]))
+ self.assertFalse(Sentence.should_parse(['word', []]))
+
+ def test_is_block(self):
+ from certbot_nginx._internal.parser_obj import Block
+ self.assertFalse(Block.should_parse([]))
+ self.assertFalse(Block.should_parse(['']))
+ self.assertFalse(Block.should_parse(['two', 'words']))
+ self.assertFalse(Block.should_parse([[[]], []]))
+ self.assertFalse(Block.should_parse([['block_name'], ['hi', []], []]))
+ self.assertFalse(Block.should_parse([['block_name'], 'lol']))
+ self.assertTrue(Block.should_parse([['block_name'], ['hi', []]]))
+ self.assertTrue(Block.should_parse([['hello'], []]))
+ self.assertTrue(Block.should_parse([['block_name'], [['many'], ['statements'], 'here']]))
+ self.assertTrue(Block.should_parse([['if', ' ', '(whatever)'], ['hi']]))
+
+ def test_parse_raw(self):
+ fake_parser1 = mock.Mock()
+ fake_parser1.should_parse = lambda x: True
+ fake_parser2 = mock.Mock()
+ fake_parser2.should_parse = lambda x: False
+ # First encountered "match" should parse.
+ parse_raw([])
+ fake_parser1.called_once()
+ fake_parser2.not_called()
+ fake_parser1.reset_mock()
+ # "match" that returns False shouldn't parse.
+ parse_raw([])
+ fake_parser1.not_called()
+ fake_parser2.called_once()
+
+ @mock.patch("certbot_nginx._internal.parser_obj.Parsable.parsing_hooks")
+ def test_parse_raw_no_match(self, parsing_hooks):
+ from certbot import errors
+ fake_parser1 = mock.Mock()
+ fake_parser1.should_parse = lambda x: False
+ parsing_hooks.return_value = (fake_parser1,)
+ self.assertRaises(errors.MisconfigurationError, parse_raw, [])
+ parsing_hooks.return_value = tuple()
+ self.assertRaises(errors.MisconfigurationError, parse_raw, [])
+
+ def test_parse_raw_passes_add_spaces(self):
+ fake_parser1 = mock.Mock()
+ fake_parser1.should_parse = lambda x: True
+ parse_raw([])
+ fake_parser1.parse.called_with([None])
+ parse_raw([], add_spaces=True)
+ fake_parser1.parse.called_with([None, True])
+
+class SentenceTest(unittest.TestCase):
+ def setUp(self):
+ from certbot_nginx._internal.parser_obj import Sentence
+ self.sentence = Sentence(None)
+
+ def test_parse_bad_sentence_raises_error(self):
+ from certbot import errors
+ self.assertRaises(errors.MisconfigurationError, self.sentence.parse, 'lol')
+ self.assertRaises(errors.MisconfigurationError, self.sentence.parse, [[]])
+ self.assertRaises(errors.MisconfigurationError, self.sentence.parse, [5])
+
+ def test_parse_sentence_words_hides_spaces(self):
+ og_sentence = ['\r\n', 'hello', ' ', ' ', '\t\n ', 'lol', ' ', 'spaces']
+ self.sentence.parse(og_sentence)
+ self.assertEqual(self.sentence.words, ['hello', 'lol', 'spaces'])
+ self.assertEqual(self.sentence.dump(), ['hello', 'lol', 'spaces'])
+ self.assertEqual(self.sentence.dump(True), og_sentence)
+
+ def test_parse_sentence_with_add_spaces(self):
+ self.sentence.parse(['hi', 'there'], add_spaces=True)
+ self.assertEqual(self.sentence.dump(True), ['hi', ' ', 'there'])
+ self.sentence.parse(['one', ' ', 'space', 'none'], add_spaces=True)
+ self.assertEqual(self.sentence.dump(True), ['one', ' ', 'space', ' ', 'none'])
+
+ def test_iterate(self):
+ expected = [['1', '2', '3']]
+ self.sentence.parse(['1', ' ', '2', ' ', '3'])
+ for i, sentence in enumerate(self.sentence.iterate()):
+ self.assertEqual(sentence.dump(), expected[i])
+
+ def test_set_tabs(self):
+ self.sentence.parse(['tabs', 'pls'], add_spaces=True)
+ self.sentence.set_tabs()
+ self.assertEqual(self.sentence.dump(True)[0], '\n ')
+ self.sentence.parse(['tabs', 'pls'], add_spaces=True)
+
+ def test_get_tabs(self):
+ self.sentence.parse(['no', 'tabs'])
+ self.assertEqual(self.sentence.get_tabs(), '')
+ self.sentence.parse(['\n \n ', 'tabs'])
+ self.assertEqual(self.sentence.get_tabs(), ' ')
+ self.sentence.parse(['\n\t ', 'tabs'])
+ self.assertEqual(self.sentence.get_tabs(), '\t ')
+ self.sentence.parse(['\n\t \n', 'tabs'])
+ self.assertEqual(self.sentence.get_tabs(), '')
+
+class BlockTest(unittest.TestCase):
+ def setUp(self):
+ from certbot_nginx._internal.parser_obj import Block
+ self.bloc = Block(None)
+ self.name = ['server', 'name']
+ self.contents = [['thing', '1'], ['thing', '2'], ['another', 'one']]
+ self.bloc.parse([self.name, self.contents])
+
+ def test_iterate(self):
+ # Iterates itself normally
+ self.assertEqual(self.bloc, next(self.bloc.iterate()))
+ # Iterates contents while expanded
+ expected = [self.bloc.dump()] + self.contents
+ for i, elem in enumerate(self.bloc.iterate(expanded=True)):
+ self.assertEqual(expected[i], elem.dump())
+
+ def test_iterate_match(self):
+ # can match on contents while expanded
+ from certbot_nginx._internal.parser_obj import Block, Sentence
+ expected = [['thing', '1'], ['thing', '2']]
+ for i, elem in enumerate(self.bloc.iterate(expanded=True,
+ match=lambda x: isinstance(x, Sentence) and 'thing' in x.words)):
+ self.assertEqual(expected[i], elem.dump())
+ # can match on self
+ self.assertEqual(self.bloc, next(self.bloc.iterate(
+ expanded=True,
+ match=lambda x: isinstance(x, Block) and 'server' in x.names)))
+
+ def test_parse_with_added_spaces(self):
+ import copy
+ self.bloc.parse([copy.copy(self.name), self.contents], add_spaces=True)
+ self.assertEqual(self.bloc.dump(), [self.name, self.contents])
+ self.assertEqual(self.bloc.dump(True), [
+ ['server', ' ', 'name', ' '],
+ [['thing', ' ', '1'],
+ ['thing', ' ', '2'],
+ ['another', ' ', 'one']]])
+
+ def test_bad_parse_raises_error(self):
+ from certbot import errors
+ self.assertRaises(errors.MisconfigurationError, self.bloc.parse, [[[]], [[]]])
+ self.assertRaises(errors.MisconfigurationError, self.bloc.parse, ['lol'])
+ self.assertRaises(errors.MisconfigurationError, self.bloc.parse, ['fake', 'news'])
+
+ def test_set_tabs(self):
+ self.bloc.set_tabs()
+ self.assertEqual(self.bloc.names.dump(True)[0], '\n ')
+ for elem in self.bloc.contents.dump(True)[:-1]:
+ self.assertEqual(elem[0], '\n ')
+ self.assertEqual(self.bloc.contents.dump(True)[-1][0], '\n')
+
+ def test_get_tabs(self):
+ self.bloc.parse([[' \n \t', 'lol'], []])
+ self.assertEqual(self.bloc.get_tabs(), ' \t')
+
+class StatementsTest(unittest.TestCase):
+ def setUp(self):
+ from certbot_nginx._internal.parser_obj import Statements
+ self.statements = Statements(None)
+ self.raw = [
+ ['sentence', 'one'],
+ ['sentence', 'two'],
+ ['and', 'another']
+ ]
+ self.raw_spaced = [
+ ['\n ', 'sentence', ' ', 'one'],
+ ['\n ', 'sentence', ' ', 'two'],
+ ['\n ', 'and', ' ', 'another'],
+ '\n\n'
+ ]
+
+ def test_set_tabs(self):
+ self.statements.parse(self.raw)
+ self.statements.set_tabs()
+ for statement in self.statements.iterate():
+ self.assertEqual(statement.dump(True)[0], '\n ')
+
+ def test_set_tabs_with_parent(self):
+ # Trailing whitespace should inherit from parent tabbing.
+ self.statements.parse(self.raw)
+ self.statements.parent = mock.Mock()
+ self.statements.parent.get_tabs.return_value = '\t\t'
+ self.statements.set_tabs()
+ for statement in self.statements.iterate():
+ self.assertEqual(statement.dump(True)[0], '\n ')
+ self.assertEqual(self.statements.dump(True)[-1], '\n\t\t')
+
+ def test_get_tabs(self):
+ self.raw[0].insert(0, '\n \n \t')
+ self.statements.parse(self.raw)
+ self.assertEqual(self.statements.get_tabs(), ' \t')
+ self.statements.parse([])
+ self.assertEqual(self.statements.get_tabs(), '')
+
+ def test_parse_with_added_spaces(self):
+ self.statements.parse(self.raw, add_spaces=True)
+ self.assertEqual(self.statements.dump(True)[0], ['sentence', ' ', 'one'])
+
+ def test_parse_bad_list_raises_error(self):
+ from certbot import errors
+ self.assertRaises(errors.MisconfigurationError, self.statements.parse, 'lol not a list')
+
+ def test_parse_hides_trailing_whitespace(self):
+ self.statements.parse(self.raw + ['\n\n '])
+ self.assertTrue(isinstance(self.statements.dump()[-1], list))
+ self.assertTrue(self.statements.dump(True)[-1].isspace())
+ self.assertEqual(self.statements.dump(True)[-1], '\n\n ')
+
+ def test_iterate(self):
+ self.statements.parse(self.raw)
+ expected = [['sentence', 'one'], ['sentence', 'two']]
+ for i, elem in enumerate(self.statements.iterate(match=lambda x: 'sentence' in x)):
+ self.assertEqual(expected[i], elem.dump())
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/parser_test.py b/certbot-nginx/tests/parser_test.py
new file mode 100644
index 000000000..2f3b260ca
--- /dev/null
+++ b/certbot-nginx/tests/parser_test.py
@@ -0,0 +1,487 @@
+"""Tests for certbot_nginx._internal.parser."""
+import glob
+import re
+import shutil
+import unittest
+
+from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
+from certbot import errors
+from certbot.compat import os
+from certbot_nginx._internal import nginxparser
+from certbot_nginx._internal import obj
+from certbot_nginx._internal import parser
+import test_util as util
+
+
+class NginxParserTest(util.NginxTest):
+ """Nginx Parser Test."""
+
+ def tearDown(self):
+ shutil.rmtree(self.temp_dir)
+ shutil.rmtree(self.config_dir)
+ shutil.rmtree(self.work_dir)
+
+ def test_root_normalized(self):
+ path = os.path.join(self.temp_dir, "etc_nginx/////"
+ "ubuntu_nginx/../../etc_nginx")
+ nparser = parser.NginxParser(path)
+ self.assertEqual(nparser.root, self.config_path)
+
+ def test_root_absolute(self):
+ curr_dir = os.getcwd()
+ try:
+ # On Windows current directory may be on a different drive than self.tempdir.
+ # However a relative path between two different drives is invalid. So we move to
+ # self.tempdir to ensure that we stay on the same drive.
+ os.chdir(self.temp_dir)
+ nparser = parser.NginxParser(os.path.relpath(self.config_path))
+ self.assertEqual(nparser.root, self.config_path)
+ finally:
+ os.chdir(curr_dir)
+
+ def test_root_no_trailing_slash(self):
+ nparser = parser.NginxParser(self.config_path + os.path.sep)
+ self.assertEqual(nparser.root, self.config_path)
+
+ def test_load(self):
+ """Test recursive conf file parsing.
+
+ """
+ nparser = parser.NginxParser(self.config_path)
+ nparser.load()
+ self.assertEqual({nparser.abs_path(x) for x in
+ ['foo.conf', 'nginx.conf', 'server.conf',
+ 'sites-enabled/default',
+ 'sites-enabled/example.com',
+ 'sites-enabled/headers.com',
+ 'sites-enabled/migration.com',
+ 'sites-enabled/sslon.com',
+ 'sites-enabled/globalssl.com',
+ 'sites-enabled/ipv6.com',
+ 'sites-enabled/ipv6ssl.com']},
+ set(nparser.parsed.keys()))
+ self.assertEqual([['server_name', 'somename', 'alias', 'another.alias']],
+ nparser.parsed[nparser.abs_path('server.conf')])
+ self.assertEqual([[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*']]]],
+ nparser.parsed[nparser.abs_path(
+ 'sites-enabled/example.com')])
+
+ def test_abs_path(self):
+ nparser = parser.NginxParser(self.config_path)
+ if os.name != 'nt':
+ self.assertEqual('/etc/nginx/*', nparser.abs_path('/etc/nginx/*'))
+ self.assertEqual(os.path.join(self.config_path, 'foo/bar'),
+ nparser.abs_path('foo/bar'))
+ else:
+ self.assertEqual('C:\\etc\\nginx\\*', nparser.abs_path('C:\\etc\\nginx\\*'))
+ self.assertEqual(os.path.join(self.config_path, 'foo\\bar'),
+ nparser.abs_path('foo\\bar'))
+
+
+ def test_filedump(self):
+ nparser = parser.NginxParser(self.config_path)
+ nparser.filedump('test', lazy=False)
+ # pylint: disable=protected-access
+ parsed = nparser._parse_files(nparser.abs_path(
+ 'sites-enabled/example.com.test'))
+ self.assertEqual(3, len(glob.glob(nparser.abs_path('*.test'))))
+ self.assertEqual(8, len(
+ glob.glob(nparser.abs_path('sites-enabled/*.test'))))
+ self.assertEqual([[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*']]]],
+ parsed[0])
+
+ def test__do_for_subarray(self):
+ # pylint: disable=protected-access
+ mylists = [([[2], [3], [2]], [[0], [2]]),
+ ([[2], [3], [4]], [[0]]),
+ ([[4], [3], [2]], [[2]]),
+ ([], []),
+ (2, []),
+ ([[[2], [3], [2]], [[2], [3], [2]]],
+ [[0, 0], [0, 2], [1, 0], [1, 2]]),
+ ([[[0], [3], [2]], [[2], [3], [2]]], [[0, 2], [1, 0], [1, 2]]),
+ ([[[0], [3], [4]], [[2], [3], [2]]], [[1, 0], [1, 2]]),
+ ([[[0], [3], [4]], [[5], [3], [2]]], [[1, 2]]),
+ ([[[0], [3], [4]], [[5], [3], [0]]], [])]
+
+ for mylist, result in mylists:
+ paths = [] # type: List[List[int]]
+ parser._do_for_subarray(mylist,
+ lambda x: isinstance(x, list) and
+ len(x) >= 1 and
+ x[0] == 2,
+ lambda x, y, pts=paths: pts.append(y))
+ self.assertEqual(paths, result)
+
+ def test_get_vhosts_global_ssl(self):
+ nparser = parser.NginxParser(self.config_path)
+ vhosts = nparser.get_vhosts()
+
+ vhost = obj.VirtualHost(nparser.abs_path('sites-enabled/globalssl.com'),
+ [obj.Addr('4.8.2.6', '57', True, False,
+ False, False)],
+ True, True, set(['globalssl.com']), [], [0])
+
+ globalssl_com = [x for x in vhosts if 'globalssl.com' in x.filep][0]
+ self.assertEqual(vhost, globalssl_com)
+
+ def test_get_vhosts(self):
+ nparser = parser.NginxParser(self.config_path)
+ vhosts = nparser.get_vhosts()
+
+ vhost1 = obj.VirtualHost(nparser.abs_path('nginx.conf'),
+ [obj.Addr('', '8080', False, False,
+ False, False)],
+ False, True,
+ set(['localhost',
+ r'~^(www\.)?(example|bar)\.']),
+ [], [10, 1, 9])
+ vhost2 = obj.VirtualHost(nparser.abs_path('nginx.conf'),
+ [obj.Addr('somename', '8080', False, False,
+ False, False),
+ obj.Addr('', '8000', False, False,
+ False, False)],
+ False, True,
+ set(['somename', 'another.alias', 'alias']),
+ [], [10, 1, 12])
+ vhost3 = obj.VirtualHost(nparser.abs_path('sites-enabled/example.com'),
+ [obj.Addr('69.50.225.155', '9000',
+ False, False, False, False),
+ obj.Addr('127.0.0.1', '', False, False,
+ False, False)],
+ False, True,
+ set(['.example.com', 'example.*']), [], [0])
+ vhost4 = obj.VirtualHost(nparser.abs_path('sites-enabled/default'),
+ [obj.Addr('myhost', '', False, True,
+ False, False),
+ obj.Addr('otherhost', '', False, True,
+ False, False)],
+ False, True, set(['www.example.org']),
+ [], [0])
+ vhost5 = obj.VirtualHost(nparser.abs_path('foo.conf'),
+ [obj.Addr('*', '80', True, True,
+ False, False)],
+ True, True, set(['*.www.foo.com',
+ '*.www.example.com']),
+ [], [2, 1, 0])
+
+ self.assertEqual(13, len(vhosts))
+ example_com = [x for x in vhosts if 'example.com' in x.filep][0]
+ self.assertEqual(vhost3, example_com)
+ default = [x for x in vhosts if 'default' in x.filep][0]
+ self.assertEqual(vhost4, default)
+ fooconf = [x for x in vhosts if 'foo.conf' in x.filep][0]
+ self.assertEqual(vhost5, fooconf)
+ localhost = [x for x in vhosts if 'localhost' in x.names][0]
+ self.assertEqual(vhost1, localhost)
+ somename = [x for x in vhosts if 'somename' in x.names][0]
+ self.assertEqual(vhost2, somename)
+
+ def test_has_ssl_on_directive(self):
+ nparser = parser.NginxParser(self.config_path)
+ mock_vhost = obj.VirtualHost(None, None, None, None, None,
+ [['listen', 'myhost default_server'],
+ ['server_name', 'www.example.org'],
+ [['location', '/'], [['root', 'html'], ['index', 'index.html index.htm']]]
+ ], None)
+ self.assertFalse(nparser.has_ssl_on_directive(mock_vhost))
+ mock_vhost.raw = [['listen', '*:80', 'default_server', 'ssl'],
+ ['server_name', '*.www.foo.com', '*.www.example.com'],
+ ['root', '/home/ubuntu/sites/foo/']]
+ self.assertFalse(nparser.has_ssl_on_directive(mock_vhost))
+ mock_vhost.raw = [['listen', '80 ssl'],
+ ['server_name', '*.www.foo.com', '*.www.example.com']]
+ self.assertFalse(nparser.has_ssl_on_directive(mock_vhost))
+ mock_vhost.raw = [['listen', '80'],
+ ['ssl', 'on'],
+ ['server_name', '*.www.foo.com', '*.www.example.com']]
+ self.assertTrue(nparser.has_ssl_on_directive(mock_vhost))
+
+
+ def test_remove_server_directives(self):
+ nparser = parser.NginxParser(self.config_path)
+ mock_vhost = obj.VirtualHost(nparser.abs_path('nginx.conf'),
+ None, None, None,
+ set(['localhost',
+ r'~^(www\.)?(example|bar)\.']),
+ None, [10, 1, 9])
+ example_com = nparser.abs_path('sites-enabled/example.com')
+ names = set(['.example.com', 'example.*'])
+ mock_vhost.filep = example_com
+ mock_vhost.names = names
+ mock_vhost.path = [0]
+ nparser.add_server_directives(mock_vhost,
+ [['foo', 'bar'], ['ssl_certificate',
+ '/etc/ssl/cert2.pem']])
+ nparser.remove_server_directives(mock_vhost, 'foo')
+ nparser.remove_server_directives(mock_vhost, 'ssl_certificate')
+ self.assertEqual(nparser.parsed[example_com],
+ [[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+ []]]])
+
+ def test_add_server_directives(self):
+ nparser = parser.NginxParser(self.config_path)
+ mock_vhost = obj.VirtualHost(nparser.abs_path('nginx.conf'),
+ None, None, None,
+ set(['localhost',
+ r'~^(www\.)?(example|bar)\.']),
+ None, [10, 1, 9])
+ nparser.add_server_directives(mock_vhost,
+ [['foo', 'bar'], ['\n ', 'ssl_certificate', ' ',
+ '/etc/ssl/cert.pem']])
+ ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem')
+ dump = nginxparser.dumps(nparser.parsed[nparser.abs_path('nginx.conf')])
+ self.assertEqual(1, len(re.findall(ssl_re, dump)))
+
+ example_com = nparser.abs_path('sites-enabled/example.com')
+ names = set(['.example.com', 'example.*'])
+ mock_vhost.filep = example_com
+ mock_vhost.names = names
+ mock_vhost.path = [0]
+ nparser.add_server_directives(mock_vhost,
+ [['foo', 'bar'], ['ssl_certificate',
+ '/etc/ssl/cert2.pem']])
+ nparser.add_server_directives(mock_vhost, [['foo', 'bar']])
+ from certbot_nginx._internal.parser import COMMENT
+ self.assertEqual(nparser.parsed[example_com],
+ [[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+ ['foo', 'bar'],
+ ['#', COMMENT],
+ ['ssl_certificate', '/etc/ssl/cert2.pem'],
+ ['#', COMMENT], [], []
+ ]]])
+
+ server_conf = nparser.abs_path('server.conf')
+ names = set(['alias', 'another.alias', 'somename'])
+ mock_vhost.filep = server_conf
+ mock_vhost.names = names
+ mock_vhost.path = []
+ self.assertRaises(errors.MisconfigurationError,
+ nparser.add_server_directives,
+ mock_vhost,
+ [['foo', 'bar'],
+ ['ssl_certificate', '/etc/ssl/cert2.pem']])
+
+ def test_comment_is_repeatable(self):
+ nparser = parser.NginxParser(self.config_path)
+ example_com = nparser.abs_path('sites-enabled/example.com')
+ mock_vhost = obj.VirtualHost(example_com,
+ None, None, None,
+ set(['.example.com', 'example.*']),
+ None, [0])
+ nparser.add_server_directives(mock_vhost,
+ [['\n ', '#', ' ', 'what a nice comment']])
+ nparser.add_server_directives(mock_vhost,
+ [['\n ', 'include', ' ',
+ nparser.abs_path('comment_in_file.conf')]])
+ from certbot_nginx._internal.parser import COMMENT
+ self.assertEqual(nparser.parsed[example_com],
+ [[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', '.example.com'],
+ ['server_name', 'example.*'],
+ ['#', ' ', 'what a nice comment'],
+ [],
+ ['include', nparser.abs_path('comment_in_file.conf')],
+ ['#', COMMENT],
+ []]]]
+)
+
+ def test_replace_server_directives(self):
+ nparser = parser.NginxParser(self.config_path)
+ target = set(['.example.com', 'example.*'])
+ filep = nparser.abs_path('sites-enabled/example.com')
+ mock_vhost = obj.VirtualHost(filep, None, None, None, target, None, [0])
+ nparser.update_or_add_server_directives(
+ mock_vhost, [['server_name', 'foobar.com']])
+ from certbot_nginx._internal.parser import COMMENT
+ self.assertEqual(
+ nparser.parsed[filep],
+ [[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', 'foobar.com'], ['#', COMMENT],
+ ['server_name', 'example.*'], []
+ ]]])
+ mock_vhost.names = set(['foobar.com', 'example.*'])
+ nparser.update_or_add_server_directives(
+ mock_vhost, [['ssl_certificate', 'cert.pem']])
+ self.assertEqual(
+ nparser.parsed[filep],
+ [[['server'], [['listen', '69.50.225.155:9000'],
+ ['listen', '127.0.0.1'],
+ ['server_name', 'foobar.com'], ['#', COMMENT],
+ ['server_name', 'example.*'], [],
+ ['ssl_certificate', 'cert.pem'], ['#', COMMENT], [],
+ ]]])
+
+ def test_get_best_match(self):
+ target_name = 'www.eff.org'
+ names = [set(['www.eff.org', 'irrelevant.long.name.eff.org', '*.org']),
+ set(['eff.org', 'ww2.eff.org', 'test.www.eff.org']),
+ set(['*.eff.org', '.www.eff.org']),
+ set(['.eff.org', '*.org']),
+ set(['www.eff.', 'www.eff.*', '*.www.eff.org']),
+ set(['example.com', r'~^(www\.)?(eff.+)', '*.eff.*']),
+ set(['*', r'~^(www\.)?(eff.+)']),
+ set(['www.*', r'~^(www\.)?(eff.+)', '.test.eff.org']),
+ set(['*.org', r'*.eff.org', 'www.eff.*']),
+ set(['*.www.eff.org', 'www.*']),
+ set(['*.org']),
+ set([]),
+ set(['example.com'])]
+ winners = [('exact', 'www.eff.org'),
+ (None, None),
+ ('exact', '.www.eff.org'),
+ ('wildcard_start', '.eff.org'),
+ ('wildcard_end', 'www.eff.*'),
+ ('regex', r'~^(www\.)?(eff.+)'),
+ ('wildcard_start', '*'),
+ ('wildcard_end', 'www.*'),
+ ('wildcard_start', '*.eff.org'),
+ ('wildcard_end', 'www.*'),
+ ('wildcard_start', '*.org'),
+ (None, None),
+ (None, None)]
+
+ for i, winner in enumerate(winners):
+ self.assertEqual(winner,
+ parser.get_best_match(target_name, names[i]))
+
+ def test_comment_directive(self):
+ # pylint: disable=protected-access
+ block = nginxparser.UnspacedList([
+ ["\n", "a", " ", "b", "\n"],
+ ["c", " ", "d"],
+ ["\n", "e", " ", "f"]])
+ from certbot_nginx._internal.parser import comment_directive, COMMENT_BLOCK
+ comment_directive(block, 1)
+ comment_directive(block, 0)
+ self.assertEqual(block.spaced, [
+ ["\n", "a", " ", "b", "\n"],
+ COMMENT_BLOCK,
+ "\n",
+ ["c", " ", "d"],
+ COMMENT_BLOCK,
+ ["\n", "e", " ", "f"]])
+
+ def test_comment_out_directive(self):
+ server_block = nginxparser.loads("""
+ server {
+ listen 80;
+ root /var/www/html;
+ index star.html;
+
+ server_name *.functorkitten.xyz;
+ ssl_session_timeout 1440m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+
+ ssl_prefer_server_ciphers on;
+ }""")
+ block = server_block[0][1]
+ from certbot_nginx._internal.parser import _comment_out_directive
+ _comment_out_directive(block, 4, "blah1")
+ _comment_out_directive(block, 5, "blah2")
+ _comment_out_directive(block, 6, "blah3")
+ self.assertEqual(block.spaced, [
+ ['\n ', 'listen', ' ', '80'],
+ ['\n ', 'root', ' ', '/var/www/html'],
+ ['\n ', 'index', ' ', 'star.html'],
+ ['\n\n ', 'server_name', ' ', '*.functorkitten.xyz'],
+ ['\n ', '#', ' ssl_session_timeout 1440m; # duplicated in blah1'],
+ [' ', '#', ' ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # duplicated in blah2'],
+ ['\n\n ', '#', ' ssl_prefer_server_ciphers on; # duplicated in blah3'],
+ '\n '])
+
+ def test_parse_server_raw_ssl(self):
+ server = parser._parse_server_raw([ #pylint: disable=protected-access
+ ['listen', '443']
+ ])
+ self.assertFalse(server['ssl'])
+
+ server = parser._parse_server_raw([ #pylint: disable=protected-access
+ ['listen', '443', 'ssl']
+ ])
+ self.assertTrue(server['ssl'])
+
+ server = parser._parse_server_raw([ #pylint: disable=protected-access
+ ['listen', '443'], ['ssl', 'off']
+ ])
+ self.assertFalse(server['ssl'])
+
+ server = parser._parse_server_raw([ #pylint: disable=protected-access
+ ['listen', '443'], ['ssl', 'on']
+ ])
+ self.assertTrue(server['ssl'])
+
+ def test_parse_server_raw_unix(self):
+ server = parser._parse_server_raw([ #pylint: disable=protected-access
+ ['listen', 'unix:/var/run/nginx.sock']
+ ])
+ self.assertEqual(len(server['addrs']), 0)
+
+ def test_parse_server_global_ssl_applied(self):
+ nparser = parser.NginxParser(self.config_path)
+ server = nparser.parse_server([
+ ['listen', '443']
+ ])
+ self.assertTrue(server['ssl'])
+
+ def test_duplicate_vhost(self):
+ nparser = parser.NginxParser(self.config_path)
+
+ vhosts = nparser.get_vhosts()
+ default = [x for x in vhosts if 'default' in x.filep][0]
+ new_vhost = nparser.duplicate_vhost(default, remove_singleton_listen_params=True)
+ nparser.filedump(ext='')
+
+ # check properties of new vhost
+ self.assertFalse(next(iter(new_vhost.addrs)).default)
+ self.assertNotEqual(new_vhost.path, default.path)
+
+ # check that things are written to file correctly
+ new_nparser = parser.NginxParser(self.config_path)
+ new_vhosts = new_nparser.get_vhosts()
+ new_defaults = [x for x in new_vhosts if 'default' in x.filep]
+ self.assertEqual(len(new_defaults), 2)
+ new_vhost_parsed = new_defaults[1]
+ self.assertFalse(next(iter(new_vhost_parsed.addrs)).default)
+ self.assertEqual(next(iter(default.names)), next(iter(new_vhost_parsed.names)))
+ self.assertEqual(len(default.raw), len(new_vhost_parsed.raw))
+ self.assertTrue(next(iter(default.addrs)).super_eq(next(iter(new_vhost_parsed.addrs))))
+
+ def test_duplicate_vhost_remove_ipv6only(self):
+ nparser = parser.NginxParser(self.config_path)
+
+ vhosts = nparser.get_vhosts()
+ ipv6ssl = [x for x in vhosts if 'ipv6ssl' in x.filep][0]
+ new_vhost = nparser.duplicate_vhost(ipv6ssl, remove_singleton_listen_params=True)
+ nparser.filedump(ext='')
+
+ for addr in new_vhost.addrs:
+ self.assertFalse(addr.ipv6only)
+
+ identical_vhost = nparser.duplicate_vhost(ipv6ssl, remove_singleton_listen_params=False)
+ nparser.filedump(ext='')
+
+ called = False
+ for addr in identical_vhost.addrs:
+ if addr.ipv6:
+ self.assertTrue(addr.ipv6only)
+ called = True
+ self.assertTrue(called)
+
+
+
+if __name__ == "__main__":
+ unittest.main() # pragma: no cover
diff --git a/certbot-nginx/tests/test_util.py b/certbot-nginx/tests/test_util.py
new file mode 100644
index 000000000..8dfd18637
--- /dev/null
+++ b/certbot-nginx/tests/test_util.py
@@ -0,0 +1,130 @@
+"""Common utilities for certbot_nginx."""
+import copy
+import shutil
+import tempfile
+
+import josepy as jose
+import mock
+import pkg_resources
+import zope.component
+
+from certbot import util
+from certbot.compat import os
+from certbot.plugins import common
+from certbot.tests import util as test_util
+from certbot_nginx._internal import configurator
+from certbot_nginx._internal import nginxparser
+
+
+class NginxTest(test_util.ConfigTestCase):
+
+ def setUp(self):
+ super(NginxTest, self).setUp()
+
+ self.configuration = self.config
+ self.config = None
+
+ self.temp_dir, self.config_dir, self.work_dir = common.dir_setup(
+ "etc_nginx", __name__)
+ self.logs_dir = tempfile.mkdtemp('logs')
+
+ self.config_path = os.path.join(self.temp_dir, "etc_nginx")
+
+ self.rsa512jwk = jose.JWKRSA.load(test_util.load_vector(
+ "rsa512_key.pem"))
+
+ def tearDown(self):
+ # Cleanup opened resources after a test. This is usually done through atexit handlers in
+ # Certbot, but during tests, atexit will not run registered functions before tearDown is
+ # called and instead will run them right before the entire test process exits.
+ # It is a problem on Windows, that does not accept to clean resources before closing them.
+ util._release_locks() # pylint: disable=protected-access
+
+ shutil.rmtree(self.temp_dir)
+ shutil.rmtree(self.config_dir)
+ shutil.rmtree(self.work_dir)
+ shutil.rmtree(self.logs_dir)
+
+ def get_nginx_configurator(self, config_path, config_dir, work_dir, logs_dir,
+ version=(1, 6, 2), openssl_version="1.0.2g"):
+ """Create an Nginx Configurator with the specified options."""
+
+ backups = os.path.join(work_dir, "backups")
+
+ self.configuration.nginx_server_root = config_path
+ self.configuration.le_vhost_ext = "-le-ssl.conf"
+ self.configuration.config_dir = config_dir
+ self.configuration.work_dir = work_dir
+ self.configuration.logs_dir = logs_dir
+ self.configuration.backup_dir = backups
+ self.configuration.temp_checkpoint_dir = os.path.join(work_dir, "temp_checkpoints")
+ self.configuration.in_progress_dir = os.path.join(backups, "IN_PROGRESS")
+ self.configuration.server = "https://acme-server.org:443/new"
+ self.configuration.http01_port = 80
+ self.configuration.https_port = 5001
+
+ with mock.patch("certbot_nginx._internal.configurator.NginxConfigurator."
+ "config_test"):
+ with mock.patch("certbot_nginx._internal.configurator.util."
+ "exe_exists") as mock_exe_exists:
+ mock_exe_exists.return_value = True
+ config = configurator.NginxConfigurator(
+ self.configuration,
+ name="nginx",
+ version=version,
+ openssl_version=openssl_version)
+ config.prepare()
+
+ # Provide general config utility.
+ zope.component.provideUtility(self.configuration)
+
+ return config
+
+
+def get_data_filename(filename):
+ """Gets the filename of a test data file."""
+ return pkg_resources.resource_filename(
+ __name__, os.path.join(
+ "testdata", "etc_nginx", filename))
+
+
+def filter_comments(tree):
+ """Filter comment nodes from parsed configurations."""
+
+ def traverse(tree):
+ """Generator dropping comment nodes"""
+ for entry in tree:
+ # key, values = entry
+ spaceless = [e for e in entry if not nginxparser.spacey(e)]
+ if spaceless:
+ key = spaceless[0]
+ values = spaceless[1] if len(spaceless) > 1 else None
+ else:
+ key = values = ""
+ if isinstance(key, list):
+ new = copy.deepcopy(entry)
+ new[1] = filter_comments(values)
+ yield new
+ else:
+ if key != '#' and spaceless:
+ yield spaceless
+
+ return list(traverse(tree))
+
+
+def contains_at_depth(haystack, needle, n):
+ """Is the needle in haystack at depth n?
+
+ Return true if the needle is present in one of the sub-iterables in haystack
+ at depth n. Haystack must be an iterable.
+ """
+ # Specifically use hasattr rather than isinstance(..., collections.Iterable)
+ # because we want to include lists but reject strings.
+ if not hasattr(haystack, '__iter__') or hasattr(haystack, 'strip'):
+ return False
+ if n == 0:
+ return needle in haystack
+ for item in haystack:
+ if contains_at_depth(item, needle, n - 1):
+ return True
+ return False
diff --git a/certbot-nginx/tests/testdata/etc_nginx/broken.conf b/certbot-nginx/tests/testdata/etc_nginx/broken.conf
new file mode 100644
index 000000000..98aef55d6
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/broken.conf
@@ -0,0 +1,12 @@
+# A faulty configuration file
+
+pid logs/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+include foo.conf;
+
+@@@
diff --git a/certbot-nginx/tests/testdata/etc_nginx/comment_in_file.conf b/certbot-nginx/tests/testdata/etc_nginx/comment_in_file.conf
new file mode 100644
index 000000000..f761079fa
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/comment_in_file.conf
@@ -0,0 +1 @@
+# a comment inside a file \ No newline at end of file
diff --git a/certbot-nginx/tests/testdata/etc_nginx/edge_cases.conf b/certbot-nginx/tests/testdata/etc_nginx/edge_cases.conf
new file mode 100644
index 000000000..477cb1c45
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/edge_cases.conf
@@ -0,0 +1,27 @@
+# This is not a valid nginx config file but it tests edge cases in valid nginx syntax
+
+server {
+ server_name simple;
+}
+
+server {
+ server_name with.if;
+ location ~ ^/services/.+$ {
+ if ($request_filename ~* \.(ttf|woff)$) {
+ add_header Access-Control-Allow-Origin "*";
+ }
+ }
+}
+
+server {
+ server_name with.complicated.headers;
+
+ location ~* \.(?:gif|jpe?g|png)$ {
+
+ add_header Pragma public;
+ add_header Cache-Control 'public, must-revalidate, proxy-revalidate' "test,;{}" foo;
+ blah "hello;world";
+
+ try_files $uri @rewrites;
+ }
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/foo.conf b/certbot-nginx/tests/testdata/etc_nginx/foo.conf
new file mode 100644
index 000000000..574955398
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/foo.conf
@@ -0,0 +1,25 @@
+# a test nginx conf
+user www-data;
+
+http {
+ server {
+ listen *:80 default_server ssl;
+ server_name *.www.foo.com *.www.example.com;
+ root /home/ubuntu/sites/foo/;
+
+ location /status {
+ types {
+ image/jpeg jpg;
+ }
+ }
+
+ location ~ case_sensitive\.php$ {
+ index index.php;
+ root /var/root;
+ }
+ location ~* case_insensitive\.php$ {}
+ location = exact_match\.php$ {}
+ location ^~ ignore_regex\.php$ {}
+
+ }
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/mime.types b/certbot-nginx/tests/testdata/etc_nginx/mime.types
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/mime.types
diff --git a/certbot-nginx/tests/testdata/etc_nginx/minimalistic_comments.conf b/certbot-nginx/tests/testdata/etc_nginx/minimalistic_comments.conf
new file mode 100644
index 000000000..cf4648592
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/minimalistic_comments.conf
@@ -0,0 +1,12 @@
+# Use bar.conf when it's a full moon!
+include foo.conf; # Kilroy was here
+check_status;
+
+server {
+ #
+ # Don't forget to open up your firewall!
+ #
+ listen 1234;
+ # listen 80;
+}
+
diff --git a/certbot-nginx/tests/testdata/etc_nginx/multiline_quotes.conf b/certbot-nginx/tests/testdata/etc_nginx/multiline_quotes.conf
new file mode 100644
index 000000000..74cd84bcd
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/multiline_quotes.conf
@@ -0,0 +1,16 @@
+# Test nginx configuration file with multiline quoted strings.
+# Good example of usage for multilined quoted values is when
+# using Openresty's Lua directives and you wish to keep the
+# inline Lua code readable.
+http {
+ server {
+ listen *:443; # because there should be no other port open.
+
+ location / {
+ body_filter_by_lua 'ngx.ctx.buffered = (ngx.ctx.buffered or "") .. string.sub(ngx.arg[1], 1, 1000)
+ if ngx.arg[2] then
+ ngx.var.resp_body = ngx.ctx.buffered
+ end';
+ }
+ }
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/nginx.conf b/certbot-nginx/tests/testdata/etc_nginx/nginx.conf
new file mode 100644
index 000000000..ccce4dc1b
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/nginx.conf
@@ -0,0 +1,122 @@
+# standard default nginx config
+
+user nobody;
+worker_processes 1;
+
+error_log logs/error.log;
+error_log logs/error.log notice;
+error_log logs/error.log info;
+
+pid logs/nginx.pid;
+
+
+events {
+ worker_connections 1024;
+}
+
+empty {
+}
+
+include foo.conf;
+
+http {
+ include mime.types;
+ include sites-enabled/*;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log logs/access.log main;
+
+ sendfile on;
+ tcp_nopush on;
+
+ keepalive_timeout 0;
+
+ gzip on;
+
+ server {
+ listen 8080;
+ server_name localhost;
+ server_name ~^(www\.)?(example|bar)\.;
+
+ charset koi8-r;
+
+ access_log logs/host.access.log main;
+
+ location / {
+ root html;
+ index index.html index.htm;
+ }
+
+ error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ error_page 500 502 503 504 /50x.html;
+ location = /50x.html {
+ root html;
+ }
+
+ # proxy the PHP scripts to Nginx listening on 127.0.0.1:80
+ #
+ location ~ \.php$ {
+ proxy_pass http://127.0.0.1;
+ }
+
+ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+ #
+ location ~ \.php$ {
+ root html;
+ fastcgi_pass 127.0.0.1:9000;
+ fastcgi_index index.php;
+ fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
+ }
+
+ # deny access to .htaccess files, if Nginx's document root
+ # concurs with nginx's one
+ #
+ location ~ /\.ht {
+ deny all;
+ }
+ }
+
+
+ # another virtual host using mix of IP-, name-, and port-based configuration
+ #
+ server {
+ listen 8000;
+ listen somename:8080;
+ include server.conf;
+
+ location / {
+ root html;
+ index index.html index.htm;
+ }
+ }
+
+
+ # HTTPS server
+ #
+ #server {
+ # listen 443 ssl;
+ # server_name localhost;
+
+ # ssl_certificate cert.pem;
+ # ssl_certificate_key cert.key;
+
+ # ssl_session_cache shared:SSL:1m;
+ # ssl_session_timeout 5m;
+
+ # ssl_ciphers HIGH:!aNULL:!MD5;
+ # ssl_prefer_server_ciphers on;
+
+ # location / {
+ # root html;
+ # index index.html index.htm;
+ # }
+ #}
+
+ #include conf.d/test.conf;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/server.conf b/certbot-nginx/tests/testdata/etc_nginx/server.conf
new file mode 100644
index 000000000..5fc4c8b24
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/server.conf
@@ -0,0 +1 @@
+server_name somename alias another.alias;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/default b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/default
new file mode 100644
index 000000000..e167761d1
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/default
@@ -0,0 +1,10 @@
+server {
+ listen myhost default_server;
+ listen otherhost default_server;
+ server_name "www.example.org";
+
+ location / {
+ root html;
+ index index.html index.htm;
+ }
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/example.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/example.com
new file mode 100644
index 000000000..fd9117188
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/example.com
@@ -0,0 +1,6 @@
+server {
+ listen 69.50.225.155:9000;
+ listen 127.0.0.1;
+ server_name .example.com;
+ server_name example.*;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/globalssl.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/globalssl.com
new file mode 100644
index 000000000..969447d6e
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/globalssl.com
@@ -0,0 +1,9 @@
+server {
+ server_name globalssl.com;
+ listen 4.8.2.6:57;
+}
+
+server {
+ server_name globalsslsetssl.com;
+ listen 4.8.2.6:57 ssl;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/headers.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/headers.com
new file mode 100644
index 000000000..6c032928c
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/headers.com
@@ -0,0 +1,4 @@
+server {
+ server_name headers.com;
+ add_header X-Content-Type-Options nosniff;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6.com
new file mode 100644
index 000000000..7a7744b92
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6.com
@@ -0,0 +1,5 @@
+server {
+ listen 80;
+ listen [::]:80;
+ server_name ipv6.com;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com
new file mode 100644
index 000000000..875a9ee1b
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com
@@ -0,0 +1,7 @@
+server {
+ listen 443 ssl;
+ listen [::]:443 ssl ipv6only=on;
+ listen 5001 ssl;
+ listen [::]:5001 ssl ipv6only=on;
+ server_name ipv6ssl.com;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/migration.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/migration.com
new file mode 100644
index 000000000..17bc6d0c3
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/migration.com
@@ -0,0 +1,19 @@
+server {
+ server_name migration.com;
+ server_name summer.com;
+}
+
+server {
+ listen 443 ssl;
+ server_name migration.com;
+ server_name geese.com;
+
+ ssl_certificate cert.pem;
+ ssl_certificate_key cert.key;
+
+ ssl_session_cache shared:SSL:1m;
+ ssl_session_timeout 5m;
+
+ ssl_ciphers HIGH:!aNULL:!MD5;
+ ssl_prefer_server_ciphers on;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/sslon.com b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/sslon.com
new file mode 100644
index 000000000..b93e6ba2d
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/sites-enabled/sslon.com
@@ -0,0 +1,6 @@
+server {
+ server_name sslon.com;
+ ssl on;
+ ssl_certificate snakeoil.cert;
+ ssl_certificate_key snakeoil.key;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params
new file mode 100644
index 000000000..4ee14e98d
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params
@@ -0,0 +1,25 @@
+fastcgi_param QUERY_STRING $query_string;
+fastcgi_param REQUEST_METHOD $request_method;
+fastcgi_param CONTENT_TYPE $content_type;
+fastcgi_param CONTENT_LENGTH $content_length;
+
+fastcgi_param SCRIPT_FILENAME $request_filename;
+fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+fastcgi_param REQUEST_URI $request_uri;
+fastcgi_param DOCUMENT_URI $document_uri;
+fastcgi_param DOCUMENT_ROOT $document_root;
+fastcgi_param SERVER_PROTOCOL $server_protocol;
+
+fastcgi_param GATEWAY_INTERFACE CGI/1.1;
+fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
+
+fastcgi_param REMOTE_ADDR $remote_addr;
+fastcgi_param REMOTE_PORT $remote_port;
+fastcgi_param SERVER_ADDR $server_addr;
+fastcgi_param SERVER_PORT $server_port;
+fastcgi_param SERVER_NAME $server_name;
+
+fastcgi_param HTTPS $https if_not_empty;
+
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf
new file mode 100644
index 000000000..1edb9474f
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf
@@ -0,0 +1,108 @@
+# This map is not a full koi8-r <> utf8 map: it does not contain
+# box-drawing and some other characters. Besides this map contains
+# several koi8-u and Byelorussian letters which are not in koi8-r.
+# If you need a full and standard map, use contrib/unicode2nginx/koi-utf
+# map instead.
+
+charset_map koi8-r utf-8 {
+
+ 80 E282AC; # euro
+
+ 95 E280A2; # bullet
+
+ 9A C2A0; # &nbsp;
+
+ 9E C2B7; # &middot;
+
+ A3 D191; # small yo
+ A4 D194; # small Ukrainian ye
+
+ A6 D196; # small Ukrainian i
+ A7 D197; # small Ukrainian yi
+
+ AD D291; # small Ukrainian soft g
+ AE D19E; # small Byelorussian short u
+
+ B0 C2B0; # &deg;
+
+ B3 D081; # capital YO
+ B4 D084; # capital Ukrainian YE
+
+ B6 D086; # capital Ukrainian I
+ B7 D087; # capital Ukrainian YI
+
+ B9 E28496; # numero sign
+
+ BD D290; # capital Ukrainian soft G
+ BE D18E; # capital Byelorussian short U
+
+ BF C2A9; # (C)
+
+ C0 D18E; # small yu
+ C1 D0B0; # small a
+ C2 D0B1; # small b
+ C3 D186; # small ts
+ C4 D0B4; # small d
+ C5 D0B5; # small ye
+ C6 D184; # small f
+ C7 D0B3; # small g
+ C8 D185; # small kh
+ C9 D0B8; # small i
+ CA D0B9; # small j
+ CB D0BA; # small k
+ CC D0BB; # small l
+ CD D0BC; # small m
+ CE D0BD; # small n
+ CF D0BE; # small o
+
+ D0 D0BF; # small p
+ D1 D18F; # small ya
+ D2 D180; # small r
+ D3 D181; # small s
+ D4 D182; # small t
+ D5 D183; # small u
+ D6 D0B6; # small zh
+ D7 D0B2; # small v
+ D8 D18C; # small soft sign
+ D9 D18B; # small y
+ DA D0B7; # small z
+ DB D188; # small sh
+ DC D18D; # small e
+ DD D189; # small shch
+ DE D187; # small ch
+ DF D18A; # small hard sign
+
+ E0 D0AE; # capital YU
+ E1 D090; # capital A
+ E2 D091; # capital B
+ E3 D0A6; # capital TS
+ E4 D094; # capital D
+ E5 D095; # capital YE
+ E6 D0A4; # capital F
+ E7 D093; # capital G
+ E8 D0A5; # capital KH
+ E9 D098; # capital I
+ EA D099; # capital J
+ EB D09A; # capital K
+ EC D09B; # capital L
+ ED D09C; # capital M
+ EE D09D; # capital N
+ EF D09E; # capital O
+
+ F0 D09F; # capital P
+ F1 D0AF; # capital YA
+ F2 D0A0; # capital R
+ F3 D0A1; # capital S
+ F4 D0A2; # capital T
+ F5 D0A3; # capital U
+ F6 D096; # capital ZH
+ F7 D092; # capital V
+ F8 D0AC; # capital soft sign
+ F9 D0AB; # capital Y
+ FA D097; # capital Z
+ FB D0A8; # capital SH
+ FC D0AD; # capital E
+ FD D0A9; # capital SHCH
+ FE D0A7; # capital CH
+ FF D0AA; # capital hard sign
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win
new file mode 100644
index 000000000..c6930fc4f
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win
@@ -0,0 +1,102 @@
+charset_map koi8-r windows-1251 {
+
+ 80 88; # euro
+
+ 95 95; # bullet
+
+ 9A A0; # &nbsp;
+
+ 9E B7; # &middot;
+
+ A3 B8; # small yo
+ A4 BA; # small Ukrainian ye
+
+ A6 B3; # small Ukrainian i
+ A7 BF; # small Ukrainian yi
+
+ AD B4; # small Ukrainian soft g
+ AE A2; # small Byelorussian short u
+
+ B0 B0; # &deg;
+
+ B3 A8; # capital YO
+ B4 AA; # capital Ukrainian YE
+
+ B6 B2; # capital Ukrainian I
+ B7 AF; # capital Ukrainian YI
+
+ B9 B9; # numero sign
+
+ BD A5; # capital Ukrainian soft G
+ BE A1; # capital Byelorussian short U
+
+ BF A9; # (C)
+
+ C0 FE; # small yu
+ C1 E0; # small a
+ C2 E1; # small b
+ C3 F6; # small ts
+ C4 E4; # small d
+ C5 E5; # small ye
+ C6 F4; # small f
+ C7 E3; # small g
+ C8 F5; # small kh
+ C9 E8; # small i
+ CA E9; # small j
+ CB EA; # small k
+ CC EB; # small l
+ CD EC; # small m
+ CE ED; # small n
+ CF EE; # small o
+
+ D0 EF; # small p
+ D1 FF; # small ya
+ D2 F0; # small r
+ D3 F1; # small s
+ D4 F2; # small t
+ D5 F3; # small u
+ D6 E6; # small zh
+ D7 E2; # small v
+ D8 FC; # small soft sign
+ D9 FB; # small y
+ DA E7; # small z
+ DB F8; # small sh
+ DC FD; # small e
+ DD F9; # small shch
+ DE F7; # small ch
+ DF FA; # small hard sign
+
+ E0 DE; # capital YU
+ E1 C0; # capital A
+ E2 C1; # capital B
+ E3 D6; # capital TS
+ E4 C4; # capital D
+ E5 C5; # capital YE
+ E6 D4; # capital F
+ E7 C3; # capital G
+ E8 D5; # capital KH
+ E9 C8; # capital I
+ EA C9; # capital J
+ EB CA; # capital K
+ EC CB; # capital L
+ ED CC; # capital M
+ EE CD; # capital N
+ EF CE; # capital O
+
+ F0 CF; # capital P
+ F1 DF; # capital YA
+ F2 D0; # capital R
+ F3 D1; # capital S
+ F4 D2; # capital T
+ F5 D3; # capital U
+ F6 C6; # capital ZH
+ F7 C2; # capital V
+ F8 DC; # capital soft sign
+ F9 DB; # capital Y
+ FA C7; # capital Z
+ FB D8; # capital SH
+ FC DD; # capital E
+ FD D9; # capital SHCH
+ FE D7; # capital CH
+ FF DA; # capital hard sign
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types
new file mode 100644
index 000000000..fcce4a58d
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types
@@ -0,0 +1,79 @@
+types {
+ text/html html htm shtml;
+ text/css css;
+ text/xml xml rss;
+ image/gif gif;
+ image/jpeg jpeg jpg;
+ application/x-javascript js;
+ application/atom+xml atom;
+
+ text/mathml mml;
+ text/plain txt;
+ text/vnd.sun.j2me.app-descriptor jad;
+ text/vnd.wap.wml wml;
+ text/x-component htc;
+
+ image/png png;
+ image/tiff tif tiff;
+ image/vnd.wap.wbmp wbmp;
+ image/x-icon ico;
+ image/x-jng jng;
+ image/x-ms-bmp bmp;
+ image/svg+xml svg svgz;
+
+ application/java-archive jar war ear;
+ application/json json;
+ application/mac-binhex40 hqx;
+ application/msword doc;
+ application/pdf pdf;
+ application/postscript ps eps ai;
+ application/rtf rtf;
+ application/vnd.ms-excel xls;
+ application/vnd.ms-powerpoint ppt;
+ application/vnd.wap.wmlc wmlc;
+ application/vnd.google-earth.kml+xml kml;
+ application/vnd.google-earth.kmz kmz;
+ application/x-7z-compressed 7z;
+ application/x-cocoa cco;
+ application/x-java-archive-diff jardiff;
+ application/x-java-jnlp-file jnlp;
+ application/x-makeself run;
+ application/x-perl pl pm;
+ application/x-pilot prc pdb;
+ application/x-rar-compressed rar;
+ application/x-redhat-package-manager rpm;
+ application/x-sea sea;
+ application/x-shockwave-flash swf;
+ application/x-stuffit sit;
+ application/x-tcl tcl tk;
+ application/x-x509-ca-cert der pem crt;
+ application/x-xpinstall xpi;
+ application/xhtml+xml xhtml;
+ application/zip zip;
+
+ application/octet-stream bin exe dll;
+ application/octet-stream deb;
+ application/octet-stream dmg;
+ application/octet-stream eot;
+ application/octet-stream iso img;
+ application/octet-stream msi msp msm;
+ application/ogg ogx;
+
+ audio/midi mid midi kar;
+ audio/mpeg mpga mpega mp2 mp3 m4a;
+ audio/ogg oga ogg spx;
+ audio/x-realaudio ra;
+ audio/webm weba;
+
+ video/3gpp 3gpp 3gp;
+ video/mp4 mp4;
+ video/mpeg mpeg mpg mpe;
+ video/ogg ogv;
+ video/quicktime mov;
+ video/webm webm;
+ video/x-flv flv;
+ video/x-mng mng;
+ video/x-ms-asf asx asf;
+ video/x-ms-wmv wmv;
+ video/x-msvideo avi;
+}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1
new file mode 100644
index 000000000..f4eb9d49d
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1
@@ -0,0 +1,16 @@
+[nx_extract]
+username = naxsi_web
+password = test
+port = 8081
+rules_path = /etc/nginx/naxsi_core.rules
+
+[nx_intercept]
+port = 8080
+
+[sql]
+dbtype = sqlite
+username = root
+password =
+hostname = 127.0.0.1
+dbname = naxsi_sig
+
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules
new file mode 100644
index 000000000..fec21ea4f
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules
@@ -0,0 +1,13 @@
+# Sample rules file for default vhost.
+
+LearningMode;
+SecRulesEnabled;
+#SecRulesDisabled;
+DeniedUrl "/RequestDenied";
+
+## check rules
+CheckRule "$SQL >= 8" BLOCK;
+CheckRule "$RFI >= 8" BLOCK;
+CheckRule "$TRAVERSAL >= 4" BLOCK;
+CheckRule "$EVADE >= 4" BLOCK;
+CheckRule "$XSS >= 8" BLOCK;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules
new file mode 100644
index 000000000..9826e02cb
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules
@@ -0,0 +1,75 @@
+##################################
+## INTERNAL RULES IDS:1-10 ##
+##################################
+#weird_request : 1
+#big_body : 2
+#no_content_type : 3
+
+#MainRule "str:yesone" "msg:foobar test pattern" "mz:ARGS" "s:$SQL:42" id:1999;
+
+##################################
+## SQL Injections IDs:1000-1099 ##
+##################################
+MainRule "rx:select|union|update|delete|insert|table|from|ascii|hex|unhex" "msg:sql keywords" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1000;
+MainRule "str:\"" "msg:double quote" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1001;
+MainRule "str:0x" "msg:0x, possible hex encoding" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:2" id:1002;
+## Hardcore rules
+MainRule "str:/*" "msg:mysql comment (/*)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1003;
+MainRule "str:*/" "msg:mysql comment (*/)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1004;
+MainRule "str:|" "msg:mysql keyword (|)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1005;
+MainRule "rx:&&" "msg:mysql keyword (&&)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:8" id:1006;
+## end of hardcore rules
+MainRule "str:--" "msg:mysql comment (--)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1007;
+MainRule "str:;" "msg:; in stuff" "mz:BODY|URL|ARGS" "s:$SQL:4" id:1008;
+MainRule "str:=" "msg:equal in var, probable sql/xss" "mz:ARGS|BODY" "s:$SQL:2" id:1009;
+MainRule "str:(" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1010;
+MainRule "str:)" "msg:parenthesis, probable sql/xss" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1011;
+MainRule "str:'" "msg:simple quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1013;
+MainRule "str:\"" "msg:double quote" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1014;
+MainRule "str:," "msg:, in stuff" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1015;
+MainRule "str:#" "msg:mysql comment (#)" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$SQL:4" id:1016;
+
+###############################
+## OBVIOUS RFI IDs:1100-1199 ##
+###############################
+MainRule "str:http://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1100;
+MainRule "str:https://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1101;
+MainRule "str:ftp://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1102;
+MainRule "str:php://" "msg:html comment tag" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$RFI:8" id:1103;
+
+#######################################
+## Directory traversal IDs:1200-1299 ##
+#######################################
+MainRule "str:.." "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1200;
+MainRule "str:/etc/passwd" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1202;
+MainRule "str:c:\\" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1203;
+MainRule "str:cmd.exe" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1204;
+MainRule "str:\\" "msg:html comment tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:4" id:1205;
+#MainRule "str:/" "msg:slash in args" "mz:ARGS|BODY|$HEADERS_VAR:Cookie" "s:$TRAVERSAL:2" id:1206;
+########################################
+## Cross Site Scripting IDs:1300-1399 ##
+########################################
+MainRule "str:<" "msg:html open tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1302;
+MainRule "str:>" "msg:html close tag" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1303;
+MainRule "str:'" "msg:simple quote" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1306;
+MainRule "str:\"" "msg:double quote" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1307;
+MainRule "str:(" "msg:parenthesis" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1308;
+MainRule "str:)" "msg:parenthesis" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1309;
+MainRule "str:[" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1310;
+MainRule "str:]" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1311;
+MainRule "str:~" "msg:html close comment tag" "mz:BODY|URL|ARGS|$HEADERS_VAR:Cookie" "s:$XSS:4" id:1312;
+MainRule "str:;" "msg:semi coma" "mz:ARGS|URL|BODY" "s:$XSS:8" id:1313;
+MainRule "str:`" "msg:grave accent !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1314;
+MainRule "rx:%[2|3]." "msg:double encoding !" "mz:ARGS|URL|BODY|$HEADERS_VAR:Cookie" "s:$XSS:8" id:1315;
+
+####################################
+## Evading tricks IDs: 1400-1500 ##
+####################################
+MainRule "str:&#" "msg: utf7/8 encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1400;
+MainRule "str:%U" "msg: M$ encoding" "mz:ARGS|BODY|URL|$HEADERS_VAR:Cookie" "s:$EVADE:4" id:1401;
+MainRule negative "rx:multipart/form-data|application/x-www-form-urlencoded" "msg:Content is neither multipart/x-www-form.." "mz:$HEADERS_VAR:Content-type" "s:$EVADE:4" id:1402;
+
+#############################
+## File uploads: 1500-1600 ##
+#############################
+MainRule "rx:.ph*|.asp*" "msg:asp/php file upload!" "mz:FILE_EXT" "s:$UPLOAD:8" id:1500;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf
new file mode 100644
index 000000000..52219b940
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf
@@ -0,0 +1,95 @@
+user www-data;
+worker_processes 4;
+pid /run/nginx.pid;
+
+events {
+ worker_connections 768;
+ # multi_accept on;
+}
+
+http {
+
+ ##
+ # Basic Settings
+ ##
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+ # server_tokens off;
+
+ # server_names_hash_bucket_size 64;
+ # server_name_in_redirect off;
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ ##
+ # Logging Settings
+ ##
+
+ access_log /var/log/nginx/access.log;
+ error_log /var/log/nginx/error.log;
+
+ ##
+ # Gzip Settings
+ ##
+
+ gzip on;
+ gzip_disable "msie6";
+
+ # gzip_vary on;
+ # gzip_proxied any;
+ # gzip_comp_level 6;
+ # gzip_buffers 16 8k;
+ # gzip_http_version 1.1;
+ # gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
+
+ ##
+ # nginx-naxsi config
+ ##
+ # Uncomment it if you installed nginx-naxsi
+ ##
+
+ #include /etc/nginx/naxsi_core.rules;
+
+ ##
+ # nginx-passenger config
+ ##
+ # Uncomment it if you installed nginx-passenger
+ ##
+
+ #passenger_root /usr;
+ #passenger_ruby /usr/bin/ruby;
+
+ ##
+ # Virtual Host Configs
+ ##
+
+ include /etc/nginx/conf.d/*.conf;
+ include /etc/nginx/sites-enabled/*;
+}
+
+
+#mail {
+# # See sample authentication script at:
+# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
+#
+# # auth_http localhost/auth.php;
+# # pop3_capabilities "TOP" "USER";
+# # imap_capabilities "IMAP4rev1" "UIDPLUS";
+#
+# server {
+# listen localhost:110;
+# protocol pop3;
+# proxy on;
+# }
+#
+# server {
+# listen localhost:143;
+# protocol imap;
+# proxy on;
+# }
+#}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params
new file mode 100644
index 000000000..df75bc5d7
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params
@@ -0,0 +1,4 @@
+proxy_set_header Host $http_host;
+proxy_set_header X-Real-IP $remote_addr;
+proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+proxy_set_header X-Forwarded-Proto $scheme;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params
new file mode 100644
index 000000000..76e858628
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params
@@ -0,0 +1,14 @@
+scgi_param REQUEST_METHOD $request_method;
+scgi_param REQUEST_URI $request_uri;
+scgi_param QUERY_STRING $query_string;
+scgi_param CONTENT_TYPE $content_type;
+
+scgi_param DOCUMENT_URI $document_uri;
+scgi_param DOCUMENT_ROOT $document_root;
+scgi_param SCGI 1;
+scgi_param SERVER_PROTOCOL $server_protocol;
+
+scgi_param REMOTE_ADDR $remote_addr;
+scgi_param REMOTE_PORT $remote_port;
+scgi_param SERVER_PORT $server_port;
+scgi_param SERVER_NAME $server_name;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default
new file mode 100644
index 000000000..5d8f3ac15
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default
@@ -0,0 +1,112 @@
+# You may add here your
+# server {
+# ...
+# }
+# statements for each of your virtual hosts to this file
+
+##
+# You should look at the following URL's in order to grasp a solid understanding
+# of Nginx configuration files in order to fully unleash the power of Nginx.
+# http://wiki.nginx.org/Pitfalls
+# http://wiki.nginx.org/QuickStart
+# http://wiki.nginx.org/Configuration
+#
+# Generally, you will want to move this file somewhere, and start with a clean
+# file but keep this around for reference. Or just disable in sites-enabled.
+#
+# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
+##
+
+server {
+ listen 80 default_server;
+ listen [::]:80 default_server ipv6only=on;
+
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+
+ # Make site accessible from http://localhost/
+ server_name localhost;
+
+ location / {
+ # First attempt to serve request as file, then
+ # as directory, then fall back to displaying a 404.
+ try_files $uri $uri/ =404;
+ # Uncomment to enable naxsi on this location
+ # include /etc/nginx/naxsi.rules
+ }
+
+ # Only for nginx-naxsi used with nginx-naxsi-ui : process denied requests
+ #location /RequestDenied {
+ # proxy_pass http://127.0.0.1:8080;
+ #}
+
+ #error_page 404 /404.html;
+
+ # redirect server error pages to the static page /50x.html
+ #
+ #error_page 500 502 503 504 /50x.html;
+ #location = /50x.html {
+ # root /usr/share/nginx/html;
+ #}
+
+ # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
+ #
+ #location ~ \.php$ {
+ # fastcgi_split_path_info ^(.+\.php)(/.+)$;
+ # # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
+ #
+ # # With php5-cgi alone:
+ # fastcgi_pass 127.0.0.1:9000;
+ # # With php5-fpm:
+ # fastcgi_pass unix:/var/run/php5-fpm.sock;
+ # fastcgi_index index.php;
+ # include fastcgi_params;
+ #}
+
+ # deny access to .htaccess files, if Apache's document root
+ # concurs with nginx's one
+ #
+ #location ~ /\.ht {
+ # deny all;
+ #}
+}
+
+
+# another virtual host using mix of IP-, name-, and port-based configuration
+#
+#server {
+# listen 8000;
+# listen somename:8080;
+# server_name somename alias another.alias;
+# root html;
+# index index.html index.htm;
+#
+# location / {
+# try_files $uri $uri/ =404;
+# }
+#}
+
+
+# HTTPS server
+#
+#server {
+# listen 443;
+# server_name localhost;
+#
+# root html;
+# index index.html index.htm;
+#
+# ssl on;
+# ssl_certificate cert.pem;
+# ssl_certificate_key cert.key;
+#
+# ssl_session_timeout 5m;
+#
+# ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
+# ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
+# ssl_prefer_server_ciphers on;
+#
+# location / {
+# try_files $uri $uri/ =404;
+# }
+#}
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default
new file mode 120000
index 000000000..6d9ba3371
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default
@@ -0,0 +1 @@
+../sites-available/default \ No newline at end of file
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params
new file mode 100644
index 000000000..3f72dbf0e
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params
@@ -0,0 +1,15 @@
+uwsgi_param QUERY_STRING $query_string;
+uwsgi_param REQUEST_METHOD $request_method;
+uwsgi_param CONTENT_TYPE $content_type;
+uwsgi_param CONTENT_LENGTH $content_length;
+
+uwsgi_param REQUEST_URI $request_uri;
+uwsgi_param PATH_INFO $document_uri;
+uwsgi_param DOCUMENT_ROOT $document_root;
+uwsgi_param SERVER_PROTOCOL $server_protocol;
+uwsgi_param UWSGI_SCHEME $scheme;
+
+uwsgi_param REMOTE_ADDR $remote_addr;
+uwsgi_param REMOTE_PORT $remote_port;
+uwsgi_param SERVER_PORT $server_port;
+uwsgi_param SERVER_NAME $server_name;
diff --git a/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf
new file mode 100644
index 000000000..cd2885292
--- /dev/null
+++ b/certbot-nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf
@@ -0,0 +1,125 @@
+# This map is not a full windows-1251 <> utf8 map: it does not
+# contain Serbian and Macedonian letters. If you need a full map,
+# use contrib/unicode2nginx/win-utf map instead.
+
+charset_map windows-1251 utf-8 {
+
+ 82 E2809A; # single low-9 quotation mark
+
+ 84 E2809E; # double low-9 quotation mark
+ 85 E280A6; # ellipsis
+ 86 E280A0; # dagger
+ 87 E280A1; # double dagger
+ 88 E282AC; # euro
+ 89 E280B0; # per mille
+
+ 91 E28098; # left single quotation mark
+ 92 E28099; # right single quotation mark
+ 93 E2809C; # left double quotation mark
+ 94 E2809D; # right double quotation mark
+ 95 E280A2; # bullet
+ 96 E28093; # en dash
+ 97 E28094; # em dash
+
+ 99 E284A2; # trade mark sign
+
+ A0 C2A0; # &nbsp;
+ A1 D18E; # capital Byelorussian short U
+ A2 D19E; # small Byelorussian short u
+
+ A4 C2A4; # currency sign
+ A5 D290; # capital Ukrainian soft G
+ A6 C2A6; # borken bar
+ A7 C2A7; # section sign
+ A8 D081; # capital YO
+ A9 C2A9; # (C)
+ AA D084; # capital Ukrainian YE
+ AB C2AB; # left-pointing double angle quotation mark
+ AC C2AC; # not sign
+ AD C2AD; # soft hyphen
+ AE C2AE; # (R)
+ AF D087; # capital Ukrainian YI
+
+ B0 C2B0; # &deg;
+ B1 C2B1; # plus-minus sign
+ B2 D086; # capital Ukrainian I
+ B3 D196; # small Ukrainian i
+ B4 D291; # small Ukrainian soft g
+ B5 C2B5; # micro sign
+ B6 C2B6; # pilcrow sign
+ B7 C2B7; # &middot;
+ B8 D191; # small yo
+ B9 E28496; # numero sign
+ BA D194; # small Ukrainian ye
+ BB C2BB; # right-pointing double angle quotation mark
+
+ BF D197; # small Ukrainian yi
+
+ C0 D090; # capital A
+ C1 D091; # capital B
+ C2 D092; # capital V
+ C3 D093; # capital G
+ C4 D094; # capital D
+ C5 D095; # capital YE
+ C6 D096; # capital ZH
+ C7 D097; # capital Z
+ C8 D098; # capital I
+ C9 D099; # capital J
+ CA D09A; # capital K
+ CB D09B; # capital L
+ CC D09C; # capital M
+ CD D09D; # capital N
+ CE D09E; # capital O
+ CF D09F; # capital P
+
+ D0 D0A0; # capital R
+ D1 D0A1; # capital S
+ D2 D0A2; # capital T
+ D3 D0A3; # capital U
+ D4 D0A4; # capital F
+ D5 D0A5; # capital KH
+ D6 D0A6; # capital TS
+ D7 D0A7; # capital CH
+ D8 D0A8; # capital SH
+ D9 D0A9; # capital SHCH
+ DA D0AA; # capital hard sign
+ DB D0AB; # capital Y
+ DC D0AC; # capital soft sign
+ DD D0AD; # capital E
+ DE D0AE; # capital YU
+ DF D0AF; # capital YA
+
+ E0 D0B0; # small a
+ E1 D0B1; # small b
+ E2 D0B2; # small v
+ E3 D0B3; # small g
+ E4 D0B4; # small d
+ E5 D0B5; # small ye
+ E6 D0B6; # small zh
+ E7 D0B7; # small z
+ E8 D0B8; # small i
+ E9 D0B9; # small j
+ EA D0BA; # small k
+ EB D0BB; # small l
+ EC D0BC; # small m
+ ED D0BD; # small n
+ EE D0BE; # small o
+ EF D0BF; # small p
+
+ F0 D180; # small r
+ F1 D181; # small s
+ F2 D182; # small t
+ F3 D183; # small u
+ F4 D184; # small f
+ F5 D185; # small kh
+ F6 D186; # small ts
+ F7 D187; # small ch
+ F8 D188; # small sh
+ F9 D189; # small shch
+ FA D18A; # small hard sign
+ FB D18B; # small y
+ FC D18C; # small soft sign
+ FD D18D; # small e
+ FE D18E; # small yu
+ FF D18F; # small ya
+}