0

在我的组织中,我们有许多运行 OS X Yosemite 的 Mac。每台机器都有一个默认帐户,我们的 IT 团队可以使用该帐户访问机器以提供 IT 帮助。我们希望定期更改此帐户的密码,并且随着组织中 mac 数量的增长,我们希望找到一种方法来自动执行此任务。

我编写了一个 Python 脚本,使用pexpect该脚本将 SSH 连接到每台机器并执行dscl以更改登录密码,然后通过 SSH 连接到每台机器以运行security以更改登录钥匙串密码。

这些方法是存储新旧密码old_passwordnew_password属性的类的一部分:

def _change_login_password(self, host):
    """Change the login password of a machine.

    Returns True on success, False on failure.
    """

    try:
        child = pexpect.spawn(
            "ssh default@{} dscl . passwd /Users/default".format(host))
        child.expect("Password:")
        child.sendline(self.old_password)
        child.expect("New Password: ")
        child.sendline(self.new_password)
        child.expect(
            "Permission denied. Please enter user's old password:")
        child.sendline(self.old_password)
        child.close()

        return not child.exitstatus

    except pexpect.TIMEOUT:
        return False

def _change_keychain_password(self, host, login_password):
    """Change the keychain password of a machine.

    Changes the keychain password to match the login password.

    Returns True on success, False on failure.
    """

    try:
        child = pexpect.spawn(
            "ssh default@{} security set-keychain-password"
            " login.keychain".format(host))
        child.expect("Password:")
        child.sendline(login_password)
        child.expect("Old Password: ")
        child.sendline(self.old_password)
        child.expect("New Password: ")
        child.sendline(login_password)
        child.expect("Retype New Password: ")
        child.sendline(login_password)
        child.close()

        return not child.exitstatus

    except pexpect.TIMEOUT:
        return False

这些方法都未能针对我的 OS X Yosemite 工作笔记本电脑进行测试。通过 ssh 执行的每个命令都会返回非零退出状态,并且笔记本电脑上的密码不会更改。

...但是,当我在任一方法的顶部插入pdb断点,然后在调试器中单步执行该方法而不进行任何更改时,退出状态变为 0,并且笔记本电脑上的密码发生更改。

海森堡。

在调用周围插入打印语句pexpect有时也会导致方法成功。

我已经尝试了pexepect'sdelaybeforesend属性,它在sendline将其有效负载发送到子进程之前添加了一个延迟,认为在调试器中单步执行的时间延迟可能是问题所在,但这并没有解决问题。

有谁知道我接下来可能会看哪里?我的一些同事怀疑这可能是一个tty问题。有谁知道pdb可能会如何影响环境并导致这些命令成功,或者 OS X 导致它们失败的原因是什么?

4

1 回答 1

0

啊,所以这是一件事。经过仔细检查,两个进程都以状态码 130 退出,对应于 Ctrl+C。我之前需要在expect(pexpect.EOF)这两种方法中添加一个close,以便它们有机会在被关闭之前完成。

def _change_login_password(self, host):
    """Change the login password of a machine.

    Returns True on success, False on failure.
    """

    try:
        child = pexpect.spawn(
            "ssh -ttt default@{} dscl . passwd /Users/default".format(host))
        child.expect("Password:")
        child.sendline(self.old_password)
        child.expect("New Password: ")
        child.sendline(self.new_password)
        child.expect(
            "Permission denied. Please enter user's old password:")
        child.sendline(self.old_password)
        child.expect(pexpect.EOF)  # Wait for EOF.
        child.close()

        return not child.exitstatus

    except pexpect.TIMEOUT:
        return False

    def _change_keychain_password(self, host, login_password):
    """Change the keychain password of a machine.

    Changes the keychain password to match the login password.

    Returns True on success, False on failure.
    """

    try:
        child = pexpect.spawn(
            "ssh -ttt default@{} security set-keychain-password"
            " login.keychain".format(host))
        child.expect("Password:")
        child.sendline(login_password)
        child.expect("Old Password: ")
        child.sendline(self.old_password)
        child.expect("New Password: ")
        child.sendline(login_password)
        child.expect("Retype New Password: ")
        child.sendline(login_password)
        child.expect(pexpect.EOF)  # Wait for EOF.
        child.close()

        return not child.exitstatus

    except pexpect.TIMEOUT:
        return False

正如 robertklep 所建议的那样,我也使用过ssh -t(实际上ssh -ttt,这样我就可以得到那个 tty)。这两者的结合解决了这个问题,我现在很高兴通过 ssh 更改密码。

于 2015-06-03T19:11:28.517 回答