Quantcast
Channel: User Kamil Maciorowski - Super User
Viewing all articles
Browse latest Browse all 680

Answer by Kamil Maciorowski for Equiv of `ssh a -tt ssh b` or how to use remote ControlMaster

$
0
0

Analysis

I then write a RemoteCommand ssh -tt -q b to the ssh config for host a on my laptop, so I can just run ssh a.

(For consistency I will use capital A and B even in code.)

This creates a chain consisting of the local ssh (that connects the local machine to A) and ssh -tt running on A (and connecting A to B).

Locally invoked ssh A, depending on what options and operands you (or anything, e.g. rsync) add, either allocates a tty on A or not; but the next ssh in the chain is always ssh -tt, so it always allocates a tty on B. ssh that allocates a tty cannot be used as (a part of) a transport pipe because it will break any protocol that sends binary data. See this question (and the links therein) for some insight: ssh with separate stdin, stdout, stderr AND tty.

In general in a link of sshs almost always we should either make each link allocate a tty, or make each link not allocate a tty.

A tool like rsync invokes ssh in a way that does not allocate a tty on the remote side; it needs an 8-bit clean channel. Adding ssh -tt to the chain breaks this. Or would break this, because…

In the first place a tool like rsync invokes ssh with argument(s) that constitute a remote command the tool needs. In your case this command should get to a shell on B, so it would need to be provided as additional argument(s) to the ssh inside your remote command. In fact the flow never gets to the remote command. In my tests ssh from OpenSSH 9.6 does not allow me to use the RemoteCommand option together with argument(s) building a remote command:

$ ssh -o RemoteCommand=foo stranger@nonexistent bar bazCannot execute a command-line and remote command.

Solution

In OpenSSH there is a way to supply a RemoteCommand-like command together with a command build from arguments.

This solution requires you to be able to log in to A via SSH by using a key, and to be able to edit your ~/.ssh/authorized_keys on A (in general: one of whatever files sshd on A is configured to use; but this answer assumes the defaults).

Proceed as follows:

  1. Remove the RemoteCommand ssh -tt -q b from your local SSH config.

  2. Generate a new key pair on the local computer:

    # on localssh-keygen -f ~/.ssh/special_AB -C 'Special key to connect to A and then to B.'
  3. Make A recognize the key:

    # on localssh-copy-id -i ~/.ssh/special_AB A

    (If the default key allows you to log in to A and you get All keys were skipped, retry with -f exactly one time.)

  4. Force A to run specific code when the key is used. The full code we want A to run is somewhat complicated, therefore we will put it in a separate helper script (in a moment). Now let's just associate the key with the future script.

    To do this, log in to A (to an interactive shell), open ~/.ssh/authorized_keys in a text editor, locate the line that ends with Special key to connect to A and then to B. (most likely this will be the last line). To the beginning of this line add:

    command="exec ~/.ssh/helper_AB" 

    Note there is a whitespace after the last ". The resulting line shall look similarly to the following example:

    command="exec ~/.ssh/helper_AB" ssh-ed25519 AAAAC3…

    Save the file.

  5. Still on A, create our helper script:

    # on Acat >~/.ssh/helper_AB <<'EOF'#!/bin/shset -- ssh[ -t 0 ] && set -- "$@" -texec "$@" -q -- B "$SSH_ORIGINAL_COMMAND"EOF

    And make it executable:

    # on Achmod +x ~/.ssh/helper_AB
  6. Tell your local ssh how to reach B; put this at the beginning* of your local SSH config (~/.ssh/config):

    Host B  Hostname A  IdentitiesOnly yes  IdentityFile ~/.ssh/special_ABHost *

    * Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end (elaborated in this answer).

    Host * in the snippet is in case your original file did not start with Host/Match and there were some lines applied unconditionally. If so, we want them to still apply unconditionally, not under Host B.

Now local usage of ssh B will reach B via A. Requests for a tty (or for no tty) will propagate properly. Command(s) given in the local command line will be passed properly.


Drawbacks

The solution is not perfect, there are drawbacks:

  • Upon disconnecting, you may see Connection to A closed (not Connection to B …).
  • Forwarding (tunnels) will not involve B automatically; so local tools that use ssh B to create tunnel(s) (and that obviously expect the remote side to be B) may still fail. In particular ssh -A B will expose your local agent only to A.

Possibly more. In general any implication of the fact your local ssh connects to A, not to B, may manifest itself. To solve these problems you need "nested tubes" instead of the chain (read this answer to see what I mean); but this is exactly the "ProxyJump" solution you have tried, I understand why it doesn't fit your needs.


Explanation, divagation

The core of the solution comes from this fragment of man 8 sshd:

command="command"

Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify no-pty. A quote may be included in the command by quoting it with a backslash.

This option might be useful to restrict certain public keys to perform just a specific operation. […]

The command originally supplied by the client is available in the SSH_ORIGINAL_COMMAND environment variable. […]

It seems to me that conceptually this command= is similar to RemoteCommand, still only the former supports using and accessing whatever command the user supplied in the command line. The mentioned Cannot execute a command-line and remote command seems to be an arbitrary obstacle, our solution works by using commands from two sources. It would be nice if also in case of RemoteCommand it worked and SSH_ORIGINAL_COMMAND was in the environment; we could then build a solution similar to the above, but without needing to involve a key (i.e. the solution could work with any authentication method).


Viewing all articles
Browse latest Browse all 680

Latest Images

Trending Articles



Latest Images