Skip to content

Commit 7fa7588

Browse files
committed
multi: SSH key file support
1 parent 715cbb0 commit 7fa7588

File tree

2 files changed

+47
-2
lines changed

2 files changed

+47
-2
lines changed

Diff for: config.go

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type sshConfig struct {
2626
LocalPort int `long:"local-port" description:"local port to connect to"`
2727
RemotePort int `long:"remote-port" description:"remote port to connect to"`
2828
KnownHosts []string `long:"known-hosts"`
29+
KeyFile string `long:"key-file" description:"path to SSH private key file"`
2930
}
3031

3132
type bitcoindConfig struct {

Diff for: main.go

+46-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"bufio"
45
"context"
56
"fmt"
67
"net"
@@ -103,9 +104,20 @@ func findSetting(key string, settings []debug.BuildSetting) string {
103104
}
104105

105106
// setupSSHTunnel creates an SSH tunnel by running the ssh command
106-
func setupSSHTunnel(ctx context.Context, conf sshConfig, out chan<- error) error {
107+
func setupSSHTunnel(ctx context.Context, conf sshConfig, out chan error) error {
108+
if conf.KeyFile == "" {
109+
return fmt.Errorf("ssh: key file is required")
110+
}
111+
107112
args := []string{
108-
"-N",
113+
"-v", "-N",
114+
"-F", "none", // don't read the default config file
115+
"-o", "PasswordAuthentication=no", // disable password authentication
116+
"-o", "PreferredAuthentications=publickey", // only use public key authentication
117+
"-o", "IdentitiesOnly=yes", // only use explicitly provided keys
118+
// "-o", "UseKeychain=no", // disable macOS keychain
119+
// "-o", "IdentityAgent=none", // disable SSH agent
120+
"-i", conf.KeyFile, // specify the key file to use
109121
"-L", fmt.Sprintf("%d:localhost:%d", conf.LocalPort, conf.RemotePort),
110122
conf.Host,
111123
}
@@ -129,6 +141,17 @@ func setupSSHTunnel(ctx context.Context, conf sshConfig, out chan<- error) error
129141
// -L: Local port forwarding
130142
cmd := exec.CommandContext(ctx, "ssh", args...)
131143

144+
// Capture stdout and stderr
145+
stdout, err := cmd.StdoutPipe()
146+
if err != nil {
147+
return fmt.Errorf("create stdout pipe: %w", err)
148+
}
149+
150+
stderr, err := cmd.StderrPipe()
151+
if err != nil {
152+
return fmt.Errorf("create stderr pipe: %w", err)
153+
}
154+
132155
// Start the SSH tunnel
133156
if err := cmd.Start(); err != nil {
134157
return fmt.Errorf("starting SSH tunnel: %w", err)
@@ -144,6 +167,24 @@ func setupSSHTunnel(ctx context.Context, conf sshConfig, out chan<- error) error
144167
}
145168
}()
146169

170+
// Log stdout in background
171+
go func() {
172+
scanner := bufio.NewScanner(stdout)
173+
for scanner.Scan() {
174+
zerolog.Ctx(ctx).Debug().
175+
Msgf("SSH tunnel stdout: %s", scanner.Text())
176+
}
177+
}()
178+
179+
// Log stderr in background
180+
go func() {
181+
scanner := bufio.NewScanner(stderr)
182+
for scanner.Scan() {
183+
zerolog.Ctx(ctx).Debug().
184+
Msgf("SSH tunnel stderr: %s", scanner.Text())
185+
}
186+
}()
187+
147188
// Wait for the tunnel to be established
148189
for i := 0; i < 10; i++ {
149190
if conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%d", conf.LocalPort)); err == nil {
@@ -153,6 +194,9 @@ func setupSSHTunnel(ctx context.Context, conf sshConfig, out chan<- error) error
153194
select {
154195
case <-ctx.Done():
155196
return fmt.Errorf("wait for SSH tunnel: %w", ctx.Err())
197+
case err := <-out:
198+
return fmt.Errorf("setup SSH tunnel: %w", err)
199+
156200
case <-time.After(time.Second):
157201
}
158202
}

0 commit comments

Comments
 (0)