Merge pull request #848 from jemmy1794/feature/Proxy_Protocol_V2

Add support for Proxy Protocol V1 and V2 in streamproxy configuration
This commit is contained in:
Toby Chui
2025-10-15 07:28:44 +08:00
committed by GitHub
7 changed files with 107 additions and 77 deletions

View File

@@ -17,6 +17,7 @@ require (
github.com/likexian/whois v1.15.1 github.com/likexian/whois v1.15.1
github.com/microcosm-cc/bluemonday v1.0.26 github.com/microcosm-cc/bluemonday v1.0.26
github.com/monperrus/crawler-user-agents v1.1.0 github.com/monperrus/crawler-user-agents v1.1.0
github.com/pires/go-proxyproto v0.8.1
github.com/shirou/gopsutil/v4 v4.25.1 github.com/shirou/gopsutil/v4 v4.25.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0 github.com/syndtr/goleveldb v1.0.0

View File

@@ -610,6 +610,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc= github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@@ -47,7 +47,7 @@ func (m *Manager) HandleAddProxyConfig(w http.ResponseWriter, r *http.Request) {
useTCP, _ := utils.PostBool(r, "useTCP") useTCP, _ := utils.PostBool(r, "useTCP")
useUDP, _ := utils.PostBool(r, "useUDP") useUDP, _ := utils.PostBool(r, "useUDP")
useProxyProtocol, _ := utils.PostBool(r, "useProxyProtocol") ProxyProtocolVersion, _ := utils.PostInt(r, "proxyProtocolVersion")
enableLogging, _ := utils.PostBool(r, "enableLogging") enableLogging, _ := utils.PostBool(r, "enableLogging")
//Create the target config //Create the target config
@@ -58,7 +58,7 @@ func (m *Manager) HandleAddProxyConfig(w http.ResponseWriter, r *http.Request) {
Timeout: timeout, Timeout: timeout,
UseTCP: useTCP, UseTCP: useTCP,
UseUDP: useUDP, UseUDP: useUDP,
UseProxyProtocol: useProxyProtocol, ProxyProtocolVersion: ProxyProtocolVersion,
EnableLogging: enableLogging, EnableLogging: enableLogging,
}) })
@@ -79,7 +79,7 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request)
proxyAddr, _ := utils.PostPara(r, "proxyAddr") proxyAddr, _ := utils.PostPara(r, "proxyAddr")
useTCP, _ := utils.PostBool(r, "useTCP") useTCP, _ := utils.PostBool(r, "useTCP")
useUDP, _ := utils.PostBool(r, "useUDP") useUDP, _ := utils.PostBool(r, "useUDP")
useProxyProtocol, _ := utils.PostBool(r, "useProxyProtocol") proxyProtocolVersion, _ := utils.PostInt(r, "proxyProtocolVersion")
enableLogging, _ := utils.PostBool(r, "enableLogging") enableLogging, _ := utils.PostBool(r, "enableLogging")
newTimeoutStr, _ := utils.PostPara(r, "timeout") newTimeoutStr, _ := utils.PostPara(r, "timeout")
@@ -100,7 +100,7 @@ func (m *Manager) HandleEditProxyConfigs(w http.ResponseWriter, r *http.Request)
NewProxyAddr: proxyAddr, NewProxyAddr: proxyAddr,
UseTCP: useTCP, UseTCP: useTCP,
UseUDP: useUDP, UseUDP: useUDP,
UseProxyProtocol: useProxyProtocol, ProxyProtocolVersion: proxyProtocolVersion,
EnableLogging: enableLogging, EnableLogging: enableLogging,
NewTimeout: newTimeout, NewTimeout: newTimeout,
} }

View File

@@ -29,7 +29,7 @@ type ProxyRelayOptions struct {
Timeout int Timeout int
UseTCP bool UseTCP bool
UseUDP bool UseUDP bool
UseProxyProtocol bool ProxyProtocolVersion int
EnableLogging bool EnableLogging bool
} }
@@ -41,7 +41,7 @@ type ProxyRuleUpdateConfig struct {
NewProxyAddr string //New proxy target address, leave empty for no change NewProxyAddr string //New proxy target address, leave empty for no change
UseTCP bool //Enable TCP proxy, default to false UseTCP bool //Enable TCP proxy, default to false
UseUDP bool //Enable UDP proxy, default to false UseUDP bool //Enable UDP proxy, default to false
UseProxyProtocol bool //Enable Proxy Protocol, default to false ProxyProtocolVersion int //Enable Proxy Protocol v1/v2, default to disabled
EnableLogging bool //Enable Logging TCP/UDP Message, default to true EnableLogging bool //Enable Logging TCP/UDP Message, default to true
NewTimeout int //New timeout for the connection, leave -1 for no change NewTimeout int //New timeout for the connection, leave -1 for no change
} }
@@ -56,7 +56,7 @@ type ProxyRelayInstance struct {
ProxyTargetAddr string //Proxy target address ProxyTargetAddr string //Proxy target address
UseTCP bool //Enable TCP proxy UseTCP bool //Enable TCP proxy
UseUDP bool //Enable UDP proxy UseUDP bool //Enable UDP proxy
UseProxyProtocol bool //Enable Proxy Protocol ProxyProtocolVersion int //Proxy Protocol v1/v2
EnableLogging bool //Enable logging for ProxyInstance EnableLogging bool //Enable logging for ProxyInstance
Timeout int //Timeout for connection in sec Timeout int //Timeout for connection in sec
@@ -178,7 +178,7 @@ func (m *Manager) NewConfig(config *ProxyRelayOptions) string {
ProxyTargetAddr: config.ProxyAddr, ProxyTargetAddr: config.ProxyAddr,
UseTCP: config.UseTCP, UseTCP: config.UseTCP,
UseUDP: config.UseUDP, UseUDP: config.UseUDP,
UseProxyProtocol: config.UseProxyProtocol, ProxyProtocolVersion: config.ProxyProtocolVersion,
EnableLogging: config.EnableLogging, EnableLogging: config.EnableLogging,
Timeout: config.Timeout, Timeout: config.Timeout,
tcpStopChan: nil, tcpStopChan: nil,
@@ -224,7 +224,7 @@ func (m *Manager) EditConfig(newConfig *ProxyRuleUpdateConfig) error {
foundConfig.UseTCP = newConfig.UseTCP foundConfig.UseTCP = newConfig.UseTCP
foundConfig.UseUDP = newConfig.UseUDP foundConfig.UseUDP = newConfig.UseUDP
foundConfig.UseProxyProtocol = newConfig.UseProxyProtocol foundConfig.ProxyProtocolVersion = newConfig.ProxyProtocolVersion
foundConfig.EnableLogging = newConfig.EnableLogging foundConfig.EnableLogging = newConfig.EnableLogging
if newConfig.NewTimeout != -1 { if newConfig.NewTimeout != -1 {

View File

@@ -11,6 +11,8 @@ import (
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
proxyproto "github.com/pires/go-proxyproto"
) )
func isValidIP(ip string) bool { func isValidIP(ip string) bool {
@@ -44,20 +46,22 @@ func (c *ProxyRelayInstance) connCopy(conn1 net.Conn, conn2 net.Conn, wg *sync.W
wg.Done() wg.Done()
} }
func writeProxyProtocolHeaderV1(dst net.Conn, src net.Conn) error { func WriteProxyProtocolHeader(dst net.Conn, src net.Conn, version int) error {
clientAddr, ok1 := src.RemoteAddr().(*net.TCPAddr) clientAddr, ok1 := src.RemoteAddr().(*net.TCPAddr)
proxyAddr, ok2 := src.LocalAddr().(*net.TCPAddr) proxyAddr, ok2 := src.LocalAddr().(*net.TCPAddr)
if !ok1 || !ok2 { if !ok1 || !ok2 {
return errors.New("invalid TCP address for proxy protocol") return errors.New("invalid TCP address for proxy protocol")
} }
header := fmt.Sprintf("PROXY TCP4 %s %s %d %d\r\n", header := proxyproto.Header{
clientAddr.IP.String(), Version: byte(version),
proxyAddr.IP.String(), Command: proxyproto.PROXY,
clientAddr.Port, TransportProtocol: proxyproto.TCPv4,
proxyAddr.Port) SourceAddr: clientAddr,
DestinationAddr: proxyAddr,
}
_, err := dst.Write([]byte(header)) _, err := header.WriteTo(dst)
return err return err
} }
@@ -161,9 +165,9 @@ func (c *ProxyRelayInstance) Port2host(allowPort string, targetAddress string, s
} }
c.LogMsg("[→] connect target address ["+targetAddress+"] success.", nil) c.LogMsg("[→] connect target address ["+targetAddress+"] success.", nil)
if c.UseProxyProtocol { if c.ProxyProtocolVersion != 0 {
c.LogMsg("[+] write proxy protocol header to target address ["+targetAddress+"]", nil) c.LogMsg("[+] write proxy protocol header to target address ["+targetAddress+"]", nil)
err = writeProxyProtocolHeaderV1(target, conn) err = WriteProxyProtocolHeader(target, conn, c.ProxyProtocolVersion)
if err != nil { if err != nil {
c.LogMsg("[x] Write proxy protocol header failed: "+err.Error(), nil) c.LogMsg("[x] Write proxy protocol header failed: "+err.Error(), nil)
target.Close() target.Close()

View File

@@ -1,11 +1,14 @@
package streamproxy package streamproxy
import ( import (
"bytes"
"errors" "errors"
"log" "log"
"net" "net"
"strings" "strings"
"time" "time"
proxyproto "github.com/pires/go-proxyproto"
) )
/* /*
@@ -82,6 +85,24 @@ func (c *ProxyRelayInstance) CloseAllUDPConnections() {
}) })
} }
// Write Proxy Protocol v2 header to UDP connection
func WriteProxyProtocolHeaderUDP(conn *net.UDPConn, srcAddr, dstAddr *net.UDPAddr) error {
header := proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.UDPv4,
SourceAddr: srcAddr,
DestinationAddr: dstAddr,
}
var buf bytes.Buffer
_, err := header.WriteTo(&buf)
if err != nil {
return err
}
_, err = conn.Write(buf.Bytes())
return err
}
func (c *ProxyRelayInstance) ForwardUDP(address1, address2 string, stopChan chan bool) error { func (c *ProxyRelayInstance) ForwardUDP(address1, address2 string, stopChan chan bool) error {
//By default the incoming listen Address is int //By default the incoming listen Address is int
//We need to add the loopback address into it //We need to add the loopback address into it
@@ -142,6 +163,10 @@ func (c *ProxyRelayInstance) ForwardUDP(address1, address2 string, stopChan chan
// Fire up routine to manage new connection // Fire up routine to manage new connection
go c.RunUDPConnectionRelay(conn, lisener) go c.RunUDPConnectionRelay(conn, lisener)
// Send Proxy Protocol header if enabled
if c.ProxyProtocolVersion == 2 {
_ = WriteProxyProtocolHeaderUDP(conn.ServerConn, cliaddr, targetAddr)
}
} else { } else {
c.LogMsg("[UDP] Found connection for client "+saddr, nil) c.LogMsg("[UDP] Found connection for client "+saddr, nil)
conn = rawConn.(*udpClientServerConn) conn = rawConn.(*udpClientServerConn)

View File

@@ -74,14 +74,6 @@
<small>Forward UDP request on this listening socket</small></label> <small>Forward UDP request on this listening socket</small></label>
</div> </div>
</div> </div>
<div class="field">
<div class="ui toggle checkbox">
<input type="checkbox" tabindex="0" name="useProxyProtocol" class="hidden">
<label>Enable Proxy Protocol V1<br>
<small>Enable TCP Proxy Protocol header V1</small>
</label>
</div>
</div>
<div class="field"> <div class="field">
<div class="ui toggle checkbox"> <div class="ui toggle checkbox">
<input type="checkbox" tabindex="0" name="enableLogging" class="hidden"> <input type="checkbox" tabindex="0" name="enableLogging" class="hidden">
@@ -90,6 +82,15 @@
</label> </label>
</div> </div>
</div> </div>
<div class="field">
<label>Proxy Protocol</label>
<select name="proxyProtocolVersion" class="ui dropdown">
<option value="0">Disabled</option>
<option value="1">Proxy Protocol V1</option>
<option value="2">Proxy Protocol V2</option>
</select>
<small>Select Proxy Protocol v1 / v2 to use (if any)</small>
</div>
<button id="addStreamProxyButton" class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button> <button id="addStreamProxyButton" class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
<button id="editStreamProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event, this);" style="display:none;"><i class="ui green check icon"></i> Update</button> <button id="editStreamProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event, this);" style="display:none;"><i class="ui green check icon"></i> Update</button>
<button class="ui basic red button" onclick="event.preventDefault(); cancelStreamProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button> <button class="ui basic red button" onclick="event.preventDefault(); cancelStreamProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
@@ -138,7 +139,7 @@
function clearStreamProxyAddEditForm(){ function clearStreamProxyAddEditForm(){
$('#streamProxyForm').find('input:not([type=checkbox]), select').val(''); $('#streamProxyForm').find('input:not([type=checkbox]), select').val('');
$('#streamProxyForm select').dropdown('clear'); $('#streamProxyForm select[name=proxyProtocolVersion]').dropdown('set selected', '0');
$("#streamProxyForm input[name=timeout]").val(10); $("#streamProxyForm input[name=timeout]").val(10);
$("#streamProxyForm .toggle.checkbox").checkbox("set unchecked"); $("#streamProxyForm .toggle.checkbox").checkbox("set unchecked");
} }
@@ -212,8 +213,10 @@
modeText.push("UDP") modeText.push("UDP")
} }
if (config.UseProxyProtocol){ if (config.ProxyProtocolVersion === 1) {
modeText.push("ProxyProtocol V1") modeText.push("ProxyProtocol V1");
} else if (config.ProxyProtocolVersion === 2) {
modeText.push("ProxyProtocol V2");
} }
modeText = modeText.join(" & ") modeText = modeText.join(" & ")
@@ -277,13 +280,8 @@
$(checkboxEle).checkbox("set unchecked"); $(checkboxEle).checkbox("set unchecked");
} }
return; return;
}else if (key == "UseProxyProtocol"){ }else if (key == "ProxyProtocolVersion") {
let checkboxEle = $("#streamProxyForm input[name=useProxyProtocol]").parent(); $("#streamProxyForm select[name=proxyProtocolVersion]").dropdown("set selected", value);
if (value === true){
$(checkboxEle).checkbox("set checked");
}else{
$(checkboxEle).checkbox("set unchecked");
}
return; return;
}else if (key == "EnableLogging"){ }else if (key == "EnableLogging"){
let checkboxEle = $("#streamProxyForm input[name=enableLogging]").parent(); let checkboxEle = $("#streamProxyForm input[name=enableLogging]").parent();
@@ -342,7 +340,7 @@
proxyAddr: $("#streamProxyForm input[name=proxyAddr]").val().trim(), proxyAddr: $("#streamProxyForm input[name=proxyAddr]").val().trim(),
useTCP: $("#streamProxyForm input[name=useTCP]")[0].checked , useTCP: $("#streamProxyForm input[name=useTCP]")[0].checked ,
useUDP: $("#streamProxyForm input[name=useUDP]")[0].checked , useUDP: $("#streamProxyForm input[name=useUDP]")[0].checked ,
useProxyProtocol: $("#streamProxyForm input[name=useProxyProtocol]")[0].checked , proxyProtocolVersion: parseInt($("#streamProxyForm select[name=proxyProtocolVersion]").val(), 10),
enableLogging: $("#streamProxyForm input[name=enableLogging]")[0].checked , enableLogging: $("#streamProxyForm input[name=enableLogging]")[0].checked ,
timeout: parseInt($("#streamProxyForm input[name=timeout]").val().trim()), timeout: parseInt($("#streamProxyForm input[name=timeout]").val().trim()),
}, },