How to use Paramiko with host definition in ~/.ssh/config? - python

For my SSH connections, I use this ~/.ssh/config:
Host gwhost
Hostname gw.hostname.com
User user
IdentityFile /home/user/.ssh/priv_key
ControlMaster auto
ControlPath ~/.ssh/%h-%p-%r.sock
ControlPersist 120
Host *.my-example.com
User user
IdentityFile /home/user/.ssh/priv_key
StrictHostKeyChecking no
ProxyCommand ssh -q 'gwhost' -W %h:22
From the terminal I can connect to the host like this:
ssh one.my-example.com
I want to execute some commands on a remote host using Paramiko.
I tried to do it like this:
host = 'one.my-example.com'
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
user_config_file = os.path.expanduser("~/.ssh/config")
config = SSHConfig.from_path(user_config_file)
ssh.connect(hostname=host)
stdin, stdout, stderr = ssh.exec_command('ls')
lines = stdout.readlines()
print(lines)
After starting I got this error
in <lambda>
retry_on_signal(lambda: sock.connect(addr))
TimeoutError: [Errno 110] Connection timed out
So how can I use ~/.ssh/config or maybe I shouldn't ~/.ssh/config?

Paramiko has only very limited support for OpenSSH ssh_config configuration file.
If definitely won't use ssh_config automatically, as OpenSSH ssh does.
You would have to instantiate SSHConfig class using SSHConfig.from_path. And then use SSHConfig.lookup to lookup configuration for your hostname. And then use the returned dictionary to feed the arguments of SSHClient.connect.
Obligatory warning: Do not use AutoAddPolicy – You are losing a protection against MITM attacks by doing so. For a correct solution, see Paramiko "Unknown Server".

Related

Use custom command to start SFTP server in pysftp/Paramiko

In WinSCP is an option to edit the SFTP server command/path (in the protocol options):
Is there also such an option in pysftp/Paramiko or in an another SFTP package for Python?
Thanks!
What that option in WinSCP does is that it runs SFTP over the "exec" channel, instead of the "sftp subsystem" channel. An (untested) equivalent in Python Paramiko:
ssh = paramiko.SSHClient()
# authenticate here
chan = ssh.get_transport().open_session()
chan.exec_command("/path/to/sftp-server")
sftp = paramiko.SFTPClient(chan)

python Telnet through a SSH tunnel

I wrote a python script that will start an SSH session, and afterwards it will Telnet into a switch. using pexpect, pxssh and telnetlib.
everything seems to be working and I am able to access some switches. but for the majority I get this error:
socket.error: [Errno 111] Connection refused
OR sometimes:
NO ROUTE TO HOST
I checked, there is a server listening on the default port, and my firewall is turned off.
as a note there are no restrictions on the switch, and my IP is whitelisted*
also what seems off, when I try to use SSH and TELNET commands outside the script (in shell), everything will work and I can access the switch.
I would appreciate any feedback.
from pexpect import pxssh
import sys
import telnetlib
s = pxssh.pxssh()
hostname = ('#')
username = ('##')
password = ('###')
#LOGIN TO SSH HOST
s.login(hostname,username,password)
user=('####')
passw=('#####')
tn = telnetlib.Telnet(host)
tn.read_until("username: ")
tn.write(user+"\n")
tn.read_until("password: ")
tn.write(passw+"\n")
tn.write("terminal length 0\n")
tn.write("show interface description\n")
tn.write("exit\n")
data = tn.read_all()
print data
SSH isn't really a tunnel itself; it's an application layer protocol. Telnet, which is also an application layer protocol, needs at least TCP layer tunnel. If you want to telnet inside SSH, you need to use telnet command on the remote server via SSH instead.
from pexpect import pxssh
s = pxssh.pxssh()
hostname = ('#')
username = ('##')
password = ('###')
s.login(hostname,username,password)
s.sendline('telnet [your hostname]')
s.sendline('show interface description\n')

How to change Paramiko SSH Banner/version ?

I am developing a custom SSH server and I am looking to change Paramiko (http://www.paramiko.org/) SSH Banner/version.
Here is the nmap output:
PORT STATE SERVICE VERSION
22/tcp open ssh Paramiko Python sshd 2.1.1 (protocol 2.0)
I would like to change it to :
PORT STATE SERVICE VERSION
22/tcp open ssh My sshd 1.0 (protocol 2.0)
Here is the code I am using to create my SSH server : https://github.com/paramiko/paramiko/blob/master/demos/demo_simple.py
Any ideas?
Thanks
The banner used by the client/server comes from the local_version attribute of the Transport class, so if you change it before you call start_server() or start_client() on the transport then it should work, e.g:
transport.local_version = 'SSH-2.0-My sshd 1.0'
transport.start_server(...)
Note that what nmap reports depends on which probe in nmap-service-probes is triggered, so your output in nmap might be different from what you expect. The line that matches for paramiko would be:
match ssh m|^SSH-([\d.]+)-paramiko_([\w._-]+)\r?\n| p/Paramiko Python sshd/ v/$2/ i/protocol $1/

Using Paramiko to ssh into ftp servers in Python

I'm using the code below to ssh into the ftp servers:
ssh = paramiko.SSHClient()
ssh.connect(server, username=username, password=password)
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command("locate my_file.txt")
print ssh_stdout
However, I'm using multiple servers so I replace the server argument a lot. On the main ftp server I'm trying to connect to, I get this error:
socket.error: [Errno 60] Operation timed out
Whenever I try to use other servers though, I usually get this error:
paramiko.ssh_exception.S SHException:
Server 'ftp.server.org' not found in known_hosts
Does anyone know of any possible solutions to solve either one or both of these problems?
To fix your 2nd error, you can tell Paramiko to auto-add new servers:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
Take a look at the docs.
For your second problem, you need to add the following line after ssh = paramiko.SSHClient():
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
This will allow paramiko to auto-accept unknown keys (and should allow you to SSH into those other servers)

SSH server routes tunnel by user

Diagram of what I'm trying to accomplish:
$ sftp joe#gatewayserver.horse
SSHCLIENT_JOE --------> GATEWAY_SERVER (does logic by username to determine
| socket to forward the connection to.)
|
\ 127.0.0.1:1030 CONTAINRR_SSHD_SALLY
---> 127.0.0.1:1031 CONTAINER_SSHD_JOE
127.0.0.1:1032 CONTAINER_SSHD_MRAYMOND
This seems closest to what I'm trying to do:
paramiko server mode port forwarding
http://bitprophet.org/blog/2012/11/05/gateway-solutions/
But instead of the client doing a ProxyCommand or requesting a "direct-tcpip" channel, I want the forwarding to be done by the server, invisibly for the client.
I have been trying to do this with a paramiko server by taking the Transport object of the connecting client and making a direct-tcpip channel on behalf of the client, but I'm running into roadblocks.
I'm Using https://github.com/paramiko/paramiko/blob/master/demos/demo_server.py as a template
# There's a ServerInterface class definition that overrides check_channel_request
# (allowing for direct-tcpip and session), and the other expected overides like
# check_auth_password, etc that I'm leaving out for brevity.
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', 2200))
except Exception as e:
print('*** Bind failed: ' + str(e))
traceback.print_exc()
sys.exit(1)
try:
sock.listen(100)
print('Listening for connection ...')
client, addr = sock.accept()
except Exception as e:
print('*** Listen/accept failed: ' + str(e))
traceback.print_exc()
sys.exit(1)
print('Got a connection!')
try:
t = paramiko.Transport(client)
t.add_server_key(host_key)
server = Server()
try:
t.start_server(server=server)
except paramiko.SSHException:
print('*** SSH negotiation failed.')
sys.exit(1)
# Waiting for authentication.. returns an unwanted channel object since
# it isn't the right "kind" of channel
unwanted_chan = t.accept(20)
dest_addr = ("127.0.0.1", 1030) # target container port
local_addr = ("127.0.0.1", 1234) # Arbitrary port on gateway server
# Trying to put words in the client's mouth here.. fails
# What should I do?
print(" Attempting creation of direct-tcpip channel on client Transport")
tunnel_chan = t.open_channel("direct-tcpip", dest_addr, local_addr)
print("tunnel_chan created.")
tunnel_client = SSHClient()
tunnel_client.load_host_keys(host_key)
print("attempting connection using tunnel_chan")
tunnel_client.connect("127.0.0.1", port=1234, sock=tunnel_channel)
stdin, stdout, stderr = tunnel_client.exec_command('hostname')
print(stdout.readlines())
except Exception as e:
print('*** Caught exception: ' + str(e.__class__) + ': ' + str(e))
traceback.print_exc()
try:
t.close()
except:
pass
sys.exit(1)
current output:
Read key: bc1112352a682284d04f559b5977fb00
Listening for connection ...
Got a connection!
Auth attempt with key: 5605063f1d81253cddadc77b2a7b0273
Attempting creation of direct-tcpip channel on client Transport
*** Caught exception: <class 'paramiko.ssh_exception.ChannelException'>: (1, 'Administratively prohibited')
Traceback (most recent call last):
File "./para_server.py", line 139, in <module>
tunnel_chan = t.open_channel("direct-tcpip", dest_addr, local_addr)
File "/usr/lib/python2.6/site-packages/paramiko/transport.py", line 740, in open_channel
raise e
ChannelException: (1, 'Administratively prohibited')
We currently have a straight-forward sftp server where clients connect and they are chroot-ed to their respective ftp directories.
We are wanting to move the clients into lxc containers but don't want to alter how they connect to sftp.. (Since they are probably using gui ftp clients like filezilla.) I'm also not wanting to make a bridge interface and assign new ips to all the containers. Thus the containers don't have separate ips from the host, they share the same network space.
The client containers' sshds would bind to separate ports on localhost. That way they can have unique ports, and the logic of which port is chosen could conceptually be moved out to... a simple server on the physical host.
This is more of a proof-of-concept, and general curiosity on my part.
As I mentioned in a comment above, I don't know anything about paramiko, but I can comment on this from an OpenSSH perspective, and perhaps you can translate the concepts to what you need.
NAT was mentioned in comments. NAT is something done at a lower level than SSH, not something that would be set up on the basis of an SSH login (SOCK5 notwithstanding). You'd implement it in your firewall, not in your SSH configuration. The way ProxyCommand works is to negotiate the SSH connection, then hand the client to the next hop saying "Here, negotiate with this guy too." It's something implemented right inside the SSH protocol.
You may not be totally out of luck.
A standard ProxyCommand setup might look like this, with the target port specified on the client side:
host joecontainer
User joe
ProxyCommand ssh -x -a -q -Wlocalhost:1031 gatewayserver.horse
An older fashioned version of this might have used Netcat:
host joecontainer
User joe
ProxyCommand ssh -x -a -q gatewayserver.horse nc localhost 1031
The idea here is that nc localhost 1031 is the command which provides SSH access to the "next hop" in the SSH chain. You could run any command here as long as the result of that command is a connection to an SSH daemon.
But you want the port selection to be handled by the GATEWAY rather than by the client. And therein lies a bit of a crunch, because the SSH daemon is only using the target username to select which user account's authorized_keys file to read. It's the keys which are important, not the user. By the time the server gets around to running an script or command associated with a user, the SSH negotiation is complete, and it's too late to forward the connection on to the next hop.
So ... you might consider having everyone connect to a common user, and then have the port selection done on the basis of SSH key. This is the way, for example, gitolite handles users. In your case, Joe and Sally could both connect to common#gatewayserver.horse using their DSA or RSA key.
The fun part is that all your port selection gets handled within the "common" user's .ssh/authorized_keys file. The file would look something like this:
command="/usr/bin/nc localhost 1030",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ... sally#office
command="/usr/bin/nc localhost 1031",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa ... joe#home
You can read about this under the "AUTHORIZED_KEYS FILE FORMAT" section of the sshd(8) man page.
To use this technique, we still need a client-side ProxyCommand, but because port selection happens server-side based on the client's key, everyone can use exactly the same ProxyCommand:
host mycontainer
ProxyCommand ssh -xaq common#gatewayserver.horse
Sally and Joe will run ssh-keygen to create a key pair if they haven't already. They'll send you the public key which you'll add to ~common/.ssh/authorized_keys using the format above.
When Joe connects using his key, the ssh server only runs the nc command associated with his key. And because of the ProxyCommand, that netcat's output is interpreted as a "next hope" for SSH.
I've tested this with sftp (running on my eventual target, akin to your container) and it appears to work for me.
SSH is magic. :-)
Attempting creation of direct-tcpip channel on client Transport
*** Caught exception: <class '[...]'>: (1, 'Administratively prohibited')
The container ssh server is rejecting your direct-tcpip channel request because it has been configured to refuse these requests. I gather the intent here is to proxy SFTP sessions to the correct container? And I imagine the container SSH server has been configured in the usual fashion to only permit these people to do SFTP? SFTP sessions go through a session channel, not a direct-tcpip channel.
I'm not a python coder and can't give you the specific paramiko code, but your relay agent should open a session channel to the container server and invoke the "sftp" subsystem. And if possible, your relay agent should only do this when the remote client requested an SFTP session, not for other types of channel requests.

Categories

Resources