feat: passphrase encrypted private key authentication #382

This commit is contained in:
Bill Church 2024-12-03 20:09:13 +00:00
parent 056e87b40d
commit 796145186b
No known key found for this signature in database
5 changed files with 70 additions and 15 deletions

View file

@ -84,7 +84,7 @@ Renamed and expanded options:
## Detailed Changes
### 1. Authentication Options
- Added support for SSH private key authentication via `user.privateKey`
- Added support for SSH private key authentication via `user.privateKey` and passphrase encrypted private keys via `user.passphrase`
- Removed `user.overridebasic` option
- Added keyboard-interactive authentication controls
@ -128,7 +128,8 @@ These settings are now managed client-side.
"user": {
"name": null,
"password": null,
"privateKey": null
"privateKey": null,
"passphrase": null
},
"ssh": {
"host": null,
@ -138,7 +139,39 @@ These settings are now managed client-side.
"keepaliveInterval": 120000,
"keepaliveCountMax": 10,
"algorithms": {
// ... algorithm configurations ...
"cipher": [
"aes128-ctr",
"aes192-ctr",
"aes256-ctr",
"aes128-gcm",
"aes128-gcm@openssh.com",
"aes256-gcm",
"aes256-gcm@openssh.com",
"aes256-cbc"
],
"compress": [
"none",
"zlib@openssh.com",
"zlib"
],
"hmac": [
"hmac-sha2-256",
"hmac-sha2-512",
"hmac-sha1"
],
"kex": [
"ecdh-sha2-nistp256",
"ecdh-sha2-nistp384",
"ecdh-sha2-nistp521",
"diffie-hellman-group-exchange-sha256",
"diffie-hellman-group14-sha1"
],
"serverHostKey": [
"ecdsa-sha2-nistp256",
"ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp521",
"ssh-rsa"
]
}
},
"options": {

View file

@ -196,7 +196,7 @@ For more information on SSH keyboard-interactive authentication, refer to [RFC 4
### SSH Private Key Authentication
WebSSH2 supports SSH private key authentication when using the `/ssh/host/` endpoint with a private key configured in the server settings.
WebSSH2 supports SSH private key authentication when using the `/ssh/host/` endpoint with a private key configured in the server settings or via the interactive method with the `/ssh/` endpoint.
#### Configuration
@ -215,13 +215,17 @@ Private key authentication can only be configured through the `config.json` file
#### Key Requirements
- Only `ssh-rsa` type keys are supported
- Passphrase encryption is supported, and if used the `passphrase` must be provided
- The private key must be in PEM format
- The key in `config.json` must be on a single line with `\n` as line separators
- Must include the appropriate header and footer:
```
-----BEGIN RSA PRIVATE KEY-----\n[... key content ...]\n-----END RSA PRIVATE KEY-----
```
or for encrypted keys:
```
-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,5930F19760F7FBBC865400940A89D954\n\n[... key content ...]\n-----END RSA PRIVATE KEY-----
```
#### Generating a Private Key
To generate a new SSH private key, you can use the following command:
@ -231,10 +235,10 @@ ssh-keygen -m PEM -t rsa -b 4096 -f ~/.ssh/id_rsa
#### Converting Your Private Key
To convert your existing SSH private key into the correct format for `config.json`, you can use this bash command:
Keys uploaded or pasted using the interactive mode through the `/ssh` endpoint can work as-is, however if using a key with `config.json` you must convert your existing SSH private key into the correct format (single line). A bash one-liner you can to accomplish this is:
```bash
echo '"'$(cat ~/.ssh/id_rsa | tr '\n' '~' | sed 's/~/\\n/g')'"'
echo ' "privateKey": "'$(cat ~/.ssh/id_rsa | tr '\n' '~' | sed 's/~/\\n/g')'"'
```
This command:

View file

@ -22,7 +22,9 @@ const defaultConfig = {
},
user: {
name: null,
password: null
password: null,
privateKey: null,
passphrase: null
},
ssh: {
host: null,

View file

@ -27,7 +27,8 @@ const configSchema = {
properties: {
name: { type: ["string", "null"] },
password: { type: ["string", "null"] },
privateKey: { type: ["string", "null"] }
privateKey: { type: ["string", "null"] },
passphrase: { type: ["string", "null"] }
},
required: ["name", "password"]
},

View file

@ -40,6 +40,15 @@ class SSHConnection extends EventEmitter {
return standardKeyPattern.test(key) || encryptedKeyPattern.test(key)
}
/**
* Checks if a private key is encrypted
* @param {string} key - The private key to check
* @returns {boolean} - Whether the key is encrypted
*/
isEncryptedKey(key) {
return key.includes("Proc-Type: 4,ENCRYPTED")
}
/**
* Attempts to connect using the provided credentials
* @param {Object} creds - The credentials object
@ -207,9 +216,9 @@ class SSHConnection extends EventEmitter {
/**
* Generates the SSH configuration object based on credentials.
* @param {Object} creds - The credentials object containing host, port, username, and optional password/privateKey/passphrase.
* @param {Object} creds - The credentials object
* @param {boolean} useKey - Whether to attempt key authentication
* @returns {Object} - The SSH configuration object.
* @returns {Object} - The SSH configuration object
*/
getSSHConfig(creds, useKey) {
const config = {
@ -235,10 +244,16 @@ class SSHConnection extends EventEmitter {
config.privateKey = privateKey
// Add passphrase if provided
if (creds.passphrase) {
debug("Passphrase provided for private key")
config.passphrase = creds.passphrase
// Check if key is encrypted and passphrase is needed
if (this.isEncryptedKey(privateKey)) {
const passphrase = creds.passphrase || this.config.user.passphrase
if (!passphrase) {
throw new SSHConnectionError(
"Encrypted private key requires a passphrase"
)
}
debug("Adding passphrase for encrypted private key")
config.passphrase = passphrase
}
} else if (creds.password) {
debug("Using password authentication")