You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

285 lines
7.4 KiB

//go:build !windows
// +build !windows
package transparent
import (
"context"
"fmt"
"net"
"os/exec"
"runtime"
"strings"
"syscall"
"github.com/sirupsen/logrus"
)
type TransparentProxy struct {
logger *logrus.Logger
proxyPort int
transparentPort int
dnsPort int
originalDNS []string
rules []string
}
type Config struct {
ProxyPort int // HTTP代理端口
TransparentPort int // 透明代理端口
DNSPort int // DNS代理端口
BypassIPs []string // 不代理的IP列表
BypassDomains []string // 不代理的域名列表
}
func NewTransparentProxy(config Config, logger *logrus.Logger) *TransparentProxy {
if config.TransparentPort == 0 {
config.TransparentPort = 8888
}
if config.DNSPort == 0 {
config.DNSPort = 5353
}
return &TransparentProxy{
logger: logger,
proxyPort: config.ProxyPort,
transparentPort: config.TransparentPort,
dnsPort: config.DNSPort,
rules: make([]string, 0),
}
}
// SetupTransparentProxy 设置透明代理规则
func (tp *TransparentProxy) SetupTransparentProxy(ctx context.Context) error {
if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
return fmt.Errorf("transparent proxy only supported on Linux and macOS")
}
tp.logger.Info("Setting up transparent proxy rules...")
// 备份当前DNS设置
if err := tp.backupDNS(); err != nil {
tp.logger.WithError(err).Warn("Failed to backup DNS settings")
}
switch runtime.GOOS {
case "linux":
return tp.setupLinuxRules()
case "darwin":
return tp.setupMacOSRules()
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
}
// CleanupTransparentProxy 清理透明代理规则
func (tp *TransparentProxy) CleanupTransparentProxy() error {
tp.logger.Info("Cleaning up transparent proxy rules...")
var errors []string
// 恢复DNS设置
if err := tp.restoreDNS(); err != nil {
errors = append(errors, fmt.Sprintf("DNS restore: %v", err))
}
switch runtime.GOOS {
case "linux":
if err := tp.cleanupLinuxRules(); err != nil {
errors = append(errors, fmt.Sprintf("Linux rules: %v", err))
}
case "darwin":
if err := tp.cleanupMacOSRules(); err != nil {
errors = append(errors, fmt.Sprintf("macOS rules: %v", err))
}
}
if len(errors) > 0 {
return fmt.Errorf("cleanup errors: %s", strings.Join(errors, "; "))
}
return nil
}
// setupLinuxRules 设置Linux iptables规则
func (tp *TransparentProxy) setupLinuxRules() error {
rules := []string{
// 创建新的链
"iptables -t nat -N WORMHOLE_OUTPUT",
"iptables -t nat -N WORMHOLE_PREROUTING",
// 跳过本地流量
fmt.Sprintf("iptables -t nat -A WORMHOLE_OUTPUT -d 127.0.0.0/8 -j RETURN"),
fmt.Sprintf("iptables -t nat -A WORMHOLE_OUTPUT -d 10.0.0.0/8 -j RETURN"),
fmt.Sprintf("iptables -t nat -A WORMHOLE_OUTPUT -d 172.16.0.0/12 -j RETURN"),
fmt.Sprintf("iptables -t nat -A WORMHOLE_OUTPUT -d 192.168.0.0/16 -j RETURN"),
// 重定向TCP流量到透明代理
fmt.Sprintf("iptables -t nat -A WORMHOLE_OUTPUT -p tcp -j REDIRECT --to-ports %d", tp.transparentPort),
// 应用规则
"iptables -t nat -A OUTPUT -j WORMHOLE_OUTPUT",
"iptables -t nat -A PREROUTING -j WORMHOLE_PREROUTING",
// DNS重定向
fmt.Sprintf("iptables -t nat -A WORMHOLE_PREROUTING -p udp --dport 53 -j REDIRECT --to-ports %d", tp.dnsPort),
fmt.Sprintf("iptables -t nat -A WORMHOLE_OUTPUT -p udp --dport 53 -j REDIRECT --to-ports %d", tp.dnsPort),
}
return tp.executeRules(rules)
}
// setupMacOSRules 设置macOS pfctl规则
func (tp *TransparentProxy) setupMacOSRules() error {
// macOS需要使用pfctl,配置更复杂
pfRules := fmt.Sprintf(`
# Wormhole transparent proxy rules
rdr pass on lo0 inet proto tcp from any to any port 1:65535 -> 127.0.0.1 port %d
rdr pass on lo0 inet proto udp from any to any port 53 -> 127.0.0.1 port %d
pass out quick proto tcp from any to any flags S/SA keep state
pass out quick proto udp from any to any keep state
`, tp.transparentPort, tp.dnsPort)
// 写入pfctl规则文件
if err := tp.writePfRules(pfRules); err != nil {
return err
}
// 加载规则
rules := []string{
"pfctl -f /tmp/wormhole.pf.conf",
"pfctl -e",
}
return tp.executeRules(rules)
}
// GetOriginalDestination 获取透明代理的原始目标地址
func (tp *TransparentProxy) GetOriginalDestination(conn net.Conn) (string, error) {
tcpConn, ok := conn.(*net.TCPConn)
if !ok {
return "", fmt.Errorf("not a TCP connection")
}
// 获取socket文件描述符
file, err := tcpConn.File()
if err != nil {
return "", err
}
defer file.Close()
fd := int(file.Fd())
switch runtime.GOOS {
case "linux":
return tp.getLinuxOriginalDest(fd)
case "darwin":
return tp.getMacOSOriginalDest(fd)
default:
return "", fmt.Errorf("unsupported OS for transparent proxy")
}
}
func (tp *TransparentProxy) executeRules(rules []string) error {
for _, rule := range rules {
tp.rules = append(tp.rules, rule)
parts := strings.Fields(rule)
if len(parts) == 0 {
continue
}
cmd := exec.Command(parts[0], parts[1:]...)
if output, err := cmd.CombinedOutput(); err != nil {
tp.logger.WithFields(logrus.Fields{
"rule": rule,
"output": string(output),
}).WithError(err).Error("Failed to execute rule")
// 继续执行其他规则,不要因为一个失败就停止
} else {
tp.logger.WithField("rule", rule).Debug("Rule executed successfully")
}
}
return nil
}
func (tp *TransparentProxy) cleanupLinuxRules() error {
cleanupRules := []string{
"iptables -t nat -D OUTPUT -j WORMHOLE_OUTPUT",
"iptables -t nat -D PREROUTING -j WORMHOLE_PREROUTING",
"iptables -t nat -F WORMHOLE_OUTPUT",
"iptables -t nat -F WORMHOLE_PREROUTING",
"iptables -t nat -X WORMHOLE_OUTPUT",
"iptables -t nat -X WORMHOLE_PREROUTING",
}
return tp.executeRules(cleanupRules)
}
func (tp *TransparentProxy) cleanupMacOSRules() error {
rules := []string{
"pfctl -d",
"rm -f /tmp/wormhole.pf.conf",
}
return tp.executeRules(rules)
}
func (tp *TransparentProxy) backupDNS() error {
// 这里简化处理,实际应该备份/etc/resolv.conf
tp.originalDNS = []string{"8.8.8.8", "8.8.4.4"}
return nil
}
func (tp *TransparentProxy) restoreDNS() error {
// 恢复DNS设置
tp.logger.Info("DNS settings restored")
return nil
}
func (tp *TransparentProxy) writePfRules(rules string) error {
return exec.Command("sh", "-c", fmt.Sprintf("echo '%s' > /tmp/wormhole.pf.conf", rules)).Run()
}
func (tp *TransparentProxy) getLinuxOriginalDest(fd int) (string, error) {
// Linux SO_ORIGINAL_DST
const SO_ORIGINAL_DST = 80
_, err := syscall.GetsockoptIPv6Mreq(fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST)
if err != nil {
return "", err
}
// 解析地址结构(这里简化处理)
return "unknown:80", nil
}
func (tp *TransparentProxy) getMacOSOriginalDest(fd int) (string, error) {
// macOS implementation would be more complex
return "unknown:80", nil
}
// IsTransparentSupported 检查系统是否支持透明代理
func IsTransparentSupported() bool {
switch runtime.GOOS {
case "linux":
// 检查是否有iptables
if _, err := exec.LookPath("iptables"); err != nil {
return false
}
return true
case "darwin":
// 检查是否有pfctl
if _, err := exec.LookPath("pfctl"); err != nil {
return false
}
return true
default:
return false
}
}
// RequiresRoot 检查是否需要root权限
func RequiresRoot() bool {
return runtime.GOOS == "linux" || runtime.GOOS == "darwin"
}