How to run commands inside the root session in python

Viewed 343

I have to implement a scenario where I need to login as root and then perform some ssh and scp commands through python to automate certain workflows.

Example: Suppose MACHINE12 is my host and current user is test, i.e test@MACHINE12 I want a interactive code to be written in such a way that , if I put the following commands it should login as root user and the session should be maintained to perform other operations in python.

{test@MACHINE12} su

password:

once password is entered, logged in as root.

{test@MACHINE12 test}

Perform other commands here

{test@MACHINE12 test} ssh user1@abc

I tried this using the pexpect command in python, but the root session is not maintained or staying to perform other operations. The commands can be only executed if the logged in session is root because abc host need root permission.

Find my tried snippet here:

def loginAsroot():
    childTab = pexpect.spawn("su") //command su to enter to root
    childTab.expect(":")
    childTab.sendline("asdgfgh")  // entering password
    childTab.expect("#")

def runCommands():
    command = "ssh user1@abc 'cat /dfg/hello.txt'>> /doc/g/auth.txt"
    process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    output, error = process.communicate(timeout=10000)
    return output

//function call
loginAsroot()
runCommands()

I'am trying to do linux command execution it is giving permission denied error, because the root session is not staying, its getting logged out. Hence Permission denied

How can I achieve this kind of executions?

2 Answers

You're trying to use both pexpect and subprocess. These are possibly spawning different sessions. Does the following work?

import pexpect
child = pexpect.spawn('su')
child.expect(':')
child.sendline('asdgfgh')
child.expect('#')
command = "ssh user1@abc 'cat /dfg/hello.txt'>> /doc/g/auth.txt"
child.sendline(command)
child.expect('#')

I think using either of the modules throughout would solve your issue.

For SSH, I like Paramiko, because it takes care of overhead. To demonstrate, I wrote two versions of the code: one using Pexpect all the way, and the other using Paramiko. Just remember:

  1. Make sure the IP address of the remote machine is not in the /root/.ssh/known_hosts; otherwise you get the WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! error.
  2. I prefer expect_exact because expect uses regex patterns. With expect, you can get an error if you look for a prompts that use regex characters, such as $.
  3. I also prefer using su - over su if I have to use root, since it clears the environment variables.

Thanks for the question, by the way. Sometimes I forget what I just told you to remember :facepalm: !

NOTE - Tested using two Debian VM's and Python 3.9. I changed the prompts and obscured the passwords and IP addresses.

Using Pexpect (with everything echoed to STDOUT):

import sys

import pexpect

child = pexpect.spawn("su -", logfile=sys.stdout.buffer)
child.expect_exact("Password:")
child.sendline("**********")
child.expect_exact("#")
child.sendline("ssh user@192.168.X.X")
while True:
    # Must use expect_exact to avoid expect regex conflict with prompt ($)!
    index = child.expect_exact(
        ["Are you sure you want to continue connecting", "password:", "$", ])
    if index == 0:
        child.sendline("yes")
    elif index == 1:
        child.sendline("**********")
    else:
        break
child.sendline("cat hello.txt >> auth.txt")
child.expect_exact("$")
child.sendline("cat auth.txt")
child.expect_exact("$")
# If you do not use the logfile, you can make sure the file got copied using:
# print(child.before)
child.sendline("exit")
child.expect_exact("#")
child.sendline("exit")
# The child closes after the exit command (EOF); this is just to make sure
child.expect_exact(["$", pexpect.EOF, ])
child.close()
print("Script complete. Have a nice day.")

Output:

Password: **********

test@MACHINE12:~# ssh user1@192.168.X.X
ssh user1@192.168.X.X
The authenticity of host '192.168.X.X (192.168.X.X)' can't be established.
RSA key fingerprint is SHA256:********************************************
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
yes
Warning: Permanently added '192.168.X.X' (RSA) to the list of known hosts.
**********@192.168.X.X's password: **********

Last login: Sat Dec  4 20:40:12 2021 from 192.168.X.X
user1@192.168.X.X:~$ cat hello.txt >> auth.txt
cat hello.txt >> auth.txt
user1@192.168.X.X:~$ cat auth.txt
cat auth.txt
Hello, world!
user1@192.168.X.X:~$ b' cat auth.txt\r\nHello, world!\r\nuser1@192.168.X.X:~'
exit
exit
logout
Connection to 192.168.X.X closed.

test@MACHINE12:~# exit
exit
Script complete. Have a nice day.

Process finished with exit code 0

Using Paramiko (without echoing):

import paramiko
import pexpect

child = pexpect.spawn("su -")
child.expect_exact("Password:")
child.sendline("**********")
child.expect_exact("#")
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect("192.168.X.X", username="user1", password="**********")
ssh.exec_command("cat hello.txt >> auth.txt")
_, chan_out, _ = ssh.exec_command("cat auth.txt")
print("\nauth contents:", chan_out.read().decode())
ssh.close()
child.sendline("exit")
child.expect_exact(["$", pexpect.EOF, ])
child.close()
print("Script complete. Have a nice day.")

Output:

auth contents: Hello, world!

Good luck with your code!

Related