Журнал Эмбеддед-Инженера

nginx: советы и шпаргалки

Настройки безопасности

Чтобы не светить версии nginx и php делаем следующее: в файле /etc/nginx/nginx.conf в секции http дописываем:

server_tokens off;

а в файле /etc/php.ini дописываем:

expose_php = Off

Рестартуем сервисы:

sudo systemctl restart php-fpm.service
sudo systemctl restart nginx.service

Проверяем командой:

$ curl -I http://idoka.ru

HTTP/1.1 200 OK
Server: nginx
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Keep-Alive: timeout=60
Vary: Accept-Encoding, Cookie
Cache-Control: max-age=3, must-revalidate
Cache-Control: public

Прикрутка GeoIP

Если по  nginx -V  не находим опцию --with-http_geoip_module, но у нас CentOS 7, то, начиная с версии nginx-1.10.0, выход есть:

sudo yum install -y nginx-module-geoip

Либа будет установлена в /usr/lib64/nginx/modules:

ls -l /usr/lib64/nginx/modules/
ngx_http_geoip_module-debug.so*
ngx_http_geoip_module.so*
ngx_http_js_module-debug.so*
ngx_http_js_module.so*

Скачиваем и разворачиваем:

wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
wget http://download.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz
gunzip GeoIP.dat.gz
gunzip GeoLiteCity.dat.gz
gunzip GeoIPASNum.dat.gz

Далее в секции http конфига nginx прописываем пути к скаченным БД (страны, города, провайдеры):

geoip_country  /path/../GeoIP.dat;
geoip_city     /path/../GeoLiteCity.dat;
geoip_org      /path/../GeoIPASNum.dat;

А вне любых секций  конфига nginx добавить:

load_module "modules/ngx_http_geoip_module.so";

Теперь перегружаем nginx:

sudo systemctl restart nginx.service

Вообще говоря, в  директиве geoip_org можно указать БД провайдеров или организаций, но в nginx нельзя иметь одновременно подключенные обе БД — надо определиться с тем что подключать.

Теперь можно, там где надо, передавать необходимые fastcgi параметры (имеет смысл добавить эти параметры в конец файла /etc/nginx/fastcgi_params):

# для стран:
fastcgi_param GEOIP_ADDR $remote_addr;
fastcgi_param GEOIP_COUNTRY_CODE $geoip_country_code;
fastcgi_param GEOIP_COUNTRY_NAME $geoip_country_name;
# для городов:
fastcgi_param GEOIP_REGION $geoip_region;
fastcgi_param GEOIP_REGION_NAME $geoip_region_name;
fastcgi_param GEOIP_CITY $geoip_city;
fastcgi_param GEOIP_AREA_CODE $geoip_area_code;
fastcgi_param GEOIP_LATITUDE $geoip_latitude;
fastcgi_param GEOIP_LONGITUDE $geoip_longitude;
fastcgi_param GEOIP_POSTAL_CODE $geoip_postal_code;
# для провайдеров/организаций:
fastcgi_param GEOIP_ORGANIZATION $geoip_org;

Тестируем работоспособность, включив в секцию server следующий код (понадобится установленный и подключенный в конфиге модуль echo-nginx-module):

# Testing GeoIP
location ^~ /geoip/ {
  add_header Content-Type application/json;
  if ($geoip_country_code ~ "RU") {
    echo '{"key":"russia","country":"$geoip_country_code"}';
  }
  if ($geoip_country_code !~ "RU") {
    echo '{"key":"global","country":"$geoip_country_code"}';
  }
}

либо кодом php:

<?php 
  echo "Your country is {$_SERVER['GEOIP_COUNTRY_NAME']} and your city is {$_SERVER['GEOIP_CITY']}.";
?>

Запрет посещения средствами nginx для определенных стран

В ситуации, когда необходимо заблокировать доступ к хосту пользователям из определенных стран, используются также базы GeoIP для определения принадлежности к стране. Одной из разновидностей задачи является показ сайта на языке, нативном для страны нахождения постетителя.

Впишем следующий маппинг в секцию http конфига nginx:

map $geoip_country_code $allowed_country {
  default yes;
  CN no;
}
Это пример пермиссивной политики: доступ всем, кроме …
Для того чтобы наоборот указать кому открыть доступ, перепишем:
map $geoip_country_code $allowed_country {
  default no;
  RU yes;
  UA yes;
  BY yes;
  KZ yes;
}
Теперь осталось добавить в конфиг виртуального хоста (секция server):
if ($allowed_country = no) {
  return 404;
}

UPD: вариант с автообновлением GEOIP

Ставим:

sudo yum install -y GeoIP GeoIP-data GeoIP-update

GeoIP-update — для автообновления через крон. БД будут располагаться в /usr/share/GeoIP. Теперь осталось прописать в конфиге:

geoip_country /usr/share/GeoIP/GeoIP.dat;
geoip_city    /usr/share/GeoIP/GeoIPCity.dat;
geoip_org     /usr/share/GeoIP/GeoIPASNum.dat;

 

Настройка Let’s Encrypt сертификатов в LEMP на CentOS 7

Если вкратце — не нужно поднимать сервер на 9999 порту, юзаем самый простой способ webroot, это значит что letsencrypt’у достаточно указать в какой папке будет лежать файл для обмена с сервером (подтверждение прав на домен).

Забираем с гитхаба:

git clone --depth=1 https://github.com/letsencrypt/letsencrypt
cd letsencrypt
./letsencrypt-auto

Просит рута, ну ок:

Package gcc-4.8.5-11.el7.x86_64 already installed and latest version
Package augeas-libs-1.4.0-2.el7.x86_64 already installed and latest version
Package 1:openssl-1.0.1e-60.el7.x86_64 already installed and latest version
Package 1:openssl-devel-1.0.1e-60.el7.x86_64 already installed and latest version
Package libffi-devel-3.0.13-18.el7.x86_64 already installed and latest version
Package redhat-rpm-config-9.1.0-72.el7.centos.noarch already installed and latest version
Package ca-certificates-2015.2.6-73.el7.noarch already installed and latest version
Package python-2.7.5-48.el7.x86_64 already installed and latest version
Package python-devel-2.7.5-48.el7.x86_64 already installed and latest version
Package python-virtualenv-1.10.1-3.el7.noarch already installed and latest version
Package python-tools-2.7.5-48.el7.x86_64 already installed and latest version
Package python2-pip-8.1.2-5.el7.noarch already installed and latest version

Питоническая шелуха имелась в системе, а теперь самое интересное: letsencrypt-auto настолько тупой, что не видит, что репозиторий EPEL уже установлен и докучи ставит зачем-то httpd, httpd-tools и mod_ssl (это жесть, господа!). Даже при вызове с опцией —dry-run!! Крайне рукожопный скрипт!
Теперь при попытке удаления httpd он заодно сносит и php, а при установке php после таки-сноса httpd, тянет в качестве зависимости опять же httpd — чтоооооооооооо??????

Ладно, сертификаты важнее, идём дальше. Я не стал конфигурировать (и перезапускать) nginx, как того рекомендуют для указания размещения папки .well-known, если будет необходимость задать расположение .well-known вне папки сайта, синтаксис такой:

location /.well-known {
    root /var/site/idoka.ru;
}

далее пробуем запустить  letsencrypt-auto для нашего домена:

$ ./letsencrypt-auto certonly --agree-tos --register-unsafely-without-email --noninteractive --webroot --webroot-path /var/site/idoka.ru -d idoka.ru

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Registering without email!
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for idoka.ru
Using the webroot path /var/site/idoka.ru for all unmatched domains.
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0001_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0001_csr-certbot.pem

Успех!

Для получения доп.сертификатов для поддоменов можно указывать несколько опций -d с именами поддоменов.

Настройка SSL в nginx сводится к добавлению:

######### SSL #########
ssl on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'HIGH:!aNULL:!MD5:!kEDH';
ssl_certificate     /etc/letsencrypt/live/idoka.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/idoka.ru/privkey.pem;

Или вот настройки, которые предлагает сам letsencrypt в файле certbot-nginx/certbot_nginx/options-ssl-nginx.conf:

ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-SHA384 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES128-SHA DHE-RSA-AES256-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-SHA256 EDH-RSA-DES-CBC3-SHA";

Для OCSP stapling делаем следующее:

wget -O /etc/nginx/ssl/chain.pem "https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem"

А в конфиг добавляем следующее:

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;

После чего перегружаем конфиг nginx:

sudo systemctl reload nginx.service

Последний штрих: автообновление сертификатов. Добавляем в /etc/crontab:

0 2 1 * * letsencrypt-auto renew >> /dev/null 2>&1

Желательно также тамже рестартануть nginx:

5 2 1 * * systemctl reload nginx.service >> /dev/null 2>&1

— первого числа каждого месяца в 2:00 будет осуществлен перевыпуск сертификатов, а в 2:05 перезачитывание конфигов nginx.

Актуальная версия справки: https://letsencrypt.readthedocs.io/en/latest/using.html#webroot

PS: если заметка помогла Вам, поделитесь ей с друзьями или коллегами: