gpt4 book ai didi

linux - ssh 端口转发 ("ssh -fNL") 无法通过 expect spawn 自动提供密码

转载 作者:行者123 更新时间:2023-12-02 14:06:03 27 4
gpt4 key购买 nike

我知道要做端口转发,命令是ssh -L。我还使用其他选项来装饰它。因此,例如,最终的完整命令可能如下所示 ssh -fCNL *:10000:127.0.0.1:10001 127.0.0.1。输入密码后一切正常。

然后,因为不是只有一个端口需要转发,我决定把这个工作留给 shell 脚本,并使用 expect(tcl) 来提供密码(都一样)。

虽然对expect没有很深的理解,但还是借助网络写出了代码。该脚本成功生成 ssh 并提供正确的密码。但是当我尝试使用 ps -ef | 进行检查时,我最终发现没有这样的过程。 grep sshnetstat -anp | grep 10000.

我为 ssh 提供了 -v 选项,输出似乎没问题。

那么问题出在哪里呢?我通过 Internet 进行了搜索,但大多数问题都与端口转发无关。我不确定使用expect是否合适,而我只想让脚本自动提供密码。

这是脚本。

#!/bin/sh

# Port Forwarding

# set -x

## function definition
connection ()
{
ps -ef | grep -v grep | grep ssh | grep $1 | grep $2 > /dev/null
if [ $? -eq 0 ] ; then
echo "forward $1 -> $2 done"
exit 0
fi

# ssh-keygen -f "$HOME/.ssh/known_hosts" -R "127.0.0.1"

/usr/bin/expect << EOF
set timeout 30
spawn /usr/bin/ssh -v -fCNL *:$1:127.0.0.1:$2 127.0.0.1
expect {
"yes/no" {send "yes\r" ; exp_continue}
"password:" {send "1234567\r" ; exp_continue}
eof
}
catch wait result
exit [lindex \$result 3]
EOF
echo "expect ssh return $?"
echo "forward $1 -> $2 done"
}

## check expect available
which expect > /dev/null
if [ $? -ne 0 ] ; then
echo "command expect not available"
exit 1
fi

login_port="10000"
forward_port="10001"

## check whether the number of elements is equal
login_port_num=$(echo ${login_port} | wc -w)
forward_port_num=$(echo ${forward_port} | wc -w)
if [ ${login_port_num} -ne ${forward_port_num} ] ; then
echo "The numbers of login ports and forward ports are not equal"
exit 1
fi
port_num=${login_port_num}

## provide pair of arguments to ssh main function
index=1
while [ ${index} -le ${port_num} ] ; do
login_p=$(echo ${login_port} | awk '{print $'$index'}')
forward_p=$(echo ${forward_port} | awk '{print $'$index'}')
connection ${login_p} ${forward_p}
index=$((index + 1))
done

这里是脚本的输出

spawn /usr/bin/ssh -v -fCNL *:10000:127.0.0.1:10001 127.0.0.1
OpenSSH_7.2p2 Ubuntu-4ubuntu2.10, OpenSSL 1.0.2g 1 Mar 2016
...
debug1: Next authentication method: password
wang@127.0.0.1's password:
debug1: Enabling compression at level 6.
debug1: Authentication succeeded (password).
Authenticated to 127.0.0.1 ([127.0.0.1]:22).
debug1: Local connections to *:10000 forwarded to remote address 127.0.0.1:10001
debug1: Local forwarding listening on 0.0.0.0 port 10000.
debug1: channel 0: new [port listener]
debug1: Local forwarding listening on :: port 10000.
debug1: channel 1: new [port listener]
debug1: Requesting no-more-sessions@openssh.com
debug1: forking to background
expect ssh return 0
forward 10000 -> 10001 done

最佳答案

这应该适合你:

spawn -ignore SIGHUP ssh -f ...

更新:

另一种解决方法是:

spawn bash -c "ssh -f ...; sleep 1"

更新 2(一些解释):

ssh -f 调用 daemon()使自己成为守护进程。参见 ssh.c在源代码中:

/* Do fork() after authentication. Used by "ssh -f" */
static void
fork_postauth(void)
{
if (need_controlpersist_detach)
control_persist_detach();
debug("forking to background");
fork_after_authentication_flag = 0;
if (daemon(1, 1) == -1)
fatal("daemon() failed: %.200s", strerror(errno));
}

daemon()this 一样实现:

int
daemon(int nochdir, int noclose)
{
int fd;

switch (fork()) {
case -1:
return (-1);
case 0:
break;
default:
_exit(0);
}

if (setsid() == -1)
return (-1);

if (!nochdir)
(void)chdir("/");

if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
(void)close (fd);
}
return (0);
}

在父进程中的 _exit()setsid() 之间存在竞争条件(不确定这里的术语是否正确)> 在子进程中。这里 _exit() 总是先完成,因为“函数 _exit() 终止调用进程立即”和 setsid()重量要重得多。所以当父进程退出时,setsid()还没有生效,子进程还在和父进程在同一个 session 中。根据apue book (我指的是 2005 版,第 10 章:信号),SIGHUP 如果 session 领导者终止也会生成。在这种情况下,信号被发送到每个进程在前台进程组中。”

简而言之:

  • Expect 分配一个 pty 并在 pty 上运行 ssh。此处,ssh 将在新 session 中运行并成为 session 负责人
  • ssh -f 调用 daemon()。父进程( session 领导者)调用 _exit()。此时,子进程仍在 session 中,因此它会收到 SIGHUP,其默认行为是终止进程。

解决方法的工作原理:

  • nohup 方法 (spawn -ignore SIGHUP) 是明确要求进程忽略 SIGHUP,这样它就不会终止。
  • 对于 bash -c 'sshh -f ...; sleep 1'bash 将成为 session 负责人,sleep 1 最后会防止 session 负责人过早退出。所以在 sleep 1 之后,子 ssh 进程的 setsid() 已经完成并且子 ssh 已经 在新的进程 session 中

更新 3:

您可以通过以下修改(在 ssh.c 中)编译 ssh 并验证:

static int
my_daemon(int nochdir, int noclose)
{
int fd;

switch (fork()) {
case -1:
return (-1);
case 0:
break;
default:
// wait a while for child's setsid() to complete
sleep(1);
// ^^^^^^^^
_exit(0);
}

if (setsid() == -1)
return (-1);

if (!nochdir)
(void)chdir("/");

if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
(void)close (fd);
}
return (0);
}

/* Do fork() after authentication. Used by "ssh -f" */
static void
fork_postauth(void)
{
if (need_controlpersist_detach)
control_persist_detach();
debug("forking to background");
fork_after_authentication_flag = 0;
if (my_daemon(1, 1) == -1)
// ^^^^^^^^^
fatal("my_daemon() failed: %.200s", strerror(errno));
}

关于linux - ssh 端口转发 ("ssh -fNL") 无法通过 expect spawn 自动提供密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63951895/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com