Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,25 @@ public override Hashtable[] SSHConnection
set;
}

/// <summary>
/// Hashtable containing options to be passed to OpenSSH.
/// </summary>
[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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ internal struct SSHConnection
public int Port;
public string Subsystem;
public int ConnectingTimeout;
public Hashtable Options;
}

/// <summary>
Expand Down Expand Up @@ -805,6 +806,13 @@ public virtual Hashtable[] SSHConnection
set;
}

/// <summary>
/// Gets or sets the Hashtable containing options to be passed to OpenSSH.
/// </summary>
[Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
[ValidateNotNullOrEmpty]
public virtual Hashtable Options { get; set; }

#endregion

#endregion Properties
Expand Down Expand Up @@ -866,6 +874,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

Expand Down Expand Up @@ -969,6 +978,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(
Expand Down Expand Up @@ -1462,7 +1475,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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,24 @@ public class EnterPSSessionCommand : PSRemotingBaseCmdlet
[ValidateNotNullOrEmpty()]
public new string HostName { get; set; }

/// <summary>
/// Gets or sets the Hashtable containing options to be passed to OpenSSH.
/// </summary>
[Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)]
[ValidateNotNullOrEmpty]
public override Hashtable Options
{
get
{
return base.Options;
}

set
{
base.Options = value;
}
}

#endregion

/// <summary>
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,8 @@ private List<RemoteRunspace> CreateRunspacesForSSHHostParameterSet()
this.KeyFilePath,
port,
Subsystem,
ConnectingTimeout);
ConnectingTimeout,
Options);
var typeTable = TypeTable.LoadDefaultTypeFiles();
string rsName = GetRunspaceName(index, out int rsIdUnused);
index++;
Expand All @@ -1120,7 +1121,8 @@ private List<RemoteRunspace> 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++;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -1969,18 +1970,27 @@ public int ConnectingTimeout
set;
}

/// The SSH options to pass to OpenSSH.
/// Gets or sets the SSH options to pass to OpenSSH.
/// </summary>
private Hashtable Options
{
get;
set;
}

#endregion

#region Constructors

/// <summary>
/// Constructor.
/// Initializes a new instance of the <see cref="SSHConnectionInfo" /> class.
/// </summary>
private SSHConnectionInfo()
{ }

/// <summary>
/// Constructor.
/// Initializes a new instance of the <see cref="SSHConnectionInfo" /> class.
/// </summary>
/// <param name="userName">User Name.</param>
/// <param name="computerName">Computer Name.</param>
Expand All @@ -2001,7 +2011,7 @@ public SSHConnectionInfo(
}

/// <summary>
/// Constructor.
/// Initializes a new instance of the <see cref="SSHConnectionInfo" /> class.
/// </summary>
/// <param name="userName">User Name.</param>
/// <param name="computerName">Computer Name.</param>
Expand All @@ -2018,7 +2028,7 @@ public SSHConnectionInfo(
}

/// <summary>
/// Constructor.
/// Initializes a new instance of the <see cref="SSHConnectionInfo" /> class.
/// </summary>
/// <param name="userName">User Name.</param>
/// <param name="computerName">Computer Name.</param>
Expand Down Expand Up @@ -2055,6 +2065,28 @@ public SSHConnectionInfo(
ConnectingTimeout = connectingTimeout;
}

/// <summary>
/// Initializes a new instance of the <see cref="SSHConnectionInfo" /> class.
/// </summary>
/// <param name="userName">User Name.</param>
/// <param name="computerName">Computer Name.</param>
/// <param name="keyFilePath">Key File Path.</param>
/// <param name="port">Port number for connection (default 22).</param>
/// <param name="subsystem">Subsystem to use (default 'powershell').</param>
/// <param name="connectingTimeout">Timeout time for terminating connection attempt.</param>
/// <param name="options">Options for the SSH connection.</param>
public SSHConnectionInfo(
string userName,
string computerName,
string keyFilePath,
int port,
string subsystem,
int connectingTimeout,
Hashtable options) : this(userName, computerName, keyFilePath, port, subsystem, connectingTimeout)
{
Options = options;
}

#endregion

#region Overrides
Expand Down Expand Up @@ -2111,6 +2143,7 @@ internal override RunspaceConnectionInfo InternalCopy()
newCopy.Port = Port;
newCopy.Subsystem = Subsystem;
newCopy.ConnectingTimeout = ConnectingTimeout;
newCopy.Options = Options;

return newCopy;
}
Expand Down Expand Up @@ -2163,9 +2196,9 @@ internal int StartSSHProcess(
//
// Local ssh invoked as:
// windows:
// ssh.exe [-i identity_file] [-l login_name] [-p port] -s <destination> <command>
// ssh.exe [-i identity_file] [-l login_name] [-p port] [-o option] -s <destination> <command>
// linux|macos:
// ssh [-i identity_file] [-l login_name] [-p port] -s <destination> <command>
// ssh [-i identity_file] [-l login_name] [-p port] [-o option] -s <destination> <command>
// where <command> is interpreted as the subsystem due to the -s flag.
//
// Remote sshd configured for PowerShell Remoting Protocol (PSRP) over Secure Shell Protocol (SSH)
Expand Down Expand Up @@ -2216,6 +2249,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));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we get this through public API should we make additional validations?
I mean that Key and Value could contain composite values.
/cc @PaulHigin

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point, right now the Format method will just try to convert whatever is passed in to a string, would we want to handle composite values any other way?

I also could try to add some validation to ensure that we're only passing options that OpenSSH would consider valid, but that would be a lot to maintain as well. Please let me know your thoughts.

}
}

// 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));
Expand Down
7 changes: 7 additions & 0 deletions test/SSHRemoting/SSHRemoting.Basic.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Comment on lines +182 to +187
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test checks that the parameter is only recognized, but does not verify that it is being applied.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely add more integration tests, it will take me a few days to get those done though.


It "Verifies explicit Subsystem parameter" {
Write-Verbose -Verbose "It Starting: Verifies explicit Subsystem parameter"
$portNum = 22
Expand Down