How to return more than one line from ssh config LocalCommand when doing LocalForward - ssh-tunnel

I wish to do a local forward of port 8888:localhost:8888 so that I can ssh to my home network and get redirected to my main computer and run a jupyter notebook there.
For convenience I want to make it a ssh config and have the shortcut return the token for the notebook so that I can simply go there in my browser.
My config file is
Host mystuff
HostName mystuff.duckdns.org
User ivar
Port xzyx
SessionType none
PermitLocalCommand yes
LocalCommand jupyter notebook list
LocalForward 8888 localhost:8888
This opens the tunnel but only returns
"Currently running servers:"
which is the line preceeding the notebooks running and their respective tokens.
How can I modify my config so as to see all that is being printed?
I have tried things like adding a LocalCommand sleep(1) before LocalForward

Related

How do I run a local command before starting SSH connection and after SSH connection closes?

Essentially what I want to do is run a Bash script I created that switches WiFi SSIDs before starting the SSH connection, and after the SSH connection closes.
I have added this to ~/.ssh/config by setting ProxyCommand to ./run-script; ssh %h:%p but by doing this, I feel like it would ignore any parameters I passed when I run the ssh command. Also, I have no idea how to get the script to run again when the SSH connection closes.
For OpenSSH you can specify a LocalCommand in your ssh config (~/.ssh/config).
But for that to work you also need the system-wide option (in /etc/ssh/ssh_config) PermitLocalCommand to yes. (By default it is set to no.)
It gets executed on the local machine after authenticating but before the remote shell is started.
There appears to be no (easy) way of executing something after the connection has been closed, though.
Assuming that it is not possible to implement a wrapper to 'ssh' (using alias, or some other method), it is possible to implement the following in the proxyCommand.
Important to note that there is no protection against multiple invocation of 'ssh' - possible that during a specific invocation that WIFI is already connected. Also, it is possible that when a specific ssh is terminated, the WIFI has to stay active because of other pending conditions.
Possible implementation of the proxy script is
ProxyCommand /path/to/run-script %h %p
#! /bin/sh
pre-command # connect to WIFI
nc -N "$1" "$2" # Tunnel, '%h' and '%p' are passed in
post-command # Disconnect WIFI
You do not want to use simple ssh in the proxy script, as this will translate into another call to the 'run-script'. Also note that all options provided to the original ssh will be handled by the initial 'ssh' session that will be leveraging the proxy 'nc' tunnel.

iTerm2 - How to pass environment-variables when started via url-scheme?

Most of you certainly now the MacOS terminal emulator iTerm 2
I want to pass my environment variables which I've set/saved in ~/.ssh/environment to iTerm2, when it (the profile) is configured as default handler for this url-scheme. ( ssh://== )
Normal example ← works
You open the app iTerm2
Enter your ssh-command:
ssh hostname
It connects into your server and you can see with the command printenv your environment-variables you've put into your local ~/.ssh/environment file.
URL-Scheme example ← doesn't work
Some external application ( like the alfred-ssh workflow from deanishe) can access your .ssh/config file to make it easier to access all your configured hosts quickly and opens them then via url-scheme.
Because iTerm2 is configured for the ssh-scheme iTerm2 starts and connects quickly to the server.
You enter printenv and doesn't find your environment-varialbes.
You realize that iTerm2 started instantly and doesn't loaded the local environment-variables. Okay, I doesn't realized this at the beginning and created an issue for the workflow I used. But the developer is right, iTerm2 starts and isn't able to load the environment variables.
I've searched already several weeks for an solution, but wasn't able to solve this problem yet. That's why I'm asking here now.
My local SSH configuration (cleaned)
Content of ~/.ssh/environment is:
echo "RMATE_HOST=localhost" > sshenv
echo "RMATE_PORT=52699" > sshenv
Content of ~/.ssh/config is:
Host *
AddKeysToAgent yes
ServerAliveInterval 120
TCPKeepAlive no
UseKeychain yes
SendEnv RMATE_*
RemoteForward 52699 localhost:52699
Host personal
HostName personal.tld
IdentityFile ~/.ssh/keyFileName1
User user
Port 22
Host work
HostName business.tld
IdentityFile ~/.ssh/keyFileName2
User user
Port 22
And yeah, indeed! I just want to pass my RMATE variables to the servers via the workflow with Alfred ;-)

ssh to another machine after sshing via script

I have 3 servers,
server1 -> server2 -> server3
Server2 is reachable only via server 1 and server3 via server2.
Every time connection breaks I have to manually login to both the servers.
Is there any way to login and open bash terminal to server3 through this path via a script?
I have had same problem and I have a solution. I use xdotool to emulate keys (and xclip to copy password that is extracted from other file). This script opens ssh connections to list of servers in separate console tabs. Edit it according to Your needs.
for IP in $SERVERS
do
xdotool key ctrl+shift+t type "ssh $USER#$IP"
xdotool key Return
sleep 1
xdotool key ctrl+shift+v
xdotool key Return
done
Script simply iterate over table of servers. It opens new console tab, prints "ssh some_user#some_ip" and next emulate retrun key.
Sleep is used just to make sure script has enought time to connect to server. At the end password is pasted and You enter first server.
One more thing:
dont touch keyboard while script is running. I hope it can help You.
Use a ssh_config file, this will allow you to easily set this up and then directly connect by using ssh -F ssh_config servername.
Assuming you're logged in to server_1 and want to connect to server_3 via server_2 it would look something like this:
Host server_2
HostName xxx.xxx.xxx.xxx
Port xxxx
User server2_user
Host server_3
HostName xxx.xxx.xxx.xxx
Port xxxx
ProxyCommand ssh -F ssh_config server_2
User server3_user
With this you can use ssh -F ssh_config server_3 and it will connect to server_2 and from there take you directly to server_3.
If you put the ssh_config in the default location you can also omit the -F ssh_config part (in the command and the config file) since it will get picked up automatically.
For more information check out this link, or search the web for 'ssh jumphost', that's a more widely used description for your setup (server 2 is jumphost for server 3 in your case).

Editing files through multi-hop ssh in Sublime Text 3

I was wondering if it is possible to edit a file with Sublime Text 3 through multi-hop SSH tunnel. In my particular case I have my Mac (let's call it A) and two Linux Machines: B and C. The files are located in C, and I access them with my machine like this:
A -> B -> C
I found these articles that can help but they only talk about editing files in B.
How to open remote files in sublime text 3
Editing files remotely via SSH on SublimeText 3
According to these articles, I can edit files in B installing rsub in the remote machine and a plugin in Sublime at A. I tried to do that in C (yes, i know it is not so useful, but who knows) but I got the error:
user#remote-C:~$ rsub
/usr/local/bin/rsub: connect: Connection refused
/usr/local/bin/rsub: line 327: /dev/tcp/localhost/52698: Connection refused
Unable to connect to TextMate on localhost:52698
I would be happy to know if there is a way to achieve this. Thanks in advance.
I will answer to myself. The solution is to do a SSH tunnelling from A to C with B in between using the ProxyCommand in the ssh config file at ~/.ssh/config.
I added these lines:
Host myMachineC
HostName NAME_OF_MACHINE_C
ProxyCommand ssh USER_IN_B#NAME_OF_MACHINE_B nc %h %p
User USER_IN_C
RemoteForward 52698 localhost:52698 # this is required by rsub
Host defines an alias for the real hostname which is written after the HostName directive. ProxyCommand is a command that is executed when you try to log in myMachineC. nc is a command that...
...by default creates a TCP socket either in listening mode (server socket) or a socket that is used in order to connect to a server (client mode) [1]
Now the machine C is accessible from A by only typing:
$ ssh myMachineC
It is recommendable that you already allowed password-less logins. To achieve this you need to have installed the public key from your home computer into the ~/.ssh/authorized_keys of each host along the way. [2]
In conclusion: With all this procedure, there will be a normal SSH connection to the intermediary machine B and then nc will be used to extend the connection to C. Using this tunnelling, the client can act as if the connection were direct using ssh. That will be useful to use with rsub.
Then, you should install and use rsub as normal and it will work like a charm.
I tried this in OSX Yosemite, but should run in almost any *nix system. I hope it will be useful for you.
Netcat Explanation and Examples
Transparent Multihop in SSH
The accepted solution didn't work for me because I use Host B as a SSH server where my SSH keys are stored. Also my SSH keys have passwords so the ProxyCommand command won't work.
But There's an easier way to do this.
You can add the following to the .ssh/config file on Host B;
Host *
RemoteForward 52698 localhost:52698
You can define a specific host or give the * wildcard for all hosts. This will forward port 52698 for all SSH sessions from Host B.

Proxy tunnel through multiple systems with Ruby Net::SSH

I need some suggestions on how to use the Ruby Net::SSH and Net::SCP gem to proxy through several systems in order to execute commands or copy files.
It's very similar (if not almost exactly the same) as this previous post I made, using basic ssh from the linux command line.
How to script multiple ssh and scp commands to various systems
For example, LOCAL is my local system.
System A is a second system connected to LOCAL
System B is a third system connected to System A only. Also, System B is configured to only allow access from System A by way of it's ssh key.
For normal ssh from the command line, I have my .ssh/config file set up in this way:
Host systemA
HostName 192.168.0.10
User A-user
Host systemB
ProxyCommand ssh -e none systemA exec /bin/nc %h %p 2>/dev/null
HostName 192.168.0.11
User B-user
IdentityFile ~/.ssh/systemA_id_dsa
From this point, as long as my pub key is in the authorized_hosts of sysA (let's assume it always will be), and sysA's pub key is in the authorized_hosts sysB (same assumption), the following will work seamlessly:
ssh systemB
I would like to implement this exact behavior in Ruby. I have some code similar to the following:
require 'net/ssh'
require 'net/ssh/proxy/command'
str = 'ssh -l A-user -i /home/A-user/.ssh/id_dsa -e none 192.168.0.10 exec /bin/nc %h %p 2>/dev/null'
proxy = Net::SSH::Proxy::Command.new(str)
Net::SSH.start('192.168.0.11', 'B-user', :proxy => proxy) do |ssh|
ssh.exec! "ls -lA"
end
Unfortunately, this isn't working. I get an authentication failure.
~/.rvm/gems/ruby-1.9.3-p327/gems/net-ssh-2.6.2/lib/net/ssh.rb:201:in `start': root (Net::SSH::AuthenticationFailed)
What am I missing here?
Did you verify that your proxy command actually works on its own from the command line? It seems you might have mixed the order of the identity keys.
SystemA already knows you(?), you should not need to specify an identity for it. This is also based on the config setup you posted.
Instead to me it seems you need to forward the identity of SystemA to SystemB in the start command:
Net::SSH.start('192.168.0.11', 'B-user',
:proxy => proxy,
:keys => [ "~/.ssh/systemA_id_dsa" ] ) do |ssh|
ssh.exec! "ls -lA"
end
And then skip just skip the identity file in the Proxy setup command.
I solved this problem with Net::SSH, but without the need for external configuration files. Net::SSH::Gateway was also helpful in my solution. I wrapped the solution into a gem called tunneler.
require "tunneler"
# Create SSH tunnel
tunnel = Tunneler::SshTunnel.new(bastion_user, bastion_host, {:keys => [bastion_key]})
# Establish remote connection
destination_host_connection = tunnel.remote(destination_user, destination_host, {:keys => [destination_key]})
# Upload file to destination host via tunnel
destination_host_connection.scp(local_file_path, destination_file_path)
# Execute command on destination host via tunnel
response = destination_host_connection.ssh(command)

Resources