68 lines
1.6 KiB
Go
68 lines
1.6 KiB
Go
package client
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
)
|
|
|
|
// LoadPrivateKey loads an SSH private key from either:
|
|
// - A file path (if the value starts with "/" or "./")
|
|
// - Raw PEM content (if the value looks like a PEM key)
|
|
//
|
|
// This allows TUNNEL_KEY to be set as a file path or pasted PEM content.
|
|
func LoadPrivateKey(keyOrPath string) (ssh.Signer, error) {
|
|
var keyBytes []byte
|
|
|
|
if isFilePath(keyOrPath) {
|
|
data, err := os.ReadFile(keyOrPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("read key file %s: %w", keyOrPath, err)
|
|
}
|
|
keyBytes = data
|
|
} else {
|
|
// Treat as raw PEM content.
|
|
keyBytes = []byte(keyOrPath)
|
|
}
|
|
|
|
signer, err := ssh.ParsePrivateKey(keyBytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("parse private key: %w", err)
|
|
}
|
|
|
|
return signer, nil
|
|
}
|
|
|
|
// isFilePath returns true if the value looks like a filesystem path
|
|
// rather than raw PEM key content.
|
|
func isFilePath(v string) bool {
|
|
if strings.HasPrefix(v, "/") || strings.HasPrefix(v, "./") || strings.HasPrefix(v, "~") {
|
|
return true
|
|
}
|
|
// If it doesn't look like PEM, assume it's a path.
|
|
if !strings.Contains(v, "-----BEGIN") {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Connect establishes an SSH connection to the tunnel server.
|
|
func Connect(addr string, signer ssh.Signer) (*ssh.Client, error) {
|
|
config := &ssh.ClientConfig{
|
|
User: "tunnel",
|
|
Auth: []ssh.AuthMethod{
|
|
ssh.PublicKeys(signer),
|
|
},
|
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
|
}
|
|
|
|
client, err := ssh.Dial("tcp", addr, config)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("SSH dial %s: %w", addr, err)
|
|
}
|
|
|
|
return client, nil
|
|
}
|