Fix terminal failing for SSH hosts with a non-cert "certificate" secret (#26)
The Terminal page failed to open a shell for one host (Linode) while
Host Metrics worked fine for the same host, and other hosts (pve1/pve2)
worked everywhere. Root cause: the terminal route takes a special
certificate-auth path whenever an SSH integration has ANY `certificate`
secret set, and that path shells out to the system `ssh` binary under a
pty instead of using the ssh2 library. The metrics path always uses
ssh2, which is why it was unaffected.
That host's `certificate` secret was actually a plain public key
(`ssh-ed25519 AAAA...`), not an OpenSSH certificate. ssh discarded it
("is not a certificate") and then could not load the private key under
the container's libcrypto ("error in libcrypto: unsupported"), ending in
"Permission denied (publickey)". With ssh2 (the metrics path), the same
private key authenticates fine.
Two fixes:
- Only take the cert-auth path when the secret is a genuine OpenSSH
certificate (key type ends in `-cert-v01@openssh.com`). A plain public
key now falls through to the normal ssh2 key/password path, which
already works (proven by the metrics endpoint using the same key).
- Add `-o IdentitiesOnly=yes` to the cert-auth ssh invocation so it only
offers the provided key/cert and isn't confused by a stray file.
No server-side or key changes were needed on the affected host; this is
purely a routing/robustness fix in the terminal WebSocket handler.
Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
This commit is contained in:
parent
b2600e2577
commit
993792e193
1 changed files with 14 additions and 1 deletions
|
|
@ -17,6 +17,18 @@ interface ClientMessage {
|
|||
|
||||
const TMUX_NAME_RE = /^[A-Za-z0-9_-]{1,64}$/
|
||||
|
||||
/**
|
||||
* True only if the stored secret is a genuine OpenSSH certificate, i.e. its key type ends in
|
||||
* `-cert-v01@openssh.com` (e.g. `ssh-ed25519-cert-v01@openssh.com AAAA...`). A plain public key
|
||||
* (`ssh-ed25519 AAAA...`) is NOT a certificate — storing one in the `certificate` field must not
|
||||
* trigger the cert-auth path, which shells out to the system `ssh` binary and fails to load some
|
||||
* key formats. In that case we fall through to the normal ssh2 key/password path instead. */
|
||||
function isOpenSshCertificate(value: string | undefined): boolean {
|
||||
if (!value) return false
|
||||
const firstToken = value.trim().split(/\s+/)[0] ?? ''
|
||||
return firstToken.endsWith('-cert-v01@openssh.com')
|
||||
}
|
||||
|
||||
const SESSION_LOG_DIR = process.env.ARCHNEST_SESSION_LOG_DIR ?? './data/session-logs'
|
||||
|
||||
function send(socket: { send: (data: string) => void }, payload: Record<string, unknown>) {
|
||||
|
|
@ -50,6 +62,7 @@ function connectWithCertificate(
|
|||
'-p', String(Number(target.config.port) || 22),
|
||||
'-i', keyFile,
|
||||
'-o', `CertificateFile=${certFile}`,
|
||||
'-o', 'IdentitiesOnly=yes',
|
||||
'-o', 'StrictHostKeyChecking=accept-new',
|
||||
'-o', `UserKnownHostsFile=${join(keyDir, 'known_hosts')}`,
|
||||
`${target.config.username}@${target.config.host}`,
|
||||
|
|
@ -156,7 +169,7 @@ export async function terminalRoutes(app: FastifyInstance) {
|
|||
const cols = msg.cols ?? 80
|
||||
const rows = msg.rows ?? 24
|
||||
|
||||
if (target.secrets.certificate) {
|
||||
if (isOpenSshCertificate(target.secrets.certificate)) {
|
||||
connectWithCertificate(
|
||||
target,
|
||||
cols,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue