⬆️ ⬇️

Reverse tunnel for RDP access using putty and icinga2





It is not always possible to organize a single local area network due to various circumstances, but there is a need for remote access via RDP to hosts behind a nat with a gray ip, for example, a small remote branch with a communication channel via cellular communication. Yes, of course, you can raise something like openVPN or use TeamViewer, but again, such options are not always acceptable. We will use icinga2, putty, SSH server and a couple of scripts on powershell.



First the theory. In order to connect to the server without knowing its real ip address and at the same time it is also behind naty, it is necessary that the server itself creates an outgoing connection. We need an external server with a fixed ip address and an SSH server deployed on it, for this we can use a VDS server with minimal configuration for ridiculous money , or use our own.



Install SSH server:

')

sudo apt-get install openssh-server 


Creating a user without access to the shell:



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


Just in case, we change the default port from 22 to 2201. This can be done by changing the Port parameter in the / etc / ssh / sshd_config file.



Create a tunnel. On a remote server (to which we want to connect as a result) it is necessary in some way to execute the command:



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


And on the administrator’s computer execute the commands:



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


But this is all theory, but in practice we may encounter different nuances. If the channel is not stable, the tunnel may break and the ports used remain open, the remote server needs to be informed that it would raise the tunnel, we may need to connect to several servers at the same time.



The simplest solution is to create a task in the scheduler which will poll every n minutes for some resource on the Internet for the presence of a specific flag for action and, if this flag is detected, create a tunnel with email sent to our address. We will do everything more elegantly, we will use the monitoring system that we have installed, for example, such as icinga2 . In this case, the remote server with the installed agent can simply send a command to create a tunnel at the right time.



If you have not encountered this monitoring system yet, I recommend to look at:





Create a service in icinga2 to create a tunnel:



 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" } 


Now you need to distribute the tunnel creation script and the plink.exe file to the agents. The previous article described how this can be done using icinga2.



Tunnel creation script
 <# 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) } 




The script takes into account such a moment as a request to save the ssh keys of the remote server. If the remote server has never connected to your SSH server, putty will give a warning when connecting, and plink will refuse to connect at all.





To avoid this, you need to connect to the SSH server from any other machine, pull the cached keys out of the registry (HKCU: \ Software \ SimonTatham \ PuTTY \ SshHostKeys) and change the $ regSSHval variable in the script. The script creates a connection to a pseudo-random port number, which will help avoid conflicts with multiple connections. The script also sends a push message using the pushover.net service.



Send a command to the agent to create a tunnel. This can be done via the web interface or in the console of our monitoring server:



 /bin/echo "[`date +%s`] SCHEDULE_FORCED_SVC_CHECK;;create-rdp-tunnel;`date +%s`" >> /var/run/icinga2/cmd/icinga2.cmd 


Now the administrator on his computer will only have to run the script with the name of the remote server as an argument. The rest will be made by the script, it will find out on which port the tunnel is created, establish a tunnel with port forwarding, start the rdp client with the necessary parameters.



Tunnel connection script
 <# 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() 








Links



→ Putty

→ Use powershell scripts in icinga2

Source: https://habr.com/ru/post/315536/



All Articles