Как настроить обратный прокси nginx для серверов tomcat?

Вопрос или проблема

Итак, мне поручено “модернизировать” приложение плейстоценовой эпохи, и я не могу заставить его работать. Позвольте объяснить:

У меня есть серия серверов Tomcat, каждый со следующей структурой:

(partner1 - tomcatServer1)
http://myServerIp1:8080/ -> индекс Tomcat
http://myServerIp1:8080/webapp1 -> webapp1 (основное)
http://myServerIp1:8080/webapp2 -> webapp2
(partner2 - tomcatServer2)
http://myServerIp2:8080/ -> индекс Tomcat
http://myServerIp2:8080/webapp1 -> webapp1 (основное)
http://myServerIp2:8080/webapp2 -> webapp2
http://myServerIp2:8080/webapp3 -> webapp3

Как вы видите, изначально я нахожу несколько очень заметных проблем.

  1. Во-первых, они используют публичный IP, что усложняет задачу
    найти SSL-сертификат (но это будет решено позже).
  2. Каждый сервер содержит несколько приложений.

Моя первоначальная идея для “модернизации” приложения заключалась в использовании домена и через выделенный сервер nginx реализовать реверс-прокси, который бы использовал следующую структуру:

(partner1 - tomcatServer1)
http://partner1.myDomain.com/ -> webapp1 (основное)
http://partner1.myDomain.com/webapp2 -> webapp2
(partner2 - tomcatServer2)
http://partner2.myDomain.com/ -> webapp1 (основное)
http://partner2.myDomain.com/webapp2 -> webapp2
http://partner2.myDomain.com/webapp3 -> webapp3

И как только это будет достигнуто, я начну разбираться с использованием SSL. Я говорю это и делаю на это особый акцент, потому что не хочу начинать с SSL, не проверив сначала, что это работает без него, потому что я не могу даже сделать хороший proxy_pass на индекс Tomcat, так как он загружает только текст без изображений. Если бы я сделал редирект, проблем бы не было, но идея состоит в том, чтобы клиент перестал использовать IP и наконец имел серьезный домен.

Конечно, я не только попробовал с индексом Tomcat, до этого я пробовал начать напрямую с webapp1 и обнаружил, что ошибки не было… но он ничего не показывал. Увидев это, я пошел посмотреть на индекс и понял, что он тоже не загружался должным образом.

Сначала я думал, что проблему можно решить, создав виртуальный хост в Tomcat для каждого webapp, потому что я считал, что проблема была грубой и сделал прямой proxy_pass на http://myServerIp1:8080/webapp1/, но после настройки виртуального хоста Tomcat результат был тот же – полностью белая страница.

Мне нужна помощь, и я думаю, что лучший способ начать – это начать с самого начала. Давайте посмотрим, сможете ли вы помочь мне правильно загрузить индекс Tomcat.

Для этого я оставляю конфигурацию Tomcat на данный момент без виртуальных хостов, и это будет конфигурация nginx для индекса Tomcat на http://partner1.myDomain.com/ (позже, если это будет загружено хорошо, я начну тестировать с приложениями)

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    index index.html index.htm index.nginx-debian.html;
    server_name partner1.myDomain.com;

    location / {
        proxy_pass  http://myServerIp1:8080/;
        proxy_set_header Host               $host;
        proxy_set_header X-Real-IP          $remote_addr;
        proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
        try_files $uri $uri/ =404;
    }
}

Как вы видите, это небольшая модификация файла по умолчанию nginx в Debian.
Если я проверю инструменты разработки браузера, я могу увидеть, что у него есть проблемы с загрузкой элементов, таких как “tomcat.css, favicon.ico…”.

У вас ситуация, аналогичная этому вопросу. Tomcat не знает, что он используется через прокси, и сгенерированные абсолютные URL начинаются с http://myServerIp:8080.

Вам нужно добавить RemoteIpValve в <Host>:

<Valve className="org.apache.catalina.valves.RemoteIpValve"
       protocolHeader="X-Forwarded-Proto" />

и установите requestAttributesEnabled=true в вашем AccessLogValve (чтобы логи содержали реальные IP-адреса).

NGINX нужно будет отправлять на Tomcat заголовки Host, X-Forwarded-For и X-Forwarded-Proto:

proxy_set_header Host $server_name;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

(заголовок X-Real-IP уже содержится в X-Forwarded-For).

После изменения конфигурации приложения, работающие на Tomcat, будут вести себя так, как если бы Tomcat работал на partner1.example.com:80. Более того, когда вы переключитесь на HTTPS, Tomcat поймет, что запрос поступил с защищенного канала, и будет генерировать ссылки вида https://.

Редактировать: В вашей обновленной конфигурации вы изменяете абсолютный путь проксированных страниц с /myWebapp/ на /. Я советую этого не делать: Tomcat устанавливает путь cookie для JSESSIONID в путь контекста /myWebapp/, поэтому веб-браузер никогда не отправляет cookie обратно. Используйте вместо этого такую конфигурацию:

    location = / {
        return 301 http://partner1.example.com/myWebapp/;
    }

    location / {
        proxy_pass http://myServerIp:8080;
        include    nginxconfig.io/proxy.conf;
    }

которая перенаправляет клиентов с / на /myWebapp/, но в остальном сохраняет пути нетронутыми.

Примечание: myDomain.com – это действительный домен, принадлежащий кому-то, поэтому лучше использовать домены, зарезервированные для документации в вопросах и ответах.

Мне наконец удалось достичь этого, не касаясь Tomcat, фактически я смог увидеть приложение таким образом:
http://myServerIp.8080/myWebapp/ –>
http://partner1.myDomain.com/

Но затем я столкнулся с другой проблемой.
Сразу после запуска приложения jsessionid не сохраняется и обновляется при каждом запросе. Я пробовал решения, такие как:
Добавить samesite в cookies, используя nginx в качестве реверс-прокси
Использование nginx в качестве реверс-прокси для tomcat приводит к новым jsessionid для каждого запроса SSL
https://stackoverflow.com/questions/53542525/nginx-how-to-take-value-from-header-and-set-it-into-cookie
и теперь я попробовал модифицировать Tomcat, как вы рекомендуете. Однако Jsessionid продолжает изменяться, мне удается войти в приложение, но когда я делаю этот запрос, он снова изменяется, и когда я вхожу в приложение, все рушится и меня отображает как неавторизованного.

Это будет моя конфигурация

---------------
-----nginx.conf
---------------
user                 www-data;
pid                  /run/nginx.pid;
worker_processes     auto;
worker_rlimit_nofile 65535;

events {
    multi_accept       on;
    worker_connections 65535;
}

http {
    charset              utf-8;
    sendfile             on;
    tcp_nopush           on;
    tcp_nodelay          on;
    server_tokens        off;
    log_not_found        off;
    types_hash_max_size  2048;
    client_max_body_size 16M;

    # MIME
    include              mime.types;
    default_type         application/octet-stream;

    # Logging
    access_log           /var/log/nginx/access.log;
    error_log            /var/log/nginx/error.log warn;

    # Load configs
    include              /etc/nginx/conf.d/*.conf;
    include              /etc/nginx/sites-enabled/*;
}

---------------
-----General
---------------


# favicon.ico
location = /favicon.ico {
    log_not_found off;
    access_log    off;
}

# robots.txt
location = /robots.txt {
    log_not_found off;
    access_log    off;
}

# gzip
gzip            on;
gzip_vary       on;
gzip_proxied    any;
gzip_comp_level 6;
gzip_types      text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;

---------------
-----Proxy
---------------


proxy_http_version                 1.1;
proxy_cache_bypass                 $http_upgrade;

# Proxy headers
proxy_set_header Upgrade           $http_upgrade;
proxy_set_header Connection        "upgrade";
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 X-Forwarded-Host  $host;
proxy_set_header X-Forwarded-Port  $server_port;

# Proxy timeouts
proxy_connect_timeout              60s;
proxy_send_timeout                 60s;
proxy_read_timeout                 60s;


---------------
-----webapp
---------------


server {
    listen      80;
    listen      [::]:80;
    server_name partner1.myDomain.com;

    # logging
    access_log  /var/log/nginx/partner1.myDomain.com.access.log;
    error_log   /var/log/nginx/partner1.myDomain.com.error.log warn;

    # reverse proxy
    location / {
        proxy_pass http://myServerIp.8080/myWebapp/;
        include    nginxconfig.io/proxy.conf;
    }

    # additional config
    include nginxconfig.io/general.conf;
}

Ответ или решение

Конфигурация nginx в качестве обратного прокси для серверов Tomcat – это задача, которая требует стратегического подхода из-за особенностей работы обоих серверов. Рассмотрим, как можно выполнить эту задачу, учитывая описанную структуру серверов и приложений.

Теория

  1. Обратный прокси: nginx выступает в роли посредника между пользователями и Tomcat-серверами, перенаправляя запросы к соответствующим приложениям на Tomcat. Такая архитектура обеспечивает дополнительную безопасность и позволяет использовать дружественные для пользователя доменные имена вместо IP-адресов.

  2. Проблемы с абсолютными URL и кэшированием: Когда Tomcat работает за обратным прокси, он может не осознавать, что конечный пользователь использует другой домен или протокол, например, HTTPS. Это может вызвать проблемы с формированием URL, приводя к неправильной загрузке ресурсов. Для решения этой проблемы важно правильно передавать заголовки, такие как Host, X-Forwarded-For и X-Forwarded-Proto.

  3. Управление сессиями: Неправильная настройка прокси может привести к тому, что каждый запрос будет создаваться с новой сессией, как указано в случае с JSESSIONID. Это вызвано тем, что Tomcat может не сохранять куки сессий, если путь или домен установлен неправильно.

Пример

Давайте рассмотрим, как можно настроить nginx с учетом этих моментов.

  1. Настройка nginx:

    server {
       listen 80;
       server_name partner1.myDomain.com;
    
       location / {
           proxy_pass http://myServerIp1:8080/webapp1/;
           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 X-Forwarded-Host $host;
           proxy_set_header X-Forwarded-Port $server_port;
    
           # Proxy timeouts
           proxy_connect_timeout 60s;
           proxy_send_timeout 60s;
           proxy_read_timeout 60s;
       }
    }
  2. Конфигурация Tomcat:

    В файле server.xml добавьте RemoteIpValve для корректной обработки заголовков:

    <Valve className="org.apache.catalina.valves.RemoteIpValve"
          protocolHeader="X-Forwarded-Proto"
          remoteIpHeader="X-Forwarded-For"
          internalProxies="192\.168\.\d{1,3}\.\d{1,3}"
    />

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

Применение

  1. Настройте заголовки: Убедитесь, что nginx правильно пересылает необходимые заголовки. Это позволяет Tomcat корректно обрабатывать запросы как будто они идут непосредственно к нему.

  2. Проверка конфигурации: Убедитесь, что абсолютные URL, генерируемые вашим приложением, корректны и ведут к страницам без указания порта Tomcat. Это можно проверить в браузере, используя инструменты разработчика.

  3. Устойчивость и безопасность: После проверки работоспособности перейдите к конфигурированию SSL, что повысит безопасность соединений. Используйте сертификаты, чтобы настроить HTTPS для доменов, что укрепит доверие пользователей и повысит защиту данных.

Следуя вышеописанным шагам, вы сможете успешно настроить nginx как обратный прокси для ваших серверов Tomcat, обеспечив стабильную и безопасную работу приложений. Если после выполнения настроек остаются проблемы с сессиями, убедитесь, что пути и домены установлены правильно, и изучите возможность добавления заголовков для управления "cookie".

Оцените материал
Добавить комментарий

Капча загружается...