mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-01-07 07:34:26 +08:00
feat: Support enabling proxy when adding nodes (#11525)
This commit is contained in:
parent
d9a5418029
commit
6205eac51c
4 changed files with 155 additions and 114 deletions
|
|
@ -709,7 +709,7 @@ func checkProxy(req dto.ProxyUpdate) error {
|
|||
if len(req.ProxyUser) != 0 {
|
||||
proxyURL.User = url.UserPassword(req.ProxyUser, req.ProxyPasswd)
|
||||
}
|
||||
transport = http.Transport{Proxy: http.ProxyURL(proxyURL)}
|
||||
transport = http.Transport{Proxy: http.ProxyURL(proxyURL), TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||
case "socks5":
|
||||
var auth *proxy.Auth
|
||||
if len(req.ProxyUser) == 0 {
|
||||
|
|
|
|||
|
|
@ -1,103 +0,0 @@
|
|||
package cloud_storage
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type SftpClient struct {
|
||||
connInfo string
|
||||
config *ssh.ClientConfig
|
||||
}
|
||||
|
||||
func NewSftpClient(vars map[string]interface{}) (*SftpClient, error) {
|
||||
address := loadParamFromVars("address", vars)
|
||||
port := loadParamFromVars("port", vars)
|
||||
if len(port) == 0 {
|
||||
global.LOG.Errorf("load param port from vars failed, err: not exist!")
|
||||
}
|
||||
authMode := loadParamFromVars("authMode", vars)
|
||||
passPhrase := loadParamFromVars("passPhrase", vars)
|
||||
username := loadParamFromVars("username", vars)
|
||||
password := loadParamFromVars("password", vars)
|
||||
|
||||
var auth []ssh.AuthMethod
|
||||
if authMode == "key" {
|
||||
var signer ssh.Signer
|
||||
var err error
|
||||
if len(passPhrase) != 0 {
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(password), []byte(passPhrase))
|
||||
} else {
|
||||
signer, err = ssh.ParsePrivateKey([]byte(password))
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth = []ssh.AuthMethod{ssh.PublicKeys(signer)}
|
||||
} else {
|
||||
auth = []ssh.AuthMethod{ssh.Password(password)}
|
||||
}
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: auth,
|
||||
Timeout: 30 * time.Second,
|
||||
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
addr := net.JoinHostPort(address, port)
|
||||
if _, err := ssh.Dial("tcp", addr, clientConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SftpClient{connInfo: addr, config: clientConfig}, nil
|
||||
}
|
||||
|
||||
func (s SftpClient) Upload(src, target string) (bool, error) {
|
||||
sshClient, err := ssh.Dial("tcp", s.connInfo, s.config)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer sshClient.Close()
|
||||
client, err := sftp.NewClient(sshClient)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
targetDir, _ := path.Split(target)
|
||||
if len(targetDir) != 0 {
|
||||
if _, err = client.Stat(targetDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err = client.MkdirAll(targetDir); err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
dstFile, err := client.Create(target)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
if _, err := io.Copy(dstFile, srcFile); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
81
core/utils/ssh/http.go
Normal file
81
core/utils/ssh/http.go
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
)
|
||||
|
||||
type HTTPProxyDialer struct {
|
||||
Type string
|
||||
URL string
|
||||
User string
|
||||
Password string
|
||||
}
|
||||
|
||||
func HTTPDial(dialer HTTPProxyDialer, network, addr string) (net.Conn, error) {
|
||||
var conn net.Conn
|
||||
var err error
|
||||
|
||||
global.LOG.Debugf("Dialing HTTP proxy %s for %s", dialer.URL, addr)
|
||||
dialer.URL = strings.TrimPrefix(dialer.URL, dialer.Type+"://")
|
||||
if dialer.Type == "https" {
|
||||
conn, err = tls.DialWithDialer(
|
||||
&net.Dialer{Timeout: 30 * time.Second},
|
||||
network,
|
||||
dialer.URL,
|
||||
&tls.Config{InsecureSkipVerify: true},
|
||||
)
|
||||
} else {
|
||||
conn, err = net.DialTimeout(network, dialer.URL, 30*time.Second)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.SetDeadline(time.Now().Add(30 * time.Second))
|
||||
connectReq := fmt.Sprintf("CONNECT %s HTTP/1.1\r\n", addr)
|
||||
connectReq += fmt.Sprintf("Host: %s\r\n", addr)
|
||||
connectReq += "User-Agent: Go-ssh-client/1.0\r\n"
|
||||
|
||||
if dialer.User != "" {
|
||||
auth := base64.StdEncoding.EncodeToString(
|
||||
[]byte(dialer.User + ":" + dialer.Password),
|
||||
)
|
||||
connectReq += fmt.Sprintf("Proxy-Authorization: Basic %s\r\n", auth)
|
||||
}
|
||||
connectReq += "Connection: keep-alive\r\n\r\n"
|
||||
if _, err := conn.Write([]byte(connectReq)); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
reader := bufio.NewReader(conn)
|
||||
response, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if !strings.HasPrefix(response, "HTTP/1.1 200") &&
|
||||
!strings.HasPrefix(response, "HTTP/1.0 200") {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("proxy connection failed: %s", strings.TrimSpace(response))
|
||||
}
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if line == "\r\n" || line == "\n" {
|
||||
break
|
||||
}
|
||||
}
|
||||
conn.SetDeadline(time.Time{})
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
|
@ -8,18 +8,23 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/core/app/repo"
|
||||
"github.com/1Panel-dev/1Panel/core/global"
|
||||
"github.com/1Panel-dev/1Panel/core/utils/encrypt"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
type ConnInfo struct {
|
||||
User string `json:"user"`
|
||||
Addr string `json:"addr"`
|
||||
Port int `json:"port"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey []byte `json:"privateKey"`
|
||||
PassPhrase []byte `json:"passPhrase"`
|
||||
User string `json:"user"`
|
||||
Addr string `json:"addr"`
|
||||
Port int `json:"port"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey []byte `json:"privateKey"`
|
||||
PassPhrase []byte `json:"passPhrase"`
|
||||
|
||||
UseProxy bool `json:"useProxy"`
|
||||
DialTimeOut time.Duration `json:"dialTimeOut"`
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +57,7 @@ func NewClient(c ConnInfo) (*SSHClient, error) {
|
|||
if strings.Contains(c.Addr, ":") {
|
||||
proto = "tcp6"
|
||||
}
|
||||
client, err := DialWithTimeout(proto, addr, config)
|
||||
client, err := DialWithTimeout(proto, addr, c.UseProxy, config)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -240,8 +245,14 @@ func (c *SSHClient) RunWithStreamOutput(command string, outputCallback func(stri
|
|||
return err
|
||||
}
|
||||
|
||||
func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.Client, error) {
|
||||
conn, err := net.DialTimeout(network, addr, config.Timeout)
|
||||
func DialWithTimeout(network, addr string, useProxy bool, config *gossh.ClientConfig) (*gossh.Client, error) {
|
||||
var conn net.Conn
|
||||
var err error
|
||||
if useProxy {
|
||||
conn, err = loadSSHConnByProxy(network, addr, config.Timeout)
|
||||
} else {
|
||||
conn, err = net.DialTimeout(network, addr, config.Timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -256,3 +267,55 @@ func DialWithTimeout(network, addr string, config *gossh.ClientConfig) (*gossh.C
|
|||
}
|
||||
return gossh.NewClient(c, chans, reqs), nil
|
||||
}
|
||||
|
||||
func loadSSHConnByProxy(network, addr string, timeout time.Duration) (net.Conn, error) {
|
||||
settingRepo := repo.NewISettingRepo()
|
||||
proxyType, err := settingRepo.Get(repo.WithByKey("ProxyType"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get proxy type from db failed, err: %v", err)
|
||||
}
|
||||
if len(proxyType.Value) == 0 {
|
||||
return nil, fmt.Errorf("get proxy type from db failed, err: %v", err)
|
||||
}
|
||||
proxyUrl, _ := settingRepo.Get(repo.WithByKey("ProxyUrl"))
|
||||
port, _ := settingRepo.Get(repo.WithByKey("ProxyPort"))
|
||||
user, _ := settingRepo.Get(repo.WithByKey("ProxyUser"))
|
||||
passwd, _ := settingRepo.Get(repo.WithByKey("ProxyPasswd"))
|
||||
|
||||
pass, _ := encrypt.StringDecrypt(passwd.Value)
|
||||
proxyItem := fmt.Sprintf("%s:%s", proxyUrl.Value, port.Value)
|
||||
switch proxyType.Value {
|
||||
case "http", "https":
|
||||
item := HTTPProxyDialer{
|
||||
Type: proxyType.Value,
|
||||
URL: proxyItem,
|
||||
User: user.Value,
|
||||
Password: pass,
|
||||
}
|
||||
return HTTPDial(item, network, addr)
|
||||
case "socks5":
|
||||
var auth *proxy.Auth
|
||||
if len(user.Value) == 0 {
|
||||
auth = nil
|
||||
} else {
|
||||
auth = &proxy.Auth{
|
||||
User: user.Value,
|
||||
Password: pass,
|
||||
}
|
||||
}
|
||||
dialer, err := proxy.SOCKS5("tcp", proxyItem, auth, &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new socks5 proxy failed, err: %v", err)
|
||||
}
|
||||
return dialer.Dial(network, addr)
|
||||
default:
|
||||
conn, err := net.DialTimeout(network, addr, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue