Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ssh connection to windows targets #813

Closed
ndelo opened this issue Dec 28, 2018 · 7 comments
Closed

Support ssh connection to windows targets #813

ndelo opened this issue Dec 28, 2018 · 7 comments
Assignees
Labels
Feature New features and improvements.

Comments

@ndelo
Copy link

ndelo commented Dec 28, 2018

Problem
Bolt transport make assumptions about what shell and commands are available on the the target based on the transport that is being used.

Solution:
Command building logic should be split out of command execution logic in the transports. Each transport should probe what the login shell is on the other side of the connection and cache that information with the "connection" for the duration of a bolt execution. It should then build shell specific commands to run over the transports execute functionality.

We only need to support sh/bash for linux and we should assume the same LCD versions of unix commands are available. The code should be written in such a way that we can add csh support without a further refactor however.
We do not need to implement shell discovery functionality in the winrm transport but should for local, ssh, and docker.

Questions:

  • How will transport provided features work? We should lazily evaluate provided features by connecting to the target when a user requests features or runs a task for the first time.
  • If we know the login shell should we use the login shell when running commands with sudo? Yes, we should make this the default v2.0 behavior.
  • How do we detect powershell?
@ndelo ndelo changed the title SSH script run against Win32 OpenSSH fails Bolt script run against OpenSSH on Windows fails Dec 28, 2018
@lucywyman
Copy link
Contributor

Hi @ndelo! We do indeed assume that users using the ssh transport are connecting to unix hosts - https://github.com/puppetlabs/bolt/blob/master/lib/bolt/transport/ssh/connection.rb#L309 is the specific lines that will need to change. I'm not sure what the best approach to this will be though - maybe adding a check for the host os? Or having some kind of 'tag' on the hosts that are windows, and using that instead of the transport to determine commands like mkdir? Most of the team is off this week and early next week, so you might hear more next Wednesday or so!

@MikaelSmith
Copy link
Contributor

MikaelSmith commented Jan 2, 2019

We'd like to separate the context of transport from the shell you're operating in on the target, but that'll be a bit of an undertaking. We've started it for powershell. I imagine it would look like

Transports: ssh, winrm, local
Shell: powershell, (linux) shell

ssh would default to shell, but could have a config to override that and use powershell on the target.
winrm would default to powershell and could be overridden to shell.
local would default based on OS, but could also be overidden.

@ndelo
Copy link
Author

ndelo commented Jan 2, 2019 via email

@MikaelSmith
Copy link
Contributor

It would definitely be a configuration option in inventory. We add it elsewhere (bolt.yaml and CLI) when it seems useful.

@pigam
Copy link

pigam commented Apr 23, 2019

bolt file upload also fails for windows hosts over ssh, with Could not make tempdir. I think it's the same cause, am I right ?

bolt --version
1.17.0

@adreyer adreyer added the Feature New features and improvements. label Sep 25, 2019
@adreyer adreyer changed the title Bolt script run against OpenSSH on Windows fails Support ssh connection to windows targets Sep 25, 2019
@nmaludy
Copy link
Contributor

nmaludy commented Oct 6, 2019

+1

@adreyer adreyer added this to the 2.0 milestone Dec 12, 2019
@yasminrajabi yasminrajabi reopened this Jan 15, 2020
@lucywyman lucywyman added the Blocked Work blocked by other issues or PRs. label Jan 15, 2020
@beechtom beechtom removed this from the 2.0 milestone Feb 14, 2020
@nicklewis nicklewis self-assigned this Mar 4, 2020
@lucywyman
Copy link
Contributor

Relates to #1492

@nicklewis nicklewis removed the Blocked Work blocked by other issues or PRs. label Mar 30, 2020
nicklewis added a commit to nicklewis/bolt that referenced this issue Mar 31, 2020
…transports

Previously, the similarities between the local and SSH transports were
represented by the Sudoable base transport class. This made the two
transports tightly coupled to bash. It also meant we had two separate
implementations of sudo support, one for each of the transports.

This commit introduces a conceptual split between the "connection"
portion of a transport and the "shell" that it talks to on the other
end. In this case, "local" and "SSH" are two implementations of how to
connect to a target, but the Bolt::Shell::Bash class contains all the logic of
how to actually get something done over the connection.

The SSH and local connection classes now are required to implement only
two methods: `execute` and `copy_file`. All other operations are built
in terms of those two primitives. All logic for how to run commands,
scripts, and tasks on a bash target, including how to invoke sudo, is
now contained in th Bolt::Shell::Bash class.

This represents the first step in eventually supporting running against
Powershell targets over SSH.
nicklewis added a commit to nicklewis/bolt that referenced this issue Mar 31, 2020
…transports

Previously, the similarities between the local and SSH transports were
represented by the Sudoable base transport class. This made the two
transports tightly coupled to bash. It also meant we had two separate
implementations of sudo support, one for each of the transports.

This commit introduces a conceptual split between the "connection"
portion of a transport and the "shell" that it talks to on the other
end. In this case, "local" and "SSH" are two implementations of how to
connect to a target, but the Bolt::Shell::Bash class contains all the logic of
how to actually get something done over the connection.

The SSH and local connection classes now are required to implement only
two methods: `execute` and `copy_file`. All other operations are built
in terms of those two primitives. All logic for how to run commands,
scripts, and tasks on a bash target, including how to invoke sudo, is
now contained in th Bolt::Shell::Bash class.

This represents the first step in eventually supporting running against
Powershell targets over SSH.
nicklewis added a commit to nicklewis/bolt that referenced this issue Mar 31, 2020
…transports

Previously, the similarities between the local and SSH transports were
represented by the Sudoable base transport class. This made the two
transports tightly coupled to bash. It also meant we had two separate
implementations of sudo support, one for each of the transports.

This commit introduces a conceptual split between the "connection"
portion of a transport and the "shell" that it talks to on the other
end. In this case, "local" and "SSH" are two implementations of how to
connect to a target, but the Bolt::Shell::Bash class contains all the logic of
how to actually get something done over the connection.

The SSH and local connection classes now are required to implement only
two methods: `execute` and `copy_file`. All other operations are built
in terms of those two primitives. All logic for how to run commands,
scripts, and tasks on a bash target, including how to invoke sudo, is
now contained in th Bolt::Shell::Bash class.

This represents the first step in eventually supporting running against
Powershell targets over SSH.
nicklewis added a commit to nicklewis/bolt that referenced this issue Mar 31, 2020
Previously, we set environment variables with the `sudo` invocation and
relied on `sudo` to pass them through to the command being executed.
That doesn't work in every configuration or with every run-as command.
It also unnecessarily makes those values available to `sudo`. We now
set the environment variables *inside* the `sudo` invocation, ensuring
they're passed to the command we're trying to execute.

This changes the local transport to no longer rely on Open3's ability to
set environment variables directly. That means SSH and local now pass
environment variables in exactly the same way.
nicklewis added a commit to nicklewis/bolt that referenced this issue Mar 31, 2020
…transports

Previously, the similarities between the local and SSH transports were
represented by the Sudoable base transport class. This made the two
transports tightly coupled to bash. It also meant we had two separate
implementations of sudo support, one for each of the transports.

This commit introduces a conceptual split between the "connection"
portion of a transport and the "shell" that it talks to on the other
end. In this case, "local" and "SSH" are two implementations of how to
connect to a target, but the Bolt::Shell::Bash class contains all the logic of
how to actually get something done over the connection.

The SSH and local connection classes now are required to implement only
two methods: `execute` and `copy_file`. All other operations are built
in terms of those two primitives. All logic for how to run commands,
scripts, and tasks on a bash target, including how to invoke sudo, is
now contained in th Bolt::Shell::Bash class.

This represents the first step in eventually supporting running against
Powershell targets over SSH.
nicklewis added a commit to nicklewis/bolt that referenced this issue Mar 31, 2020
Previously, we set environment variables with the `sudo` invocation and
relied on `sudo` to pass them through to the command being executed.
That doesn't work in every configuration or with every run-as command.
It also unnecessarily makes those values available to `sudo`. We now
set the environment variables *inside* the `sudo` invocation, ensuring
they're passed to the command we're trying to execute.

This changes the local transport to no longer rely on Open3's ability to
set environment variables directly. That means SSH and local now pass
environment variables in exactly the same way.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 1, 2020
…transports

Previously, the similarities between the local and SSH transports were
represented by the Sudoable base transport class. This made the two
transports tightly coupled to bash. It also meant we had two separate
implementations of sudo support, one for each of the transports.

This commit introduces a conceptual split between the "connection"
portion of a transport and the "shell" that it talks to on the other
end. In this case, "local" and "SSH" are two implementations of how to
connect to a target, but the Bolt::Shell::Bash class contains all the logic of
how to actually get something done over the connection.

The SSH and local connection classes now are required to implement only
two methods: `execute` and `copy_file`. All other operations are built
in terms of those two primitives. All logic for how to run commands,
scripts, and tasks on a bash target, including how to invoke sudo, is
now contained in th Bolt::Shell::Bash class.

This represents the first step in eventually supporting running against
Powershell targets over SSH.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 1, 2020
Previously, we set environment variables with the `sudo` invocation and
relied on `sudo` to pass them through to the command being executed.
That doesn't work in every configuration or with every run-as command.
It also unnecessarily makes those values available to `sudo`. We now
set the environment variables *inside* the `sudo` invocation, ensuring
they're passed to the command we're trying to execute.

This changes the local transport to no longer rely on Open3's ability to
set environment variables directly. That means SSH and local now pass
environment variables in exactly the same way.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 1, 2020
When using run-as, we first `cd` to the user's homedir to ensure we're
not running from a directory that the run-as user can't access. However,
the run-as user's homedir doesn't necessarily have to exist. Previously,
that would cause a failure as the `cd` would fail and we would therefore
refuse to run the actual command. Now, we just *try* to `cd` to the
homedir if possible and then run the command regardless.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 1, 2020
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 13, 2020
This commit factors out the Powershell code from the WinRM and Windows
local transports to match the transport/shell split implemented for SSH
and Bash. That is, the transports are now merely responsible for
connecting to the target, executing commands and uploading files, while
the powershell shell class handles running tasks, etc.

We no longer have a dedicated local_windows transport, instead using the
standard local transport. The local transport will run any command it's
given via powershell. This matches the non-windows behavior where
commands are run via shell.

Due to bugs in passing stdin from ruby to powershell, we cannot simply
send the commands over stdin. Therefore, we write the commands to a file
and point powershell at that. This also avoids limitations around
command-line length if we were to instead run via `powershell.exe -Command`.

Because commands are now run via powershell, we can run into problems
with powershell inserting a byte order marker in content that we supply
to a task via stdin. For that reason, we now detect whether the
[Console] object is configured to use UTF-8 and, if so, we replace its
encoding a non-BOM version of UTF-8.

This also standardizes the newline behavior between the local and WinRM
transports. Previously, the WinRM transport would return \r\n while the
local transport would return \n. They now consistently return \r\n.

!feature

* **Commands run over local transport on Windows use powershell**
  ([puppetlabs#1708](puppetlabs#1708))

  Previously, the local transport on Windows would exec commands
  directly, meaning powershell constructs couldn't be used. These
  commands are now always executed through powershell, so powershell
  commands and script snippets can be run.

* **Commands and tasks on Windows now consistently return \r\n**
  ([puppetlabs#1708](puppetlabs#1708))

  The local transport on Windows was returning \n while WinRM returned
  \r\n. They are now consistent and always use \r\n.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 13, 2020
…gin shell

This adds a new `login-shell` option to the SSH transport which can be
used to tell Bolt which shell to expect. We support a number of
bash-compatible shells and powershell, which is experimental. Selecting
any of the supported non-powershell options will result in identical
behavior.

!feature

* **Experimental support for interacting with Windows hosts via PowerShell over SSH**
  ([puppetlabs#813](puppetlabs#813))

  The `login-shell: powershell` config setting can be set on a target to
  connect over SSH while running commands and tasks via PowerShell instead
  of Bash.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 13, 2020
We now disallow tty/run-as when using PowerShell and allow extensions.
We don't fail if extension is set when not using PowerShell, though it
will be ignored.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 13, 2020
…gin shell

This adds a new `login-shell` option to the SSH transport which can be
used to tell Bolt which shell to expect. We support a number of
bash-compatible shells and powershell, which is experimental. Selecting
any of the supported non-powershell options will result in identical
behavior.

!feature

* **Experimental support for interacting with Windows hosts via PowerShell over SSH**
  ([puppetlabs#813](puppetlabs#813))

  The `login-shell: powershell` config setting can be set on a target to
  connect over SSH while running commands and tasks via PowerShell instead
  of Bash.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 13, 2020
We now disallow tty/run-as when using PowerShell and allow extensions.
We don't fail if extension is set when not using PowerShell, though it
will be ignored.
lucywyman added a commit that referenced this issue Apr 14, 2020
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 14, 2020
…gin shell

This adds a new `login-shell` option to the SSH transport which can be
used to tell Bolt which shell to expect. We support a number of
bash-compatible shells and powershell, which is experimental. Selecting
any of the supported non-powershell options will result in identical
behavior.

!feature

* **Experimental support for interacting with Windows hosts via PowerShell over SSH**
  ([puppetlabs#813](puppetlabs#813))

  The `login-shell: powershell` config setting can be set on a target to
  connect over SSH while running commands and tasks via PowerShell instead
  of Bash.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 14, 2020
We now disallow tty/run-as when using PowerShell and allow extensions.
We don't fail if extension is set when not using PowerShell, though it
will be ignored.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 16, 2020
Older versions of OpenSSH have a bug that causes quotes and backslashes
to be eaten before they can be properly interpreted by powershell. This
causes every task invocation to fail. We now detect the SSH version and
fail if it's one that is known to be incompatible.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 16, 2020
…gin shell

This adds a new `login-shell` option to the SSH transport which can be
used to tell Bolt which shell to expect. We support a number of
bash-compatible shells and powershell, which is experimental. Selecting
any of the supported non-powershell options will result in identical
behavior.

!feature

* **Experimental support for interacting with Windows hosts via PowerShell over SSH**
  ([puppetlabs#813](puppetlabs#813))

  The `login-shell: powershell` config setting can be set on a target to
  connect over SSH while running commands and tasks via PowerShell instead
  of Bash. This feature requires OpenSSH >= 7.9 on the target.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 16, 2020
We now disallow tty/run-as when using PowerShell and allow extensions.
We don't fail if extension is set when not using PowerShell, though it
will be ignored.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 16, 2020
Older versions of OpenSSH have a bug that causes quotes and backslashes
to be eaten before they can be properly interpreted by powershell. This
causes every task invocation to fail. We now detect the SSH version and
fail if it's one that is known to be incompatible.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 16, 2020
Older versions of OpenSSH have a bug that causes quotes and backslashes
to be eaten before they can be properly interpreted by powershell. This
causes every task invocation to fail. We now detect the SSH version and
fail if it's one that is known to be incompatible.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 17, 2020
We can't execute commands that are 32700+ bytes long over SSH.
Therefore, we now wrap such commands in a script and invoke that. This
factors out the code that already does such wrapping for the local
transport and moves it to the Powershell class where it more rightly
belongs. This has the benefit that we no longer generate a script for
commands that are smaller than the limit when using the local transport.
Except for tasks like apply_catalog which receive large amounts of data
on stdin, most things should fit under the limit.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 17, 2020
We can't execute commands that are 32700+ bytes long over SSH.
Therefore, we now wrap such commands in a script and invoke that. This
factors out the code that already does such wrapping for the local
transport and moves it to the Powershell class where it more rightly
belongs. This has the benefit that we no longer generate a script for
commands that are smaller than the limit when using the local transport.
Except for tasks like apply_catalog which receive large amounts of data
on stdin, most things should fit under the limit.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
We can't execute commands that are 32700+ bytes long over SSH.
Therefore, we now wrap such commands in a script and invoke that. This
factors out the code that already does such wrapping for the local
transport and moves it to the Powershell class where it more rightly
belongs. This has the benefit that we no longer generate a script for
commands that are smaller than the limit when using the local transport.
Except for tasks like apply_catalog which receive large amounts of data
on stdin, most things should fit under the limit.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
…gin shell

This adds a new `login-shell` option to the SSH transport which can be
used to tell Bolt which shell to expect. We support a number of
bash-compatible shells and powershell, which is experimental. Selecting
any of the supported non-powershell options will result in identical
behavior.

!feature

* **Experimental support for interacting with Windows hosts via PowerShell over SSH**
  ([puppetlabs#813](puppetlabs#813))

  The `login-shell: powershell` config setting can be set on a target to
  connect over SSH while running commands and tasks via PowerShell instead
  of Bash. This feature requires OpenSSH >= 7.9 on the target.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
We now disallow tty/run-as when using PowerShell and allow extensions.
We don't fail if extension is set when not using PowerShell, though it
will be ignored.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
Older versions of OpenSSH have a bug that causes quotes and backslashes
to be eaten before they can be properly interpreted by powershell. This
causes every task invocation to fail. We now detect the SSH version and
fail if it's one that is known to be incompatible.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
We can't execute commands that are 32700+ bytes long over SSH.
Therefore, we now wrap such commands in a script and invoke that. This
factors out the code that already does such wrapping for the local
transport and moves it to the Powershell class where it more rightly
belongs. This has the benefit that we no longer generate a script for
commands that are smaller than the limit when using the local transport.
Except for tasks like apply_catalog which receive large amounts of data
on stdin, most things should fit under the limit.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
We need to close this file on Windows before it can be moved.
nicklewis added a commit to nicklewis/bolt that referenced this issue Apr 20, 2020
This needs to be double-quoted (escape_arguments) instead of
single-quoted (quote_string).
pull bot pushed a commit to NeatNerdPrime/bolt that referenced this issue Apr 21, 2020
…wershell-over-ssh

(puppetlabsGH-813) Add the ability to select powershell as the SSH login shell
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature New features and improvements.
Projects
None yet
Development

No branches or pull requests

9 participants