Тем, кому надо обеспечить себе, любимому, доступ к своим серверам из любой точки мира по SSH/RDP/иное — небольшое RTFM/шпора.


Нам нужно обойтись без VPN и других наворотов, с любого устройства под руками.


И так, чтобы с сервером не слишком упражняться.


Всё, что для этого нужно — knockd, прямые руки и 5 минут работы.


"В интернете всё есть", конечно (даже на Хабре), но когда дело доходит до конкретной реализации — тут и начинается...


Будем упражняться на примере Fedora/CentOS, но это неважно.


Шпора подойдет как новичкам, так и зубрам этого дела, поэтому будут комментарии, но покороче.


1. Сервер


  • ставим knock-server:
    yum/dnf install knock-server


  • настраиваем его (например на ssh) — /etc/knockd.conf:


    [options]
        UseSyslog
        interface = enp1s0f0
    [SSHopen]
        sequence        = 33333,22222,11111
        seq_timeout     = 5
        tcpflags        = syn
        start_command   = iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
        cmd_timeout     = 3600
        stop_command    = iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
    [SSHclose]
        sequence        = 11111,22222,33333
        seq_timeout     = 5
        tcpflags        = syn
        command         = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

    "Открывающая" часть настроена на автозакрытие через 1 час. Мало ли...


  • /etc/sysconfig/iptables:


    ...
    -A INPUT -p tcp -m state --state NEW -m tcp --dport 11111 -j ACCEPT
    -A INPUT -p tcp -m state --state NEW -m tcp --dport 22222 -j ACCEPT
    -A INPUT -p tcp -m state --state NEW -m tcp --dport 33333 -j ACCEPT
    ...

  • вперед:


    service iptables restart
    service knockd start

  • можно добавить RDP на крутящийся внутри виртуальный Windows Server (/etc/knockd.conf; название интерфейса подставить по вкусу):


    [RDPopen]
        sequence        = 44444,33333,22222
        seq_timeout     = 5
        tcpflags        = syn
        start_command   = iptables -t nat -A PREROUTING -s %IP% -i enp1s0f0 -p tcp -m tcp --dport 3389 -j DNAT --to-destination 192.168.0.2
        cmd_timeout     = 3600
        stop_command    = iptables -t nat -D PREROUTING -s %IP% -i enp1s0f0 -p tcp -m tcp --dport 3389 -j DNAT --to-destination 192.168.0.2
    [RDPclose]
        sequence        = 22222,33333,44444
        seq_timeout     = 5
        tcpflags        = syn
        command         = iptables -t nat -D PREROUTING -s %IP% -i enp1s0f0 -p tcp -m tcp --dport 3389 -j DNAT --to-destination 192.168.0.2

    Все наши пинки от клиента отслеживаем на сервере командой iptables -S.



2. Путеводитель по граблям


knockd.conf:


В манах тоже всё есть (но это неточно), однако knockd — товарищ довольно скупой на сообщения, поэтому надо быть очень внимательным.


  • версия
    В репозитариях Fedora/CentOS крайний knockd на сегодня — 0.63. Кто хочет UDP — ищите пакеты 0.70.
  • interface
    В дефолтной конфигурации Fedora/CentOS эта строка отсутствует. Добавить руками, иначе не будет работать.
  • timeout
    Здесь подобрать по вкусу. Надо чтобы и клиенту времени хватило на все пинки — и бот-сканер портов обломался (а сканировать будут 146%).
  • start/stop/command.
    Если команда одна — то command, если две — то start_command+stop_command.
    Если ошибетесь — knockd промолчит, но работать не будет.
  • proto
    Теоретически можно использовать UDP. На практике я было смешал tcp и udp, а клиент с пляжа в Бали смог открыть себе калитку только с пятого раза. Ибо TCP долетели когда надо, а UDP — не факт. Но это дело вкуса, опять же.
  • sequence
    Неявные грабли в том, что последовательности не должны пересекаться… как бы это сказать...

Например, такое:


open: 11111,22222,33333
close: 22222,11111,33333

По пинку 11111 open будет ждать следующего пинка на 22222. Однако по этому (22222) пинку начнет работать close и всё поломается. Это зависит от delay клиента в том числе. Такие дела ©.


iptables


Если в /etc/sysconfig/iptables вот это вот:


*nat
:PREROUTING ACCEPT [0:0]

нам особо не мешает, то вот это вот:


*filter
:INPUT ACCEPT [0:0]
...
-A INPUT -j REJECT --reject-with icmp-host-prohibited

Таки мешает.


Так как knockd добавляет правила в конец цепочки INPUT, то мы получим reject.


А отключить этот reject — это открыть машину всем ветрам.


Дабы не изгаляться в iptables что куда перед чем вставлять (как вот люди предлагают) сделаем проще:


  • дефолтное в CentOS/Fedora первое правило ("что не запрещено — разрешено") заменим на обратное,
  • а последнее правило убираем.

В итоге должно получиться:


*filter
:INPUT DROP [0:0]
...
#-A INPUT -j REJECT --reject-with icmp-host-prohibited

Можно, конечно, вместо DROP сделать REJECT, но с DROP ботам будет жить веселее.


3. Клиент


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


В принципе ряд клиентов перечислены на сайте проекта, но это из той же серии "в интернете всё есть". Поэтому перечислю то, что здесь и сейчас работает у меня под руками.


При выборе клиента необходимо проследить, чтобы он поддерживал опцию delay между пакетами. Да, пляж пляжу рознь и 100 мегабит ни разу не гарантируют долетание пакетов в нужном порядке в нужное время из данного места.


И да — при настройке клиента delay надо подбирать самостоятельно. Много timeout — боты нападут, мало — клиент не успеет. Много delay — клиент не успеет или будет конфликт простуков (см. "грабли"), мало — пакеты перезаблудятся в интернетах.


При timeout=5s вполне рабочий вариант delay=100..500ms


Windows


Как это ни смешно звучит, но нагуглить внятный knock-клиент под эту платформу довольно нетривиально. Такой, чтобы CLI, поддерживал delay, TCP — и без бантиков.


Как вариант можно попробовать это вот. Видимо у меня гугль не торт.


Linux


Здесь всё просто:


dnf install knock -y
knock -d <delay> <dst_ip> 11111 22222 33333

MacOS


Проще всего поставить порт из homebrew:
brew install knock
и нарисовать себе нужные батники командники вида:


#!bin/sh
knock -d <delay> <dst_ip> 11111 22222 33333

iOS


Рабочий вариант — KnockOnD (бесплатный, из магазина).


Android


"Knock on Ports". Не реклама, а просто он работает. И разработчики достаточно отзывчивые.


P.S. markdown на Хабре, конечно, дай бог ему здоровья когда-нибудь...


UPD1: благодаря хорошему человеку нашелся рабочий клиент под Windows.
UPD2: еще один хороший человек напомнил, что ставить новые правила в конец iptables не всегда полезно. Но — it depends.

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


  1. barbos6
    03.10.2019 20:24
    +1

    Здесь подобрать по вкусу. Надо чтобы и клиенту времени хватило на все пинки — и бот-сканер портов обломался (а сканировать будут 146%).
    … Много timeout — боты нападут


    -A INPUT -i eth0 -p tcp -m tcp --dport 20:10000 -j TARPIT --tarpit
    sshd убрать с 22 порта, диапазон левых портов по вкусу, боты упарятся стучаться.

    2. Не упарятся, стучат по всем портам постоянно. Бот-нету из нескольких сотен тысяч хостов нетрудно в100500ром стукнут оптом по всем портам всеми протоколами.

    И кто ж так сможет синхронизировать предполагаемый ботнет, что 100500 тазов cтукнут одновременно во все порты одного и того же ip?
    Разве что специально, чтобы попалиться.
    В общем фантазии. :)


    1. justhabrauser Автор
      03.10.2019 20:43
      -1

      1. Это статья больше не для гуру iptables, а для «компьютерщиков широкого профиля» — кому нужно быстро, просто и достаточно надежно.
      2. Не упарятся, стучат по всем портам постоянно. Бот-нету из нескольких сотен тысяч хостов нетрудно в100500ром стукнут оптом по всем портам всеми протоколами.


    1. justhabrauser Автор
      03.10.2019 23:57

      Кстати, в ответ на Ваш ответ — выписка из протокола logwatch одного из серверов сегодня утром:

      **Unmatched Entries**
         knockd: 141.98.11.12: RDPopen: Stage 1: 1 Time(s)
         knockd: 141.98.11.12: RDPopen: sequence timeout (stage 1): 1 Time(s)
         knockd: 193.188.22.193: SSHclose: Stage 1: 1 Time(s)
         knockd: 193.188.22.193: SSHclose: sequence timeout (stage 1): 1 Time(s)
         knockd: 223.105.4.244: RDPopen: Stage 1: 1 Time(s)
         knockd: 223.105.4.244: RDPopen: sequence timeout (stage 1): 1 Time(s)
         knockd: 5.63.151.100: SSHclose: Stage 1: 1 Time(s)
         knockd: 5.63.151.100: SSHclose: sequence timeout (stage 1): 1 Time(s)
         knockd: 81.22.45.100: SSHclose: sequence timeout (stage 1): 1 Time(s)
         knockd: 81.22.45.184: RDPclose: Stage 1: 1 Time(s)
         knockd: 91.237.127.143: RDPclose: Stage 1: 1 Time(s)
         knockd: 91.237.127.143: RDPclose: sequence timeout (stage 1): 1 Time(s)
      

      Не спрашивайте меня кто их синхронизирует и что они забыли на портах вида 56789.


  1. KoPBuH
    03.10.2019 20:57

    Что под виндой, что под линем вполне хватает обычного telnet. Небольшой скрипт или bat со sleep'ами и готов дятел.


  1. Harbour
    04.10.2019 06:12

    CLI knock по венду:

    github.com/Sfinx/knock


    1. justhabrauser Автор
      04.10.2019 09:42

      Огромное спасибо, добавил.


  1. Shtucer
    04.10.2019 06:32

    Так как knockd добавляет правила в конец цепочки INPUT, то мы получим reject.

    А это точно кнокд тупой, а не вот это вот кто написал:


    start_command   = iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT

    вместо


    start_command   = iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT

    Или он ещё чего-то накидывает?


    1. justhabrauser Автор
      04.10.2019 09:19

      И то и то работает — knock'у всё равно что запускать.
      Просто во втором случае мне было лениво.
      Да и небезопасно — я могу в очередной раз переколпашить свой развесистый iptables и про нюансы для knockd забыть.


      1. Shtucer
        04.10.2019 09:57

        Забыть "нюансы кнока" (какие?) это "небезопасно", поэтому давайте откроем нахрен всё? Интересный подход.


        1. justhabrauser Автор
          04.10.2019 10:14

          Забыть перепилить (и проверить!) в knockd номер правила в iptables после пересобачивания этого iptables — легко. Или не забыть и перепилить — но поломать iptables.
          Поэтому давайте не откроем нахрен всё, а закроем нахрен всё (что и предложено в данной статье).


          1. Shtucer
            04.10.2019 11:02

            Забыть перепилить (и проверить!) в knockd номер правила в iptables

            Зачем запоминать номер правила в iptables?!


            1. justhabrauser Автор
              04.10.2019 11:27

              Нет, ну если согласиться с собой чтобы knockd ставил свои правила в начало, а не в конец — то да, тоже вариант, согласен.
              НО:
              * тот же knock может добавить в начало правило типа «все порты — прямо мне» — и в Вашем случае 22 может поломаться.
              * или же я временно сам поставил в начало подобное правило (бывает для полного контроля сервера издалека) — в Вашем случае это может перестать работать.
              * если же добавить в конец правило «всем остальным — в мусорку» (перенаправить ботов в honey point) — то мой вариант может не работать.
              It depends.
              Но за напоминание спасибо, добавил в конец статьи.


              1. Tangeman
                04.10.2019 14:35

                Логичнее создать отдельную ветку для knockd в iptables, и писать в неё. Как-то так:

                :KNOCK -
                -A INPUT -m state --state NEW -p tcp --dport 22 -j KNOCK

                А в самой конифигурации уже ссылаться на неё:

                start_command = iptables -A KNOCK -s %IP% -j ACCEPT

                Таким образом, творите в своих обычных правилах что хотите, а knockd будет в своей песочнице, и ничего другого при всём желании не испортит. Есть правило — ок, порт открыт, нет его — не открыт, и никакой мешанины.


  1. MaxRAF
    04.10.2019 09:59

    В CentOS по умолчанию firewalld, но все равно многие упорно его отключают и устанавливают iptables. В чем причина?


    1. justhabrauser Автор
      04.10.2019 10:09

      Сложность и непривычность firewalld.
      Сисадмины — народ довольно консервативный: «Работает — не трожЪ»!
      Поэтому эти ваши firewalld, systemd и прочие NetworkManager'ы сисадминам как серпом по я не знаю как проще выразить.
      PS. а всяким поттерингам в аду приготовлена отдельная серверная.


    1. sohmstyle
      04.10.2019 14:10

      1) Привычка работы с iptables т.к. не всё можно сделать с помощью firewalld без direct-rules.
      2) Многие приложения динамически добавляют правила iptables в runtime. И restart/reload firewalld стирает runtime-rules.

      Возможно есть ещё причины, но они не столь значительны.


  1. impalex
    04.10.2019 14:01

    На мой взгляд «автозакрытие» через час — перебор. Открытый порт нужен только на время установления соединения. Минуты — за глаза хватит, а то и несколькими секундами обойтись можно. Т.е. открыли, подключились, закрыли. Соединение остаётся активным. При этом «ручное» закрытие порта становится не особо нужным. Всё сказанное справедливо при наличии правила ACCEPT для state RELATED,ESTABLISHED.

    PS: Спасибо за то, что отметили «Knock on Ports», было приятно :) Это мой маленький проект, делается just for fun и больше для себя :)


    1. justhabrauser Автор
      04.10.2019 14:07

      Все циферки проставлены для примера. У меня обычно стоит таймаут 15 минут. Иногда интернет может быть ну очень печален. И обрывист.
      Но если справился быстро — то лучше закрыть побыстрее. На всякий случай.
      За «Knock on Port» — не за что, с вами приятно работать (это насчет issue о непроверке наличия интернетов). Еще бы сделали это дело перемещаемым на SD-карту (а не как поделия яндекса прибиты гвоздями к системному разделу) — и вообще отлично. Но это я попозже побаграпортую.


      1. impalex
        04.10.2019 14:41

        Еще бы сделали это дело перемещаемым на SD-карту

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


  1. MarvinD
    04.10.2019 16:26

    Хотел бы обратить внимание:

    дефолтное в CentOS/Fedora первое правило («что не запрещено — разрешено») заменим на обратное, а последнее правило убираем.

    Имхо, практически всегда при настройке нового сервера надо ставить INPUT DROP для всего, и только по мере необходимости разрешать что-то. Т.е. в нормальной ситуации должно быть «что не разрешено — то запрещено».


    1. justhabrauser Автор
      04.10.2019 18:14

      Это статья не об iptables, но раз уж так зашло…
      Пару раз было накосячил в iptables и если бы не дефолтное «всё всем» — видел бы я свой сервер (в датацентре в другой стране).
      Так что где-то дефолтное правило имеет смысл.


      1. MarvinD
        04.10.2019 18:38

        Да, статья не про это, но мне глаз резануло то, что в статье подразумевается, что по дефолту все можно, хотя даже домашние роутеры на input wan ничего не разрешают, пока явно не разрешить, а в статье речь так и вообще про сервер.


        Заодно вопрос — после того, как клиент knock отработал, порт открылся, дальнейшая работа возможно либо до истечения таймаута knock, либо до разрыва stateful сессии на iptables сервера? Там никаких служебных пингов, скажем, раз в 5 сек, клиент не отправляет для поддержания сессии?


        1. justhabrauser Автор
          04.10.2019 19:11

          Knockd — это не чудо и не магия.
          Он просто выполняет команду по приходу определенных последовательностей на прослушиваемые порты.
          Любую команду. И только её.
          Всё.


          1. MarvinD
            05.10.2019 00:27

            Поэтому я и спросил про клиента ;) а не про серверную часть.


            1. justhabrauser Автор
              05.10.2019 12:52

              А клиенту что? Пнул — и на выход.
              Кстати, клиентом могут быть обыкновенные сетевые утилиты.
              Вроде бы в Linux и MacOS какие-то «искаропки».