Repository: certbot/certbot

Issue:
Doesn't understand `ssl on` directive
In Nginx there are two ways to turn on SSL for a server:

```
server {
  listen 443 ssl;
}
```

```
server {
  listen 443;
  ssl on;
}
```

Our Nginx plugin doesn't understand the latter. In parser.py's `get_vhosts`, the `VirtualHost` object will be created with `ssl = False`, leading later code to attempt to "enable" SSL on that vhost, creating a fatal conflict for the ssl_certificate directive. One possible easy fix: Look for `ssl_certificate` to determine whether a vhost has ssl enabled, instead of looking at the `listen` or `ssl` directives.



Discussion:
so here's the code that's likely at fault 

```
446 def _parse_server(server):                                                       
...
453     parsed_server = {'addrs': set(),                                             
454                      'ssl': False,                                               
455                      'names': set()}                                             
456                                                                                  
457     for directive in server:                                                     
458         if directive[0] == 'listen':                                             
459             addr = obj.Addr.fromstring(directive[1])                             
460             parsed_server['addrs'].add(addr)                                     
461             if not parsed_server['ssl'] and addr.ssl:                            
462                 parsed_server['ssl'] = True                                      
463         elif directive[0] == 'server_name':                                      
464             parsed_server['names'].update(                                       
465                 _get_servernames(directive[1]))                                  
466                                                                                  
467     return parsed_server                     
```

It seems that we're just looking at one line of the directive - we could easily make this part of the code more robust, to check multiple lines for ssl and also check for ssl_certificate

can you share some conf files with me?
I'm curious to see what these lines in parser.py spit out

```
109             for i, server in enumerate(servers[filename]):                       
110                 new_server = self._get_included_directives(server)               
111                 servers[filename][i] = new_server                                
```

crud.net:

```
server {
    listen 80;
    listen [::]:80;
    server_name crud.net;

    location / {
        return 301 https://$host$request_uri;
    }
}
```

crud.net-ssl:

```
server {
    listen 443;
    listen [::]:443;
    server_name crud.net;
    ssl on;
    ssl_certificate /etc/apache2/certs/2015/crud.net.crt;
    ssl_certificate_key /etc/apache2/certs/2015/crud.net.key;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_stapling on;
    ssl_stapling_verify off;

    root /var/www/crud;

    location / {
        try_files $uri $uri/ /index.html /index.php;
    }
}
```


Resolving patch:
diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/letsencrypt-nginx/letsencrypt_nginx/parser.py
--- a/letsencrypt-nginx/letsencrypt_nginx/parser.py
+++ b/letsencrypt-nginx/letsencrypt_nginx/parser.py
@@ -113,7 +113,7 @@ def get_vhosts(self):
         for filename in servers:
             for server in servers[filename]:
                 # Parse the server block into a VirtualHost object
-                parsed_server = _parse_server(server)
+                parsed_server = parse_server(server)
                 vhost = obj.VirtualHost(filename,
                                         parsed_server['addrs'],
                                         parsed_server['ssl'],
@@ -451,7 +451,7 @@ def _get_servernames(names):
     return names.split(' ')
 
 
-def _parse_server(server):
+def parse_server(server):
     """Parses a list of server directives.
 
     :param list server: list of directives in a server block
@@ -471,6 +471,8 @@ def _parse_server(server):
         elif directive[0] == 'server_name':
             parsed_server['names'].update(
                 _get_servernames(directive[1]))
+        elif directive[0] == 'ssl' and directive[1] == 'on':
+            parsed_server['ssl'] = True
 
     return parsed_server
 


Test patch:
diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py b/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py
--- a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py
+++ b/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py
@@ -228,6 +228,26 @@ def test_get_all_certs_keys(self):
         c_k = nparser.get_all_certs_keys()
         self.assertEqual(set([('foo.pem', 'bar.key', filep)]), c_k)
 
+    def test_parse_server_ssl(self):
+        server = parser.parse_server([
+	    ['listen', '443']
+	])
+        self.assertFalse(server['ssl'])
+
+        server = parser.parse_server([
+	    ['listen', '443 ssl']
+	])
+        self.assertTrue(server['ssl'])
+
+        server = parser.parse_server([
+	    ['listen', '443'], ['ssl', 'off']
+	])
+        self.assertFalse(server['ssl'])
+
+        server = parser.parse_server([
+	    ['listen', '443'], ['ssl', 'on']
+	])
+        self.assertTrue(server['ssl'])
 
 if __name__ == "__main__":
     unittest.main()  # pragma: no cover