У меня есть один пользовательский test
.
Я устанавливаю смену пароля при входе пользователя в систему с помощью команды chage
.
chage -E 2012-01-25 -M 30 -d 0 -W 10 -I 5 test
Поэтому, когда я пытаюсь запустить команду ls
[root@localhost ~]# ssh test@localhost "ls"
WARNING: Your password has expired.
Password change required but no TTY available.
You have new mail in /var/spool/mail/root
Затем я пытаюсь подключиться к ssh
[root@localhost ~]# ssh test@localhost
You are required to change your password immediately (root enforced)
Last login: Tue Dec 27 09:55:55 2011 from localhost
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user test.
Changing password for test.
(current) UNIX password:
И чем я могу установить пароль для пользователя.
Если я попытаюсь подключить их к paramiko
.
In [1]: import paramiko
In [2]: ssh_conn = paramiko.SSHClient()
In [3]: ssh_conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
In [4]: ssh_conn.load_system_host_keys()
In [5]: ssh_conn.connect('n2001', username='root_acc23', password='test')
In [6]: a = ssh_conn.exec_command('ls')
In [7]: print a[2].read()
WARNING: Your password has expired.
Password change required but no TTY available.
Затем я делаю некоторые google и нахожу какое-то решение для установки нового пароля с помощью invoke_shell
show. Я написал одну функцию
def chage_password_change(ssh_conn, password, curr_pass):
'''
If got error on login then set with interactive mode.
'''
interact = ssh_conn.invoke_shell()
buff = ''
while not buff.endswith('UNIX password: '):
resp = interact.recv(9999)
buff += resp
interact.send(curr_pass + '\n')
buff = ''
while not buff.endswith('New password: '):
resp = interact.recv(9999)
buff += resp
interact.send(password + '\n')
buff = ''
while not buff.endswith('Retype new password: '):
resp = interact.recv(9999)
buff += resp
interact.send(password + '\n')
interact.shutdown(2)
if interact.exit_status_ready():
print "EXIT :", interact.recv_exit_status()
print "Last Password"
print "LST :", interact.recv(-1)
Это работает в некоторых случаях, например, когда мы вводим правильный пароль с цифрами, alpa и специальной комбинацией символов.
Но когда мы вводим короткий пароль или ошибку в изменении пароля, как это
[root@localhost ~]# ssh test@localhost
You are required to change your password immediately (root enforced)
Last login: Tue Dec 27 10:41:15 2011 from localhost
WARNING: Your password has expired.
You must change your password now and login again!
Changing password for user test.
Changing password for test.
(current) UNIX password:
New password:
Retype new password:
BAD PASSWORD: it is too short
В этой команде мы получили ошибку BAD PASSWORD: она слишком короткая. Это я не могу определить в своей функции. Я получаю эту ошибку, когда я interact.recv(-1)
но это, как мне кажется, это stdout. Итак, есть ли способ определить, что это ошибка.
Я проверяю paramiko doc и обнаруживаю, что класс Channel
имеет некоторый метод recv_stderr_ready
и recv_stderr
но эта ошибка не входит в эти данные.
спасибо за вашу помощь заранее.
Легкий ответ заключается в том, чтобы ваша функция проверяла длину пароля ДО того, как вы вызываете свою оболочку, если вы знаете, что такое отсечка. Лучшая производительность. Но если вы не знаете обреза, это не сработает.
Я не понимаю вашего описания, но если сообщение BAD PASSWORD возвращается из interactive.recv(-1), то вы знаете, что это произошло, и можете действовать соответствующим образом. Кажется, что он должен возвращаться из std err или stdout, поэтому проверьте оба. Если вы знаете, какой текст возвращается, если новый пароль был принят, вы также можете проверить это; в зависимости от того, что вы получите, сначала расскажет вам, что произошло, и ваша функция может продолжаться оттуда.
Следующие строки могут быть проблематичными и могут вызвать некоторые ошибки:
while not buff.endswith('Retype new password: '):
resp = interact.recv(9999)
buff += resp // this will append the output from the shell
исправление кода:
лучше использовать его таким образом
while not buff.endswith('Retype new password: '):
resp = interact.recv(9999)
buff = resp
теперь каждая итерация цикла будет анализировать текущий\обновленный, выводимый текст из оболочки.
С уважением, Эльдад
это также может сработать, если вы ищете метод смены пароля с истекшим сроком действия. (питон 3)
import time
from contextlib import closing
import paramiko
def wait_until_channel_endswith(channel, endswith, wait_in_seconds=15):
"""Continues execution if the specified string appears at the end of the channel
Raises: TimeoutError if string cannot be found on the channel
"""
timeout = time.time() + wait_in_seconds
read_buffer = b''
while not read_buffer.endswith(endswith):
if channel.recv_ready():
read_buffer += channel.recv(4096)
elif time.time() > timeout:
raise TimeoutError(f"Timeout while waiting for '{endswith}' on the channel")
else:
time.sleep(1)
def change_expired_password_over_ssh(host, username, current_password, new_password):
"""Changes expired password over SSH with paramiko"""
with closing(paramiko.SSHClient()) as ssh_connection:
ssh_connection.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_connection.connect(hostname=host, username=username, password=current_password)
ssh_channel = ssh_connection.invoke_shell()
wait_until_channel_endswith(ssh_channel, b'UNIX password: ')
ssh_channel.send(f'{current_password}\n')
wait_until_channel_endswith(ssh_channel, b'New password: ')
ssh_channel.send(f'{new_password}\n')
wait_until_channel_endswith(ssh_channel, b'Retype new password: ')
ssh_channel.send(f'{new_password}\n')
wait_until_channel_endswith(ssh_channel, b'all authentication tokens updated successfully.\r\n')
Использование:
change_expired_password_over_ssh('192.168.1.1', 'username', 'expired-password', 'new-password')