Полезный SSH, редирект портов из-за NAT-а и поднятие VPN на tun девайсах

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

Задача:

И так дано: удаленная сетка в офисе, у которой гейт на FreeBSD 8.0(7.2 не суть), удаленная машина на Gentoo дома, за натом, и очень надо тунель внутрь рабочей сети :slight_smile:

Проблемы:

Вроде бы нет проблем: у фряхи есть кошерный демон MPD (Multi Protocol Daemon), который умеет много, работает с мощной сетевой подсистемой netgraph, которая живет в ядре, в дереве portage есть pptp-client, который юзает нативный демон pppd и все ему причитающееся, говорим emerge поставить, настраиваем, поднимается тунель, все прекрасно, пытаемся зайти по RDP на удаленную винду, пять минут радости и… коннект рвется :slight_smile: Долго трясем бубном и понимаем, что проблемма в pptp клиенте, и что качественно запихнуть в кадр оно может только пинги, ssh трафик и всего по мелочи, но когда нужно пропихнуть что-то более существенное (защифрованое и непонятное) при этом используя mppe, mppc: клиент и сервер не могут договориться и рвут коннект. Обидно. Особенно ввиду того, что виндовые ppp клиенты работают наура.

Решение:

Предупреждаю: все ip и адреса вымышлены :slight_smile:

Но работать то надо… Мало того: не хочется сильно ни чего такого делать в чем можно зарыться по уши, но при этом очень хочется зашифрованый тунель :wink: , а время жаль… И тут, открываем *man ssh и понеслась…

Многие знают о достоинствах openssh, что в легкую можно прокинуть к себе на локальную машину любой порт удаленной машины, которая находиться за гейтом, на котором висит демон ssh… для этого надо выполнить что-то вроде:

ssh -f -N -L 127.0.0.10:3389:10.4.4.10:3389 user@remote_gate.com

а можно и проще:

rem_openport user@remote_gate.com 10.4.4.10 3389

Этой функции по умолчанию нет, она описана ниже в “плюшках” :slight_smile:

И совершенно “бесплатно” (то есть без усилий) - мы получаем на 3389 порту, по адресу 127.0.0.10, на loopback интерфейсе локальной машины - RDP порт(3389) машинки с адресом 10.4.4.10, которая живет в своей сети, за гейтом remote_gate.com, и логонимся мы при этом юзером user. Ключами -f -N мы говорим ssh “уйти в бэкграунд” и ничего не выводить в консоль, а ключик -L собственно для редиректа

Волшебный ключик -w

Но это решение не оригинальное и неудобно как-то по каждому чиху открывать тунель для каждого порта и тут мы находим ключик w:
*??
-w tunnel:tunnel
Requests a tun(4) device on the client (first tunnel arg) and server (second tunnel arg). The devices may be specified by numerical ID or the keyword ‘‘any’’, which uses the next available tunnel device. See also the Tunnel directive in ssh_config.
??*

Ага! Вот оно то нам и надо… дальше лезем в гугл, и он нам рассказывает, что мы не первые с такой проблемой :slight_smile: Мы пробуем “как там написано” и ничего не получается, или получается, но не так как хотелось бы :wink: И как всегда, мы собираем все в кучу и делаем свое решение, не забывая ссылаться на тех, кто нам помогал (ссылки внизу статьи).

Поднимаем VPN:

Сначала реквизируем “что мы имеем” на обоих концах:

Локальная машина с Gentoo:

modprobe tun

Если не ругнулось - значит модуль есть! Если ругнулось - собираем ядро с опцией --menuconfig, не забывая включить TUN/TAP модуль:

Device Drivers--->
  Network device support--->
    <M> Universal TUN/TAP device driver support 

Затем приводим /etc/conf.d/net к такому виду:

### up your stuff %))
config_tun0=( "10.4.4.200/24" )

preup() {
    case ${IFACE} in
        tun0)
            modprobe tun
            ssh -i /root/.ssh/netvpn -S /var/run/tun0-ssh -M -f -w 0:0 remote_gate.com true
            sleep 5
            ;;
    esac
    return 0
}

postup() {
    case ${IFACE} in
        tun0)
            route add -net 10.4.4.0/24 tun0
            ;;
    esac
    return 0
}

postdown() {
    case ${IFACE} in
        tun0)
            ssh -S /var/run/tun0-ssh -O exit remote_gate.com
            sleep 2
            ;;
    esac
    return 0
}

Знающие люди, тут же зададут вопрос: “и чего - под рутом на удаленку ходим?” ответ: “да”, но не совсем :slight_smile: Но об этом позже…
Затем делаем симлинк в init.d директории:

ln -s /etc/init.d/net.lo /etc/init.d/net.tun0

Затем делаем правки в /etc/ssh/sshd_config:

#...
PermitTunnel point-to-point
#...

С локальной машиной практически все. Осталось сгенерить ключи - так как мы люди ленивые и пароль постоянно набивать не хотим, да и портит это картинку вывода runscript-а :wink: Но и в этом вопросе мы все равно ленивые, поэтому предлагаю, ставших уже традиционными, немного своих плюшек…
Открываем допустим /root/.bashrc , и рисуем туда малость полезных функций:

### up your stuff %))

[[ -f /etc/init.d/functions.sh ]] && . /etc/init.d/functions.sh

function rem_openport(){
        /etc/init.d/sshd stop &>/dev/null
        #
        _gw="$1"; _tg=$2; _p=$3
        node_octets=( `echo -e $_tg | sed -e "s/\./ /g"` )
        octet=${node_octets[3]}
        forw_str="127.0.0.$octet:$_p:$_tg:$_p"
        #
        #netstat -ln | grep -o "127.0.0.6:22"
        #
        ssh -f -N -L $forw_str $_gw
}
#
function rem_makekey(){
        kf_name=${2:-"id_dsa"}
        #
        cur_user=`whoami`
        if [ $cur_user = "root" ]; then
                _remdir="/root"
        else
                _remdir="/home/$cur_user"
        fi
        kf="${_remdir}/.ssh/${kf_name}.pub"
        #
        if ask "Do you want generate a new key?..."; then
                ssh-keygen -N "" -t dsa -f ${_remdir}/.ssh/${kf_name}
                chmod 0600 ${_remdir}/.ssh/${kf_name}
        fi
        #
        full_key="$3 `cat ${kf}`"
        echo -e ${full_key} | ssh $1 "cat >> ${_remdir}/.ssh/authorized_keys"
        if ask "Do you want to delete a .pub key?..."; then
                rm -f ${kf}
        fi
}
#
function ask(){
        einfon "$@ (y/n): " ; read ans
        udyesno $ans
}
#
function udyesno(){
        [ -z "$1" ] && return 1
        case "$1" in
                [Yy]|[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0;;
                [Nn]|[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1;;
        esac
}

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

env-update && source/etc/profile

и… генерим в легкую ключик:

rem_makekey remote_gate.com netvpn 'tunnel="0",command="ifconfig tun0 10.4.4.1 10.4.4.200; arp -s 10.4.4.200 auto pub"'

Сами функции я разжевывать не буду, а использование поясню:
Мы говорим функции rem_makekey создать ключики с именем netvpn в каталоге пользователя ~/.ssh/, и прописать публичный ключик на удаленном хосте. При этом удаленным ssh при использовании этого ключика будет подниматься интерфейс tun0, настраиваться так как нам нужно, а также добавлять для него поддержку proxy arp (что ой как немаловажно, иначе можно долго удивляться почему при наличии правильного роутинга машины внутри сети не знают кому все-таки отдать пакет :))

И на этом с локальной машинкой все… :slight_smile: Теперь можно сбегать перекурить(попить чаю, поцеловать жену и ребенка - ненужное зачеркнуть) а потом сделаем пару телодвижений на удаленном гейте :slight_smile:

Удаленный гейт:

Неважно что используется на удаленном гейте, в моем случае это выше озвученная FreeBSD, действия для всех unix одинаковы. Итак.

Момент раз… - правим /etc/ssh/sshd_config:

#...
PermitRootLogin no
#...
AllowTcpForwarding yes
GatewayPorts yes
#...
PermitTunnel yes
#...
 # For Tuneling
Match Host aaa.bbb.ccc.ddd,127.0.0.1
        PermitRootLogin yes

Где aaa.bbb.ccc.ddd - это ваш удаленный внешний ip, либо того гейта за которым вы сидите… Оговорка: если такового(ip) нет - то я бы не рисковал открывать возможность такого логона, но и это поправимо (то есть можно описать в конфиге, что рут может коннектится, и только по ключам, и при этом ничего более как написаная в строке ключа комманда выполняться им не может - тогда можно не опасаться за безопасность такого “открытия”). Какие опции можно задавать - можете прочитать в мане самого ssh, ссылка на который выше.

Момент два…
Производим манипуляции с пакетным фильтром на удаленной системе… Так как у меня фряха - я рассказал pf, что во внутренней сети можно гонять пакеты с интерфейса tun0.
Еще оговорка: опять-таки на фряхе по дефолту tun-девайс в ядре, если у Вас на том конце linux и не собран модуль оного - то проводим с ядром манипуляции такие же как и на локальной системе…

Проверяем…

Ну вот и все. Оба конца настроены, пробуем? :slight_smile: На локальной машине говорим:

/etc/init.d/net.tun0 start

и наблюдаем примерно следующее:

/etc/init.d/net.tun0 start
* Bringing up interface tun0
*   Running preup ...
using interface xx0 for proxy with address 00:b1:8d:e1:10:a2 [ ok ]
*   10.4.4.200/24 ...
*   Running postup ...

проверяем:

ifconfig tun0
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet addr:10.4.4.200  P-t-P:10.4.4.200  Mask:255.255.255.0
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:10 errors:0 dropped:0 overruns:0 frame:0
          TX packets:13 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:1329 (1.2 KiB)  TX bytes:1232 (1.2 KiB)

ping -c 3 10.4.4.6
PING 10.4.4.6 (10.4.4.6) 56(84) bytes of data.
64 bytes from 10.4.4.6: icmp_seq=1 ttl=63 time=4.39 ms
64 bytes from 10.4.4.6: icmp_seq=2 ttl=63 time=6.13 ms
64 bytes from 10.4.4.6: icmp_seq=3 ttl=63 time=5.98 ms

--- 10.4.4.6 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 4.395/5.506/6.136/0.792 ms

Ура! свершилось :slight_smile:

В заключении…

Итого: мы имеем полноценный шифрованый тунель в удаленную сеть, который ходит поверх обычного кошерного ssh, при этом требования к машинам на обоих концах минимальные

Автор гоняет свои “админские пакеты” по тунелю и проблем с оным пока не выявил, канал не падает, работать можно.

В качестве рабочего “промышленного решения” (т. е.) для объединения сетей таким образом, я бы это решение не рекомендовал, ибо такой канал не потянет серьезную нагрузку… Но для удаленного администрирования своего хозяйства - вполне сойдет.

Использованные материалы:
http://ru.gentoo-wiki.com/wiki/HOWTO_Создание_VPN_средствами_OpenSSH
http://www.openbsd.ru/docs/steps/ssh-vpn.html

Удачи и успехов Вам в нашем не легком деле, коллеги!

Ну хотя бы поправку на Calculate сделали бы…

Не буду, потому как оно работает на любой Gentoo, а Calculate с ней полностью совместим

Интересное решение. А чем обычный OpenVPN вас не устроил?

OpenVPN против этого решения во-первых требователен к дополнительному софту, во-вторых, как следствие первого - к дополнительному гиморою…

Вообще то я в среде BSD использую описанный выше MPD - наиболее производительное решение… мои знакомые, работающие в провайдерах разных на нем 1500-2500 pppoe соединений одновременных поднимают… И оно ж обновляется из коробки и работает не юзерленд, а в ядре… Вот и получается, что для полноценного построения VPN распределенных сетей - OpenVPN не тянет, а для банально “в сетку походить” его очень много…

А это решение порадовало минимальными требованиями, всего-то: модуль ядра, 2 конфига и ssh - это есть в любом дистрибутиве, любого юникс, и не требует замороченой настройки, равно как и замороченных клиентов и так далее…

Удачи!

Вот жишь… не посмотрел, что не залогонен… %) Пост выше это мой )))

Я вот подумал еще… эту всю фишку можно в легкую перепилить под мульти-конект решение с автоматизацией… параскриптов на двух концах… Возможно в скором времени появиться немного скриптов в моей гит-репе, адрес которой пока не скажу, ибо там все сыро %)