Go 的 SSH 客户端和 AIX 上的 PTY

Go's SSH client and PTY on AIX

我怀疑我会在这里得到答案,因为 AIX 是非常罕见的东西,但我至少应该尝试一下。

背景

我们有程序。该程序使用 golang.org/x/crypto/ssh 库连接到远程服务并做一些事情。该程序是大型服务的一部分,并由最终用户广泛测试。它不仅适用于所有基于 Linux 的客户端(包括相当古老的东西,如 Ubuntu 12.02),而且适用于 FreeBSD、OpenBSD、NetBSD、MacOSX 上的客户端,它可以毫无问题地工作(至少与连接有关), Solaris SPARC、HP-UX 和其他 *nixes。所以看起来它不仅仅在三星冰箱上进行过测试。昨天我确信它能够连接到冰箱并毫无问题地执行所需的操作。但那是昨天...

问题

今天我们决定将 AIX 支持添加到我们的程序中。我们部分失败了。

问题描述很简单:pty请求后程序停止运行。我的意思是我可以 ssh.RequestPty 它执行时没有任何问题,但是当我在应用程序挂起后尝试执行命令时。没有错误,什么都没有。只是挂起。

什么时候生效?

  1. 它在 PuTTY/KiTTY 中工作,所以我能够连接到远程主机。
  2. 如果我删除 requestPty - 一切正常。但是 sudo 我们需要 pty
  3. 如果我请求 session.Shell,即使请求 pty,它也可以正常工作。因此,如果我编写某种交互式 shell,它会完美运行。

到目前为止我尝试了什么

我尽可能调试。最后执行的命令是 ssh/channel.go 中的 ch.sendMessage(msg)。我的意思是它写数据包,仅此而已。没有从远程主机返回数据。

为了测试,我使用了 3 个版本的 AIX - 5.3、6.1 和 7.1。没有区别。

OpenSSH 版本不同:

所有机器都在 运行LPAR 中,但我怀疑这与问题有关。

我不知道出了什么问题。我什至不能说这是常见的 AIX 问题还是只是我们的测试机器。这是示例程序,如果它工作

应该写 IT WORKS
package main

import (
    "golang.org/x/crypto/ssh"
)

func main() {
    server := "127.0.0.1:22"
    user := "root"
    p := "password"

    config := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{ssh.Password(p)},
    }
    conn, err := ssh.Dial("tcp", server, config)
    if err != nil {
        panic(err.Error())
    }
    defer conn.Close()
    session, err := conn.NewSession()
    if err != nil {
        panic(err.Error())
    }
    defer session.Close()

    // Comment below and everything works
    modes := ssh.TerminalModes{
        ssh.ECHO:          0,
        ssh.TTY_OP_ISPEED: 14400,
        ssh.TTY_OP_OSPEED: 14400,
    }

    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        panic(err.Error())
    }
    // Comment above and everything works
    session.Run("echo 1")
    println("IT WORKS")
}

如果您附近有 AIX,并且可以 运行 此代码反对它,我将不胜感激您的反馈。

如果您有任何想法(甚至是疯狂的想法)为什么它可能会失败以及我还可以看看其他地方,请不要害羞。

更新(2017-03-02):

根据@LorinczyZsigmond 的建议,我在调试模式下启动了 sshd。结果有点奇怪。

这是示例程序执行后 Debian 9.0 OpenSSH_6.0p1 Debian-4+deb7u3, OpenSSL 1.0.1t 3 May 2016 日志的一部分:

debug1: session_input_channel_req: session 0 req pty-req
debug1: Allocating pty.
debug1: session_pty_req: session 0 alloc /dev/pts/1
debug1: SELinux support disabled
debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec

debug2: fd 3 setting TCP_NODELAY

debug3: packet_set_tos: set IP_TOS 0x10

debug1: Setting controlling tty using TIOCSCTTY.

debug2: channel 0: rfd 10 isatty
debug2: fd 10 setting O_NONBLOCK

debug3: fd 8 is O_NONBLOCK

debug2: channel 0: rcvd eof
debug2: channel 0: output open -> drain

它按预期工作。

现在来自 AIX 7.1 OpenSSH_6.0p1, OpenSSL 1.0.1e 11 Feb 2013 日志的相同块:

debug1: session_input_channel_req: session 0 req pty-req
debug1: Allocating pty.
debug1: session_pty_req: session 0 alloc /dev/pts/42
debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
debug1: Values: options.num_allow_users: 0
debug1: RLOGIN VALUE  :1
debug1: audit run command euid 0 user root command 'whoami'

setsid: Operation not permitted.

setsid: Operation not permitted. 之后它什么都不做,直到我用 Ctrl+C 杀死它。当我杀死它时 returns:

debug2: fd 4 setting TCP_NODELAY
debug3: packet_set_tos: set IP_TOS 0x10
debug2: channel 0: rfd 10 isatty
debug2: fd 10 setting O_NONBLOCK
debug3: fd 8 is O_NONBLOCK
debug2: notify_done: reading
Exiting on signal 2
debug1: do_cleanup
debug1: session_pty_cleanup: session 0 release /dev/pts/42
debug1: audit session close euid 0 user root tty name /dev/pts/42
debug1: audit event euid 0 user root event 12 (SSH_connabndn)
debug1: Return Val-1 for auditproc:0

并将whoami的结果回传给客户端。这看起来像是 SSH 服务器中的错误,但这对于 2 个不同的版本是否可行?

另一个有趣的事实是,当我使用 运行 sshdtruss(AIX 的 strace 类型)时,输出如下所示:

debug1: session_input_channel_req: session 0 req pty-req
debug1: Allocating pty.
debug1: session_pty_req: session 0 alloc /dev/pts/42
debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
debug1: Values: options.num_allow_users: 0
debug1: RLOGIN VALUE  :1
debug1: audit run command euid 0 user root command 'whoami'

debug2: fd 4 setting TCP_NODELAY

debug3: packet_set_tos: set IP_TOS 0x10

debug2: channel 0: rfd 10 isatty
debug2: fd 10 setting O_NONBLOCK

debug3: fd 8 is O_NONBLOCK

setsid: Operation not permitted.

debug2: channel 0: rcvd eof
debug2: channel 0: output open -> drain
debug2: channel 0: obuf empty
debug2: channel 0: close_write
debug2: channel 0: output drain -> closed

但是 truss 输出比 strace 有点奇怪(至少对于那些不每天使用 *nix 跟踪工具的人来说)所以我不明白什么是在日志中进行。如果有人更擅长这些东西,这里是来自 debug1: RLOGIN VALUE :1.

的跟踪数据 http://pastebin.com/YdzQwbt2 的一部分

此外,在日志中,我发现 ssh.Shell() 有效,因为它不请求 pty。它启动一个交互式会话(或类似的东西)。但就我而言,交互式会话不是一个选项。

我遇到了与 "Allocating pty" 类似的问题,然后退出了 ssh 会话。这是我的 sshd 调试日志:

sshd 断开连接并出现错误:3004-010 设置终端所有权和模式失败。

debug1: Allocating pty.
debug1: session_pty_req: session 0 alloc /dev/pts/2
debug1: Ignoring unsupported tty mode opcode 13 (0xd)
debug1: Ignoring unsupported tty mode opcode 18 (0x12)

debug1: server_input_channel_req: channel 0 request env reply 0
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req env
debug2: Ignoring env request LANG: disallowed name
debug1: server_input_channel_req: channel 0 request shell reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req shell
debug1: Values: options.num_allow_users: 0
debug1: RLOGIN VALUE :1

setsid: Operation not permitted.

The OS is AIX 7.1 (7100-04-03-1642)

我的环境的目标是通过 ssh 上的远程 ldap 用户对 AIX 上的用户进行身份验证(ldap 服务器实际上是 novell eDirectory)。 所以,我在用户身份验证方面遇到了类似的问题。

我修复了在 eDirectory 模式 (rfc2703) 中通过 ssh 登录,向用户添加了以下对象扩展:

posix帐号
posix组
影子账户
uamPosixUser(因为我不确定是否需要这个对象)

我只想指出,在 OS AIX 上,以下用户不是本地用户,不存在于 /etc/passwd/etc/group.

V.Davidov

迟到总比不到好

IBM 说这是 openssh 中的错误 - PTY 分配时的竞争条件 https://www-01.ibm.com/support/docview.wss?uid=isg1IV82042

已在包 openssh.base.server:7.5.102.1500

中修复

奇怪的是,bug 只出现在 aix 中,从来没有出现在 linux 中。尽管如此,我的问题还是解决了