如何在第一次调用 paramiko 命令时设置密码?

发布于 2024-12-23 11:01:06 字数 2985 浏览 3 评论 0原文

我有一个用户测试

当用户使用 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.

然后我做了一些谷歌,找到了一些解决方案来使用 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

在这个命令中,我们收到错误 BAD PASSWORD:它太短 所以我无法在我的函数中确定这一点。当我执行 interact.recv(-1) 时,我收到此错误,但这是我认为的标准输出。那么有没有什么办法可以确定是这个错误呢。

我检查了 paramiko 文档,发现 Channel 类有一些方法 recv_stderr_readyrecv_stderr 但此错误不会出现在该数据中。

提前感谢您的帮助。

I have one user test.

I set password change when that user login with chage command.

chage -E 2012-01-25 -M 30 -d 0 -W 10 -I 5 test

So when i try to run command 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

Then i try to connect with 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: 

And than i can set the password for the user.

If I try to connect the same with 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.

Then I do some google and find some solution to set new password with invoke_shell show I wrote one function

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)

This is working in some cases like when we give proper password with digits, alpa and special character combination.

But when we give some short password or error occur in password change like this

[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

In this command we got error BAD PASSWORD: it is too short So this I cant determine in my function. I get this error when I do interact.recv(-1) but this is the stdout I think. So is there any way to determine that this is the error.

I check the paramiko doc and find that Channel class has some method recv_stderr_ready and recv_stderr but this error not come in that data.

Thx for your help in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

惯饮孤独 2024-12-30 11:01:06

简单的答案是让您的函数在调用 shell 之前检查密码的长度(如果您知道截止值是多少)。性能也更好。但如果你不知道截止点,那是行不通的。

从您的描述中我不清楚,但如果从 interact.recv(-1) 返回错误密码消息,那么您就知道它发生了,并且可以相应地继续。似乎它应该从 std err 或 stdout 返回,因此请检查两者。如果您知道接受新密码后会返回什么文本,那么您也可以进行检查;无论你第一个得到的是什么,都会告诉你发生了什么,你的函数可以从那里继续。

The easy answer is to have your function check the length of the password BEFORE you invoke your shell, if you know what the cutoff is. Better performance, too. But if you don't know the cutoff, that won't work.

I'm not clear from your description, but if the BAD PASSWORD message comes back from interact.recv(-1), then you know it happened, and can proceed accordingly. It seems that it should be coming back from either std err or stdout, so check both. If you know what text comes back if the new password was accepted, then you can check for that as well; whichever one you get first tells you what happened, and your function can go on from there.

二手情话 2024-12-30 11:01:06

如果您正在寻找过期密码更改方法,这也可能有效。
(蟒蛇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')

this might work also, if you are looking for an expired password change method.
(python 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')

Usage:

change_expired_password_over_ssh('192.168.1.1', 'username', 'expired-password', 'new-password')
苦行僧 2024-12-30 11:01:06

以下几行可能有问题,并可能导致一些错误:

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

现在循环的每次迭代都会解析来自 shell 的当前\更新的输出文本。

问候,
埃尔达德

The following lines might be a problematic and might cause some bugs:

while not buff.endswith('Retype new password: '):
      resp = interact.recv(9999)
      buff += resp // this will append the output from the shell 

code fix:

it will better to use it this way

while not buff.endswith('Retype new password: '):
     resp = interact.recv(9999)
     buff = resp

now each iteration of the loop, will parse the current \ updated, output text from the shell.

Regards,
Eldad

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文