Как установить пароль при первом вызове команды от paramiko? - PullRequest
1 голос
/ 27 декабря 2011

У меня есть один пользователь 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 покажу, что написал одну функцию

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

В этой команде мы получили ошибку Плохой пароль: он слишком короткий Так что это я не могу определить в своей функции. Я получаю эту ошибку, когда я interact.recv(-1), но я думаю, что это стандартный вывод. Так есть ли способ определить, что это ошибка.

Я проверяю документацию paramiko и нахожу, что у класса Channel есть некоторый метод recv_stderr_ready и recv_stderr, но эта ошибка не появляется в этих данных.

Спасибо за вашу помощь заранее.

Ответы [ 3 ]

3 голосов
/ 27 декабря 2011

Простой ответ - заставить вашу функцию проверять длину пароля ДО того, как вы вызовете свою оболочку, если знаете, что такое отсечение. Лучшая производительность тоже. Но если вы не знаете обрезание, это не сработает.

Мне не понятно из вашего описания, но если сообщение BAD PASSWORD возвращается из interactive.recv (-1), то вы знаете, что это произошло, и можете действовать соответственно. Кажется, он должен возвращаться либо из std err, либо из stdout, поэтому проверьте оба. Если вы знаете, какой текст возвращается, если новый пароль был принят, то вы также можете проверить это; какой бы из них вы ни получили первым, он сообщит вам о том, что произошло, и с этого момента ваша функция может продолжаться.

1 голос
/ 22 мая 2019

это также может работать, если вы ищете метод смены пароля с истекшим сроком действия. (питон 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')
1 голос
/ 02 января 2014

Следующие строки могут быть проблематичными и могут вызывать некоторые ошибки:

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

теперь каждая итерация цикла будет анализировать текущий \ обновленный, выводить текст из оболочки.

С уважением, Eldad

...