Начало

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

Но, благодаря этому, я, так же, вспомнил, как когда-то давно, я наткнулся на статью про ngrok. Ссылки на нее, к сожалению, я не сохранил и, благополучно забыл. Но теперь, с пылающей идеей в голове, я устремился на поиски самого ценного ресурса, для осуществления задуманного — информации.

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

Кстати, о железе. Мой сервер, это крохотный raspberry pi 4 c 8-ю гигами оперативной памяти под управлением 21 Ubuntu, на котором у меня постоянно крутятся мои разработки.

Установка ngrok

Для начала, регистрируемся на сайте и получаем токен.

Далее выполняем:

snap install ngrok

Далее нужно добавить полученный токен командой:

ngrok authtoken <ваш токен>

И все, установка завершена.

Туннель в небе

Теперь остается только открыть туннель для вашего ресурса. Это мог бы быть сайт, или еще какой проект, или, вообще, БД.
Мы же открываем удаленный доступ. Значит, ssh. Значит 22 порт по умолчанию.

ngrok tcp 22

На экране появится что-то вроде:

Процесс работы сервиса

Туннель открыт. Главное здесь это поле Forwarding, которе имеет примерно такое содержимое: tcp://0.tcp.eu.ngrok.io:12345.
Теперь все запросы по этому адресу будут транслироваться на localhost:22 где их ожидает ssh.

Что бы подключиться удаленно выполним:

ssh 0.tcp.eu.ngrok.io -p 12345

Введя имя пользователя и пароль, попадаем на устройство. Готово! Поздравляю!

А если перезагрузится?

Ну, а правда, если по какой-то причине, сервер перезагрузится, туннель же закроется. Непорядок.
Но я уже работал с systemctl, так что эта проблема — вопрос нескольких минут.
Очень советую ознакомится с этой крутой штукой.

systemd — подсистема инициализации и управления службами в Linux, фактически вытеснившая в 2010-е годы традиционную подсистему init. Основная особенность — интенсивное распараллеливание запуска служб в процессе загрузки системы, что позволяет существенно ускорить запуск операционной системы. Основная единица управления — модуль, одним из типов модулей являются «службы» — аналог демонов — наборы процессов, запускаемые и управляемые средствами подсистемы и изолируемые контрольными группами.

Пишем файл ngrok.service и помещаем его в /etc/systemd/system/ (требуются рут права)

Содержимое файла:

[Unit]
Description=ngrok
After=network.target
After=syslog.target

[Service]
Type=simple
ExecStart=/snap/bin/ngrok tcp 22
Restart=always
RestartSec=1
WorkingDirectory=/home/vladimir/prog/python/projects/ngrok 
User=vladimir  
Group=vladimir
StartLimitBurst=99999
StartLimitInterval=999999

[Install]
WantedBy=multi-user.target

Активируем сервис:

sudo systemctl enable ngrok.service

И запускаем:

sudo systemctl start ngrok.service

Теперь, даже, если сервер перезагрузится, этот сервис запусится автоматически.

Ngrok API

При каждом открытии туннеля ngrok, адрес подключения будет разным.
Как тогда удаленно узнать новый адрес? Это несложно. У ngrok есть собстенный API
И есть там требуемый роут /tunnels.

Однако, для начала, нужно получить API токен. На сайте сервиса.

Подобный запрос:

curl --location --request GET 'https://api.ngrok.com/tunnels' --header 'Authorization: Bearer <ваш API токен>' --header 'Ngrok-Version: 2'

Даст такой результат, если не открыто ни одного туннеля:

{
    "tunnels": [],
    "uri": "https://api.ngrok.com/tunnels",
    "next_page_uri": null
}

И если есть:

{
    "tunnels": [
        {
            "id": "<privat>",
            "public_url": "tcp://0.tcp.eu.ngrok.io:12345",
            "started_at": "2099-06-15T10:46:13Z",
            "metadata": "",
            "proto": "tcp",
            "region": "eu",
            "tunnel_session": {
                "id": "<privat>",
                "uri": "https://api.ngrok.com/tunnel_sessions/<privat>
            },
            "endpoint": {
                "id": "<privat>",
                "uri": "https://api.ngrok.com/endpoints/<privat>"
            },
            "forwards_to": "localhost:22"
        }
    ],
    "uri": "https://api.ngrok.com/tunnels",
    "next_page_uri": null
}

Как видите, вся необходимая информация, для подключения есть в поле "public_url". Можно подключаться.

Еще немножко автоматизации

Как говорил один великий: "Я человек простой: вижу то, что можно автоматизировать — автоматизирую".

Каждый раз курлить или залезать в постман ради нового адреса туннеля — не дело.

Пишем скрипт.

В баше я не особо силен, но написанного хватает с головой:

#!/bin/bash

res=$(curl --location --request GET 'https://api.ngrok.com/tunnels' \
--header 'Authorization: Bearer <ваш API токен>' \
--header 'Ngrok-Version: 2' | awk 'BEGIN { FS="\""; RS="," }; { if ($2 == "public_url") {print $4}}')

if [ -z "$res" ]  
then
    echo "No tunnels"
    exit
fi

ip_port=$(echo ${res:6})

arr=($(echo $ip_port | tr ":" "\n"))

ip=$(echo ${arr[0]})
port=$(echo ${arr[1]})

ssh $ip -p $port

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

Итоги

В этой статье был описан способ организации удаленного доступа по протоколу ssh к серверу под управлением Ubuntu.

Будте осторожны. Помните, вы открываете туннель не только для себя, но и для всего мира. Озаботьтесь безопасностью.

У меня управление туннелями выведено в свой telegram бот. И оповещения о попытках ssh подключении приходят туда же. Если кому будет интересно, расскажу, как это сделал, но уже в другой статье.