feat: passphrase encrypted private key authentication #382
This commit is contained in:
parent
056e87b40d
commit
796145186b
5 changed files with 70 additions and 15 deletions
39
CONFIG.md
39
CONFIG.md
|
@ -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": {
|
||||||
|
|
12
README.md
12
README.md
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"]
|
||||||
},
|
},
|
||||||
|
|
27
app/ssh.js
27
app/ssh.js
|
@ -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")
|
||||||
|
|
Loading…
Reference in a new issue