mirror of
https://github.com/Eugeny/tabby.git
synced 2026-08-24 02:34:19 -05:00
fix(ssh): honor profile port settings on connect (#11366)
* fix(ssh): honor profile port settings on connect Coerce and resolve SSH port at connect time instead of relying on raw profile values. Normalize port on profile save. Refresh OpenSSH import cache when included .ssh/config files change. * cleanup * Update sshImporters.ts --------- Co-authored-by: Eugene <inbox@null.page>
This commit is contained in:
@@ -90,23 +90,45 @@ function convertSSHConfigValuesToString (arg: string | string[] | object[]): str
|
||||
.join(' ')
|
||||
}
|
||||
|
||||
// Function to read in the SSH config file recursively and parse any Include directives
|
||||
// ssh_config(5) says "Files without absolute paths are assumed to be in ~/.ssh if included in a user configuration file or /etc/ssh if included from the system configuration file."
|
||||
function resolveSSHIncludePath (value: string): string {
|
||||
if (path.isAbsolute(value)) {
|
||||
return value
|
||||
}
|
||||
if (value.startsWith('~')) {
|
||||
return path.join(process.env.HOME ?? '~', value.slice(1))
|
||||
}
|
||||
return path.join(process.env.HOME ?? '~', '.ssh', value)
|
||||
}
|
||||
|
||||
interface ParsedSSHConfig {
|
||||
config: SSHConfig
|
||||
// Newest mtime across the config file and every file it Includes, so the
|
||||
// import cache is invalidated when an included file changes — not only
|
||||
// when the top-level ~/.ssh/config does.
|
||||
mtime: number
|
||||
}
|
||||
|
||||
// Read the SSH config file recursively, merging any Include directives and
|
||||
// tracking the newest mtime among all files involved.
|
||||
async function parseSSHConfigFile (
|
||||
filePath: string,
|
||||
visited = new Set<string>(),
|
||||
): Promise<SSHConfig> {
|
||||
): Promise<ParsedSSHConfig> {
|
||||
// If we've already processed this file, return an empty config to avoid infinite recursion
|
||||
if (visited.has(filePath)) {
|
||||
return SSHConfig.parse('')
|
||||
return { config: SSHConfig.parse(''), mtime: 0 }
|
||||
}
|
||||
visited.add(filePath)
|
||||
|
||||
let raw = ''
|
||||
let mtime = 0
|
||||
try {
|
||||
mtime = (await fs.stat(filePath)).mtimeMs
|
||||
raw = await fs.readFile(filePath, 'utf8')
|
||||
} catch (err) {
|
||||
console.error(`Error reading SSH config file: ${filePath}`, err)
|
||||
return SSHConfig.parse('')
|
||||
return { config: SSHConfig.parse(''), mtime: 0 }
|
||||
}
|
||||
|
||||
const parsed = SSHConfig.parse(raw)
|
||||
@@ -118,30 +140,21 @@ async function parseSSHConfigFile (
|
||||
continue
|
||||
}
|
||||
|
||||
// ssh_config(5) says "Files without absolute paths are assumed to be in ~/.ssh if included in a user configuration file or /etc/ssh if included from the system configuration file."
|
||||
let incPath = ''
|
||||
if (path.isAbsolute(directive.value)) {
|
||||
incPath = directive.value
|
||||
} else if (directive.value.startsWith('~')) {
|
||||
incPath = path.join(process.env.HOME ?? '~', directive.value.slice(1))
|
||||
} else {
|
||||
incPath = path.join(process.env.HOME ?? '~', '.ssh', directive.value)
|
||||
}
|
||||
|
||||
const matches = glob.sync(incPath)
|
||||
const matches = glob.sync(resolveSSHIncludePath(directive.value))
|
||||
for (const match of matches) {
|
||||
const stat = await fs.stat(match)
|
||||
if (stat.isDirectory()) {
|
||||
continue
|
||||
}
|
||||
const matchedConfig = await parseSSHConfigFile(match, visited)
|
||||
merged.push(...matchedConfig)
|
||||
const included = await parseSSHConfigFile(match, visited)
|
||||
mtime = Math.max(mtime, included.mtime)
|
||||
merged.push(...included.config)
|
||||
}
|
||||
} else {
|
||||
merged.push(entry)
|
||||
}
|
||||
}
|
||||
return merged
|
||||
return { config: merged, mtime }
|
||||
}
|
||||
|
||||
// Function to convert an SSH Profile name into a sha256 hash-based ID
|
||||
@@ -381,8 +394,7 @@ export class OpenSSHImporter extends SSHProfileImporter {
|
||||
|
||||
_openSSHCachePromise = (async () => {
|
||||
try {
|
||||
const stat = await fs.stat(configPath)
|
||||
const mtime = stat.mtimeMs
|
||||
const { config, mtime } = await parseSSHConfigFile(configPath)
|
||||
|
||||
if (_openSSHCache && _openSSHCacheMtime === mtime) {
|
||||
return _openSSHCache
|
||||
@@ -400,7 +412,6 @@ export class OpenSSHImporter extends SSHProfileImporter {
|
||||
}
|
||||
} catch { /* no cache or invalid */ }
|
||||
|
||||
const config: SSHConfig = await parseSSHConfigFile(configPath)
|
||||
_openSSHCache = await convertToSSHProfiles(config)
|
||||
_openSSHCacheMtime = mtime
|
||||
|
||||
|
||||
Reference in New Issue
Block a user