Skip to content

Commit 4bbec09

Browse files
authored
Merge pull request #4496 from fatedier/dev
bump version
2 parents ccfe8c9 + f7a06cb commit 4bbec09

File tree

19 files changed

+125
-51
lines changed

19 files changed

+125
-51
lines changed

.github/workflows/golangci-lint.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ jobs:
1717
- uses: actions/checkout@v4
1818
- uses: actions/setup-go@v5
1919
with:
20-
go-version: '1.22'
20+
go-version: '1.23'
2121
cache: false
2222
- name: golangci-lint
2323
uses: golangci/golangci-lint-action@v4
2424
with:
2525
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
26-
version: v1.57
26+
version: v1.61
2727

2828
# Optional: golangci-lint command line arguments.
2929
# args: --issues-exit-code=0

.github/workflows/goreleaser.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Set up Go
1616
uses: actions/setup-go@v5
1717
with:
18-
go-version: '1.22'
18+
go-version: '1.23'
1919

2020
- name: Make All
2121
run: |

.golangci.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
service:
2-
golangci-lint-version: 1.57.x # use the fixed version to not introduce new linters unexpectedly
2+
golangci-lint-version: 1.61.x # use the fixed version to not introduce new linters unexpectedly
33

44
run:
55
concurrency: 4
@@ -14,7 +14,7 @@ linters:
1414
enable:
1515
- unused
1616
- errcheck
17-
- exportloopref
17+
- copyloopvar
1818
- gocritic
1919
- gofumpt
2020
- goimports
@@ -90,6 +90,7 @@ linters-settings:
9090
- G402
9191
- G404
9292
- G501
93+
- G115 # integer overflow conversion
9394

9495
issues:
9596
# List of regexps of issue texts to exclude, empty list by default.

README_zh.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
9595

9696
您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。
9797

98-
国内用户可以通过 [爱发电](https://afdian.net/a/fatedier) 赞助我们。
98+
国内用户可以通过 [爱发电](https://afdian.com/a/fatedier) 赞助我们。
9999

100100
企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。
101101

Release.md

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
### Features
22

3-
* Added a new plugin `tls2raw`: Enables TLS termination and forwarding of decrypted raw traffic to local service.
4-
* Added a default timeout of 30 seconds for the frpc subcommands to prevent commands from being stuck for a long time due to network issues.
5-
6-
### Fixes
7-
8-
* Fixed the issue that when `loginFailExit = false`, the frpc stop command cannot be stopped correctly if the server is not successfully connected after startup.
3+
* The frpc visitor command-line parameter adds the `--server-user` option to specify the username of the server-side proxy to connect to.
4+
* Support multiple frpc instances with different subjects when using oidc authentication.

client/control.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func (ctl *Control) registerMsgHandlers() {
230230
ctl.msgDispatcher.RegisterHandler(&msg.Pong{}, ctl.handlePong)
231231
}
232232

233-
// headerWorker sends heartbeat to server and check heartbeat timeout.
233+
// heartbeatWorker sends heartbeat to server and check heartbeat timeout.
234234
func (ctl *Control) heartbeatWorker() {
235235
xl := ctl.xl
236236

client/proxy/proxy_wrapper.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ func (pw *Wrapper) SetRunningStatus(remoteAddr string, respErr string) error {
137137
pw.Phase = ProxyPhaseStartErr
138138
pw.Err = respErr
139139
pw.lastStartErr = time.Now()
140-
return fmt.Errorf(pw.Err)
140+
return fmt.Errorf("%s", pw.Err)
141141
}
142142

143143
if err := pw.pxy.Run(); err != nil {

conf/frpc_full_example.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ requestHeaders.set.x-from-where = "frp"
327327

328328
[[proxies]]
329329
name = "plugin_tls2raw"
330-
type = "https"
330+
type = "tcp"
331331
remotePort = 6008
332332
[proxies.plugin]
333333
type = "tls2raw"

dockerfiles/Dockerfile-for-frpc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.22 AS building
1+
FROM golang:1.23 AS building
22

33
COPY . /building
44
WORKDIR /building

dockerfiles/Dockerfile-for-frps

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.22 AS building
1+
FROM golang:1.23 AS building
22

33
COPY . /building
44
WORKDIR /building

pkg/auth/auth.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ func NewAuthVerifier(cfg v1.AuthServerConfig) (authVerifier Verifier) {
5050
case v1.AuthMethodToken:
5151
authVerifier = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
5252
case v1.AuthMethodOIDC:
53-
authVerifier = NewOidcAuthVerifier(cfg.AdditionalScopes, cfg.OIDC)
53+
tokenVerifier := NewTokenVerifier(cfg.OIDC)
54+
authVerifier = NewOidcAuthVerifier(cfg.AdditionalScopes, tokenVerifier)
5455
}
5556
return authVerifier
5657
}

pkg/auth/oidc.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,18 @@ func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (e
8787
return err
8888
}
8989

90+
type TokenVerifier interface {
91+
Verify(context.Context, string) (*oidc.IDToken, error)
92+
}
93+
9094
type OidcAuthConsumer struct {
9195
additionalAuthScopes []v1.AuthScope
9296

93-
verifier *oidc.IDTokenVerifier
94-
subjectFromLogin string
97+
verifier TokenVerifier
98+
subjectsFromLogin []string
9599
}
96100

97-
func NewOidcAuthVerifier(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCServerConfig) *OidcAuthConsumer {
101+
func NewTokenVerifier(cfg v1.AuthOIDCServerConfig) TokenVerifier {
98102
provider, err := oidc.NewProvider(context.Background(), cfg.Issuer)
99103
if err != nil {
100104
panic(err)
@@ -105,9 +109,14 @@ func NewOidcAuthVerifier(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCSer
105109
SkipExpiryCheck: cfg.SkipExpiryCheck,
106110
SkipIssuerCheck: cfg.SkipIssuerCheck,
107111
}
112+
return provider.Verifier(&verifierConf)
113+
}
114+
115+
func NewOidcAuthVerifier(additionalAuthScopes []v1.AuthScope, verifier TokenVerifier) *OidcAuthConsumer {
108116
return &OidcAuthConsumer{
109117
additionalAuthScopes: additionalAuthScopes,
110-
verifier: provider.Verifier(&verifierConf),
118+
verifier: verifier,
119+
subjectsFromLogin: []string{},
111120
}
112121
}
113122

@@ -116,7 +125,9 @@ func (auth *OidcAuthConsumer) VerifyLogin(loginMsg *msg.Login) (err error) {
116125
if err != nil {
117126
return fmt.Errorf("invalid OIDC token in login: %v", err)
118127
}
119-
auth.subjectFromLogin = token.Subject
128+
if !slices.Contains(auth.subjectsFromLogin, token.Subject) {
129+
auth.subjectsFromLogin = append(auth.subjectsFromLogin, token.Subject)
130+
}
120131
return nil
121132
}
122133

@@ -125,11 +136,11 @@ func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err err
125136
if err != nil {
126137
return fmt.Errorf("invalid OIDC token in ping: %v", err)
127138
}
128-
if token.Subject != auth.subjectFromLogin {
139+
if !slices.Contains(auth.subjectsFromLogin, token.Subject) {
129140
return fmt.Errorf("received different OIDC subject in login and ping. "+
130-
"original subject: %s, "+
141+
"original subjects: %s, "+
131142
"new subject: %s",
132-
auth.subjectFromLogin, token.Subject)
143+
auth.subjectsFromLogin, token.Subject)
133144
}
134145
return nil
135146
}

pkg/auth/oidc_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package auth_test
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/coreos/go-oidc/v3/oidc"
9+
"github.com/stretchr/testify/require"
10+
11+
"github.com/fatedier/frp/pkg/auth"
12+
v1 "github.com/fatedier/frp/pkg/config/v1"
13+
"github.com/fatedier/frp/pkg/msg"
14+
)
15+
16+
type mockTokenVerifier struct{}
17+
18+
func (m *mockTokenVerifier) Verify(ctx context.Context, subject string) (*oidc.IDToken, error) {
19+
return &oidc.IDToken{
20+
Subject: subject,
21+
}, nil
22+
}
23+
24+
func TestPingWithEmptySubjectFromLoginFails(t *testing.T) {
25+
r := require.New(t)
26+
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{})
27+
err := consumer.VerifyPing(&msg.Ping{
28+
PrivilegeKey: "ping-without-login",
29+
Timestamp: time.Now().UnixMilli(),
30+
})
31+
r.Error(err)
32+
r.Contains(err.Error(), "received different OIDC subject in login and ping")
33+
}
34+
35+
func TestPingAfterLoginWithNewSubjectSucceeds(t *testing.T) {
36+
r := require.New(t)
37+
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{})
38+
err := consumer.VerifyLogin(&msg.Login{
39+
PrivilegeKey: "ping-after-login",
40+
})
41+
r.NoError(err)
42+
43+
err = consumer.VerifyPing(&msg.Ping{
44+
PrivilegeKey: "ping-after-login",
45+
Timestamp: time.Now().UnixMilli(),
46+
})
47+
r.NoError(err)
48+
}
49+
50+
func TestPingAfterLoginWithDifferentSubjectFails(t *testing.T) {
51+
r := require.New(t)
52+
consumer := auth.NewOidcAuthVerifier([]v1.AuthScope{v1.AuthScopeHeartBeats}, &mockTokenVerifier{})
53+
err := consumer.VerifyLogin(&msg.Login{
54+
PrivilegeKey: "login-with-first-subject",
55+
})
56+
r.NoError(err)
57+
58+
err = consumer.VerifyPing(&msg.Ping{
59+
PrivilegeKey: "ping-with-different-subject",
60+
Timestamp: time.Now().UnixMilli(),
61+
})
62+
r.Error(err)
63+
r.Contains(err.Error(), "received different OIDC subject in login and ping")
64+
}

pkg/config/flags.go

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func registerVisitorBaseConfigFlags(cmd *cobra.Command, c *v1.VisitorBaseConfig,
140140
cmd.Flags().BoolVarP(&c.Transport.UseCompression, "uc", "", false, "use compression")
141141
cmd.Flags().StringVarP(&c.SecretKey, "sk", "", "", "secret key")
142142
cmd.Flags().StringVarP(&c.ServerName, "server_name", "", "", "server name")
143+
cmd.Flags().StringVarP(&c.ServerUser, "server-user", "", "", "server user")
143144
cmd.Flags().StringVarP(&c.BindAddr, "bind_addr", "", "", "bind addr")
144145
cmd.Flags().IntVarP(&c.BindPort, "bind_port", "", 0, "bind port")
145146
}

pkg/config/types/types.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -159,18 +159,18 @@ func NewPortsRangeSliceFromString(str string) ([]PortsRange, error) {
159159
out = append(out, PortsRange{Single: int(singleNum)})
160160
case 2:
161161
// range numbers
162-
min, err := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
162+
minNum, err := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
163163
if err != nil {
164164
return nil, fmt.Errorf("range number is invalid, %v", err)
165165
}
166-
max, err := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
166+
maxNum, err := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
167167
if err != nil {
168168
return nil, fmt.Errorf("range number is invalid, %v", err)
169169
}
170-
if max < min {
170+
if maxNum < minNum {
171171
return nil, fmt.Errorf("range number is invalid")
172172
}
173-
out = append(out, PortsRange{Start: int(min), End: int(max)})
173+
out = append(out, PortsRange{Start: int(minNum), End: int(maxNum)})
174174
default:
175175
return nil, fmt.Errorf("range number is invalid")
176176
}

pkg/nathole/utils.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,19 @@ func ListAllLocalIPs() ([]net.IP, error) {
7878
return ips, nil
7979
}
8080

81-
func ListLocalIPsForNatHole(max int) ([]string, error) {
82-
if max <= 0 {
83-
return nil, fmt.Errorf("max must be greater than 0")
81+
func ListLocalIPsForNatHole(maxItems int) ([]string, error) {
82+
if maxItems <= 0 {
83+
return nil, fmt.Errorf("maxItems must be greater than 0")
8484
}
8585

8686
ips, err := ListAllLocalIPs()
8787
if err != nil {
8888
return nil, err
8989
}
9090

91-
filtered := make([]string, 0, max)
91+
filtered := make([]string, 0, maxItems)
9292
for _, ip := range ips {
93-
if len(filtered) >= max {
93+
if len(filtered) >= maxItems {
9494
break
9595
}
9696

pkg/util/util/util.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -85,21 +85,21 @@ func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) {
8585
numbers = append(numbers, singleNum)
8686
case 2:
8787
// range numbers
88-
min, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
88+
minValue, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
8989
if errRet != nil {
9090
err = fmt.Errorf("range number is invalid, %v", errRet)
9191
return
9292
}
93-
max, errRet := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
93+
maxValue, errRet := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
9494
if errRet != nil {
9595
err = fmt.Errorf("range number is invalid, %v", errRet)
9696
return
9797
}
98-
if max < min {
98+
if maxValue < minValue {
9999
err = fmt.Errorf("range number is invalid")
100100
return
101101
}
102-
for i := min; i <= max; i++ {
102+
for i := minValue; i <= maxValue; i++ {
103103
numbers = append(numbers, i)
104104
}
105105
default:
@@ -118,13 +118,13 @@ func GenerateResponseErrorString(summary string, err error, detailed bool) strin
118118
}
119119

120120
func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Duration {
121-
min := int64(minRatio * 1000.0)
122-
max := int64(maxRatio * 1000.0)
121+
minValue := int64(minRatio * 1000.0)
122+
maxValue := int64(maxRatio * 1000.0)
123123
var n int64
124-
if max <= min {
125-
n = min
124+
if maxValue <= minValue {
125+
n = minValue
126126
} else {
127-
n = mathrand.Int64N(max-min) + min
127+
n = mathrand.Int64N(maxValue-minValue) + minValue
128128
}
129129
d := duration * time.Duration(n) / time.Duration(1000)
130130
time.Sleep(d)

pkg/util/version/version.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
package version
1616

17-
var version = "0.60.0"
17+
var version = "0.61.0"
1818

1919
func Full() string {
2020
return version

server/proxy/proxy.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,17 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn,
137137
dstAddr string
138138
srcPortStr string
139139
dstPortStr string
140-
srcPort int
141-
dstPort int
140+
srcPort uint64
141+
dstPort uint64
142142
)
143143

144144
if src != nil {
145145
srcAddr, srcPortStr, _ = net.SplitHostPort(src.String())
146-
srcPort, _ = strconv.Atoi(srcPortStr)
146+
srcPort, _ = strconv.ParseUint(srcPortStr, 10, 16)
147147
}
148148
if dst != nil {
149149
dstAddr, dstPortStr, _ = net.SplitHostPort(dst.String())
150-
dstPort, _ = strconv.Atoi(dstPortStr)
150+
dstPort, _ = strconv.ParseUint(dstPortStr, 10, 16)
151151
}
152152
err := msg.WriteMsg(workConn, &msg.StartWorkConn{
153153
ProxyName: pxy.GetName(),
@@ -190,8 +190,8 @@ func (pxy *BaseProxy) startCommonTCPListenersHandler() {
190190
} else {
191191
tempDelay *= 2
192192
}
193-
if max := 1 * time.Second; tempDelay > max {
194-
tempDelay = max
193+
if maxTime := 1 * time.Second; tempDelay > maxTime {
194+
tempDelay = maxTime
195195
}
196196
xl.Infof("met temporary error: %s, sleep for %s ...", err, tempDelay)
197197
time.Sleep(tempDelay)

0 commit comments

Comments
 (0)