diff --git a/core/app/service/setting.go b/core/app/service/setting.go index 2555066c2..7ab3a40ec 100644 --- a/core/app/service/setting.go +++ b/core/app/service/setting.go @@ -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 { diff --git a/core/utils/cloud_storage/sftp.go b/core/utils/cloud_storage/sftp.go deleted file mode 100644 index 82d7ccb46..000000000 --- a/core/utils/cloud_storage/sftp.go +++ /dev/null @@ -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 -} diff --git a/core/utils/ssh/http.go b/core/utils/ssh/http.go new file mode 100644 index 000000000..b64b66b67 --- /dev/null +++ b/core/utils/ssh/http.go @@ -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 +} diff --git a/core/utils/ssh/ssh.go b/core/utils/ssh/ssh.go index c50b11db4..197b974f3 100644 --- a/core/utils/ssh/ssh.go +++ b/core/utils/ssh/ssh.go @@ -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 + } +}