Данное руководство покажет, как настроить балансировку нагрузки Nginx и SSL-терминацию, используя всего один SSLсертификат на балансировщике нагрузки. Это позволяет сократить расходы на управление SSL, так как управлять обновлениями OpenSSL, ключами и сертификатами теперь можно с самого балансировщика.
Что такое терминация SSL?
Nginx можно настроить как балансировщик нагрузки, что позволяет распределять входящий трафик между несколькими внутренними серверами. SSL-терминация является процессом на балансировщике нагрузки, который обрабатывает шифрование и дешифровку SSL таким образом, чтобы трафик между балансировщиком и внутренними серверами был в HTTP. Серверы на бэкэнде должны быть защищены путем ограничения доступа к IP балансировщика нагрузки, о чём и пойдёт рчь в данной статье.
Требования
Все команды нужно запускать с правами root или sudo. Подробнее об этом можно прочитать в этом руководстве.
Кроме того, могут оказаться полезными следующие руководства:
- Установка LAMP stack на Ubuntu 14.04
- Установка нескольких SSL-сертификатов на один IP с помощью Nginx
- Балансировка нагрузки Nginx
В целом, стек LAMP не является обязательным, но далее он будет использован в качестве примера.
Подготовка серверов
В данном руководстве будет использовано три облачных сервера:
Сервер 1 (фронт-энд)
- Ubuntu 14.04
- Имя хоста: loadbalancer
- IP-адрес: 10.130.227.33
Сервер 2 (бэкэнд)
- Ubuntu 14.04
- Имя хоста: web1
- IP-адрес: 10.130.227.11
Сервер 3 (бэкэнд)
- Ubuntu 14.04
- Имя хоста: web2
- IP-адрес: 10.130.227.22
Условное доменное имя – example.com.
Кроме того, на всех серверах нужно включить поддержку частной сети.
Обновите программы на всех серверах:
apt-get update && apt-get upgrade -y
Перезапустите каждый сервер, чтобы активировать обновления. Это очень важно, так как для работы понадобится последняя версия OpenSSL.
Далее нужно будет создать для домена новый виртуальный хост Nginx с модулем upstream.
Если веб-сервер Nginx не был установлен на облачный сервер ранее, установите его при помощи команды:
apt-get install nginx
На серверах бэкэнда обновите репозитории и установите Apache:
apt-get install apache2
Также на эти серверы нужно установить PHP:
apt-get install php5 libapache2-mod-php5 php5-mcrypt
Примечание: Более подробную информацию по установке этих программ можно найти здесь.
Генерирование ключей и создание SSL-сертификата
Примечание: Подробнее об этом можно прочесть здесь.
Создайте каталог для хранения SSL-сертификата и перейдите в него:
mkdir -p /etc/nginx/ssl/example.com
cd /etc/nginx/ssl/example.com
Создайте закрытый ключ:
openssl genrsa -des3 -out server.key 2048
Удалите фразовый пароль:
openssl rsa -in server.key -out server.key
Создайте запрос на подпись сертификата (CSR):
openssl req -new -key server.key -out server.csr
Используйте этот CSR, чтобы подписать сертификат в надёжном центре сертификации, или же создайте самоподписанный сертификат при помощи команды:
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
После этого в каталоге появятся следующие файлы:
- server.key – закрытый ключ
- ca-certs.pem – промежуточные сертификаты (появится только в случае подписи сертификата в ЦС).
- server.crt – SSL-сертификат для доменного имени.
Виртуальный хост и модуль Upstream
Создайте виртуальный хост в каталоге Nginx:
nano /etc/nginx/sites-available/example.com
Добавьте модуль upstream, указав IP-адреса серверов бэкэнда:
upstream mywebapp1 {
server 10.130.227.11;
server 10.130.227.22;
}
После этой строки внесите в файл хоста код блока server. Этот код содержит доменное имя, ссылки на серверы upstream и заголовки, которые нужно передать бэкэнду.
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://mywebapp1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Директива proxy_set_header используется для передачи важной информации о запросе серверам upstream.
Сохраните файл и создайте символьную ссылку на каталог sites-enabled.
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
Проверьте конфигурацию на наличие ошибок.
service nginx configtest
Если ошибок не обнаружено, перезапустите сервер nginx.
service nginx reload
Теперь настройка балансировки нагрузки для HTTP завершена.
Включение SSL
Добавьте следующие директивы в файл виртуального хоста (/etc/nginx/sites-available/example.com) в блок server {}.
listen 443 ssl;
ssl on;
ssl_certificate /etc/nginx/ssl/example.com/server.crt;
ssl_certificate_key /etc/nginx/ssl/example.com/server.key;
ssl_trusted_certificate /etc/nginx/ssl/example.com/ca-certs.pem;
Пропустите директиву ssl_trusted_certificate, если вы используете самоподписанный сертификат. Теперь блок server выглядит так:
server {
listen 80;
listen 443 ssl;
server_name example.com www.example.com;
ssl on;
ssl_certificate /etc/nginx/ssl/example.com/server.crt;
ssl_certificate_key /etc/nginx/ssl/example.com/server.key;
ssl_trusted_certificate /etc/nginx/ssl/example.com/ca-certs.pem;
location / {
proxy_pass http://mywebapp1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Проверьте код на наличие ошибок и перезапустите сервер Nginx:
service nginx configtest && service nginx reload
Защита серверов бэкэнда
В настоящее время сайт размещается на серверах бэкэнда, доступных любому пользователю, который знает их внешний IP-адрес. Это необходимо исправить; для этого нужно настроить эти серверы для прослушивания только персонального интерфейса. На веб-сервере Apache это делается следующим образом.
Отредактируйте ports.conf.
nano /etc/apache2/ports.conf
Найдите следующую строку:
Listen 80
Замените номер порта закрытым IP-адресом сервера, например:
Listen 10.130.227.22:80
Выполните это на всех серверах бэкэнда и перезапустите Apache.
service apache2 restart
Затем нужно ограничить HTTP-доступ к внутреннему IP балансировщика нагрузки. Для этого добавьте следующее правило брандмауэра:
iptables -I INPUT -m state --state NEW -p tcp --dport 80 ! -s 10.130.227.33 -j DROP
Не забудьте указать свой IP-адрес.
Тестирование настройки
Создайте файл PHP на всех серверах бэкэнда (в данном случае web1 и web2). Он нужен для тестирования, после этого его можно удалить.
nano /var/www/html/test.php
Он должен содержать доменное имя, IP-адрес сервера, IP-адрес пользователя, и порт для доступа.
<?php
header( 'Content-Type: text/plain' );
echo 'Host: ' . $_SERVER['HTTP_HOST'] . "\n";
echo 'Remote Address: ' . $_SERVER['REMOTE_ADDR'] . "\n";
echo 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR'] . "\n";
echo 'X-Forwarded-Proto: ' . $_SERVER['HTTP_X_FORWARDED_PROTO'] . "\n";
echo 'Server Address: ' . $_SERVER['SERVER_ADDR'] . "\n";
echo 'Server Port: ' . $_SERVER['SERVER_PORT'] . "\n\n";
?>
Откройте этот файл в браузере несколько раз при помощи команды curl.
Примечание: В случае использования самоподписанного сертификата используйте команду curl –k, чтоб игнорировать ошибки SSL.
curl https://example.com/test.php https://example.com/test.php https://example.com/test.php
Вывод будет выглядеть так:
Host: example.com
Remote Address: 10.130.245.116
X-Forwarded-For: 117.193.105.174
X-Forwarded-Proto: https
Server Address: 10.130.227.11
Server Port: 80
Host: example.com
Remote Address: 10.130.245.116
X-Forwarded-For: 117.193.105.174
X-Forwarded-Proto: https
Server Address: 10.130.227.22
Server Port: 80
Host: example.com
Remote Address: 10.130.245.116
X-Forwarded-For: 117.193.105.174
X-Forwarded-Proto: https
Server Address: 10.130.227.11
Server Port: 80
Обратите внимание: Server Address изменяется при каждом запросе, а это значит, что запросы обрабатываются разными серверами.
Защита SSL
В данном разделе показано, как защитить SSL от уязвимостей. Полный код можно найти в конце раздела.
Включение кэша SSL-сессий увеличивает производительность сайтов HTTPS. Добавьте следующие директивы после ssl_trusted_certificate. Это включит кэш в 20 мегабайт со сроком хранения в 10 минут.
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
Укажите протоколы и шифры, используемые для подключения SSL. Ниже пропущен SSLv2 и отключены ненадёжные шифры (такие как MD5 и DSS).
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
Strict Transport Security рекомендует браузерам использовать только HTTPS. Для этого добавьте директиву add_header:
add_header Strict-Transport-Security "max-age=31536000";
Проверьте конфигурации на наличие ошибок и перезапустите Nginx.
service nginx configtest && service nginx reload
Полная конфигурация
В результате настройки и защиты SSL конфигурационный файл будет иметь такой вид:
/etc/nginx/sites-available/example.com
upstream mywebapp1 {
server 10.130.227.11;
server 10.130.227.22;
}
server {
listen 80;
listen 443 ssl;
server_name example.com www.emxaple.com;
ssl on;
ssl_certificate /etc/nginx/ssl/example.com/server.crt;
ssl_certificate_key /etc/nginx/ssl/example.com/server.key;
ssl_trusted_certificate /etc/nginx/ssl/example.com/ca-certs.pem;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS;
add_header Strict-Transport-Security "max-age=31536000";
location / {
proxy_pass http://mywebapp1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Выполните SSL Server Test, чтобы убедиться, что эта конфигурация надёжна.
Запустите curl, и вы увидите, что всё работает должным образом:
curl https://example.com/test.php https://example.com/test.php https://example.com/test.php
Примечание: Дополнительную информацию о настройке балансировки нагрузки можно получить здесь.