How do I automate this process, and include all of the password prompts?
Machine 1> ssh user2@machine2
password:
Machine 2> lxc-attach -n 0x1000
Container> ssh user3@machine3
password:
Machine 3> get_temperature.sh
temperature is 65C
I am running some automated scripts on a computer (Machine 1) with several other test systems cascaded from this one. Only the first computer has internet access and stable state. Anything can be installed here and the automation is mostly python.
The other test systems are not connected to the internet, are shutdown unexpectedly, and have their memory wiped often. Because of this, any settings I change or ssh keys I load outside of this automated script are not retained. I am just testing the hardware; none of this will be in production anywhere, and I don't have any way to change the state it is reset to.
I have tried to solve this running paramiko on Machine 1. I can connect to Machine 2, but I have no way to answer the password prompt when connecting to Machine 3 from the Machine 2. This is what I have tried:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('192.168.2.2', port=22, username='user2', password='pass2')
print("connect to machine 2")
channel = ssh.invoke_shell()
channel_data = ''
go = 1
while True:
if channel.recv_ready():
channel_data = str(channel.recv(9999))
print('Channel Data: ', channel_data)
else:
continue
if channel_data.endswith("~ >'"):
if go == 1:
channel.send('lxc-attach -n 0x1000')
channel.send('\n')
go += 1
else:
channel.send('ssh user3@192.168.3.2')
print("sending ssh command")
elif 'assword:' in channel_data:
channel.send('pass3')
channel.send('\n')
print("put the pass")
break
stdin, stdout, stderr = ssh.exec_command('pwd')
print(stdout)
Next I tried using fabric to jump from one host to another. If I try to open a connection to Machine 3 or 4 with gateway settings, this exits with the error: ChannelException(2, 'Connect failed')
. If I try to run the lxc-attach command first, it just hangs forever.
import fabric
machine2 = fabric.Connection('192.168.2.2', user='user2', connect_kwargs={"password": "pass2"})
result = machine2.run('hostname')
print('machine2 hostname', result)
result = machine2.run('lxc-attach -n 0x1000')
print('machine2 lxc ', result)
machine3 = fabric.Connection('192.168.3.2', user='user3', gateway=machine2, connect_kwargs={"password": "pass3"})
machine4 = fabric.Connection('192.168.4.2', user='user4', gateway=machine3, connect_kwargs={"password": "pass4"})
result = machine3.run('hostname')
print('machine3 hostname', result)
These similar questions about automating password entries:
Are answered with "dont".
Martin pointed me toward jump hosting, but I haven't gotten it to work. For this topic, most questions seem to point back to this one:
I believe that my problem is trying to jump host through the container. It seems the lxc-attach command opens a raw shell that doesn't get passed back.
Is there a way to spawn a shell that acts more like it's interactive counterparts? I tried pexpect
, but Machine 1 is Windows, so it doesn't work. The commands on the Pexpect Windows Documentation exit with module 'pexpect' has no attribute 'popen_spawn'
.
Pexpect doesn't work in Windows.
I created a new 'Machine 1' with Linux and the following code works:
child = pexpect.spawn(f'ssh {m2.user}@{m2.ip}')
child.expect('assword:')
child.sendline(m2.pswd)
child.sendline('\n')
print('Note: p2 sent')
child.expect(m2.prompt)
child.sendline('hostname')
print('Out: ', child.before)
child.expect(m2.prompt)
child.sendline('lxc-attach -n 0x1000')
child.sendline('\n')
print('Note: attach sent')
child.expect(container.prompt)
child.sendline('hostname')
print('Out: ', child.before)
child.expect(container.prompt)
child.sendline(f'ssh {m3.user}@{m3.ip}')
child.expect('assword:')
child.sendline(m3.pswd)
print('Note: p3 sent')
# child.expect(pexpect.EOF)
child.expect_exact(m3.prompt)
child.sendline('hostname')
child.expect_exact(m3.prompt)
print('Before: ', child.before)
child.sendline(f'ssh {m4.ip}')
print('Note: M4 connected')
child.expect_exact(m4.prompt)
child.sendline('hostname')
child.expect_exact(m4.prompt)
print('Host: ', child.before)