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 ## Detailed Changes
### 1. Authentication Options ### 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 - Removed `user.overridebasic` option
- Added keyboard-interactive authentication controls - Added keyboard-interactive authentication controls
@ -128,7 +128,8 @@ These settings are now managed client-side.
"user": { "user": {
"name": null, "name": null,
"password": null, "password": null,
"privateKey": null "privateKey": null,
"passphrase": null
}, },
"ssh": { "ssh": {
"host": null, "host": null,
@ -138,7 +139,39 @@ These settings are now managed client-side.
"keepaliveInterval": 120000, "keepaliveInterval": 120000,
"keepaliveCountMax": 10, "keepaliveCountMax": 10,
"algorithms": { "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": { "options": {

View file

@ -196,7 +196,7 @@ For more information on SSH keyboard-interactive authentication, refer to [RFC 4
### SSH Private Key Authentication ### 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 #### Configuration
@ -215,13 +215,17 @@ Private key authentication can only be configured through the `config.json` file
#### Key Requirements #### Key Requirements
- Only `ssh-rsa` type keys are supported - 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 private key must be in PEM format
- The key in `config.json` must be on a single line with `\n` as line separators - The key in `config.json` must be on a single line with `\n` as line separators
- Must include the appropriate header and footer: - Must include the appropriate header and footer:
``` ```
-----BEGIN RSA PRIVATE KEY-----\n[... key content ...]\n-----END RSA PRIVATE KEY----- -----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 #### Generating a Private Key
To generate a new SSH private key, you can use the following command: 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 #### 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 ```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: This command:

View file

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

View file

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

View file

@ -40,6 +40,15 @@ class SSHConnection extends EventEmitter {
return standardKeyPattern.test(key) || encryptedKeyPattern.test(key) 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 * Attempts to connect using the provided credentials
* @param {Object} creds - The credentials object * @param {Object} creds - The credentials object
@ -207,9 +216,9 @@ class SSHConnection extends EventEmitter {
/** /**
* Generates the SSH configuration object based on credentials. * 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 * @param {boolean} useKey - Whether to attempt key authentication
* @returns {Object} - The SSH configuration object. * @returns {Object} - The SSH configuration object
*/ */
getSSHConfig(creds, useKey) { getSSHConfig(creds, useKey) {
const config = { const config = {
@ -235,10 +244,16 @@ class SSHConnection extends EventEmitter {
config.privateKey = privateKey config.privateKey = privateKey
// Add passphrase if provided // Check if key is encrypted and passphrase is needed
if (creds.passphrase) { if (this.isEncryptedKey(privateKey)) {
debug("Passphrase provided for private key") const passphrase = creds.passphrase || this.config.user.passphrase
config.passphrase = creds.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) { } else if (creds.password) {
debug("Using password authentication") debug("Using password authentication")