Поднимаем несколько HTTPS сайтов на одном IP адресе

Очень часто необходимо разместить сразу несколько HTTPS сайтов с разными сертификатами на одном IP-адресе, однако большинство хостеров заявляют, что это невозможно физически и в таком случае требуется покупать очень дорогой wildcard-сертификат. В данном HOWTO мы развенчаем этот миф с использованием веб-сервера nginx и технологии SNI.

Введение

Для реализации задуманного нам потребуются:

  • сервер с root-доступом и установленным nginx версии 0.8 и выше (рекомендуется 1.4.x);
  • сертификаты для нескольких доменов.

Если у вас веб-сервер не nginx, а например Apache или lighttpd, то вы не сможете разместить несколько HTTPS сайтов на одном IP с использованием технологии SNI, поэтому установите перед ними nginx в качестве фронтенда.

Далее мы будем считать, что у вас имеется хотя бы базовый опыт администрирования веб-сервера nginx.

Установка сертификатов

Не имеет значение какие у вас сертификаты и где они будут расположены, но мы рекомендуем разместить все сертификаты в отдельном каталоге, например, /etc/keys/.

На все сертификаты и их приватные (закрытые) ключи следует установить владельца и группу root:root, а также права доступа chmod 600 для того, чтобы никто не смог похитить закрытую часть ключа.

Первоначальная инициализация nginx всегда выполняется с правами root, поэтому для сервера не будет проблемой считать конфигурационные файлы и сертификаты.

В нашем примере мы рассмотрим сертификаты srv1_example_org.crt, srv2_example_org.crt и их закрытые ключи srv1_example_org.key и srv2_example_org.key соответственно. Каждый выдан только для своего домена (srv1.example.org и srv2.example.org).

Настройка HTTPS Virtual Hosts

Создаём в каталоге /etc/nginx/sites-available/ конфиги для наших доменов srv1.example.org и srv2.example.org: srv1.conf и srv2.conf.

Листинг файла srv1.conf:

upstream backend-srv1 {server unix:/var/run/php5-srv1.sock;}

server {
	listen 80;
	server_name www.srv1.example.org srv1.example.org;
	access_log /var/log/nginx/srv1.log;
	error_log /var/log/nginx/srv1_error.log;
	rewrite ^(.*) https://srv1.example.org$1 permanent;
	server_tokens off;
}

server {
	listen 443 ssl;
	ssl on;
	ssl_certificate /etc/keys/srv1_example_org.crt;
	ssl_certificate_key /etc/keys/srv1_example_org.key;
	ssl_session_timeout 5m;
	ssl_protocols SSLv2 SSLv3 TLSv1;
	ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
	ssl_prefer_server_ciphers on;
	server_name srv1.example.org;
	root /home/srv1/www;
	access_log /var/log/nginx/srv1.log;
	error_log /var/log/nginx/srv1_error.log;
	index index.php;
	rewrite_log on;
	server_tokens off;
	location ~ \.php$ {
		include fastcgi_params;
		fastcgi_pass backend-srv1;
	}
}

Листинг файла srv2.conf:

upstream backend-srv2 {server unix:/var/run/php5-srv2.sock;}

server {
	listen 80;
	server_name www.srv2.example.org srv2.example.org;
	access_log /var/log/nginx/srv2.log;
	error_log /var/log/nginx/srv2_error.log;
	rewrite ^(.*) https://srv2.example.org$1 permanent;
	server_tokens off;
}

server {
	listen 443 ssl;
	ssl on;
	ssl_certificate /etc/keys/srv2_example_org.crt;
	ssl_certificate_key /etc/keys/srv2_example_org.key;
	ssl_session_timeout 5m;
	ssl_protocols SSLv2 SSLv3 TLSv1;
	ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
	ssl_prefer_server_ciphers on;
	server_name srv2.example.org;
	root /home/srv2/www;
	access_log /var/log/nginx/srv2.log;
	error_log /var/log/nginx/srv2_error.log;
	index index.php;
	rewrite_log on;
	server_tokens off;
	location ~ \.php$ {
		include fastcgi_params;
		fastcgi_pass backend-srv2;
	}
}

Перезапустите веб-сервер:

sudo service nginx restart

На этом настройка завершена. Теперь nginx будет использовать разные сертификаты для сайтов srv1.example.org и srv2.example.org, а также выполнять принудительное перенаправление (редирект) с HTTP версии на HTTPS с кодом 301 (moved permanently).

10 commentaries to post

  1. Nikola :

    А как будет реагировать nginx, если стучаться по https://ip ?

    Сработает виртуальный хост server_name IP, либо тот, который прописан в качестве дефолтного для HTTPS (директивой default).

  2. Vladimir :

    @Vitaly
    А если такого нет?

    Если в вашем конфиге нет ни server_name IP, ни виртуального хоста, помеченного как default, то будет использоваться первый HTTPS вхост по списку.

  3. Подскажите, что такое в данном случае строка
    server unix:/var/run/php5-srv1 . sock
    и что если я использую php7?
    А
    root /home/srv1/www
    — это просто путь к файлам сайта?

    1. и что если я использую php7?

      В статье просто примеры. Вы должны использовать те сокеты, которые используются в вашей системе.

      это просто путь к файлам сайта?

      Да.

  4. А как быть, когда на одном IP размещен https://site1 и http://site2? В таком случае при ошибочном обращении по https://site2 сервер любезно отдаст SSL-сертификат от site1 и пользователь увидит алерт о несоответствии сертификата сайту. Порекомендуете рецепт от такого недуга?

    1. Если отдельного виртуального хоста для site2 по HTTPS в файле конфигурации сервера нет, то будет использоваться хост HTTPS по умолчанию. Если для данного домена нет сертификата, то браузером будет выдана ошибка несоответствия домена.

      Поэтому рекомендуется для обоих доменов получить сертификаты бесплатного УЦ Let’s Encrypt. Более того, LE позволяет иметь несколько совершенно разных доменов в CN.

  5. Помогите решить проблему с перекрёстными ссылками. У меня в nginx два домена на одном IP, у каждого свой конфиг и SSL сертификат, и с виду всё работает отлично. Но на втором сайте (который не default) логи переполнены ошибками. Дело в том, что у обоих сайтов есть похожие адреса. И вот в логах второго сайта появляются 404 сообщения типа страница не найдена, — все эти ошибки связаны с URL’ами первого сайта. Т.е. как будто обращение к первому сайту идёт по айпишнику, и на этот запрос зачем-то пытается ответить второй сайт…

    Говорят, можно каждому домену назначить свой IP, но как это сделать правильно и какие IP прописывать? Или есть другое решение?

    1. 1. При правильной настройке default должен отдавать ошибку 403 и ничего больше. Сами ресурсы должны располагаться на виртуальных хостах.

      2. У каждого виртуального хоста должны быть свои собственные журналы доступа и ошибок (access.log и error.log соответственно). Если использовать одинаковые адреса, то они будут перемешиваться и это вполне нормально.

      3. Вручную назначать IP адреса можно только если на сервере несколько публичных. Во всех остальных случаях лучше оставить дефолт.

Обсуждение закрыто.