MCPcopy Create free account
hub / github.com/bytebase/dbhub / establishChain

Method establishChain

src/utils/ssh-tunnel.ts:90–166  ·  view source on GitHub ↗

* Establish a chain of SSH connections through jump hosts. * @returns The final SSH client connected to the target host

(
    jumpHosts: JumpHost[],
    targetConfig: SSHTunnelConfig,
    privateKey: Buffer | undefined
  )

Source from the content-addressed store, hash-verified

88 * @returns The final SSH client connected to the target host
89 */
90 private async establishChain(
91 jumpHosts: JumpHost[],
92 targetConfig: SSHTunnelConfig,
93 privateKey: Buffer | undefined
94 ): Promise<Client> {
95 let previousStream: Duplex | undefined;
96
97 // Connect through each jump host
98 for (let i = 0; i < jumpHosts.length; i++) {
99 const jumpHost = jumpHosts[i];
100 const nextHost = i + 1 < jumpHosts.length
101 ? jumpHosts[i + 1]
102 : { host: targetConfig.host, port: targetConfig.port || 22 };
103
104 // Per-hop credentials: use a hop's own resolved key when it has one, falling
105 // back to the target's key otherwise. The target password is always offered as
106 // a fallback (as before) — a hop may carry only a default-discovered key, so
107 // suppressing the password on "has a key" would break password auth.
108 const hopPrivateKey = jumpHost.privateKey ? this.loadPrivateKey(jumpHost.privateKey) : privateKey;
109 const hopPassword = targetConfig.password;
110 const hopPassphrase = jumpHost.passphrase ?? targetConfig.passphrase;
111
112 let client: Client | null = null;
113 let forwardStream: Duplex;
114 try {
115 client = await this.connectToHost(
116 {
117 host: jumpHost.host,
118 port: jumpHost.port,
119 username: jumpHost.username || targetConfig.username,
120 },
121 hopPassword,
122 hopPrivateKey,
123 hopPassphrase,
124 previousStream,
125 `jump host ${i + 1}`,
126 targetConfig.keepaliveInterval,
127 targetConfig.keepaliveCountMax
128 );
129
130 // Forward to the next host
131 console.error(` → Forwarding through ${jumpHost.host}:${jumpHost.port} to ${nextHost.host}:${nextHost.port}`);
132 forwardStream = await this.forwardTo(client, nextHost.host, nextHost.port);
133 } catch (error) {
134 if (client) {
135 try {
136 client.end();
137 } catch {
138 // Ignore errors during cleanup of partially established client
139 }
140 }
141 throw error;
142 }
143
144 this.sshClients.push(client);
145 previousStream = forwardStream;
146 }
147

Callers 1

establishMethod · 0.95

Calls 3

loadPrivateKeyMethod · 0.95
connectToHostMethod · 0.95
forwardToMethod · 0.95

Tested by

no test coverage detected