From 3cdf4a6fdba8b4f6945b64c67393a44178eed457 Mon Sep 17 00:00:00 2001 From: Brannen Hall Date: Mon, 25 May 2020 16:30:16 -0500 Subject: [PATCH 1/4] Add Options parameter to PSRP over SSH commands * Options parameter added to Invoke-Command * Options parameter added to Start-PSSession * Options parameter added to SSHConnection Hashtable --- .../remoting/commands/InvokeCommandCommand.cs | 20 +++++++++ .../remoting/commands/PSRemotingCmdlet.cs | 22 +++++++++- .../remoting/commands/newrunspacecommand.cs | 6 ++- .../remoting/common/RunspaceConnectionInfo.cs | 43 ++++++++++++++++++- test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 | 7 +++ 5 files changed, 93 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs index c86f7903a1a..5ffaf4a4f64 100644 --- a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs @@ -792,6 +792,26 @@ public override Hashtable[] SSHConnection set; } + /// + /// Hashtable containing options to be passed to OpenSSH. + /// + [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + [Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)] + [ValidateNotNullOrEmpty()] + public override Hashtable Options + { + get + { + return base.Options; + } + + set + { + base.Options = value; + } + } + + #endregion #region Remote Debug Parameters diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index d4f5ca427ab..e761d3d7df8 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -287,6 +287,7 @@ internal struct SSHConnection public int Port; public string Subsystem; public int ConnectingTimeout; + public Hashtable Options; } /// @@ -805,6 +806,20 @@ public virtual Hashtable[] SSHConnection set; } + /// + /// This parameter specifies the SSH subsystem to use for the remote connection. + /// + [Parameter(ValueFromPipelineByPropertyName = true, + ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + public virtual string Subsystem { get; set; } + + /// + /// Hashtable containing options to be passed to OpenSSH. + /// + [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] + [ValidateNotNullOrEmpty()] + public virtual Hashtable Options { get; set; } + #endregion #endregion Properties @@ -866,6 +881,7 @@ internal static void ValidateSpecifiedAuthentication(PSCredential credential, st private const string PortParameter = "Port"; private const string SubsystemParameter = "Subsystem"; private const string ConnectingTimeoutParameter = "ConnectingTimeout"; + private const string OptionsParameter = "Options"; #endregion @@ -969,6 +985,10 @@ internal SSHConnection[] ParseSSHConnectionHashTable() { connectionInfo.ConnectingTimeout = GetSSHConnectionIntParameter(item[paramName]); } + else if (paramName.Equals(OptionsParameter, StringComparison.OrdinalIgnoreCase)) + { + connectionInfo.Options = item[paramName] as Hashtable; + } else { throw new PSArgumentException( @@ -1462,7 +1482,7 @@ protected void CreateHelpersForSpecifiedSSHComputerNames() { ParseSshHostName(computerName, out string host, out string userName, out int port); - var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout); + var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout, Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, Host, typeTable) as RemoteRunspace; var pipeline = CreatePipeline(remoteRunspace); diff --git a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs index d4a0a3e7508..a9ba8007a2b 100644 --- a/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/newrunspacecommand.cs @@ -1093,7 +1093,8 @@ private List CreateRunspacesForSSHHostParameterSet() this.KeyFilePath, port, Subsystem, - ConnectingTimeout); + ConnectingTimeout, + Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); string rsName = GetRunspaceName(index, out int rsIdUnused); index++; @@ -1120,7 +1121,8 @@ private List CreateRunspacesForSSHHostHashParameterSet() sshConnection.KeyFilePath, sshConnection.Port, sshConnection.Subsystem, - sshConnection.ConnectingTimeout); + sshConnection.ConnectingTimeout, + sshConnection.Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); string rsName = GetRunspaceName(index, out int rsIdUnused); index++; diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index 04ce777964e..3b0a2a9ad5f 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System.Collections; using System.Collections.Generic; using System.ComponentModel; // Win32Exception using System.Diagnostics; @@ -1969,6 +1970,14 @@ public int ConnectingTimeout set; } + /// The SSH options to pass to OpenSSH. + /// + private Hashtable Options + { + get; + set; + } + #endregion #region Constructors @@ -2055,6 +2064,26 @@ public SSHConnectionInfo( ConnectingTimeout = connectingTimeout; } + /// + /// Constructor. + /// + /// User Name. + /// Computer Name. + /// Key File Path. + /// Port number for connection (default 22). + /// Subsystem to use (default 'powershell'). + /// Options for the SSH connection. + public SSHConnectionInfo( + string userName, + string computerName, + string keyFilePath, + int port, + string subsystem, + Hashtable options) : this(userName, computerName, keyFilePath, port, subsystem) + { + this.Options = options; + } + #endregion #region Overrides @@ -2111,6 +2140,7 @@ internal override RunspaceConnectionInfo InternalCopy() newCopy.Port = Port; newCopy.Subsystem = Subsystem; newCopy.ConnectingTimeout = ConnectingTimeout; + newCopy.Options = Options; return newCopy; } @@ -2163,9 +2193,9 @@ internal int StartSSHProcess( // // Local ssh invoked as: // windows: - // ssh.exe [-i identity_file] [-l login_name] [-p port] -s + // ssh.exe [-i identity_file] [-l login_name] [-p port] [-o option] -s // linux|macos: - // ssh [-i identity_file] [-l login_name] [-p port] -s + // ssh [-i identity_file] [-l login_name] [-p port] [-o option] -s // where is interpreted as the subsystem due to the -s flag. // // Remote sshd configured for PowerShell Remoting Protocol (PSRP) over Secure Shell Protocol (SSH) @@ -2216,6 +2246,15 @@ internal int StartSSHProcess( startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-p {0}", this.Port)); } + // pass "-o option=value" command line argument to ssh if options are provided + if (this.Options != null) + { + foreach (DictionaryEntry pair in this.Options) + { + startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-o {0}={1}", pair.Key, pair.Value)); + } + } + // pass "-s destination command" command line arguments to ssh where command is the subsystem to invoke on the destination // note that ssh expects IPv6 addresses to not be enclosed in square brackets so trim them if present startInfo.ArgumentList.Add(string.Format(CultureInfo.InvariantCulture, @"-s {0} {1}", this.ComputerName.TrimStart('[').TrimEnd(']'), this.Subsystem)); diff --git a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 index 7383cc2b8a1..29e7096d49a 100644 --- a/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 +++ b/test/SSHRemoting/SSHRemoting.Basic.Tests.ps1 @@ -179,6 +179,13 @@ Describe "SSHRemoting Basic Tests" -tags CI { Write-Verbose -Verbose "It Complete" } + It "Verifies explicit Options parameter" { + $options = @{"Port"="22"} + $script:session = New-PSSession -HostName localhost -Options $options -ErrorVariable err + $err | Should -HaveCount 0 + VerifySession $script:session + } + It "Verifies explicit Subsystem parameter" { Write-Verbose -Verbose "It Starting: Verifies explicit Subsystem parameter" $portNum = 22 From e26ba2a893e21bafa0e60db29697a2479b206af2 Mon Sep 17 00:00:00 2001 From: Brannen Date: Sat, 30 May 2020 20:28:37 +0000 Subject: [PATCH 2/4] Add Options parameter to Enter-PSSession --- .../remoting/commands/PushRunspaceCommand.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs b/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs index 996b043d77e..53dae350c06 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs @@ -58,6 +58,24 @@ public class EnterPSSessionCommand : PSRemotingBaseCmdlet [ValidateNotNullOrEmpty()] public new string HostName { get; set; } + /// + /// Host name for an SSH remote connection. + /// + [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] + [ValidateNotNullOrEmpty()] + public override Hashtable Options + { + get + { + return base.Options; + } + + set + { + base.Options = value; + } + } + #endregion /// @@ -1262,7 +1280,7 @@ private RemoteRunspace GetRunspaceForContainerSession() private RemoteRunspace GetRunspaceForSSHSession() { ParseSshHostName(HostName, out string host, out string userName, out int port); - var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout); + var sshConnectionInfo = new SSHConnectionInfo(userName, host, KeyFilePath, port, Subsystem, ConnectingTimeout, Options); var typeTable = TypeTable.LoadDefaultTypeFiles(); // Use the class _tempRunspace field while the runspace is being opened so that StopProcessing can be handled at that time. From 9ca1e59e0678d37495a17daa5105b9b683db4b6c Mon Sep 17 00:00:00 2001 From: Brannen Date: Sat, 30 May 2020 20:39:15 +0000 Subject: [PATCH 3/4] Fix CodeFactor style issues --- .../engine/remoting/commands/InvokeCommandCommand.cs | 3 +-- .../engine/remoting/commands/PSRemotingCmdlet.cs | 6 +++--- .../engine/remoting/commands/PushRunspaceCommand.cs | 4 ++-- .../engine/remoting/common/RunspaceConnectionInfo.cs | 11 ++++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs index 5ffaf4a4f64..4b75e3dc66c 100644 --- a/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/InvokeCommandCommand.cs @@ -797,7 +797,7 @@ public override Hashtable[] SSHConnection /// [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] [Parameter(ParameterSetName = InvokeCommandCommand.FilePathSSHHostParameterSet)] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] public override Hashtable Options { get @@ -811,7 +811,6 @@ public override Hashtable Options } } - #endregion #region Remote Debug Parameters diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index e761d3d7df8..e1bd27af628 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -812,12 +812,12 @@ public virtual Hashtable[] SSHConnection [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] public virtual string Subsystem { get; set; } - + /// - /// Hashtable containing options to be passed to OpenSSH. + /// Gets or sets the Hashtable containing options to be passed to OpenSSH. /// [Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] public virtual Hashtable Options { get; set; } #endregion diff --git a/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs b/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs index 53dae350c06..bf8041d2e48 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PushRunspaceCommand.cs @@ -59,10 +59,10 @@ public class EnterPSSessionCommand : PSRemotingBaseCmdlet public new string HostName { get; set; } /// - /// Host name for an SSH remote connection. + /// Gets or sets the Hashtable containing options to be passed to OpenSSH. /// [Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] public override Hashtable Options { get diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index 3b0a2a9ad5f..d5351a7ad71 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -1971,6 +1971,7 @@ public int ConnectingTimeout } /// The SSH options to pass to OpenSSH. + /// Gets or sets the SSH options to pass to OpenSSH. /// private Hashtable Options { @@ -1983,13 +1984,13 @@ private Hashtable Options #region Constructors /// - /// Constructor. + /// Initializes a new instance of the class. /// private SSHConnectionInfo() { } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. @@ -2010,7 +2011,7 @@ public SSHConnectionInfo( } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. @@ -2027,7 +2028,7 @@ public SSHConnectionInfo( } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. @@ -2065,7 +2066,7 @@ public SSHConnectionInfo( } /// - /// Constructor. + /// Initializes a new instance of the class. /// /// User Name. /// Computer Name. From 2a2d1496616bd0de5048ea6d2a6a31f7762ae019 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 21 Jul 2021 14:31:51 -0700 Subject: [PATCH 4/4] fix merge conflict --- .../engine/remoting/commands/PSRemotingCmdlet.cs | 7 ------- .../engine/remoting/common/RunspaceConnectionInfo.cs | 6 ++++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs index e1bd27af628..0516214cb4a 100644 --- a/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs +++ b/src/System.Management.Automation/engine/remoting/commands/PSRemotingCmdlet.cs @@ -806,13 +806,6 @@ public virtual Hashtable[] SSHConnection set; } - /// - /// This parameter specifies the SSH subsystem to use for the remote connection. - /// - [Parameter(ValueFromPipelineByPropertyName = true, - ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)] - public virtual string Subsystem { get; set; } - /// /// Gets or sets the Hashtable containing options to be passed to OpenSSH. /// diff --git a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs index d5351a7ad71..b8a3aee67e5 100644 --- a/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs +++ b/src/System.Management.Automation/engine/remoting/common/RunspaceConnectionInfo.cs @@ -2073,6 +2073,7 @@ public SSHConnectionInfo( /// Key File Path. /// Port number for connection (default 22). /// Subsystem to use (default 'powershell'). + /// Timeout time for terminating connection attempt. /// Options for the SSH connection. public SSHConnectionInfo( string userName, @@ -2080,9 +2081,10 @@ public SSHConnectionInfo( string keyFilePath, int port, string subsystem, - Hashtable options) : this(userName, computerName, keyFilePath, port, subsystem) + int connectingTimeout, + Hashtable options) : this(userName, computerName, keyFilePath, port, subsystem, connectingTimeout) { - this.Options = options; + Options = options; } #endregion