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.
7 回答
理想的解决方案是在您的服务器上创建一个仅用于部署的用户(例如,deploy
)。然后,env.user=deploy
在您的 fabfile 中设置。然后在您的服务器上,您可以在 sudoers 文件中逐个命令地授予用户必要的权限:
重要提示:始终用于sudo visudo
修改 sudoers 文件
Cmnd_Alias RELOAD_SITE = /bin/bash -l -c supervisorctl*, /usr/bin/supervisorctl*
deploy ALL = NOPASSWD: RELOAD_SITE
您可以Cmnd_Alias
根据部署用户的需要添加任意数量的指令,然后授予NOPASSWD
每个命令的访问权限。有关man sudoers
更多详细信息,请参阅。
我喜欢保留我的特定于部署的 sudoers 配置,并通过在最后添加:来/etc/sudoers.d/deploy
包含该文件。/etc/sudoers
includedir /etc/suoders.d
您可以使用:
fabric.api import env
# [...]
env.password = 'yourpassword'
最好的方法是使用子任务。您可以在 fabfile中提示输入密码,并且永远不要公开任何密码,也不要对远程系统上的 sudo 进行鲁莽的配置更改。
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)
请注意,您甚至可以将其与并行执行结合起来,并且该prompt
任务仍然只运行一次,而该deploy
任务为角色中的每个主机并行运行。
最后,一个如何调用它的示例:
$ fab prompt:deploy,some_arg,another_arg,key=value
似乎sudo
毕竟可能不是那么糟糕的选择。您可以指定用户可以运行哪些命令以及该命令可能采用的参数 ( man sudoers
)。如果问题只是必须输入密码,一个选项将涉及使用pexpect
模块自动登录,也许使用可以加密存储的密码:
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)
使用密钥环模块安全地存储和访问密码。
这是我使用 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')
您还可以使用 GPG 或任何其他命令行密码工具。例如:
connection.config.sudo.password = connection.local('gpg --quiet -d /path/to/secret.gpg', hide=True).strip()
该secret.gpg
文件可以用echo "mypassword" | gpg -e > secret.gpg
. 该hide
参数避免将密码回显到控制台。
要保留对 的支持--prompt-for-sudo-password
,请添加条件:
if not connection.config.sudo.password:
connection.config.sudo.password = keyring.get_password(connection.host, 'some-user')
您还可以为多台机器使用密码:
from fabric import env
env.hosts = ['user1@host1:port1', 'user2@host2.port2']
env.passwords = {'user1@host1:port1': 'password1', 'user2@host2.port2': 'password2'}
正如 Bartek 还建议的那样,在 sudoers 文件中为部署“用户”启用无密码 sudo。
就像是:
run('echo "{0} ALL=(ALL) ALL" >> /etc/sudoers'.format(env.user))