From c99986fa28bf12b4e46e32d599ddac98b0f87eb0 Mon Sep 17 00:00:00 2001 From: CrynTox <88785107+CrynTox@users.noreply.github.com> Date: Tue, 6 May 2025 07:11:20 +0300 Subject: [PATCH 1/7] build: add x64 openbsd (#4780) Co-authored-by: CrynTox <> --- Makefile.cross-compiles | 2 +- package.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.cross-compiles b/Makefile.cross-compiles index b75792a7..d084bbef 100644 --- a/Makefile.cross-compiles +++ b/Makefile.cross-compiles @@ -2,7 +2,7 @@ export PATH := $(PATH):`go env GOPATH`/bin export GO111MODULE=on LDFLAGS := -s -w -os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm:7 linux:arm:5 linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 linux:loong64 android:arm64 +os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 openbsd:amd64 linux:amd64 linux:arm:7 linux:arm:5 linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 linux:loong64 android:arm64 all: build diff --git a/package.sh b/package.sh index 4699f599..16dfcee0 100755 --- a/package.sh +++ b/package.sh @@ -17,7 +17,7 @@ make -f ./Makefile.cross-compiles rm -rf ./release/packages mkdir -p ./release/packages -os_all='linux windows darwin freebsd android' +os_all='linux windows darwin freebsd openbsd android' arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle riscv64 loong64' extra_all='_ hf' From 077ba80ba35d8ad257f99bbb819fb4cbeaa9e1ba Mon Sep 17 00:00:00 2001 From: scientificworld <30764166+scientificworld@users.noreply.github.com> Date: Mon, 19 May 2025 11:39:35 +0800 Subject: [PATCH 2/7] fix: type error in server_plugin doc (#4799) --- doc/server_plugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/server_plugin.md b/doc/server_plugin.md index b8fa1619..6ddef827 100644 --- a/doc/server_plugin.md +++ b/doc/server_plugin.md @@ -121,7 +121,7 @@ Create new proxy // http and https only "custom_domains": [], "subdomain": , - "locations": , + "locations": [], "http_user": , "http_pwd": , "host_header_rewrite": , From 8eb525a64853aa15fb2bd21865ccaad3092baf29 Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 23 May 2025 19:25:34 +0800 Subject: [PATCH 3/7] feat: support YAML merge in strict configuration mode (#4809) --- Release.md | 4 +- pkg/config/load.go | 34 ++++++++++- pkg/config/load_test.go | 119 ++++++++++++++++++++++++++++++++++++ pkg/util/version/version.go | 2 +- 4 files changed, 154 insertions(+), 5 deletions(-) diff --git a/Release.md b/Release.md index 94cba964..07c58d4a 100644 --- a/Release.md +++ b/Release.md @@ -1,3 +1,3 @@ -### Bug Fixes +## Features -* **VirtualNet:** Resolved various issues related to connection handling, TUN device management, and stability in the virtual network feature. \ No newline at end of file +* Support for YAML merge functionality (anchors and references with dot-prefixed fields) in strict configuration mode without requiring `--strict-config=false` parameter. \ No newline at end of file diff --git a/pkg/config/load.go b/pkg/config/load.go index fa394dda..bb050b40 100644 --- a/pkg/config/load.go +++ b/pkg/config/load.go @@ -111,6 +111,33 @@ func LoadConfigureFromFile(path string, c any, strict bool) error { return LoadConfigure(content, c, strict) } +// parseYAMLWithDotFieldsHandling parses YAML with dot-prefixed fields handling +// This function handles both cases efficiently: with or without dot fields +func parseYAMLWithDotFieldsHandling(content []byte, target any) error { + var temp any + if err := yaml.Unmarshal(content, &temp); err != nil { + return err + } + + // Remove dot fields if it's a map + if tempMap, ok := temp.(map[string]any); ok { + for key := range tempMap { + if strings.HasPrefix(key, ".") { + delete(tempMap, key) + } + } + } + + // Convert to JSON and decode with strict validation + jsonBytes, err := json.Marshal(temp) + if err != nil { + return err + } + decoder := json.NewDecoder(bytes.NewReader(jsonBytes)) + decoder.DisallowUnknownFields() + return decoder.Decode(target) +} + // LoadConfigure loads configuration from bytes and unmarshal into c. // Now it supports json, yaml and toml format. func LoadConfigure(b []byte, c any, strict bool) error { @@ -134,10 +161,13 @@ func LoadConfigure(b []byte, c any, strict bool) error { } return decoder.Decode(c) } - // It wasn't JSON. Unmarshal as YAML. + + // Handle YAML content if strict { - return yaml.UnmarshalStrict(b, c) + // In strict mode, always use our custom handler to support YAML merge + return parseYAMLWithDotFieldsHandling(b, c) } + // Non-strict mode, parse normally return yaml.Unmarshal(b, c) } diff --git a/pkg/config/load_test.go b/pkg/config/load_test.go index 980a332a..95d6101e 100644 --- a/pkg/config/load_test.go +++ b/pkg/config/load_test.go @@ -187,3 +187,122 @@ unixPath = "/tmp/uds.sock" err = LoadConfigure([]byte(pluginStr), &clientCfg, true) require.Error(err) } + +// TestYAMLMergeInStrictMode tests that YAML merge functionality works +// even in strict mode by properly handling dot-prefixed fields +func TestYAMLMergeInStrictMode(t *testing.T) { + require := require.New(t) + + yamlContent := ` +serverAddr: "127.0.0.1" +serverPort: 7000 + +.common: &common + type: stcp + secretKey: "test-secret" + localIP: 127.0.0.1 + transport: + useEncryption: true + useCompression: true + +proxies: +- name: ssh + localPort: 22 + <<: *common +- name: web + localPort: 80 + <<: *common +` + + clientCfg := v1.ClientConfig{} + // This should work in strict mode + err := LoadConfigure([]byte(yamlContent), &clientCfg, true) + require.NoError(err) + + // Verify the merge worked correctly + require.Equal("127.0.0.1", clientCfg.ServerAddr) + require.Equal(7000, clientCfg.ServerPort) + require.Len(clientCfg.Proxies, 2) + + // Check first proxy + sshProxy := clientCfg.Proxies[0].ProxyConfigurer + require.Equal("ssh", sshProxy.GetBaseConfig().Name) + require.Equal("stcp", sshProxy.GetBaseConfig().Type) + + // Check second proxy + webProxy := clientCfg.Proxies[1].ProxyConfigurer + require.Equal("web", webProxy.GetBaseConfig().Name) + require.Equal("stcp", webProxy.GetBaseConfig().Type) +} + +// TestOptimizedYAMLProcessing tests the optimization logic for YAML processing +func TestOptimizedYAMLProcessing(t *testing.T) { + require := require.New(t) + + yamlWithDotFields := []byte(` +serverAddr: "127.0.0.1" +.common: &common + type: stcp +proxies: +- name: test + <<: *common +`) + + yamlWithoutDotFields := []byte(` +serverAddr: "127.0.0.1" +proxies: +- name: test + type: tcp + localPort: 22 +`) + + // Test that YAML without dot fields works in strict mode + clientCfg := v1.ClientConfig{} + err := LoadConfigure(yamlWithoutDotFields, &clientCfg, true) + require.NoError(err) + require.Equal("127.0.0.1", clientCfg.ServerAddr) + require.Len(clientCfg.Proxies, 1) + require.Equal("test", clientCfg.Proxies[0].ProxyConfigurer.GetBaseConfig().Name) + + // Test that YAML with dot fields still works in strict mode + err = LoadConfigure(yamlWithDotFields, &clientCfg, true) + require.NoError(err) + require.Equal("127.0.0.1", clientCfg.ServerAddr) + require.Len(clientCfg.Proxies, 1) + require.Equal("test", clientCfg.Proxies[0].ProxyConfigurer.GetBaseConfig().Name) + require.Equal("stcp", clientCfg.Proxies[0].ProxyConfigurer.GetBaseConfig().Type) +} + +// TestYAMLEdgeCases tests edge cases for YAML parsing, including non-map types +func TestYAMLEdgeCases(t *testing.T) { + require := require.New(t) + + // Test array at root (should fail for frp config) + arrayYAML := []byte(` +- item1 +- item2 +`) + clientCfg := v1.ClientConfig{} + err := LoadConfigure(arrayYAML, &clientCfg, true) + require.Error(err) // Should fail because ClientConfig expects an object + + // Test scalar at root (should fail for frp config) + scalarYAML := []byte(`"just a string"`) + err = LoadConfigure(scalarYAML, &clientCfg, true) + require.Error(err) // Should fail because ClientConfig expects an object + + // Test empty object (should work) + emptyYAML := []byte(`{}`) + err = LoadConfigure(emptyYAML, &clientCfg, true) + require.NoError(err) + + // Test nested structure without dots (should work) + nestedYAML := []byte(` +serverAddr: "127.0.0.1" +serverPort: 7000 +`) + err = LoadConfigure(nestedYAML, &clientCfg, true) + require.NoError(err) + require.Equal("127.0.0.1", clientCfg.ServerAddr) + require.Equal(7000, clientCfg.ServerPort) +} diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go index 8fa6bc19..966a942f 100644 --- a/pkg/util/version/version.go +++ b/pkg/util/version/version.go @@ -14,7 +14,7 @@ package version -var version = "0.62.1" +var version = "0.63.0" func Full() string { return version From 3fa76b72f3e6b2b9d8b0093ea755d5d1eb0d6b92 Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 23 May 2025 21:39:47 +0800 Subject: [PATCH 4/7] add proxy protocol support for UDP proxies (#4810) --- README.md | 2 +- Release.md | 3 +- client/proxy/proxy.go | 24 +--- client/proxy/sudp.go | 2 +- client/proxy/udp.go | 4 +- pkg/proto/udp/udp.go | 16 ++- pkg/util/net/proxyprotocol.go | 45 ++++++++ pkg/util/net/proxyprotocol_test.go | 178 +++++++++++++++++++++++++++++ test/e2e/v1/features/real_ip.go | 50 ++++++++ 9 files changed, 299 insertions(+), 25 deletions(-) create mode 100644 pkg/util/net/proxyprotocol.go create mode 100644 pkg/util/net/proxyprotocol_test.go diff --git a/README.md b/README.md index 25537220..f0ab4273 100644 --- a/README.md +++ b/README.md @@ -1025,7 +1025,7 @@ You can get user's real IP from HTTP request headers `X-Forwarded-For`. #### Proxy Protocol -frp supports Proxy Protocol to send user's real IP to local services. It support all types except UDP. +frp supports Proxy Protocol to send user's real IP to local services. Here is an example for https service: diff --git a/Release.md b/Release.md index 07c58d4a..19b79d64 100644 --- a/Release.md +++ b/Release.md @@ -1,3 +1,4 @@ ## Features -* Support for YAML merge functionality (anchors and references with dot-prefixed fields) in strict configuration mode without requiring `--strict-config=false` parameter. \ No newline at end of file +* Support for YAML merge functionality (anchors and references with dot-prefixed fields) in strict configuration mode without requiring `--strict-config=false` parameter. +* Support for proxy protocol in UDP proxies to preserve real client IP addresses. \ No newline at end of file diff --git a/client/proxy/proxy.go b/client/proxy/proxy.go index debda9fa..876ca579 100644 --- a/client/proxy/proxy.go +++ b/client/proxy/proxy.go @@ -20,13 +20,11 @@ import ( "net" "reflect" "strconv" - "strings" "sync" "time" libio "github.com/fatedier/golib/io" libnet "github.com/fatedier/golib/net" - pp "github.com/pires/go-proxyproto" "golang.org/x/time/rate" "github.com/fatedier/frp/pkg/config/types" @@ -35,6 +33,7 @@ import ( plugin "github.com/fatedier/frp/pkg/plugin/client" "github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/util/limit" + netpkg "github.com/fatedier/frp/pkg/util/net" "github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/vnet" ) @@ -176,24 +175,9 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor } if baseCfg.Transport.ProxyProtocolVersion != "" && m.SrcAddr != "" && m.SrcPort != 0 { - h := &pp.Header{ - Command: pp.PROXY, - SourceAddr: connInfo.SrcAddr, - DestinationAddr: connInfo.DstAddr, - } - - if strings.Contains(m.SrcAddr, ".") { - h.TransportProtocol = pp.TCPv4 - } else { - h.TransportProtocol = pp.TCPv6 - } - - if baseCfg.Transport.ProxyProtocolVersion == "v1" { - h.Version = 1 - } else if baseCfg.Transport.ProxyProtocolVersion == "v2" { - h.Version = 2 - } - connInfo.ProxyProtocolHeader = h + // Use the common proxy protocol builder function + header := netpkg.BuildProxyProtocolHeaderStruct(connInfo.SrcAddr, connInfo.DstAddr, baseCfg.Transport.ProxyProtocolVersion) + connInfo.ProxyProtocolHeader = header } connInfo.Conn = remote connInfo.UnderlyingConn = workConn diff --git a/client/proxy/sudp.go b/client/proxy/sudp.go index ad9db89a..13741d0d 100644 --- a/client/proxy/sudp.go +++ b/client/proxy/sudp.go @@ -205,5 +205,5 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) { go workConnReaderFn(workConn, readCh) go heartbeatFn(sendCh) - udp.Forwarder(pxy.localAddr, readCh, sendCh, int(pxy.clientCfg.UDPPacketSize)) + udp.Forwarder(pxy.localAddr, readCh, sendCh, int(pxy.clientCfg.UDPPacketSize), pxy.cfg.Transport.ProxyProtocolVersion) } diff --git a/client/proxy/udp.go b/client/proxy/udp.go index b08fe160..b70ffe4a 100644 --- a/client/proxy/udp.go +++ b/client/proxy/udp.go @@ -171,5 +171,7 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) { go workConnSenderFn(pxy.workConn, pxy.sendCh) go workConnReaderFn(pxy.workConn, pxy.readCh) go heartbeatFn(pxy.sendCh) - udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh, int(pxy.clientCfg.UDPPacketSize)) + + // Call Forwarder with proxy protocol version (empty string means no proxy protocol) + udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh, int(pxy.clientCfg.UDPPacketSize), pxy.cfg.Transport.ProxyProtocolVersion) } diff --git a/pkg/proto/udp/udp.go b/pkg/proto/udp/udp.go index 7a11984b..f97b3b43 100644 --- a/pkg/proto/udp/udp.go +++ b/pkg/proto/udp/udp.go @@ -24,6 +24,7 @@ import ( "github.com/fatedier/golib/pool" "github.com/fatedier/frp/pkg/msg" + netpkg "github.com/fatedier/frp/pkg/util/net" ) func NewUDPPacket(buf []byte, laddr, raddr *net.UDPAddr) *msg.UDPPacket { @@ -69,7 +70,7 @@ func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UDPPacket, sendCh } } -func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UDPPacket, sendCh chan<- msg.Message, bufSize int) { +func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UDPPacket, sendCh chan<- msg.Message, bufSize int, proxyProtocolVersion string) { var mu sync.RWMutex udpConnMap := make(map[string]*net.UDPConn) @@ -110,6 +111,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UDPPacket, sendCh chan<- if err != nil { continue } + mu.Lock() udpConn, ok := udpConnMap[udpMsg.RemoteAddr.String()] if !ok { @@ -122,6 +124,18 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UDPPacket, sendCh chan<- } mu.Unlock() + // Add proxy protocol header if configured + if proxyProtocolVersion != "" && udpMsg.RemoteAddr != nil { + ppBuf, err := netpkg.BuildProxyProtocolHeader(udpMsg.RemoteAddr, dstAddr, proxyProtocolVersion) + if err == nil { + // Prepend proxy protocol header to the UDP payload + finalBuf := make([]byte, len(ppBuf)+len(buf)) + copy(finalBuf, ppBuf) + copy(finalBuf[len(ppBuf):], buf) + buf = finalBuf + } + } + _, err = udpConn.Write(buf) if err != nil { udpConn.Close() diff --git a/pkg/util/net/proxyprotocol.go b/pkg/util/net/proxyprotocol.go new file mode 100644 index 00000000..5f0cd51f --- /dev/null +++ b/pkg/util/net/proxyprotocol.go @@ -0,0 +1,45 @@ +// Copyright 2025 The frp Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package net + +import ( + "bytes" + "fmt" + "net" + + pp "github.com/pires/go-proxyproto" +) + +func BuildProxyProtocolHeaderStruct(srcAddr, dstAddr net.Addr, version string) *pp.Header { + var versionByte byte + if version == "v1" { + versionByte = 1 + } else { + versionByte = 2 // default to v2 + } + return pp.HeaderProxyFromAddrs(versionByte, srcAddr, dstAddr) +} + +func BuildProxyProtocolHeader(srcAddr, dstAddr net.Addr, version string) ([]byte, error) { + h := BuildProxyProtocolHeaderStruct(srcAddr, dstAddr, version) + + // Convert header to bytes using a buffer + var buf bytes.Buffer + _, err := h.WriteTo(&buf) + if err != nil { + return nil, fmt.Errorf("failed to write proxy protocol header: %v", err) + } + return buf.Bytes(), nil +} diff --git a/pkg/util/net/proxyprotocol_test.go b/pkg/util/net/proxyprotocol_test.go new file mode 100644 index 00000000..187801f6 --- /dev/null +++ b/pkg/util/net/proxyprotocol_test.go @@ -0,0 +1,178 @@ +package net + +import ( + "net" + "testing" + + pp "github.com/pires/go-proxyproto" + "github.com/stretchr/testify/require" +) + +func TestBuildProxyProtocolHeader(t *testing.T) { + require := require.New(t) + + tests := []struct { + name string + srcAddr net.Addr + dstAddr net.Addr + version string + expectError bool + }{ + { + name: "UDP IPv4 v2", + srcAddr: &net.UDPAddr{IP: net.ParseIP("192.168.1.100"), Port: 12345}, + dstAddr: &net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 3306}, + version: "v2", + expectError: false, + }, + { + name: "TCP IPv4 v1", + srcAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.100"), Port: 12345}, + dstAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 80}, + version: "v1", + expectError: false, + }, + { + name: "UDP IPv6 v2", + srcAddr: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 12345}, + dstAddr: &net.UDPAddr{IP: net.ParseIP("::1"), Port: 3306}, + version: "v2", + expectError: false, + }, + { + name: "TCP IPv6 v1", + srcAddr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 12345}, + dstAddr: &net.TCPAddr{IP: net.ParseIP("2001:db8::1"), Port: 80}, + version: "v1", + expectError: false, + }, + { + name: "nil source address", + srcAddr: nil, + dstAddr: &net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 3306}, + version: "v2", + expectError: false, + }, + { + name: "nil destination address", + srcAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.100"), Port: 12345}, + dstAddr: nil, + version: "v2", + expectError: false, + }, + { + name: "unsupported address type", + srcAddr: &net.UnixAddr{Name: "/tmp/test.sock", Net: "unix"}, + dstAddr: &net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 3306}, + version: "v2", + expectError: false, + }, + } + + for _, tt := range tests { + header, err := BuildProxyProtocolHeader(tt.srcAddr, tt.dstAddr, tt.version) + + if tt.expectError { + require.Error(err, "test case: %s", tt.name) + continue + } + + require.NoError(err, "test case: %s", tt.name) + require.NotEmpty(header, "test case: %s", tt.name) + } +} + +func TestBuildProxyProtocolHeaderStruct(t *testing.T) { + require := require.New(t) + + tests := []struct { + name string + srcAddr net.Addr + dstAddr net.Addr + version string + expectedProtocol pp.AddressFamilyAndProtocol + expectedVersion byte + expectedCommand pp.ProtocolVersionAndCommand + expectedSourceAddr net.Addr + expectedDestAddr net.Addr + }{ + { + name: "TCP IPv4 v2", + srcAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.100"), Port: 12345}, + dstAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 80}, + version: "v2", + expectedProtocol: pp.TCPv4, + expectedVersion: 2, + expectedCommand: pp.PROXY, + expectedSourceAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.100"), Port: 12345}, + expectedDestAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.1"), Port: 80}, + }, + { + name: "UDP IPv6 v1", + srcAddr: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 12345}, + dstAddr: &net.UDPAddr{IP: net.ParseIP("::1"), Port: 3306}, + version: "v1", + expectedProtocol: pp.UDPv6, + expectedVersion: 1, + expectedCommand: pp.PROXY, + expectedSourceAddr: &net.UDPAddr{IP: net.ParseIP("2001:db8::1"), Port: 12345}, + expectedDestAddr: &net.UDPAddr{IP: net.ParseIP("::1"), Port: 3306}, + }, + { + name: "TCP IPv6 default version", + srcAddr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 12345}, + dstAddr: &net.TCPAddr{IP: net.ParseIP("2001:db8::1"), Port: 80}, + version: "", + expectedProtocol: pp.TCPv6, + expectedVersion: 2, // default to v2 + expectedCommand: pp.PROXY, + expectedSourceAddr: &net.TCPAddr{IP: net.ParseIP("::1"), Port: 12345}, + expectedDestAddr: &net.TCPAddr{IP: net.ParseIP("2001:db8::1"), Port: 80}, + }, + { + name: "nil source address", + srcAddr: nil, + dstAddr: &net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 3306}, + version: "v2", + expectedProtocol: pp.UNSPEC, + expectedVersion: 2, + expectedCommand: pp.LOCAL, + expectedSourceAddr: nil, // go-proxyproto sets both to nil when srcAddr is nil + expectedDestAddr: nil, + }, + { + name: "nil destination address", + srcAddr: &net.TCPAddr{IP: net.ParseIP("192.168.1.100"), Port: 12345}, + dstAddr: nil, + version: "v2", + expectedProtocol: pp.UNSPEC, + expectedVersion: 2, + expectedCommand: pp.LOCAL, + expectedSourceAddr: nil, // go-proxyproto sets both to nil when dstAddr is nil + expectedDestAddr: nil, + }, + { + name: "unsupported address type", + srcAddr: &net.UnixAddr{Name: "/tmp/test.sock", Net: "unix"}, + dstAddr: &net.UDPAddr{IP: net.ParseIP("10.0.0.1"), Port: 3306}, + version: "v2", + expectedProtocol: pp.UNSPEC, + expectedVersion: 2, + expectedCommand: pp.LOCAL, + expectedSourceAddr: nil, // go-proxyproto sets both to nil for unsupported types + expectedDestAddr: nil, + }, + } + + for _, tt := range tests { + header := BuildProxyProtocolHeaderStruct(tt.srcAddr, tt.dstAddr, tt.version) + + require.NotNil(header, "test case: %s", tt.name) + + require.Equal(tt.expectedCommand, header.Command, "test case: %s", tt.name) + require.Equal(tt.expectedSourceAddr, header.SourceAddr, "test case: %s", tt.name) + require.Equal(tt.expectedDestAddr, header.DestinationAddr, "test case: %s", tt.name) + require.Equal(tt.expectedProtocol, header.TransportProtocol, "test case: %s", tt.name) + require.Equal(tt.expectedVersion, header.Version, "test case: %s", tt.name) + } +} diff --git a/test/e2e/v1/features/real_ip.go b/test/e2e/v1/features/real_ip.go index 216f531d..a52cf0a2 100644 --- a/test/e2e/v1/features/real_ip.go +++ b/test/e2e/v1/features/real_ip.go @@ -227,6 +227,56 @@ var _ = ginkgo.Describe("[Feature: Real IP]", func() { }) }) + ginkgo.It("UDP", func() { + serverConf := consts.DefaultServerConfig + clientConf := consts.DefaultClientConfig + + localPort := f.AllocPort() + localServer := streamserver.New(streamserver.UDP, streamserver.WithBindPort(localPort), + streamserver.WithCustomHandler(func(c net.Conn) { + defer c.Close() + rd := bufio.NewReader(c) + ppHeader, err := pp.Read(rd) + if err != nil { + log.Errorf("read proxy protocol error: %v", err) + return + } + + // Read the actual UDP content after proxy protocol header + if _, err := rpc.ReadBytes(rd); err != nil { + return + } + + buf := []byte(ppHeader.SourceAddr.String()) + _, _ = rpc.WriteBytes(c, buf) + })) + f.RunServer("", localServer) + + remotePort := f.AllocPort() + clientConf += fmt.Sprintf(` + [[proxies]] + name = "udp" + type = "udp" + localPort = %d + remotePort = %d + transport.proxyProtocolVersion = "v2" + `, localPort, remotePort) + + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + framework.NewRequestExpect(f).Protocol("udp").Port(remotePort).Ensure(func(resp *request.Response) bool { + log.Tracef("udp proxy protocol get SourceAddr: %s", string(resp.Content)) + addr, err := net.ResolveUDPAddr("udp", string(resp.Content)) + if err != nil { + return false + } + if addr.IP.String() != "127.0.0.1" { + return false + } + return true + }) + }) + ginkgo.It("HTTP", func() { vhostHTTPPort := f.AllocPort() serverConf := consts.DefaultServerConfig + fmt.Sprintf(` From 720c09c06b31b44295a90dab9428a910e3c1b222 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 26 May 2025 14:54:03 +0800 Subject: [PATCH 5/7] update test package (#4814) --- .github/workflows/stale.yml | 2 +- pkg/proto/udp/udp_test.go | 8 +++---- pkg/util/metric/counter_test.go | 12 +++++----- pkg/util/metric/date_counter_test.go | 18 +++++++------- pkg/util/util/util_test.go | 36 +++++++++++++--------------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 784053e8..8f10d641 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,4 +1,4 @@ -name: "Close stale issues" +name: "Close stale issues and PRs" on: schedule: - cron: "20 0 * * *" diff --git a/pkg/proto/udp/udp_test.go b/pkg/proto/udp/udp_test.go index 0e61d9e6..1a7f0091 100644 --- a/pkg/proto/udp/udp_test.go +++ b/pkg/proto/udp/udp_test.go @@ -3,16 +3,16 @@ package udp import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestUdpPacket(t *testing.T) { - assert := assert.New(t) + require := require.New(t) buf := []byte("hello world") udpMsg := NewUDPPacket(buf, nil, nil) newBuf, err := GetContent(udpMsg) - assert.NoError(err) - assert.EqualValues(buf, newBuf) + require.NoError(err) + require.EqualValues(buf, newBuf) } diff --git a/pkg/util/metric/counter_test.go b/pkg/util/metric/counter_test.go index 4925c25b..dca72052 100644 --- a/pkg/util/metric/counter_test.go +++ b/pkg/util/metric/counter_test.go @@ -3,21 +3,21 @@ package metric import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCounter(t *testing.T) { - assert := assert.New(t) + require := require.New(t) c := NewCounter() c.Inc(10) - assert.EqualValues(10, c.Count()) + require.EqualValues(10, c.Count()) c.Dec(5) - assert.EqualValues(5, c.Count()) + require.EqualValues(5, c.Count()) cTmp := c.Snapshot() - assert.EqualValues(5, cTmp.Count()) + require.EqualValues(5, cTmp.Count()) c.Clear() - assert.EqualValues(0, c.Count()) + require.EqualValues(0, c.Count()) } diff --git a/pkg/util/metric/date_counter_test.go b/pkg/util/metric/date_counter_test.go index c9997c70..8752f198 100644 --- a/pkg/util/metric/date_counter_test.go +++ b/pkg/util/metric/date_counter_test.go @@ -3,25 +3,25 @@ package metric import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestDateCounter(t *testing.T) { - assert := assert.New(t) + require := require.New(t) dc := NewDateCounter(3) dc.Inc(10) - assert.EqualValues(10, dc.TodayCount()) + require.EqualValues(10, dc.TodayCount()) dc.Dec(5) - assert.EqualValues(5, dc.TodayCount()) + require.EqualValues(5, dc.TodayCount()) counts := dc.GetLastDaysCount(3) - assert.EqualValues(3, len(counts)) - assert.EqualValues(5, counts[0]) - assert.EqualValues(0, counts[1]) - assert.EqualValues(0, counts[2]) + require.EqualValues(3, len(counts)) + require.EqualValues(5, counts[0]) + require.EqualValues(0, counts[1]) + require.EqualValues(0, counts[2]) dcTmp := dc.Snapshot() - assert.EqualValues(5, dcTmp.TodayCount()) + require.EqualValues(5, dcTmp.TodayCount()) } diff --git a/pkg/util/util/util_test.go b/pkg/util/util/util_test.go index 00618611..0a63ba6d 100644 --- a/pkg/util/util/util_test.go +++ b/pkg/util/util/util_test.go @@ -3,45 +3,41 @@ package util import ( "testing" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRandId(t *testing.T) { - assert := assert.New(t) + require := require.New(t) id, err := RandID() - assert.NoError(err) + require.NoError(err) t.Log(id) - assert.Equal(16, len(id)) + require.Equal(16, len(id)) } func TestGetAuthKey(t *testing.T) { - assert := assert.New(t) + require := require.New(t) key := GetAuthKey("1234", 1488720000) - assert.Equal("6df41a43725f0c770fd56379e12acf8c", key) + require.Equal("6df41a43725f0c770fd56379e12acf8c", key) } func TestParseRangeNumbers(t *testing.T) { - assert := assert.New(t) + require := require.New(t) numbers, err := ParseRangeNumbers("2-5") - if assert.NoError(err) { - assert.Equal([]int64{2, 3, 4, 5}, numbers) - } + require.NoError(err) + require.Equal([]int64{2, 3, 4, 5}, numbers) numbers, err = ParseRangeNumbers("1") - if assert.NoError(err) { - assert.Equal([]int64{1}, numbers) - } + require.NoError(err) + require.Equal([]int64{1}, numbers) numbers, err = ParseRangeNumbers("3-5,8") - if assert.NoError(err) { - assert.Equal([]int64{3, 4, 5, 8}, numbers) - } + require.NoError(err) + require.Equal([]int64{3, 4, 5, 8}, numbers) numbers, err = ParseRangeNumbers(" 3-5,8, 10-12 ") - if assert.NoError(err) { - assert.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers) - } + require.NoError(err) + require.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers) _, err = ParseRangeNumbers("3-a") - assert.Error(err) + require.Error(err) } From 43cf1688e483bc210f334b6e4c664fea0e3324d8 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 27 May 2025 16:46:15 +0800 Subject: [PATCH 6/7] update golangci-lint version (#4817) --- .github/workflows/golangci-lint.yml | 10 +- .golangci.yml | 216 +++++++++++-------------- client/service.go | 7 +- go.mod | 16 +- go.sum | 32 ++-- hack/run-e2e.sh | 6 +- pkg/config/legacy/client.go | 7 +- pkg/config/legacy/conversion.go | 44 ++--- pkg/config/legacy/proxy.go | 2 +- pkg/config/v1/proxy.go | 2 +- pkg/metrics/mem/server.go | 2 +- pkg/util/net/conn.go | 2 +- pkg/util/vhost/http.go | 6 +- pkg/util/vhost/vhost.go | 6 +- test/e2e/legacy/basic/client_server.go | 8 +- test/e2e/legacy/plugin/server.go | 4 +- test/e2e/v1/basic/client_server.go | 8 +- test/e2e/v1/plugin/server.go | 4 +- 18 files changed, 172 insertions(+), 210 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 80585928..40aba1ce 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -20,10 +20,10 @@ jobs: go-version: '1.23' cache: false - name: golangci-lint - uses: golangci/golangci-lint-action@v4 + 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: v1.61 + version: v2.1 # Optional: golangci-lint command line arguments. # args: --issues-exit-code=0 @@ -34,9 +34,3 @@ jobs: # Optional: if set to true then the all caching functionality will be complete disabled, # takes precedence over all other caching options. # skip-cache: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true diff --git a/.golangci.yml b/.golangci.yml index a45e4ba3..be0a717e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,139 +1,111 @@ -service: - golangci-lint-version: 1.61.x # use the fixed version to not introduce new linters unexpectedly - +version: "2" run: concurrency: 4 - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 20m build-tags: - integ - integfuzz - linters: - disable-all: true + default: none enable: - - unused - - errcheck + - asciicheck - copyloopvar + - errcheck - gocritic - - gofumpt - - goimports - - revive - - gosimple + - gosec - govet - ineffassign - lll + - makezero - misspell - - staticcheck - - stylecheck - - typecheck - - unconvert - - unparam - - gci - - gosec - - asciicheck - prealloc - predeclared - - makezero - fast: false - -linters-settings: - errcheck: - # report about not checking of errors in type assetions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: false - - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; - # default is false: such cases aren't reported by default. - check-blank: false - govet: - # report about shadowed variables - disable: - - shadow - maligned: - # print struct with more effective memory layout or not, false by default - suggest-new: true - misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. - locale: US - ignore-words: - - cancelled - - marshalled - lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option - line-length: 160 - # tab width in spaces. Default to 1. - tab-width: 1 - gocritic: - disabled-checks: - - exitAfterDefer - unused: - check-exported: false - unparam: - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - gci: - sections: - - standard - - default - - prefix(github.com/fatedier/frp/) - gosec: - severity: "low" - confidence: "low" - excludes: - - G401 - - G402 - - G404 - - G501 - - G115 # integer overflow conversion - + - revive + - staticcheck + - unconvert + - unparam + - unused + settings: + errcheck: + check-type-assertions: false + check-blank: false + gocritic: + disabled-checks: + - exitAfterDefer + gosec: + excludes: + - G401 + - G402 + - G404 + - G501 + - G115 + severity: low + confidence: low + govet: + disable: + - shadow + lll: + line-length: 160 + tab-width: 1 + misspell: + locale: US + ignore-rules: + - cancelled + - marshalled + unparam: + check-exported: false + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - errcheck + - maligned + path: _test\.go$|^tests/|^samples/ + - linters: + - revive + - staticcheck + text: use underscores in Go names + - linters: + - revive + text: unused-parameter + - linters: + - unparam + text: is always false + paths: + - .*\.pb\.go + - .*\.gen\.go + - genfiles$ + - vendor$ + - bin$ + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gci + - gofumpt + - goimports + settings: + gci: + sections: + - standard + - default + - prefix(github.com/fatedier/frp/) + exclusions: + generated: lax + paths: + - .*\.pb\.go + - .*\.gen\.go + - genfiles$ + - vendor$ + - bin$ + - third_party$ + - builtin$ + - examples$ issues: - # List of regexps of issue texts to exclude, empty list by default. - # But independently from this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. To list all - # excluded by default patterns execute `golangci-lint run --help` - # exclude: - # - composite literal uses unkeyed fields - - exclude-rules: - # Exclude some linters from running on test files. - - path: _test\.go$|^tests/|^samples/ - linters: - - errcheck - - maligned - - linters: - - revive - - stylecheck - text: "use underscores in Go names" - - linters: - - revive - text: "unused-parameter" - - linters: - - unparam - text: "is always false" - - exclude-dirs: - - genfiles$ - - vendor$ - - bin$ - exclude-files: - - ".*\\.pb\\.go" - - ".*\\.gen\\.go" - - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: true - - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. - max-per-linter: 0 - - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-issues-per-linter: 0 max-same-issues: 0 diff --git a/client/service.go b/client/service.go index e163cac4..d6a12970 100644 --- a/client/service.go +++ b/client/service.go @@ -325,10 +325,9 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE proxyCfgs := svr.proxyCfgs visitorCfgs := svr.visitorCfgs svr.cfgMu.RUnlock() - connEncrypted := true - if svr.clientSpec != nil && svr.clientSpec.Type == "ssh-tunnel" { - connEncrypted = false - } + + connEncrypted := svr.clientSpec == nil || svr.clientSpec.Type != "ssh-tunnel" + sessionCtx := &SessionContext{ Common: svr.common, RunID: svr.runID, diff --git a/go.mod b/go.mod index 0b8ee2a5..e3bdc711 100644 --- a/go.mod +++ b/go.mod @@ -10,8 +10,8 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/yamux v0.1.1 - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.34.2 + github.com/onsi/ginkgo/v2 v2.23.4 + github.com/onsi/gomega v1.36.3 github.com/pelletier/go-toml/v2 v2.2.0 github.com/pion/stun/v2 v2.0.0 github.com/pires/go-proxyproto v0.7.0 @@ -46,12 +46,11 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/pprof v0.0.0-20241206021119-61a79c692802 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/reedsolomon v1.12.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/pion/dtls/v2 v2.2.7 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/transport/v2 v2.2.1 // indirect @@ -67,14 +66,15 @@ require ( github.com/tidwall/pretty v1.2.0 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/vishvananda/netns v0.0.4 // indirect + go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.24.0 // indirect golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/tools v0.31.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/protobuf v1.34.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect diff --git a/go.sum b/go.sum index a39d174e..a65c3033 100644 --- a/go.sum +++ b/go.sum @@ -14,7 +14,6 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -50,10 +49,11 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20241206021119-61a79c692802 h1:US08AXzP0bLurpzFUV3Poa9ZijrRdd1zAIOVtoHEiS8= -github.com/google/pprof v0.0.0-20241206021119-61a79c692802/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -72,10 +72,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= -github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= +github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= +github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= @@ -94,6 +94,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -152,6 +154,8 @@ github.com/xtaci/kcp-go/v5 v5.6.13/go.mod h1:75S1AKYYzNUSXIv30h+jPKJYZUwqpfvLshu github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -170,8 +174,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -239,8 +243,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= @@ -261,8 +265,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/hack/run-e2e.sh b/hack/run-e2e.sh index 8009359c..d1b20b52 100755 --- a/hack/run-e2e.sh +++ b/hack/run-e2e.sh @@ -3,10 +3,10 @@ SCRIPT=$(readlink -f "$0") ROOT=$(unset CDPATH && cd "$(dirname "$SCRIPT")/.." && pwd) -ginkgo_command=$(which ginkgo 2>/dev/null) -if [ -z "$ginkgo_command" ]; then +# Check if ginkgo is available +if ! command -v ginkgo >/dev/null 2>&1; then echo "ginkgo not found, try to install..." - go install github.com/onsi/ginkgo/v2/ginkgo@v2.17.1 + go install github.com/onsi/ginkgo/v2/ginkgo@v2.23.4 fi debug=false diff --git a/pkg/config/legacy/client.go b/pkg/config/legacy/client.go index 0d677d9c..8cc02614 100644 --- a/pkg/config/legacy/client.go +++ b/pkg/config/legacy/client.go @@ -194,7 +194,7 @@ func UnmarshalClientConfFromIni(source any) (ClientCommonConf, error) { } common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_") - common.ClientConfig.OidcAdditionalEndpointParams = GetMapWithoutPrefix(s.KeysHash(), "oidc_additional_") + common.OidcAdditionalEndpointParams = GetMapWithoutPrefix(s.KeysHash(), "oidc_additional_") return common, nil } @@ -229,10 +229,7 @@ func LoadAllProxyConfsFromIni( startProxy[s] = struct{}{} } - startAll := true - if len(startProxy) > 0 { - startAll = false - } + startAll := len(startProxy) == 0 // Build template sections from range section And append to ini.File. rangeSections := make([]*ini.Section, 0) diff --git a/pkg/config/legacy/conversion.go b/pkg/config/legacy/conversion.go index dd8c4a11..4ae54f88 100644 --- a/pkg/config/legacy/conversion.go +++ b/pkg/config/legacy/conversion.go @@ -26,20 +26,20 @@ import ( func Convert_ClientCommonConf_To_v1(conf *ClientCommonConf) *v1.ClientCommonConfig { out := &v1.ClientCommonConfig{} out.User = conf.User - out.Auth.Method = v1.AuthMethod(conf.ClientConfig.AuthenticationMethod) - out.Auth.Token = conf.ClientConfig.Token - if conf.ClientConfig.AuthenticateHeartBeats { + out.Auth.Method = v1.AuthMethod(conf.AuthenticationMethod) + out.Auth.Token = conf.Token + if conf.AuthenticateHeartBeats { out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeHeartBeats) } - if conf.ClientConfig.AuthenticateNewWorkConns { + if conf.AuthenticateNewWorkConns { out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeNewWorkConns) } - out.Auth.OIDC.ClientID = conf.ClientConfig.OidcClientID - out.Auth.OIDC.ClientSecret = conf.ClientConfig.OidcClientSecret - out.Auth.OIDC.Audience = conf.ClientConfig.OidcAudience - out.Auth.OIDC.Scope = conf.ClientConfig.OidcScope - out.Auth.OIDC.TokenEndpointURL = conf.ClientConfig.OidcTokenEndpointURL - out.Auth.OIDC.AdditionalEndpointParams = conf.ClientConfig.OidcAdditionalEndpointParams + out.Auth.OIDC.ClientID = conf.OidcClientID + out.Auth.OIDC.ClientSecret = conf.OidcClientSecret + out.Auth.OIDC.Audience = conf.OidcAudience + out.Auth.OIDC.Scope = conf.OidcScope + out.Auth.OIDC.TokenEndpointURL = conf.OidcTokenEndpointURL + out.Auth.OIDC.AdditionalEndpointParams = conf.OidcAdditionalEndpointParams out.ServerAddr = conf.ServerAddr out.ServerPort = conf.ServerPort @@ -59,10 +59,10 @@ func Convert_ClientCommonConf_To_v1(conf *ClientCommonConf) *v1.ClientCommonConf out.Transport.QUIC.MaxIncomingStreams = conf.QUICMaxIncomingStreams out.Transport.TLS.Enable = lo.ToPtr(conf.TLSEnable) out.Transport.TLS.DisableCustomTLSFirstByte = lo.ToPtr(conf.DisableCustomTLSFirstByte) - out.Transport.TLS.TLSConfig.CertFile = conf.TLSCertFile - out.Transport.TLS.TLSConfig.KeyFile = conf.TLSKeyFile - out.Transport.TLS.TLSConfig.TrustedCaFile = conf.TLSTrustedCaFile - out.Transport.TLS.TLSConfig.ServerName = conf.TLSServerName + out.Transport.TLS.CertFile = conf.TLSCertFile + out.Transport.TLS.KeyFile = conf.TLSKeyFile + out.Transport.TLS.TrustedCaFile = conf.TLSTrustedCaFile + out.Transport.TLS.ServerName = conf.TLSServerName out.Log.To = conf.LogFile out.Log.Level = conf.LogLevel @@ -87,18 +87,18 @@ func Convert_ClientCommonConf_To_v1(conf *ClientCommonConf) *v1.ClientCommonConf func Convert_ServerCommonConf_To_v1(conf *ServerCommonConf) *v1.ServerConfig { out := &v1.ServerConfig{} - out.Auth.Method = v1.AuthMethod(conf.ServerConfig.AuthenticationMethod) - out.Auth.Token = conf.ServerConfig.Token - if conf.ServerConfig.AuthenticateHeartBeats { + out.Auth.Method = v1.AuthMethod(conf.AuthenticationMethod) + out.Auth.Token = conf.Token + if conf.AuthenticateHeartBeats { out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeHeartBeats) } - if conf.ServerConfig.AuthenticateNewWorkConns { + if conf.AuthenticateNewWorkConns { out.Auth.AdditionalScopes = append(out.Auth.AdditionalScopes, v1.AuthScopeNewWorkConns) } - out.Auth.OIDC.Audience = conf.ServerConfig.OidcAudience - out.Auth.OIDC.Issuer = conf.ServerConfig.OidcIssuer - out.Auth.OIDC.SkipExpiryCheck = conf.ServerConfig.OidcSkipExpiryCheck - out.Auth.OIDC.SkipIssuerCheck = conf.ServerConfig.OidcSkipIssuerCheck + out.Auth.OIDC.Audience = conf.OidcAudience + out.Auth.OIDC.Issuer = conf.OidcIssuer + out.Auth.OIDC.SkipExpiryCheck = conf.OidcSkipExpiryCheck + out.Auth.OIDC.SkipIssuerCheck = conf.OidcSkipIssuerCheck out.BindAddr = conf.BindAddr out.BindPort = conf.BindPort diff --git a/pkg/config/legacy/proxy.go b/pkg/config/legacy/proxy.go index e6653ee9..0c461a1a 100644 --- a/pkg/config/legacy/proxy.go +++ b/pkg/config/legacy/proxy.go @@ -206,7 +206,7 @@ func (cfg *BaseProxyConf) decorate(_ string, name string, section *ini.Section) } // plugin_xxx - cfg.LocalSvrConf.PluginParams = GetMapByPrefix(section.KeysHash(), "plugin_") + cfg.PluginParams = GetMapByPrefix(section.KeysHash(), "plugin_") return nil } diff --git a/pkg/config/v1/proxy.go b/pkg/config/v1/proxy.go index d53d05e3..34bd7125 100644 --- a/pkg/config/v1/proxy.go +++ b/pkg/config/v1/proxy.go @@ -129,7 +129,7 @@ func (c *ProxyBaseConfig) Complete(namePrefix string) { c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient) if c.Plugin.ClientPluginOptions != nil { - c.Plugin.ClientPluginOptions.Complete() + c.Plugin.Complete() } } diff --git a/pkg/metrics/mem/server.go b/pkg/metrics/mem/server.go index d92546c4..70cfc1c1 100644 --- a/pkg/metrics/mem/server.go +++ b/pkg/metrics/mem/server.go @@ -109,7 +109,7 @@ func (m *serverMetrics) NewProxy(name string, proxyType string) { m.info.ProxyTypeCounts[proxyType] = counter proxyStats, ok := m.info.ProxyStatistics[name] - if !(ok && proxyStats.ProxyType == proxyType) { + if !ok || proxyStats.ProxyType != proxyType { proxyStats = &ProxyStatistics{ Name: name, ProxyType: proxyType, diff --git a/pkg/util/net/conn.go b/pkg/util/net/conn.go index 20ac73ed..ff7d1c37 100644 --- a/pkg/util/net/conn.go +++ b/pkg/util/net/conn.go @@ -223,7 +223,7 @@ func (conn *wrapQuicStream) RemoteAddr() net.Addr { } func (conn *wrapQuicStream) Close() error { - conn.Stream.CancelRead(0) + conn.CancelRead(0) return conn.Stream.Close() } diff --git a/pkg/util/vhost/http.go b/pkg/util/vhost/http.go index 46ebc3c7..05ec174b 100644 --- a/pkg/util/vhost/http.go +++ b/pkg/util/vhost/http.go @@ -225,11 +225,7 @@ func (rp *HTTPReverseProxy) getVhost(domain, location, routeByHTTPUser string) ( // *.example.com // *.com domainSplit := strings.Split(domain, ".") - for { - if len(domainSplit) < 3 { - break - } - + for len(domainSplit) >= 3 { domainSplit[0] = "*" domain = strings.Join(domainSplit, ".") vr, ok = findRouter(domain, location, routeByHTTPUser) diff --git a/pkg/util/vhost/vhost.go b/pkg/util/vhost/vhost.go index 66dd577d..007751d7 100644 --- a/pkg/util/vhost/vhost.go +++ b/pkg/util/vhost/vhost.go @@ -169,11 +169,7 @@ func (v *Muxer) getListener(name, path, httpUser string) (*Listener, bool) { } domainSplit := strings.Split(name, ".") - for { - if len(domainSplit) < 3 { - break - } - + for len(domainSplit) >= 3 { domainSplit[0] = "*" name = strings.Join(domainSplit, ".") diff --git a/test/e2e/legacy/basic/client_server.go b/test/e2e/legacy/basic/client_server.go index 03bca0c5..d85b5acc 100644 --- a/test/e2e/legacy/basic/client_server.go +++ b/test/e2e/legacy/basic/client_server.go @@ -24,12 +24,14 @@ type generalTestConfigures struct { } func renderBindPortConfig(protocol string) string { - if protocol == "kcp" { + switch protocol { + case "kcp": return fmt.Sprintf(`kcp_bind_port = {{ .%s }}`, consts.PortServerName) - } else if protocol == "quic" { + case "quic": return fmt.Sprintf(`quic_bind_port = {{ .%s }}`, consts.PortServerName) + default: + return "" } - return "" } func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) { diff --git a/test/e2e/legacy/plugin/server.go b/test/e2e/legacy/plugin/server.go index cf600be2..9120b7d7 100644 --- a/test/e2e/legacy/plugin/server.go +++ b/test/e2e/legacy/plugin/server.go @@ -223,7 +223,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() { handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.PingContent) - record = content.Ping.PrivilegeKey + record = content.PrivilegeKey ret.Unchange = true return &ret } @@ -273,7 +273,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() { handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewWorkConnContent) - record = content.NewWorkConn.RunID + record = content.RunID ret.Unchange = true return &ret } diff --git a/test/e2e/v1/basic/client_server.go b/test/e2e/v1/basic/client_server.go index 16270781..85dd1227 100644 --- a/test/e2e/v1/basic/client_server.go +++ b/test/e2e/v1/basic/client_server.go @@ -24,12 +24,14 @@ type generalTestConfigures struct { } func renderBindPortConfig(protocol string) string { - if protocol == "kcp" { + switch protocol { + case "kcp": return fmt.Sprintf(`kcpBindPort = {{ .%s }}`, consts.PortServerName) - } else if protocol == "quic" { + case "quic": return fmt.Sprintf(`quicBindPort = {{ .%s }}`, consts.PortServerName) + default: + return "" } - return "" } func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) { diff --git a/test/e2e/v1/plugin/server.go b/test/e2e/v1/plugin/server.go index b043c57f..6637650d 100644 --- a/test/e2e/v1/plugin/server.go +++ b/test/e2e/v1/plugin/server.go @@ -232,7 +232,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() { handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.PingContent) - record = content.Ping.PrivilegeKey + record = content.PrivilegeKey ret.Unchange = true return &ret } @@ -284,7 +284,7 @@ var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() { handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewWorkConnContent) - record = content.NewWorkConn.RunID + record = content.RunID ret.Unchange = true return &ret } From c777891f7589e3491f6e5c375b91fd637680aa04 Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 25 Jun 2025 11:16:48 +0800 Subject: [PATCH 7/7] update .golangci.yml (#4848) --- .gitignore | 3 +++ .golangci.yml | 1 + 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 0f69b089..c6480f59 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ client.key # Cache *.swp + +# AI +CLAUDE.md diff --git a/.golangci.yml b/.golangci.yml index be0a717e..09848bc7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,6 +1,7 @@ version: "2" run: concurrency: 4 + timeout: 20m build-tags: - integ - integfuzz