如何使用 perl 和 Net::OpenSSH 检测远程端是不是只处理协议 1?

Posted

技术标签:

【中文标题】如何使用 perl 和 Net::OpenSSH 检测远程端是不是只处理协议 1?【英文标题】:How with perl and Net::OpenSSH can I detect if the remote side only handles protocol 1?如何使用 perl 和 Net::OpenSSH 检测远程端是否只处理协议 1? 【发布时间】:2015-01-20 02:56:31 【问题描述】:

tl;dr; 如何从脚本中捕获 stderr 以获得更具体的错误,而不是仅仅依赖来自 Net::OpenSSH 的一般错误?

我正在尝试解决一个棘手的问题。 Net::OpenSSH 仅适用于协议版本 2。但是我们有许多网络设备仅支持版本 1。我正在尝试寻找一种优雅的方法来检测远程端是否是错误的版本。

连接到版本 1 设备时,stderr 上会显示以下消息

协议主要版本不同:2 与 1

但是 Net::OpenSSH 返回的错误如下

无法建立主 SSH 连接:密码错误或主进程意外退出

这个特定的错误太笼统了,并不仅仅解决协议版本的差异。我需要通过切换到另一个库来处理协议差异,我不想为每个连接错误都这样做。

我们使用了一个相当复杂的过程,该过程最初仅用于 telnet 访问。我们加载一个“comm”对象,然后确定路由器类型等内容。该 comm 对象调用 Net::OpenSSH 来传递命令。

例子:

my $sshHandle = eval  $commsObject->go($router) ;
my $sshError = $sshHandle->ssh->error;

if ($sshError) 
    $sshHandle->connect_error = $sshError;
    return $sshHandle;

stderr 上出现协议错误的地方就在这里

    $args->ssh = eval 
        Net::OpenSSH->new(
            $args->node_name,
            user        => $args->user,
            password    => $args->tacacs,
            timeout     => $timeout,
            master_opts => [ -o => "StrictHostKeyChecking=no" ]
        );
    ;

我想做的是传入标准错误协议错误,而不是 Net::OpenSSH 传回的一般错误。我想在脚本中执行此操作,但我不确定如何从脚本中捕获 stderr。

任何想法都将不胜感激。

【问题讨论】:

【参考方案1】:

捕获主 stderr 流并在之后检查它。

看here怎么做。

您可以使用的另一种方法是打开一个到远程 SSH 服务器的套接字。它发回的第一件事是它的版本字符串。例如:

$ nc localhost 22
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-8
^C

根据这些信息,您应该能够推断出服务器是否支持 SSH v2。

最后,如果你还需要与 SSH v1 服务器通信,我的另一个模块 Net::SSH::Any 的开发版本可以使用 OS 本地 SSH 客户端来完成,尽管它会为每个命令建立一个新的 SSH 连接。

use Net::SSH::Any;

my $ssh = Net::SSH::Any->new($args->node_name,
                             user        => $args->user,
                             password    => $args->tacacs,
                             timeout     => $timeout,
                             backends    => 'SSH_Cmd',
                             strict_host_key_checking => 0);

更新:针对以下 Bill 关于在同一会话中发送多个命令的问题的评论:

在同一个会话中发送命令的问题是您必须与远程 shell 对话,并且没有办法以通用的方式可靠地做到这一点,因为每个 shell 做的事情都不同,特别是对于网络设备 shell这对自动化非常不友好。

无论如何,CPAN 上有几个模块试图做到这一点,为每种外壳(或操作系统)实现一个处理程序。例如,查看 Oliver Gorwits 的模块 Net::CLI::Interact、Net::Appliance::Session 和 Net::Appliance::Phrasebook。短语手册的方法似乎很合适。

【讨论】:

有趣的想法。我真的不想处理写入文件的开销,但第二种方法很有趣。我可能只写一个探针来处理检测。在第三个选项中,Net::OpenSSH 为每个命令打开一个新会话。我不得不在 telnet 中包装命令来解决这个问题(参考我之前的一些问题)。如果你可以开发一个不需要使用 telnet 发送多个命令的 ssh 库,那将增加很多价值。

以上是关于如何使用 perl 和 Net::OpenSSH 检测远程端是不是只处理协议 1?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 perl 的 openssh 中指定 -v 选项

如何使用 Perl 的 OpenSSH 读取输出的每一行

Net::OpenSSH 中的交互模式

Perl:使用 Net::OpenSSH 的 scp_put 拒绝权限

在 Perl 的 ssh 连接中捕获错误主机名的错误消息(使用 Net::OpenSSH)

ctl_dir /root/.libnet-openssh-perl/ 在 /usr/local/share/perl/5.18.2/Net/OpenSSH/Compat/Perl.pm 第 123