Mailing List Archive

Uniquely Identifying the Local TTY of an SSH Connection
Hi,

I'm working on a tool for persistent terminal sessions that works much
like tmux, and I would like to be able to make it so that people can
set things up so that when they ssh onto a remote host, they
automatically connect to a persistent session based on the local
terminal they are connecting from. The idea would be that users can
just type `ssh my-host` if their connection drops and they will
immediately be dropped back into their old session with all the
context like they left it.

Right now, I have two different approaches to this, both of which have
some problems. The first thing I tried is setting up some config on
the local machine in `~/.ssh/config` to make use of the RemoteCommand
and LocalCommand options. I basically want to do the equivalent of
sshing in and immediately running `tmux attach-session -t tty-1`
(though for my tool the attach command obviously looks a bit
different). The tricky bit is that I can't write down the name of the
local tty in my RemoteCommand because it is dynamic, and I can't have
my RemoteCommand derive it by itself because the RemoteCommand runs on
the remote host. To work around this, what I do is have a
RemoteCommand that connects to the session multiplexer and parks,
occupying a single global slot, then I have a LocalCommand which is
itself a nested invocation of `ssh` to connect to the remote host and
inform this parked RemoteCommand about the name of the local tty. To
make this a bit more concrete, the config block to make this work with
my tool looks like

```
Host = your-ssh-target-name
Hostname your.ssh.host.example.com

RemoteCommand shpool plumbing ssh-remote-command
PermitLocalCommand yes
LocalCommand ssh -oPermitLocalCommand=no -oRemoteCommand="shpool
plumbing ssh-local-command-set-metadata '%u@%h:%p$(tty)'" %n
```

This kinda works, but has several fairly big problems. A really
obvious one is that single global parking slot which opens this
protocol up to race conditions and limits the ability to quickly shell
in from multiple terminals at once. The second big problem is that it
requires sshing in twice to establish a connection, which means
authenticating twice. This can be pretty slow depending on how exactly
authentication has been set up.

The second approach I have is much simpler and, except for one big
issue, smoother. Instead of editing your local machine
`~/.ssh/config`, you just edit the `.bashrc` on your remote machine to
include the following block

```
if [[ $- =~ i ]] && [[ -n "$SSH_TTY" ]]; then
shpool attach "ssh-$(basename $SSH_TTY)"
fi
```

I think the equivilant for tmux is

```
if [[ $- =~ i ]] && [[ -n "$SSH_TTY" ]]; then
tmux attach-session -t "ssh-$(basename $SSH_TTY)"
fi
```

this works quite well, except that $SSH_TTY holds the path of the
remote pty rather than the local pty, so there is nothing that ensures
that you will reconnect to the same session if you ssh back into your
remote machine from the same local terminal emulator.

First of all, can anyone think of some obvious thing I'm missing that
will make this easier? Perhaps there is some config feature of
environment variable ssh sets or can be persuaded to set that I've
missed. If so, apologies for the noise.

If there really isn't such a feature, what do you think would be the
easiest way to add support for uniquely identifying the local tty and
would you be open to adding support for such a feature? I would be
willing to try to contribute support if the ssh maintainers are open
to it.

I can think of two possible ways to uniquely identify the local tty.
One way would be to add a new % format code that is available in the
context of a RemoteCommand which contains the result of evaluating
$(tty) on the local machine (or something semantically equivalent, it
wouldn't have to actually fork a subprocess). Another way would be to
just start injecting the information into the environment when sshd
forks a new subprocess in a new env var called something like
$SSH_LOCAL_TTY.

Finally, some administrative notes: I wasn't able to sign up for this
mailing list at
https://lists.mindrot.org/mailman/subscribe/openssh-unix-dev because
attempts to do so were met by a "Bug in Mailman version 2.1.39" page.
I'd also like to pre-emptively apologize if I've accidentally sent an
HTML email since I'm sending from a webmail client and this is my
first attempt to send plaintext email.

Thanks!
- Ethan Pailes
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Uniquely Identifying the Local TTY of an SSH Connection [ In reply to ]
> if their connection drops and they will immediately be dropped back into their old session with all the context like they left it.

Perhaps mosh fits your bill?


Other than that, would it work to just build up the right command (tmux with args) locally in a shell script thats stored in /usr/local/bin/ssh?
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Uniquely Identifying the Local TTY of an SSH Connection [ In reply to ]
On Thu, 2 Mar 2023 at 00:12, Ethan Pailes <ethan@pailes.org> wrote:
[...]
> this works quite well, except that $SSH_TTY holds the path of the
> remote pty rather than the local pty, so there is nothing that ensures
> that you will reconnect to the same session if you ssh back into your
> remote machine from the same local terminal emulator.
>
> First of all, can anyone think of some obvious thing I'm missing that
> will make this easier? Perhaps there is some config feature of
> environment variable ssh sets or can be persuaded to set that I've
> missed. If so, apologies for the noise.

Send the local tty name using SendEnv? You'd probably want to include
some kind of host identifier so you don't get collisions if sshing in
from multiple machines (although that might not matter for your use
case).

SendEnv variables are not accepted by default on the server side, so
if you don't control the server you could maybe pack them into $TERM
(which is required by the protocol for pty requests), although you'll
need to unpack it on the server side before trying to use anything
that cares about TERM.

$ TERM="vt100 myhost:/dev/pts/0" ssh localhost
Last login: Thu Mar 2 12:28:59 2023 from 127.0.0.1
$ echo $TERM
vt100 myhost:/dev/pts/0

> Finally, some administrative notes: I wasn't able to sign up for this
> mailing list at
> https://lists.mindrot.org/mailman/subscribe/openssh-unix-dev because
> attempts to do so were met by a "Bug in Mailman version 2.1.39" page.

I just tried that and didn't get that error, although I also haven't
got the confirmation email yet. Could you please try again and if it
happens again. mail me the error message off-list?

--
Darren Tucker (dtucker at dtucker.net)
GPG key 11EAA6FA / A86E 3E07 5B19 5880 E860 37F4 9357 ECEF 11EA A6FA
Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Uniquely Identifying the Local TTY of an SSH Connection [ In reply to ]
Packing the data in TERM is a great idea! I’ll see what I can do with that.

>> Finally, some administrative notes: I wasn't able to sign up for this
>> mailing list at
>> https://lists.mindrot.org/mailman/subscribe/openssh-unix-dev because
>> attempts to do so were met by a "Bug in Mailman version 2.1.39" page.
>
> I just tried that and didn't get that error, although I also haven't
> got the confirmation email yet. Could you please try again and if it
> happens again. mail me the error message off-list?

yeah I got that worked out and forgot to delete that part of my message when I resent it. Sorry about that.

>
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Uniquely Identifying the Local TTY of an SSH Connection [ In reply to ]
I've managed to figure out a scheme using SendEnv. The way it works is that I add the following to my local .bashrc

```
export LC__LOCAL_TTY_NAME="ssh-$(basename $(tty))"
```

then on the same local machine add an entry to my .ssh/config

```
Host = remote
Hostname = my.remote.host
SendEnv LC__LOCAL_TTY_NAME
ControlPath ~/.ssh/cm-%r@%h:%p
ControlMaster auto
ControlPersist 10m
```

and finally in my .bashrc on my remote host I have an entry

```
if [[ $- =~ i ]] && [[ -n "$LC__LOCAL_TTY_NAME" ]]; then
exec shpool attach "$LC__LOCAL_TTY_NAME"
fi
```

I did try packing the tty name in TERM, but using that with SendEnv would have required mutating my local TERM in a way that could have messed up programs running locally.

This approach works great, but I would also like to be able to set things up so that I can explicitly name the session I want to connect to and do `ssh main` or `ssh edit` to go directly to my remote host and attach to a session named `main` or `edit`. The SetEnv ssh config option seems to be just what the doctor ordered in order to do this, so I've added a few blocks to my local .ssh/config

```
Host = main
Hostname = my.remote.host
SetEnv LC__LOCAL_TTY_NAME=main
ControlPath ~/.ssh/cm-%r@%h:%p
ControlMaster auto
ControlPersist 10m

Host = edit
Hostname = my.remote.host
SetEnv LC__LOCAL_TTY_NAME=edit
ControlPath ~/.ssh/cm-%r@%h:%p
ControlMaster auto
ControlPersist 10m
```

this sorta works, but there seems to be a strange interaction between the Control{Path,Master,Persist} options and SetEnv. If I try to immediately ssh to my remote host from two different terminals using `ssh main` then `ssh edit`, LC__LOCAL_TTY_NAME will get set to `main` in both, presumably because of some sticky state on the shared connection. Oddly, if I first do `ssh remote` in a separate terminal to use the SendEnv mechanism, then do `ssh main` and `ssh edit` in terminals two and three, this approach works as I was hoping and terminal two has LC__LOCAL_TTY_NAME set to `main` while terminal three has it set to `edit`. This feels like a bug in ssh to me, but maybe it is actually working as intended and I'm just holding it wrong.

On the assumption that this is a bug, I've been trying to repro locally by cloning and building https://github.com/openssh/openssh-portable.git, then running the sshd it builds locally and logging in with the ssh binary it builds. Would those two components contain everything that could be in play here, or do I need to worry about using the ssh-agent from HEAD as well? (I know my setup is using my system ssh-agent because I configured an authorized_keys file with my id_rsa.pub in it, then was able to log in to the locally running sshd with no password after running ssh-add).
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Re: Uniquely Identifying the Local TTY of an SSH Connection [ In reply to ]
On Fri, 3 Mar 2023, Ethan Pailes wrote:

>
> this sorta works, but there seems to be a strange interaction between
> the Control{Path,Master,Persist} options and SetEnv. If I try to
> immediately ssh to my remote host from two different terminals using
> `ssh main` then `ssh edit`, LC__LOCAL_TTY_NAME will get set to `main`
> in both, presumably because of some sticky state on the shared
> connection. Oddly, if I first do `ssh remote` in a separate terminal
> to use the SendEnv mechanism, then do `ssh main` and `ssh edit` in
> terminals two and three, this approach works as I was hoping and
> terminal two has LC__LOCAL_TTY_NAME set to `main` while terminal three
> has it set to `edit`. This feels like a bug in ssh to me, but maybe it
> is actually working as intended and I'm just holding it wrong.

hmm, that's not supposed to happen - the environment variables are
supposed to be sent separately for multiplexed sessions. We do have
a regression test for this (regress/multiplex.sh) so I have some
confidence that ssh should be doing the right thing here.

-d
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@mindrot.org
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev