mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 22:11:48 +08:00
Merge pull request #1568 from fmcontrib/issue_420_proxy_aka_jumphost_support
SSH tunneling aka jumphost implemented
This commit is contained in:
@@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
### Added
|
||||
- #545: Option to minimize to system tray on closing
|
||||
- #283: Support for native PowerShell remoting as new protocol
|
||||
- #420: SSH tunneling implemented
|
||||
### Changed
|
||||
- #1460: Updated GeckoFX to v60
|
||||
### Fixed
|
||||
|
||||
@@ -66,5 +66,7 @@
|
||||
public TType VNCViewOnly { get; set; }
|
||||
public TType RdpVersion { get; set; }
|
||||
public TType UseEnhancedMode { get; set; }
|
||||
public TType SSHOptions { get; set; }
|
||||
public TType SSHTunnelConnectionName { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,6 +248,7 @@ namespace mRemoteNGTests.UI.Window.ConfigWindowTests
|
||||
nameof(ConnectionInfo.MacAddress),
|
||||
nameof(ConnectionInfo.UserField),
|
||||
nameof(ConnectionInfo.Favorite),
|
||||
nameof(ConnectionInfo.SSHTunnelConnectionName),
|
||||
};
|
||||
|
||||
if (!isContainer)
|
||||
@@ -308,6 +309,7 @@ namespace mRemoteNGTests.UI.Window.ConfigWindowTests
|
||||
nameof(ConnectionInfo.Username),
|
||||
nameof(ConnectionInfo.Password),
|
||||
nameof(ConnectionInfo.Port),
|
||||
nameof(ConnectionInfo.SSHOptions),
|
||||
nameof(ConnectionInfo.PuttySession)
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -96,6 +96,10 @@ namespace mRemoteNG.Config.Serializers.Csv
|
||||
connectionRecord.Domain = headers.Contains("Domain") ? connectionCsv[headers.IndexOf("Domain")] : "";
|
||||
connectionRecord.Hostname = headers.Contains("Hostname") ? connectionCsv[headers.IndexOf("Hostname")] : "";
|
||||
connectionRecord.VmId = headers.Contains("VmId") ? connectionCsv[headers.IndexOf("VmId")] : "";
|
||||
connectionRecord.SSHOptions =
|
||||
headers.Contains("SSHOptions") ? connectionCsv[headers.IndexOf("SSHOptions")] : "";
|
||||
connectionRecord.SSHTunnelConnectionName =
|
||||
headers.Contains("SSHTunnelConnectionName") ? connectionCsv[headers.IndexOf("SSHTunnelConnectionName")] : "";
|
||||
connectionRecord.PuttySession = headers.Contains("PuttySession") ? connectionCsv[headers.IndexOf("PuttySession")] : "";
|
||||
connectionRecord.LoadBalanceInfo = headers.Contains("LoadBalanceInfo")
|
||||
? connectionCsv[headers.IndexOf("LoadBalanceInfo")]
|
||||
@@ -480,6 +484,20 @@ namespace mRemoteNG.Config.Serializers.Csv
|
||||
connectionRecord.Inheritance.Protocol = value;
|
||||
}
|
||||
|
||||
if (headers.Contains("InheritSSHTunnelConnectionName"))
|
||||
{
|
||||
bool value;
|
||||
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritSSHTunnelConnectionName")], out value))
|
||||
connectionRecord.Inheritance.SSHTunnelConnectionName = value;
|
||||
}
|
||||
|
||||
if (headers.Contains("InheritSSHOptions"))
|
||||
{
|
||||
bool value;
|
||||
if (bool.TryParse(connectionCsv[headers.IndexOf("InheritSSHOptions")], out value))
|
||||
connectionRecord.Inheritance.SSHOptions = value;
|
||||
}
|
||||
|
||||
if (headers.Contains("InheritPuttySession"))
|
||||
{
|
||||
bool value;
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace mRemoteNG.Config.Serializers.Csv
|
||||
if (_saveFilter.SaveDomain)
|
||||
sb.Append("Domain;");
|
||||
|
||||
sb.Append("Hostname;Port;VmId;Protocol;PuttySession;ConnectToConsole;UseCredSsp;UseVmId;UseEnhancedMode;RenderingEngine;ICAEncryptionStrength;RDPAuthenticationLevel;" +
|
||||
sb.Append("Hostname;Port;VmId;Protocol;SSHTunnelConnectionName;SSHOptions;PuttySession;ConnectToConsole;UseCredSsp;UseVmId;UseEnhancedMode;RenderingEngine;ICAEncryptionStrength;RDPAuthenticationLevel;" +
|
||||
"LoadBalanceInfo;Colors;Resolution;AutomaticResize;DisplayWallpaper;DisplayThemes;EnableFontSmoothing;EnableDesktopComposition;" +
|
||||
"CacheBitmaps;RedirectDiskDrives;RedirectPorts;RedirectPrinters;RedirectClipboard;RedirectSmartCards;RedirectSound;RedirectKeys;" +
|
||||
"PreExtApp;PostExtApp;MacAddress;UserField;ExtApp;Favorite;VNCCompression;VNCEncoding;VNCAuthMode;VNCProxyType;VNCProxyIP;" +
|
||||
@@ -66,7 +66,7 @@ namespace mRemoteNG.Config.Serializers.Csv
|
||||
if (_saveFilter.SaveInheritance)
|
||||
sb.Append("InheritCacheBitmaps;InheritColors;InheritDescription;InheritDisplayThemes;InheritDisplayWallpaper;" +
|
||||
"InheritEnableFontSmoothing;InheritEnableDesktopComposition;InheritDomain;InheritIcon;InheritPanel;InheritPassword;InheritPort;" +
|
||||
"InheritProtocol;InheritPuttySession;InheritRedirectDiskDrives;InheritRedirectKeys;InheritRedirectPorts;InheritRedirectPrinters;" +
|
||||
"InheritProtocol;InheritSSHTunnelConnectionName;InheritSSHOptions;InheritPuttySession;InheritRedirectDiskDrives;InheritRedirectKeys;InheritRedirectPorts;InheritRedirectPrinters;" +
|
||||
"InheritRedirectClipboard;InheritRedirectSmartCards;InheritRedirectSound;InheritResolution;InheritAutomaticResize;" +
|
||||
"InheritUseConsoleSession;InheritUseCredSsp;InheritUseVmId;InheritUseEnhancedMode;InheritVmId;InheritRenderingEngine;InheritUsername;InheritICAEncryptionStrength;" +
|
||||
"InheritRDPAuthenticationLevel;InheritLoadBalanceInfo;InheritPreExtApp;InheritPostExtApp;InheritMacAddress;InheritUserField;" +
|
||||
@@ -119,6 +119,8 @@ namespace mRemoteNG.Config.Serializers.Csv
|
||||
.Append(FormatForCsv(con.Port))
|
||||
.Append(FormatForCsv(con.VmId))
|
||||
.Append(FormatForCsv(con.Protocol))
|
||||
.Append(FormatForCsv(con.SSHTunnelConnectionName))
|
||||
.Append(FormatForCsv(con.SSHOptions))
|
||||
.Append(FormatForCsv(con.PuttySession))
|
||||
.Append(FormatForCsv(con.UseConsoleSession))
|
||||
.Append(FormatForCsv(con.UseCredSsp))
|
||||
@@ -186,6 +188,8 @@ namespace mRemoteNG.Config.Serializers.Csv
|
||||
.Append(FormatForCsv(con.Inheritance.Password))
|
||||
.Append(FormatForCsv(con.Inheritance.Port))
|
||||
.Append(FormatForCsv(con.Inheritance.Protocol))
|
||||
.Append(FormatForCsv(con.Inheritance.SSHTunnelConnectionName))
|
||||
.Append(FormatForCsv(con.Inheritance.SSHOptions))
|
||||
.Append(FormatForCsv(con.Inheritance.PuttySession))
|
||||
.Append(FormatForCsv(con.Inheritance.RedirectDiskDrives))
|
||||
.Append(FormatForCsv(con.Inheritance.RedirectKeys))
|
||||
|
||||
@@ -91,6 +91,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
connectionInfo.VmId = (string)dataRow["VmId"];
|
||||
connectionInfo.UseEnhancedMode = (bool)dataRow["UseEnhancedMode"];
|
||||
connectionInfo.Protocol = (ProtocolType)Enum.Parse(typeof(ProtocolType), (string)dataRow["Protocol"]);
|
||||
connectionInfo.SSHTunnelConnectionName = (string)dataRow["SSHTunnelConnectionName"];
|
||||
connectionInfo.SSHOptions = (string)dataRow["SSHOptions"];
|
||||
connectionInfo.PuttySession = (string)dataRow["PuttySession"];
|
||||
connectionInfo.Port = (int)dataRow["Port"];
|
||||
connectionInfo.UseConsoleSession = (bool)dataRow["ConnectToConsole"];
|
||||
@@ -181,6 +183,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
connectionInfo.Inheritance.Password = (bool)dataRow["InheritPassword"];
|
||||
connectionInfo.Inheritance.Port = (bool)dataRow["InheritPort"];
|
||||
connectionInfo.Inheritance.Protocol = (bool)dataRow["InheritProtocol"];
|
||||
connectionInfo.Inheritance.SSHTunnelConnectionName = (bool)dataRow["InheritSSHTunnelConnectionName"];
|
||||
connectionInfo.Inheritance.SSHOptions = (bool)dataRow["InheritSSHOptions"];
|
||||
connectionInfo.Inheritance.PuttySession = (bool)dataRow["InheritPuttySession"];
|
||||
connectionInfo.Inheritance.RedirectDiskDrives = (bool)dataRow["InheritRedirectDiskDrives"];
|
||||
connectionInfo.Inheritance.RedirectKeys = (bool)dataRow["InheritRedirectKeys"];
|
||||
|
||||
@@ -109,9 +109,11 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
dataTable.Columns.Add("DomainName", typeof(string));
|
||||
dataTable.Columns.Add("Password", typeof(string));
|
||||
dataTable.Columns.Add("Hostname", typeof(string));
|
||||
dataTable.Columns.Add("Protocol", typeof(string));
|
||||
dataTable.Columns.Add("PuttySession", typeof(string));
|
||||
dataTable.Columns.Add("Port", typeof(int));
|
||||
dataTable.Columns.Add("Protocol", typeof(string));
|
||||
dataTable.Columns.Add("SSHTunnelConnectionName", typeof(string));
|
||||
dataTable.Columns.Add("SSHOptions", typeof(string));
|
||||
dataTable.Columns.Add("PuttySession", typeof(string));
|
||||
dataTable.Columns.Add("ConnectToConsole", typeof(bool));
|
||||
dataTable.Columns.Add("UseCredSsp", typeof(bool));
|
||||
dataTable.Columns.Add("RenderingEngine", typeof(string));
|
||||
@@ -168,6 +170,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
dataTable.Columns.Add("InheritPassword", typeof(bool));
|
||||
dataTable.Columns.Add("InheritPort", typeof(bool));
|
||||
dataTable.Columns.Add("InheritProtocol", typeof(bool));
|
||||
dataTable.Columns.Add("InheritSSHTunnelConnectionName", typeof(bool));
|
||||
dataTable.Columns.Add("InheritSSHOptions", typeof(bool));
|
||||
dataTable.Columns.Add("InheritPuttySession", typeof(bool));
|
||||
dataTable.Columns.Add("InheritRedirectDiskDrives", typeof(bool));
|
||||
dataTable.Columns.Add("InheritRedirectKeys", typeof(bool));
|
||||
@@ -497,6 +501,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
dataRow["Hostname"] = connectionInfo.Hostname;
|
||||
dataRow["VmId"] = connectionInfo.VmId;
|
||||
dataRow["Protocol"] = connectionInfo.Protocol;
|
||||
dataRow["SSHTunnelConnectionName"] = connectionInfo.SSHTunnelConnectionName;
|
||||
dataRow["SSHOptions"] = connectionInfo.SSHOptions;
|
||||
dataRow["PuttySession"] = connectionInfo.PuttySession;
|
||||
dataRow["Port"] = connectionInfo.Port;
|
||||
dataRow["ConnectToConsole"] = connectionInfo.UseConsoleSession;
|
||||
@@ -568,6 +574,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
dataRow["InheritPassword"] = connectionInfo.Inheritance.Password;
|
||||
dataRow["InheritPort"] = connectionInfo.Inheritance.Port;
|
||||
dataRow["InheritProtocol"] = connectionInfo.Inheritance.Protocol;
|
||||
dataRow["InheritSSHTunnelConnectionName"] = connectionInfo.Inheritance.SSHTunnelConnectionName;
|
||||
dataRow["InheritSSHOptions"] = connectionInfo.Inheritance.SSHOptions;
|
||||
dataRow["InheritPuttySession"] = connectionInfo.Inheritance.PuttySession;
|
||||
dataRow["InheritRedirectDiskDrives"] = connectionInfo.Inheritance.RedirectDiskDrives;
|
||||
dataRow["InheritRedirectKeys"] = connectionInfo.Inheritance.RedirectKeys;
|
||||
@@ -631,6 +639,8 @@ namespace mRemoteNG.Config.Serializers.MsSql
|
||||
dataRow["InheritPassword"] = false;
|
||||
dataRow["InheritPort"] = false;
|
||||
dataRow["InheritProtocol"] = false;
|
||||
dataRow["InheritSSHTunnelConnectionName"] = false;
|
||||
dataRow["InheritSSHOptions"] = false;
|
||||
dataRow["InheritPuttySession"] = false;
|
||||
dataRow["InheritRedirectDiskDrives"] = false;
|
||||
dataRow["InheritRedirectKeys"] = false;
|
||||
|
||||
@@ -68,6 +68,8 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
|
||||
element.Add(new XAttribute("Hostname", connectionInfo.Hostname));
|
||||
element.Add(new XAttribute("Protocol", connectionInfo.Protocol));
|
||||
element.Add(new XAttribute("SSHTunnelConnectionName", connectionInfo.SSHTunnelConnectionName));
|
||||
element.Add(new XAttribute("SSHOptions", connectionInfo.SSHOptions));
|
||||
element.Add(new XAttribute("PuttySession", connectionInfo.PuttySession));
|
||||
element.Add(new XAttribute("Port", connectionInfo.Port));
|
||||
element.Add(new XAttribute("ConnectToConsole",
|
||||
@@ -185,6 +187,10 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
connectionInfo.Inheritance.Port.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritProtocol",
|
||||
connectionInfo.Inheritance.Protocol.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritSSHTunnelConnectionName",
|
||||
connectionInfo.Inheritance.SSHTunnelConnectionName.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritSSHOptions",
|
||||
connectionInfo.Inheritance.SSHOptions.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritPuttySession",
|
||||
connectionInfo.Inheritance.PuttySession.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritRedirectDiskDrives",
|
||||
@@ -293,6 +299,8 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
element.Add(new XAttribute("InheritPassword", falseString));
|
||||
element.Add(new XAttribute("InheritPort", falseString));
|
||||
element.Add(new XAttribute("InheritProtocol", falseString));
|
||||
element.Add(new XAttribute("InheritSSHTunnelConnectionName", falseString));
|
||||
element.Add(new XAttribute("InheritSSHOptions", falseString));
|
||||
element.Add(new XAttribute("InheritPuttySession", falseString));
|
||||
element.Add(new XAttribute("InheritRedirectDiskDrives", falseString));
|
||||
element.Add(new XAttribute("InheritRedirectKeys", falseString));
|
||||
|
||||
@@ -76,6 +76,8 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
element.Add(new XAttribute("Hostname", connectionInfo.Hostname));
|
||||
element.Add(new XAttribute("Protocol", connectionInfo.Protocol));
|
||||
element.Add(new XAttribute("RdpVersion", connectionInfo.RdpVersion.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("SSHTunnelConnectionName", connectionInfo.SSHTunnelConnectionName));
|
||||
element.Add(new XAttribute("SSHOptions", connectionInfo.SSHOptions));
|
||||
element.Add(new XAttribute("PuttySession", connectionInfo.PuttySession));
|
||||
element.Add(new XAttribute("Port", connectionInfo.Port));
|
||||
element.Add(new XAttribute("ConnectToConsole",
|
||||
@@ -197,6 +199,10 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
connectionInfo.Inheritance.Protocol.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritRdpVersion",
|
||||
connectionInfo.Inheritance.RdpVersion.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritSSHTunnelConnectionName",
|
||||
connectionInfo.Inheritance.SSHTunnelConnectionName.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritSSHOptions",
|
||||
connectionInfo.Inheritance.SSHOptions.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritPuttySession",
|
||||
connectionInfo.Inheritance.PuttySession.ToString().ToLowerInvariant()));
|
||||
element.Add(new XAttribute("InheritRedirectDiskDrives",
|
||||
@@ -316,6 +322,8 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
element.Add(new XAttribute("InheritPassword", falseString));
|
||||
element.Add(new XAttribute("InheritPort", falseString));
|
||||
element.Add(new XAttribute("InheritProtocol", falseString));
|
||||
element.Add(new XAttribute("InheritSSHTunnelConnectionName", falseString));
|
||||
element.Add(new XAttribute("InheritSSHOptions", falseString));
|
||||
element.Add(new XAttribute("InheritPuttySession", falseString));
|
||||
element.Add(new XAttribute("InheritRedirectDiskDrives", falseString));
|
||||
element.Add(new XAttribute("InheritRedirectKeys", falseString));
|
||||
|
||||
@@ -533,8 +533,7 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
xmlnode.GetAttributeAsBool("InheritRDPMinutesToIdleTimeout");
|
||||
connectionInfo.RDPAlertIdleTimeout = xmlnode.GetAttributeAsBool("RDPAlertIdleTimeout");
|
||||
connectionInfo.Inheritance.RDPAlertIdleTimeout =
|
||||
xmlnode.GetAttributeAsBool("InheritRDPAlertIdleTimeout");
|
||||
}
|
||||
xmlnode.GetAttributeAsBool("InheritRDPAlertIdleTimeout"); }
|
||||
|
||||
if (_confVersion >= 2.7)
|
||||
{
|
||||
@@ -543,13 +542,17 @@ namespace mRemoteNG.Config.Serializers.Xml
|
||||
connectionInfo.UseVmId = xmlnode.GetAttributeAsBool("UseVmId");
|
||||
connectionInfo.VmId = xmlnode.GetAttributeAsString("VmId");
|
||||
connectionInfo.UseEnhancedMode = xmlnode.GetAttributeAsBool("UseEnhancedMode");
|
||||
connectionInfo.RdpVersion = xmlnode.GetAttributeAsEnum("RdpVersion", RdpVersion.Highest);
|
||||
connectionInfo.SSHTunnelConnectionName = xmlnode.GetAttributeAsString("SSHTunnelConnectionName");
|
||||
connectionInfo.SSHOptions = xmlnode.GetAttributeAsString("SSHOptions");
|
||||
connectionInfo.Inheritance.RedirectClipboard = xmlnode.GetAttributeAsBool("InheritRedirectClipboard");
|
||||
connectionInfo.Inheritance.Favorite = xmlnode.GetAttributeAsBool("InheritFavorite");
|
||||
connectionInfo.RdpVersion = xmlnode.GetAttributeAsEnum("RdpVersion", RdpVersion.Highest);
|
||||
connectionInfo.Inheritance.RdpVersion = xmlnode.GetAttributeAsBool("InheritRdpVersion");
|
||||
connectionInfo.Inheritance.UseVmId = xmlnode.GetAttributeAsBool("InheritUseVmId");
|
||||
connectionInfo.Inheritance.VmId = xmlnode.GetAttributeAsBool("InheritVmId");
|
||||
connectionInfo.Inheritance.UseEnhancedMode = xmlnode.GetAttributeAsBool("InheritUseEnhancedMode");
|
||||
connectionInfo.Inheritance.SSHTunnelConnectionName = xmlnode.GetAttributeAsBool("InheritSSHTunnelConnectionName");
|
||||
connectionInfo.Inheritance.SSHOptions = xmlnode.GetAttributeAsBool("InheritSSHOptions");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -27,11 +27,15 @@ namespace mRemoteNG.Config.Serializers.Versioning
|
||||
ALTER TABLE tblCons
|
||||
ADD RedirectClipboard bit NOT NULL DEFAULT 0,
|
||||
InheritRedirectClipboard bit NOT NULL DEFAULT 0,
|
||||
VmId varchar NOT NULL DEFAULT 0,
|
||||
VmId varchar NOT NULL DEFAULT '',
|
||||
UseVmId bit NOT NULL DEFAULT 0,
|
||||
UseEnhancedMode bit NOT NULL DEFAULT 0,
|
||||
InheritVmId bit NOT NULL DEFAULT 0,
|
||||
InheritUseVmId bit NOT NULL DEFAULT 0,
|
||||
SSHTunnelConnectionName varchar NOT NULL DEFAULT '',
|
||||
InheritSSHTunnelConnectionName bit NOT NULL DEFAULT 0,
|
||||
SSHOptions varchar NOT NULL DEFAULT '',
|
||||
InheritSSHOptions bit NOT NULL DEFAULT 0,
|
||||
InheritUseEnhancedMode bit NOT NULL DEFAULT 0;
|
||||
UPDATE tblRoot
|
||||
SET ConfVersion='2.7'";
|
||||
|
||||
@@ -27,10 +27,12 @@ namespace mRemoteNG.Connection
|
||||
private string _vmId = "";
|
||||
private bool _useEnhancedMode;
|
||||
|
||||
private string _sshTunnelConnectionName = "";
|
||||
private ProtocolType _protocol;
|
||||
private RdpVersion _rdpProtocolVersion;
|
||||
private string _extApp;
|
||||
private int _port;
|
||||
private string _sshOptions = "";
|
||||
private string _puttySession;
|
||||
private IcaProtocol.EncryptionStrength _icaEncryption;
|
||||
private bool _useConsoleSession;
|
||||
@@ -194,6 +196,16 @@ namespace mRemoteNG.Connection
|
||||
set => SetField(ref _vmId, value?.Trim(), "VmId");
|
||||
}
|
||||
|
||||
[LocalizedAttributes.LocalizedCategory(nameof(Language.strCategoryConnection), 2),
|
||||
LocalizedAttributes.LocalizedDisplayName(nameof(Language.strPropertyNameSSHTunnelConnection)),
|
||||
LocalizedAttributes.LocalizedDescription(nameof(Language.strPropertyDescriptionSSHTunnelConnection)),
|
||||
TypeConverter(typeof(SshTunnelTypeConverter)),
|
||||
UsedInAllProtocolsExcept()]
|
||||
public string SSHTunnelConnectionName
|
||||
{
|
||||
get => GetPropertyValue("SSHTunnelConnectionName", _sshTunnelConnectionName).Trim();
|
||||
set => SetField(ref _sshTunnelConnectionName, value?.Trim(), "SSHTunnelConnectionName");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protocol
|
||||
@@ -242,6 +254,16 @@ namespace mRemoteNG.Connection
|
||||
set => SetField(ref _puttySession, value, "PuttySession");
|
||||
}
|
||||
|
||||
[LocalizedAttributes.LocalizedCategory(nameof(Language.strCategoryProtocol), 3),
|
||||
LocalizedAttributes.LocalizedDisplayName(nameof(Language.strPropertyNameSSHOptions)),
|
||||
LocalizedAttributes.LocalizedDescription(nameof(Language.strPropertyDescriptionSSHOptions)),
|
||||
UsedInProtocol(ProtocolType.SSH1, ProtocolType.SSH2)]
|
||||
public virtual string SSHOptions
|
||||
{
|
||||
get => GetPropertyValue("SSHOptions", _sshOptions);
|
||||
set => SetField(ref _sshOptions, value, "SSHOptions");
|
||||
}
|
||||
|
||||
[LocalizedAttributes.LocalizedCategory(nameof(Language.strCategoryProtocol), 3),
|
||||
LocalizedAttributes.LocalizedDisplayName(nameof(Language.strPropertyNameEncryptionStrength)),
|
||||
LocalizedAttributes.LocalizedDescription(nameof(Language.strPropertyDescriptionEncryptionStrength)),
|
||||
|
||||
@@ -83,6 +83,13 @@ namespace mRemoteNG.Connection
|
||||
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.strPropertyDescriptionPort)),
|
||||
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
|
||||
public bool Port { get; set; }
|
||||
|
||||
[LocalizedAttributes.LocalizedCategory(nameof(Language.strCategoryConnection), 3),
|
||||
LocalizedAttributes.LocalizedDisplayNameInheritAttribute(nameof(Language.strPropertyNameSSHTunnelConnection)),
|
||||
LocalizedAttributes.LocalizedDescriptionInheritAttribute(nameof(Language.strPropertyDescriptionSSHTunnelConnection)),
|
||||
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
|
||||
[Browsable(true)]
|
||||
public bool SSHTunnelConnectionName { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -112,6 +119,12 @@ namespace mRemoteNG.Connection
|
||||
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
|
||||
public bool PuttySession { get; set; }
|
||||
|
||||
[LocalizedAttributes.LocalizedCategory(nameof(Language.strCategoryProtocol), 4),
|
||||
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.strPropertyNameSSHOptions)),
|
||||
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.strPropertyDescriptionSSHOptions)),
|
||||
TypeConverter(typeof(MiscTools.YesNoTypeConverter))]
|
||||
public bool SSHOptions { get; set; }
|
||||
|
||||
[LocalizedAttributes.LocalizedCategory(nameof(Language.strCategoryProtocol), 4),
|
||||
LocalizedAttributes.LocalizedDisplayNameInherit(nameof(Language.strPropertyNameEncryptionStrength)),
|
||||
LocalizedAttributes.LocalizedDescriptionInherit(nameof(Language.strPropertyDescriptionEncryptionStrength)),
|
||||
|
||||
@@ -5,6 +5,8 @@ using mRemoteNG.App;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using mRemoteNG.Container;
|
||||
using mRemoteNG.Messages;
|
||||
using mRemoteNG.Tools;
|
||||
using mRemoteNG.Tree;
|
||||
using mRemoteNG.UI.Forms;
|
||||
using mRemoteNG.UI.Panels;
|
||||
using mRemoteNG.UI.Tabs;
|
||||
@@ -49,7 +51,8 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenConnection(
|
||||
// async is necessary so UI can update while OpenConnection waits for tunnel connection to get ready in case of connection through SSH tunnel
|
||||
public async void OpenConnection(
|
||||
ConnectionInfo connectionInfo,
|
||||
ConnectionInfo.Force force = ConnectionInfo.Force.None,
|
||||
ConnectionWindow conForm = null)
|
||||
@@ -75,15 +78,137 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
|
||||
var protocolFactory = new ProtocolFactory();
|
||||
var newProtocol = protocolFactory.CreateProtocol(connectionInfo);
|
||||
|
||||
var connectionPanel = SetConnectionPanel(connectionInfo, force);
|
||||
if (string.IsNullOrEmpty(connectionPanel)) return;
|
||||
var connectionForm = SetConnectionForm(conForm, connectionPanel);
|
||||
var connectionContainer = SetConnectionContainer(connectionInfo, connectionForm);
|
||||
Control connectionContainer = null;
|
||||
|
||||
// Handle connection through SSH tunnel:
|
||||
// in case of connection through SSH tunnel, connectionInfo gets cloned, so that modification of its name, hostname and port do not modify the original connection info
|
||||
// connectionInfoOriginal points to the original connection info in either case, for where its needed later on.
|
||||
var connectionInfoOriginal = connectionInfo;
|
||||
ConnectionInfo connectionInfoSshTunnel = null; // SSH tunnel connection info will be set if SSH tunnel connection is configured, can be found and connected.
|
||||
if (!string.IsNullOrEmpty(connectionInfoOriginal.SSHTunnelConnectionName))
|
||||
{
|
||||
// Find the connection info specified as SSH tunnel in the connections tree
|
||||
connectionInfoSshTunnel = getSSHConnectionInfoByName(Runtime.ConnectionsService.ConnectionTreeModel.RootNodes, connectionInfoOriginal.SSHTunnelConnectionName);
|
||||
if (connectionInfoSshTunnel == null)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
string.Format(Language.strSSHTunnelConfigProblem, connectionInfoOriginal.Name, connectionInfoOriginal.SSHTunnelConnectionName));
|
||||
return;
|
||||
}
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
$"SSH Tunnel connection '{connectionInfoOriginal.SSHTunnelConnectionName}' configured for '{connectionInfoOriginal.Name}' found. Finding free local port for use as local tunnel port ...");
|
||||
// determine a free local port to use as local tunnel port
|
||||
var l = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0);
|
||||
l.Start();
|
||||
var localSshTunnelPort = ((System.Net.IPEndPoint)l.LocalEndpoint).Port;
|
||||
l.Stop();
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
$"{localSshTunnelPort} will be used as local tunnel port. Establishing SSH connection to '{connectionInfoSshTunnel.Hostname}' with additional tunnel options for target connection ...");
|
||||
|
||||
// clone SSH tunnel connection as tunnel options will be added to it, and those changes shall not be saved to the configuration
|
||||
connectionInfoSshTunnel = connectionInfoSshTunnel.Clone();
|
||||
connectionInfoSshTunnel.SSHOptions += " -L " + localSshTunnelPort + ":" + connectionInfoOriginal.Hostname + ":" + connectionInfoOriginal.Port;
|
||||
|
||||
// clone target connection info as its hostname will be changed to localhost and port to local tunnel port to establish connection through tunnel, and those changes shall not be saved to the configuration
|
||||
connectionInfo = connectionInfoOriginal.Clone();
|
||||
connectionInfo.Name += " via " + connectionInfoSshTunnel.Name;
|
||||
connectionInfo.Hostname = "localhost";
|
||||
connectionInfo.Port = localSshTunnelPort;
|
||||
|
||||
// connect the SSH connection to setup the tunnel
|
||||
var protocolSshTunnel = protocolFactory.CreateProtocol(connectionInfoSshTunnel);
|
||||
if (!(protocolSshTunnel is PuttyBase puttyBaseSshTunnel))
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
string.Format(Language.strSSHTunnelIsNotPutty, connectionInfoOriginal.Name, connectionInfoSshTunnel.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
SetConnectionFormEventHandlers(protocolSshTunnel, connectionForm);
|
||||
SetConnectionEventHandlers(protocolSshTunnel);
|
||||
connectionContainer = SetConnectionContainer(connectionInfo, connectionForm);
|
||||
BuildConnectionInterfaceController(connectionInfoSshTunnel, protocolSshTunnel, connectionContainer);
|
||||
protocolSshTunnel.InterfaceControl.OriginalInfo = connectionInfoSshTunnel;
|
||||
|
||||
if (protocolSshTunnel.Initialize() == false)
|
||||
{
|
||||
protocolSshTunnel.Close();
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
string.Format(Language.strSSHTunnelNotInitialized, connectionInfoOriginal.Name, connectionInfoSshTunnel.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (protocolSshTunnel.Connect() == false)
|
||||
{
|
||||
protocolSshTunnel.Close();
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
string.Format(Language.strSSHTunnelNotConnected, connectionInfoOriginal.Name, connectionInfoSshTunnel.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
"Putty started for SSH connection for tunnel. Waiting for local tunnel port to become available ...");
|
||||
|
||||
// wait until SSH tunnel connection is ready, by checking if local port can be connected to, but max 60 sec.
|
||||
var testsock = new System.Net.Sockets.Socket(System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
while (stopwatch.ElapsedMilliseconds < 60000)
|
||||
{
|
||||
// confirm that SSH connection is still active
|
||||
// works only if putty is connfigured to always close window on exit
|
||||
// else, if connection attempt fails, window remains open and putty process remains running, and we cannot know that connection is already doomed
|
||||
// in this case the timeout will expire and the log message below will be created
|
||||
// awkward for user as he has already acknowledged the putty popup some seconds again when the below notification comes....
|
||||
if (!puttyBaseSshTunnel.isRunning())
|
||||
{
|
||||
protocolSshTunnel.Close();
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
string.Format(Language.strSSHTunnelFailed, connectionInfoOriginal.Name, connectionInfoSshTunnel.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
testsock.Connect(System.Net.IPAddress.Loopback, localSshTunnelPort);
|
||||
testsock.Close();
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await System.Threading.Tasks.Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
if (stopwatch.ElapsedMilliseconds >= 60000)
|
||||
{
|
||||
protocolSshTunnel.Close();
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
string.Format(Language.strSSHTunnelPortNotReadyInTime, connectionInfoOriginal.Name, connectionInfoSshTunnel.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
"Local tunnel port is now available. Hiding putty display and setting up target connection via local tunnel port ...");
|
||||
|
||||
// hide the display of the SSH tunnel connection which has been shown until this time, such that password can be entered if required or errors be seen
|
||||
// it stays invisible in the container however which will be reused for the actual connection and such that if the container is closed the SSH tunnel connection is closed as well
|
||||
protocolSshTunnel.InterfaceControl.Hide();
|
||||
}
|
||||
|
||||
var newProtocol = protocolFactory.CreateProtocol(connectionInfo);
|
||||
SetConnectionFormEventHandlers(newProtocol, connectionForm);
|
||||
SetConnectionEventHandlers(newProtocol);
|
||||
// in case of connection through SSH tunnel the container is already defined and must be use, else it needs to be created here
|
||||
if (connectionContainer == null) connectionContainer = SetConnectionContainer(connectionInfo, connectionForm);
|
||||
BuildConnectionInterfaceController(connectionInfo, newProtocol, connectionContainer);
|
||||
// in case of connection through SSH tunnel the connectionInfo was modified but connectionInfoOriginal in all cases retains the original info
|
||||
// and is stored in interface control for further use
|
||||
newProtocol.InterfaceControl.OriginalInfo = connectionInfoOriginal;
|
||||
// SSH tunnel connection is stored in Interface Control to be used in log messages etc
|
||||
newProtocol.InterfaceControl.SSHTunnelInfo = connectionInfoSshTunnel;
|
||||
|
||||
newProtocol.Force = force;
|
||||
|
||||
@@ -99,7 +224,7 @@ namespace mRemoteNG.Connection
|
||||
return;
|
||||
}
|
||||
|
||||
connectionInfo.OpenConnections.Add(newProtocol);
|
||||
connectionInfoOriginal.OpenConnections.Add(newProtocol);
|
||||
_activeConnections.Add(connectionInfo.ConstantID);
|
||||
FrmMain.Default.SelectedConnection = connectionInfo;
|
||||
}
|
||||
@@ -109,6 +234,25 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
}
|
||||
|
||||
// recursively traverse the tree to find ConnectionInfo of a specific name
|
||||
private ConnectionInfo getSSHConnectionInfoByName(IEnumerable<ConnectionInfo> rootnodes, string SSHTunnelConnectionName)
|
||||
{
|
||||
ConnectionInfo result = null;
|
||||
foreach (var node in rootnodes)
|
||||
{
|
||||
if (node is ContainerInfo container)
|
||||
{
|
||||
result = getSSHConnectionInfoByName(container.Children, SSHTunnelConnectionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node.Name == SSHTunnelConnectionName && (node.Protocol == ProtocolType.SSH1 || node.Protocol == ProtocolType.SSH2)) result = node;
|
||||
}
|
||||
if (result != null) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#region Private
|
||||
private static void StartPreConnectionExternalApp(ConnectionInfo connectionInfo)
|
||||
{
|
||||
@@ -132,7 +276,7 @@ namespace mRemoteNG.Connection
|
||||
var tab = (ConnectionTab)dockContent;
|
||||
var ic = InterfaceControl.FindInterfaceControl(tab);
|
||||
if (ic == null) continue;
|
||||
if (ic.Info == connectionInfo)
|
||||
if (ic.Info == connectionInfo || ic.OriginalInfo == connectionInfo)
|
||||
return ic;
|
||||
}
|
||||
}
|
||||
@@ -221,11 +365,16 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
}
|
||||
|
||||
var strHostname = prot.InterfaceControl.OriginalInfo.Hostname;
|
||||
if (prot.InterfaceControl.SSHTunnelInfo != null)
|
||||
{
|
||||
strHostname += " via SSH Tunnel " + prot.InterfaceControl.SSHTunnelInfo.Name;
|
||||
}
|
||||
Runtime.MessageCollector.AddMessage(msgClass,
|
||||
string.Format(
|
||||
Language.strProtocolEventDisconnected,
|
||||
disconnectedMessage,
|
||||
prot.InterfaceControl.Info.Hostname,
|
||||
strHostname,
|
||||
prot.InterfaceControl.Info.Protocol.ToString()));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -242,11 +391,11 @@ namespace mRemoteNG.Connection
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.strConnenctionCloseEvent,
|
||||
true);
|
||||
string connDetail;
|
||||
if (prot.InterfaceControl.Info.Hostname == "" &&
|
||||
if (prot.InterfaceControl.OriginalInfo.Hostname == "" &&
|
||||
prot.InterfaceControl.Info.Protocol == ProtocolType.IntApp)
|
||||
connDetail = prot.InterfaceControl.Info.ExtApp;
|
||||
else if (prot.InterfaceControl.Info.Hostname != "")
|
||||
connDetail = prot.InterfaceControl.Info.Hostname;
|
||||
else if (prot.InterfaceControl.OriginalInfo.Hostname != "")
|
||||
connDetail = prot.InterfaceControl.OriginalInfo.Hostname;
|
||||
else
|
||||
connDetail = "UNKNOWN";
|
||||
|
||||
@@ -254,13 +403,13 @@ namespace mRemoteNG.Connection
|
||||
string.Format(Language.strConnenctionClosedByUser, connDetail,
|
||||
prot.InterfaceControl.Info.Protocol,
|
||||
Environment.UserName));
|
||||
prot.InterfaceControl.Info.OpenConnections.Remove(prot);
|
||||
prot.InterfaceControl.OriginalInfo.OpenConnections.Remove(prot);
|
||||
if (_activeConnections.Contains(prot.InterfaceControl.Info.ConstantID))
|
||||
_activeConnections.Remove(prot.InterfaceControl.Info.ConstantID);
|
||||
|
||||
if (prot.InterfaceControl.Info.PostExtApp == "") return;
|
||||
var extA = Runtime.ExternalToolsService.GetExtAppByName(prot.InterfaceControl.Info.PostExtApp);
|
||||
extA?.Start(prot.InterfaceControl.Info);
|
||||
extA?.Start(prot.InterfaceControl.OriginalInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -275,7 +424,7 @@ namespace mRemoteNG.Connection
|
||||
true);
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
|
||||
string.Format(Language.strConnectionEventConnectedDetail,
|
||||
prot.InterfaceControl.Info.Hostname,
|
||||
prot.InterfaceControl.OriginalInfo.Hostname,
|
||||
prot.InterfaceControl.Info.Protocol, Environment.UserName,
|
||||
prot.InterfaceControl.Info.Description,
|
||||
prot.InterfaceControl.Info.UserField));
|
||||
@@ -290,7 +439,7 @@ namespace mRemoteNG.Connection
|
||||
var msg = string.Format(
|
||||
Language.strConnectionEventErrorOccured,
|
||||
errorMessage,
|
||||
prot.InterfaceControl.Info.Hostname,
|
||||
prot.InterfaceControl.OriginalInfo.Hostname,
|
||||
errorCode?.ToString() ?? "-");
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg, msg);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using mRemoteNG.App;
|
||||
using mRemoteNG.App;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
@@ -13,6 +13,11 @@ namespace mRemoteNG.Connection
|
||||
{
|
||||
public ProtocolBase Protocol { get; set; }
|
||||
public ConnectionInfo Info { get; set; }
|
||||
// in case the connection is through a SSH tunnel the Info is a copy of original info with hostname and port number overwritten with localhost and local tunnel port
|
||||
// and the original Info is saved in the following variable
|
||||
public ConnectionInfo OriginalInfo { get; set; }
|
||||
// in case the connection is through a SSH tunnel the Info of the SSHTunnelConnection is also saved for reference in log messages etc.
|
||||
public ConnectionInfo SSHTunnelInfo { get; set; }
|
||||
|
||||
|
||||
public InterfaceControl(Control parent, ProtocolBase protocol, ConnectionInfo info)
|
||||
@@ -37,19 +42,24 @@ namespace mRemoteNG.Connection
|
||||
|
||||
public static InterfaceControl FindInterfaceControl(DockPanel DockPnl)
|
||||
{
|
||||
if (!(DockPnl.ActiveDocument is ConnectionTab ct)) return null;
|
||||
if (ct.Controls.Count < 1) return null;
|
||||
if (ct.Controls[0] is InterfaceControl ic)
|
||||
return ic;
|
||||
|
||||
// instead of repeating the code, call the routine using ConnectionTab if called by DockPanel
|
||||
if (DockPnl.ActiveDocument is ConnectionTab ct)
|
||||
return FindInterfaceControl(ct);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static InterfaceControl FindInterfaceControl(ConnectionTab tab)
|
||||
{
|
||||
if (tab.Controls.Count < 1) return null;
|
||||
if (tab.Controls[0] is InterfaceControl ic)
|
||||
return ic;
|
||||
// if the tab has more than one controls and the second is an InterfaceControl than it must be a connection through SSH tunnel
|
||||
// and the first Control is the SSH tunnel connection and thus the second control must be returned.
|
||||
if (tab.Controls.Count > 1)
|
||||
{
|
||||
if (tab.Controls[1] is InterfaceControl ic1)
|
||||
return ic1;
|
||||
}
|
||||
if (tab.Controls[0] is InterfaceControl ic0)
|
||||
return ic0;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,11 @@ namespace mRemoteNG.Connection.Protocol
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public bool isRunning()
|
||||
{
|
||||
return !PuttyProcess.HasExited;
|
||||
}
|
||||
|
||||
public override bool Connect()
|
||||
{
|
||||
try
|
||||
@@ -136,6 +141,11 @@ namespace mRemoteNG.Connection.Protocol
|
||||
}
|
||||
|
||||
PuttyProcess.StartInfo.Arguments = arguments.ToString();
|
||||
// add additional SSH options, f.e. tunnel or noshell parameters that may be specified for the the connnection
|
||||
if (!string.IsNullOrEmpty(InterfaceControl.Info.SSHOptions))
|
||||
{
|
||||
PuttyProcess.StartInfo.Arguments += " " + InterfaceControl.Info.SSHOptions;
|
||||
}
|
||||
|
||||
PuttyProcess.EnableRaisingEvents = true;
|
||||
PuttyProcess.Exited += ProcessExited;
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
.. HowTo - Jump server / Bastion host with mRemoteNG
|
||||
|
||||
.. Need more information here to explain and work with hosts for jumps
|
||||
|
||||
**************************
|
||||
Jump server / Bastion host
|
||||
**************************
|
||||
|
||||
Introduction
|
||||
============
|
||||
This document will not go into details on what a bastion host or a jump server really is. Instead
|
||||
it will give you a howto for setting up the hosts so you can do the jump with mRemoteNG. If you need
|
||||
more information regarding the function of bastion host and jump server then see links mentioned below.
|
||||
|
||||
.. note::
|
||||
|
||||
The information below could probably be a lot better. If you have a better idea or easier
|
||||
way to work with bastion host and jump server, then please let us know.
|
||||
|
||||
References
|
||||
==========
|
||||
- `Wikipedia Bastion host <https://en.wikipedia.org/wiki/Bastion_host>`_
|
||||
- `Wikipedia Jump server <https://en.wikipedia.org/wiki/Jump_server>`_
|
||||
|
||||
Linux Server to target host
|
||||
===========================
|
||||
In this section we will use a Ubuntu 18.04 LTS to jump to another host both RDP and SSH.
|
||||
|
||||
Windows Server to target host
|
||||
=============================
|
||||
In this section we will use a Windows 2016 Server to jump to another host both RDP and SSH.
|
||||
13
mRemoteV1/Documentation/howtos/sshtunnel.rst
Normal file
13
mRemoteV1/Documentation/howtos/sshtunnel.rst
Normal file
@@ -0,0 +1,13 @@
|
||||
*************
|
||||
SSH Tunneling
|
||||
*************
|
||||
|
||||
You can use any configured SSH connection to be used as a tunnel server for another connection.
|
||||
|
||||
.. figure:: /images/ssh_tunnel.png
|
||||
|
||||
If an SSH Tunnel is configured the connection is searched and if found a free local TCP port determined. The SSH tunnel connection
|
||||
is setup with additional parameters for the tunnel. The original connection info is copied and the copy is modified to connect
|
||||
to local host and the local TCP port and the target connection is opened.
|
||||
You can use the SSH connection attribute for additional SSH options. It can be used by all
|
||||
normal SSH connections as well to specify any additional options, e.g. to not start a shell which some SSH servers.
|
||||
BIN
mRemoteV1/Documentation/images/ssh_tunnel.png
Normal file
BIN
mRemoteV1/Documentation/images/ssh_tunnel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@@ -32,7 +32,7 @@ Welcome to mRemoteNG's documentation!
|
||||
:maxdepth: 2
|
||||
:caption: HowTos
|
||||
|
||||
howtos/jumpservers.rst
|
||||
howtos/sshtunnel.rst
|
||||
howtos/external_tools.rst
|
||||
howtos/bulk_connections.rst
|
||||
howtos/vmrdp.rst
|
||||
|
||||
BIN
mRemoteV1/Icons/Admin.ico
Normal file
BIN
mRemoteV1/Icons/Admin.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
mRemoteV1/Icons/Infrastructure.ico
Normal file
BIN
mRemoteV1/Icons/Infrastructure.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
mRemoteV1/Icons/Production.ico
Normal file
BIN
mRemoteV1/Icons/Production.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
mRemoteV1/Icons/Staging.ico
Normal file
BIN
mRemoteV1/Icons/Staging.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
48
mRemoteV1/Properties/Settings.Designer.cs
generated
48
mRemoteV1/Properties/Settings.Designer.cs
generated
@@ -2927,6 +2927,54 @@ namespace mRemoteNG {
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool InhDefaultSSHTunnelConnectionName {
|
||||
get {
|
||||
return ((bool)(this["InhDefaultSSHTunnelConnectionName"]));
|
||||
}
|
||||
set {
|
||||
this["InhDefaultSSHTunnelConnectionName"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string ConDefaultSSHTunnelConnectionName {
|
||||
get {
|
||||
return ((string)(this["ConDefaultSSHTunnelConnectionName"]));
|
||||
}
|
||||
set {
|
||||
this["ConDefaultSSHTunnelConnectionName"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool InhDefaultSSHOptions {
|
||||
get {
|
||||
return ((bool)(this["InhDefaultSSHOptions"]));
|
||||
}
|
||||
set {
|
||||
this["InhDefaultSSHOptions"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string ConDefaultSSHOptions {
|
||||
get {
|
||||
return ((string)(this["ConDefaultSSHOptions"]));
|
||||
}
|
||||
set {
|
||||
this["ConDefaultSSHOptions"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
|
||||
@@ -728,6 +728,18 @@
|
||||
<Setting Name="InhDefaultRdpVersion" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="InhDefaultSSHTunnelConnectionName" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="ConDefaultSSHTunnelConnectionName" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="InhDefaultSSHOptions" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="ConDefaultSSHOptions" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="StartMinimized" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
|
||||
7286
mRemoteV1/Resources/Language/Language.Designer.cs
generated
7286
mRemoteV1/Resources/Language/Language.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -2678,6 +2678,30 @@ Development umfasst Alphas, Betas und Release Candidates.</value>
|
||||
<data name="strPropertyDescriptionRdpVersion" xml:space="preserve">
|
||||
<value>Legt die Version des RDP fest, die beim Öffnen von Verbindungen verwendet wird.</value>
|
||||
</data>
|
||||
<data name="strPropertyDescriptionSSHTunnelConnection" xml:space="preserve">
|
||||
<value>Zum Verbinden mittels eines SSH Tunnels (Jump Host) geben Sie hier den Namen der SSH Verbindung an, welche benutzt werden soll um den SSH Tunnel einzurichten.</value>
|
||||
</data>
|
||||
<data name="strPropertyDescriptionSSHOptions" xml:space="preserve">
|
||||
<value>Geben Sie hier zusaetzliche Optionen an welche fuer die SSH Verbindung verwendet werden sollen. Fuer weitere Infos zu den moeglichen Optionen konsultieren sie die Putty Dokumentation.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelConfigProblem" xml:space="preserve">
|
||||
<value>Konfigurationsfehler der Verbindung. Verbindung zu: "{0}" via SSH Tunnel: "{1}" nicht möglich. Eine Verbindung mit dem als SSH Tunnel konfiguriertem Namen konnte nicht gefunden werden. Löschen Sie die SSH Tunnel Konfiguration or geben sie eine existierende SSH Verbindung an, welche zum Aufbau eine Tunnels zum Erreichen des eigentlichen Zielsystems verwendet werden soll.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelIsNotPutty" xml:space="preserve">
|
||||
<value>Konfigurationsfehler des SSH Tunnel. Verbindung zu: "{0}" via SSH Tunnel: "{1}" nicht möglich. Die als SSH Tunnel konfigurierte Verbindung wurde zwar gefunden, aber das zugehörige Protokoll ist nicht von Putty abgeleitet. Stellen Sie sicher dass die als SSH Tunnel konfigurierte Verbindung das SSH version 1 oder 2 Protokoll verwendet.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelNotInitialized" xml:space="preserve">
|
||||
<value>Initialisierungsproblem des SSH Tunnel. Verbindung zu: "{0}" via SSH Tunnel: "{1}" nicht möglich. Initialisierung der SSH Verbindung fehlgeschlagen. Überprüfen Sie die als SSH Tunnel konfigurierte Verbindung auf etwaige Probleme.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelNotConnected" xml:space="preserve">
|
||||
<value>Verbindungsproblem des SSH Tunnel. Verbindung zu: "{0}" via SSH Tunnel: "{1}" nicht möglich. SSH Verbindungsaufbau fehlgeschlagen. Überprüfen Sie die als SSH Tunnel konfigurierte Verbindung auf etwaige Probleme.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelFailed" xml:space="preserve">
|
||||
<value>SSH Tunnel Verbindung fehlgeschlagen. Verbindung zu: "{0}" via SSH Tunnel: "{1}" nicht möglich. Putty Prozess vorzeitig beendet. Überprüfen Sie die als SSH Tunnel konfigurierte Verbindung auf etwaige Probleme.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelPortNotReadyInTime" xml:space="preserve">
|
||||
<value>Zeitüberschreitung des SSH Tunnel. Verbindung zu: "{0}" via SSH Tunnel: "{1}" nicht möglich. Lokaler Tunnel Port nicht vor Ablauf der Zeitüberschreitung verfügbar. Überprüfen Sie die als SSH Tunnel konfigurierte Verbindung auf etwaige Probleme.</value>
|
||||
</data>
|
||||
<data name="strStartMinimized" xml:space="preserve">
|
||||
<value>Minimiert starten</value>
|
||||
</data>
|
||||
|
||||
@@ -2813,6 +2813,36 @@ Development Channel includes Alphas, Betas & Release Candidates.</value>
|
||||
<data name="RdpProtocolVersionNotSupported" xml:space="preserve">
|
||||
<value>Could not create RDP client. RDP protocol version {0} is not supported on this machine. Please choose an older protocol version.</value>
|
||||
</data>
|
||||
<data name="strPropertyDescriptionSSHTunnelConnection" xml:space="preserve">
|
||||
<value>For connection through a SSH tunnel (jump host) specify SSH connection to be used to establish SSH tunnel.</value>
|
||||
</data>
|
||||
<data name="strPropertyNameSSHTunnelConnection" xml:space="preserve">
|
||||
<value>SSH Tunnel</value>
|
||||
</data>
|
||||
<data name="strPropertyDescriptionSSHOptions" xml:space="preserve">
|
||||
<value>Specify here additional options to be used for SSH connection. See putty documentation for further details.</value>
|
||||
</data>
|
||||
<data name="strPropertyNameSSHOptions" xml:space="preserve">
|
||||
<value>SSH Options</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelConfigProblem" xml:space="preserve">
|
||||
<value>Connection configuration problem. Connection to: "{0}" via SSH Tunnel: "{1}" not possible. A connection with the name configured as SSH Tunnel and protocol SSH version 1 or SSH2 version 2 cannot be found in the connection tree. Clear SSH Tunnel configuration or specify existing SSH connection.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelIsNotPutty" xml:space="preserve">
|
||||
<value>SSH tunnel configuration problem. Connection to: "{0}" via SSH Tunnel: "{1}" not possible. Connection configured as SSH Tunnel found in tree, but protocol is not derived from putty. Make sure connection configured as SSH Tunnel is using SSH protocol.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelNotInitialized" xml:space="preserve">
|
||||
<value>SSH tunnel initialization problem. Connection to: "{0}" via SSH Tunnel: "{1}" not possible. SSH connection could not be initialized. Check for any problems with the connection configured as SSH Tunnel.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelNotConnected" xml:space="preserve">
|
||||
<value>SSH tunnel connection problem. Connection to: "{0}" via SSH Tunnel: "{1}" not possible. SSH connection failed. Check for any problems with the connection configured as SSH Tunnel.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelFailed" xml:space="preserve">
|
||||
<value>SSH tunnel connection failed. Connection to: "{0}" via SSH Tunnel: "{1}" not possible. Putty process terminated. Check for any problems with the connection configured as SSH Tunnel.</value>
|
||||
</data>
|
||||
<data name="strSSHTunnelPortNotReadyInTime" xml:space="preserve">
|
||||
<value>SSH tunnel connection timed out. Connection to: "{0}" via SSH Tunnel: "{1}" not possible. Local tunnel port did not become available in time. Check for any problems with the connection configured as SSH Tunnel.</value>
|
||||
</data>
|
||||
<data name="strStartMinimized" xml:space="preserve">
|
||||
<value>Start minimized</value>
|
||||
</data>
|
||||
|
||||
@@ -36,18 +36,17 @@
|
||||
<xs:attribute name="Descr" type="xs:string" use="required" />
|
||||
<xs:attribute name="Icon" type="xs:string" use="required" />
|
||||
<xs:attribute name="Panel" type="xs:string" use="required" />
|
||||
|
||||
<!--<xs:attribute name="CredentialId" type="xs:string" use="required" />-->
|
||||
<xs:attribute name="Username" type="xs:string" use="required" />
|
||||
<xs:attribute name="Domain" type="xs:string" use="required" />
|
||||
<xs:attribute name="Password" type="xs:string" use="required" />
|
||||
|
||||
<xs:attribute name="Hostname" type="xs:string" use="required" />
|
||||
<xs:attribute name="Protocol" type="xs:string" use="required" />
|
||||
<xs:attribute name="RdpVersion" type="xs:string" use="required" />
|
||||
<xs:attribute name="VmId" type="xs:string" use="required" />
|
||||
<xs:attribute name="UseVmId" type="xs:boolean" use="required" />
|
||||
<xs:attribute name="UseEnhancedMode" type="xs:boolean" use="required" />
|
||||
<xs:attribute name="SSHTunnelConnectionName" type="xs:string" use="optional" />
|
||||
<xs:attribute name="SSHOptions" type="xs:string" use="optional" />
|
||||
<xs:attribute name="PuttySession" type="xs:string" use="required" />
|
||||
<xs:attribute name="Port" type="xs:int" use="required" />
|
||||
<xs:attribute name="ConnectToConsole" type="xs:boolean" use="required" />
|
||||
@@ -114,6 +113,8 @@
|
||||
<xs:attribute name="InheritPort" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritProtocol" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritRdpVersion" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritSSHTunnelConnectionName" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritSSHOptions" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritPuttySession" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritRedirectDiskDrives" type="xs:boolean" use="optional" />
|
||||
<xs:attribute name="InheritRedirectKeys" type="xs:boolean" use="optional" />
|
||||
|
||||
58
mRemoteV1/Tools/SSHTunnelTypeConverter.cs
Normal file
58
mRemoteV1/Tools/SSHTunnelTypeConverter.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using mRemoteNG.App;
|
||||
using mRemoteNG.Connection;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using mRemoteNG.Container;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace mRemoteNG.Tools
|
||||
{
|
||||
public class SshTunnelTypeConverter : StringConverter
|
||||
{
|
||||
public static string[] SshTunnels
|
||||
{
|
||||
get
|
||||
{
|
||||
var sshTunnelList = new List<string> {string.Empty};
|
||||
|
||||
// Add a blank entry to signify that no external tool is selected
|
||||
sshTunnelList.AddRange(GetSshConnectionNames(Runtime.ConnectionsService.ConnectionTreeModel.RootNodes));
|
||||
return sshTunnelList.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
// recursively traverse the connection tree to find all ConnectionInfo s of type SSH
|
||||
private static IEnumerable<string> GetSshConnectionNames(IEnumerable<ConnectionInfo> rootnodes)
|
||||
{
|
||||
var result = new List<string>();
|
||||
foreach (var node in rootnodes)
|
||||
if (node is ContainerInfo container)
|
||||
{
|
||||
result.AddRange(GetSshConnectionNames(container.Children));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node is PuttySessionInfo) continue;
|
||||
if (node.Protocol == ProtocolType.SSH1 || node.Protocol == ProtocolType.SSH2)
|
||||
result.Add(node.Name);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
|
||||
{
|
||||
return new StandardValuesCollection(SshTunnels);
|
||||
}
|
||||
|
||||
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,6 +753,18 @@
|
||||
<setting name="InhDefaultRdpVersion" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="InhDefaultSSHTunnelConnectionName" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="ConDefaultSSHTunnelConnectionName" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="InhDefaultSSHOptions" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
<setting name="ConDefaultSSHOptions" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="StartMinimized" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
|
||||
@@ -299,6 +299,11 @@
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Resources\Language\Language.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Language.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Resources\Themes\ColorMapTheme.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
@@ -375,6 +380,7 @@
|
||||
<Compile Include="Tools\Cmdline\CmdArgumentsInterpreter.cs" />
|
||||
<Compile Include="Tools\ConnectionsTreeToMenuItemsConverter.cs" />
|
||||
<Compile Include="Tools\ExternalToolsService.cs" />
|
||||
<Compile Include="Tools\SshTunnelTypeConverter.cs" />
|
||||
<Compile Include="Tools\ExternalToolsTypeConverter.cs" />
|
||||
<Compile Include="Tools\CustomCollections\INotifyCollectionUpdated.cs" />
|
||||
<Compile Include="Tools\Optional.cs" />
|
||||
@@ -686,11 +692,6 @@
|
||||
<Compile Include="UI\Forms\PasswordForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Resources\Language\Language.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Language.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Messages\Message.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
@@ -1026,7 +1027,7 @@
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<CustomToolNamespace>mRemoteNG</CustomToolNamespace>
|
||||
<SubType>Designer</SubType>
|
||||
<LastGenOutput>Language1.Designer.cs</LastGenOutput>
|
||||
<LastGenOutput>Language.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Resources\Language\Language.fr.resx">
|
||||
<SubType>Designer</SubType>
|
||||
@@ -1198,6 +1199,7 @@
|
||||
<None Include="Documentation\images\connections_status.png" />
|
||||
<None Include="Documentation\images\putty.png" />
|
||||
<Content Include="Console.ico" />
|
||||
<Content Include="Documentation\images\ssh_tunnel.png" />
|
||||
<Content Include="Documentation\mssql_db_setup.sql" />
|
||||
<Content Include="Documentation\mysql_db_setup.sql" />
|
||||
<None Include="Documentation\images\config_top_bar.png" />
|
||||
@@ -1219,6 +1221,7 @@
|
||||
<None Include="Documentation\images\connections_open.png" />
|
||||
<None Include="Documentation\images\screenshot_manager_rightclick_menu.png" />
|
||||
<None Include="Documentation\images\screenshot_manager_overview.png" />
|
||||
<Content Include="Icons\Admin.ico" />
|
||||
<Content Include="Firefox\AccessibleHandler.dll" />
|
||||
<Content Include="Firefox\AccessibleMarshal.dll" />
|
||||
<Content Include="Firefox\breakpadinjector.dll" />
|
||||
@@ -1286,6 +1289,15 @@
|
||||
<None Include="Resources\Images\tab_edit.png" />
|
||||
<None Include="Resources\Images\tab_delete.png" />
|
||||
<None Include="Resources\Images\star.png" />
|
||||
<Content Include="Icons\Infrastructure.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Icons\Production.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Icons\Staging.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Icons\PowerShell.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
@@ -1425,7 +1437,7 @@
|
||||
<None Include="Documentation\installation.rst" />
|
||||
<None Include="Documentation\installation\minimum_requirements.rst" />
|
||||
<None Include="Documentation\installation\uninstall.rst" />
|
||||
<None Include="Documentation\howtos\jumpservers.rst" />
|
||||
<None Include="Documentation\howtos\sshtunnel.rst" />
|
||||
<None Include="Documentation\user_interface.rst" />
|
||||
<None Include="Documentation\user_interface\main_window.rst" />
|
||||
<None Include="Documentation\user_interface\panels.rst" />
|
||||
|
||||
Reference in New Issue
Block a user