Обратный туннель для доступа по RDP с использованием putty и icinga2



    Не всегда есть возможность организовать единую локальную сеть в силу разных обстоятельств, но есть необходимость в удаленном доступе по протоколу RDP к хостам за натом с серым ip, например небольшой удаленный филиал с каналом связи через сотовую связь. Да, конечно можно поднять что-то вроде openVPN или воспользоваться TeamViewer, но опять же такие варианты не всегда приемлемы. Будем использовать icinga2, putty, SSH сервер и пару скриптов на powershell.

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

    Установка SSH сервера:

    sudo apt-get install openssh-server
    

    Создание пользователя без доступа к шелу:

    sudo useradd -d /dev/null -s /dev/null ssh_user
    sudo passwd ssh_user
    

    На всякий случай изменим порт по умолчанию с 22 на 2201. Это можно сделать изменив параметр Port в файле /etc/ssh/sshd_config.

    Создаем туннель. На удаленном сервере (к которому в итоге мы хотим подключиться) необходимо каким-то способом выполнить команду:

    plink.exe -batch -P 2201 -N -C -v -R 33899:localhost:3389 ssh_user@222.222.222.222 -pw password
    

    А на компьютере администратора выполнить команды:

    plink.exe -P 2201 -N -C -v -L 3379:localhost:33899 ssh_user@222.222.222.222 -pw password
    mstsc.exe /v localhost:3379
    

    Но это все теория, на практике же мы можем столкнуться с разными нюансами. При не стабильном канале может обрываться туннель и используемые порты остаются открытыми, удаленному серверу нужно как то сообщить что бы он поднял туннель, нам может потребоваться подключение к нескольким серверам одновременно.

    Самое простое решение это создать задачу в планировщике которая каждые n минут будет опрашивать какой то ресурс в интернете на наличие определенного флага к действию и при обнаружении этого флага создавать туннель с отправкой email на наш адрес. Мы же сделаем все изящнее, воспользуемся системой мониторинга которая у нас установлена, например такой как icinga2. В этом случае удаленному серверу с установленным агентом можно будет просто послать команду на создание туннеля в нужный нам момент времени.

    Если вы еще не сталкивались с этой системой мониторинга, рекомендую посмотреть:


    Создадим сервис в icinga2 для создания туннеля:

    apply Service "create-rdp-tunnel" {
        enable_active_checks = false
        max_check_attempts = 2
        assign where host.name == NodeName
        ignore where host.vars.os == "Linux"
        check_command = "powershell"
        vars.ps_command = "c:\\ProgramData\\icinga2\\Scripts\\icinga2\\create_rdp_tunnel.ps1"
    }
    

    Теперь нужно распространить скрипт создания туннеля и файл plink.exe на агенты. В предыдущей статье было описано как это можно сделать с помощью icinga2.

    Скрипт создания туннеля
    <#
    icinga2scripts
    Version 1.0
    Description: Скрипт для Icinga 2 - Создание ssh туннеля с удаленным хостом
    Pavel Satin (c) 2016
    pslater.ru@gmail.com
    #>
    [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
    
    $returnStateOK = 0
    $returnStateWarning = 1
    $returnStateCritical = 2
    $returnStateUnknown = 3
    
    
    $portnum = "338" + (Get-Random -minimum 10 -maximum 99).ToString()
    
    
    $tunnelcmd = "c:\ProgramData\icinga2\Scripts\icinga2\plink.exe"
    $tunnelarg = "-batch -P 2201 -N -C -v -R " + $portnum + ":localhost:3389 ssh_user@222.222.222.222 -pw password"
    
    
    $regSSHkey = "HKCU:\Software\SimonTatham\PuTTY\SshHostKeys"
    $regSSHname = "rsa2@2201:222.222.222.222"
    $regSSHval = "0x10001,0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
    
    if (!(Test-Path $regSSHkey -PathType Any)) {
    	New-Item -Path $regSSHkey -Force | Out-Null
    	New-ItemProperty -Path $regSSHkey -Name $regSSHName -Value $regSSHval -PropertyType String -Force | Out-Null
    } else {
    	New-ItemProperty -Path $regSSHkey -Name $regSSHName -Value $regSSHval -PropertyType String -Force | Out-Null
    }
    
    
    $process = (start-process $tunnelcmd -argumentlist $tunnelarg -PassThru)
    
    Start-Sleep -s 5
    
    if ($process.HasExited) {
        Write-Host "Failed to start plink. The process is closed with the code: " $process.ExitCode
        [System.Environment]::Exit($returnStateCritical)
    } else {
    
        #Отправка pushover сообщения
        $uri = "https://api.pushover.net/1/messages.json"
        $parameters = @{
            token = "API_TOKEN"
            user = "API_USER"
            message = "Создан туннель на порту: $portnum с узлом: $env:computername"
        }
        
        $pushoverreq = $parameters | Invoke-RestMethod -Uri $uri -Method Post
    
        Write-Host "OK - The tunnel is created. Port number: $portnum"
        Write-Host "To connect:"
        Write-Host "plink.exe -P 2201 -N -C -v -L 3379:localhost:$portnum ssh_user@222.222.222.222 -pw password"
        [System.Environment]::Exit($returnStateOK)
    }
    
    


    В скрипте учтен такой момент как запрос на сохранение ssh ключей удаленного сервера. Если на удаленном сервере ни разу не выполнялось подключение к вашему SSH серверу, putty будет выдавать предупреждение при подключении, а plink вообще откажется подключаться.


    Что бы избежать этого, нужно подключиться к SSH серверу с любой другой машины, выдернуть из реестра кэшированные ключи (HKCU:\Software\SimonTatham\PuTTY\SshHostKeys) и изменить переменную $regSSHval в скрипте. Скрипт создает подключение к псевдослучайному номеру порта, что поможет избежать конфликтов при множестве соединений. Так же скрипт отправляет push сообщение с помощью сервиса pushover.net.

    Отправим команду агенту на создание туннеля. Это можно сделать через веб-интерфейс или в консоли нашего сервера мониторинга:

    /bin/echo "[`date +%s`] SCHEDULE_FORCED_SVC_CHECK;ИмяНоды;create-rdp-tunnel;`date +%s`" >> /var/run/icinga2/cmd/icinga2.cmd
    

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

    Скрипт подключения через туннель
    <#
    icinga2scripts
    Version 1.0
    Description: Скрипт для Icinga 2 - Запуск RemoteDesktop через туннель
    Pavel Satin (c) 2016
    pslater.ru@gmail.com
    #>
    
    $returnStateOK = 0
    $returnStateWarning = 1
    $returnStateCritical = 2
    $returnStateUnknown = 3
    
    #Windows Balloon
    [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    $objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon 
    
    if ($args[0] -eq $null) {
    
        $objNotifyIcon.Icon = "C:\Scripts\images\icinga.ico"
        $objNotifyIcon.BalloonTipIcon = "Error" 
        $objNotifyIcon.BalloonTipText = "Параметр с именем хоста не передан! Работа скрипта завершена."
        $objNotifyIcon.BalloonTipTitle = "Подключение через туннель"
     
        $objNotifyIcon.Visible = $True 
        $objNotifyIcon.ShowBalloonTip(30000)
    
        Start-Sleep -s 10
        $objNotifyIcon.Visible = $false
        $script:objNotifyIcon.Dispose()
        exit
    }
    
    $rdpHost = $args[0]
    
    $plinkPath = "C:\Scripts\bin\"
    
    add-type -TypeDefinition  @"
            using System.Net;
            using System.Security.Cryptography.X509Certificates;
            public class TrustAllCertsPolicy : ICertificatePolicy {
                public bool CheckValidationResult(
                    ServicePoint srvPoint, X509Certificate certificate,
                    WebRequest request, int certificateProblem) {
                    return true;
                }
            }
    "@
    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    
    $user = "icinga"
    $pass= "password"
    $secpasswd = ConvertTo-SecureString $pass -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential($user, $secpasswd)
    $apiurl = "https://222.222.222.222:5665/v1/objects/services/" + $rdpHost + "!create-rdp-tunnel?attrs=last_check_result"
    
    $apireq = Invoke-WebRequest -Credential $credential -Uri $apiurl -Method Get -UseBasicParsing -ContentType "text/plain; charset=Windows-1251"
    
    $outputresult = $apireq | ConvertFrom-Json | Select -expand Results | Select -expand attrs | Select -expand last_check_result 
    $strOutput = $outputresult.output
    $indxPlink = $strOutput.IndexOf("plink")
    
    $portnum = "339" + (Get-Random -minimum 10 -maximum 99).ToString()
    
    $strOutput2 = $strOutput.Substring($indxPlink, $strOutput.Length - $indxPlink)
    
    $cmdArgs = "/C " + $strOutput2.Replace("3379", $portnum)
    $mstscArgs = "/v localhost:$portnum"
    
    #Запуск процессов
    Start-Process cmd.exe $cmdArgs
    Start-Process mstsc.exe $mstscArgs
    
    $objNotifyIcon.Icon = "C:\Scripts\images\icinga.ico"
    $objNotifyIcon.BalloonTipIcon = "Info" 
    $objNotifyIcon.BalloonTipText = "Устанавливаем подключение к $rdpHost"
    $objNotifyIcon.BalloonTipTitle = "Подключение через туннель"
     
    $objNotifyIcon.Visible = $True 
    $objNotifyIcon.ShowBalloonTip(30000)
    
    Start-Sleep -s 30
    $objNotifyIcon.Visible = $false
    $script:objNotifyIcon.Dispose()
    




    Ссылки


    Putty
    Используем powershell скрипты в icinga2

    Комментарии 15

      +1
      перешел с hamachi на jabpunch, вот только под linux его нету
        +1

        Этой публикацией я не хотел показать 1001-ый способ удаленного администрирования серверов за натом, скорее всего это просто «запасной вход», когда по каким-то причинам не отрабатывают стандартные средства, такие как групповые политики или системы централизованного управления конфигурацией.


        По моему мнению для технической поддержки клиентских Windows машин должны использоваться стандартные средства: Remote Assistance или SCCM Remote Control (в зависимости от размеров вашей организации), в случае же если ваша организация профессионально занимается поддержкой внешних клиентов, то тогда это должно быть что-то вроде TeamViewer. А для администрирования Windows серверов, когда их больше 10 в одной организации, «удаленный рабочий стол» должен использоваться только в экстренных случаях.

          0

          А как же NetSupport Manager?
          Его вполне можно использовать для удаленного администрирования.

            0

            Смотря, что вы понимаете под удаленным администрированием.


            • Если это техническая поддержка пользователей и настройка рабочих станций (в одном лесу доменов, в одной объединенной сети), тогда зачем, что-то устанавливать дополнительно, когда есть Remote Assistance. Эта утилита уже встроена в Windows имеет достаточный функционал и при этом вписывается даже в самые строгие политики информационной безопасности почти любой организации (ну может кроме банков и т.п.).
            • Если это конфигурирование серверов, тогда при условии, что у вас несколько десятков серверов и более, используя любое решение, которое «показывает» вам «Рабочий стол» и вы настраиваете сервер используя мышь – вы только и будете прыгать между «Рабочими столами».
            • Если это все-таки конфигурирование серверов и их небольшое количество, тогда любое решение имеет право на жизнь, в том числе и NetSupport Mangaer и ему подобные. Главное, что бы это решение не противоречило политике информационной безопасности организации и не принесло дополнительной потенциально возможной дыры.
          0
          Создаем туннель. На удаленном сервере необходимо выполнить команду:


          В смысле — не компьютере, который прячется за NAT'ом?
          Вообще да, забавная идея — т.е. работать это должно даже тогда, когда у обоих компьютеров серые ай-пи, и оба сидят за своими NAT'ами (но пускают SSH-порты)?
            0

            На первом рисунке (схеме) видно, что используется еще и третье звено. А вообще на Хабре уже не раз поднималась эта тема. SSH-туннель

              0
              «Третье звено» — SSH-сервер — подразумевается для любого тоннеля, а для обратного он практически обязателен. Т.е. ценность его именно в том, что можно дать удалённый доступ «себе» из-за NAT'а.
              Но команда установления соединения исполняется при этом на том компьютере, к которому даётся доступ.
              Так мне помнится.
                0

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

              0
              Создаем туннель. На удаленном сервере необходимо выполнить команду:

              Эта фраза входила в теоретическую часть.

                0
                Просто может сложиться ложное впечатление, что команду нужно исполнить на SSH-сервере.
              0

              Интересная статья. Реализовал подобное на node.js + socket.


              На работе пк за натом, всё залочино типа тимвювера, хамачи и проч; дома комп с прямым айпи.


              Рабочий комп устанавливает соединение с моим домашним компом, открывая тунель. Далее никакой магии, запускаю рдп, подключаюсь на localhost:9876 (порт можно предварительно задать любой, разумеется).


              Если нужно не рдп или рдп на другой рабочий комп, не проблема, просто указал другой айпи/порт :)


              Если кому-то интересно — пишите.
              Прошу прощения за офытоп

                0
                Описанное решение сложно в настройке и содержит несколько точек отказа. Преимущество только одно: можно продиктовать (прислать в SMS) пользователю команду и получить вход. Но в статье описан предварительно настроенный туннель, при том, что есть более простые и надёжные альтернативы.

                Да, конечно можно поднять что-то вроде openVPN или воспользоваться TeamViewer, но опять же такие варианты не всегда приемлемы.


                TeamViewer, AmmyAdmin и подобные действительно могут противоречить политике информационной безопасности, поскольку вынуждают доверять третьей стороне.
                Но OpenVPN с собственным сервером администратора чем не угодил? Он работает по любому порту, который пропускает аплинк, на сервере можно принимать соединения хоть по всем портам, используя NAT (PAT).

                Я вижу только один вариант, когда описанный способю является единственным доступным: работа админом в подразделении организации, в которой «голова» запретила все соединения из внутреннего сегмента подразделения, кроме SSH на вебсервер.
                  0

                  Это решение не претендует на промышленную эксплуатацию от всего лишь "запасной вход". Например, из-за 2-3 мелких филиалов, думаю нет смысла поднимать OpenVPN, а удаленный доступ к ним может понадобиться 2-3 раза в год. Единственная серьезная точка отказа в таком решении, это сама система мониторинга, точнее клиент (агент) мониторинга, когда он по каким-то причинам не сможет подключиться к серверу мониторинга. Скорее это решение дополнительная фишка к уже имеющейся системе мониторинга, а не система удаленного доступа.

                  0
                  Для единичных подключений использовал PPtP. На нужном сервере/маршрутизаторе/etc настраивается подключение, запускаемое при старте. В центре стоял выделенный маршрутизатор с поднятым PPtP сервером, к учетке привязан IP (использовал Микротик). Просто как палка/веревка.
                    0
                    bitvise — всё это уже из коробки

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

                    Самое читаемое