Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 898
protonpopsicle Avatar asked Dec 18 '12 23:12

protonpopsicle


3 Answers

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.

like image 59
Ben Davis Avatar answered Oct 05 '22 17:10

Ben Davis


You can use:

fabric.api import env
# [...]
env.password = 'yourpassword'
like image 44
Gabriel Gcia Fdez Avatar answered Oct 05 '22 17:10

Gabriel Gcia Fdez


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
like image 32
Travis Mehlinger Avatar answered Oct 05 '22 17:10

Travis Mehlinger