Skip to content

Commit d3e997a

Browse files
author
Seth Schoen
committed
Merge remote-tracking branch 'origin/master' into nginx-map-parsing
2 parents 68500cd + 5c32979 commit d3e997a

File tree

218 files changed

+8684
-65
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

218 files changed

+8684
-65
lines changed

certbot-apache/certbot_apache/configurator.py

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from certbot import util
1919

2020
from certbot.plugins import common
21+
from certbot.plugins.util import path_surgery
2122

2223
from certbot_apache import augeas_configurator
2324
from certbot_apache import constants
@@ -141,6 +142,7 @@ def mod_ssl_conf(self):
141142
return os.path.join(self.config.config_dir,
142143
constants.MOD_SSL_CONF_DEST)
143144

145+
144146
def prepare(self):
145147
"""Prepare the authenticator/installer.
146148
@@ -157,8 +159,11 @@ def prepare(self):
157159
raise errors.NoInstallationError("Problem in Augeas installation")
158160

159161
# Verify Apache is installed
160-
if not util.exe_exists(constants.os_constant("restart_cmd")[0]):
161-
raise errors.NoInstallationError
162+
restart_cmd = constants.os_constant("restart_cmd")[0]
163+
if not util.exe_exists(restart_cmd):
164+
if not path_surgery(restart_cmd):
165+
raise errors.NoInstallationError(
166+
'Cannot find Apache control command {0}'.format(restart_cmd))
162167

163168
# Make sure configuration is valid
164169
self.config_test()
@@ -819,7 +824,7 @@ def _get_ssl_vhost_path(self, non_ssl_vh_fp):
819824
else:
820825
return non_ssl_vh_fp + self.conf("le_vhost_ext")
821826

822-
def _sift_line(self, line):
827+
def _sift_rewrite_rule(self, line):
823828
"""Decides whether a line should be copied to a SSL vhost.
824829
825830
A canonical example of when sifting a line is required:
@@ -870,18 +875,62 @@ def _copy_create_ssl_vhost_skeleton(self, avail_fp, ssl_fp):
870875
with open(avail_fp, "r") as orig_file:
871876
with open(ssl_fp, "w") as new_file:
872877
new_file.write("<IfModule mod_ssl.c>\n")
878+
879+
comment = ("# Some rewrite rules in this file were "
880+
"disabled on your HTTPS site,\n"
881+
"# because they have the potential to create "
882+
"redirection loops.\n")
883+
873884
for line in orig_file:
874-
if self._sift_line(line):
885+
A = line.lstrip().startswith("RewriteCond")
886+
B = line.lstrip().startswith("RewriteRule")
887+
888+
if not (A or B):
889+
new_file.write(line)
890+
continue
891+
892+
# A RewriteRule that doesn't need filtering
893+
if B and not self._sift_rewrite_rule(line):
894+
new_file.write(line)
895+
continue
896+
897+
# A RewriteRule that does need filtering
898+
if B and self._sift_rewrite_rule(line):
875899
if not sift:
876-
new_file.write(
877-
"# Some rewrite rules in this file were "
878-
"were disabled on your HTTPS site,\n"
879-
"# because they have the potential to "
880-
"create redirection loops.\n")
900+
new_file.write(comment)
881901
sift = True
882902
new_file.write("# " + line)
883-
else:
884-
new_file.write(line)
903+
continue
904+
905+
# We save RewriteCond(s) and their corresponding
906+
# RewriteRule in 'chunk'.
907+
# We then decide whether we comment out the entire
908+
# chunk based on its RewriteRule.
909+
chunk = []
910+
if A:
911+
chunk.append(line)
912+
line = next(orig_file)
913+
914+
# RewriteCond(s) must be followed by one RewriteRule
915+
while not line.lstrip().startswith("RewriteRule"):
916+
chunk.append(line)
917+
line = next(orig_file)
918+
919+
# Now, current line must start with a RewriteRule
920+
chunk.append(line)
921+
922+
if self._sift_rewrite_rule(line):
923+
if not sift:
924+
new_file.write(comment)
925+
sift = True
926+
927+
new_file.write(''.join(
928+
['# ' + l for l in chunk]))
929+
continue
930+
else:
931+
new_file.write(''.join(chunk))
932+
continue
933+
885934
new_file.write("</IfModule>\n")
886935
except IOError:
887936
logger.fatal("Error writing/reading to file in make_vhost_ssl")

certbot-apache/certbot_apache/tests/configurator_test.py

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# pylint: disable=too-many-public-methods
1+
# pylint: disable=too-many-public-methods,too-many-lines
22
"""Test for certbot_apache.configurator."""
33
import os
44
import shutil
@@ -49,11 +49,14 @@ def tearDown(self):
4949
shutil.rmtree(self.config_dir)
5050
shutil.rmtree(self.work_dir)
5151

52-
@mock.patch("certbot_apache.configurator.util.exe_exists")
53-
def test_prepare_no_install(self, mock_exe_exists):
54-
mock_exe_exists.return_value = False
55-
self.assertRaises(
56-
errors.NoInstallationError, self.config.prepare)
52+
@mock.patch("certbot_apache.configurator.ApacheConfigurator.init_augeas")
53+
@mock.patch("certbot_apache.configurator.path_surgery")
54+
def test_prepare_no_install(self, mock_surgery, _init_augeas):
55+
silly_path = {"PATH": "/tmp/nothingness2342"}
56+
mock_surgery.return_value = False
57+
with mock.patch.dict('os.environ', silly_path):
58+
self.assertRaises(errors.NoInstallationError, self.config.prepare)
59+
self.assertEquals(mock_surgery.call_count, 1)
5760

5861
@mock.patch("certbot_apache.augeas_configurator.AugeasConfigurator.init_augeas")
5962
def test_prepare_no_augeas(self, mock_init_augeas):
@@ -86,6 +89,7 @@ def test_prepare_old_aug(self, mock_exe_exists, _):
8689
self.assertRaises(
8790
errors.NotSupportedError, self.config.prepare)
8891

92+
8993
def test_add_parser_arguments(self): # pylint: disable=no-self-use
9094
from certbot_apache.configurator import ApacheConfigurator
9195
# Weak test..
@@ -1110,16 +1114,19 @@ def test_create_own_redirect_for_old_apache_version(self):
11101114
self.config._enable_redirect(self.vh_truth[1], "")
11111115
self.assertEqual(len(self.config.vhosts), 9)
11121116

1113-
def test_sift_line(self):
1117+
def test_sift_rewrite_rule(self):
11141118
# pylint: disable=protected-access
11151119
small_quoted_target = "RewriteRule ^ \"http://\""
1116-
self.assertFalse(self.config._sift_line(small_quoted_target))
1120+
self.assertFalse(self.config._sift_rewrite_rule(small_quoted_target))
11171121

11181122
https_target = "RewriteRule ^ https://satoshi"
1119-
self.assertTrue(self.config._sift_line(https_target))
1123+
self.assertTrue(self.config._sift_rewrite_rule(https_target))
11201124

11211125
normal_target = "RewriteRule ^/(.*) http://www.a.com:1234/$1 [L,R]"
1122-
self.assertFalse(self.config._sift_line(normal_target))
1126+
self.assertFalse(self.config._sift_rewrite_rule(normal_target))
1127+
1128+
not_rewriterule = "NotRewriteRule ^ ..."
1129+
self.assertFalse(self.config._sift_rewrite_rule(not_rewriterule))
11231130

11241131
@mock.patch("certbot_apache.configurator.zope.component.getUtility")
11251132
def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility):
@@ -1147,9 +1154,63 @@ def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility):
11471154
"https://%{SERVER_NAME}%{REQUEST_URI} "
11481155
"[L,QSA,R=permanent]")
11491156
self.assertTrue(commented_rewrite_rule in conf_text)
1157+
mock_get_utility().add_message.assert_called_once_with(mock.ANY,
1158+
1159+
mock.ANY)
1160+
@mock.patch("certbot_apache.configurator.zope.component.getUtility")
1161+
def test_make_vhost_ssl_with_existing_rewrite_conds(self, mock_get_utility):
1162+
self.config.parser.modules.add("rewrite_module")
1163+
1164+
http_vhost = self.vh_truth[0]
1165+
1166+
self.config.parser.add_dir(
1167+
http_vhost.path, "RewriteEngine", "on")
1168+
1169+
# Add a chunk that should not be commented out.
1170+
self.config.parser.add_dir(http_vhost.path,
1171+
"RewriteCond", ["%{DOCUMENT_ROOT}/%{REQUEST_FILENAME}", "!-f"])
1172+
self.config.parser.add_dir(
1173+
http_vhost.path, "RewriteRule",
1174+
["^(.*)$", "b://u%{REQUEST_URI}", "[P,QSA,L]"])
1175+
1176+
# Add a chunk that should be commented out.
1177+
self.config.parser.add_dir(http_vhost.path,
1178+
"RewriteCond", ["%{HTTPS}", "!=on"])
1179+
self.config.parser.add_dir(http_vhost.path,
1180+
"RewriteCond", ["%{HTTPS}", "!^$"])
1181+
self.config.parser.add_dir(
1182+
http_vhost.path, "RewriteRule",
1183+
["^",
1184+
"https://%{SERVER_NAME}%{REQUEST_URI}",
1185+
"[L,QSA,R=permanent]"])
1186+
1187+
self.config.save()
1188+
1189+
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
1190+
1191+
conf_line_set = set(open(ssl_vhost.filep).read().splitlines())
1192+
1193+
not_commented_cond1 = ("RewriteCond "
1194+
"%{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f")
1195+
not_commented_rewrite_rule = ("RewriteRule "
1196+
"^(.*)$ b://u%{REQUEST_URI} [P,QSA,L]")
1197+
1198+
commented_cond1 = "# RewriteCond %{HTTPS} !=on"
1199+
commented_cond2 = "# RewriteCond %{HTTPS} !^$"
1200+
commented_rewrite_rule = ("# RewriteRule ^ "
1201+
"https://%{SERVER_NAME}%{REQUEST_URI} "
1202+
"[L,QSA,R=permanent]")
1203+
1204+
self.assertTrue(not_commented_cond1 in conf_line_set)
1205+
self.assertTrue(not_commented_rewrite_rule in conf_line_set)
1206+
1207+
self.assertTrue(commented_cond1 in conf_line_set)
1208+
self.assertTrue(commented_cond2 in conf_line_set)
1209+
self.assertTrue(commented_rewrite_rule in conf_line_set)
11501210
mock_get_utility().add_message.assert_called_once_with(mock.ANY,
11511211
mock.ANY)
11521212

1213+
11531214
def get_achalls(self):
11541215
"""Return testing achallenges."""
11551216
account_key = self.rsa512jwk
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Eventually there will also be a compatibility test here like the Apache one.
2+
3+
Right now, this is data for the roundtrip test (checking that the parser
4+
can parse each file and that the reserialized config file it generates is
5+
identical to the original).
6+
7+
If run in a virtualenv or otherwise so that certbot_nginx can be imported,
8+
the roundtrip test can run as
9+
10+
python roundtrip.py nginx-roundtrip-testdata
11+
12+
It gives exit status 0 for success and 1 if at least one parse or roundtrip
13+
failure occurred.
14+
15+
16+
The directory nginx-roundtrip-testdata includes some config files that were
17+
contributed to our project as well as most of the configs linked from
18+
19+
https://www.nginx.com/resources/wiki/start/
20+
21+
Some exceptions that were skipped are
22+
23+
https://www.nginx.com/resources/wiki/start/topics/recipes/moinmoin/
24+
https://www.nginx.com/resources/wiki/start/topics/examples/SSL-Offloader/ (not much nginx configuration)
25+
https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/ (likewise)
26+
https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
27+
https://www.nginx.com/resources/wiki/start/topics/examples/fcgiwrap/
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
upstream django_server_random18709.example.org {
2+
server unix:/srv/http/random22194/live/website.sock;
3+
}
4+
5+
server {
6+
listen 80;
7+
server_name random18709.example.org;
8+
9+
location /media/ {
10+
alias /srv/http/random22194/live/dynamic/public/;
11+
expires 7d;
12+
include upload_folder_security_params;
13+
}
14+
location /static/ {
15+
alias /srv/http/random22194/live/static_collected/;
16+
expires 7d;
17+
}
18+
19+
location / {
20+
proxy_pass http://django_server_random18709.example.org;
21+
proxy_set_header Host $host;
22+
proxy_set_header X-Real-IP $remote_addr;
23+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
24+
}
25+
26+
access_log /var/log/nginx/random22194/live/access.log combined_plus;
27+
error_log /var/log/nginx/random22194/live/error.log;
28+
}
29+
30+
server {
31+
server_name www.random18709.example.org;
32+
server_name random24607.example.org www.random24607.example.org;
33+
return 301 http://random18709.example.org$request_uri;
34+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
upstream django_server_random1413.example.org {
2+
server unix:/srv/http/random25151/live/website.sock;
3+
}
4+
5+
server {
6+
listen 443;
7+
server_name www.random25266.example.org;
8+
9+
ssl on;
10+
ssl_certificate /etc/ssl/public/random25266.example.org.bundle.crt;
11+
ssl_certificate_key /etc/ssl/private/random25266.example.org.key;
12+
13+
location /media/ {
14+
alias /srv/http/random25151/live/dynamic/public/;
15+
expires 7d;
16+
include upload_folder_security_params;
17+
}
18+
location /static/ {
19+
alias /srv/http/random25151/live/static_collected/;
20+
expires 7d;
21+
}
22+
23+
location / {
24+
proxy_pass http://django_server_random1413.example.org;
25+
proxy_set_header Host $host;
26+
proxy_set_header X-Real-IP $remote_addr;
27+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
28+
}
29+
30+
access_log /var/log/nginx/random25151/live/access.log combined_plus;
31+
error_log /var/log/nginx/random25151/live/error.log;
32+
}
33+
34+
35+
server {
36+
listen 443;
37+
server_name random1413.example.org www.random1413.example.org;
38+
39+
ssl on;
40+
ssl_certificate /etc/ssl/public/random1413.example.org.bundle.crt;
41+
ssl_certificate_key /etc/ssl/private/random1413.example.org.key;
42+
43+
location / {
44+
return 301 https://www.random25266.example.org$request_uri;
45+
}
46+
}
47+
48+
server {
49+
listen 443;
50+
server_name random25266.example.org;
51+
52+
ssl on;
53+
ssl_certificate /etc/ssl/public/random25266.example.org.bundle.crt;
54+
ssl_certificate_key /etc/ssl/private/random25266.example.org.key;
55+
56+
location / {
57+
return 301 https://www.random25266.example.org$request_uri;
58+
}
59+
}
60+
61+
server {
62+
listen 80;
63+
server_name random1413.example.org www.random1413.example.org;
64+
server_name random28524.example.org www.random28524.example.org;
65+
server_name random25266.example.org www.random25266.example.org;
66+
server_name random26791.example.org www.random26791.example.org;
67+
68+
location / {
69+
return 301 https://www.random25266.example.org$request_uri;
70+
}
71+
}

0 commit comments

Comments
 (0)