zsh4humans icon indicating copy to clipboard operation
zsh4humans copied to clipboard

More detailed SSH docs

Open matschaffer opened this issue 4 years ago • 12 comments

https://github.com/romkatv/zsh4humans#SSH mentions that the environment can be sent with you over SSH but I can't seem to find details on how to make that happen.

After grepping my way through ~/.cache/zsh4humans I found z4h help ssh, so I gave it a try on a random GCP host with

z4h ssh -i ~/.ssh/id_rsa -- matschaffer@(IP)

But on the remote host I'm still just getting a typical ubuntu bash prompt.

Sorry if I'm missing something and thanks in advance for the advice!

matschaffer avatar Oct 28 '21 01:10 matschaffer

SSH teleportation works through regular ssh commands. Plain ssh -i ~/.ssh/id_rsa -- matschaffer@(IP) will teleport your shell environment if teleportation is enabled for (IP). It's disabled by default for all hosts though. Here's the relevant part of .zshrc:

https://github.com/romkatv/zsh4humans/blob/0987e32ad9a67906aadf068c440c9b3ff69b7bab/.zshrc#L31-L40

You can try the whitelist approach first: keep the fallback value of enable at no and list specific hosts that should have teleportation enabled. Note that the hostname part of zstyle context is exactly what you pass in ssh hostname command. In your case it's (IP).

I usually add all hosts to ~/.ssh/config so that I can type ssh blah instead of the longer command. This requires the following block in ~/.ssh/config or a file included from it:

Host blah
  HostName (IP)
  User matschaffer
  IdentityFile ~/.ssh/id_rsa

If you do it this way, I recommend adding the following styles to ~/.zshrc:

zstyle ':completion:*:ssh:argument-1:'       tag-order    hosts users
zstyle ':completion:*:scp:argument-rest:'    tag-order    hosts files users
zstyle ':completion:*:(ssh|scp|rdp):*:hosts' hosts

This will allow you to TAB-complete hosts nicely.

If you like ssh teleportation, you can switch to the blacklist approach (I do it this way): flip the fallback to yes and list hosts that should NOT have ssh teleportation. You can force-disable teleportation by using command ssh instead of ssh.

romkatv avatar Oct 28 '21 05:10 romkatv

Ahhh.. that worked a treat! And thanks for the ssh config tip.

  ~ ·······························································  16:03:48
❯ ssh -F ~/.ssh/vscode gcp-workstation
z4h: fetching z4h.zsh
z4h: installing zsh4humans
z4h: installing systemd completions
z4h: installing zsh-history-substring-search
z4h: installing zsh-autosuggestions
z4h: installing zsh-completions
z4h: installing zsh-syntax-highlighting
z4h: installing terminfo
z4h: installing fzf
z4h: fetching fzf binary
z4h: installing tmux
z4h: installing ohmyzsh/ohmyzsh
z4h: installing powerlevel10k
z4h: fetching gitstatus binary
z4h: initializing zsh
direnv: loading ~/.envrc
direnv: export +GITHUB_TOKEN

  ~ ··································································································· ▼ matschaffer@workstation-matschaffer  07:03:56

I may add this host to my main ssh config, but for now I've been keeping it in a dedicated place for my vscode ssh remote hosts.

I've opened https://github.com/romkatv/zsh4humans/pull/151 with a recommendation that I think might have pointed me in the right direction. Though learning how z4h cloned itself was very interesting too :)

matschaffer avatar Oct 28 '21 07:10 matschaffer

The discoverability of z4h features is currently awful. This is partially caused by my laziness and partially intentional. The intentional part is that I want the barrier to entry to be artificially high at the moment to avoid opening the floodgates. I spend enough of my free time providing user support for powerlevel10k and I don't want to double that. Eventually I plan to polish z4h and write docs but not soon.

By the way, you can make command history on gcp-workstation "persistent". That is, you ssh to it, run some commands, disconnect, completely wipe the instance, then ssh again (perhaps to a different IP this time) and have your command history in there as if nothing has happened. If you are interested in this, I can show you how to configure it. It'll take a few minutes of my time, so I'm first asking if it'll be useful to you.

romkatv avatar Oct 28 '21 07:10 romkatv

Wow, so it's server history but persisted client side? Sounds pretty awesome and I'd love to hear more. Curious if it extends to things like sessions after sudo -i as well.

You're a magician @romkatv - don't let anyone tell you otherwise! 🧡

matschaffer avatar Oct 28 '21 14:10 matschaffer

Wow, so it's server history but persisted client side?

That's right. More accurately, history gets pulled from remote hosts to the local, and when you open an ssh connection it gets sent from local to remote. It's as "persistent" as your local history. If you wipe your local machine, all history is gone unless you backup your history together with dotfiles like I do.

I'd love to hear more.

On second thought I think we can do it differently. Instead of my explaining how you can configure distributed command history on top of the existing low-level API, I'll add a new higher-level API that you can test drive. This way you'll get a simpler and more powerful setup and I'll get feedback that I need to ensure that the feature is useful and understandable. This will take some time. I'll ping you here when it's ready. How does that sound?

Curious if it extends to things like sessions after sudo -i as well.

It does not. Try sudo -Es instead.

romkatv avatar Oct 29 '21 06:10 romkatv

I'll add a new higher-level API that you can test drive. This way you'll get a simpler and more powerful setup and I'll get feedback that I need to ensure that the feature is useful and understandable. This will take some time. I'll ping you here when it's ready. How does that sound?

Sounds great! I'll keep an eye out for the notification.

It does not. Try sudo -Es instead.

Gotcha. I might even think of this as a "feature". :)

matschaffer avatar Nov 01 '21 00:11 matschaffer

Instead of my explaining how you can configure distributed command history on top of the existing low-level API, I'll add a new higher-level API that you can test drive.

This will take a lot longer than I've originally expected due to life intervening and all that. I'll keep the issue open.

romkatv avatar Nov 10 '21 21:11 romkatv

Thanks for the update @romkatv , no worries.

I was chatting with @aaronfeng about this yesterday and he was curious if it'd be possible to have shared history for a collection of ssh targets.

For example when working with EC2, hosts come and go all the time, but it'd be great to preserve the history.

I suspect if you had a ProxyCommand and accessed all hosts via a ssh config Host, this would work. But it might be a nice option to just have one common history file for a glob of SSH targets.

Looking forward to hearing more whenever your schedule allows. Take care of yourself first! 🧡

matschaffer avatar Nov 11 '21 01:11 matschaffer

I was chatting with @aaronfeng about this yesterday and he was curious if it'd be possible to have shared history for a collection of ssh targets.

For example when working with EC2, hosts come and go all the time, but it'd be great to preserve the history.

Yep, that's exactly the use case that shared history in z4h was implemented for.

To not keep you waiting until I implemented a new API, here's something to get you started that works with the current (rather complex I'm afraid) API.

# This function is invoked by zsh4humans on every ssh command after
# the instructions from ssh-related zstyles have been applied. It allows
# us to configure ssh teleportation in ways that cannot be done with
# zstyles.
#
# Within this function we have readonly access to the following parameters:
#
# - z4h_ssh_client  local hostname
# - z4h_ssh_host    remote hostname as it was specified on the command line
#
# We also have read & write access to these:
#
# - z4h_ssh_enable          1 to use ssh teleportation, 0 for plain ssh
# - z4h_ssh_send_files      list of files to send to the remote; keys are local
#                           file names, values are remote file names
# - z4h_ssh_retrieve_files  the same as z4h_ssh_send_files but for pulling
#                           files from remote to local
# - z4h_retrieve_history    list of local files into which remote $HISTFILE
#                           should be merged at the end of the connection
# - z4h_ssh_command         command to use instead of `ssh`
function z4h-ssh-configure() {
  emulate -L zsh

  # Bail out if ssh teleportation is disabled. We could also
  # override this parameter here if we wanted to.
  (( z4h_ssh_enable )) || return 0

  # Figure out what kind of machine we are about to connect to.
  local machine_tag
  case $z4h_ssh_host in
    ec2-*) machine_tag=ec2;;
    *)     machine_tag=$z4h_ssh_host;;
  esac

  # This is where we are locally keeping command history
  # retrieved from machines of this kind.
  local local_hist=$ZDOTDIR/.zsh/history/retrieved_from_$machine_tag

  # This is where our $local_hist ends up on the remote machine when
  # we connect to it. Command history from files with names like this
  # is explicitly loaded by our zshrc (see below). All new commands
  # on the remote machine will still be written to the regular $HISTFILE.
  local remote_hist='"$ZDOTDIR"/.zsh/history/received_from_'${(q)z4h_ssh_client}

  # At the start of the SSH connection, send $local_hist over and
  # store it as $remote_hist.
  z4h_ssh_send_files[$local_hist]=$remote_hist

  # At the end of the SSH connection, retrieve $HISTFILE from the
  # remote machine and merge it with $local_hist.
  z4h_retrieve_history+=($local_hist)
}

# Load command history that was sent to this machine over ssh.
() {
  emulate -L zsh -o extended_glob
  local hist
  for hist in $ZDOTDIR/.zsh/history/received_from_*(NOm); do
    fc -RI $hist
  done
}

You'll need to add this block to ~/.zshrc below z4h init. Before trying it out you'll probably want to modify the logic that computes machine_tag based on $z4h_ssh_host, although you can also use it as is -- there is a reasonable fallback. Oh, and make sure to run z4h update beforehand -- this code relies on https://github.com/romkatv/zsh4humans/commit/5fea79d79748bbc8e222690ca44272213843611c that I have just pushed.

If you are defining z4h-ssh-configure, you don't actually need to use ssh-specific zstyles but you still can if you want to. The function is invoked after zstyles are applied, so you can observe and/or override their effect within z4h-ssh-configure. For example, z4h_ssh_enable within the function is set to 0 or 1 according to the value of zstyle :z4h:ssh:$hostname enable. The implementation of z4h-ssh-configure that I've posted above bails out if z4h_ssh_enable is zero, so it doesn't do anything unless you enable ssh teleportation via zstyle for the target host. You could instead set z4h_ssh_enable in the function itself based on $z4h_ssh_host or anything else.

You can add the following line at the top of z4h-ssh-configure to see the initial values of all ssh parameters that z4h lets you read/write.

typeset -pm 'z4h_ssh_*'

You'll notice that there are a few more parameters than what I've documented in the comments above z4h-ssh-configure. Those are low-level blocks of code that get executed on the remote host. You probably shouldn't touch them.

romkatv avatar Nov 11 '21 10:11 romkatv

thanks @romkatv - I'll take it for a test drive and let you know if it works as expected. These past couple weeks aren't as SSH heavy for me so maybe I can rope in @aaronfeng too :)

matschaffer avatar Nov 16 '21 07:11 matschaffer

Did you have a chance to try it out?

romkatv avatar Nov 27 '21 07:11 romkatv

Not yet @romkatv - I've been doing a lot of selenium work lately which doesn't like to run on anything other than my laptop. Will send an update when I get a chance to use it.

matschaffer avatar Nov 29 '21 04:11 matschaffer

There are barebone docs for SSH teleportation here: https://github.com/romkatv/zsh4humans/blob/master/tips.md#ssh

romkatv avatar Jan 12 '24 05:01 romkatv