heisenbug: pexpect и смена паролей в Mac OS X через ssh
В моей организации у нас много компьютеров под управлением OS X Yosemite. У каждой из этих машин есть учетная запись по умолчанию, которую наша ИТ-команда может использовать для доступа к машине для оказания ИТ-помощи. Мы хотим периодически менять пароль для этой учетной записи, и, по мере роста числа маков в организации, мы хотим найти способ автоматизировать эту задачу.
Я написал скрипт на Python, используя pexpect
это будет SSH в каждую машину и выполнить dscl
чтобы изменить пароль для входа, затем SSH на каждой машине для запуска security
изменить логин брелок пароль.
Эти методы являются частью класса, в котором старые и новые пароли хранятся в old_password
а также new_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
"s delaybeforesend
атрибут, который добавляет задержку перед sendline
отправляет свою полезную нагрузку дочернему процессу, думая, что задержка при включении в отладчик могла быть проблемой, но это не решило проблему.
Кто-нибудь знает, где я мог бы посмотреть дальше? Некоторые из моих коллег подозревают, что tty
вопрос. Кто-нибудь знает как pdb
может влиять на среду и вызывать успешное выполнение этих команд, или что в OS X вызывает их сбой?
1 ответ
Ааа, так вот в чем дело. При ближайшем рассмотрении оба процесса выходили с кодом состояния 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
Я также, как предложил Робертклеп, использовал ssh -t
(на самом деле ssh -ttt
, так, что я чертовски хорошо получаю это tty). Сочетание обоих этих способов устранило проблему, и теперь я с радостью меняю пароли по ssh.