[GH-ISSUE #2922] [Feature Request] Access FRP groups by username #2331

Closed
opened 2026-05-05 13:30:15 -06:00 by gitea-mirror · 5 comments
Owner

Originally created by @roeeklinger on GitHub (May 1, 2022).
Original GitHub issue: https://github.com/fatedier/frp/issues/2922

Describe the feature request

In FRP, I can define TCP load balancing groups as follows:

# frpc.ini
[group1_memeber1]
type = tcp
local_port = 8080
remote_port = 80
group = web1
group_key = 123

[group1_memeber2]
type = tcp
local_port = 8080
remote_port = 80
group = web1
group_key = 123

[group2_memeber1]
type = tcp
local_port = 8080
remote_port = 90
group = web2
group_key = 456

[group2_memeber2]
type = tcp
local_port = 8080
remote_port = 90
group = web2
group_key = 456

This way when I make a request to FRPS on port 80, it will load balance to memebers in group1, and if I make a request on port 90, it will load balance to members on group2.

However, I have a need for many groups, a few hundred, and this forces me to open a few hundred ports on my FRPS, which can be problematic in many cases.
For security and scalability, it would be better if we can accept the traffic on FRPS on a single port, and then select the group based on an HTTP username, or some other similar parameter.

For example, I can configure FRPC members as such:

# frpc.ini
[group1_memeber1]
type = tcp
local_port = 8080
remote_port = 80
group = web1
group_key = 123
username = my_username1

[group1_memeber2]
type = tcp
local_port = 8080
remote_port = 80
group = web1
group_key = 123
username = my_username1

[group2_memeber1]
type = tcp
local_port = 8080
remote_port = 80
group = web2
group_key = 456
username = my_username2

[group2_memeber2]
type = tcp
local_port = 8080
remote_port = 80
group = web2
group_key = 456
username = my_username2

This way, I can simply run this to get to group1:
curl -x 'http://my_username1:my_password@url-of-frps:80' 'https://ipinfo.io'

or run this to get to group2:
curl -x 'http://my_username2:my_password@url-of-frps:80' 'https://ipinfo.io'

This would allow us to select any group needed, using only one port.

As far as I am aware, this is currently impossible using existing configurations, please correct me if I am wrong.

Describe alternatives you've considered

It might be possible to use a Squid proxy server before connecting to FRPS, Squid will add an outgoing TOS or NFMARK to the outgoing request, which will go to FRPS. Then, on FRPS side, we can use the TOS or NFMARK and some routing rules to route the request to the right port on FRPS.

This is a hackish solution that requires a lot of work on 2 separate services and is not elegant, furthermore, it forces us to use Squid proxy before the FRPS installation.

Affected area

  • Docs
  • Installation
  • Performance and Scalability
  • Security
  • User Experience
  • Test and Release
  • Developer Infrastructure
  • Client Plugin
  • Server Plugin
  • Extensions
  • Others
Originally created by @roeeklinger on GitHub (May 1, 2022). Original GitHub issue: https://github.com/fatedier/frp/issues/2922 ### Describe the feature request In FRP, I can define TCP load balancing groups as follows: ``` # frpc.ini [group1_memeber1] type = tcp local_port = 8080 remote_port = 80 group = web1 group_key = 123 [group1_memeber2] type = tcp local_port = 8080 remote_port = 80 group = web1 group_key = 123 [group2_memeber1] type = tcp local_port = 8080 remote_port = 90 group = web2 group_key = 456 [group2_memeber2] type = tcp local_port = 8080 remote_port = 90 group = web2 group_key = 456 ``` This way when I make a request to FRPS on port 80, it will load balance to memebers in group1, and if I make a request on port 90, it will load balance to members on group2. However, I have a need for many groups, a few hundred, and this forces me to open a few hundred ports on my FRPS, which can be problematic in many cases. For security and scalability, it would be better if we can accept the traffic on FRPS on a single port, and then select the group based on an HTTP username, or some other similar parameter. For example, I can configure FRPC members as such: ``` # frpc.ini [group1_memeber1] type = tcp local_port = 8080 remote_port = 80 group = web1 group_key = 123 username = my_username1 [group1_memeber2] type = tcp local_port = 8080 remote_port = 80 group = web1 group_key = 123 username = my_username1 [group2_memeber1] type = tcp local_port = 8080 remote_port = 80 group = web2 group_key = 456 username = my_username2 [group2_memeber2] type = tcp local_port = 8080 remote_port = 80 group = web2 group_key = 456 username = my_username2 ``` This way, I can simply run this to get to group1: `curl -x 'http://my_username1:my_password@url-of-frps:80' 'https://ipinfo.io'` or run this to get to group2: `curl -x 'http://my_username2:my_password@url-of-frps:80' 'https://ipinfo.io'` This would allow us to select any group needed, using only one port. As far as I am aware, this is currently impossible using existing configurations, please correct me if I am wrong. ### Describe alternatives you've considered It might be possible to use a Squid proxy server before connecting to FRPS, Squid will add an outgoing TOS or NFMARK to the outgoing request, which will go to FRPS. Then, on FRPS side, we can use the TOS or NFMARK and some routing rules to route the request to the right port on FRPS. This is a hackish solution that requires a lot of work on 2 separate services and is not elegant, furthermore, it forces us to use Squid proxy before the FRPS installation. ### Affected area - [ ] Docs - [ ] Installation - [X] Performance and Scalability - [X] Security - [X] User Experience - [ ] Test and Release - [X] Developer Infrastructure - [X] Client Plugin - [X] Server Plugin - [ ] Extensions - [ ] Others
gitea-mirror 2026-05-05 13:30:15 -06:00
Author
Owner

@fatedier commented on GitHub (May 2, 2022):

You can try tcpmux proxy https://github.com/fatedier/frp#tcp-port-multiplexing

<!-- gh-comment-id:1115254716 --> @fatedier commented on GitHub (May 2, 2022): You can try `tcpmux` proxy https://github.com/fatedier/frp#tcp-port-multiplexing
Author
Owner

@roeeklinger commented on GitHub (May 2, 2022):

@fatedier Thanks, I followed the README file, this is my configuration:

## frps.ini
[common]
bind_port = 7000
tcpmux_httpconnect_port = 1337
output on FRPS:
root@hostname1:~/frp# docker run --network host -v /root/frp/frps.ini:/etc/frp/frps.ini snowdreamtech/frps
2022/05/02 19:27:34 [I] [root.go:200] frps uses config file: /etc/frp/frps.ini
2022/05/02 19:27:34 [I] [service.go:139] tcpmux httpconnect multiplexer listen on 0.0.0.0:1337
2022/05/02 19:27:34 [I] [service.go:194] frps tcp listen on 0.0.0.0:7000
2022/05/02 19:27:34 [I] [root.go:209] frps started successfully
2022/05/02 19:28:47 [I] [service.go:450] [xxxx] client login info: ip [x.x.x.x:34388] version [0.42.0] hostname [] os [linux] arch [amd64]
2022/05/02 19:28:47 [I] [tcpmux.go:46] [xxxx] [proxy1] tcpmux httpconnect multiplexer listens for host [test1]
2022/05/02 19:28:47 [I] [control.go:465] [xxxx] new proxy [proxy1] success
2022/05/02 19:28:47 [I] [tcpmux.go:46] [xxxx] [proxy2] tcpmux httpconnect multiplexer listens for host [test2]
2022/05/02 19:28:47 [I] [control.go:465] [xxxx] new proxy [proxy2] success
## frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000

[proxy1]
type = tcpmux
multiplexer = httpconnect
custom_domains = test1
local_port = 4301

[proxy2]
type = tcpmux
multiplexer = httpconnect
custom_domains = test2
local_port = 8080
output on FRPC:
root@hostname2:~/frp# docker run --network host -v /root/frp/frpc.ini:/etc/frp/frpc.ini snowdreamtech/frpc
2022/05/02 19:28:47 [I] [service.go:349] [xxxxxxx] login to server success, get run id [xxxxxxx], server udp port [0]
2022/05/02 19:28:47 [I] [proxy_manager.go:144] [xxxxxxx] proxy added: [proxy1 proxy2]
2022/05/02 19:28:47 [I] [control.go:181] [xxxxxxx] [proxy1] start proxy success
2022/05/02 19:28:47 [I] [control.go:181] [xxxxxxx] [proxy2] start proxy success

I have a transparent forward Squid proxy server running on port 4301 on frpc.

I make a request like so:

root@hostname3:~# curl -v --header "test1: HTTP/1.1\r\n\r\n" --proxy http://18.134.244.111:1337 ipinfo.io
*   Trying x.x.x.x:1337...
* TCP_NODELAY set
* Connected to x.x.x.x (x.x.x.x) port 1337 (#0)
> GET http://ipinfo.io/ HTTP/1.1
> Host: ipinfo.io
> User-Agent: curl/7.68.0
> Accept: */*
> Proxy-Connection: Keep-Alive
> test1: HTTP/1.1\r\n\r\n
> 
* Empty reply from server
* Connection #0 to host x.x.x.x left intact
curl: (52) Empty reply from server

but no matter what I do I am getting an empty reply from the server, and FRPS and FRPC show no new log output at all, am I using it wrong?

<!-- gh-comment-id:1115292779 --> @roeeklinger commented on GitHub (May 2, 2022): @fatedier Thanks, I followed the README file, this is my configuration: ``` ## frps.ini [common] bind_port = 7000 tcpmux_httpconnect_port = 1337 ``` ``` output on FRPS: root@hostname1:~/frp# docker run --network host -v /root/frp/frps.ini:/etc/frp/frps.ini snowdreamtech/frps 2022/05/02 19:27:34 [I] [root.go:200] frps uses config file: /etc/frp/frps.ini 2022/05/02 19:27:34 [I] [service.go:139] tcpmux httpconnect multiplexer listen on 0.0.0.0:1337 2022/05/02 19:27:34 [I] [service.go:194] frps tcp listen on 0.0.0.0:7000 2022/05/02 19:27:34 [I] [root.go:209] frps started successfully 2022/05/02 19:28:47 [I] [service.go:450] [xxxx] client login info: ip [x.x.x.x:34388] version [0.42.0] hostname [] os [linux] arch [amd64] 2022/05/02 19:28:47 [I] [tcpmux.go:46] [xxxx] [proxy1] tcpmux httpconnect multiplexer listens for host [test1] 2022/05/02 19:28:47 [I] [control.go:465] [xxxx] new proxy [proxy1] success 2022/05/02 19:28:47 [I] [tcpmux.go:46] [xxxx] [proxy2] tcpmux httpconnect multiplexer listens for host [test2] 2022/05/02 19:28:47 [I] [control.go:465] [xxxx] new proxy [proxy2] success ``` ``` ## frpc.ini [common] server_addr = x.x.x.x server_port = 7000 [proxy1] type = tcpmux multiplexer = httpconnect custom_domains = test1 local_port = 4301 [proxy2] type = tcpmux multiplexer = httpconnect custom_domains = test2 local_port = 8080 ``` ``` output on FRPC: root@hostname2:~/frp# docker run --network host -v /root/frp/frpc.ini:/etc/frp/frpc.ini snowdreamtech/frpc 2022/05/02 19:28:47 [I] [service.go:349] [xxxxxxx] login to server success, get run id [xxxxxxx], server udp port [0] 2022/05/02 19:28:47 [I] [proxy_manager.go:144] [xxxxxxx] proxy added: [proxy1 proxy2] 2022/05/02 19:28:47 [I] [control.go:181] [xxxxxxx] [proxy1] start proxy success 2022/05/02 19:28:47 [I] [control.go:181] [xxxxxxx] [proxy2] start proxy success ``` I have a transparent forward `Squid proxy server` running on `port 4301` on `frpc`. I make a request like so: ``` root@hostname3:~# curl -v --header "test1: HTTP/1.1\r\n\r\n" --proxy http://18.134.244.111:1337 ipinfo.io * Trying x.x.x.x:1337... * TCP_NODELAY set * Connected to x.x.x.x (x.x.x.x) port 1337 (#0) > GET http://ipinfo.io/ HTTP/1.1 > Host: ipinfo.io > User-Agent: curl/7.68.0 > Accept: */* > Proxy-Connection: Keep-Alive > test1: HTTP/1.1\r\n\r\n > * Empty reply from server * Connection #0 to host x.x.x.x left intact curl: (52) Empty reply from server ``` but no matter what I do I am getting an empty reply from the server, and FRPS and FRPC show no new log output at all, am I using it wrong?
Author
Owner

@fatedier commented on GitHub (May 3, 2022):

You should send HTTP Connect request. Please search google for more information.

<!-- gh-comment-id:1115557609 --> @fatedier commented on GitHub (May 3, 2022): You should send `HTTP Connect` request. Please search google for more information.
Author
Owner

@roeeklinger commented on GitHub (May 3, 2022):

@fatedier Oh you are right, I missed the part where it has to be CONNECT. Because I did not specify --proxytunnel or https:// it used GET instead, if I specify them it seems to work:

➜  ~ curl -v --proxytunnel --proxy http://x.x.x.x:1337 http://ipinfo.io
*   Trying x.x.x.x:1337...
* Connected to x.x.x.x (x.x.x.x) port 1337 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to ipinfo.io:80
> CONNECT ipinfo.io:80 HTTP/1.1
> Host: ipinfo.io:80
> User-Agent: curl/7.77.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.0 404 Not Found
< Content-Type: text/html
< Server: frp/0.42.0
< 
* Received HTTP code 404 from proxy after CONNECT
* CONNECT phase completed!
* Closing connection 0
curl: (56) Received HTTP code 404 from proxy after CONNECT
➜  ~ curl -v --proxy http://x.x.x.x:1337 https://ipinfo.io 
*   Trying 18.134.244.111:1337...
* Connected to x.x.x.x (x.x.x.x) port 1337 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to ipinfo.io:443
> CONNECT ipinfo.io:443 HTTP/1.1
> Host: ipinfo.io:443
> User-Agent: curl/7.77.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.0 404 Not Found
< Content-Type: text/html
< Server: frp/0.42.0
< 
* Received HTTP code 404 from proxy after CONNECT
* CONNECT phase completed!
* Closing connection 0
curl: (56) Received HTTP code 404 from proxy after CONNECT

However, on the FRPS side I get:
[D] [vhost.go:158] http request for host [ipinfo.io] path [] not found

This seems to happen since the request that is sent is CONNECT ipinfo.io:80 HTTP/1.1 and not CONNECT test1 HTTP/1.1, since if I make a request like this, it does seem to work:

➜  ~ curl -v --proxytunnel --proxy http://x.x.x.x:1337 http://test1.x.x.x.x.com
*   Trying x.x.x.x:1337...
* Connected to x.x.x.x (x.x.x.x) port 1337 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to x.x.x.x:80
> CONNECT test1.x.x.x.x:80 HTTP/1.1
> Host: test1.x.x.x.x:80
> User-Agent: curl/7.77.0
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 OK
< Content-Length: 0
* Ignoring Content-Length in CONNECT 200 response
< 
* Proxy replied 200 to CONNECT request
* CONNECT phase completed!
> GET / HTTP/1.1
> Host: test1.thesocialproxy.com
> User-Agent: curl/7.77.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< Server: squid/4.10
< Mime-Version: 1.0
< Date: Tue, 03 May 2022 09:44:27 GMT
< Content-Type: text/html;charset=utf-8
< Content-Length: 3506
< X-Squid-Error: ERR_INVALID_URL 0
< Vary: Accept-Language
< Content-Language: en
< X-Cache: MISS from x.x.x.x
< X-Cache-Lookup: NONE from x.x.x.x:10000
< Connection: close
< 

output on frps:

2022/05/03 09:44:27 [D] [vhost.go:191] [xxxxxx] [proxy1] get new http request host [x.x.x.x] path []
2022/05/03 09:44:27 [I] [proxy.go:179] [xxxxxx] [proxy1] get a user connection [x.x.x.x:1539]
2022/05/03 09:44:27 [D] [control.go:248] [xxxxxx] get work connection from pool
2022/05/03 09:44:27 [D] [proxy.go:104] [xxxxxx] [proxy1] get a new work connection: [x.x.x.x:41076]
2022/05/03 09:44:27 [T] [proxy.go:279] [xxxxxx] [proxy1] handler user tcp connection, use_encryption: false, use_compression: false
2022/05/03 09:44:27 [D] [proxy.go:290] [xxxxxx] [proxy1] join connections, workConn(l[x.x.x.x:7000] r[185.229.226.82:41076]) userConn(l[x.x.x.x:1337] r[x.x.x.x:1539])
2022/05/03 09:44:27 [D] [control.go:219] [xxxxxx] new work connection registered
2022/05/03 09:44:27 [D] [proxy.go:300] [xxxxxx] [proxy1] join connections closed

However, of course, Squid doesn't know how to handle this request, since the request is for test1.x.x.x.x.com, my question is, is this option only good for web servers or can I somehow use it for upstream proxies as well?

<!-- gh-comment-id:1115917935 --> @roeeklinger commented on GitHub (May 3, 2022): @fatedier Oh you are right, I missed the part where it has to be CONNECT. Because I did not specify --proxytunnel or https:// it used GET instead, if I specify them it seems to work: ``` ➜ ~ curl -v --proxytunnel --proxy http://x.x.x.x:1337 http://ipinfo.io * Trying x.x.x.x:1337... * Connected to x.x.x.x (x.x.x.x) port 1337 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to ipinfo.io:80 > CONNECT ipinfo.io:80 HTTP/1.1 > Host: ipinfo.io:80 > User-Agent: curl/7.77.0 > Proxy-Connection: Keep-Alive > < HTTP/1.0 404 Not Found < Content-Type: text/html < Server: frp/0.42.0 < * Received HTTP code 404 from proxy after CONNECT * CONNECT phase completed! * Closing connection 0 curl: (56) Received HTTP code 404 from proxy after CONNECT ``` ``` ➜ ~ curl -v --proxy http://x.x.x.x:1337 https://ipinfo.io * Trying 18.134.244.111:1337... * Connected to x.x.x.x (x.x.x.x) port 1337 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to ipinfo.io:443 > CONNECT ipinfo.io:443 HTTP/1.1 > Host: ipinfo.io:443 > User-Agent: curl/7.77.0 > Proxy-Connection: Keep-Alive > < HTTP/1.0 404 Not Found < Content-Type: text/html < Server: frp/0.42.0 < * Received HTTP code 404 from proxy after CONNECT * CONNECT phase completed! * Closing connection 0 curl: (56) Received HTTP code 404 from proxy after CONNECT ``` However, on the FRPS side I get: `[D] [vhost.go:158] http request for host [ipinfo.io] path [] not found` This seems to happen since the request that is sent is `CONNECT ipinfo.io:80 HTTP/1.1` and not `CONNECT test1 HTTP/1.1`, since if I make a request like this, it does seem to work: ``` ➜ ~ curl -v --proxytunnel --proxy http://x.x.x.x:1337 http://test1.x.x.x.x.com * Trying x.x.x.x:1337... * Connected to x.x.x.x (x.x.x.x) port 1337 (#0) * allocate connect buffer! * Establish HTTP proxy tunnel to x.x.x.x:80 > CONNECT test1.x.x.x.x:80 HTTP/1.1 > Host: test1.x.x.x.x:80 > User-Agent: curl/7.77.0 > Proxy-Connection: Keep-Alive > < HTTP/1.1 200 OK < Content-Length: 0 * Ignoring Content-Length in CONNECT 200 response < * Proxy replied 200 to CONNECT request * CONNECT phase completed! > GET / HTTP/1.1 > Host: test1.thesocialproxy.com > User-Agent: curl/7.77.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 400 Bad Request < Server: squid/4.10 < Mime-Version: 1.0 < Date: Tue, 03 May 2022 09:44:27 GMT < Content-Type: text/html;charset=utf-8 < Content-Length: 3506 < X-Squid-Error: ERR_INVALID_URL 0 < Vary: Accept-Language < Content-Language: en < X-Cache: MISS from x.x.x.x < X-Cache-Lookup: NONE from x.x.x.x:10000 < Connection: close < ``` output on frps: ``` 2022/05/03 09:44:27 [D] [vhost.go:191] [xxxxxx] [proxy1] get new http request host [x.x.x.x] path [] 2022/05/03 09:44:27 [I] [proxy.go:179] [xxxxxx] [proxy1] get a user connection [x.x.x.x:1539] 2022/05/03 09:44:27 [D] [control.go:248] [xxxxxx] get work connection from pool 2022/05/03 09:44:27 [D] [proxy.go:104] [xxxxxx] [proxy1] get a new work connection: [x.x.x.x:41076] 2022/05/03 09:44:27 [T] [proxy.go:279] [xxxxxx] [proxy1] handler user tcp connection, use_encryption: false, use_compression: false 2022/05/03 09:44:27 [D] [proxy.go:290] [xxxxxx] [proxy1] join connections, workConn(l[x.x.x.x:7000] r[185.229.226.82:41076]) userConn(l[x.x.x.x:1337] r[x.x.x.x:1539]) 2022/05/03 09:44:27 [D] [control.go:219] [xxxxxx] new work connection registered 2022/05/03 09:44:27 [D] [proxy.go:300] [xxxxxx] [proxy1] join connections closed ``` However, of course, Squid doesn't know how to handle this request, since the request is for `test1.x.x.x.x.com`, my question is, is this option only good for web servers or can I somehow use it for upstream proxies as well?
Author
Owner

@github-actions[bot] commented on GitHub (Jun 3, 2022):

Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.

<!-- gh-comment-id:1145472277 --> @github-actions[bot] commented on GitHub (Jun 3, 2022): Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/frp#2331
No description provided.