如何在后台正确打开 SSH 隧道?

时间:2021-05-11 20:46:09

标签: ssh

我希望通过 SSH 从远程主机建立一个端口。我希望将其实现为 oclif 插件;我希望用户体验如下所示:

laptop$ give-jupyter
http://localhost:4040/
laptop$ kill-jupyter
laptop$

...这应该相对简单;我只是*需要维护一个pidfile,对吗?类似的东西:

import child_process from 'child_process';
const childProcess = child_process.spawn('ssh', [/* flags */], {detached: true, stdio: 'ignore'});
childProcess.unref();
writeToSomePidfile({childProcess.pid);

然而,所有这些基本上都是无关紧要的。问题是我必须弄清楚这些标志!哦,那好吧;这有效:

laptop$ ssh machine -L 4040:localhost:4040
machine$ 

...但是,它也会在远程端打开一个shell。没问题,man ssh 说这就是 -L 的用途:

laptop$ ssh machine -LN 4040:localhost:4040

这很好,但它现在劫持了我用户的 shell。好吧,让我们将进程发送到后台:

laptop$ ssh machine -LN 4040:localhost:4040 &
laptop$ f
f: file not found
laptop$ fg
^C

ssh 的后台版本与 STDIN 上的 shell 进入竞争条件,一切都非常糟糕。好吧,man ssh 说这就是 -n 的用途:

laptop$ ssh machine -nLN 4040:localhost:4040 &
laptop$Job 1, 'ssh machine -nNL 40…' has ended

...好吧,那太好了:ssh 现在立即退出,隧道也是如此。

SSH 提到 -f 应该启用某种后台模式,但 ssh -fnN 也没有这样做; ssh 立即退出。

如果我不能拥有好东西,也许我可以用一个即使没有标准输入也能永远运行的命令来近似它们。服务器故障suggests

laptop$ ssh machine -nL 4040:localhost:4040 tail -f /dev/null &
laptop$

还是不行?!很好:

laptop$ ssh machine -nL 4040:localhost:4040 sleep infinity &
laptop$

这似乎在一个微小过程的低成本下起作用,远比我在编写此问题时尝试过的其他迭代要好得多,主要涉及 yes...

然而,是否有一种...在后台运行 SSH 隧道的不那么笨拙的方法?加分项:我也需要它在 OSX 笔记本电脑上工作...

1 个答案:

答案 0 :(得分:2)

我会亲自在系统上创建一个文件,该文件使用 ssh -M -S ~/jupyter-tunnel -o "ExitOnForwardFailure yes" -fN machine -L 4040:localhost:4040 控制套接字绑定到隧道。肯定比处理 PID 更容易。

开放隧道

ssh -S ~/jupyter-tunnel -O exit machine

关闭隧道

PImage stage;
PImage model;
PImage [] hat = new PImage [5];
PImage [] pants = new PImage [4];
PImage [] shirt = new PImage [5];

int selectedHat = -1;
int selectedPants = -1;
int selectedShirt = -1;

int choice = 0;
int page = 0;


void setup(){
  size (800,800);
  stage = loadImage("background.png");
  background(255);
  image(stage,0,0);
  initializeImages()
}


void draw(){
  println(mouseX, mouseY); //398, 237 for hats.
  image(stage,0,0);

  if (selectedHat > 0) {
    image(hat[selectedHat], 349,98);
  }

  if (selectedPants > 0) {
    image(pants[choice], 228,263);
  }
}

void mouseClicked() {
  if(dist(404,363, mouseX, mouseY) <90 && mousePressed) {
    selectedPants = floor(random(4));
  }

  if(dist(398,237, mouseX, mouseY) <90 && mousePressed) {
    selectedHat = floor(random(4));
  }
}

void initializeImages() {
  hat[0] = loadImage ("hat1.png");
  hat[1] = loadImage ("hat2.png");
  hat[2] = loadImage ("hat3.png");
  hat[3] = loadImage ("hat4.png");

  pants[0] = loadImage ("pant1.png");
  pants[1] = loadImage ("pant2.png");
  pants[2] = loadImage ("pant3.png");
  pants[3] = loadImage ("pant4.png");
}