Reuse in PowerShell a running PuTTY agent (pageant) - powershell

Is there a way in PS 5.1 to reuse the PuTTY agent keys?
Now, the details.
To use key agents one has an Agent that holds the keys (left box), and Client Applications that delegate administration of the keys (right box).
E.g., client application C1=putty can use its own agent A1=pageant, of course.
Client application C2=winscp knows how to use directly A1.
Certain Clients cannot use certain Agents directly, but there are Proxy agents that bridge the gap.
For instance to use A1 with C3, I need Proxy P1=ssh-pageant, see example below.
This helps centralizing in a single Agent the keys for many Clients.
Now I mean to use A1 for all my Clients (currently, only missing A1-C5 and A1-C6).
Is there a way in PS 5.1 to reuse the same PuTTY agent keys?
(I.e., a Proxy Px to use A1 with C5)
Possibly helpful:
https://superuser.com/a/1173570/245595
NOTES:
I did not try it, but it seems like winssh-pageant is a Proxy to link A2 with Client applications that understand A1 directly.
I am currently trying to use in PS the same ssh-pageant from msys2 (it is a Windows program in the end, and often times they do work), manually replacing what eval does in msys2 (so far with no luck, but I think this is fixable):
> cd <dir where ssh-pageant is>
> .\ssh-pageant -r -a "$env:USERPROFILE\tmp\.ssh-pageant-$env:USERNAME"
SSH_AUTH_SOCK='C:\Users\USER1\tmp\.ssh-pageant-USER1'; export SSH_AUTH_SOCK;
SSH_PAGEANT_PID=714; export SSH_PAGEANT_PID;
echo ssh-pageant pid 714;
> $env:SSH_AUTH_SOCK='C:\Users\USER1\tmp\.ssh-pageant-USER1'
> $env:SSH_PAGEANT_PID=714
> ssh myserver
Enter passphrase for key 'C:\Users\USER1/.ssh/id_rsa':
As an alternative workaround, is there a non-admin (I wouldn't want to interfere with it) way to load keys into a separate agent such that when I start a PS session it uses those other keys, and which allows me to keep working with my reused keys in msys2?
This would perhaps amount to using a two different agents at the same time...
weasel-pageant is such a proxy agent (based on Cygwin's ssh-pageant), for WSL (A1-C7)... still looking for a solution for PS.
ssh-agent-wsl
is a fork of weasel-pageant that includes support for using keys held by Microsoft's SSH Agent service (instead of PuTTY Pageant) (A2-C7... I guess it's remarkable that WSL needs a proxy to use Win OpenSSH agent).
Example on how to use "Proxy" agent ssh-pageant to link PuTTY pageant with Cygwin bash
The steps to achieve this are:
When I start my session in Windows, the portable PuTTY agent (pageant) is executed, loading at the same time one key.
For this, a shortcut pageant is added to C:\Users\USER1\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup, pointing at %myputty%\pageant.exe "%mykeys%\key1.ppk".
This makes the key usable by PuTTY and WinSCP, e.g.
But if I now enter a PS session, or an msys2/cygwin terminal, the keys would not be used, and I am asked for the password for the keys.
So if I now
$ ssh myserver
Enter passphrase for key 'C:\Users\USER1/.ssh/id_rsa':
In msys2/cygwin I can use ssh-pageant ("An SSH authentication agent for Cygwin/MSYS that links OpenSSH to PuTTY's Pageant"), such that it reuses whatever keys a previously loaded PuTTY agent has.
For this purpose, I simply add eval $(/usr/bin/ssh-pageant -r -a "/tmp/.ssh-pageant-$USERNAME") to my ~/.bashrc of msys2.
Now whenever I start an msys2 terminal, the link PuTTY's Pageant -> ssh-pageant is established, a couple of environment variables are created, and I can ssh without entering the password
$ env | grep -i ssh
SSH_AUTH_SOCK=/tmp/.ssh-pageant-USER1
SSH_PAGEANT_PID=960
$ ssh myserver
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-40-generic x86_64)
...
Judging from this, it seems the same can be achieved for Git bash.
Related:
http://rabexc.org/posts/pitfalls-of-ssh-agents
How to check if ssh-agent is already running in bash?
https://superuser.com/questions/1327633/how-to-maintain-ssh-agent-login-session-with-windows-10s-new-openssh-and-powers
https://superuser.com/questions/1293725/gpg-agent-under-windows-as-ssh-agent-for-git-bash

I made this work, using the same Cygwin tools (i.e., both ssh-pageant and Cygwin OpenSSH client) in a PS session.
So I would do (assuming ssh-pageant is already running from Msys2):
> cd <dir where ssh-pageant is>
> .\ssh-pageant -r -a "$env:USERPROFILE\tmp\.ssh-pageant-$env:USERNAME"
SSH_AUTH_SOCK='C:\Users\USER1\tmp\.ssh-pageant-USER1'; export SSH_AUTH_SOCK;
> $env:SSH_AUTH_SOCK='C:\Users\USER1\tmp\.ssh-pageant-USER1'
> .\ssh myserver
Logged in to myserver
I have added this to my profile.ps1 (again, it will work when ssh-pageant is already running when I start the PS session)
$env:MSYS2_DIR=<mydir>
# Assuming a proxy ssh agent is already running
$env:SSH_AUTH_SOCK="$env:MSYS2_DIR\tmp\.ssh-pageant-$env:USERNAME"
# We have to make sure we use Msys2 OpenSSH ssh client, not Windows OpenSSH ssh client
function ssh_msys2 {
& $env:MSYS2_DIR\usr\bin\ssh.exe $args
}
If an ssh-pageant is not yet active, this should work (not tested yet; the PID number may be different):
> cd <dir where ssh-pageant is>
> .\ssh-pageant -r -a "$env:USERPROFILE\tmp\.ssh-pageant-$env:USERNAME"
SSH_AUTH_SOCK='C:\Users\USER1\tmp\.ssh-pageant-USER1'; export SSH_AUTH_SOCK;
SSH_PAGEANT_PID=714; export SSH_PAGEANT_PID;
echo ssh-pageant pid 714;
> $env:SSH_AUTH_SOCK='C:\Users\USER1\tmp\.ssh-pageant-USER1'
> $env:SSH_PAGEANT_PID=714
> .\ssh myserver
Logged in to myserver
Still have to test a couple of points, and automate the operation.
In particular, executing ssh-pageant, detecting the PID # if it is returned, and setting environment variable SSH_PAGEANT_PID from PS if that is the case.
This is a little bit more cumbersome than in Msys2, since ssh-pageant spits something directly executable by bash.

Related

Redirect WSL input in Powershell

I've been trying to write a powershell script that automates my windows workspace setup and configuration and am currently stuck trying to redirect input to WSL when executing it for the first time. The core of the problem is that Ubuntu's first launch prompts for a username and password, then logs in to a bash shell. I tried writing down the input lines into a text file like so:
Username
Password
Password
exit
Then, I tried redirecting the input of wsl to the file:
Start-Process ubuntu2004.exe -RedirectStandardInput stdin.txt -NoNewWindow -Wait
The above didn't work as executing WSL just starts spamming Enter new UNIX username: adduser: only one or two names allowed. I tried doing same in CMD with the < input redirection, but the result is the same.
This is not exactly the answer to your question, but in my opinion, ansible is better suited for such a task.
I myself recently became interested in assembling a workspace in wsl and ansible seemed to me the best solution.
Before starting the build, you will need to perform minimal steps (create a user and install several packages, all this can be placed in the readme), but then there will be no restrictions.
You can find several ready-made examples of wsl assembly via ansible on github.
A few ideas for setting the username/password in a new Ubuntu WSL instance:
First, a "PowerShell sendkeys" via COM or Interop might work for this. It's probably the closest in behavior to what you are actually asking.
Second, and perhaps most promising, I just tried this with a new Debian WSL installed from the Store (since I didn't want to mess with my Ubuntu install).
When running debian.exe (like ubuntu2004.exe), I let it run the install, then I Ctrl+C'd out of it when it started asking for the default username/password. At that point, the WSL instance is installed, but with only root. I assume that your script can let the command run for a certain period of time, then kill the process to replicate this.
From your script, you should then be able to run wsl -u root useradd --create-home --user-group --groups adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev --password "encryptedPassword" username (see here for creating the encrypted password). I think that will get you a stock Ubuntu user the way that WSL sets it up.
You'll then need to either create a /etc/wsl.conf file (instructions) letting the instance know that that user is the default, or LxRunOffline lists this as one of its features.
But I'd also throw in that you might just want to keep a "backup" of an existing WSL instance that you start from. Do a wsl --export <distroname> <imagename.tgz>, then you can import it when setting up the new Windows host by copying the tgz over and doing a wsl --import <DistroName> <DirectoryWhereYouwantItToLive> <imagename.tgz>.
If you want, you can keep this image up to date with your desired WSL configuration, so that you don't have to recreate it when you rebuild the Windows hosts. That said, this is where I do follow #Mystic's suggestion of using Ansible to store my WSL "configuration as code". It allows me to not only recreate my WSL instances, but also that same configuration when I set (or reset) a Linode host or another Linux system.

Run perl script on remote server

Is it possible to run perl script, which is located on a remote server, on that server from Windows? There is a job on a remote server that I want to get done every time I make something on Windows.
You have to have something listening for an instruction to run the script, and then you have to send the instruction.
There are lots of approaches you could take to that, including:
Running an SSH server and then connecting to it from an ssh client on the windows machine
Running an HTTP server, running the script through FastCGI, and then requesting the URL for it from curl or a browser on the Windows machine
Writing a custom protocol, listening on a socket, and then writing a custom client that you run on the Windows machine
Absolutely.
You can use plink to run commands on the server from Windows, assuming the server is running sshd.
plink user#a.domain.ext echo hi
This will print "hi\n" to the standard output.
Substitute /path/to/perl/script for echo above and substitute hi with any command line argument that the script needs.
plink is available here: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
One cautionary personal note from doing this many times is that the environment in which the perl script will be run is much less complete than what you would experience when logging in via a full SSH session and running the command interactively. Many environment variables you would normally expect are unset.
For instance using "set | wc -l" in the command above produces only 39 environment variables defined, but from an interactive SSH session, there are 57 environment variables defined. You have to make sure your perl script isn't depending on an environment variable that hasn't been set. For instance, you may need to use full paths for any modules that it uses, or by using the -I flag in the shebang line, because #INC may not be what you expect it to be.

Is there a SCP alternative for PowerShell?

I need to write a script that transfers files from a folder onto another server (Linux), but the script that's transferring files is on windows, and I was wondering if there was an alternative to scp for PowerShell (or if there was another way of doing this)
There is a handy little tool that comes with Putty called pscp.exe that will do this and can be called in powershell easily.
Example below copies from windows to a CentOS box (logging in as the usercode "bill") and you use the -pw switch in pscp to pass in a password (otherwise the command window that is spawned will prompt for the Linux password):
Start-Process 'C:\Program Files (x86)\PuTTY\pscp.exe' -ArgumentList ("-scp -pw password C:\Document.rtf bill#192.168.0.28:/home/bill/")
PuTTY Secure Copy client
Release 0.62
Usage: pscp [options] [user#]host:source target
pscp [options] source [source...] [user#]host:target
pscp [options] -ls [user#]host:filespec
Options:
-V print version information and exit
-pgpfp print PGP key fingerprints and exit
-p preserve file attributes
-q quiet, don't show statistics
-r copy directories recursively
-v show verbose messages
-load sessname Load settings from saved session
-P port connect to specified port
-l user connect with specified username
-pw passw login with specified password
-1 -2 force use of particular SSH protocol version
-4 -6 force use of IPv4 or IPv6
-C enable compression
-i key private key file for authentication
-noagent disable use of Pageant
-agent enable use of Pageant
-batch disable all interactive prompts
-unsafe allow server-side wildcards (DANGEROUS)
-sftp force use of SFTP protocol
-scp force use of SCP protocol
pscp.exe is a viable option, but I have been using a library from Rebex for a couple years now for SFTP and FTPS transfers in both C# apps and PowerShell scripts with great success. Their package also includes an SCP object but I haven't personally used it.
It does cost money vs. pscp being free. Before selecting the Rebex package, I had considered going the PuTTY route but my team decided that having a library we could easily roll into any app/script was worthwhile in the long term.
You can use WinSCP .NET assembly from PowerShell for SCP transfers.
For example see http://winscp.net/eng/docs/library_powershell#example
The example uses SFTP protocol. To use SCP, just modify it to:
$sessionOptions.Protocol = [WinSCP.Protocol]::Scp
Though if your server support SCP protocol, it's likely it also supports SFTP. SFTP is better choice, if you have the option.
Why use an alternative to SCP when you can use SCP?
Windows has OpenSSH (which includes SCP) as an optional component these days, so you could just use that. It first appeared in the Autumn 2018 version of Windows 10. It's nearly identical to the command you find in most Linux distributions, as it's derived from the same code base. Microsoft just made one or two minor tweaks to make it work on windows.
It is simple to install just go to Start->Settings->Apps->Optional Features->View Features enter in OpenSSH in the search box and check the OpenSSH client and click next. See OpenSSH in Windows for more detailed instructions on how to set it up. For a more PowerShell way of installing it run this from an elevated PowerShell prompt:
Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
You can use this command to check if there is a newer version and whether you already have it installed:
Get-WindowsCapability -Online | Where Name -Like '*ssh*'
Once installed, you call scp from PowerShell just like you would any other executable command.
scp file.dat user.name#example.com:/target/path
If you really don't want to use SCP use Copy-Item
This requires PowerShell to be installed on your server. Yes you can install PowerShell on Linux. Just call Copy-Item with the -ToSession parameter passed an SSH connected session, I've never actually tried it though. It requires a recent version of PowerShell and some setting up see PowerShell remoting over SSH. Something like this:
Copy-Item C:\localPath\*.* ~\remotePath\ -ToSession (New-PSSession -HostName UserA#LinuxServer01:22 -KeyFilePath c:\\userAKey_rsa)
If both machines are Windows machines you can use the same -ToSession parameter to copy files over WinRM. But both machines have to be domain joined or there is the possibility of security issues.
There is also a ".NET friendly" way:
you can use the SharpSSH dll to execute ssh commands, and do scp/sftp tranfers.
For example:
[Reflection.Assembly]::LoadFrom((Resolve-Path .\Tamir.SharpSSH.dll))
$ssh = New-Object Tamir.SharpSsh.Sftp("server","user","password")
$ssh.Connect()
$ssh.Put("C:\localfile","distantfile")
$ssh.Close()
There is the SSH.Net library, too, it does approximatively the same things.

PuTTY scripting to log onto host

I'm using PuTTY to remotely log onto my school's host. Upon logging in, we are required to do these steps:
enter username
enter password
command "add oracle"
command "sqlplus"
enter username
enter password
I will be logging into this host a lot over the course of this semester and I was hoping to create a script that would eliminate the redundancy of the above steps. Ignoring the obvious security oversights of having my password in the script, how would I achieve this? I have zero experience with scripting, so your feedback is greatly appreciated. Thanks!
Edit: I played around with the command-line options for Putty and I was able to bypass steps 1-2 using:
putty -load "host" -l username -pw password
I've also created a shell file that looks like so:
#!/bin/bash
add oracle10g
sqlplus username password
When I try to add this option to the command-line using the -m option, it looks like PuTTY logs into the host and then immediately exits. Is there a way to keep my session open after running the shell file or am I using the -m option wrongly? Here is a link to a PuTTY guide that I have been following: http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter3.html.
Here is the total command that I am trying to run from the command-line:
putty -load "host" -l username -pw password -m c:\test.sh
Figured this out with the help of a friend. The -m PuTTY option will end your session immediately after it executes the shell file. What I've done instead is I've created a batch script called putty.bat with these contents on my Windows machine:
#echo off
putty -load "host" -l username -pw password
This logs me in remotely to the Linux host. On the host side, I created a shell file called sql with these contents:
#!/bin/tcsh
add oracle10g
sqlplus username password
My host's Linux build used tcsh. Other Linux builds might use bash, so simply replace tcsh with bash and you should be fine.
To summarize, automating these steps are now done in two easy steps:
Double-click putty.bat. This opens PuTTY and logs me into the host.
Run command tcsh sql. This adds the oracle tool to my host, and logs me into the sql database.
I'm not sure why previous answers haven't suggested that the original poster set up a shell profile (bashrc, .tcshrc, etc.) that executed their commands automatically every time they log in on the server side.
The quest that brought me to this page for help was a bit different -- I wanted multiple PuTTY shortcuts for the same host that would execute different startup commands.
I came up with two solutions, both of which worked:
(background) I have a folder with a variety of PuTTY shortcuts, each with the "target" property in the shortcut tab looking something like:
"C:\Program Files (x86)\PuTTY\putty.exe" -load host01
with each load corresponding to a PuTTY profile I'd saved (with different hosts in the "Session" tab). (Mostly they only differ in color schemes -- I like to have each group of related tasks share a color scheme in the terminal window, with critical tasks, like logging in as root on a production system, performed only in distinctly colored windows.)
The folder's Windows properties are set to very clean and stripped down -- it functions as a small console with shortcut icons for each of my frequent remote PuTTY and RDP connections.
(solution 1)
As mentioned in other answers the -m switch is used to configure a script on the Windows side to run, the -t switch is used to stay connected, but I found that it was order-sensitive if I wanted to get it to run without exiting
What I finally got to work after a lot of trial and error was:
(shortcut target field):
"C:\Program Files (x86)\PuTTY\putty.exe" -t -load "SSH Proxy" -m "C:\Users\[me]\Documents\hello-world-bash.txt"
where the file being executed looked like
echo "Hello, World!"
echo ""
export PUTTYVAR=PROXY
/usr/local/bin/bash
(no semicolons needed)
This runs the scripted command (in my case just printing "Hello, world" on the terminal) and sets a variable that my remote session can interact with.
Note for debugging: when you run PuTTY it loads the -m script, if you edit the script you need to re-launch PuTTY instead of just restarting the session.
(solution 2)
This method feels a lot cleaner, as the brains are on the remote Unix side instead of the local Windows side:
From Putty master session (not "edit settings" from existing session) load a saved config and in the SSH tab set remote command to:
export PUTTYVAR=GREEN; bash -l
Then, in my .bashrc, I have a section that performs different actions based on that variable:
case ${PUTTYVAR} in
"")
echo ""
;;
"PROXY")
# this is the session config with all the SSH tunnels defined in it
echo "";
echo "Special window just for holding tunnels open." ;
echo "";
PROMPT_COMMAND='echo -ne "\033]0;Proxy Session #master01\$\007"'
alias temppass="ssh keyholder.example.com makeonetimepassword"
alias | grep temppass
;;
"GREEN")
echo "";
echo "It's not easy being green"
;;
"GRAY")
echo ""
echo "The gray ghost"
;;
*)
echo "";
echo "Unknown PUTTYVAR setting ${PUTTYVAR}"
;;
esac
(solution 3, untried)
It should also be possible to have bash skip my .bashrc and execute a different startup script, by putting this in the PuTTY SSH command field:
bash --rcfile .bashrc_variant -l
When you use the -m option putty does not allocate a tty, it runs the command and quits. If you want to run an interactive script (such as a sql client), you need to tell it to allocate a tty with -t, see 3.8.3.12 -t and -T: control pseudo-terminal allocation. You'll avoid keeping a script on the server, as well as having to invoke it once you're connected.
Here's what I'm using to connect to mysql from a batch file:
#mysql.bat
start putty -t -load "sessionname" -l username -pw password -m c:\mysql.sh
#mysql.sh
mysql -h localhost -u username --password="foo" mydb
https://superuser.com/questions/587629/putty-run-a-remote-command-after-login-keep-the-shell-running
I want to suggest a common solution for those requirements, maybe it is a use for you: AutoIt. With that program, you can write scripts on top of any window like Putty and execute all commands you want to (like button pressing or mouse clicking in textboxes or buttons).
This way you can emulate all steps you are always doing with Putty.
entering a command after you logged in can be done by going through SSH section at the bottom of putty and you should have an option Remote command (data to send to the server) separate the two commands with ;
mputty can do that but it does not seem to work always. (if that wait period is too slow)
mputty uses putty and it extends putty.
There is an option to run a script.
If it does not work, make sure that wait period before typing is a high value or increase that value. See putty sessions , then name of session, right mouse button,properties/script page.
For me it works this way:
putty -ssh root#1.1.1.1 22 -pw password
putty, protocol, user name # ip address port and password. To connect in less than a second.
You can use the -i privatekeyfilelocation in case you are using a private key instead of password based.

Is it possible to have Perl run shell script aliases?

Is it possible to have a Perl script run shell aliases? I am running into a situation where we've got a Perl module I don't have access to modify and one of the things it does is logs into multiple servers via SSH to run some commands remotely. Sadly some of the systems (which I also don't have access to modify) have a buggy SSH server that will disconnect as soon as my system tries to send an SSH public key. I have the SSH agent running because I need it to connect to some other servers.
My initial solution was to set up an alias to set ssh to ssh -o PubkeyAuthentication=no, but Perl runs the ssh binary it finds in the PATH instead of trying to use the alias.
It looks like the only solutions are disable the SSH agent while I am connecting to the problem servers or override the Perl module that does the actual connection.
Perhaps you could put a command called ssh in PATH ahead of the ssh which runs ssh as you want it to be run.
Alter the PATH before you run the perl script, or use this in your .ssh/config
Host *
PubkeyAuthentication no
Why don't you skip the alias and just create a shell script called ssh in a directory somewhere, then change the path to put that directory before the one containing the real ssh?
I had to do this recently with iostat because the new version output a different format that a third-party product couldn't handle (it scanned the output to generate a report).
I just created an iostat shell script which called the real iostat (with hardcoded path, but you could be more sophisticated), passing the output through an awk script to massage it into the original format. Then, I changed the path for the third-party program and it started working fine.
You could declare a function in .bashrc (or .profile or whatever) with that name. It could look like this (might break):
function ssh {
/usr/bin/ssh -o PubkeyAuthentication=no "$#"
}
But using a config file might be the best solution in your case.