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

Эта статья не претендует на инструкцию и была написана как черновик, чтобы потом мог вспомнить как это делается.

За основу взяты материалы из разных источников, как говориться, с миру по нитке.

Итак, начнем!

Я буду настраивать NGINX как шлюз для нескольких серверов с своей локальной сети.

Имеем:

Ubuntu server 18.04.3

Обновляем и устанавливаем Web-сервер

sudo apt install nginx -y

Далее потребуется создать директорию для сайта, например: /var/www/mysite.ru

sudo mkdir /var/www/misite.ru

И создадим файл конфига для нашего сайта

sudo nano /etc/nginx/sites-available/mysite.ru

Заполним его следующим содержимым:

upstream mysite.ru {
        server 192.168.0.6;
}

server {
        listen 80; # прослушиваемый порт
        server_name mysite.ru; # здесь можно указать несколько сайтов, например
                               # mysite.ru www.mysite.ru mail.mysite.ru
        # в следующей секции указывается суб-директория для добавления / обновления
        # сертификатов для папки созданной на предыдущем шаге
        # данная директория проксироваться не будет
        location /.well-known {
                alias /var/www/mysite.ru/.well-known;
        }

        location / {
        proxy_pass https://192.168.0.6;
        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;
    }
}

Далее активируем сайт

sudo ln -s /etc/nginx/sites-available/mysite.ru /etc/nginx/sites-enabled/mysite.ru

И перезапускаем службу сервера

sudo service nginx restart 

Затем устанавливаем Letsencrypt

sudo apt-get install letsencrypt

И выпускаем сертификат для нашего сайта (сайтов)

sudo letsencrypt certonly --webroot -w /var/www/mysite.ru -d mysite.ru

Или так, для нескольких доменных имен

sudo letsencrypt certonly --webroot -w /var/www/mysite.ru -d mysite.ru -d www.mysite.ru -d mail.mysite.ru

Чтобы сертбот мог выпустить сертификат, публичные DNS-сервера должны знать о доменных именах указываемых сертботу.

Далее, нужно сгенерировать dhparam сертификат для нашего сайта, чтобы было удобнее, лучше создать отдельную папку для каждого сайта

sudo mkdir /etc/nginx/ssl/mysite.ru
sudo openssl dhparam -out dhparam.pem 2048

Теперь надо переключить сайт на SSL

upstream mysite.domain.ru {
        server 192.168.0.6;
}

server {
        listen 80;
        server_name mysite.ru;
        return 301 https://$host$request_uri; #правило перенаправления с http на https
}

server {
        listen 443 ssl http2;
        server_name mysite.ru;
        ssl_certificate /etc/letsencrypt/live/mysite.ru/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mysite.ru/privkey.pem;
        ssl_trusted_certificate /etc/letsencrypt/live/mysite.ru/chain.pem;
        ssl_dhparam /etc/nginx/ssl/mysite.ru/dhparam.pem;
        
        #Настройки ssl ниже оставляю без изменений для большинства сайтов
        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
        ssl_prefer_server_ciphers On;        
        
        #Параметр ниже нужно раскомментировать после проверки сайта
        #add_header Strict-Transport-Security "max-age=31536000";
        
        #Для обновления сертификатов 
        location /.well-known {
                alias /var/www/mysite.ru/.well-known;
        }

        location / {
        proxy_pass https://192.168.0.6;
        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;
        #В этот файл складываю общие настройки прокси (см ниже)
        include /etc/nginx/sites-available/proxy.cfg;
    }
}

И добавляем общий прокси конфиг

proxy_buffers 32 4m;
proxy_busy_buffers_size 25m;
proxy_buffer_size 512k;
proxy_ignore_headers "Cache-Control" "Expires";
proxy_max_temp_file_size 0;
client_max_body_size 1024m;
client_body_buffer_size 4m;
proxy_connect_timeout 300;
proxy_read_timeout 300;
proxy_send_timeout 300;
proxy_intercept_errors off;

Для исключения возможности подключения к серверу по IP, достаточно добавить в конфигурацию первого сайта по алфавиту (сразу после блока upstream):

server {    
    return 444;
}

Это заблокирует доступ по http, но если добавить https перед ip — доступ останется. Если попробовать заблокировать также https — перестают работать корректно https сайты. Поэтому, в блок сервера https добавить условие сразу после строки с server_name:

..
server_name example.com
if ($host != "example.com") {
    return 444;
}
..

Для обновления сертификатов, на мой взгляд удобнее обновлять все сразу, используем команду certbot renew

Поскольку сертификаты выдаются на три месяца и обновлять сертификаты можно уже через неделю, проще добавить задание в планировщик

sudo crontab -e
...
0 1 * * 6 certbot renew

Данная команда будет запускать обновление всех сертификатов каждую субботу в час ночи

Если сертификат уже не нужен, его удалить нельзя, можно просто отключить обновление удалив файл сертификата /etc/letsencrypt/renewal/mysite.ru.conf предварительно сохранив его копию

sudo letsencrypt renew --dry-run --agree-tos

Проверить, что сертификат не будет обновляться можно запустив тестовое обновление:

sudo letsencrypt renew --dry-run --agree-tos

ЕСЛИ НА BACK-END СЕРВЕРЕ УСТАНОВЛЕН APACHE2

Если на сервер в локальной сети работает апач, для корректной работы https, сайт должен иметь SSL сертификаты.

Установи Letcencrypt

sudo apt update
sudo apt install python-letsencrypt-apache -y

Система установит все необходимые пакеты, после чего надо активировать SSL модуль апача

sudo a2enmod ssl

Далее, активируем дефолтный SSL конфиг или создадим свой

sudo cp /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-available/mysite-ssl.ru.conf

И поправим его

sudo nano /etc/apache2/sites-available/mysite-ssl.ru.conf
<IfModule mod_ssl.c>
        <VirtualHost _default_:443>
                ServerName mysite.ru
                ServerAlias www.mysite.ru
                ServerAdmin webmaster@localhost
 
                DocumentRoot /var/www/mysite.ru
                
                # Available loglevels: trace8, ..., trace1, debug, info, notice$
                # error, crit, alert, emerg.
                # It is also possible to configure the loglevel for particular
                # modules, e.g.
                #LogLevel info ssl:warn

                ErrorLog ${APACHE_LOG_DIR}/error.log
                CustomLog ${APACHE_LOG_DIR}/access.log combined

                # For most configuration files from conf-available/, which are
                # enabled or disabled at a global level, it is possible to
                # include a line for only one particular virtual host. For exam$
                # following line enables the CGI configuration for this host on$
                # after it has been globally disabled with "a2disconf".
                #Include conf-available/serve-cgi-bin.conf

                #   SSL Engine Switch:
                #   Enable/Disable SSL for this virtual host.
                SSLEngine on

                #   A self-signed (snakeoil) certificate can be created by inst$
                #   the ssl-cert package. See
                #   /usr/share/doc/apache2/README.Debian.gz for more info.
                #   If both key and certificate are stored in the same file, on$
                #   SSLCertificateFile directive is needed.
                SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
                SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key                   
                
                #   Server Certificate Chain:
                #   Point SSLCertificateChainFile at a file containing the
                #   concatenation of PEM encoded CA certificates which form the
                #   certificate chain for the server certificate. Alternatively
                #   the referenced file can be the same as SSLCertificateFile
                #   when the CA certificates are directly appended to the server
                #   certificate for convinience.
                #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt

                #   Certificate Authority (CA):
                #   Set the CA certificate verification path where to find CA
                #   certificates for client authentication or alternatively one
                #   huge file containing all of them (file must be PEM encoded)
                #   Note: Inside SSLCACertificatePath you need hash symlinks
                #                to point to the certificate files. Use the pro$
                #                Makefile to update the hash symlinks after cha$
                #SSLCACertificatePath /etc/ssl/certs/
                #SSLCACertificateFile /etc/apache2/ssl.crt/ca-bundle.crt                

                #   Certificate Revocation Lists (CRL):
                #   Set the CA revocation path where to find CA CRLs for client
                #   authentication or alternatively one huge file containing all
                #   of them (file must be PEM encoded)
                #   Note: Inside SSLCARevocationPath you need hash symlinks
                #                to point to the certificate files. Use the pro$
                #                Makefile to update the hash symlinks after cha$
                #SSLCARevocationPath /etc/apache2/ssl.crl/
                #SSLCARevocationFile /etc/apache2/ssl.crl/ca-bundle.crl

                #   Client Authentication (Type):
                #   Client certificate verification type and depth.  Types are
                #   none, optional, require and optional_no_ca.  Depth is a
                #   number which specifies how deeply to verify the certificate
                #   issuer chain before deciding the certificate is not valid.
                #SSLVerifyClient require
                #SSLVerifyDepth  10

                #   SSL Engine Options:
                #   Set various options for the SSL engine.
                #   o FakeBasicAuth:
                #        Translate the client X.509 into a Basic Authorisation.$
                #        the standard Auth/DBMAuth methods can be used for acce$
                #        user name is the `one line' version of the client's X.$
                #        Note that no password is obtained from the user. Every$
                #        file needs this password: `xxj31ZMTZzkVA'.
                #   o ExportCertData:
                #        This exports two additional environment variables: SSL$
                #        SSL_SERVER_CERT. These contain the PEM-encoded certifi$
                #        server (always existing) and the client (only existing$
                #        authentication is used). This can be used to import th$
                #        into CGI scripts.
                #   o StdEnvVars:
                #        This exports the standard SSL/TLS related `SSL_*' envi$
                #        Per default this exportation is switched off for perfo$
                #        because the extraction step is an expensive operation $
                #        useless for serving static content. So one usually ena$
                #        exportation for CGI and SSI requests only.
                #   o OptRenegotiate:
                #        This enables optimized SSL connection renegotiation ha$
                #        directives are used in per-directory context.
                #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
                <FilesMatch "\.(cgi|shtml|phtml|php)$">
                                SSLOptions +StdEnvVars
                </FilesMatch>
                <Directory /usr/lib/cgi-bin>
                                SSLOptions +StdEnvVars
                </Directory>

                #   SSL Protocol Adjustments:
                #   The safe and default but still SSL/TLS standard compliant s$
                #   approach is that mod_ssl sends the close notify alert but d$
                #   the close notify alert from client. When you need a differe$
                #   approach you can use one of the following variables:
                #   o ssl-unclean-shutdown:
                #        This forces an unclean shutdown when the connection is$
                #        SSL close notify alert is send or allowed to received.$
                #        the SSL/TLS standard but is needed for some brain-dead$
                #        this when you receive I/O errors because of the standa$
                #        mod_ssl sends the close notify alert.
                #   o ssl-accurate-shutdown:
                #        This forces an accurate shutdown when the connection i$
                #        SSL close notify alert is send and mod_ssl waits for t$
                #        alert of the client. This is 100% SSL/TLS standard com$
                #        practice often causes hanging connections with brain-d$
                #        this only for browsers where you know that their SSL i$
                #        works correctly.
                #   Notice: Most problems of broken clients are also related to$
                #   keep-alive facility, so you usually additionally want to di$
                #   keep-alive for those clients, too. Use variable "nokeepaliv$
                #   Similarly, one has to force some clients to use HTTP/1.0 to$
                #   their broken HTTP/1.1 implementation. Use variables "downgr$
                #   "force-response-1.0" for this.
                # BrowserMatch "MSIE [2-6]" \
                #               nokeepalive ssl-unclean-shutdown \
                #               downgrade-1.0 force-response-1.0

        </VirtualHost>
</IfModule>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

Так же создадим файл виртуального хоста

sudo nano /etc/apache2/sites-available/mysite.ru.conf

<VirtualHost *:80>
        ServerName mysite.ru
        ServerAlias www.mysite.ru
        ServerAdmin webmaster@mysite.ru
        DocumentRoot /var/www/mysite.ru
        <Directory /var/www/mysite.ru>
                AllowOverride All
        </Directory>

        ErrorLog /var/www/mysite.ru/logs/error.log
        CustomLog /var/www/mysite.ru/logs/access.log combined
        Include conf-available/serve-cgi-bin.conf
</VirtualHost>

Активируем наши конфиги

sudo a2ensite mysite-ssl.ru.conf
sudo a2ensite mysite.ru.conf
sudo service apache2 reload

Выпускаем сертификат

sudo letsencrypt --apache -d  mysite.ru  -d  www.mysite.ru

Служба задаст несколько вопросов, потребуется зарегистрировать почту, без этого ни как, согласиться на отправку спама от EFF (главного спонсора) и выбрать, будет ли осуществляться автоматическая переадресация на SSL, в результате мы получим такой конфиг

<VirtualHost *:80>
        ServerName mysite.ru
        ServerAlias www.mysite.ru
        ServerAdmin webmaster@mysite.ru
        DocumentRoot /var/www/mysite.ru
        <Directory /var/www/mysite.ru>
                AllowOverride All
        </Directory>

        ErrorLog /var/www/mysite.ru/logs/error.log
        CustomLog /var/www/mysite.ru/logs/access.log combined
        Include conf-available/serve-cgi-bin.conf
RewriteEngine on
RewriteCond %{SERVER_NAME} =www.mysite.ru [OR]
RewriteCond %{SERVER_NAME} =mysite.ru
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>

Все, после этих манипуляций наш сайт будет работать по безопасному протоколу HTTPS