8 Commit-ok f9065a6a78 ... 14253afe2f

Szerző SHA1 Üzenet Dátum
  immomo808 14253afe2f remove quotes (#4938) 4 napja
  fatedier 024c334d9d Merge pull request #4928 from fatedier/xtcp 1 hete
  fatedier f795950742 bump version to v0.64.0 (#4924) 1 hete
  fatedier 024e4f5f1d improve random TLS certificate generation (#4923) 1 hete
  fatedier dc3bc9182c update sponsor info (#4917) 1 hete
  fatedier e6dacf3a67 Fix SSH tunnel gateway binding address issue #4900 (#4902) 3 hete
  fatedier 7fe295f4f4 update golangci-lint version (#4897) 3 hete
  maguowei c3bf952d8f fix webserver port not being released on frpc svr.Close() (#4896) 3 hete

+ 1 - 11
.github/workflows/golangci-lint.yml

@@ -23,14 +23,4 @@ jobs:
       uses: golangci/golangci-lint-action@v8
       with:
         # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
-        version: v2.1
-
-        # Optional: golangci-lint command line arguments.
-        # args: --issues-exit-code=0
-
-        # Optional: show only new issues if it's a pull request. The default value is `false`.
-        # only-new-issues: true
-
-        # Optional: if set to true then the all caching functionality will be complete disabled,
-        #           takes precedence over all other caching options.
-        # skip-cache: true
+        version: v2.3

+ 3 - 0
.golangci.yml

@@ -73,6 +73,9 @@ linters:
     - linters:
       - revive
       text: unused-parameter
+    - linters:
+      - revive
+      text: "avoid meaningless package names"
     - linters:
       - unparam
       text: is always false

+ 18 - 1
README.md

@@ -13,19 +13,36 @@ frp is an open source project with its ongoing development made possible entirel
 
 <h3 align="center">Gold Sponsors</h3>
 <!--gold sponsors start-->
+<p align="center">
+  <a href="https://go.warp.dev/frp" target="_blank">
+    <img width="360px" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-01.png">
+    <br>
+    <b>Warp, the intelligent terminal</b>
+    <br>
+	<sub>Available for macOS, Linux and Windows</sub>
+  </a>
+</p>
 <p align="center">
   <a href="https://jb.gg/frp" target="_blank">
     <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_jetbrains.jpg">
+	<br>
+	<b>The complete IDE crafted for professional Go developers</b>
   </a>
 </p>
 <p align="center">
   <a href="https://github.com/daytonaio/daytona" target="_blank">
     <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
+	<br>
+	<b>Secure and Elastic Infrastructure for Running Your AI-Generated Code</b>
   </a>
 </p>
 <p align="center">
   <a href="https://github.com/beclab/Olares" target="_blank">
     <img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_olares.jpeg">
+	<br>
+	<b>The sovereign cloud that puts you in control</b>
+	<br>
+	<sub>An open source, self-hosted alternative to public clouds, built for data ownership and privacy</sub>
   </a>
 </p>
 <!--gold sponsors end-->
@@ -502,7 +519,7 @@ name = "ssh"
 type = "tcp"
 localIP = "127.0.0.1"
 localPort = 22
-remotePort = "{{ .Envs.FRP_SSH_REMOTE_PORT }}"
+remotePort = {{ .Envs.FRP_SSH_REMOTE_PORT }}
 ```
 
 With the config above, variables can be passed into `frpc` program like this:

+ 5 - 1
Release.md

@@ -1,3 +1,7 @@
 ## Features
 
-* Support tokenSource for loading authentication tokens from files
+* Support tokenSource for loading authentication tokens from files.
+
+## Fixes
+
+* Fix SSH tunnel gateway incorrectly binding to proxyBindAddr instead of bindAddr, which caused external connections to fail when proxyBindAddr was set to 127.0.0.1.

+ 4 - 0
client/service.go

@@ -403,6 +403,10 @@ func (svr *Service) stop() {
 		svr.ctl.GracefulClose(svr.gracefulShutdownDuration)
 		svr.ctl = nil
 	}
+	if svr.webServer != nil {
+		svr.webServer.Close()
+		svr.webServer = nil
+	}
 }
 
 func (svr *Service) getProxyStatus(name string) (*proxy.WorkingStatus, bool) {

+ 23 - 26
client/visitor/xtcp.go

@@ -145,7 +145,7 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {
 			return
 		case <-ticker.C:
 			xl.Debugf("keepTunnelOpenWorker try to check tunnel...")
-			conn, err := sv.getTunnelConn()
+			conn, err := sv.getTunnelConn(sv.ctx)
 			if err != nil {
 				xl.Warnf("keepTunnelOpenWorker get tunnel connection error: %v", err)
 				_ = sv.retryLimiter.Wait(sv.ctx)
@@ -161,9 +161,9 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {
 
 func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
 	xl := xlog.FromContextSafe(sv.ctx)
-	isConnTransfered := false
+	isConnTransferred := false
 	defer func() {
-		if !isConnTransfered {
+		if !isConnTransferred {
 			userConn.Close()
 		}
 	}()
@@ -172,7 +172,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
 
 	// Open a tunnel connection to the server. If there is already a successful hole-punching connection,
 	// it will be reused. Otherwise, it will block and wait for a successful hole-punching connection until timeout.
-	ctx := context.Background()
+	ctx := sv.ctx
 	if sv.cfg.FallbackTo != "" {
 		timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(sv.cfg.FallbackTimeoutMs)*time.Millisecond)
 		defer cancel()
@@ -191,7 +191,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
 			xl.Errorf("transfer connection to visitor %s error: %v", sv.cfg.FallbackTo, err)
 			return
 		}
-		isConnTransfered = true
+		isConnTransferred = true
 		return
 	}
 
@@ -219,40 +219,37 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
 // openTunnel will open a tunnel connection to the target server.
 func (sv *XTCPVisitor) openTunnel(ctx context.Context) (conn net.Conn, err error) {
 	xl := xlog.FromContextSafe(sv.ctx)
-	ticker := time.NewTicker(500 * time.Millisecond)
-	defer ticker.Stop()
+	ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
+	defer cancel()
 
-	timeoutC := time.After(20 * time.Second)
-	immediateTrigger := make(chan struct{}, 1)
-	defer close(immediateTrigger)
-	immediateTrigger <- struct{}{}
+	timer := time.NewTimer(0)
+	defer timer.Stop()
 
 	for {
 		select {
 		case <-sv.ctx.Done():
 			return nil, sv.ctx.Err()
 		case <-ctx.Done():
+			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
+				return nil, fmt.Errorf("open tunnel timeout")
+			}
 			return nil, ctx.Err()
-		case <-immediateTrigger:
-			conn, err = sv.getTunnelConn()
-		case <-ticker.C:
-			conn, err = sv.getTunnelConn()
-		case <-timeoutC:
-			return nil, fmt.Errorf("open tunnel timeout")
-		}
-
-		if err != nil {
-			if err != ErrNoTunnelSession {
-				xl.Warnf("get tunnel connection error: %v", err)
+		case <-timer.C:
+			conn, err = sv.getTunnelConn(ctx)
+			if err != nil {
+				if !errors.Is(err, ErrNoTunnelSession) {
+					xl.Warnf("get tunnel connection error: %v", err)
+				}
+				timer.Reset(500 * time.Millisecond)
+				continue
 			}
-			continue
+			return conn, nil
 		}
-		return conn, nil
 	}
 }
 
-func (sv *XTCPVisitor) getTunnelConn() (net.Conn, error) {
-	conn, err := sv.session.OpenConn(sv.ctx)
+func (sv *XTCPVisitor) getTunnelConn(ctx context.Context) (net.Conn, error) {
+	conn, err := sv.session.OpenConn(ctx)
 	if err == nil {
 		return conn, nil
 	}

+ 29 - 7
pkg/transport/tls.go

@@ -22,6 +22,7 @@ import (
 	"encoding/pem"
 	"math/big"
 	"os"
+	"time"
 )
 
 func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) {
@@ -32,12 +33,30 @@ func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) {
 	return &tlsCert, nil
 }
 
-func newRandomTLSKeyPair() *tls.Certificate {
+func newRandomTLSKeyPair() (*tls.Certificate, error) {
 	key, err := rsa.GenerateKey(rand.Reader, 2048)
 	if err != nil {
-		panic(err)
+		return nil, err
+	}
+
+	// Generate a random positive serial number with 128 bits of entropy.
+	// RFC 5280 requires serial numbers to be positive integers (not zero).
+	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+	if err != nil {
+		return nil, err
+	}
+	// Ensure serial number is positive (not zero)
+	if serialNumber.Sign() == 0 {
+		serialNumber = big.NewInt(1)
+	}
+
+	template := x509.Certificate{
+		SerialNumber: serialNumber,
+		NotBefore:    time.Now().Add(-1 * time.Hour),
+		NotAfter:     time.Now().Add(365 * 24 * time.Hour * 10),
 	}
-	template := x509.Certificate{SerialNumber: big.NewInt(1)}
+
 	certDER, err := x509.CreateCertificate(
 		rand.Reader,
 		&template,
@@ -45,16 +64,16 @@ func newRandomTLSKeyPair() *tls.Certificate {
 		&key.PublicKey,
 		key)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
 	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
 	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
 
 	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
-	return &tlsCert
+	return &tlsCert, nil
 }
 
 // Only support one ca file to add
@@ -76,7 +95,10 @@ func NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
 
 	if certPath == "" || keyPath == "" {
 		// server will generate tls conf by itself
-		cert := newRandomTLSKeyPair()
+		cert, err := newRandomTLSKeyPair()
+		if err != nil {
+			return nil, err
+		}
 		base.Certificates = []tls.Certificate{*cert}
 	} else {
 		cert, err := newCustomTLSKeyPair(certPath, keyPath)

+ 1 - 1
pkg/util/version/version.go

@@ -14,7 +14,7 @@
 
 package version
 
-var version = "0.63.0"
+var version = "0.64.0"
 
 func Full() string {
 	return version

+ 1 - 1
server/service.go

@@ -262,7 +262,7 @@ func NewService(cfg *v1.ServerConfig) (*Service, error) {
 	}
 
 	if cfg.SSHTunnelGateway.BindPort > 0 {
-		sshGateway, err := ssh.NewGateway(cfg.SSHTunnelGateway, cfg.ProxyBindAddr, svr.sshTunnelListener)
+		sshGateway, err := ssh.NewGateway(cfg.SSHTunnelGateway, cfg.BindAddr, svr.sshTunnelListener)
 		if err != nil {
 			return nil, fmt.Errorf("create ssh gateway error: %v", err)
 		}