3

I have a blog about Ghost on DigitalOcean. The same is served by Nginx, with the following configuration file:

server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    include snippets/ssl-params.conf;

    location / {
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_pass http://127.0.0.1:2825;
    }
}

The certificates were generated with Let's Encrypt, both for the domain with and without www. In the Ghost config.js file, the URL is written with the rules to take the SSL: https://example.com/ The problem is that when I enter my blog, My domain without wwww, login correctly and with SSL, but when I try to login with https://www.example.com I get an SSL certificate authentication error. I really do not understand what the problem might be here. I need that when entering my domain with www I redirect to the domain, but without the www. This operation I have done before with other applications node without problem, with the same configuration code above.

gach3z
  • 531
  • 3
  • 20
  • any progress on this issue? – John Siu Mar 21 '17 at 18:07
  • @JohnSiu, finding a bug, apparently found in the use of Let's Encrypt, but I still can not prove it. I find myself with little time, but when I have progress I will publish them, since this code works for me not to use an SSL certificate. – gach3z Mar 23 '17 at 15:25
  • Base on last part of your post, you may have cert issue I mentioned in my answer. Double check your cert works for BOTH with and without `www`. – John Siu Mar 23 '17 at 15:34
  • @JohnSiu, The certificate works correctly for both domains, both with www, and without www. The problem arises with the redirect with Nginx, at that moment I say that the domain can not verify the identity of my site and that the certificate is not trustworthy, and if I try to make the redirect in reverse, so does the same , Saying that the domain without www may not see. Now I'm using a proxy from my Node server, and I refuse to use Nginx since I really could not find a solution to this. – gach3z Mar 23 '17 at 15:48

5 Answers5

1

I was researching a good part of the weekend a solution for this inconvenience, and managed to find the solution. There is no bug in Let's Encrypt, nor in Nginx, all referring to a bad configuration on my part; However, I can not stop thinking that it is a strange thing, and that my solution can be improved even without saying more, this is my Nginx configuration file:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    include snippets/ssl-params.conf;

    if ($http_host = www.example.com) {
            return 301 https://example.com$request_uri;
    }

    location / {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://localhost:8080/;
            proxy_ssl_session_reuse off;
            proxy_set_header Host $http_host;
            proxy_cache_bypass $http_upgrade;
            proxy_redirect off;
    }
}

To avoid dragging errors, completely remove my virtual machine and create it from scratch, also install the latest version of Nginx for Debian, which is 1.10.3, which enables me to use http2, which is also an improvement.

gach3z
  • 531
  • 3
  • 20
  • Interesting. So the correct way to do the www redirect is just put it in the same block. – John Siu Mar 27 '17 at 06:19
  • @JohnSiu, this is the only way to achieve optimal performance in all situations. In this way, avoided using the rewrite clause (which does not work either), and do re-addressing in separate server blocks. However, I'm sure you can improve this configuration yet, but, I'm glad I could have solved this problem. Thanks for your answers. – gach3z Mar 27 '17 at 06:23
  • I am not familiar with nginx config. But the url redirect logic look similar to my nodejs solution. I don't think there is much improvement to that part, and not worth the time, as it should only encounter once per browser(or until they clear the cache). On the other hand, why 'proxy_ssl_session_reuse' is off? – John Siu Mar 27 '17 at 06:40
  • Since you are running multiple site with nginx, maybe this is useful for you : http://pnommensen.com/2014/09/07/high-performance-ghost-configuration-with-nginx/ – John Siu Mar 27 '17 at 06:41
1

One solution is to request and install a second SSL certificate for the www subdomain.

Steps:

  1. Open your nginx HTTP configuration file: sudo nano /etc/nginx/sites-available/example.com.conf. Duplicate the existing server block. Leave the first block unchanged so it will continue to work for example.com. In the second block, include www in the server_name line: server_name www.example.com;. Save & close the file.

  2. Open your nginx SSL configuration file: sudo nano /etc/nginx/sites-available/example.com-ssl.conf. Leave the first block unchanged so it will continue to work for example.com. In the second block, include www in the server_name line, which will give you server_name www.example.com;. Then delete the lines starting with ssl_certificate and ssl_certificate_key because we don't need these lines because we will be requesting a new SSL certificate for the www subdomain. Save & close the file.

  3. Request a Let's Encrypt SSL certificate for the www subdomain: sudo /etc/letsencrypt/acme.sh --issue --home /etc/letsencrypt --domain www.example.com -- webroot /var/www/ghost/system/nginx-root --reloadcmd "nginx -s reload" --accountemail exampleEmailAddress@example.com

  4. Re-open your nginx SSL configuration file: sudo nano /etc/nginx/sites-available/example.com-ssl.conf. Return to the www.example.com server block you set up in step 2 and re-add your ssl_certificate lines: ssl_certificate /etc/letsencrypt/www.example.com/fullchain.cer; and ssl_certificate_key /etc/letsencrypt/www.example.com/www.example.com.key;. Save & close the file.

  5. Reload nginx with sudo nginx -s reload, and the error should be gone.

hbere
  • 306
  • 1
  • 5
  • 8
0

Your first server block does not have a certificate definition. So you might want to duplicate the ssl_certificate and ssl_certificate_key statements in that server block.

If these are the only SSL server blocks, and because you have a common certificate file for both domains, you could move the ssl_certificate and ssl_certificate_key statements to the http level and allow them to be inherited by both server blocks.

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

server {
    listen 443 ssl;
    server_name www.example.com;
    return 301 $scheme://example.com$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;
    ...
}

See this document for more.

Richard Smith
  • 45,711
  • 6
  • 82
  • 81
  • Make the changes, but now I do not like certificate problems, but when entering my domain without www my blog works correctly, but when entering the domain with www I throw the following error Chrome: ERR_SOCKET_NOT_CONNECTED Try on other browsers and give me the same error, saying that the server is overloaded. Do you have any idea what may be happening? – gach3z Feb 27 '17 at 20:56
  • I do not. Have you cleared the browser's cache since changing the server? – Richard Smith Feb 27 '17 at 21:06
  • Clear cache, even perform tests on other devices and the same thing happens. I think it is a drawback with nodejs applications, since in other applications that I am developing I have the same inconvenience. – gach3z Mar 01 '17 at 01:48
  • @AtaSanchez You can chose either with or without www. as the official url, then use the proxy to do redirect for the other one. That is how I do it with mine. – John Siu Mar 13 '17 at 04:04
  • @JohnSiu could you share your Nginx configuration file as a response? Since I honestly can not solve it and continue with the problem. – gach3z Mar 15 '17 at 20:20
  • @AtaSanchez I don't use Nginx myself. How busy is your site? And are you using nginx on the same box as Ghost? If you are using a one box setup and your site is not too busy (<20 page hit / second), I can give you my nodejs proxy setup. – John Siu Mar 15 '17 at 21:02
  • @JohnSiu Currently we use pm2 to manage a site with Ghost, and I use Nginx to be able to manage several domains at the same time, since they are housed in a droplet of Digital Ocean. I am curious about the code of your proxy in NodeJS, could you share it for you to try? – gach3z Mar 16 '17 at 00:00
0

(OP did request to see my nodejs proxy code in comment from another answer.)

Before we jump into nodejs proxy, we should check several things:

Certificate

Is your certificate valid for both with and without www?

If your certificate is only valid for without www (or the other way round), then no proxy (nginx or mine or any other) setting can help. Because the SSL/TLS handshake happen BEFORE redirect. The browser will complaint right away, before any redirect happen. End user see certificate error in browser.

Mine is valid for both and you can check out my blog https://johnsiu.com and https://www.johnsiu.com . The redirect happen without any certificate complain.

DNS

Is your DNS setup correctly for both with and without www? They should point to the same IP address.

I am wondering if this has anything to do with the ERR_SOCKET_NOT_CONNECTED error.

Nodejs Proxy

It is hosted in ghithub: https://github.com/J-Siu/ghost-https-nodejs-proxy

I just finish fine tuning my nodejs proxy, following is the latest version:

// HTTPS

const fs = require('fs');
const url = require('url');
const http = require('http');
const https = require('spdy'); // http2 support
const proxy = require('http-proxy').createProxyServer();
const compression = require('compression');
const ex = require('express')();
const fqdn = '<your domain name here>';

// Fill in your certificate files
const serverKey = '<KEY file>';
const serverCrt = '<CRT file>';
//const serverCa='<CA file>';

const httpsOptions = {
 key: fs.readFileSync(serverKey),
 cert: fs.readFileSync(serverCrt),
 // ca: fs.readFileSync(serverCa),
 ciphers: [
  "ECDHE-RSA-AES256-SHA384",
  "DHE-RSA-AES256-SHA384",
  "ECDHE-RSA-AES256-SHA256",
  "DHE-RSA-AES256-SHA256",
  "ECDHE-RSA-AES128-SHA256",
  "DHE-RSA-AES128-SHA256",
  "HIGH",
  "!aNULL",
  "!eNULL",
  "!EXPORT",
  "!DES",
  "!RC4",
  "!MD5",
  "!PSK",
  "!SRP",
  "!CAMELLIA"
 ].join(':'),
};

// Ghost Proxy configuration

proxy.on('proxyReq', function (proxyReq, req, res, options) {

 // Ngix: proxy_set_header Host $http_host;
 proxyReq.setHeader('Host', req.headers.host);

 // Ngix: proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
 proxyReq.setHeader('X-Forwarded-For', req.connection.remoteAddress);

 // Ngix: proxy_set_header X-Forwarded-Proto $scheme;
 proxyReq.setHeader('X-Forwarded-Proto', 'https');

});

ex
 .use(compression())
 .use((req, res) => {
  if (req.header.host == fqdn) {
   proxy.web(req, res, { target: 'http://localhost:2368' });
  } else {
   res.writeHead(301, { 'location': 'https://' + fqdn + req.url });
   res.end();
  }
 })

// HTTPS Server

https.createServer(httpsOptions, ex).listen(443, '0.0.0.0');

// HTTP Redirect to HTTPS

http.createServer(function (req, res) {
 res.writeHead(301, { 'location': 'https://' + fqdn + req.url });
 res.end();
}).listen(80, '0.0.0.0');

It is able to:

  1. redirect any http traffic to https (I am not checking url here)
  2. redirect any https traffic to the correct fqdn (any request not matching my fqdn is redirected)
  3. ab test show ~16request/sec

I create this for my single box single site setup and specifically to avoid any apache/nginx package while supporting HTTPS/HTTP2. This may not suite your need completely as you are doing multiple sites.

John Siu
  • 5,056
  • 2
  • 26
  • 47
  • This works well for me, but it's not exactly what I want to do, it would turn out to be an alternative solution. With this configuration, adapting to my needs managed to perform the redirection correctly, but using Nginx was impossible. – gach3z Mar 23 '17 at 15:45
  • @AtaSanchez Glad this help. Since my code works, that means you do not have cert issue. And the nginx config you post look good once you move the cert to top level. It actually feel strange that my code works while Nginx does not. :P – John Siu Mar 23 '17 at 15:52
  • actually, I think there is some kind of bug in Nginx when using a certificate of Let's Encrypt, since I can not find another logical reason to explain this phenomenon, however I do the same in 4 applications that work across different domains in it VM, so it's frustrating. – gach3z Mar 23 '17 at 16:00
0

This is my Nginx configuration file, where I am serving a blog with Ghost. The certificate is generated with Let's Encrypt, and works correctly for both domains. The disadvantage is given when performing the 301 redirection, since it does not work correctly because the domain with www tells me that the certificate is not valid, however if I try to make the redirection to the reverse, from not www to www, it shows me the Same message, but for the domain without www:

server {
    listen 80;
    server_name www.example.com;
    return 301 https://example.com$request_uri;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

server {
    listen 443;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

server {
    listen 443;
    server_name www.example.com;

    ssl on;
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;

    include snippets/ssl-params.conf;

    location / {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_set_header X-NginX-Proxy true;
            proxy_pass http://localhost:9002/;
            proxy_redirect off;
    }
}

Currently, I am using a droplet of Digital Ocean, where I have configured 3 fields A, a field A with the value * that is directed to the IP of the virtual machine, a field @ that also addresses the IP of the server, and Finally a field A with value www that goes to the IP of my server.

gach3z
  • 531
  • 3
  • 20