Repository: certbot/certbot

Issue:
Error: duplicate listen options for [::]:443
## My operating system is (include version):
Ubuntu 16.04

## I installed Certbot with (certbot-auto, OS package manager, pip, etc):
`sudo apt-get install python-certbot-nginx`

## I ran this command and it produced this output:
`nginx: [emerg] duplicate listen options for [::]:443 in /etc/nginx/sites-enabled/example.online:29
`

## Certbot's behavior differed from what I expected because:
It should be no errors

## Here is the relevant nginx server block or Apache virtualhost for the domain I am configuring:

```
server {
  listen 80;
  listen [::]:80;

  server_name example.online;

  root /home/example/deploy;
  index index.html;

  location / {
    try_files $uri $uri/ =404;
  }
}

server {
  listen 80;
  listen [::]:80;
  server_name www.example.online;
  return 301 $scheme://example.online$request_uri;
}

```


Discussion:
@iamdubx did you figure this out? I'm having the same issue. 
Same issue... It works for my default site, but not for custom sub domain
I'm having the same issue.

I do have a config to catch multiple domains though.

```
server {
  listen 80;
  listen [::]:80;

  root /home/primarydomain/public;
  index index.html index.htm;

  server_name domain1.com *.domain1.com domain2.com *.domain2.com domain3.com *.domain3.com domain4.com *.domain4.com;

  return 302 $scheme://primarydomain.com$request_uri;

  access_log /var/log/nginx/others.access.log;
  error_log /var/log/nginx/others.error.log;

  location / {
    try_files $uri $uri/ /index.html =404;
  }
}
```

I get `nginx: [emerg] duplicate listen options for [::]:443 in /etc/nginx/sites-enabled/others:19` for this config.

OS: Ubuntu 16.04. Any help?


Same problem.  

I ran the command: `certbot --redirect --nginx -d readacted.com -d www.redacted.com`

my original conf file looks like: 

```
    server {
        server_name redacted.com;
        location / {
        root   /home/redacted/www;
        index  index.html;
      }
    }

    server {
        listen 80;
        listen [::]:80;
        server_name www.redacted.com;
        return 301 $scheme://redacted.com$request_uri;
    }
```

according to /var/log/letsencrypt/letsencrypt.log I see certbot is trying to do this:

```
    server {
        server_name redacted.com;
        location / {
        root   /home/redacted/www;
        index  index.html;
      }

        listen [::]:443 ssl ipv6only=on; # managed by Certbot
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/redacted.com-0001/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/redacted.com-0001/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    }

    server {
        listen 80;
        listen [::]:80;
        server_name www.redacted.com;
        return 301 $scheme://redacted.com$request_uri;

        listen [::]:443 ssl ipv6only=on; # managed by Certbot
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/www.redacted.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/www.redacted.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    }
```

nginx complains that on the line `listen [::]:443 ssl ipv6only=on; # managed by Certbot`

the actual error message:

`nginx: [emerg] duplicate listen options for [::]:443 in /etc/nginx/sites-enabled/redacted.com:23`

a quick google brought up this page from 2010:

http://www.serverphorums.com/read.php?5,203912

which suggests that nginx gets confused due to some internal implementation detail.

I'm not an nginx expert, but I've tested that the following seems to work:

```
    server {
        server_name redacted.com;
        location / {
        root   /home/redacted/www;
        index  index.html;
      }

        listen [::]:443 ssl ipv6only=on; # managed by Certbot
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/redacted.com-0001/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/redacted.com-0001/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    }

    server {
        listen 80;
        listen [::]:80;
        server_name www.redacted.com;
        return 301 http://redacted.com$request_uri;

        listen [::]:443; # manually changed
        ssl on;  #manually changed
        listen 443 ssl; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/www.redacted.com/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/www.redacted.com/privkey.pem; # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

    }
```

I'd love a better solution that this workaround ideally...

@ohemorange, do you know if we have an existing issue tracking this? It feels familiar to me but I don't remember whether or not it's something that we've looked into before.
I have never seen this before. Looks like they fixed the original bug, except when you're using IPv6. And since we just launched IPv6 support, that's why people are hitting this. The solution above will work; I'll see if there's a reason this hasn't been fixed in Nginx for IPv6 yet. 
Actually, you don't even have to do the `ssl on` change -- removing `ipv6only=on` from either or both fixes the problem.

@joohoi, we probably want to fix this by either removing `ipv6only=on` completely or only putting it in once per unique address line that we add. Do you have a sense of what would be best here?
Having the same issue here. Everything was fine for my first domain. The second domain started having these issues.
It looks like Certbot is unable to detect ipv6only directive completely for some reason. Removing that would fix the issue for *most* of the users. This can cause some issues with really old Nginx versions, as the behavior of ipv6only and the defaults have changed over the time.
Apologies for the nasty patch, but this fixed it for me, hopefully there will be a proper fix soon!

```patch
--- /usr/lib/python3/dist-packages/certbot_nginx/configurator.py.orig	2018-02-14 18:38:30.380863045 +0000
+++ /usr/lib/python3/dist-packages/certbot_nginx/configurator.py	2018-02-14 18:38:01.501018553 +0000
@@ -507,10 +507,10 @@ class NginxConfigurator(common.Installer
                           '[::]:{0}'.format(self.config.tls_sni_01_port),
                           ' ',
                           'ssl']
-            if not ipv6info[1]:
-                # ipv6only=on is absent in global config
-                ipv6_block.append(' ')
-                ipv6_block.append('ipv6only=on')
+            #if not ipv6info[1]:
+            #    # ipv6only=on is absent in global config
+            #    ipv6_block.append(' ')
+            #    ipv6_block.append('ipv6only=on')
 
         if vhost.ipv4_enabled():
             ipv4_block = ['\n    ',
```

```
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:        16.04
Codename:       xenial
$ nginx -V
nginx version: nginx/1.10.3 (Ubuntu)
built with OpenSSL 1.0.2g  1 Mar 2016
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_v2_module --with-http_sub_module --with-http_xslt_module --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-threads
$ apt show python-certbot-nginx
Package: python-certbot-nginx
Version: 0.21.1-1+ubuntu16.04.1+certbot+1
Priority: optional
Section: oldlibs
Maintainer: Debian Let's Encrypt <letsencrypt-devel@lists.alioth.debian.org>
Installed-Size: 9,216 B
Depends: python3-certbot-nginx
Download-Size: 2,470 B
APT-Manual-Installed: yes
APT-Sources: http://ppa.launchpad.net/certbot/certbot/ubuntu xenial/main amd64 Packages
Description: transitional dummy package
 This is a transitional dummy package for the migration of certbot
 from python2 to python3.  It can be safely removed.

```
Same issue. Will this be addressed?
Sorry for the wall of text, but here we go. 

To shed some light on the issue:
ipv6only option is used to be able to handle multiple listen statements per socket. Unfortunately it can only be used once in the server configuration for the socket. So Nginx will fail to start in case of:
```
server {
    ...
    server_name first.example.org;
    listen [::]:80 ipv6only=on;
    listen 80;
} 
server {
    ...
    server_name second.example.org;
    listen [::]:80 ipv6only=on;
    listen 80;
}
```
With the recent versions of Nginx this problem does not exist if the `ipv6only` setting is omitted completely, as the default value of the variable is `ipv6only=on`. So the following is valid and working configuration in Nginx versions >= 1.3.4:
```
server {
    ...
    server_name first.example.org;
    listen [::]:80;
    listen 80;
} 
server {
    ...
    server_name second.example.org;
    listen [::]:80;
    listen 80;
}
```
However the default value of `ipv6only` variable in Nginx versions prior to 1.3.4 was `ipv6only=off`, so older versions will fail with the following configuration:
```
server {
    ...
    server_name first.example.org;
    listen [::]:80;
    listen 80;
} 
```

Currently the situation with distribution packaging is that the only distribution that ships with older version of Nginx would be Debian Wheezy (Debian 7), which ships version 1.2.1 of Nginx from the default repositories.

If we would remove the `ipv6only` detection and setting completely from Certbot, this would break for all users on Debian Wheezy. Fortunately EOL date for Wheezy is set for [May 2018](https://wiki.debian.org/DebianReleases#Production_Releases), so we're close to being able to remove this additional complexity from Certbot code altogether.

The current functionality of Certbot is parsing the complete Nginx configuration, detecting `ipv6only=on` setting already present in one of the `server{}` blocks and omitting addition of it if so. If however the value was not found, Certbot would then add it. This issue boils down to Certbot not being able to detect this already existing variable from some block of the users current configuration, and hence tries to add it to the `server{}` block that's being configured.

To make decision of the route to fix this issue, we'd need a complete example configuration where Certbot fails in the way described above to be able to improve the detection of an already existing `ipv6only=on`variable if we decide to fix it that way instead of removing this functionality altogether.
Thanks for the patch; that worked for me. FWIW, I'm on Ubuntu 17.
I had to remove all the 

    listen [::]:80;
    listen 80;

To make it work
https://github.com/chilion - thanks!  removing: 

```
listen [::]:80;
listen 80;
```

worked for me as well.

I have two domains on one Ubuntu server.  The first one worked no problem.  Then I got the error as above. Your solution worked for me.  I just installed nginx on a fresh server with fresh everything.

Thank you.
removing `listen [::]:80` but leaving `listen 80;` worked for me for installation on non-default domain

Resolving patch:
diff --git a/certbot-nginx/certbot_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py
--- a/certbot-nginx/certbot_nginx/configurator.py
+++ b/certbot-nginx/certbot_nginx/configurator.py
@@ -311,6 +311,9 @@ def ipv6_info(self, port):
             configuration, and existence of ipv6only directive for specified port
         :rtype: tuple of type (bool, bool)
         """
+        # port should be a string, but it's easy to mess up, so let's
+        # make sure it is one
+        port = str(port)
         vhosts = self.parser.get_vhosts()
         ipv6_active = False
         ipv6only_present = False


Test patch:
diff --git a/certbot-nginx/certbot_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py
--- a/certbot-nginx/certbot_nginx/tests/configurator_test.py
+++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py
@@ -181,6 +181,18 @@ def test_ipv6only(self):
         # Port 443 has ipv6only=on because of ipv6ssl.com vhost
         self.assertEquals((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())
diff --git a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com
--- a/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com
+++ b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/ipv6ssl.com
@@ -1,5 +1,7 @@
 server {
     listen 443 ssl;
     listen [::]:443 ssl ipv6only=on;
+    listen 5001 ssl;
+    listen [::]:5001 ssl ipv6only=on;
     server_name ipv6ssl.com;
 }