布置Fabric时如何使Sudo无需密码

7
这个问题涉及最佳实践。我正在使用Fabric运行部署脚本。我的部署用户“deploy”需要sudo权限来重启服务。因此,我在脚本中使用Fabric的sudo函数来运行这些命令。这样做可以正常工作,但在脚本执行期间会提示输入密码。我不想在部署过程中输入密码。这里有什么最佳实践?我唯一能想到的解决方案是更改sudo权限,使其不需要为我的部署用户运行的命令输入密码。但我觉得这不太对劲。

3
你可以考虑添加一个新用户,专门用于部署,该用户具有访问所需命令而无需使用sudo的权限。 - Bartek
7个回答

6

最佳解决方案是在您的服务器上创建一个仅用于部署的用户(例如,deploy)。然后,在您的fabfile中设置env.user=deploy。接下来,在您的服务器上,您可以通过sudoers文件逐个命令地授予该用户必要的权限:

重要提示:修改sudoers文件时,请始终使用sudo visudo

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中,并通过在结尾处添加includedir /etc/suoders.d来从/etc/sudoers中包含该文件。

2

最好的方法是使用子任务。您可以在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

2
您可以使用以下方法:
fabric.api import env
# [...]
env.password = 'yourpassword'

1
这些东西会进入版本控制系统,揭示密码绝不是最好的主意,特别是当我们在谈论服务器密码时。 - Ramez Ashraf

1

看起来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)

1
使用 keyring 模块来安全地存储和访问密码。
以下是我在 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')

-1

您也可以为多台机器使用密码:

from fabric import env
env.hosts = ['user1@host1:port1', 'user2@host2.port2']
env.passwords = {'user1@host1:port1': 'password1', 'user2@host2.port2': 'password2'}

请参考此答案:https://dev59.com/L3E95IYBdhLWcg3wXsyR#5568219


-2
如Bartek所建议的那样,在sudoers文件中为部署“用户”启用免密码sudo。
类似于:
run('echo "{0} ALL=(ALL) ALL" >> /etc/sudoers'.format(env.user))

4
这并不是一种好的方法,原因有几个。首先,每次运行命令都会添加这一行。其次,sudoers文件不应以这种方式进行修改。如果sudoers文件存在错误,您可能需要手动获取root访问权限才能再次运行sudo命令。 - Ben Davis
1
我的答案是启用无密码sudo。用户如何做到这一点是另一个问题,我只是建议了一种方法。是的,用户应该意识到您提到的缺陷。 - Jasper van den Bosch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接