MilkCrunch

SSH Is the Most Underrated Platform in Tech

· by Michael Doornbos · 1956 words

A few months ago, I watched a contractor spend forty-five minutes trying to reach a Postgres instance on an internal network. He installed a VPN client, fought with split tunneling, restarted his DNS resolver twice, and opened a ticket with IT. I was sitting next to him. I typed ssh -L 5432:db-host:5432 bastion, pointed my client at localhost, and had a query running before he got his VPN connected.

He looked at me like I’d performed a magic trick. I’d typed one line.

That’s the thing about SSH. Most developers use it to log into servers. Type a password or bounce off a key, run some commands, disconnect. That’s their entire relationship with the most capable remote access tool ever built.

It’s like buying a Swiss Army knife and only using the toothpick.

SSH isn’t a remote login protocol. It’s a platform. It does tunneling, file transfer, key management, port forwarding, jump hosts, proxy negotiation, agent forwarding, multiplexed connections, and certificate-based authentication. Most of this has been stable for twenty years. All of it works on basically every server you’ll ever touch.

And almost nobody uses more than 10% of it.

You’re already using it more than you think

Here’s the funny part. If you push code to GitHub, GitLab, or Bitbucket over SSH, you’re already using SSH as a transport protocol for git. Every git push and git pull opens an SSH connection, authenticates with your key, and streams data through an encrypted channel. You just don’t think of it that way because git hides the plumbing.

That’s SSH being a platform without anyone noticing. The same protocol that logs you into a server also moves your code around. And the same config file that simplifies your server connections can simplify your git remotes too. Different IdentityFile for your work GitHub and your personal GitHub? That’s a three-line SSH config entry, not a git config hack.

The config file nobody writes

The average developer types something like ssh -i ~/.ssh/my-key -p 2222 admin@192.168.1.50 every single time. Sometimes they paste it from a note. Sometimes they scroll through shell history and hope they find the right one.

Meanwhile, ~/.ssh/config exists. It has existed for decades.

Host prod
    HostName 192.168.1.50
    User admin
    Port 2222
    IdentityFile ~/.ssh/my-key

Now ssh prod does the same thing. Tab-completable. Readable. You can set per-host options for keepalive intervals, compression, which key to use, which proxy command to run. You can wildcard entire domains.

Host *.internal.company.com
    User deploy
    ProxyJump bastion
    IdentityFile ~/.ssh/work-key

Every host matching that pattern now routes through the bastion automatically. No wrapper scripts. No VPN. The SSH client just does it.

I’ve seen teams build custom tooling to manage connection strings when a 15-line config file would have replaced all of it. This is the Unix philosophy in action. A plain text config file, readable by humans, version-controllable, and more powerful than the custom tool it replaces. The same argument I made about text files outlasting every proprietary format applies here. Your SSH config will outlive whatever connection manager is trendy this year.

Port forwarding replaces half your VPN

This is where SSH starts looking less like a login tool and more like infrastructure.

Local forwarding. ssh -L 8080:internal-db:5432 bastion. Now localhost:8080 talks to a database that’s only accessible from the bastion host. No VPN, no firewall changes, no IT ticket.

Remote forwarding. ssh -R 9090:localhost:3000 server. Now port 9090 on the server points back to your laptop’s dev server. Useful for webhooks, demos, testing callbacks against a real endpoint.

Dynamic forwarding. ssh -D 1080 server. You just created a SOCKS proxy. Route your browser through it, and you’re browsing from the server’s network perspective. When you need to access an internal web console that’s only reachable from inside the network, this is the two-second solution.

All three of these go in the config file too.

Host db-tunnel
    HostName bastion.company.com
    LocalForward 5432 internal-db:5432
    User deploy

ssh db-tunnel. Done. Your local Postgres client connects to localhost:5432, and the traffic flows through an encrypted tunnel to the internal database. No special software. No agent running on your machine. Just SSH.

I’ve watched people install VPN clients, deal with split tunneling configuration, fight with DNS resolution, and wait for IT approval. For access to a single internal service. That they could have reached with one line in their SSH config.

Jump hosts and ProxyJump

The old way to reach a server behind a bastion was ssh bastion, then ssh internal-server from there. Two hops. Manual. Breaks scp and rsync workflows because they can’t chain interactive sessions.

ProxyJump fixed this years ago.

Host internal
    HostName 10.0.1.50
    ProxyJump bastion

ssh internal now transparently connects through the bastion. Your terminal acts like you connected directly. scp, rsync, VS Code remote, all of it works because the connection is a single logical session. The bastion never sees your private key, because the auth happens end-to-end.

You can chain multiple jumps. ProxyJump bastion1,bastion2. Two hops, one command. Each hop is authenticated independently.

Multiplexing: one connection, many sessions

Opening an SSH connection takes time. TCP handshake, key exchange, authentication. If you’re running scripts that SSH into the same host repeatedly, that overhead adds up.

SSH multiplexing reuses a single connection for multiple sessions.

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h-%p
    ControlPersist 600

The first connection to a host creates a socket. Every subsequent connection reuses it. The second ssh prod is instant, no handshake. scp prod:file . reuses the same connection. So does rsync. So does git push if you’re using SSH remotes.

For deploy scripts that run multiple SSH commands against the same host, multiplexing can cut total time significantly. And you get it by adding four lines to your config.

Agent forwarding vs. ProxyJump

This is the one people get wrong.

Agent forwarding (ForwardAgent yes) sends your SSH agent’s socket to the remote host. Any process on that host can then use your keys to authenticate to other servers. It’s convenient. It’s also a security risk. If the remote host is compromised, an attacker can use your forwarded agent to authenticate as you, to any server your keys can reach, for the duration of your session.

ProxyJump doesn’t have this problem. Your keys never leave your machine. The connection is tunneled through the intermediate host, but auth happens locally. The bastion is a wire, not a trusted intermediary.

Use ProxyJump. Don’t forward your agent to hosts you don’t fully control. If you must use agent forwarding, at least set AddKeysToAgent confirm so each use requires approval.

Certificates over keys

This is the feature almost nobody knows about.

Standard SSH key auth works, but it has a management problem. Every user’s public key needs to be in authorized_keys on every server they need to access. People leave, keys don’t get removed. New servers get set up, keys don’t get copied. You end up with a mess of key distribution that nobody fully understands and everyone’s afraid to clean up.

SSH certificates solve this. You create a certificate authority (just an SSH key pair), sign user keys with it, and configure servers to trust the CA. Now any user with a signed certificate can log in. No authorized_keys file. Certificates can have expiry dates, restrict which hosts they work on, and limit which principals (usernames) they can authenticate as.

ssh-keygen -s /path/to/ca -I user@company -n deploy -V +52w user_key.pub

That signs the user’s key for one year, only valid as the deploy user. When they leave, you don’t have to hunt down their key on every server. The certificate expires, or you add its serial to a revocation list.

For organizations running more than a handful of servers, this is the difference between manageable and ungovernable. And it’s been in OpenSSH since version 5.4, released in 2010.

File transfer without the industry

Speaking of things SSH already does that people pay money for: scp and sftp have been part of the SSH suite from the start. Encrypted file transfer, authenticated by the same keys you already manage, over the same port you already have open.

There is an entire enterprise software category called “managed file transfer.” Companies pay real money for products that do what scp user@host:/path/to/file . does for free. Some of them are just SFTP with a web interface and a sales team. The ones that add value do it through audit logging, compliance reporting, and scheduling. The actual file transfer part? SSH solved that decades ago.

For most developers, scp or rsync over SSH is all you need. And unlike the managed alternatives, it works with the same config file, the same keys, and the same multiplexed connections as everything else.

Why nobody learns this

So if SSH can do all of this, why do most developers stop at ssh user@host?

Part of it is that the documentation isn’t inviting. man ssh_config is thorough, but it reads like a reference manual because it is one. Nobody learns port forwarding from a man page. They learn it from watching someone do it, the way I learned it from a sysadmin in 2004 who tunneled a database connection in front of me and changed how I thought about networking.

Part of it is the cloud abstraction layer. If you’ve only ever worked with managed services, you may never have needed to reach a host behind a bastion. Your database has a public endpoint. Your logs go to a SaaS dashboard. Your deploys happen through a CI pipeline that handles the SSH for you. The tool is invisible, and invisible tools don’t get learned.

And part of it is that there’s money in replacing SSH with products. Tunnel-as-a-service. Zero-trust network access. Remote access platforms. Some of these genuinely improve on SSH for specific use cases, especially at enterprise scale with compliance requirements. But a lot of them are selling convenience wrappers around capabilities that SSH already has, to people who never learned SSH had those capabilities.

It’s the same pattern I keep seeing in tech. A simple, composable tool that does the job gets buried under layers of abstraction and product marketing, until people forget the tool existed. Then someone builds a startup to re-expose the same capability with a nicer UI and a monthly subscription.

What SSH actually is

SSH is a secure, multiplexed, authenticated channel between two machines that can carry arbitrary TCP traffic in both directions. Everything I described above is just a consequence of that design.

Login sessions, file transfers, port forwards, SOCKS proxies, tunneled connections, certificate auth. None of these are special features bolted on after the fact. They’re natural applications of the underlying capability. An encrypted, authenticated pipe that lets programs use it however they need to.

That’s a platform. Not a login tool. Not a remote shell. A platform.

I’m not saying SSH replaces VPNs, tunnel services, and managed file transfer in every scenario. VPNs have their place when you need full network-level access. But for the common case of “I need to reach this one thing on that network,” SSH has had the answer since before most of those tools existed.

The 90% you’re leaving on the table

Write a config file. Set up multiplexing. Use ProxyJump instead of agent forwarding. Learn local and dynamic port forwarding. If you manage servers, look into certificates.

None of this is new. None of it is exotic. It’s all in man ssh_config, stable, tested, and available on every machine you’re likely to touch.

SSH is the most capable tool most developers will never fully learn. It does more than your VPN, more than your tunnel service, more than your key management tool, and it’s already installed.

Use more of the knife.


What percentage of SSH’s features do you actually use? I’m at mike@imapenguin.com | @mrdoornbos

<< Previous Post

|

Next Post >>

#Opinion