Merge pull request #3184 from certbot/nginx-space-preservation

Nginx space preservation
This commit is contained in:
schoen
2016-06-27 12:50:34 -07:00
committed by GitHub
11 changed files with 340 additions and 217 deletions
+10 -10
View File
@@ -152,17 +152,17 @@ class NginxConfigurator(common.Plugin):
"install a cert.")
vhost = self.choose_vhost(domain)
cert_directives = [['ssl_certificate', fullchain_path],
['ssl_certificate_key', key_path]]
cert_directives = [['\n', 'ssl_certificate', ' ', fullchain_path],
['\n', 'ssl_certificate_key', ' ', key_path]]
# OCSP stapling was introduced in Nginx 1.3.7. If we have that version
# or greater, add config settings for it.
stapling_directives = []
if self.version >= (1, 3, 7):
stapling_directives = [
['ssl_trusted_certificate', chain_path],
['ssl_stapling', 'on'],
['ssl_stapling_verify', 'on']]
['\n', 'ssl_trusted_certificate', ' ', chain_path],
['\n', 'ssl_stapling', ' ', 'on'],
['\n', 'ssl_stapling_verify', ' ', 'on'], ['\n']]
if len(stapling_directives) != 0 and not chain_path:
raise errors.PluginError(
@@ -225,7 +225,7 @@ class NginxConfigurator(common.Plugin):
if not matches:
# No matches. Create a new vhost with this name in nginx.conf.
filep = self.parser.loc["root"]
new_block = [['server'], [['server_name', target_name]]]
new_block = [['server'], [['\n', 'server_name', ' ', target_name]]]
self.parser.add_http_directives(filep, new_block)
vhost = obj.VirtualHost(filep, set([]), False, True,
set([target_name]), list(new_block[1]))
@@ -337,10 +337,10 @@ class NginxConfigurator(common.Plugin):
"""
snakeoil_cert, snakeoil_key = self._get_snakeoil_paths()
ssl_block = [['listen', '{0} ssl'.format(self.config.tls_sni_01_port)],
['ssl_certificate', snakeoil_cert],
['ssl_certificate_key', snakeoil_key],
['include', self.parser.loc["ssl_options"]]]
ssl_block = [['\n', 'listen', ' ', '{0} ssl'.format(self.config.tls_sni_01_port)],
['\n', 'ssl_certificate', ' ', snakeoil_cert],
['\n', 'ssl_certificate_key', ' ', snakeoil_key],
['\n', 'include', ' ', self.parser.loc["ssl_options"]]]
self.parser.add_server_directives(
vhost.filep, vhost.names, ssl_block, replace=False)
vhost.ssl = True
+162 -39
View File
@@ -1,23 +1,29 @@
"""Very low-level nginx config parser based on pyparsing."""
import copy
import logging
import string
from pyparsing import (
Literal, White, Word, alphanums, CharsNotIn, Forward, Group,
Literal, White, Word, alphanums, CharsNotIn, Combine, Forward, Group,
Optional, OneOrMore, Regex, ZeroOrMore)
from pyparsing import stringEnd
from pyparsing import restOfLine
logger = logging.getLogger(__name__)
class RawNginxParser(object):
# pylint: disable=expression-not-assigned
"""A class that parses nginx configuration with pyparsing."""
# constants
space = Optional(White())
nonspace = Regex(r"\S+")
left_bracket = Literal("{").suppress()
right_bracket = Literal("}").suppress()
right_bracket = space.leaveWhitespace() + Literal("}").suppress()
semicolon = Literal(";").suppress()
space = White().suppress()
key = Word(alphanums + "_/+-.")
dollar_var = Combine(Literal('$') + nonspace)
condition = Regex(r"\(.+\)")
# Matches anything that is not a special character AND any chars in single
# or double quotes
value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+")
@@ -26,20 +32,25 @@ class RawNginxParser(object):
modifier = Literal("=") | Literal("~*") | Literal("~") | Literal("^~")
# rules
comment = Literal('#') + restOfLine()
assignment = (key + Optional(space + value, default=None) + semicolon)
location_statement = Optional(space + modifier) + Optional(space + location)
if_statement = Literal("if") + space + Regex(r"\(.+\)") + space
map_statement = Literal("map") + space + Regex(r"\S+") + space + Regex(r"\$\S+") + space
comment = space + Literal('#') + restOfLine()
assignment = space + key + Optional(space + value, default=None) + semicolon
location_statement = space + Optional(modifier) + Optional(space + location + space)
if_statement = space + Literal("if") + space + condition + space
map_statement = space + Literal("map") + space + nonspace + space + dollar_var + space
block = Forward()
block << Group(
(Group(key + location_statement) ^ Group(if_statement) ^ Group(map_statement)) +
# key could for instance be "server" or "http", or "location" (in which case
# location_statement needs to have a non-empty location)
(Group(space + key + location_statement) ^ Group(if_statement) ^
Group(map_statement)).leaveWhitespace() +
left_bracket +
Group(ZeroOrMore(Group(comment | assignment) | block)) +
Group(ZeroOrMore(Group(comment | assignment) | block) + space).leaveWhitespace() +
right_bracket)
script = OneOrMore(Group(comment | assignment) ^ block) + stringEnd
script = OneOrMore(Group(comment | assignment) ^ block) + space + stringEnd
script.parseWithTabs()
def __init__(self, source):
self.source = source
@@ -52,42 +63,48 @@ class RawNginxParser(object):
"""Returns the parsed tree as a list."""
return self.parse().asList()
class RawNginxDumper(object):
# pylint: disable=too-few-public-methods
"""A class that dumps nginx configuration from the provided tree."""
def __init__(self, blocks, indentation=4):
def __init__(self, blocks):
self.blocks = blocks
self.indentation = indentation
def __iter__(self, blocks=None, current_indent=0, spacer=' '):
def __iter__(self, blocks=None):
"""Iterates the dumped nginx content."""
blocks = blocks or self.blocks
for key, values in blocks:
indentation = spacer * current_indent
for b0 in blocks:
if isinstance(b0, str):
yield b0
continue
b = copy.deepcopy(b0)
if spacey(b[0]):
yield b.pop(0) # indentation
if not b:
continue
key = b.pop(0)
values = b.pop(0)
if isinstance(key, list):
if current_indent:
yield ''
yield indentation + spacer.join(key) + ' {'
yield "".join(key) + '{'
for parameter in values:
dumped = self.__iter__([parameter], current_indent + self.indentation)
for line in dumped:
for line in self.__iter__([parameter]): # negate "for b0 in blocks"
yield line
yield indentation + '}'
yield '}'
else:
if key == '#':
yield spacer * current_indent + key + values
if isinstance(key, str) and key.strip() == '#':
yield key + values
else:
if values is None:
yield spacer * current_indent + key + ';'
else:
yield spacer * current_indent + key + spacer + values + ';'
gap = ""
# Sometimes the parser has stuck some gap whitespace in here;
# if so rotate it into gap
if values and spacey(values):
gap = values
values = b.pop(0)
yield key + gap + values + ';'
def __str__(self):
"""Return the parsed block as a string."""
return '\n'.join(self) + '\n'
return ''.join(self)
# Shortcut functions to respect Python's serialization interface
@@ -101,7 +118,7 @@ def loads(source):
:rtype: list
"""
return RawNginxParser(source).as_list()
return UnspacedList(RawNginxParser(source).as_list())
def load(_file):
@@ -115,24 +132,130 @@ def load(_file):
return loads(_file.read())
def dumps(blocks, indentation=4):
def dumps(blocks):
"""Dump to a string.
:param list block: The parsed tree
:param UnspacedList block: The parsed tree
:param int indentation: The number of spaces to indent
:rtype: str
"""
return str(RawNginxDumper(blocks, indentation))
return str(RawNginxDumper(blocks.spaced))
def dump(blocks, _file, indentation=4):
def dump(blocks, _file):
"""Dump to a file.
:param list block: The parsed tree
:param UnspacedList block: The parsed tree
:param file _file: The file to dump to
:param int indentation: The number of spaces to indent
:rtype: NoneType
"""
return _file.write(dumps(blocks, indentation))
return _file.write(dumps(blocks))
spacey = lambda x: (isinstance(x, str) and x.isspace()) or x == ''
class UnspacedList(list):
"""Wrap a list [of lists], making any whitespace entries magically invisible"""
def __init__(self, list_source):
# ensure our argument is not a generator, and duplicate any sublists
self.spaced = copy.deepcopy(list(list_source))
# Turn self into a version of the source list that has spaces removed
# and all sub-lists also UnspacedList()ed
list.__init__(self, list_source)
for i, entry in reversed(list(enumerate(self))):
if isinstance(entry, list):
sublist = UnspacedList(entry)
list.__setitem__(self, i, sublist)
self.spaced[i] = sublist.spaced
elif spacey(entry):
# don't delete comments
if "#" not in self[:i]:
list.__delitem__(self, i)
def _coerce(self, inbound):
"""
Coerce some inbound object to be appropriately usable in this object
:param inbound: string or None or list or UnspacedList
:returns: (coerced UnspacedList or string or None, spaced equivalent)
:rtype: tuple
"""
if not isinstance(inbound, list): # str or None
return (inbound, inbound)
else:
if not hasattr(inbound, "spaced"):
inbound = UnspacedList(inbound)
return (inbound, inbound.spaced)
def insert(self, i, x):
item, spaced_item = self._coerce(x)
self.spaced.insert(self._spaced_position(i), spaced_item)
list.insert(self, i, item)
def append(self, x):
item, spaced_item = self._coerce(x)
self.spaced.append(spaced_item)
list.append(self, item)
def extend(self, x):
item, spaced_item = self._coerce(x)
self.spaced.extend(spaced_item)
list.extend(self, item)
def __add__(self, other):
l = copy.deepcopy(self)
l.extend(other)
return l
def pop(self, _i=None):
raise NotImplementedError("UnspacedList.pop() not yet implemented")
def remove(self, _):
raise NotImplementedError("UnspacedList.remove() not yet implemented")
def reverse(self):
raise NotImplementedError("UnspacedList.reverse() not yet implemented")
def sort(self, _cmp=None, _key=None, _Rev=None):
raise NotImplementedError("UnspacedList.sort() not yet implemented")
def __setslice__(self, _i, _j, _newslice):
raise NotImplementedError("Slice operations on UnspacedLists not yet implemented")
def __setitem__(self, i, value):
if isinstance(i, slice):
raise NotImplementedError("Slice operations on UnspacedLists not yet implemented")
item, spaced_item = self._coerce(value)
self.spaced.__setitem__(self._spaced_position(i), spaced_item)
list.__setitem__(self, i, item)
def __delitem__(self, i):
self.spaced.__delitem__(self._spaced_position(i))
list.__delitem__(self, i)
def __deepcopy__(self, unused_memo):
l = UnspacedList(self[:])
l.spaced = copy.deepcopy(self.spaced)
return l
def _spaced_position(self, idx):
"Convert from indexes in the unspaced list to positions in the spaced one"
pos = spaces = 0
# Normalize indexes like list[-1] etc, and save the result
if idx < 0:
idx = len(self) + idx
if not 0 <= idx < len(self):
raise IndexError("list index out of range")
idx0 = idx
# Count the number of spaces in the spaced list before idx in the unspaced one
while idx != -1:
if spacey(self.spaced[pos]):
spaces += 1
else:
idx -= 1
pos += 1
return idx0 + spaces
+6
View File
@@ -85,6 +85,9 @@ class Addr(common.Addr):
return parts
def __repr__(self):
return "Addr(" + self.__str__() + ")"
def __eq__(self, other):
if isinstance(other, self.__class__):
return (self.tup == other.tup and
@@ -126,6 +129,9 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods
"enabled: %s" % (self.filep, addr_str,
self.names, self.ssl, self.enabled))
def __repr__(self):
return "VirtualHost(" + self.__str__().replace("\n", ", ") + ")\n"
def __eq__(self, other):
if isinstance(other, self.__class__):
return (self.filep == other.filep and
+19 -5
View File
@@ -1,4 +1,5 @@
"""NginxParser is a member object of the NginxConfigurator class."""
import copy
import glob
import logging
import os
@@ -17,7 +18,7 @@ logger = logging.getLogger(__name__)
class NginxParser(object):
"""Class handles the fine details of parsing the Nginx Configuration.
:ivar str root: Normalized abosulte path to the server root
:ivar str root: Normalized absolute path to the server root
directory. Without trailing slash.
:ivar dict parsed: Mapping of file paths to parsed trees
@@ -113,6 +114,7 @@ class NginxParser(object):
for filename in servers:
for server in servers[filename]:
# Parse the server block into a VirtualHost object
parsed_server = parse_server(server)
vhost = obj.VirtualHost(filename,
parsed_server['addrs'],
@@ -132,7 +134,7 @@ class NginxParser(object):
:rtype: list
"""
result = list(block) # Copy the list to keep self.parsed idempotent
result = copy.deepcopy(block) # Copy the list to keep self.parsed idempotent
for directive in block:
if _is_include_directive(directive):
included_files = glob.glob(
@@ -153,7 +155,9 @@ class NginxParser(object):
:rtype: list
"""
files = glob.glob(filepath)
files = glob.glob(filepath) # nginx on unix calls glob(3) for this
# XXX Windows nginx uses FindFirstFile, and
# should have a narrower call here
trees = []
for item in files:
if item in self.parsed and not override:
@@ -208,14 +212,17 @@ class NginxParser(object):
empty, this overrides the existing conf files.
"""
# Best-effort atomicity is enforced above us by reverter.py
for filename in self.parsed:
tree = self.parsed[filename]
if ext:
filename = filename + os.path.extsep + ext
try:
logger.debug('Dumping to %s:\n%s', filename, nginxparser.dumps(tree))
out = nginxparser.dumps(tree)
logger.debug('Writing nginx conf tree to %s:\n%s', filename, out)
with open(filename, 'w') as _file:
nginxparser.dump(tree, _file)
_file.write(out)
except IOError:
logger.error("Could not open file for writing: %s", filename)
@@ -463,6 +470,8 @@ def parse_server(server):
'names': set()}
for directive in server:
if not directive:
continue
if directive[0] == 'listen':
addr = obj.Addr.fromstring(directive[1])
parsed_server['addrs'].add(addr)
@@ -503,6 +512,11 @@ def _add_directive(block, directive, replace):
See _add_directives for more documentation.
"""
directive = nginxparser.UnspacedList(directive)
if len(directive) == 0:
# whitespace
block.append(directive)
return
location = -1
# Find the index of a config line where the name of the directive matches
# the name of the directive we want to add.
@@ -83,7 +83,7 @@ class NginxConfiguratorTest(util.NginxTest):
filep = self.config.parser.abs_path('sites-enabled/example.com')
self.config.parser.add_server_directives(
filep, set(['.example.com', 'example.*']),
[['listen', '5001 ssl']],
[['listen', ' ', '5001 ssl']],
replace=False)
self.config.save()
@@ -265,7 +265,8 @@ class NginxConfiguratorTest(util.NginxTest):
@mock.patch("certbot_nginx.configurator.tls_sni_01.NginxTlsSni01.perform")
@mock.patch("certbot_nginx.configurator.NginxConfigurator.restart")
def test_perform(self, mock_restart, mock_perform):
@mock.patch("certbot_nginx.configurator.NginxConfigurator.revert_challenge_config")
def test_perform_and_cleanup(self, mock_revert, mock_restart, mock_perform):
# Only tests functionality specific to configurator.perform
# Note: As more challenges are offered this will have to be expanded
achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
@@ -291,7 +292,11 @@ class NginxConfiguratorTest(util.NginxTest):
self.assertEqual(mock_perform.call_count, 1)
self.assertEqual(responses, expected)
self.assertEqual(mock_restart.call_count, 1)
self.config.cleanup([achall1, achall2])
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.configurator.subprocess.Popen")
def test_get_version(self, mock_popen):
@@ -1,11 +1,13 @@
"""Test for certbot_nginx.nginxparser."""
import copy
import operator
import os
import unittest
from pyparsing import ParseException
from certbot_nginx.nginxparser import (
RawNginxParser, loads, load, dumps, dump)
RawNginxParser, loads, load, dumps, dump, UnspacedList)
from certbot_nginx.tests import util
@@ -17,39 +19,39 @@ class TestRawNginxParser(unittest.TestCase):
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'])
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'], []]])
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']]]])
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 = FIRST(parsed)
self.assertEqual(FIRST(content), [['bar'], []])
self.assertEqual(FIRST(content), [[' ', 'bar', ' '], []])
self.assertEqual(FIRST(block), 'foo')
def test_dump_as_string(self):
dumped = dumps([
['user', 'www-data'],
[['server'], [
['listen', '80'],
['server_name', 'foo.com'],
['root', '/home/ubuntu/sites/foo/'],
[['location', '/status'], [
['check_status', None],
[['types'], [['image/jpeg', 'jpg']]],
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,
self.assertEqual(dumped.split('\n'),
'user www-data;\n'
'server {\n'
' listen 80;\n'
@@ -60,10 +62,7 @@ class TestRawNginxParser(unittest.TestCase):
' check_status;\n'
'\n'
' types {\n'
' image/jpeg jpg;\n'
' }\n'
' }\n'
'}\n')
' image/jpeg jpg;}}}'.split('\n'))
def test_parse_from_file(self):
with open(util.get_data_filename('foo.conf')) as handle:
@@ -116,24 +115,29 @@ class TestRawNginxParser(unittest.TestCase):
def test_dump_as_file(self):
with open(util.get_data_filename('nginx.conf')) as handle:
parsed = util.filter_comments(load(handle))
parsed[-1][-1].append([['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']]]]])
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 open(util.get_data_filename('nginx.new.conf'), 'w') as handle:
dump(parsed, handle)
with open(util.get_data_filename('nginx.new.conf')) as handle:
parsed_new = util.filter_comments(load(handle))
self.assertEquals(parsed, parsed_new)
parsed_new = load(handle)
try:
self.maxDiff = None
self.assertEquals(parsed[0], parsed_new[0])
self.assertEquals(parsed[1:], parsed_new[1:])
finally:
os.unlink(util.get_data_filename('nginx.new.conf'))
def test_comments(self):
with open(util.get_data_filename('minimalistic_comments.conf')) as handle:
@@ -145,20 +149,23 @@ class TestRawNginxParser(unittest.TestCase):
with open(util.get_data_filename('minimalistic_comments.new.conf')) as handle:
parsed_new = load(handle)
self.assertEquals(parsed, parsed_new)
try:
self.assertEquals(parsed, parsed_new)
self.assertEqual(parsed_new, [
['#', " Use bar.conf when it's a full moon!"],
['include', 'foo.conf'],
['#', ' Kilroy was here'],
['check_status', None],
[['server'],
[['#', ''],
['#', " Don't forget to open up your firewall!"],
['#', ''],
['listen', '1234'],
['#', ' listen 80;']]],
])
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;']]],
])
finally:
os.unlink(util.get_data_filename('minimalistic_comments.new.conf'))
def test_issue_518(self):
parsed = loads('if ($http_accept ~* "webp") { set $webp "true"; }')
@@ -168,5 +175,62 @@ class TestRawNginxParser(unittest.TestCase):
[['set', '$webp "true"']]]
])
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_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"])
if __name__ == '__main__':
unittest.main() # pragma: no cover
@@ -126,7 +126,7 @@ class NginxParserTest(util.NginxTest):
nparser.add_server_directives(nparser.abs_path('nginx.conf'),
set(['localhost',
r'~^(www\.)?(example|bar)\.']),
[['foo', 'bar'], ['ssl_certificate',
[['foo', 'bar'], ['\n ', 'ssl_certificate', ' ',
'/etc/ssl/cert.pem']],
replace=False)
ssl_re = re.compile(r'\n\s+ssl_certificate /etc/ssl/cert.pem')
@@ -1,11 +0,0 @@
# 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;
}
@@ -1,83 +0,0 @@
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;
}
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;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
location ~ \.php$ {
proxy_pass http://127.0.0.1;
}
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
}
location ~ /\.ht {
deny all;
}
}
server {
listen 8000;
listen somename:8080;
include server.conf;
location / {
root html;
index index.html index.htm;
}
}
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;
}
}
}
+7 -3
View File
@@ -1,4 +1,5 @@
"""Common utilities for certbot_nginx."""
import copy
import os
import pkg_resources
import unittest
@@ -82,12 +83,15 @@ def filter_comments(tree):
def traverse(tree):
"""Generator dropping comment nodes"""
for key, values in tree:
for entry in tree:
key, values = entry
if isinstance(key, list):
yield [key, filter_comments(values)]
new = copy.deepcopy(entry)
new[1] = filter_comments(values)
yield new
else:
if key != '#':
yield [key, values]
yield entry
return list(traverse(tree))
+11 -10
View File
@@ -91,10 +91,10 @@ class NginxTlsSni01(common.TLSSNI01):
# Add the 'include' statement for the challenges if it doesn't exist
# already in the main config
included = False
include_directive = ['include', self.challenge_conf]
include_directive = ['include', ' ', self.challenge_conf]
root = self.configurator.parser.loc["root"]
bucket_directive = ['server_names_hash_bucket_size', '128']
bucket_directive = ['server_names_hash_bucket_size', ' ', '128']
main = self.configurator.parser.parsed[root]
for key, body in main:
@@ -116,6 +116,7 @@ class NginxTlsSni01(common.TLSSNI01):
config = [self._make_server_block(pair[0], pair[1])
for pair in itertools.izip(self.achalls, ll_addrs)]
config = nginxparser.UnspacedList(config)
self.configurator.reverter.register_file_creation(
True, self.challenge_conf)
@@ -140,19 +141,19 @@ class NginxTlsSni01(common.TLSSNI01):
document_root = os.path.join(
self.configurator.config.work_dir, "tls_sni_01_page")
block = [['listen', str(addr)] for addr in addrs]
block = [['listen', ' ', str(addr)] for addr in addrs]
block.extend([['server_name',
block.extend([['server_name', ' ',
achall.response(achall.account_key).z_domain],
['include', self.configurator.parser.loc["ssl_options"]],
['include', ' ', self.configurator.parser.loc["ssl_options"]],
# access and error logs necessary for
# integration testing (non-root)
['access_log', os.path.join(
['access_log', ' ', os.path.join(
self.configurator.config.work_dir, 'access.log')],
['error_log', os.path.join(
['error_log', ' ', os.path.join(
self.configurator.config.work_dir, 'error.log')],
['ssl_certificate', self.get_cert_path(achall)],
['ssl_certificate_key', self.get_key_path(achall)],
[['location', '/'], [['root', document_root]]]])
['ssl_certificate', ' ', self.get_cert_path(achall)],
['ssl_certificate_key', ' ', self.get_key_path(achall)],
[['location', ' ', '/'], [['root', ' ', document_root]]]])
return [['server'], block]