标题:RELAY 出处:Felix021 时间:Wed, 22 Feb 2012 12:06:05 +0000 作者:felix021 地址:https://www.felix021.com/blog/read.php?2065 内容: 挑战:某Linux机器A有外网访问权限,但其上运行的ssh服务(22端口)仅对内网开放,希望通过外网的某Linux机器B进行RELAY,实现对机器A的ssh登录。特别地,只要能够进行ssh连接,就可以建立socks代理,实现内网其余机器的访问。 原理:(ssh服务器)A:22 <---- 连接 ~ 连接 ----> 监听B:10001 ~ 监听B:10002 <---- 连接(ssh客户端) 其中的 ~ 表示将两个连接/监听的socket的输入和输出分别连接起来。 简单实现(nc + shell): 1. 在机器B上运行引用 mkfifo pipe nc -l -p 10002 < pipe | nc -l -p 10001 > pipe 2. 在机器A上运行引用 mkfifo pipe nc localhost 22 < pipe | nc [B.ip] 10002 > pipe 3. 使用ssh客户端连接B:10001即可。 简单实现的主要问题是,一旦ssh客户端断开连接,部分/所有的nc会结束,无法再建立连接。所以需要改进: 1. 写一个死循环脚本来保证nc的运行,例如 for ((;;)); do nc localhost 22 ;pipe; done 2. 将该脚本放入 /etc/rc.local ,保证每次开机后自动运行。 还有一个蛋疼的问题是,(在我的测试中)如果ssh客户端被强制断开连接(不是 $exit ),B上面监听10002端口的那个nc不一定会结束。虽然我特意安排了B机器的脚本管道前监听10002,管道后监听10001,希望能利用SIGPIPE来搞定,但是系统似乎抽风。所以还是需要一个机制来保证一旦某个nc结束了,另一个nc也会结束。可能还有一些其他更蛋疼的情况,无法一一列出来。 为了解决nc不结束的蛋疼情况,可以用脚本来实现:记录2个nc的PID,然后定时grep之。如果只剩下1个,就把另一个也kill掉。不过我没有采用这个方案,而是写了一个c程序来处理,pipe出两对fd,fork出两个child,把两对fd dup成两个child的stdin/stdout,child分别exec执行nc,然后wait之,当wait返回以后,就用kill向两个pid送个SIGTERM,结束。然后进入下一轮循环 代码如下(此代码用于B机器,A机器只要稍微修改下exec的参数就行了):#include #include #include #include #include #include #include #include void error(const char *fmt, ...) { perror("Infomation"); fprintf(stderr, " => "); va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } int main(int argc, char *argv[]) { int fd_left[2], fd_right[2]; if (pipe(fd_left) < 0 ) { perror("pipe left failed"); return 1; } if (pipe(fd_right) < 0 ) { perror("pipe right failed"); return 1; } pid_t pid1 = fork(); if (pid1 < 0) { perror("fork1"); return 1; } if (pid1 == 0) { //child if (dup2(fd_left[0], STDIN_FILENO) < 0) { error("dup2@1@stdin"); } if (dup2(fd_right[1], STDOUT_FILENO) < 0) { error("dup2@1@stdout"); } execlp("nc", "nc", "-l", "-p", "10001", NULL); perror("execlp"); return 1; } fprintf(stderr, "pid1 = %d\n", pid1); //parent pid_t pid2 = fork(); if (pid2 < 0) { perror("fork2"); return 1; } if (pid2 == 0) { //child if (dup2(fd_right[0], STDIN_FILENO) < 0) { error("dup2@1@stdin"); } if (dup2(fd_left[1], STDOUT_FILENO) < 0) { error("dup2@1@stdout"); } execlp("nc", "nc", "-l", "-p", "10002", NULL); perror("execlp"); return 1; } fprintf(stderr, "pid2 = %d\n", pid2); int status; pid_t pid = wait(&status); error("Process[%d] exits\n", pid); kill(pid1, SIGTERM); kill(pid2, SIGTERM); return 0; } Generated by Bo-blog 2.1.0