我理解 Open Directory 是 OpenLDAP + SASL(密码服务器)+ Kerberos。看来 OpenLDAP 遵从 SASL 进行身份验证;我不知道 Kerberos。
我想通过脚本更改用户密码,最好是远程更改,并且我希望正确更改密码。(即,在任何情况下,我都不希望用户根据他们针对进入 Open Directory 的三个服务中的哪一个进行身份验证而拥有不同的密码。)
我可以dsimport
从未绑定到目录的机器上通过网络顺利完成操作,但是,当您尝试导入密码时(尽管将 AuthType 设置为 dsAuthMethodStandard:dsAuthClearText),只有在之前未设置密码的情况下才会起作用。(我相信可以设置 Crypt 密码,但我担心这意味着只有 OD 的 LDAP 部分会知道当前密码。)
除了启动与服务器的 ssh 会话并在那里更改密码外,我还能做什么吗?如果我这样做,是否有任何命令可以让我在一行中指定多个用户及其新密码?
哪些命令可以更改所有打开的目录密码?有没有哪个命令是比较好的?
apropos password
给出了这些有趣的结果:
- kpasswd(1) - 更改用户的 Kerberos 密码
- ldappasswd(1) - 更改 LDAP 条目的密码
- lppasswd(1) - 添加、更改或删除摘要密码
- passwd(1)——修改用户密码
- pwpolicy(8) - 获取和设置密码策略
- saslpasswd2(8) - 设置用户的 sasl 密码
- slappasswd(8) - OpenLDAP 密码实用程序
我会查看一些手册页,并且我认为这pwpolicy
是最好的选择,但我很想知道使用这些手册页是否有任何微妙之处(例如,不要在未更改 LDAP 和 SASL 密码的情况下更改 Kerberos 密码),以及它们是否可以在没有 ssh 会话的情况下远程工作。
答案1
我遇到的最方便的答案是将 passwd 命令与 dscl 结合使用。以下是交互式会话的输出(密码被星号替换):
$ dscl -u diradmin -p ces
Password:
> cd /LDAPv3/127.0.0.1/
/LDAPv3/127.0.0.1 > auth diradmin *****
/LDAPv3/127.0.0.1 > passwd Users/Atwo807 *****
/LDAPv3/127.0.0.1 > passwd Users/Atwo249 *****
/LDAPv3/127.0.0.1 > passwd Users/doesnotexist foobar
passwd: Invalid Path
<dscl_cmd> DS Error: -14009 (eDSUnknownNodeName)
/LDAPv3/127.0.0.1 > exit
Goodbye
这是进行更改的 Python 脚本。您将需要 pexpect 模块(sudo easy_install pexpect
应该会为您获取;我认为您不需要安装开发工具)。
#!/usr/bin/env python
import pexpect
def ChangePasswords(host, path, diradmin, diradmin_password, user_passwords, record_type='Users'):
"""Changes passwords in a Open Directory or similar directory service.
host = the dns name or IP of the computer hosting the directory
path = the pathname to the directory (ex. '/LDAPv3/127.0.0.1')
diradmin = the directory administrator's shortname (ex. 'diradmin')
diradmin_password = the directory administrator's password
user_passwords = a dictionary mapping record names (typically, user's short
names) onto their new password
record_type = the sort of records you are updating. Typically 'Users'
Returns a tuple. The first entry is a list of all records (users) who
failed to update. The second entry is a list of all records (users)
who successfully updated.
"""
failed_list = []
succeeded_list = []
prompt = " > "
child = pexpect.spawn("dscl -u %s -p %s" % (diradmin, host))
if not (ReplyOnGoodResult(child, "Password:", diradmin_password) and
ReplyOnGoodResult(child, prompt, "cd %s" % path) and
ReplyOnGoodResult(child, prompt,
"auth %s %s" % (diradmin, diradmin_password)) and
ReplyOnGoodResult(child, prompt, None)):
print "Failed to log in and authenticate"
failed_list = user_passwords.keys()
return (failed_list, succeeded_list)
# We are now logged in, and have a prompt waiting for us
expected_list = [ pexpect.EOF, pexpect.TIMEOUT,
'(?i)error', 'Invalid Path', prompt ]
desired_index = len(expected_list) - 1
for record_name in user_passwords:
#print "Updating password for %s" % record_name,
child.sendline("passwd %s/%s %s" % (record_type, record_name,
user_passwords[record_name]))
if child.expect(expected_list) == desired_index:
#print ": Succeeded"
succeeded_list.append(record_name)
else:
#print ": Failed"
failed_list.append(record_name)
child.expect(prompt)
child.sendline("exit")
child.expect(pexpect.EOF)
return (failed_list, succeeded_list)
def ReplyOnGoodResult(child, desired, reply):
"""Helps analyze the results as we try to set passwords.
child = a pexpect child process
desired = The value we hope to see
reply = text to send if we get the desired result (or None for no reply)
If we do get the desired result, we send the reply and return true.
If not, we return false."""
expectations = [ pexpect.EOF, pexpect.TIMEOUT, '(?i)error', desired ]
desired_index = len(expectations) - 1
index = child.expect(expectations)
if index == desired_index:
if reply:
child.sendline(reply)
return True
else:
return False
您可以按如下方式使用它:
# This example assumes that you have named the script given above 'pwchange.py'
# and that it is in the current working directory
import pwchange
(failed, succeeded) = pwchange.ChangePasswords("ces", "/LDAPv3/127.0.0.1",
"diradmin", "******",
{ 'Atwo807' : '*****', 'Atwo249' : '*****',
'Nonexist' : 'foobar', 'Bad' : 'bad' })
print failed, succeeded
['Bad', 'Nonexist'] ['Atwo249', 'Atwo807']
答案2
请记住,每个用户的登录钥匙串上的密码通常与他们的 Open Directory 密码保持同步。Mac OS X 非常智能,它不仅会使用在登录窗口中输入的密码来向 OD 验证用户身份,还会解锁他们的钥匙串。如果两个密码不同步,对于大多数用户来说,这将很不方便,也容易让人感到困惑。
据我所知,在服务器端更改 OD 密码的任何方法都无法修改用户的钥匙串密码。不过,AFP548 的人们为这个问题创建了一个解决方案,称为钥匙扣保管员这对遇到这种情况的用户有帮助。
答案3
尝试使用
dscl -u xxxxxx -P xxxxxxx /LDAPv3/127.0.0.1/ -authonly username
进而
dscl -u diradmin -P xxxxx /LDAPv3/127.0.0.1/ -passwd Users/username newpassword
这些命令可以从本地计算机或远程计算机运行。远程客户端必须连接到目录实用程序中的目录。来自授权计算机的远程身份验证检查如下所示
dscl -u diradmin -P 123456 /LDAPv3/ldap.remote.com/ -authonly username
我也用过
dscl -u diradmin -P 123456 -url //LDAPv3/ldap.remote.com/ -authonly username
答案4
passwd -i 打开目录 [用户]
将更改用户的本地 OpenDirectory 密码。不过我认为它需要旧密码。