Solved Nginx SNI and Letsencrypt; Wrong certificate

Hello there,

I have a VPS with 14 domains and I setup letskencrypt to automatically retrieve a separate certificate for each domain with all sub-domains included. So, I have 14 certs. Obviously, putting all domains in one cert is not an option because soon I'll hit the maximum 100 domain/sub-domain per cert for Letsencrypt.

So, I was happy for a month until I found out that nginx serves wrong certs for all domains except one (the one that it automatically picks up - or, I'll set - as the default server for port 443). After a lot of headache I found out that each SSL cert must have its own IP not a shared one. Then also I found out there is SNI as a workaround for this issue.

Code:
$ nginx -V
TLS SNI support enabled

So make the long story short; The problem is no matter what I do nginx stubbornly serve's the wrong cert:
Code:
$ curl --insecure -v https://babaei.net 2>&1 | awk 'BEGIN { cert=0 } /^\* Server certificate:/ { cert=1 } /^\*/ { if (cert) print }'
* Server certificate:
*  subject: CN=babaei.net
*  start date: Aug 28 13:30:00 2016 GMT
*  expire date: Nov 26 13:30:00 2016 GMT
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Connection #0 to host babaei.net left intact

$ curl --insecure -v https://learnmyway.net 2>&1 | awk 'BEGIN { cert=0 } /^\* Server certificate:/ { cert=1 } /^\*/ { if (cert) print }'
* Server certificate:
*  subject: CN=babaei.net
*  start date: Aug 28 13:30:00 2016 GMT
*  expire date: Nov 26 13:30:00 2016 GMT
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Connection #0 to host learnmyway.net left intact

$ curl --insecure -v https://3rr0r.org 2>&1 | awk 'BEGIN { cert=0 } /^\* Server certificate:/ { cert=1 } /^\*/ { if (cert) print }'
* Server certificate:
*  subject: CN=babaei.net
*  start date: Aug 28 13:30:00 2016 GMT
*  expire date: Nov 26 13:30:00 2016 GMT
*  issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3
*  SSL certificate verify ok.
* Connection #0 to host 3rr0r.org left intact

And, don't get me wrong the actual certs are what they are supposed to be:
Code:
$ openssl x509 -noout -subject -in /path/to/certs/babaei.net.pem
subject= /CN=babaei.net

$ openssl x509 -noout -subject -in /path/to/certs/learnmyway.net.pem
subject= /CN=learnmyway.net

$ openssl x509 -noout -subject -in /path/to/certs/3rr0r.org.pem
subject= /CN=3rr0r.org

So, let's say we have two domains alpha.com and omega.com. How would you configure SNI enabled nginx to serve the right SSL cert for each?

Code:
server {
  server_tokens  off;

  listen  443 ssl http2;
  listen  [::]:443 ssl http2;
  server_name  www.alpha.com;

  ssl  on;
  ssl_certificate  /path/to/alpha.com/cert.pem;
  ssl_certificate_key /path/to/alpha.com/key.pem;
}

server {
  server_tokens  off;

  listen  443 ssl http2;
  listen  [::]:443 ssl http2;
  server_name  www.omega.com;

  ssl  on;
  ssl_certificate  /path/to/omega.com/cert.pem;
  ssl_certificate_key /path/to/omega.com/key.pem;
}

Thanks
 
Use the same server_name as for your certificates. Your certificates are made for alpha.com, not www.alpha.com, so the server_name should also be alpha.com, not www.alpha.com.

Tahnk you SirDice for the answer and sorry for my tardy response. But, the certificate contains all the subdomains (including www) in the Certificate Subject Alt Name field as with all Let's Encrypt certificates. You can check for example both babaei.net and learnmyway.net domains certificates in Firefox. So, I'm sure that's not the issue.
 
If it helps here is the nginx config:

Code:
server {
  server_tokens  off;

  listen  80;
  listen  [::]:80;
  server_name  learnmyway.net;

  location / {
  return 301 https://www.$server_name$request_uri;  # enforce https / www
  }

  # Error Pages
  include /path/to/snippets/error;

  # Anti-DDoS
  include /path/to/snippets/anti-ddos;

  # letsencrypt acme challenges
  include /path/to/snippets/letsencrypt-acme-challenge;
}

server {
  server_tokens  off;

  listen  80;
  listen  [::]:80;
  server_name  *.learnmyway.net;

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

  # Error Pages
  include /path/to/snippets/error;

  # Anti-DDoS
  include /path/to/snippets/anti-ddos;

  # letsencrypt acme challenges
  include /path/to/snippets/letsencrypt-acme-challenge;
}

server {
  server_tokens  off;

  listen  443 ssl http2;
  listen  [::]:443 ssl http2;
  server_name  www.learnmyway.net;

  # Hardened SSL
  include  /path/to/snippets/hardened-ssl;
  ssl_certificate  /path/to/certs/learnmyway.net.pem;
  ssl_certificate_key  /path/to/keys/learnmyway.net.pem;
  ssl_trusted_certificate /path/to/certs/learnmyway.net.pem;

  #error_log  /path/to/learnmyway.net/log/www_error_log;
  #access_log  /path/to/learnmyway.net/log/www_access_log;

  root  /path/to/learnmyway.net/www/;
  index  index.html;

  # Error Pages
  include  /path/to/snippets/error;

  # Anti-DDoS
  include  /path/to/snippets/anti-ddos;

  # letsencrypt acme challenges
  include /path/to/snippets/letsencrypt-acme-challenge;

  # Compression
  include  /path/to/snippets/compression;

  # Static Resource Caching
  include  /path/to/snippets/static-resource-caching;
}
 
Ops! Thank you so much SirDice. You are right! Reading your response again, I just figured it out. Adding the following block solved the issue:

Code:
server {
  server_tokens  off;

  listen  443 ssl http2;
  listen  [::]:443 ssl http2;
  server_name  learnmyway.net;

  # Hardened SSL
  include  /path/to/snippets/hardened-ssl;
  ssl_certificate  /path/to/certs/learnmyway.net.pem;
  ssl_certificate_key  /path/to/keys/learnmyway.net.pem;
  ssl_trusted_certificate /path/to/certs/learnmyway.net.pem;

  return 301 https://www.$server_name$request_uri;  # enforce www
}
 
Back
Top