SSH tips and tricks
carlosbecker.dev
carlosbecker.dev
To be more precise ~ is the default ssh escape character. It is only treated as the ssh escape character if it is at the beginning of a new line. From the ssh(1):
> The escape character followed by a dot (`.') closes the connection; followed by control-Z suspends the connection; and followed by itself sends the escape character once.
There's more commands than the dot too but I admit I forgot the others
~?
will print a list of the available escape sequences.
EDIT: I started distrusting my memory here and went hunting, it appears my memory has the escape nesting backwards https://lgfang.github.io/computer/2009/05/08/quit-nested-ssh...
It was [wait]+++[wait] and then ATH or ATH0 by the way, on a modem that implemented the Hayes command set correctly. To avoid at least some inadvertent triggering of this in-band signalling.
> is this the same as hitting CTRL-D?
No. ^d is a character that is sent to the remote shell (bash, zsh, etc) to tell it to exit. ~. is something for the SSH client to tell it to cut the connect if (e.g.) the shell has hung.
Thing of it like working at different OSI layers: ^d is HTTP, while ~. is like TCP or IP.
I have this in fish config:
function tsh
ssh -o RequestTTY=yes $argv tmux -u -CC new -A -s tmux-main
end
and use `tsh host`[1] https://unix.stackexchange.com/questions/628607/how-to-bypas...
That was clearly one of those paper cut type of fixes that makes the world a tiny bit nicer.
I am actually switching to having wezterm handling the mux functionality. It's nice to have this stuff running on the local client which allows all familiar keyboard shortcuts to work without conflict. Automatic Pane/Tab support is really nice. Mosh like functionality comes for free as well.
I used it when trying to use my iPad for minimalist development (mosh + ec2 + vim + Go) and it worked great compared to SSH.
Mosh has more features than just handling unreliable connections though, so SSH over Wireguard is not a proper replacement. Mosh reduces input latency by just echoing text right away and fixing it if needed and only syncs the visible part of the screen instead of making output block while catching up. It does other interesting things too that I don't understand very well!
You can always run Mosh over Wireguard too! This is what I'm going to start doing I think.
https://github.com/mobile-shell/mosh/releases/tag/mosh-1.3.2...
If anyone here is more knowledgeable, it'd be good to know why it's been this long since an official release. It looks like most of the authors of mosh are academics, so my best guess is that mosh is a volunteer effort and it's just something that slipped through the cracks
Re: why has it been five years, I feel like I've written this message many times on HN (https://news.ycombinator.com/item?id=28151637 , https://news.ycombinator.com/item?id=31010005), but here's another go. :-) We take Mosh's security seriously. In the ten years that Mosh has been out (https://news.ycombinator.com/item?id=3819382), we've never had a real security hole -- that we know about. That's a fantastic (apparent) track record. I don't want us to boast about it because it's just tempting fate, and of course you never really know if you have a security hole (just the ones you find or people tell you about), but, in terms of "security holes discovered," Mosh's track record compares really well with OpenSSH, OpenSSL, etc. Of course those codebases (a) have a lot more features than Mosh, and do more than Mosh, and (b) release more often than Mosh, but I'm happy (and I think the rest of the team is too) that Mosh does the thing it does well and without having made our users vulnerable. Back in 2012 when Mosh first came out, with a novel C++ codebase, and a novel secure datagram protocol, a lot of people were skeptical that it was worth trusting, and I'm pretty happy with how things turned out. This was all before HeartBleed and before QUIC, when self-assured people told me to "use something vetted, like OpenSSL" instead of our own new protocol and codebase. It took a long time to earn the community's trust, and now there's a few million people using Mosh, and I don't want us to fuck that up.
I had handed off the project to another maintainer, and my understanding is that he had some health challenges or maybe just got burnt out. Given the choice between "release the code with lots of new features, but without the normal procedure and without an active maintainer to take responsibility for it" vs. "don't release," we chose the conservative option. I think that was the right choice. Of course many people equate "how recently was there a release" to "how secure is this software," and... I guess we are a counterexample? Not sure what else to say.
Thank you to HN and Patrick Collison for publicizing Mosh back when it first came out ten years ago (https://news.ycombinator.com/item?id=3819382) and hope the next 10 years goes similarly... uneventfully and full of secure, reliable, mobile terminal sessions. :-) And thank you to all of you who get a chance to test the release candidate!
I did try tmux today. Also doesn't support mouse scrolling out of the box, but I did find the setting for it. Still screws with copy-paste and right clicking.
You might still use mosh if your connection is high latency and you are ok with occasional display artifacts, but its utility is much less if you already have tmux
Host example.org
RemoteCommand tmux new -A -s default
Instead of this, i go one step further, and in .zshrc on username@example.org: if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ]; then
[ -z "${TMUX}" ] && tmux new-session -A -s default
fiHere's how to define a shell function for quickly attaching to an existing screen session after connecting via ssh (or creating a new one if none exists):
sshcreen () {
ssh -t "$@" screen -xRR
}
Works with bash and zsh. Usage is pretty simple: $ sshcreen user@example.com
You can use normal ssh arguments, such as the port: $ sshcreen root@localhost -p 2222
Detach the session with CTRL-A + D, reattach by re-running the sshcreen command you previously used.PS: repurposed my comment from a recent discussion: https://news.ycombinator.com/item?id=32486892
The advantage I had in mind of using the rcfile is that it's something i configure once and then applies no matter where i ssh from.
ssh root@remotehost “apt update && apt install -y byobu && byobu-enable”
Much simpler IMO.nah.
This lets me pull in new config files and hosts without having to redo everything every time.
`Include ~/.ssh/config.d/*`
Edit to say that my compile function does a syntax check too… but this is easier than setting up the dir listener.
I just add a new entry to the bottom, `nvim ~/.ssh/config` -- what is better about your approach / what am I missing out on?
(Have never heard of ssh-compile, will also look it up when not on mobile.)
#!/bin/sh SSH_DIR="/Users/nvahalik/.ssh/config.d/" echo "" > ~/.ssh/config for i in `ls "${SSH_DIR}"`; do echo "# ${SSH_DIR}${i}" >> ~/.ssh/config cat "${SSH_DIR}${i}" >> ~/.ssh/config echo "\n" >> ~/.ssh/config done; chmod 644 ~/.ssh/config
# If ~/.ssh/config does not exist, copy file
if [ ! -f ~/.ssh/config ]; then
cp ./.ssh/config ~/.ssh/config
else
# For each block in ./.ssh/config, if it is not in ~/.ssh/config, add it
while read -r line; do
if [[ "$line" == "" ]]; then
if grep -q "$block" ~/.ssh/config; then
# If block is not found, add it to ~/.ssh/config
echo "$block" >> ~/.ssh/config
fi
else
if [[ -z "$block" ]]; then
block="$line"
else
block="$block$line\n"
fi
fi
done < ./.ssh/config
fi server# cat /etc/systemd/system/ctsftp.socket
[Unit]
Description=cleartext sftp
[Socket]
ListenStream=7777
Accept=yes
[Install]
WantedBy=sockets.target
server# cat /etc/systemd/system/ctsftp@.service
[Unit]
Description=cleartext sftp
[Service]
ExecStart=-/usr/libexec/openssh/sftp-server
StandardInput=socket
User=nobody
Group=nobody
server# systemctl start ctsftp.socket
Then, on the client, set up a netcat. client$ cat ncssh
#!/bin/sh
exec nc fileserver.myco.com 7777
client$ chmod 755 ncssh
Feel free to move around the cabin. client$ sftp -S ./ncssh bogus
Connected to bogus.
sftp> put ncssh
Uploading ncssh to /ncssh
remote open("/ncssh"): Permission denied
sftp> cd tmp
sftp> put ncssh
Uploading ncssh to /tmp/ncssh
ncssh 100% 56 128.6KB/s 00:00
sftp> quit
I've also jacked this into stunnel. I haven't really benchmarked it, though.You could probably chroot() this, if there was a desire to use it for something important.
For those who truly miss anonymous FTP, it was hiding inside of SSH the whole time. Shoehorning it back into the browsers is left as an exercise for the reader.
Kind of buried the lede there. Two pages of text followed by "Don't do this, it's unsafe!" is poor form. At least the author shows it in a host-specific configuration.
Check -D, -R (both support SOCKS5 now) and -L options in man ssh.
Let's say you have hosts A and B.
Doing this from A:
ssh -R 1080 B
will start a SOCKS5 proxy on A, to which you can connect on B through port 1080 that's being forwarded there. So your web client running on B will use traffic proxied through A while connecting to 1080 on localhost.> Does it try to autodetect whether you're speaking SOCKS to it
I think it just uses SOCKS5 out of the box if you don't specify the host for -R. According to the man page for -R:
if no explicit destination was specified, ssh will act
as a SOCKS 4/5 proxy and forward connections to the
destinations requested by the remote SOCKS client.> ssh -R 8080:localhost:8080 #Port forward
vs
> ssh -R 8080 #SOCKS
That makes using SSH ControlMaster automatic, scoped to the lifetime of the terminal session, and allows for easy automatic syncing of local shell and editor rc files to the remote host, as well as easy opening of remote files in the local editor and can even clone shell sessions into new terminal windows.
For those who do not know Francis, he is co-developing Caddy, THE. BEST. web engine there is.
I take this opportunity to thank you as well as Matt Holt for the incredible product.
One of these rare non-toxic environments where you can get and answer to some simple problems (usually because you missed something in the excellent docs), or something more advanced for which there is a solution as well :)
Match Host 192.168.123.*,another-example.org,*.example.com User myusername,myotherusername
ForwardAgent yes
PermitLocalCommand yes
LocalCommand rsync -L --exclude .netrwhist --exclude .git --exclude .config/iterm2/AppSupport/ --exclude .vim/bundle/youcompleteme/ -vRrlptze "ssh -o PermitLocalCommand=no" %d/./.screenrc %d/./.gitignore %d/./.bash_profile %d/./.ssh/git_ed25519.pub %d/./.ssh/authorized_keys %d/./.vimrc %d/./.zshrc %d/./.config/iterm2/ %d/./.vim/ %d/./bin/ %d/./.bash/ %r@%n:/home/%rAdd this function to your zshrc/bashrc:
# This function refreshes some env vars that go stale in old tmux sessions
# It must be run as a preexec function in zsh or a PROMPT_COMMAND in bash
function refresh_env {
local ssh_auth_sock=""
if [[ -v "TMUX" ]]; then
ssh_auth_sock=$(tmux show-environment | grep "^SSH_AUTH_SOCK")
fi
if [[ -n "$ssh_auth_sock" ]]; then
#shellcheck disable=SC2163
export "$ssh_auth_sock"
fi
}
Then for bash, add this: if ! [[ "$PROMPT_COMMAND" =~ refresh_env ]]; then
PROMPT_COMMAND="refresh_env; $PROMPT_COMMAND";
fi
Or for zsh, add this: autoload -U add-zsh-hook
add-zsh-hook preexec refresh_env if string match -q -r 'tmux\-\d+' "$TMUX"
tmux show-env \
| sed -nE 's/^(SSH_[^=]+)=(.\*)/set -gx \1 "\2"/p' \
| source
end
in my config.fish. sometimes it goes stale but a quick `exec fish` fixes it, I didn't want more stuff to run on every promptalso both of our solutions are nice I think, no need to edit a tmux or ssh config
function ec2_ssh
ssh -F ~/.ssh/ec2_config $argv
end
And then in the ec2_config file… StrictHostKeyChecking=no
UserKnownHostsFile=/dev/nullI don't know how EC2 instances work in detail, but I imagine there must be a way to get a hold of the host key via an API or something when it's deployed (or maybe at any point) so that integrity could be kept without creating annoyance. I'd be much more comfortable with a solution that, for example, queries AWS over HTTPS before every connection and updates the host key (if necessary).
The script is designed for ephemeral instances, where I don’t intend to ever connect to the instance again, so saving the host key doesn’t help anything. So, really, anyone not using this hypothetical API is just as vulnerable to such a MITM attack in my threat model.
I was in a secure environment, the question for the key appeared, I accepted the new key I sometimes erase known_hosts, change keys etc. so it was not suspicious). And could not log in.
It took me some time to realize that the IP I was trying to connect is not mine (as in "at home") but on the company network. I realized that when I recalled that I had the key prompt.
I don't believe that it makes you vulnerable to MitM attacks if you are authenticating with a key.
https://security.stackexchange.com/questions/67242/does-publ...
function ec2_ssh
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $argv
endhttps://github.com/dolmen/github-keygen/
github-keygen features:
* automated maintenance of the GitHub specific configuration (just run the latest release of github-keygen and your setup is safely upgraded)
* strong security settings
* eases the management of your SSH keys allowed to access your GitHub account
* performance settings (ControlMaster)Then it goes on and explains how to use ControlMaster to evade the physical access validation granted by the key! I mean... why even bother with the key in the first place?
ControlMaster itself is a bad idea generically unless you really know what you're doing, because the original connection process needs to stay alive until the last child connection exits. Hands up, all of us who ever inexplicably hung cron jobs and other automated systems by doing interactive stuff at the wrong moment. I know I have. (To be clear: it's still useful as a performance enhancement for remote work, but you absolutely have to know what you're doing and script it carefully. It shouldn't appear like this in a default config line.)
Similarly CanonicalizeHostnames is a recipe for collision with your DNS. If a name doesn't work the way you want it to you need to fix the naming and not just decide to speak a new language.
Expect for where the key is stored. How much difference that makes depends on the use-case. For a developer laptop with a passphrase protected key? Not much of a difference.
ControlMaster is great for development too. That should have been prefixed with that caveat.
In fact, it seems that all recommendations are from the point of view of a developer, not automation. That would be a bad idea for automated systems, as you point out.
Even the control master thing, I mentioned it goes against the purpose of the yubikey, but then I explain how I use it only for github and only for a few seconds to prevent having to touch the key nonstop when updating nvim plugins and such... IMHO its a good tradeoff...
What's the problem you see with the recommendation? Specially when it's talking to localhost?
local $ echo world > ~/src/hello
local $ sharewith-ssh --dir ~/src:/src code.example.com
Last login: Wed Aug 16 09:33:55 2022 from 1.2.3.4
code ~ $ cd /src
code /src $ cat hello
world
code /src $
I think it could be built with a local agent, a binary that you download on the remote host to intercept file system calls for a path (run it and it execs your shell with LD_PRELOAD to the library), and then forwards file system and I/O requests back to the the agent over SSH to get at those files. You could even have a permission prompt UI on the local agent to permit/deny individual reads/writes.Does anything like this exist?
Anyway, it needs a daemon on the remote system, but perhaps https://github.com/u-root/cpu is suitable. (I can't vouch for it, not having used it.)
The HTCondor batch system also provides something like that, but I don't know details, and it's probably not a separable component.
TRAMP works fine with shell-mode, version control (e.g. magit), dired (file browser), etc. too :)
How is that any different than FUSE?
- I find that SSH times out / disconnects if unused but only on my mac, not on my linux machine. I don't have any of the configs described here on either machine, so I'm not sure why one disconnects and one doesn't - is there some out-of-the box macOS default that needs to be changed, or is it some arcane battery optimization macOS feature that's killing the SSH sessions?
I find I like having "set-option -g mouse on" in ~/.tmux.conf so that mousewheel scrolling feels more natural (like it does in a local terminal).
> I find that SSH times out / disconnects if unused...
This one is a little tricky - it partly depends on the default settings of the remote sshd_config for sending KeepAlive pings (changed Debian 10 to 11, e.g) and what your local vendor-compiled ssh_config defaults look like. In general, to just solve the problem add this to your macOS ~/.ssh/config at the bottom/end:
Host *
TCPKeepAlive yes
ServerAliveInterval 300
TCPKeepAlive is what it sounds like, it's the L3 level tweak. ServerAliveInterval is a higher level ping-pong on the SSH session itself; kind of overkill to have both configured, but it Just Works(tm) for most people to have them set on their client. You can look these up in the man pages (ssh_config, sshd_config) and discover even more tweakable options than just these two I presented - some you can set server side, some client side, some both.Side note: bash has an envvar `TMOUT` -- if that's set, bash will auto-logout if you idle in a shell. It's usually not set on most Linux server installs, just be aware it exists and is a thing to look for if you're debugging some day.
Keyflips for all!
https://www.linuxjournal.com/content/ssh-key-rotation-posix-...
Why is wish useful to make ssh apps? Aren’t they just cli/tui apps? Or is it something different?
I've worked in an environment where $HOME is mounted on a drive that's shared across multiple servers, so you might want to consider including the hostname in the socket name (logging in on another server while already logged in can result in confusing disconnections).
2021+963[year+points] A visual guide to SSH tunnels https://news.ycombinator.com/item?id=26053323
2020+770 SSH hacks – a little sanity for remote workers¹ https://news.ycombinator.com/item?id=23025756
2021+637 Sign arbitrary data with your SSH keys https://news.ycombinator.com/item?id=29208518
2020+584 How to SSH Properly² https://news.ycombinator.com/item?id=22750850
2022+459 If you’re not using SSH certificates you’re doing SSH wrong (2019)¹ https://news.ycombinator.com/item?id=30788544 https://news.ycombinator.com/item?id=20955465
2021+363 SSH Tunneling Explained² https://news.ycombinator.com/item?id=28802493
2022+358 SSH Bastion Host Best Practices² https://news.ycombinator.com/item?id=29924053#29924246
2022+298 How to Store an SSH Key on a Yubikey https://news.ycombinator.com/item?id=31556130
2022+277 SSH Agent Restriction (new in OpenSSH 8.9) https://news.ycombinator.com/item?id=29865876 (use ProxyJump if you can!)
2022+274 Guide to Using YubiKey for GPG and SSH https://news.ycombinator.com/item?id=30081348
2022+240 Free book to master SSH tunneling concepts https://news.ycombinator.com/item?id=29946144
2021+180 The pitfalls of using SSH-agent, or how to use an agent safely https://news.ycombinator.com/item?id=28576617
2022+172 Best Practices for Securing SSH² https://news.ycombinator.com/item?id=29812819
2021+158 Simple SSH Security https://news.ycombinator.com/item?id=29153223
2020+147 How to use FIDO2 USB keys with SSH https://news.ycombinator.com/item?id=23689499
--
Reasonably recent popular discussions of complementary technologies:
2022+759 Tailscale SSH https://news.ycombinator.com/item?id=31837115
2022+319 SSH into private machines from anywhere using Cloudflare Tunnel https://news.ycombinator.com/item?id=30283987
2022+281 Show HN: Caddy-SSH https://news.ycombinator.com/item?id=30830749
--
¹ Content marketing from Smallstep: https://hn.algolia.com/?query=smallstep.com
² Content marketing from Teleport: https://hn.algolia.com/?query=goteleport.com 2020+65 Teleport 4.3: Modern Replacement for OpenSSH https://news.ycombinator.com/item?id=23784925
This can be useful if bar is configured to only be reachable by foo.
In some workflows, it's easier and faster to open a temporary connection to server bar from server foo using ssh -A, rather than opening a new terminal and using ssh -J.
$ sudo cat /etc/ssh/sshd_config.d/old-mac.conf
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsaKeys like ed25519-sk rely on using the yubikey with support built into ssh itself (without an agent or anything).
PS1="\h$ "
This will show the host name on terminal prompt.
All of the single-character shell options documented in the description of the set builtin command, including -o, can be used as options when the shell is invoked.
So set RemoteCommand to something like /bin/bash -o vi
(a) remote tab-completion works when typing out an scp command, and
(b) a ControlMaster config makes this tab-completion near-instant (i.e. doesn't require negotiating a connection every time).