A how-to for ssh-agent forwarding via Paramiko. Specifically, I used Paramiko v1.15.2 in this example.
Paramiko's docs do not document the API required to do ssh-agent forwarding. I ended up finding out how by reading pull requests for ssh-agent forwarding features in frameworks that use Paramiko under the covers, like fabric and ansible.
Besides attempting to document this process here, I've opened a bug with Paramiko to document this API in their official docs.
# get a paramiko transport - get it directly, or from a client. call it "t"
session = t.get_session()
paramiko.agent.AgentRequestHandler(s) # <---UNDOCUMENTED
# do whatever you want with your session
agent.py:
# Author: toejough
# Website: https://github.com/toejough
'''
Tries to hop connections via 'Public-key' auth type and SSH agent.
'''
# [ Imports ]
# [ - Python ]
from argparse import ArgumentParser
import sys
import time
# [ - Third Party ]
import paramiko
# [ Main ]
# Arg parsing
p = ArgumentParser(description=__doc__)
p.add_argument(
'host',
help='The host to connect to',
metavar='<hostname>'
)
p.add_argument(
'port',
help='The port to connect to',
metavar='<port>',
type=int
)
p.add_argument(
'username',
help='The username to connect as',
metavar='<username>'
)
p.add_argument(
'--debug',
help='Print verbose messages and full stack traces on internal failures',
action='store_true'
)
args = p.parse_args()
host, port, username = args.host, args.port, args.username
# Connection Attempt
try:
# Start the client
client = paramiko.client.SSHClient()
client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
client.load_system_host_keys()
client.connect(host, port, username)
# get a session
s = client.get_transport().open_session()
# set up the agent request handler to handle agent requests from the server
paramiko.agent.AgentRequestHandler(s) # <--UNDOCUMENTED??!!
# get a shell
s.get_pty()
s.invoke_shell()
except Exception as e:
# if debugging, just re-raise the error so the full stacktrace is printed
if args.debug:
raise
# On failure, print failure only (not full bt, as is the default without the try/catch)
print e
exit(1)
def recv():
while s.recv_ready():
print s.recv(sys.maxint)
while s.recv_stderr_ready():
print >> sys.stderr, s.recv_sterr(sys.maxint)
def send(text):
s.sendall(text + "\n")
time.sleep(0.1)
recv()
time.sleep(0.1)
recv()
# Play.
import pdb; pdb.set_trace() # XXX BREAKPOINT
exit(0)
Now, from Bash:
eval `ssh-agent`
ssh-add ~/.ssh/id_rsa
python agent.py
You might also want to point out that it's not useful to set up an AgentRequestHandler if there is no local agent, since in this case the connection will hang with a stack trace such as the one below as soon as a remote process tries to talk to the agent over the forwarded channel:
One way to test whether a local agent is in place is to check
paramiko.Agent.get_keys()
for non-emptiness.