8

我想要一种在带有fabricUbuntu 10.4的远程盒子上更新密码的方法。

我希望我fabfile.py看起来像这样:

def update_password(old_pw, new_pw):
    # Connects over ssh with a public key authentication
    run("some_passwd_cmd --old %s --new %s" % (old_pw, new_pd))

不幸的是,我所知道的唯一可以更改密码的命令是passwd,并且在 Ubuntu 10.4 上似乎没有任何方法可以将新(或旧)密码作为参数传递给passwd.

可以使用什么命令来更改 Ubuntu 10.4 上的用户密码fabric

编辑:我已经看过了usermod -p,这可能有效,但手册页不建议这样做。

编辑:由于某种原因usermod -p,在织物上也没有工作。

同样,我在 mikej 的答案上尝试了一个(有些不安全的)变体,它确实解决了这个问题:

# connecting & running as root.
from fabric.api import *
from fabric.contrib import files

files.append("%s\n%s" % (passwd, passwd), '.pw.tmp')
# .pw.tmp:
# PASSWD
# PASSWD

run("passwd %s < .pw.tmp" % user)

run("rm .pw.tmp")

这不是一个非常优雅的解决方案,但它确实有效。

感谢您的阅读。

布赖恩

4

5 回答 5

14

您可以将新密码和旧密码输入passwd使用,echo例如

echo -e "oldpass\\nnewpass\\nnewpass" | passwd

(启用反斜杠转义解释的-e选项,echo因此换行符被解释为这样)

于 2010-06-20T19:30:06.220 回答
11

诀窍是使用usermod和 Python 的组合crypt来更改您的密码:

from crypt import crypt
from getpass import getpass
from fabric.api import *

def change_password(user):
    password = getpass('Enter a new password for user %s:' % user)
    crypted_password = crypt(password, 'salt')
    sudo('usermod --password %s %s' % (crypted_password, user), pty=False)
于 2011-02-28T02:29:27.027 回答
5

我在 Ubuntu 11.04 上使用chpasswd

fabric.api.sudo('echo %s:%s | chpasswd' % (user, pass))

注意:通常这种模式不起作用:

$ sudo echo bla | restricted_command

因为只有 'echo' 获得提升的权限,而不是 'restricted_command'。

但是,在这里它可以工作,因为当使用 shell=True(默认)调用 fabric.api.sudo 时,fabric 会像这样组装命令:

$ sudo -S -p <sudo_prompt> /bin/bash -l -c "<command>"

sudo 生成一个新的 shell (/bin/bash),以 root 权限运行,然后升级后的 shell 运行命令。

使用 sudo管道的另一种方法是使用sudo tee

于 2011-06-18T04:09:08.257 回答
3

出于兴趣,我必须在一组 Solaris 机器上执行类似的任务(添加大量用户,设置他们的密码)。Solaris usermod 没有 --password 选项,所以过去我使用 Expect 来执行此操作,但编写 Expect 脚本可能会很痛苦。

所以这次我要使用 Python 的 crypt.crypt,直接编辑 /etc/shadow (当然有备份)。http://docs.python.org/release/2.6.1/library/crypt.html

评论者建议使用通过管道传输到 passwd 的各种回声咒语。AFAIK 这永远不会起作用,因为 passwd 被编程为忽略来自标准输入的输入,只接受来自交互式 tty 的输入。见http://en.wikipedia.org/wiki/Expect

于 2011-03-15T03:43:40.890 回答
1

我对其他方法没有运气。以为我会分享我用于一次性脚本的方法。

它使用自动回复在提示符处输入密码。然后我立即使所有密码过期,以便用户有机会选择自己的密码。

这不是最安全的方法,但根据您的用例,它可能很有用。

from collections import namedtuple
from getpass import getpass
import hashlib
from invoke import Responder
import uuid

from fabric import Connection, Config


User = namedtuple('UserRecord', ('name', 'password'))


def set_passwords(conn, user):
    print(f'Setting password for user, {user.name}')
    responder = Responder(
        pattern=r'(?:Enter|Retype) new UNIX password:',
        response=f'{user.password}\n',
    )
    result = conn.sudo(f'passwd {user.name}', warn=True, hide='both',
                       user='root', pty=True, watchers = [responder])
    if result.exited is not 0:
        print(f'Error, could not set password for user, "{user.name}". command: '
              f'{result.command}; exit code: {result.exited}; stderr: '
              f'{result.stderr}')
    else:
        print(f'Successfully set password for {user.name}')


def expire_passwords(conn, user):
    print(f'Expiring password for user, {user.name}')
    cmd = f'passwd --expire {user.name}'
    result = conn.sudo(cmd, warn=True, user='root')
    if result.exited is not 0:
        print(f'Error, could not expire password for user, "{user.name}". '
              f'command: {result.command}; exit code: {result.exited}; stderr: '
              f'{result.stderr}')
    else:
        print(f'Successfully expired password for {user.name}')


def gen_password(seed_string):
    # Don't roll your own crypto. This is for demonstration only and it is
    # expected to only create a temporary password that requires changing upon
    # initial login. I am no cryptography expert, hence this alternative
    # simplified answer to the one that uses crypt, salt, etc - 
    # https://stackoverflow.com/a/5137688/1782641.
    seed_str_enc = seed_string.encode(encoding='UTF-8')
    uuid_obj = uuid.UUID(int=int(hashlib.md5(seed_str_enc).hexdigest(), 16))
    return str(uuid_obj)[:8]


def some_function_that_returns_something_secret(conn):
    return f'dummy-seed-{conn}'

sudo_pass = getpass('Enter your sudo password:')
config = Config(overrides={'sudo': {'password': sudo_pass}})
with Connection('vm', config=config) as vm_conn:
    print(f'Making a new connection to {vm_conn.host}.')
    # I usually use the sudo connection here to run a command that returns a
    # reproducible string that only the sudo user could get access to be used 
    # for user_record.password bellow. Proceed with caution, this is not a 
    # recommended approach
    seed = some_function_that_returns_something_secret(vm_conn)
    user_record = User(name='linux_user', password=gen_password(seed))
    set_passwords(vm_conn, user_record)
    expire_passwords(vm_conn, user_record)
    print(f'Done! Disconnecting from {vm_conn.host}.')

# So that you know the temporary password, print user_record or save to file
# `ssh linux_user@vm` and it should insist that you change password
print(user_record)

于 2019-06-22T09:39:12.653 回答