通过 Python/Fabric 从命令行更改 Unix 密码

发布于 2024-09-06 08:08:44 字数 893 浏览 3 评论 0原文

我想要一种使用

我希望我的 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")

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

感谢您的阅读。

布莱恩

I would like a way to update my password on a remote Ubuntu 10.4 box with fabric.

I would expect my fabfile.py would look something like this:

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))

Unfortunately the only command I know of that lets one change the password is passwd, and on Ubuntu 10.4 there doesn't seem to be any way to pass in the new (or old) password as an argument to passwd.

What command could one use to change a user's password on Ubuntu 10.4 via fabric?

EDIT:
I've had a look at usermod -p, and that may work but it isn't recommended by the man page.

EDIT: For some reason usermod -p wasn't working either over fabric.

As well, I've tried a (somewhat insecure) variation on mikej's answer that did solve the problem:

# 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")

It's not a very elegant solution, but it works.

Thank you for reading.

Brian

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

烟雨扶苏 2024-09-13 08:08:44

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

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

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

You could feed the new and old passwords into passwd using echo e.g.

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

(the -e option for echo enables interpretation of backslash escapes so the newlines are interpreted as such)

生死何惧 2024-09-13 08:08:44

诀窍是结合使用 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)

The trick is to use a combination of usermod and Python’s crypt to change your password:

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)
笛声青案梦长安 2024-09-13 08:08:44

我在 Ubuntu 11.04 上使用 chpasswd

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

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

$ sudo echo bla | restricted_command

因为只有“echo”获得提升的权限,而“restricted_command”则不然。

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

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

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

使用 sudo 进行管道传输的另一种方法是使用 sudo tee

I use chpasswd on Ubuntu 11.04

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

Note:
Normally this pattern doesn't work:

$ sudo echo bla | restricted_command

because only the 'echo' gets elevated privileges, not the 'restricted_command'.

However, here it works because when fabric.api.sudo is caled
with shell=True (the default), fabric assembles the command like this:

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

sudo spawns a new shell (/bin/bash), running with root privileges, and
then that escalated shell runs the command.

Another way to pipe with sudo is to use sudo tee:

梦忆晨望 2024-09-13 08:08:44

出于兴趣,我必须在一组 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 被编程为忽略来自 stdin 的输入,只接受来自交互式 tty 的输入。请参阅http://en.wikipedia.org/wiki/Expect

Out of interest, I have to do a similar task on a collection of Solaris boxes (add a whole lot of users, set their password). Solaris usermod doesn't have a --password option, so in the past I've used Expect to do this, but writing Expect scripts can be painful.

So this time I'm going to use Python's crypt.crypt, edit /etc/shadow directly (with backups, of course). http://docs.python.org/release/2.6.1/library/crypt.html

Commenters have suggested using various echo incantations piped to passwd. AFAIK this will never work, as passwd is programmed to ignore input from stdin and only accept input from an interactive tty. See http://en.wikipedia.org/wiki/Expect

赠我空喜 2024-09-13 08:08:44

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

它使用自动应答器根据提示输入密码。然后我立即使所有密码失效,以便用户有机会选择自己的密码。

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

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)

I had no luck with the other methods. Thought I would share my method that I used for a once-off throwaway script.

It uses auto-responder to type in passwords at the prompts. I then immediately expire all the passwords so that users have a chance to choose their own.

This is not the most secure method, but depending on your use case it may be useful.

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文