SSL-перенаправление и аутентификация в блоге Ghost с помощью Nginx

У меня есть блог о Ghost на DigitalOcean. То же самое обслуживает Nginx со следующим файлом конфигурации:

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;
    }
}

Сертификаты были сгенерированы с помощью Let’s Encrypt, как для домена с www, так и без него. В файле Ghost config.js URL-адрес написан с правилами для использования SSL: https://example.com/ Проблема в том, что когда я вхожу в свой блог, Мой домен без wwww, я вхожу в систему правильно и с SSL, но когда я пытаюсь войти в систему с помощью https://www.example.com Я получаю сообщение об ошибке аутентификации сертификата SSL. Я действительно не понимаю, в чем может быть проблема. Мне нужно, чтобы при входе в мой домен с www я перенаправлял на домен, но без www. Эту операцию я проделывал раньше с другим узлом приложений без проблем, с тем же кодом конфигурации, что и выше.


person Ata Sanchez    schedule 26.02.2017    source источник
comment
есть прогресс в этом вопросе?   -  person John Siu    schedule 21.03.2017
comment
@JohnSiu, обнаружение ошибки, по-видимому, обнаруженной при использовании Let's Encrypt, но я до сих пор не могу это доказать. У меня мало времени, но когда у меня будет прогресс, я опубликую их, так как этот код работает для меня, чтобы не использовать сертификат SSL.   -  person Ata Sanchez    schedule 23.03.2017
comment
Основываясь на последней части вашего поста, у вас может быть проблема с сертификатом, о которой я упоминал в своем ответе. Дважды проверьте, что ваш сертификат работает ОБА с www и без него.   -  person John Siu    schedule 23.03.2017
comment
@JohnSiu, Сертификат работает корректно для обоих доменов, как с www, так и без www. Проблема возникает с редиректом с Nginx, в этот момент я говорю, что домен не может проверить подлинность моего сайта и что сертификат не заслуживает доверия, и если я пытаюсь сделать редирект в обратном порядке, то же самое, мол что домен без www может и не увидеть. Сейчас я использую прокси со своего Node-сервера, и отказываюсь от использования Nginx, так как толком не нашел решения этой проблемы.   -  person Ata Sanchez    schedule 23.03.2017


Ответы (5)


Я исследовал большую часть выходных, чтобы решить эту проблему, и мне удалось найти решение. Ни в Let’s Encrypt, ни в Nginx нет ошибок, все ссылаются на плохую конфигурацию с моей стороны; Однако я не могу перестать думать, что это странная вещь, и что мое решение можно улучшить, даже не говоря больше, это мой файл конфигурации Nginx:

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;
    }
}

Чтобы избежать ошибок перетаскивания, полностью удалите мою виртуальную машину и создайте ее с нуля, также установите последнюю версию Nginx для Debian, то есть 1.10.3, которая позволяет мне использовать http2, что также является улучшением.

person Ata Sanchez    schedule 26.03.2017
comment
Интересный. Таким образом, правильный способ сделать перенаправление www — просто поместить его в тот же блок. - person John Siu; 27.03.2017
comment
@JohnSiu, это единственный способ добиться оптимальной производительности во всех ситуациях. Таким образом, удалось избежать использования предложения перезаписи (которое тоже не работает) и выполнить переадресацию в отдельных блоках сервера. Тем не менее, я уверен, что вы еще можете улучшить эту конфигурацию, но я рад, что смог решить эту проблему. Спасибо за ваши ответы. - person Ata Sanchez; 27.03.2017
comment
Я не знаком с конфигурацией nginx. Но логика перенаправления URL-адреса похожа на мое решение nodejs. Я не думаю, что эта часть значительно улучшена, и не стоит тратить время, поскольку она должна встречаться только один раз для каждого браузера (или до тех пор, пока они не очистят кеш). С другой стороны, почему «proxy_ssl_session_reuse» отключен? - person John Siu; 27.03.2017
comment
Поскольку вы используете несколько сайтов с nginx, возможно, это будет полезно для вас: pnommensen.com/2014/09/07/ - person John Siu; 27.03.2017

Одним из решений является запрос и установка второго SSL-сертификата для поддомена www.

Шаги:

  1. Откройте файл конфигурации HTTP nginx: sudo nano /etc/nginx/sites-available/example.com.conf. Дублируйте существующий блок сервера. Оставьте первый блок без изменений, чтобы он продолжал работать для примера.com. Во втором блоке укажите www в строке server_name: server_name www.example.com;. Сохраните и закройте файл.

  2. Откройте файл конфигурации nginx SSL: sudo nano /etc/nginx/sites-available/example.com-ssl.conf. Оставьте первый блок без изменений, чтобы он продолжал работать для примера.com. Во втором блоке включите www в строку server_name, что даст вам server_name www.example.com;. Затем удалите строки, начинающиеся с ssl_certificate и ssl_certificate_key, потому что они нам не нужны, потому что мы будем запрашивать новый SSL-сертификат для поддомена www. Сохраните и закройте файл.

  3. Запросите SSL-сертификат Let's Encrypt для поддомена www: 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 [email protected]

  4. Снова откройте файл конфигурации nginx SSL: sudo nano /etc/nginx/sites-available/example.com-ssl.conf. Вернитесь к серверному блоку www.example.com, который вы настроили на шаге 2, и повторно добавьте строки ssl_certificate: ssl_certificate /etc/letsencrypt/www.example.com/fullchain.cer; и ssl_certificate_key /etc/letsencrypt/www.example.com/www.example.com.key;. Сохраните и закройте файл.

  5. Перезагрузите nginx с помощью sudo nginx -s reload, и ошибка должна исчезнуть.

person hbere    schedule 18.06.2018

В вашем первом блоке сервера нет определения сертификата. Поэтому вы можете дублировать операторы ssl_certificate и ssl_certificate_key в этом блоке server.

Если это единственные блоки SSL server и поскольку у вас есть общий файл сертификата для обоих доменов, вы можете переместить операторы ssl_certificate и ssl_certificate_key на уровень http и разрешить их наследование обоими блоками server.

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;
    ...
}

Дополнительные сведения см. в этом документе.

person Richard Smith    schedule 27.02.2017
comment
Внесите изменения, но теперь мне не нравятся проблемы с сертификатом, но при входе в мой домен без www мой блог работает правильно, но при входе в домен с www выдает следующую ошибку Chrome: ERR_SOCKET_NOT_CONNECTED Попробуйте в других браузерах и выдайте мне ту же ошибку , говоря, что сервер перегружен. Вы хоть представляете, что может происходить? - person Ata Sanchez; 27.02.2017
comment
Я не. Вы очищали кеш браузера после смены сервера? - person Richard Smith; 28.02.2017
comment
Очистите кеш, даже проведите тесты на других устройствах, и произойдет то же самое. Я думаю, что это недостаток приложений nodejs, поскольку в других приложениях, которые я разрабатываю, у меня такие же неудобства. - person Ata Sanchez; 01.03.2017
comment
@AtaSanchez Вы можете выбрать с www или без него. в качестве официального URL-адреса, затем используйте прокси-сервер для перенаправления другого. Я так со своим делаю. - person John Siu; 13.03.2017
comment
@JohnSiu, не могли бы вы поделиться своим файлом конфигурации Nginx в ответ? Так как я честно не могу решить ее и продолжаю с проблемой. - person Ata Sanchez; 15.03.2017
comment
@AtaSanchez Я сам не использую Nginx. Насколько загружен ваш сайт? И вы используете nginx на той же машине, что и Ghost? Если вы используете установку из одного блока и ваш сайт не слишком загружен (‹20 просмотров страниц в секунду), я могу дать вам настройку моего прокси-сервера nodejs. - person John Siu; 16.03.2017
comment
@JohnSiu В настоящее время мы используем pm2 для управления сайтом с Ghost, и я использую Nginx для одновременного управления несколькими доменами, поскольку они размещены в дроплете Digital Ocean. Мне любопытен код вашего прокси в NodeJS, не могли бы вы поделиться им, чтобы попробовать? - person Ata Sanchez; 16.03.2017

(OP действительно просил увидеть мой прокси-код nodejs в комментарии из другого ответа.)

Прежде чем мы перейдем к прокси-серверу nodejs, мы должны проверить несколько вещей:

Сертификат

Ваш сертификат действителен как с www, так и без него?

Если ваш сертификат действителен только для без www (или наоборот), то никакая настройка прокси (nginx или мой или любой другой) не поможет. Поскольку рукопожатие SSL/TLS происходит ДО перенаправления. Браузер будет жаловаться сразу, до того, как произойдет какое-либо перенаправление. Конечный пользователь видит ошибку сертификата в браузере.

Мой действителен для обоих, и вы можете проверить мой блог https://johnsiu.com и https://www.johnsiu.com . Перенаправление происходит без каких-либо жалоб на сертификат.

DNS

Правильно ли настроен ваш DNS как с www, так и без него? Они должны указывать на один и тот же IP-адрес.

Мне интересно, связано ли это с ошибкой ERR_SOCKET_NOT_CONNECTED.

Nodejs прокси

Он размещен на ghithub: https://github.com/J-Siu/ghost-https-nodejs-proxy

Я только что закончил тонкую настройку моего прокси-сервера nodejs, вот последняя версия:

// 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');

Он может:

  1. перенаправить любой http-трафик на https (здесь я не проверяю URL-адрес)
  2. перенаправить любой трафик https на правильное полное доменное имя (любой запрос, не соответствующий моему полному доменному имени, перенаправляется)
  3. ab тест показывает ~16запросов/сек

Я создаю это для своей единой настройки одного сайта и специально для того, чтобы избежать использования пакетов apache/nginx при поддержке HTTPS/HTTP2. Это может не полностью удовлетворить ваши потребности, поскольку вы делаете несколько сайтов.

person John Siu    schedule 16.03.2017
comment
Это хорошо работает для меня, но это не совсем то, что я хочу сделать, это оказалось бы альтернативным решением. С этой конфигурацией, адаптировавшейся к моим потребностям, удалось правильно выполнить перенаправление, но использование Nginx было невозможно. - person Ata Sanchez; 23.03.2017
comment
@AtaSanchez Рад этой помощи. Поскольку мой код работает, это означает, что у вас нет проблемы с сертификатом. И конфигурация nginx, которую вы публикуете, выглядит хорошо, как только вы переместите сертификат на верхний уровень. На самом деле странно, что мой код работает, а Nginx нет. :П - person John Siu; 23.03.2017
comment
на самом деле, я думаю, что в Nginx какой-то баг при использовании сертификата Let's Encrypt, так как я не могу найти другой логической причины, объясняющей это явление, однако я делаю то же самое в 4-х приложениях, которые работают в разных доменах в его ВМ, так что это расстраивает. - person Ata Sanchez; 23.03.2017

Это мой файл конфигурации Nginx, в котором я веду блог с помощью Ghost. Сертификат генерируется с помощью Let's Encrypt и корректно работает для обоих доменов. Недостаток дается при выполнении перенаправления 301, так как оно работает некорректно, потому что домен с www говорит мне, что сертификат недействителен, однако, если я пытаюсь сделать перенаправление в обратном направлении, с не www на www, он показывает мне такое же сообщение, но для домена без 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;
    }
}

В настоящее время я использую дроплет Digital Ocean, где я настроил 3 поля A, поле A со значением *, которое указывает на IP-адрес виртуальной машины, поле @, которое также указывает на IP-адрес сервера, и Наконец, поле A со значением www, которое соответствует IP-адресу моего сервера.

person Ata Sanchez    schedule 23.03.2017
comment
Я попробую попробовать nginx на выходных. Обновлю мой результат здесь. - person John Siu; 23.03.2017
comment
Спасибо, в эти выходные я также найду время, чтобы исследовать эту проблему, а также любой прогресс здесь. - person Ata Sanchez; 23.03.2017