feat: accept private key from client #381

This commit is contained in:
Bill Church 2024-12-02 21:13:24 +00:00
parent b511ce5eae
commit 829b5cdd75
No known key found for this signature in database
9 changed files with 24 additions and 29 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`
- Removed `user.overridebasic` option
- Added keyboard-interactive authentication controls
@ -128,7 +128,7 @@ These settings are now managed client-side.
"user": {
"name": null,
"password": null,
"privatekey": null
"privateKey": null
},
"ssh": {
"host": null,

View file

@ -115,7 +115,7 @@ Edit `config.json` to customize the following options:
- `user.name` - _string_ - Default SSH username (default: `null`)
- `user.password` - _string_ - Default SSH password (default: `null`)
- `ssh.host` - _string_ - Default SSH host (default: `null`)
- `user.privatekey` - _string_ - Default SSH private key (default: `null`)
- `user.privateKey` - _string_ - Default SSH private key (default: `null`)
- `ssh.port` - _integer_ - Default SSH port (default: `22`)
- `ssh.term` - _string_ - Terminal emulation (default: `"xterm-color"`)
- `ssh.readyTimeout` - _integer_ - SSH handshake timeout in ms (default: `20000`)
@ -206,7 +206,7 @@ Private key authentication can only be configured through the `config.json` file
{
"user": {
"name": "myuser",
"privatekey": "-----BEGIN RSA PRIVATE KEY-----\nYour-Private-Key-Here\n-----END RSA PRIVATE KEY-----",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\nYour-Private-Key-Here\n-----END RSA PRIVATE KEY-----",
"password": "optional-fallback-password"
}
}
@ -277,7 +277,7 @@ This command:
{
"user": {
"name": "myuser",
"privatekey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpA...[rest of key]...Yh5Q==\n-----END RSA PRIVATE KEY-----",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpA...[rest of key]...Yh5Q==\n-----END RSA PRIVATE KEY-----",
"password": "fallback-password"
}
}

View file

@ -26,7 +26,8 @@ const configSchema = {
type: "object",
properties: {
name: { type: ["string", "null"] },
password: { type: ["string", "null"] }
password: { type: ["string", "null"] },
privateKey: { type: ["string", "null"] }
},
required: ["name", "password"]
},
@ -39,7 +40,6 @@ const configSchema = {
readyTimeout: { type: "integer" },
keepaliveInterval: { type: "integer" },
keepaliveCountMax: { type: "integer" },
disableInteractiveAuth: { type: "boolean" },
algorithms: {
type: "object",
properties: {

View file

@ -32,14 +32,14 @@ function createAuthMiddleware(config) {
// eslint-disable-next-line consistent-return
return (req, res, next) => {
// Check if username and either password or private key is configured
if (config.user.name && (config.user.password || config.user.privatekey)) {
if (config.user.name && (config.user.password || config.user.privateKey)) {
req.session.sshCredentials = {
username: config.user.name
}
// Add credentials based on what's available
if (config.user.privatekey) {
req.session.sshCredentials.privatekey = config.user.privatekey
if (config.user.privateKey) {
req.session.sshCredentials.privateKey = config.user.privateKey
}
if (config.user.password) {
req.session.sshCredentials.password = config.user.password

View file

@ -25,7 +25,7 @@ class WebSSH2Socket extends EventEmitter {
authenticated: false,
username: null,
password: null,
privatekey: null,
privateKey: null,
keyPassword: null,
host: null,
port: null,
@ -117,11 +117,6 @@ class WebSSH2Socket extends EventEmitter {
? creds.term
: this.config.ssh.term
// Map the client's privateKey field to our internal privatekey field if present
if (creds.privateKey) {
creds.privatekey = creds.privateKey
}
this.initializeConnection(creds)
} else {
debug(`handleAuthenticate: ${this.socket.id}, CREDENTIALS INVALID`)
@ -139,8 +134,8 @@ class WebSSH2Socket extends EventEmitter {
)
// Add private key from config if available and not provided in creds
if (this.config.user.privatekey && !creds.privatekey) {
creds.privatekey = this.config.user.privatekey
if (this.config.user.privateKey && !creds.privateKey) {
creds.privateKey = this.config.user.privateKey
}
this.ssh
@ -150,7 +145,7 @@ class WebSSH2Socket extends EventEmitter {
authenticated: true,
username: creds.username,
password: creds.password,
privatekey: creds.privatekey,
privateKey: creds.privateKey,
keyPassword: creds.keyPassword,
host: creds.host,
port: creds.port

View file

@ -160,9 +160,9 @@ class SSHConnection extends EventEmitter {
}
// Try private key first if available and useKey is true
if (useKey && (creds.privatekey || this.config.user.privatekey)) {
if (useKey && (creds.privateKey || this.config.user.privateKey)) {
debug("Using private key authentication")
const privateKey = creds.privatekey || this.config.user.privatekey
const privateKey = creds.privateKey || this.config.user.privateKey
if (!this.validatePrivateKey(privateKey)) {
throw new SSHConnectionError("Invalid private key format")
}

View file

@ -87,7 +87,7 @@ function getValidatedPort(portInput) {
* - port (number)
* AND either:
* - password (string) OR
* - privatekey/privateKey (string)
* - privateKey/privateKey (string)
*
* @param {Object} creds - The credentials object.
* @returns {boolean} - Returns true if the credentials are valid, otherwise false.
@ -104,10 +104,10 @@ function isValidCredentials(creds) {
return false
}
// Must have either password or privatekey/privateKey
// Must have either password or privateKey/privateKey
const hasPassword = typeof creds.password === "string"
const hasPrivateKey =
typeof creds.privatekey === "string" || typeof creds.privateKey === "string"
typeof creds.privateKey === "string" || typeof creds.privateKey === "string"
return hasPassword || hasPrivateKey
}

View file

@ -47,7 +47,7 @@ describe("SSHConnection", () => {
user: {
name: null,
password: null,
privatekey: null
privateKey: null
}
}
@ -149,7 +149,7 @@ MIIEpTestKeyContentHere
port: 22,
username: "user",
password: "pass",
privatekey: validPrivateKey
privateKey: validPrivateKey
}
return sshConnection.connect(mockCreds).then(() => {
@ -168,7 +168,7 @@ MIIEpTestKeyContentHere
port: 22,
username: "user",
password: "pass",
privatekey: validPrivateKey
privateKey: validPrivateKey
}
let authAttempts = 0
@ -205,7 +205,7 @@ MIIEpTestKeyContentHere
host: "localhost",
port: 22,
username: "user",
privatekey: "invalid-key-format"
privateKey: "invalid-key-format"
}
return sshConnection.connect(mockCreds).catch((error) => {

View file

@ -152,7 +152,7 @@ describe("utils", () => {
user: {
name: null,
password: null,
privatekey: null
privateKey: null
},
ssh: {
host: null,