I was building a web app that needed to connect to remote SSH servers to run commands. The obvious security requirement: no passwords. SSH keys were the answer.
The straightforward approach in PHP would be OpenSSL. It's built right into PHP, so I tried it. I generated keys with `openssl_pkey_new()`, extracted them with `openssl_pkey_export()`. Everything looked fine until I tried to use them.
Here's the problem: OpenSSL generates keys in PEM format. SSH servers expect RFC 4251/4253 format. They're not the same thing.
So I had two options: write code to convert PEM to SSH format, or find a different path entirely. The conversion path seemed fragile; lots of edge cases, parsing, and potential security issues.
That's when it hit me: why am I trying to reinvent this?
The straightforward approach in PHP would be OpenSSL. It's built right into PHP, so I tried it. I generated keys with `openssl_pkey_new()`, extracted them with `openssl_pkey_export()`. Everything looked fine until I tried to use them.
Here's the problem: OpenSSL generates keys in PEM format. SSH servers expect RFC 4251/4253 format. They're not the same thing.
So I had two options: write code to convert PEM to SSH format, or find a different path entirely. The conversion path seemed fragile; lots of edge cases, parsing, and potential security issues.
That's when it hit me: why am I trying to reinvent this?
The problem
When a web app connects to remote servers via SSH, each server needs its own authorized key. Storing passwords is a security nightmare, and SSH keys solve this elegantly. But generating those keys in a web app introduces complexity:
- Keys must be in the exact format SSH servers expect
- Private keys need to be stored securely
- Each server gets a unique key pair
- The whole process needs to be reliable
The OpenSSL approach was technically possible but felt wrong. I was forcing a cryptographic library into a format conversion problem.
- Keys must be in the exact format SSH servers expect
- Private keys need to be stored securely
- Each server gets a unique key pair
- The whole process needs to be reliable
The OpenSSL approach was technically possible but felt wrong. I was forcing a cryptographic library into a format conversion problem.
The "Aha" moment
What generates SSH keys in the correct format, every single time, without any fuss?
ssh-keygen
It's the industry standard. It's installed on virtually every Linux distribution and macOS. It's the same tool developers use to generate keys for GitHub, Bitbucket, and any other SSH-based service. It outputs exactly what SSH servers want. No conversion required.
The only question: how do you run `ssh-keygen` from within a Laravel application?
The solution: Laravel's Process facade
Laravel has a built-in answer: the `Process` facade. It lets you execute shell commands from PHP code, complete with proper error handling and response parsing.
Here's the workflow:
1. Generate the keys: Use `Process::run()` to execute `ssh-keygen`, directing output to a temporary directory
2. Read the keys: Load the generated private and public keys from the temp files
3. Store securely: Save keys to the database using Laravel's `encrypt` casting, which handles automatic encryption and decryption
4. Clean up: Delete the temporary files immediately (private keys on disk are a security risk)
5. Connect: Use the stored keys for passwordless SSH connections
The pattern is simple but elegant. Each server gets its own encrypted key pair stored in the database, ready for use whenever the app needs to connect.
Here's the workflow:
1. Generate the keys: Use `Process::run()` to execute `ssh-keygen`, directing output to a temporary directory
2. Read the keys: Load the generated private and public keys from the temp files
3. Store securely: Save keys to the database using Laravel's `encrypt` casting, which handles automatic encryption and decryption
4. Clean up: Delete the temporary files immediately (private keys on disk are a security risk)
5. Connect: Use the stored keys for passwordless SSH connections
The pattern is simple but elegant. Each server gets its own encrypted key pair stored in the database, ready for use whenever the app needs to connect.
Why this works
The `Process` facade abstracts away the complexity of running shell commands. It handles escaping, environment variables, and error handling. It covers all the things that make executing commands from PHP dangerous if done naively.
Laravel's `encrypt` casting takes care of the security concern. When you cast an attribute as `encrypted`, Laravel automatically encrypts it on save and decrypts it on retrieval. Your database stores encrypted blobs; your code sees clean, usable keys.
And by generating keys directly in SSH format, you eliminate the entire conversion step. You're using the same tool the ecosystem uses, in the way it's designed to be used.
Laravel's `encrypt` casting takes care of the security concern. When you cast an attribute as `encrypted`, Laravel automatically encrypts it on save and decrypts it on retrieval. Your database stores encrypted blobs; your code sees clean, usable keys.
And by generating keys directly in SSH format, you eliminate the entire conversion step. You're using the same tool the ecosystem uses, in the way it's designed to be used.
The lesson
Sometimes the best solution isn't in your language's ecosystem. PHP has OpenSSL, but for SSH key generation, `ssh-keygen` is the right tool for the job. Recognize when reaching outside your language gives you a better, more reliable result.
For Laravel developers building tools that interact with system utilities, the `Process` facade is the bridge. It doesn't matter whether you're running `ssh-keygen`, `git`, or any other command. The facade gives you a clean, safe way to do it.
The result? Password-free SSH connections generated securely, stored encrypted, and ready for production use. No PEM-to-SSH conversion routines required.
Sometimes the elegant solution is the one that doesn't try to do everything itself.
For Laravel developers building tools that interact with system utilities, the `Process` facade is the bridge. It doesn't matter whether you're running `ssh-keygen`, `git`, or any other command. The facade gives you a clean, safe way to do it.
The result? Password-free SSH connections generated securely, stored encrypted, and ready for production use. No PEM-to-SSH conversion routines required.
Sometimes the elegant solution is the one that doesn't try to do everything itself.