Я не админ, но иногда возникают задачи, которые проще (и интереснее) решать самому чем кому-то делегировать.

Изредка у нас появляется необходимость «поднять» servlet контейнер (чаще всего Apache Tomcat) и настроить для него проксирование, ssl termination (а проще говоря https) и все это прикрыть firewall'ом (оставив наружу только ssh и http/https).

Так получилось, что за последнюю неделю я эту задачу решал трижды (так стали звезды, а до этого — года два назад) и этот опыт трансформировался в сей небольшой опус.

Итак, дано Ubuntu сервер 18.04 или 16.04 (скорее всего у вас не будет проблем и с более ранними версиями 14.04 или около). Если у вас нет Ubuntu сервера, то можете его быстро «поднять», например, на Digital Ocean (ссылка моя реферальная). После написания статьи обратил внимание, что DO для новых account'ов раздает 100$ на 60 дней на попробовать, если Вы указываете кредиту.

DNS


Для простой схемы получения бесплатного https сертификата от Let's Encrypt нам понадобится доступ к DNS серверу. Прописываем в нем IP адрес нашего Ubuntu сервера с именем, скажем, xyz. Давайте, для определенности, предположим, что у вас есть домен mydomain.com, т.е. DNS имя нашего сервера будет xyz.mydomain.com

Установка


Устанавливаем Apache Tomcat (я буду использовать 8 версию)

apt install tomcat8

А теперь Nginx

apt install nginx-core

Настройка


Nginx


Настраиваем Nginx'у прописанный ранее в DNS server name (файл /etc/nginx/sites-available/default)

server_name xyz.mydomain.com; 

Прописываем ссылку на установленный Apache Tomcat (если Вы ничего не меняли, то он «живет» на порту 8080). Нам нужно добавить блок upstream до блока server.

upstream tomcat {
  server 127.0.0.1:8080 fail_timeout=0;
}
server {
...

Вносим изменения в блок location и перенаправляем весь трафик на Apache Tomcat

server {
...
        location / {
#               try_files $uri $uri/ =404;
                include proxy_params;
                proxy_pass http://tomcat/;
        }

Проверяем, что все внесли корректно

service nginx configtest

и рестартуем nginx

service nginx restart

Apache Tomcat


В принципе эта часть опциональная и если Вам не важно чтобы на tomcat «приходили» реальные ip адреса, порты, схема по которой идет запрос (я про https) и запрашиваемый сервер, то этот шаг можно опустить. Однако, в некоторых случаях этот шаг обязателен (например, для технологии Java Web Start aka JNLP).

В файл /etc/tomcat8/server.xml в блок <Host/> добавляем.

<Host>
...
      <Valve className="org.apache.catalina.valves.RemoteIpValve"
        remoteIpHeader="x-forwarded-for"
        remoteIpProxiesHeader="x-forwarded-by"
        protocolHeader="x-forwarded-proto"
        />
</Host>

Перезапускаем tomcat

service tomcat8 restart

HTTPS сертификат


HTTPS сертификат с верификацией через http поможет получить нам бот certbot, а точнее его модификация для nginx'a — python-certbot-nginx

На Ubuntu 18.04 для установки certbot'a достаточно выполнить

apt install python-certbot-nginx

Для Ubuntu 16.04 — придется повозиться с добавлением репозиториев и пр. (см. подробное руководство по ссылке).

Запускаем

certbot --nginx 

В процессе указываем свой email, принимаем лицензионное соглашение, не разрешаем «шарить» свои данные с Let's Encrypt, подтверждаем DNS имя, на которое будет выписан сертификат, соглашаемся на то, чтобы бот сам «отконфигурил» nginx.

Вуаля :)

На всякий случай проверяем, что возобновление сертификата пройдет без проблем (сертификат выдается на 90 дней и после этого может бесконечно продлеваться на тот же срок).

certbot renew --dry-run

И для внутренней паранои проверяем, что cron файл на месте

ls -al /etc/cron.d/certbot

Firewall


Останавливаем и делаем backup (snapshot) виртуалки.

ufw allow ssh
ufw allow http
ufw allow https
ufw default allow outgoing
ufw default deny incoming
ufw show added

Молимся!
ufw enable
ufw status

Проверяем, что все получилось — сайт доступен по https, http трафик перенаправляется и порты, кроме ранее перечисленных, и ssh надежно закрыты.

P.S. Я искренне надеюсь, что сей текст может быть кому-то полезен и буду рад конструктивной критике.

P.P.S. А может всезнающий ALL мне подскажет замену certbot'a для Windows? Нужно чтобы получил начальный сертификат (в идеале, обновлял его по расписанию, ни и вообще шик-блеск) сам конфигурировал nginx. Да, я понимаю, что можно, наверняка взять тулзу для let's encrypta + IIS и использовать ее в моем сценарии, но вдруг есть уже готовый «идеал»?

Комментарии (27)


  1. BOPOHA
    22.12.2018 21:23
    -2

    Итак, что бы проксировать http(s) в localhost:8080 вы выбрали такой путь:
    Nginx, возможно даже из сторонних репозиториев, конфиги, установка sertbot и какие-то там подтверждения соглашений, ручные правки, кроны…
    Предлагаю заменить этот паровоз на два элемента systemd+traefik. Без каких-либо ручных правок файлов.
    Алгоритм автоматизации:


    • скопировать юнит-файл
    • скопировать бинарник traefik
    • cкопировать конфиг traefik
    • установка вашего или любого другого сервиса на local host:8080 ( кстати, а почему не на сокете?)
    • настройка fw
    • reboot


    1. FoxyBOA Автор
      22.12.2018 21:25

      Попробую. Спасибо!


      1. FoxyBOA Автор
        23.12.2018 12:08

        По поводу сокета. Так вроде бы не умеет в них Apache Tomcat (или я не умею его готовить). Jetty умеет, но по условиям задачи нужен Tomcat.

        Tomcat умеет в Ajp и есть связка Apache + tomcat по AJP (и в этом варианте, насколько мне помнится) даже не нужно ничего в томкате кофигурить — Apache Web Server сам корретно ip, порты и пр. прокидывает.

        Наверняка можно и nginx + tomcat ajp настроить.


        1. gecube
          23.12.2018 12:09

          На самом деле сокет vs порт — наверное, меньшая из проблем. Тем более, если планируются docker'ы или разносить бекенды на разные ноды (сокет только в пределах одной ноды (сервера) работает).


    1. gecube
      22.12.2018 22:12

      +1. Я тоже топлю за traefik.
      Он прекрасно работает с докерами, динамические конфиги и всякая радость.
      Немаловажно, что traefik дает производительность сравнимую с nginx, а пользоваться им намного удобнее.
      Также traefik прозрачно интегруется с LE и не нужно тащить всякую дрянь в систему.

      Еще я бы обязательно обратил внимание автора статьи на такой чудесный прокси, как envoy.


      1. FoxyBOA Автор
        23.12.2018 08:48

        Спасибо, гляну на оба


  1. gecube
    22.12.2018 22:13

    Автору.
    Ну, что опять за дет сад.
    Я полностью согласен с комментарием пользователя BOPOHA
    Эти действия можно сделать один раз. А дальше уже нужна автоматизация (покажите мне свои манифесты/роли и я скажу, кто Вы!!!). Я на третий раз уже всегда пишу ansible/salt роль/манифест/state, ну, либо на край — башник.
    К тому же, выкинуты за борт пользователи альтернативных дистрибутивов. Ну, там centos и прочее.


  1. moiseir
    23.12.2018 03:33

    https://hub.docker.com/r/marvambass/nginx-ssl-secure


    docker run -d -p 80:80 -p 443:443 -e 'DH_SIZE=512' -v $EXT_DIR:/etc/nginx/external/marvambass/nginx-ssl-secure

    Или еще 100500 альтернатив гуглятся.


    1. FoxyBOA Автор
      23.12.2018 08:27
      +1

      Спасибо, посмотрел. Не до конца понял, как этот docker image мне может помочь. Моя цель поднять reverse proxy с бесплатным (не самоподписным!) https сертификатом с минимальными усилиями. Кажется, для этого container'а уже нужно иметь уже готовые https сертификаты и он решает вопрос настройки nginx + ssl. Я понимаю, что если покопаться, то, наверное, найдется и такое решение (docker image for nginx + ssl + let's enctypt), но по моим внутренним ощущениям docker в этой задаче overkill. Т.е. нужна какая-то простая штука которую запускаешь и забываешь на годы (или пока Java не съест всю память :). А тут еще мы добавляем промежуточный слой docker'a с которым тоже могут быть свои особенности (от пробрасывания логов до изменений в сам образ и пр.).

      И еще раз спасибо за комментарий.


      1. Silvarum
        23.12.2018 12:00

        Есть как раз такой контейнер: hub.docker.com/r/linuxserver/letsencrypt. Один раз настроил и забыл.


      1. gecube
        23.12.2018 12:03
        +1

        Я согласен, что в этом случае docker тащить — это overkill.
        А потом придется еще бороться с особенностями docker (совместимость версий, настройка firewall, настройка драйверов storage etc).
        С другой стороны, если в инфраструктуре докер уже есть, то почему бы им не воспользоваться? К тому же, docker реально позволяет избежать «замусоривания» основной системы лишними пакетами, конфигами etc.


        1. moiseir
          23.12.2018 13:36

          Запускать ВМ для каждого сервлета и инсталлировать все окружение на нем, имхо, еще больший оверкил. Контейнер, наверняка, уже готовый можно найти, вверху, в другом комменте есть ссылка, например.


          (совместимость версий, настройка firewall, настройка драйверов storage etc).

          Все не актуально в данном конкретном случае.


  1. superyateam
    23.12.2018 09:32

    А можете написать то же самое про tomcat 9? Его нет в apt-get, а ручная настройка мне не далась вообще нивкакую — systemd скрипты которые скопипащены по всем туториалам в интернете просто не работают :(
    Спасибо!


    1. FoxyBOA Автор
      23.12.2018 11:14

      Нет.

      Точнее так. Судя по данным с сайта Ubuntu пакет tomcat9 появится только в версии disco AKA 19.04 А чем плох вариант скачать zip/gzip и установить 9 или версию 8.5 (ее к слову тоже нет в пакетах). Шаги по томкату брать отсюда, например.
      Все прочее кроме расположение tomcat файлов (в том числе и config) у вас останется без изменения (т.е. чуть больше будет прыжков по автозапуску и пр. и прописывать org.apache.catalina.valves.RemoteIpValve нужно будет в файле conf/server.xml и все, как кажется).


      1. superyateam
        24.12.2018 08:47

        Самое интересное, что я этому туториалу и следовал, но застрял на настройке systemctl.


        https://serverfault.com/questions/946034/systemctl-stops-tomcat-service-immediately-after-start
        Вот здесь я подробно изложил проблему


        1. FoxyBOA Автор
          24.12.2018 14:26

          Сделал все по инструкции. Полетело (digital ocean). По поводу порта 8005 — попробуйте его изменить в файле server.xml на любой другой свободный.

          <Server port="8005" shutdown="SHUTDOWN">

          Он, скорее всего, уже чем-то занят на это виртуалке. lsof Вам в помощь
          > lsof -i :8005
          COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
          java    3433 tomcat   86u  IPv6  32276      0t0  TCP localhost:8005 (LISTEN)


          1. superyateam
            24.12.2018 15:50
            +1

            Блин, мне значит не везет. Пробовал в точности следовать туториалу прямо на Digital Ocean — не работает.

            Что по поводу 8005 порта — то это была моя первоначальная догадка. Потом я все-таки выяснил, что причина в другом. Systemctl по какой-то причине останавливает сервис сразу же после того, как стартует его.

            В итоге, я все-таки сделал себе Tomcat8 через apt-get — требование о 9м томкате было некритичным. Обидно просто было, что так и не выяснил в чем причина ошибки.

            p.s. Большое спасибо за ваше время


            1. FoxyBOA Автор
              24.12.2018 15:56

              Если еще есть виртуалка с tomcat 9 — посмотрите в логи томката (каталог logs). Если сервис стартует и останавливается, значит между этими событиями происходит какое-то событие (например, неправильный путь к JVM, порт занят и пр.).

              Я бы на Вашем месте очистил каталог logs (сделал его пустым) и перезапустил tomcat9 сервис и посмотрел в логи.


              1. superyateam
                24.12.2018 16:12

                логи всегда пустые — все файлы 0 bytes


            1. FoxyBOA Автор
              24.12.2018 16:58

              Если логи пустые, то томкат даже не смог стартануть (обычно сообщения о занятых портах пишутся сильно позже). Те это или права или путь к jvm (хотя я в tutorial’e налажал и не изменил JAVA_HOME, так эти сообщения в логах были).


              1. superyateam
                24.12.2018 18:40

                Значит, права. Спасибо! Буду копать.


            1. gecube
              24.12.2018 17:44

              проблема скорее всего в окружении. Тот же пользователь, права, настройки systemd-юнита etc. Разобраться можно.


  1. keydon2
    23.12.2018 10:32
    +1

    Если это не тестовое окружение на одни сутки, то лучше озаботится наличием fail2ban или ограничением на файрволе доступа к ssh по белому списку адресов.


    1. FoxyBOA Автор
      23.12.2018 11:16

      Здравая идея. А есть еще port knocking :)


    1. gecube
      23.12.2018 12:08
      +1

      повторюсь, что fail2ban — не лучшее решение.
      Во-первых, как любой опенсурс — он не безгрешен. И может подвести. Ну, например, github.com/fail2ban/fail2ban/issues/798 Это все очень version-related.
      Но, во-вторых, кмк, лучше защиты выносить на другой уровень — инфраструктуры (зоны безопасности в облачном провайдере, например), так и приложения (basic auth etc.).
      Кстати, отдельный вопрос как будет дружить fail2ban c docker, учитывая, что оба переписывают правила файрволла.
      За кадром еще два вопроса — вопрос идентификации клиентов за NAT'ом (для них f2b бесполезен, т.к. он не видит оригинальный IP) и еще вопрос нагрузки (т.к. каждое правило файрволла — снижение быстродействия, причем когда правил много, то значительное)


  1. Zerthimon
    23.12.2018 11:35

    Ну и причём тут devops? Это работа обычно админа.


  1. nikitasius
    24.12.2018 17:58
    -1

    1) как запустить tomcat
    2) как сделать сертификат в letsencrypt
    3) основы proxypass (без кеша)
    Все можно нагуглить элементарными запросами.
    Если честно статья ни о чем. Кеш не рассмотрен. Минусы tomcat не показаны. Конфиг томката, дочки — ничего не рассмотрено.


    Теперь — "по условиям задачи нужен Tomcat" — orly?
    Задача поставлена самостоятельно, потому, что не смогли приготовить jetty или потому, что Jetty просто напросто отшила говнокод в отличие от tomcat, который жрет всё?


    Ейбогу, вместо того, чтобы треш писать, лучше его удалить и потратить лишние 3 часа времени, но написать нормальный материал. Заодно и самому разобраться.