Приветствую коллеги! В данной статье  хочется описать свою доработку популярной технологии защиты сетевых рубежей под названием Port Knocking, реализованную на оборудовании MIKROTIK. Технология древняя, неоднократно описана и разжевана. Используется повсеместно и довольно эффективна. По этому в статье я не стану подробно объяснять что это и для чего нужно. Предполагается что читатель в теме. Если не в теме но есть интерес, рекомендую вначале познакомиться с технологией в других публикациях, коих огромное количество.


Для чего статья:

Hidden text

Надеюсь что данная реализация кому то да будет полезной, поскольку у меня она зарекомендовала себя хорошо, а у других я чего то подобного не встречал. Идея родилась в самый разгар Корона дампа, когда на “удаленку” отправили 90% (а где то и больше) персонала. Конечно технологией удаленного доступа к локальным ресурсам компании у нас пользовались и до этого. И Port Knocking, был настроен давно. И работал по классике. Была настроена цепочка правил где микротик принимал пакеты определенного размера по протоколу ICMP (обычный пинг), если пакеты идут в правильной последовательности и правильного размера, для IP источника открывается удаленный доступ к нужному ресурсу. Для этого у меня (для WINDOWS клиентов) было написано несколько БАТ файлов. От самых простых с зацикленным пингом нужного ресурса, до довольно изощренных. В основном для VPN клиентов. Где единожды закинув клиенту на флэшку некий дистрибутив из БАТ файлов и кое каких других, процесс установки VPN соединения полностью автоматизировался. Даже если клиент до этого не пользовался ВПНом совсем. Помимо этого в зависимости от задачи через некое подобие меню можно подключать сетевые диски, устанавливать RDP соединения и выполнять ряд других действий. Этот способ является основным и по сей день. Но только для WINDOWS клиентов, поскольку в других OS я к сожалению не силен. По окончании работы батника все следы его работы и установленных соединений удаляются почти бесследно. Способ для меня крайне хорош и удобен. Но постепенно стало выяснятся что не все сидят под ВИНДОЙ (самая частая альтернатива, оказалась MAC OS) . А у кого-то и флэшку вставить некуда, или политиками разрешен запуск только определенного софта. Короче нюансов хватало.

            В результате этой вакханалии родилась идея некого кроссплатформенного решения. Простого и не замысловатого. Работающего как с флэшки так и с общедоступного ресурса. И первое что пришло на ум, старый добрый HTML + JavaScript . JS был выбран как раз по тому что способен исполнятся на стороне клиента (наверно любого). Оговорюсь, скрипт писал фрилансер, по-моему ТЗ. Возможно, что код не идеален. Поэтому прошу воздержаться от критики. Если вы специалист и можете помочь допилить код, буду рад. Если, по-вашему, прям чего-то очень не хватает, то рынок фриланса к вашим услугам. Я вам вряд ли помогу.

     Что нам нужно?

  1. На МИКРОТИКЕ настраиваем
    Port Knocking. Но не классический ICMP (пинг с заданным размером пакета), а по
    обращению к портам в определенной последовательности по протоколу TCP. В примере мы
    задействуем 4 порта. Порты лучше брать выше 1024 и до 65535 (следите что бы
    порты были свободны на вашем микротике и не использавались где либо еще.
    (Подробнее о портах). В случае если порядок соблюден верно, для адреса источника становятся доступен пинг хоста назначения (у себя вы можете изменить конечный результат по своему усмотрению). В качестве примера, ниже я размещу ссылку на работающую форму и укажу адрес хоста и код для проверки работоспособности. Подтверждением работы будет возможность пинга удаленного хоста который до выполнения скрипта будет невозможным. Может быть, когда вы будете читать статью форма уже будет недоступна. Но исходники кода я приложу, так что возможность проверки сохранится в любом случае, но уже на ваших ресурсах.

  2. Код правил “firewall filter” для микротика:

Hidden text
/ip firewall filter
add action=accept chain=input comment=ICMP in-interface=[имя внешнего интерфейса. Обычно ether1] protocol=icmp src-address-list=ICMP
add action=jump chain=input connection-state=new dst-address=[ip адрес внешнего интерфейса (по желанию)] dst-port=1025,1088,1051,1549 in-interface=[имя внешнего интерфейса. Обычно ether1] jump-target=Kn-Tcp protocol=tcp
add action=add-src-to-address-list address-list=Gate001 address-list-timeout=2s chain=Kn-Tcp connection-state=new dst-port=1025 protocol=tcp
add action=add-src-to-address-list address-list=Gate002 address-list-timeout=2s chain=Kn-Tcp connection-state=new dst-port=1088 protocol=tcp src-address-list=Gate001
add action=add-src-to-address-list address-list=Gate003 address-list-timeout= 2s chain=Kn-Tcp connection-state=new dst-port=1051 protocol=tcp src-address-list=Gate002
add action=add-src-to-address-list address-list=ICMP address-list-timeout=1h chain=Kn-Tcp connection-state=new dst-port=1549 protocol=tcp src-address-list=Gate003
add action=return chain=Kn-Tcp connection-state=new
/

В данном примере:

Hidden text

мы использовали порты: 1025, 1088, 1051 и 1549. При поступлении пакета на каждый из этих портов он попадет в созданную цепочку “Kn-Tcp” (в принципе конструкцию с правилом “Jump” и “Return” можно не использовать, работать будет и без нее. В примере эти правила как дань классике. Если откажитесь от  них то в других правилах желательно указать IN interface и выбрать цепочку input) при верной последовательности поступления пакетов, создаются временные адрес листы, последний из которых сроком на 1 час. И именем ICMP. Из этого адрес листа получает IP адреса источников правило с комментарием “ICMP”. Если IP есть в листе, то для него разрешается пинг. Перед вставкой кода в консоль микротика поправьте его под ваше оборудование. В частности укажите IP и имя внешнего интерфейса. Править код рекомендую в нормальном редакторе с подсветкой синтаксиса, к примеру notepad++, который наконец начал понимать синтаксис ROS. Если будете использовать штатный notepad windows возможны ошибки при COPY PASTE.

3. Размещаем где либо HTML страницу с JavaScript. Это может быть как общедоступный WEB сервер. Так и локальная директория на компьютере или usb флэшке.

На январь 2023 года демо страница общедоступна по адресу https://kn.investdev.ru (напомню, что не гарантирую ее вечную работу по этому адресу. Ищите исходники в конце статьи)

4. Открываем страницу, прописываем хост и код (перечень портов разделенных “-”), жмем капу “отправить”. Если не поставили галку “повторять”, получаем доступ к ресурсу на 1 час с соответствующем уведомлением (страницу с уведомлением можно переопределить в файле “index.html” на любую другую). Если галку “повторять” поставили, скрипт будет регулярно повторяться в течении 8 часов.

Работу скрипта вы можете проверить на демо ресурсе:

Hidden text

(вечную работу которого так же не гарантирую), Хост: kn.papiruss.ru Код: 1025-1088-1051-1549

После нажатия кнопки отправить вы сможете пропинговать вышеуказанный хост. Пинг которого до этого был невозможен.

            Для особо параноидально настроенных товарищей, во избежании попыток  подбора нужной комбинации портов, могу предложить до оснастить конструкцию "сладким горшочком" (Honeypot). Гугл вам в помощь. Но я не думаю что это хоть сколько то целесообразно в вашем случае. Смаил


Коды HTML и JavaScript.

Создаем 2 пустых файла с именами index.html и theend.html, а так же поддиректорию js с файлом main.js

Файл main.js (нужно положить в
поддиректорию js):

Hidden text
'use strict';

const TIMEOUT = 1000 * 60 * 60 * 8;

function main() {
    const submitButton = document.getElementById('submit');
    const hostElement = document.getElementById('host');
    const codeElement = document.getElementById('code');
    const needIterateElement = document.getElementById('needIterate');

    submitButton.addEventListener('click', (e) => {
        e.preventDefault();
        const host = hostElement.value;
        const code = codeElement.value;
        const ports = code.split("-");

        let value = needIterateElement.checked;
        if (value) {
            makeRequests(host, ports);
            let timerId = setInterval(() => {
                makeRequests(host, ports);
            }, 10 * 60 * 1000);

            setTimeout(() => {
                clearInterval(timerId);
                window.location.replace("./theend.html");
            }, TIMEOUT);
        } else {
            makeRequests(host, ports);
            setTimeout(() => {
                window.location.replace("./theend.html");
            }, 1000);
        }
    });
}
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
function makeRequests(host, ports) {
    console.log(ports);
    for (let i = 0; i < ports.length; i++) {
        try {
            console.log(ports[i]);
            const newLocal = 'https://' + host + ":" + ports[i];
            console.log(newLocal);
            // fetch(newLocal).then().catch();
            var oReq = new XMLHttpRequest();

            oReq.onload = function (e) {
                var arraybuffer = oReq.response; // not responseText
                console.log(arraybuffer);
            }
            oReq.open("GET", newLocal);
            oReq.responseType = "arraybuffer";
            oReq.send();
        } catch (e) {
            console.log(e);
        }
    }
}

main();

Cтраница index.html:

Hidden text
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>KNOCK</title>
</head>
<body>
<form id="form" autocomplete="off">
    <label for="host">Хост<input autocomplete="off" id="host" type="text" required></label>
    <label for="code">Код<input autocomplete="off" id="code" type="password" required></label>
    <label for="needIterate">Повторять<input autocomplete="off" type="checkbox" id="needIterate" name="needIterate"></label>
    <button id="submit">Отправить</button>
</form>
<script src="js/main.js"></script>
</body>
</html>

Страница theend.html:

Hidden text
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>KNOCK THE END</title>
</head>
<body>
Работа скрипта окончена. Ваш сеанс будет активен еще 1 час.
</body>
</html>

В завершении статьи в качестве бонуса за то что дочитали подскажу один не очевидный для многих лайфхак. Если вдруг вы не знали. Микротик способен защищать не только те ресурсы которые находятся за ним. Но и те которые (как бы это сказать) по бокам.

            Всего двумя правилами в таблице FIREWALL/NAT вы можете перенаправлять запросы к любому общедоступному ресурсу от имени вашего микротика. Если еще не понимаете зачем это надо, вот пример. На VPS хостинге вы подняли свою виртуалку, скажем с RDP. И уже где то минуту спустя вы сталкиваетесь с тем что вас начинают нещадно брутить. И конечно есть уйма способов как защитить себя и свою VPS от этой напасти. Но я предложу один. У вас есть микротик на котором мы только что настроили Port Knocking. Пишем в NAT два правила (редиректа и маскарада), а в FIREWALL своей VPS разрешаем подключения к RDP только с определенных IP, среди которых указываем IP своего микртотика.  Клиенты прошедшие авторизацию через Port Knocking, в качестве адреса назначения указывают адрес и порт вашего микротика, а тот перенаправляет подключения на ваш RDP. Вуаля, без особых хлопот вы получили Port Knocking на своем RDP сервере периметр которого не защищен микротиком явно. Они вообще могут быть на разных континентах.

КОД для firewall NAT:

Hidden text
/ip firewall nat
add action=dst-nat chain=dstnat comment=REDIREKT_KUDA_TO dst-port=3389 protocol=tcp src-address-list=ICMP to-addresses=1.1.1.1
add action=masquerade chain=srcnat dst-address=1.1.1.1 dst-port=3389 protocol=tcp
/

В этом примере:

Hidden text

микротик принимает TCP пакеты на порт 3389 от источников из адрес листа ICMP и пересылат их хосту 1.1.1.1 на тот же порт, с маскарадом (таким образом хост получателя видит не ваш реальный IP, а IP микротика). Если правило маскарада исключить то пакеты так же будут пересылаться, но ответа вы не получите. Поскольку хост назначения будет отвечать вам на прямую, иначе говоря ответы на ваши запросы будут приходить от хоста 1.1.1.1, в то время когда ваша система будет ожидать их от хоста микротика. При желании это можно решить правкой маршрутов на сервере "получателе" или его аппаратном шлюзе (если таковой есть). Но проще просто использовать второе правило. При чем с точки зрения лучшей производительности и при наличии статического IP адреса на микротике посреднике, макард в экшене лучше заменить на “src-nat” с указанием ip адреса внешнего интерфейса микротика. Это правило всегда более предпочтительно вместо маскарада, но при условии что на микротике имеется белая статика. Еще одним бонусом такого способа подключения будет то что ваши клиенты (если они не через чур любознательны и технически грамотны) не будут знать реального адреса сервера RDP. Получается как в офисной АТС, связь через доп номера. На RDP сервере фаервол вообще можно не трогать. Полностью оградив его от атак из внешней среды (атаки из локальной подсети при этом остаются возможными), тем что бы не указывать в настройках сети, адрес шлюза. Добавив в таблицу маршрутизации один только статистический маршрут к вашему микротику. При этом на сервере перестанет работать интернет, но и это можно решить различными способами, о которых я возможно расскажу в других статьях.

На этом считаю что статью можно закончить. Глубоко признателен всем кто дочитал до конца!

            С уважением, всем  Хабра )))

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


  1. turone
    03.01.2023 21:13
    +1

    плюсую за идею редиректа.
    идея авторизовать через страничку тоже неплоха.
    я чтобы не мучаться с батниками просто html файл сделал, а в нем пару тегов img c src c нужным портом. Конечно при веб авторизации вообще можно скрыть порты кнокинга и логировать, а потом и кому либо отключать доступ, либо переназначать на другие ресурсы - то есть есть имеем контроль и управление - это отлично! спасибо за статью! возможно моя статья сможет дополнить вашу Удаленная работа в офисе. RDP, Port Knocking, Mikrotik: просто и безопасно


    1. PAPIruss Автор
      04.01.2023 00:55
      +1

      Спасибо за поддержку. В вашем случае вместо веб странички наверно возможно так же использовать утилиту "CURL". Я с ней экспериментировал когда искал способ решения своей задачи. И даже решил её, но потом переиграл на JS. CURL я еще использовал в простенькой задаче уведомления руководства через бота в телеге о времени прихода-ухода сотрудников на свои рабочие места. Но с приходом пандемии задача утратила актуальность. Смаил.


      1. turone
        04.01.2023 22:09

        осноная идея что html файл запустить можно на любом девайсе. но теперь буду думать о реализации на вашем варианте.


        1. PAPIruss Автор
          04.01.2023 23:04

          Ну да. Я тоже html и JS выбрал как раз из за кроссплатформенности. Люди сейчас чем только не пользуются. И способ из статьи очень выручает. Рдп, доступ к корпоративному веб порталу, 1с из браузера и многое другое. Лишний рубеж защиты очень выручает, особенно в отсутствии штата сетевых безопасников ))))


  1. ALito
    03.01.2023 23:16

    Все таки Java и JavaScript, это разные языки и путать их несколько постыдно, особенно человеку, который пишет статью на Хабре.

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


    1. PAPIruss Автор
      03.01.2023 23:23

      Спасибо друг. Возможно я действительно что то напутал поскольку далек от програмирования на каких либо языках. И мне уже стыдно. Буду бесконечно благодарен если укажете конкретно в каком месте я допустил ошибку. И обязательно внесу правки в текст. С уважением и благодарностью.


      1. ALito
        03.01.2023 23:43
        +1

        3. Размещаем где либо HTML страницу с JS JAVA скриптом. Это может быть как общедоступный WEB сервер. Так и локальная директория на компьютере или usb флэшке.

        Коды HTML и JS JAVA.


        1. PAPIruss Автор
          04.01.2023 00:08

          Запутался еще больше. Вы хотите сказать что приведенный мною код неправильно называть java скрипт, или JavaScript? А правильно употреблять JS? Понимаю что выгляжу глупо. Но я правда не вижу разницы... грустный смайл


          1. thelagz
            04.01.2023 00:33
            +2

            JS это и есть JavaScript, Java это совсем другой язык програмирования. JavaScript код в вашем случае будет называться правильней, вместо Java скрипт.


            1. PAPIruss Автор
              04.01.2023 00:39
              +1

              Уважаемые Андрей, Александр и другие не равнодушные. Спасибо Вам большое за внимание к публикации и подсказки. Поправил текст в соответствии с вашими рекомендациями. Мне действительно стыдно за свое невежество в некоторых вопросах. Обещаю что буду исправляться по мере возможности )))) Всем добра, мира и здоровья в этом и последующих годах ))))


  1. kryptograf
    04.01.2023 00:40
    -1

    JAVA скрипт (


    1. PAPIruss Автор
      04.01.2023 00:40

      Спасибо!


  1. RuslanHamhoev
    04.01.2023 01:01

    Не совсем понял. Насколько я знаю основная идея port-knocking-постучаться со своего ip для того чтобы firewall открыл доступ именно моему ip. У вас надо стучаться со стороннего сайта и firewall открывает доступ всем?


    1. PAPIruss Автор
      04.01.2023 01:22
      +1

      Нет. Скрипт исполняется у Вас на компьютере. Соответсвенно стук идет с вашего ip и доступ открывается именно Вашему ip. Стороний сайт только как площадка доступа к скрипту.