[GH-ISSUE #4184] [Feature Request] Make client IP accessible to upstream by spoofing source IP #3295

Open
opened 2026-05-05 14:07:38 -06:00 by gitea-mirror · 6 comments
Owner

Originally created by @dong-zeyu on GitHub (Apr 27, 2024).
Original GitHub issue: https://github.com/fatedier/frp/issues/4184

Describe the feature request

As a reverse proxy, FRP currently supports two ways for the upstream server to obtain the client's real IP: HTTP X-Forwarded-For and Proxy Protocol. The former only supports HTTP protocol and the latter supports generic TCP connection but would require support for uptream. My current solution for upstreams that do not support proxy protocol is to use mmproxy or go-mmproxy to wrap the bare TCP with the proxy protocol. However, this requires configuring additional programs and upstream ports and also introduces overhead to copy data between the processes.

Describe alternatives you've considered

The underlying technology that mmproxy uses is TPROXY supported by the Linux kernel. This can be easily implemented in Go by a few lines of code (I referred to go-mmproxy)

dialer := net.Dialer{LocalAddr: saddr}
dialer.Control = func(network, address string, c syscall.RawConn) error {
    return c.Control(func(fd uintptr) {
        syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TRANSPARENT, 1)
    })
}

saddr is the remote client address obtained from the FRP server. Then the upstream will be connected as if it is directly connected from the IP address of the remote client.

By doing so, the FRP client would support spoofing the client IP to the upstream out-of-box, without further configuration to the upstream or running additional programs.

However, this functionality still has certain limitations: first, this would only work on the Linux system as far as I know; second, configurations to the Linux firewall/routing is still needed (these configurations are global and do not need to be set separately for each service); third, CAP_NET_ADMIN capability is required to set IP_TRANSPARENT socket opt.

I have a simple PoC for this feature in my fork, but maybe more work is needed to make it into the FRP.

Affected area

  • Docs
  • Installation
  • Performance and Scalability
  • Security
  • User Experience
  • Test and Release
  • Developer Infrastructure
  • Client Plugin
  • Server Plugin
  • Extensions
  • Others
Originally created by @dong-zeyu on GitHub (Apr 27, 2024). Original GitHub issue: https://github.com/fatedier/frp/issues/4184 ### Describe the feature request As a reverse proxy, FRP currently supports two ways for the upstream server to obtain the client's real IP: HTTP X-Forwarded-For and Proxy Protocol. The former only supports HTTP protocol and the latter supports generic TCP connection but would require support for uptream. My current solution for upstreams that do not support proxy protocol is to use [mmproxy](https://github.com/cloudflare/mmproxy) or [go-mmproxy](https://github.com/path-network/go-mmproxy) to wrap the bare TCP with the proxy protocol. However, this requires configuring additional programs and upstream ports and also introduces overhead to copy data between the processes. ### Describe alternatives you've considered The underlying technology that mmproxy uses is [TPROXY](https://www.kernel.org/doc/Documentation/networking/tproxy.txt) supported by the Linux kernel. This can be easily implemented in Go by a few lines of code (I referred to [go-mmproxy](https://github.com/path-network/go-mmproxy)) ```go dialer := net.Dialer{LocalAddr: saddr} dialer.Control = func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TRANSPARENT, 1) }) } ``` `saddr` is the remote client address obtained from the FRP server. Then the upstream will be connected as if it is directly connected from the IP address of the remote client. By doing so, the FRP client would support spoofing the client IP to the upstream out-of-box, without further configuration to the upstream or running additional programs. However, this functionality still has certain limitations: first, this would only work on the Linux system as far as I know; second, [configurations](https://github.com/path-network/go-mmproxy/tree/master?tab=readme-ov-file#routing-setup) to the Linux firewall/routing is still needed (these configurations are global and do not need to be set separately for each service); third, `CAP_NET_ADMIN` capability is required to set `IP_TRANSPARENT` socket opt. I have a simple [PoC](https://github.com/dong-zeyu/frp/tree/tproxy) for this feature in my fork, but maybe more work is needed to make it into the FRP. ### Affected area - [ ] Docs - [ ] Installation - [X] Performance and Scalability - [ ] Security - [ ] User Experience - [ ] Test and Release - [ ] Developer Infrastructure - [ ] Client Plugin - [ ] Server Plugin - [ ] Extensions - [ ] Others
gitea-mirror added the
proposal
label 2026-05-05 14:07:38 -06:00
Author
Owner

@fatedier commented on GitHub (Apr 28, 2024):

However, this functionality still has certain limitations: first, this would only work on the Linux system as far as I know; second, configurations to the Linux firewall/routing is still needed (these configurations are global and do not need to be set separately for each service); third, CAP_NET_ADMIN capability is required to set IP_TRANSPARENT socket opt.

This is one of the reasons we were not too keen on introducing related features before.

Currently, it may be a wiser choice to combine more professional tools.

I hope to see more practical demand scenarios. Changes in this area will only be considered after we have made progress in the refactoring and optimization of our core architecture.

<!-- gh-comment-id:2081299345 --> @fatedier commented on GitHub (Apr 28, 2024): > However, this functionality still has certain limitations: first, this would only work on the Linux system as far as I know; second, [configurations](https://github.com/path-network/go-mmproxy/tree/master?tab=readme-ov-file#routing-setup) to the Linux firewall/routing is still needed (these configurations are global and do not need to be set separately for each service); third, CAP_NET_ADMIN capability is required to set IP_TRANSPARENT socket opt. This is one of the reasons we were not too keen on introducing related features before. Currently, it may be a wiser choice to combine more professional tools. I hope to see more practical demand scenarios. Changes in this area will only be considered after we have made progress in the refactoring and optimization of our core architecture.
Author
Owner

@crabdancing commented on GitHub (Aug 25, 2024):

One reason this feature is potentially important is that the company I work for is interested in localizing the TLS encryption to the backend server, with the less trusted proxy essentially functioning as a dumb pipe.

<!-- gh-comment-id:2308642751 --> @crabdancing commented on GitHub (Aug 25, 2024): One reason this feature is potentially important is that the company I work for is interested in localizing the TLS encryption to the backend server, with the less trusted proxy essentially functioning as a dumb pipe.
Author
Owner

@JustKeonix commented on GitHub (Apr 7, 2025):

Another use case is allowing for client ban/rate limiting/load balancing by source IP for UDP upstreams (voice chat, video conferencing, game servers e.t.c.). Are there any alternative tools having this functionality?

<!-- gh-comment-id:2781841860 --> @JustKeonix commented on GitHub (Apr 7, 2025): Another use case is allowing for client ban/rate limiting/load balancing by source IP for UDP upstreams (voice chat, video conferencing, game servers e.t.c.). Are there any alternative tools having this functionality?
Author
Owner

@fsj2009yx commented on GitHub (Aug 24, 2025):

perhaps it can join as a client plugin,based on go-mmproxy subprocess

<!-- gh-comment-id:3217740499 --> @fsj2009yx commented on GitHub (Aug 24, 2025): perhaps it can join as a client plugin,based on go-mmproxy subprocess
Author
Owner

@fatedier commented on GitHub (Aug 25, 2025):

perhaps it can join as a client plugin,based on go-mmproxy subprocess

This is one of the important directions we are considering for the future — supporting more extensibility. We are unable to devote sufficient effort to maintaining all kinds of complex requirements within a single project.

However, this may require some refactoring of the existing code architecture and design. This is also part of the vision for the next major version, but it will take time — it’s not that simple.

<!-- gh-comment-id:3218632239 --> @fatedier commented on GitHub (Aug 25, 2025): > perhaps it can join as a client plugin,based on go-mmproxy subprocess This is one of the important directions we are considering for the future — supporting more extensibility. We are unable to devote sufficient effort to maintaining all kinds of complex requirements within a single project. However, this may require some refactoring of the existing code architecture and design. This is also part of the vision for the next major version, but it will take time — it’s not that simple.
Author
Owner

@Deng-Xian-Sheng commented on GitHub (Oct 4, 2025):

You should pay the author, like $1000, so that the author has the motivation to write new features during his sleep time, hahahaha

<!-- gh-comment-id:3368081004 --> @Deng-Xian-Sheng commented on GitHub (Oct 4, 2025): You should pay the author, like $1000, so that the author has the motivation to write new features during his sleep time, hahahaha
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#3295
No description provided.