python fabric run() sudo command without prompting for a password - python

I have this python fabric fabfile. I want to run the sudo command without prompting for a password. Would like to save the password in the file. Does Fabric3 no longer support the watchers option ? Is there any way I can put the password in the script?
from fabric.api import *
from invoke import Responder
env.user = "usera"
env.password = "password"
env.sudo_user = "usera"
env.password = "password"
env.sudo_prompt = "Password:"
sudopass = Responder (
pattern=r'Password:',
response=env.password + '\n'
)
def itm_run ():
# result = run("sudo systemctl restart S99itm", pty=True, watchers=[sudopass])
result = run("sudo systemctl restart S99itm")
print(result)

The watchers still work, otherwise check your pattern. You can also add echo=True.
For the password it is best to keep it in an env variable or such. so you can do env.password = getenv('SOMEHOST_SOMEUSER_PASSWORD')

Related

How can I invoke a program that needs data on stdin with sudo (w/ password also on stdin) from Python?

I am having this error "sudo: no tty present and no askpass program spcified" when I run the code. It's the line with 2nd subprocesscall, where I try to change the password. The user gets created perfectly, but the password is not, just gives the given error. What I am trying to do, that after you create user, the written password would be given to the user.
import subprocess
def new(username, password):
subprocess.call("echo passd23 | sudo -S adduser '{username}'".format(username=username), shell=True)
subprocess.call("echo passd23 | sudo '{username}':'{password}' | chpasswd".format(username=username, password=password), shell=True)
new("username", "password")
This is bad practice, but it's safer than what it replaces:
def new(username, password, sudopass):
p1 = subprocess.Popen(['sudo', '-S', 'adduser', username], stdin=subprocess.PIPE)
p1.communicate(sudopass)
if p1.retval != 0:
return False
p2 = subprocess.Popen(['sudo', '-S', 'chpasswd'], stdin=subprocess.PIPE)
p2.communicate('%s\n%s:%s\n' % (sudopass, username, password))
return p2.retval == 0
new('username', 'password', 'passd23')
Notably:
No user-provided strings whatsoever are parsed by a shell as code. The sudo password is passed as stdin, not on a generated command line, and the user-provided username and password in particular are never evaluated by a shell.
With chpasswd, both the sudo password and then the user's username:password are provided on stdin, one after the other.
That said -- in a real-world example, your script shouldn't have a plaintext password in memory at all; instead, it should be configured in /etc/sudoers to have passwordless privilege escalation.

Fabric run command on different hosts simultaneously

I'm using fabric and I want to download a file simultaneously on different hosts at the same time but when I use
env.hosts = ['192.168.1.2', '192.168.1.3', '192.168.1.4']
I always get No hosts found. Please specify (single) host string for connection:
from fabric.api import env , run, sudo, settings
env.user = 'root' #all the servers have the same username
env.hosts = ['192.168.1.2', '192.168.1.3', '192.168.1.4']
env.key_filename = "~/.ssh/id_rsa" # I have their ssh key
run('wget file') #The command I need to run in parrallel
I want to run this from a python code without using the fab command.
I usually use the #parallel decorator (http://docs.fabfile.org/en/1.13/usage/parallel.html) and do something like this.
env.use_ssh_config = True
env.user = 'ubuntu'
env.sudo_user = 'ubuntu'
env.roledefs = {
'uat': ['website_uat'],
'prod': ['website01', 'website02']
}
#task
def deploy(role_from_arg, **kwargs):
# on each remote download file
execute(download_file, role=role_from_arg, **kwargs)
#parallel
def download_file(*args, **kwargs):
# some code to download file here
Then i can run fab deploy:prod

How to detect expired password using Python Fabric?

Using the following code, I'm unable to get Fabric to detect an expired password prompt at login. The session doesn't timeout, and the abort_on_prompts parameter doesn't appear to be triggering. How I can configure Fabric to detect this state?
from fabric.api import env, run, execute
from fabric import network
from fabric.context_managers import settings
def host_type():
with settings(abort_on_prompts=True):
print ("Using abort mode %(abort_on_prompts)s" % env)
result = run('uname -s')
return result
if __name__ == '__main__':
print ("Fabric v%(version)s" % env)
env.user = 'myuser'
env.password = 'user67user'
env.hosts = ['10.254.254.143']
host_types = execute(host_type)
Executing this script results in a hung script, as depicted below:
Fabric v1.11.1.post1
[10.254.254.143] Executing task 'host_type'
Using abort mode True
[10.254.254.143] run: uname -s
[10.254.254.143] out: WARNING: Your password has expired.
[10.254.254.143] out: You must change your password now and login again!
[10.254.254.143] out: Changing password for myuser.
[10.254.254.143] out: (current) UNIX password:
Fabric include a way to answer prompts questions.
You can use prompts dictionary on the with settings. In the dictionary every key is the question in standard output you want to answer, and the value is your answer.
So in your example:
from fabric.api import env, run, execute
from fabric import network
from fabric.context_managers import settings
def host_type():
with settings(prompts={"(current) UNIX password": "new_password"}):
result = run('uname -s')
return result
if __name__ == '__main__':
print ("Fabric v%(version)s" % env)
env.user = 'myuser'
env.password = 'user67user'
env.hosts = ['10.254.254.143']
host_types = execute(host_type)

Plugin in Nagios does not work when using paramiko?

We're experiencing some strange issues with Nagios. We wrote a script in Python which uses paramiko, httplib and re. When we comment out the code that is written to use paramiko the script returns OK in Nagios. When we uncomment the script the status just returns (null).
This is our code
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#ssh.connect(self.get('hostname'),int(self.get('port')),self.get('username'),allow_agent=True)
ssh.connect('192.168.56.102' , 22 , 'oracle' ,allow_agent=True)
link = '127.0.0.1:4848'
stdin,stdout,stderr = ssh.exec_command('wget --no-proxy ' + link + ' 2>&1 | grep -i "failed\|error"')
result = stdout.readlines()
result = " ".join(result)
if result == "":
return MonitoringResult(MonitoringResult.OK,'Webservice up')
else:
return MonitoringResult(MonitoringResult.CRITICAL,'Webservice down %s' % result)
So when we comment out the part above
if result =="":
and add result="" above the if, it will just return 'Webservice up' in Nagios. When we enable the code above the if it will just return (null). Is there a conflict with paramiko or something?
When running the code in terminal it just returns the correct status but it doesn't show when implemented in Nagios
I found that although nagios runs as effective user "nagios" it is using the environment settings for user "root" and not able to read the root private key to make the connection.
Adding key_filename='/nagiosuserhomedir/.ssh/id_dsa' to the options for ssh.connect() solved this same problem for me (pass the private key for nagios user explicitly in the code).

Fabric Sudo No Password Solution

This question is about best practices. I'm running a deployment script with Fabric. My deployment user 'deploy' needs sudo to restart services. So I am using the sudo function from fabric to run these commands in my script. This works fine but prompts for password during script execution. I DON'T want to type a password during deployments. What's the best practice here. The only solution I can think of is changing the sudo permissions to not require password for the commands my deployment user runs. This doesn't seem right to me.
The ideal solution is to create a user on your server that is used only for deployment (eg, deploy). Then, set env.user=deploy in your fabfile. Then on your servers, you can give the user the necessary permission on a command-by-command basis in a sudoers file:
IMPORTANT: Always use sudo visudo to modify a sudoers file
Cmnd_Alias RELOAD_SITE = /bin/bash -l -c supervisorctl*, /usr/bin/supervisorctl*
deploy ALL = NOPASSWD: RELOAD_SITE
You can add as many Cmnd_Alias directives as is needed by the deploy user, then grant NOPASSWD access for each of those commands. See man sudoers for more details.
I like to keep my deploy-specific sudoers config in /etc/sudoers.d/deploy and include that file from /etc/sudoers by adding: includedir /etc/suoders.d at the end.
You can use:
fabric.api import env
# [...]
env.password = 'yourpassword'
The best way to do this is with subtasks. You can prompt for a password in the fabfile and never expose any passwords, nor make reckless configuration changes to sudo on the remote system(s).
import getpass
from fabric.api import env, parallel, run, task
from fabric.decorators import roles
from fabric.tasks import execute
env.roledefs = {'my_role': ['host1', 'host2']}
#task
# #parallel -- uncomment if you need parallel execution, it'll work!
#roles('my_role')
def deploy(*args, **kwargs):
print 'deploy args:', args, kwargs
print 'password:', env.password
run('echo hello')
#task
def prompt(task_name, *args, **kwargs):
env.password = getpass.getpass('sudo password: ')
execute(task_name, *args, role='my_role', **kwargs)
Note that you can even combine this with parallel execution and the prompt task still only runs once, while the deploy task runs for each host in the role, in parallel.
Finally, an example of how you would invoke it:
$ fab prompt:deploy,some_arg,another_arg,key=value
Seems like sudo may not be that bad of an option after all. You can specify which commands a user can run and the arguments the command may take (man sudoers). If the problem is just having to type the password, an option would involve using the pexpect module to login automatically, maybe with a password that you could store encrypted:
import pexpect, sys
pwd = getEncryptedPassword()
cmd = "yourcommand"
sCmd = pexpect.spawn('sudo {0}'.format(cmd))
sCmd.logfile_read = sys.stdout
sCmd.expect('Password:')
sCmd.sendline(pwd)
sCmd.expect(pexpect.EOF)
Use the keyring module to store and access passwords securely.
Here's how I do it with Fabric 2:
from fabric import task
import keyring
#task
def restart_apache(connection):
# set the password with keyring.set_password('some-host', 'some-user', 'passwd')
connection.config.sudo.password = keyring.get_password(connection.host, 'some-user')
connection.sudo('service apache2 restart')
You could also use GPG or any other command-line password tool. For example:
connection.config.sudo.password = connection.local('gpg --quiet -d /path/to/secret.gpg', hide=True).strip()
The secret.gpg file can be generated with echo "mypassword" | gpg -e > secret.gpg. The hide argument avoids echoing the password to the console.
To retain support for --prompt-for-sudo-password, add a conditional:
if not connection.config.sudo.password:
connection.config.sudo.password = keyring.get_password(connection.host, 'some-user')
You can also use passwords for multiple machines:
from fabric import env
env.hosts = ['user1#host1:port1', 'user2#host2.port2']
env.passwords = {'user1#host1:port1': 'password1', 'user2#host2.port2': 'password2'}
See this answer: https://stackoverflow.com/a/5568219/552671
As Bartek also suggests, enable password-less sudo for the deployment 'user' in the sudoers file.
Something like:
run('echo "{0} ALL=(ALL) ALL" >> /etc/sudoers'.format(env.user))

Categories