Passing SSH password input via python - python

I have written a simple code wherein I invoked ssh to one of my lab Device ip through os.system. Now the problem for me is how do I supply a password? Can I do it via python?
Below is the code that I have been using:
import os
os.system("ssh 192.168.1.100")
I tried to understand how the os module works but so far am not able to supply password argument - how do I supply a password via python to this program?
My environment is Bash.

Install sshpass, then launch the command with os.system like that:
os.system("sshpass -p \'yourpassword\' ssh -o StrictHostKeyChecking=no yourusername#hostname")
Or you can use a fabric script to copy your public key into the system then you will not need a password for connection
from fabric.api import env, sudo, run
def copy_pub_key(ip, user, password, pub_key):
env.host_string = ip
env.user = user
env.password = password
if user == "root":
sudo('echo \'{pub_key}\' >> /root/.ssh/authorized_keys'.format(
pub_key=pub_key,
user=user
))
else:
sudo('echo \'{pub_key}\' >> /home/{user}/.ssh/authorized_keys'.format(
pub_key=pub_key,
user=user
))

Related

paramiko ssh source ~/.bashrc

I need to automate a some start up processes on a remote machine running ROS. To do this, i'm trying to use paramiko to log into the remote machine via ssh and launch the launch file.
The issue that i'm having is that my ~/.bashrc file is not sourced.
I can source source /opt/ros/noetic/setup.bash and get roscore to work, but i can't then find any of my launch files as my work space is not sourced:
command = 'source /opt/ros/noetic/setup.bash && roscore'
my bashrc file contains both source /opt/ros/noetic/setup.bash and source source /home/ben/catkin_ws/devel/setup.bash but whenever i source this file as before, i can't even get roscore to work -
command = 'source ~/.bashrc && roscore'
Connected to 192.168.XX.XX
bash: roscore: command not found
A minimal working example -
#! /usr/bin/env python3
import paramiko
import numpy as np
import os
class Paramiko():
def __init__(self, hostname, username, password, port):
self.hostname = hostname
self.username = username
self.password = password
self.port = port
paramiko.util.log_to_file("paramiko.log")
def ExecuteCommand(self, command):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.hostname, username = self.username, password = self.password)
print("Connected to %s" % self.hostname)
except paramiko.AuthenticationException:
print("Failed to connect to %s due to wrong username/password" %self.hostname)
exit(1)
except Exception as e:
print(e.message)
exit(2)
try:
stdin, stdout, stderr = ssh.exec_command(command)
except Exception as e:
print(e.message)
err = ''.join(stderr.readlines())
out = ''.join(stdout.readlines())
final_output = str(out)+str(err)
print(final_output)
return final_output
def main():
hostname = "192.168.XX.XX"
username = "ben"
password = "lol_nice_try"
port = 22
command = 'source ~/.bashrc && roslaunch some_package some_launchfile.launch'
para = Paramiko(hostname, username, password, port)
answer = para.ExecuteCommand(command)
if __name__ == "__main__":
main()
I'm considering using a bash script to do it, or maybe using os.system to do it, but that would be a fresh start and have it's own problems.
Open to ideas. In doing some reading i'm lead to believe that the paramiko ssh isn't actually a login session?
I've tried setting try get_pty=true when calling exec_command as per Problems with python interpertor after ssh with paramiko into a remote machine but that doesn't do anything. I'm not even sure what that option actually does, as the paramiko documentation doesn't appear to have anything about it.
Another comment on that thread says something about having a dedicated profile, but isn't that what ~/.bashrc is?
Not an answer to the original question, but a work around is to use pexpect and pxssh -
from pexpect import pxssh
try:
s = pxssh.pxssh()
hostname = '192.168.XX.XX'
username = 'ben'
password = 'cmon_guy'
s.login(hostname, username, password)
s.sendline('roslaunch system_diagnostics system_diagnostics_example_node.launch') # run a command
s.prompt() # match the prompt
print(s.after) # print everything before the prompt.
s.logout()
except pxssh.ExceptionPxssh as e:
print("pxssh failed on login.")
print(e)
Not quite sure how to get it to output the ROS_INFO as it's being sent to the terminal, without closing the connection, but that's a question for another day at this point.
Run the command after launching an interactive session.
Modify the command like below:
command = 'bash --login -c "roscore"'
and then execute using paramiko.
From man bash
When bash is invoked as an interactive login shell, or as a
non-interactive shell with the --login option, it first reads and
executes commands from the
file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in
that order, and reads
and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is
started to inhibit this
behavior.
you could alternatively use systemd and create services for roscore and every launch file you want to start and stop and then you can execute sudo systemctl start roscore.service to start your roscore you could do the same for other launch files
this is an ros service template you could follow :
`
[Unit]
Description=navigation
After=NetworkManager.service time-sync.target
[Service]
Type=forking
User=youruser
ExecStart=/bin/bash -c "bashfile_location/bashscript.bash & while ! echo exit > /dev/null; do sleep 1; done"
Restart=on-failure
[Install]
WantedBy=multi-user.target`
and you should create a bash file to start roscore or really any other ros file node or launch
#!/bin/bash
source /opt/ros/noetic/setup.bash
source /home/username/catkin_ws/devel/setup.bash
source ~/.bashrc
roslaunch package launchfile
and don't forget chmod +x
sorry if you find my answer disorganized

SSHing from within a python script and run a sudo command having to give sudo password

I am trying to SSH into another host from within a python script and run a command that requires sudo.
I'm able to ssh from the python script as follows:
import subprocess
import sys
import json
HOST="hostname"
# Ports are handled in ~/.ssh/config since we use OpenSSH
COMMAND="sudo command"
ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
error = ssh.stderr.readlines()
print(error)
else:
print(result)
But I want to run a command like this after sshing :
extract_response = subprocess.check_output(['sudo -u username internal_cmd',
'-m', 'POST',
'-u', 'jobRun/-/%s/%s' % (job_id, dataset_date)])
return json.loads(extract_response.decode('utf-8'))[0]['id']
How do I do that?
Also, I don't want to be providing the sudo password every time I run this sudo command, for that I have added this command (i.e., internal_cmd from above) at the end of visudo in the new host I'm trying to ssh into. But still when just typing this command directly in the terminal like this:
ssh -t hostname sudo -u username internal_cmd -m POST -u/-/1234/2019-01-03
I am being prompted to give the password. Why is this happening?
You can pipe the password by using the -S flag, that tells sudo to read the password from the standard input.
echo 'password' | sudo -S [command]
You may need to play around with how you put in the ssh command, but this should do what you need.
Warning: you may know this already... but never store your password directly in your code, especially if you plan to push code to something like Github. If you are unaware of this, look into using environment variables or storing the password in a separate file.
If you don't want to worry about where to store the sudo password, you might consider adding the script user to the sudoers list with sudo access to only the command you want to run along with the no password required option. See sudoers(5) man page.
You can further restrict command access by prepending a "command" option to the beginning of your authorized_keys entry. See sshd(8) man page.
If you can, disable ssh password authentication to require only ssh key authentication. See sshd_config(5) man page.

SSH into Remote machine and edit file as a root user using Python

I am working on a Python script that can SSH into multiple remote CentOS machines and change the value of 'ONBOOT' from 'yes' to 'no' in '/etc/sysconfig/network-scripts/ifcfg-eth1'.
I can SSH into remote machines using Paramiko with my user credentials.
In order to edit the '/etc/sysconfig/network-scripts/ifcfg-eth1' file I have to become a sudo user and then only I can edit the file.
The Problem I am facing with my script is I cannot login directly as a root user into CentOS. I should first login with my user credentials and Then change to root using 'sudo -s' and password.
Is there any way to ssh into the remote machine with my user credentials and change to root user and edit the file.?
First, make a connection using ssh.connect of paramiko and then
import paramiko
ssh = paramiko.SSHClient()
ssh.connect("hostname", username = "username", password = "password")
cmd = "echo {} | sudo -S {}".format("password", "touch /opt/giri")
ssh.exec_command(cmd)
The logined user is a normal user with sudo permission. So first the script logins as "normal user" and then executes the command with sudo permission.
-S option of the sudo command to make sudo get the its from stdinread the
password from the standard input instead of using the
terminal device.

How fabric work with 'sudo su user'

My request is simple:
ssh to a remote server with user0
switch user to user1 using: 'sudo su user1'
list all items in current folder
My expected code:
def startRedis():
run('sudo su - user1')
print(run('ls'))
However, it ends with out: user1#server:~$
And waiting for my interactive command forever, never executing the second line. It seems sudo su opened a new shell.
Can anyone help solving this simple task?
You can set sudo_user property in env. this way fabric will switch user to the desired user.
Official doc: http://docs.fabfile.org/
Password for switching user can be specified in the env. itself to avoid getting a prompt when the method is invoked.
fabfile.py
from fabric.api import env, sudo
env.sudo_user='user1'
env.password = '***'
def list_items():
sudo('ls')
Run below command & specify the hosts after -H
fab -H host1 list_items

Python ssh tunneling over multiple machines with agent

A little context is in order for this question: I am making an application that copies files/folders from one machine to another in python. The connection must be able to go through multiple machines. I quite literally have the machines connected in serial so I have to hop through them until I get to the correct one.
Currently, I am using python's subprocess module (Popen). As a very simplistic example I have
import subprocess
# need to set strict host checking to no since we connect to different
# machines over localhost
tunnel_string = "ssh -oStrictHostKeyChecking=no -L9999:127.0.0.1:9999 -ACt machine1 ssh -L9999:127.0.0.1:22 -ACt -N machineN"
proc = subprocess.Popen(tunnel_string.split())
# Do work, copy files etc. over ssh on localhost with port 9999
proc.terminate()
My question:
When doing it like this, I cannot seem to get agent forwarding to work, which is essential in something like this. Is there a way to do this?
I tried using the shell=True keyword in Popen like so
tunnel_string = "eval `ssh-agent` && ssh-add && ssh -oStrictHostKeyChecking=no -L9999:127.0.0.1:9999 -ACt machine1 ssh -L9999:127.0.0.1:22 -ACt -N machineN"
proc = subprocess.Popen(tunnel_string, shell=True)
# etc
The problem with this is that the name of the machines is given by user input, meaning they could easily inject malicious shell code. A second problem is that I then have a new ssh-agent process running every time I make a connection.
I have a nice function in my bashrc which identifies already running ssh-agents and sets the appropriate environment variables and adds my ssh key, but of cource subprocess cannot reference functions defined in my bashrc. I tried setting the executable="/bin/bash" variable with shell=True in Popen to no avail.
You should give Fabric a try.
It provides a basic suite of operations for executing local or remote
shell commands (normally or via sudo) and uploading/downloading files,
as well as auxiliary functionality such as prompting the running user
for input, or aborting execution.
The program below will give you a test run.
First install fabric with pip install fabric then save the code below in fabfile.py
from fabric.api import *
env.hosts = ['server url/IP'] #change to ur server.
env.user = #username for the server
env.password = #password
def run_interactive():
with settings(warn_only = True)
cmd = 'clear'
while cmd is not 'stop fabric':
run(cmd)
cmd = raw_input('Command to run on server')
Change to the directory containing your fabfile and run fab run_interactive then each command you enter will be run on the server
I tested your first simplistic example and agent forwarding worked. The only think that I can see that might cause problems is that the environment variables SSH_AGENT_PID and SSH_AUTH_SOCK are not set correctly in the shell that you execute your script from. You might use ssh -v to get a better idea of where things are breaking down.
Try setting up a SSH config file: https://linuxize.com/post/using-the-ssh-config-file/
I frequently am required to tunnel through a bastion server and I use a configuration like so in my ~/.ssh/config file. Just change the host and user names. This also presumes that you have entries for these host names in your hosts (/etc/hosts) file.
Host my-bastion-server
Hostname my-bastion-server
User user123
AddKeysToAgent yes
UseKeychain yes
ForwardAgent yes
Host my-target-host
HostName my-target-host
User user123
AddKeysToAgent yes
UseKeychain yes
I then gain access with syntax like:
ssh my-bastion-server -At 'ssh my-target-host -At'
And I issue commands against my-target-host like:
ssh my-bastion-server -AT 'ssh my-target-host -AT "ls -la"'

Categories

Resources