Skip to content

Commit e25e200

Browse files
committed
feat: Support enabling proxy when adding nodes
1 parent 4e7b654 commit e25e200

File tree

3 files changed

+164
-115
lines changed

3 files changed

+164
-115
lines changed

core/utils/cloud_storage/sftp.go

Lines changed: 0 additions & 103 deletions
This file was deleted.

core/utils/ssh/http.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package ssh
2+
3+
import (
4+
"bufio"
5+
"bytes"
6+
"crypto/tls"
7+
"encoding/base64"
8+
"fmt"
9+
"net"
10+
"net/url"
11+
"strings"
12+
"time"
13+
)
14+
15+
type HTTPProxyDialer struct {
16+
ProxyURL *url.URL
17+
Timeout time.Duration
18+
TLSConfig *tls.Config
19+
}
20+
21+
func NewHTTPProxyDialer(proxyURL string) (*HTTPProxyDialer, error) {
22+
parsedURL, err := url.Parse(proxyURL)
23+
if err != nil {
24+
return nil, err
25+
}
26+
27+
return &HTTPProxyDialer{
28+
ProxyURL: parsedURL,
29+
Timeout: 30 * time.Second,
30+
TLSConfig: &tls.Config{InsecureSkipVerify: false},
31+
}, nil
32+
}
33+
34+
func (d *HTTPProxyDialer) Dial(network, addr string) (net.Conn, error) {
35+
var conn net.Conn
36+
var err error
37+
38+
if d.ProxyURL.Scheme == "https" {
39+
conn, err = tls.DialWithDialer(
40+
&net.Dialer{Timeout: d.Timeout},
41+
"tcp",
42+
d.ProxyURL.Host,
43+
d.TLSConfig,
44+
)
45+
} else {
46+
conn, err = net.DialTimeout("tcp", d.ProxyURL.Host, d.Timeout)
47+
}
48+
if err != nil {
49+
return nil, err
50+
}
51+
conn.SetDeadline(time.Now().Add(d.Timeout))
52+
connectReq := &bytes.Buffer{}
53+
54+
if d.ProxyURL.User != nil {
55+
password, _ := d.ProxyURL.User.Password()
56+
auth := base64.StdEncoding.EncodeToString(
57+
[]byte(d.ProxyURL.User.Username() + ":" + password),
58+
)
59+
fmt.Fprintf(connectReq, "Proxy-Authorization: Basic %s\r\n", auth)
60+
}
61+
fmt.Fprintf(connectReq, "Proxy-Connection: Keep-Alive\r\n")
62+
fmt.Fprintf(connectReq, "User-Agent: Go-HTTP-Proxy-Client\r\n")
63+
fmt.Fprintf(connectReq, "\r\n")
64+
65+
if _, err := conn.Write(connectReq.Bytes()); err != nil {
66+
conn.Close()
67+
return nil, err
68+
}
69+
reader := bufio.NewReader(conn)
70+
response, err := reader.ReadString('\n')
71+
if err != nil {
72+
conn.Close()
73+
return nil, err
74+
}
75+
if !strings.HasPrefix(response, "HTTP/1.1 200") &&
76+
!strings.HasPrefix(response, "HTTP/1.0 200") {
77+
conn.Close()
78+
return nil, fmt.Errorf("proxy connection failed: %s", strings.TrimSpace(response))
79+
}
80+
for {
81+
line, err := reader.ReadString('\n')
82+
if err != nil {
83+
conn.Close()
84+
return nil, err
85+
}
86+
if line == "\r\n" || line == "\n" {
87+
break
88+
}
89+
}
90+
conn.SetDeadline(time.Time{})
91+
92+
return conn, nil
93+
}

core/utils/ssh/ssh.go

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/1Panel-dev/1Panel/core/app/repo"
1112
"github.com/1Panel-dev/1Panel/core/global"
13+
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
1214
gossh "golang.org/x/crypto/ssh"
15+
"golang.org/x/net/proxy"
1316
)
1417

1518
type ConnInfo struct {
16-
User string `json:"user"`
17-
Addr string `json:"addr"`
18-
Port int `json:"port"`
19-
AuthMode string `json:"authMode"`
20-
Password string `json:"password"`
21-
PrivateKey []byte `json:"privateKey"`
22-
PassPhrase []byte `json:"passPhrase"`
19+
User string `json:"user"`
20+
Addr string `json:"addr"`
21+
Port int `json:"port"`
22+
AuthMode string `json:"authMode"`
23+
Password string `json:"password"`
24+
PrivateKey []byte `json:"privateKey"`
25+
PassPhrase []byte `json:"passPhrase"`
26+
27+
UseProxy bool `json:"useProxy"`
2328
DialTimeOut time.Duration `json:"dialTimeOut"`
2429
}
2530

@@ -52,7 +57,7 @@ func NewClient(c ConnInfo) (*SSHClient, error) {
5257
if strings.Contains(c.Addr, ":") {
5358
proto = "tcp6"
5459
}
55-
client, err := DialWithTimeout(proto, addr, config)
60+
client, err := DialWithTimeout(proto, addr, c.UseProxy, config)
5661
if nil != err {
5762
return nil, err
5863
}
@@ -240,10 +245,13 @@ func (c *SSHClient) RunWithStreamOutput(command string, outputCallback func(stri
240245
return err
241246
}
242247

243-
func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.Client, error) {
244-
conn, err := net.DialTimeout(network, addr, config.Timeout)
245-
if err != nil {
246-
return nil, err
248+
func DialWithTimeout(network, addr string, useProxy bool, config *gossh.ClientConfig) (*gossh.Client, error) {
249+
var conn net.Conn
250+
var err error
251+
if useProxy {
252+
conn, err = loadSSHConnByProxy(network, addr, config.Timeout)
253+
} else {
254+
conn, err = net.DialTimeout(network, addr, config.Timeout)
247255
}
248256
_ = conn.SetDeadline(time.Now().Add(config.Timeout))
249257
c, chans, reqs, err := gossh.NewClientConn(conn, addr, config)
@@ -256,3 +264,54 @@ func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.C
256264
}
257265
return gossh.NewClient(c, chans, reqs), nil
258266
}
267+
268+
func loadSSHConnByProxy(network, addr string, timeout time.Duration) (net.Conn, error) {
269+
settingRepo := repo.NewISettingRepo()
270+
proxyType, err := settingRepo.Get(repo.WithByKey("ProxyType"))
271+
if err != nil {
272+
return nil, fmt.Errorf("get proxy type from db failed, err: %v", err)
273+
}
274+
if len(proxyType.Value) == 0 {
275+
return nil, fmt.Errorf("get proxy type from db failed, err: %v", err)
276+
}
277+
proxyUrl, _ := settingRepo.Get(repo.WithByKey("ProxyUrl"))
278+
port, _ := settingRepo.Get(repo.WithByKey("ProxyPort"))
279+
user, _ := settingRepo.Get(repo.WithByKey("ProxyUser"))
280+
passwd, _ := settingRepo.Get(repo.WithByKey("ProxyPasswd"))
281+
282+
pass, _ := encrypt.StringDecrypt(passwd.Value)
283+
proxyItem := fmt.Sprintf("%s:%s", proxyUrl.Value, port.Value)
284+
switch proxyType.Value {
285+
case "http", "https":
286+
proxyURL := fmt.Sprintf("%s://%s:%s@%s:%s", proxyType.Value, user.Value, pass, proxyUrl.Value, port.Value)
287+
proxyDialer, err := NewHTTPProxyDialer(proxyURL)
288+
if err != nil {
289+
return nil, fmt.Errorf("failed to create proxy dialer: %w", err)
290+
}
291+
return proxyDialer.Dial(network, addr)
292+
case "socks5":
293+
var auth *proxy.Auth
294+
if len(user.Value) == 0 {
295+
auth = nil
296+
} else {
297+
auth = &proxy.Auth{
298+
User: user.Value,
299+
Password: pass,
300+
}
301+
}
302+
dialer, err := proxy.SOCKS5("tcp", proxyItem, auth, &net.Dialer{
303+
Timeout: 30 * time.Second,
304+
KeepAlive: 30 * time.Second,
305+
})
306+
if err != nil {
307+
return nil, fmt.Errorf("new socks5 proxy failed, err: %v", err)
308+
}
309+
return dialer.Dial(network, addr)
310+
default:
311+
conn, err := net.DialTimeout(network, addr, timeout)
312+
if err != nil {
313+
return nil, err
314+
}
315+
return conn, nil
316+
}
317+
}

0 commit comments

Comments
 (0)