diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py index d64f78faf..06078b351 100644 --- a/certbot-nginx/certbot_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -117,6 +117,9 @@ class NginxConfigurator(common.Installer): # Files to save self.save_notes = "" + # For creating new vhosts if no names match + self.new_vhost = None + # Add number of outstanding challenges self._chall_out = 0 @@ -191,9 +194,11 @@ class NginxConfigurator(common.Installer): "The nginx plugin currently requires --fullchain-path to " "install a cert.") - vhost = self.choose_vhost(domain) - cert_directives = [['\n', 'ssl_certificate', ' ', fullchain_path], - ['\n', 'ssl_certificate_key', ' ', key_path]] + vhost = self.choose_vhost(domain, raise_if_no_match=False) + if vhost is None: + vhost = self._vhost_from_duplicated_default(domain) + cert_directives = [['\n ', 'ssl_certificate', ' ', fullchain_path], + ['\n ', 'ssl_certificate_key', ' ', key_path]] self.parser.add_server_directives(vhost, cert_directives, replace=True) @@ -250,6 +255,32 @@ class NginxConfigurator(common.Installer): return vhost + def _vhost_from_duplicated_default(self, domain): + if self.new_vhost is None: + default_vhost = self._get_default_vhost() + self.new_vhost = self.parser.create_new_vhost_from_default(default_vhost) + self.new_vhost.names = set() + + import ipdb; ipdb.set_trace() + self.new_vhost.names.add(domain) + name_block = [['\n ', 'server_name', ' ', " ".join(self.new_vhost.names)]] + self.parser.add_server_directives(self.new_vhost, name_block, replace=True) + return self.new_vhost + + def _get_default_vhost(self): + vhost_list = self.parser.get_vhosts() + # if one has default_server set, return that one + for vhost in vhost_list: + for addr in vhost.addrs: + if addr.default: + return vhost + + # otherwise, return the first one + if len(vhost_list) > 0: + return vhost_list[0] + + raise errors.MisconfigurationError("No server blocks found in Nginx configuration.") + def _get_ranked_matches(self, target_name): """Returns a ranked list of vhosts that match target_name. The ranking gives preference to SSL vhosts. diff --git a/certbot-nginx/certbot_nginx/nginxparser.py b/certbot-nginx/certbot_nginx/nginxparser.py index 20aeeb554..5e24f28da 100644 --- a/certbot-nginx/certbot_nginx/nginxparser.py +++ b/certbot-nginx/certbot_nginx/nginxparser.py @@ -235,8 +235,8 @@ class UnspacedList(list): self.dirty = True def __deepcopy__(self, memo): - l = UnspacedList(self[:]) - l.spaced = copy.deepcopy(self.spaced, memo=memo) + new_spaced = copy.deepcopy(self.spaced, memo=memo) + l = UnspacedList(new_spaced) l.dirty = self.dirty return l diff --git a/certbot-nginx/certbot_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py index 849cefe1f..e83f6239c 100644 --- a/certbot-nginx/certbot_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -1,4 +1,5 @@ """Module contains classes used by the Nginx Configurator.""" +import copy import re import six @@ -125,6 +126,9 @@ class Addr(common.Addr): self.default == other.default) return False + def copy(self): + return Addr.fromstring(str(self)) + class VirtualHost(object): # pylint: disable=too-few-public-methods """Represents an Nginx Virtualhost. diff --git a/certbot-nginx/certbot_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py index 158cb9929..d2f34b640 100644 --- a/certbot-nginx/certbot_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -312,6 +312,24 @@ class NginxParser(object): except errors.MisconfigurationError as err: raise errors.MisconfigurationError("Problem in %s: %s" % (filename, str(err))) + def create_new_vhost_from_default(self, vhost_template): + # TODO: do the right thing for debian + # put it in the same file as the template, at the same level + enclosing_block = self.parsed[vhost_template.filep] + for index in vhost_template.path[:-1]: + enclosing_block = enclosing_block[index] + new_location = vhost_template.path[-1] + 1 + raw_in_parsed = copy.deepcopy(enclosing_block[vhost_template.path[-1]]) + enclosing_block.insert(new_location, raw_in_parsed) + new_vhost = copy.deepcopy(vhost_template) + new_vhost.path[-1] = new_location + for addr in new_vhost.addrs: + addr.default = False + for directive in enclosing_block[new_vhost.path[-1]][1]: + if len(directive) > 0 and directive[0] == 'listen' and 'default_server' in directive: + del directive[directive.index('default_server')] + return new_vhost + def _parse_ssl_options(ssl_options): if ssl_options is not None: try: