Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

x11 forwarding with paramiko

I'm trying to run a command with paramiko that should be able to open an X window. The script I'm using would something as follows:

import paramiko                                    

ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect('192.168.122.55', username='user', password='password')
transport = ssh_client.get_transport()
session = transport.open_session()

session.request_x11()
stdin = session.makefile('wb')
stdout = session.makefile('rb')
stderr = session.makefile_stderr('rb')
session.exec_command('env; xterm')
transport.accept()

print 'Exit status:', session.recv_exit_status()
print 'stdout:\n{}'.format(stdout.read())
print 'stderr:\n{}'.format(stderr.read())
session.close()

Unfortunately, when I run the script above, I get this output:

Exit status: 1
stdout:
SHELL=/bin/bash
XDG_SESSION_COOKIE=8025e1ba5e6c47be0d2f3ad6504a25ee-1347286654.617967-1932974971
SSH_CLIENT=192.168.122.1 58654 22
USER=user
MAIL=/var/mail/user
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
PWD=/home/user
LANG=en_US.UTF-8
SHLVL=1
HOME=/home/user
LOGNAME=user
SSH_CONNECTION=192.168.122.1 58654 192.168.122.55 22
DISPLAY=localhost:10.0
_=/usr/bin/env

stderr:  
xterm: Xt error: Can't open display: localhost:10.0

If I run the following command in a terminal:

ssh -X [email protected] 'env; xterm'

then I get the same environment variables (some ports changed, though), so I'd say that my environment is correct. However, I'm still missing something to make paramiko work with x11 forwarding.

A couple of things I tried are:

  • Use the handler parameter in request_x11: aside from printing values, I didn't get any further than with the default handler.
  • Use the auth_cookie parameter in request_x11: tried to hardcode a cookie value that was being used according to the xauth list output. The idea of doing this was to avoid problems that might happen according to the documentation string in paramiko itself:

If you omit the auth_cookie, a new secure random 128-bit value will be generated, used, and returned. You will need to use this value to verify incoming x11 requests and replace them with the actual local x11 cookie (which requires some knoweldge of the x11 protocol).

Is there some other thing I could do to make it work or troubleshoot the problem?

Note: This has been previously asked in:

  • superuser: the only response points to the request_x11 documentation I've already tried to use to no avail.
  • stackoverflow: the accepted response suggests to use the handler parameter, but it's wrong.
  • github: no answer provided for more than a year.
like image 896
jcollado Avatar asked Sep 10 '12 14:09

jcollado


People also ask

What can you do with Paramiko?

Paramiko helps you automate repetitive system administration tasks on remote servers. More advanced Paramiko programs send the lines of a script one at a time. It does this rather than transacting all of a command, such as df or last , synchronously to completion.

Is Paramiko safe?

Is paramiko safe to use? The python package paramiko was scanned for known vulnerabilities and missing license, and no issues were found. Thus the package was deemed as safe to use.

Why is Paramiko used?

Paramiko is a Python library that makes a connection with a remote device through SSh. Paramiko is using SSH2 as a replacement for SSL to make a secure connection between two devices. It also supports the SFTP client and server model.

What is Paramiko expect?

Paramiko Expect provides an expect-like extension for the Paramiko SSH library which allows scripts to fully interact with hosts via a true SSH connection. The class is constructed with an SSH Client object (this will likely be extended to support a transport in future for more flexibility).


1 Answers

Reading the paramiko code, I realized that paramiko only implements a way to establish an x11 channel. It does not connect the channel to the local x11 display. That is left to you.

Here is a small implementation that I have just written:

#!/usr/bin/env python

import os
import select
import sys

import paramiko
import Xlib.support.connect as xlib_connect


local_x11_display = xlib_connect.get_display(os.environ['DISPLAY'])
local_x11_socket = xlib_connect.get_socket(*local_x11_display[:3])


ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect('server', username='username', password='password')
transport = ssh_client.get_transport()
session = transport.open_session()
session.request_x11(single_connection=True)
session.exec_command('xterm')
x11_chan = transport.accept()

session_fileno = session.fileno()
x11_chan_fileno = x11_chan.fileno()
local_x11_socket_fileno = local_x11_socket.fileno()

poller = select.poll()
poller.register(session_fileno, select.POLLIN)
poller.register(x11_chan_fileno, select.POLLIN)
poller.register(local_x11_socket, select.POLLIN)
while not session.exit_status_ready():
    poll = poller.poll()
    if not poll: # this should not happen, as we don't have a timeout.
        break
    for fd, event in poll:
        if fd == session_fileno:
            while session.recv_ready():
                sys.stdout.write(session.recv(4096))
            while session.recv_stderr_ready():
                sys.stderr.write(session.recv_stderr(4096))
        if fd == x11_chan_fileno:
            local_x11_socket.sendall(x11_chan.recv(4096))
        if fd == local_x11_socket_fileno:
            x11_chan.send(local_x11_socket.recv(4096))

print 'Exit status:', session.recv_exit_status()
while session.recv_ready():
    sys.stdout.write(session.recv(4096))
while session.recv_stderr_ready():
    sys.stdout.write(session.recv_stderr(4096))
session.close()

Some notes:

  • I'm using some helper functions from python-Xlib. This is a pure python implementation of Xlib. See this question for details on installing it: How do you install Python Xlib with pip?

  • Some of the details of how I have implemented this make me believe it will only work for 1 x11 connection (hence session.request_x11(single_connection=True).) I would like to keep working at this to get it to handle multiple connections, but that will have to wait for another day.

  • This code essentially connects the following channels/sockets together in a async fashion using select.poll:

    • session.stdout -> sys.stdout
    • session.stderr -> sys.stderr
    • x11channel -> local_x11_socket
    • local_x11_socket - > x11channel
  • The paramiko module outputs alot of usefull debuging info to the logging module. You can view this by configuring the logging module:

    import logging
    logging.basicConfig(level=logging.DEBUG)
    
like image 121
Gary van der Merwe Avatar answered Oct 10 '22 06:10

Gary van der Merwe