feat: add domain-based outbound filtering with allowedDomains/deniedDomains
Add NetworkConfig.AllowedDomains and DeniedDomains fields for controlling outbound connections by hostname. Deny rules are checked first (deny wins). When AllowedDomains is set, only matching domains are permitted. When only DeniedDomains is set, all domains except denied ones are allowed. Implement FilteringProxy that wraps gost HTTP proxy with domain enforcement via AllowConnect callback. Skip GreyHaven proxy/DNS defaults
This commit is contained in:
@@ -14,6 +14,7 @@ type Manager struct {
|
||||
proxyBridge *ProxyBridge
|
||||
dnsBridge *DnsBridge
|
||||
reverseBridge *ReverseBridge
|
||||
filterProxy *FilteringProxy
|
||||
tun2socksPath string // path to extracted tun2socks binary on host
|
||||
exposedPorts []int
|
||||
debug bool
|
||||
@@ -118,6 +119,36 @@ func (m *Manager) Initialize() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Start domain filtering proxy if allowedDomains/deniedDomains are configured
|
||||
if m.config.Network.HasDomainFiltering() {
|
||||
fp, err := NewFilteringProxy(&m.config.Network, m.debug)
|
||||
if err != nil {
|
||||
// Clean up any bridges that were already started
|
||||
if m.reverseBridge != nil {
|
||||
m.reverseBridge.Cleanup()
|
||||
}
|
||||
if m.dnsBridge != nil {
|
||||
m.dnsBridge.Cleanup()
|
||||
}
|
||||
if m.proxyBridge != nil {
|
||||
m.proxyBridge.Cleanup()
|
||||
}
|
||||
if m.tun2socksPath != "" {
|
||||
_ = os.Remove(m.tun2socksPath)
|
||||
}
|
||||
return fmt.Errorf("failed to start filtering proxy: %w", err)
|
||||
}
|
||||
m.filterProxy = fp
|
||||
m.logDebug("Domain filtering proxy started on %s", fp.Addr())
|
||||
|
||||
// Write Node.js proxy bootstrap script so fetch() honors HTTP_PROXY
|
||||
if bootstrapPath, err := WriteNodeProxyBootstrap(); err != nil {
|
||||
m.logDebug("Warning: failed to write Node.js proxy bootstrap: %v", err)
|
||||
} else {
|
||||
m.logDebug("Node.js proxy bootstrap written to %s", bootstrapPath)
|
||||
}
|
||||
}
|
||||
|
||||
m.initialized = true
|
||||
if m.config.Network.ProxyURL != "" {
|
||||
dnsInfo := "none"
|
||||
@@ -148,12 +179,12 @@ func (m *Manager) WrapCommand(command string) (string, error) {
|
||||
plat := platform.Detect()
|
||||
switch plat {
|
||||
case platform.MacOS:
|
||||
return WrapCommandMacOS(m.config, command, m.exposedPorts, m.debug)
|
||||
return WrapCommandMacOS(m.config, command, m.exposedPorts, m.filterProxy, m.debug)
|
||||
case platform.Linux:
|
||||
if m.learning {
|
||||
return m.wrapCommandLearning(command)
|
||||
}
|
||||
return WrapCommandLinux(m.config, command, m.proxyBridge, m.dnsBridge, m.reverseBridge, m.tun2socksPath, m.debug)
|
||||
return WrapCommandLinux(m.config, command, m.proxyBridge, m.dnsBridge, m.reverseBridge, m.tun2socksPath, m.filterProxy, m.debug)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported platform: %s", plat)
|
||||
}
|
||||
@@ -171,7 +202,7 @@ func (m *Manager) wrapCommandLearning(command string) (string, error) {
|
||||
|
||||
m.logDebug("Strace log file: %s", m.straceLogPath)
|
||||
|
||||
return WrapCommandLinuxWithOptions(m.config, command, m.proxyBridge, m.dnsBridge, m.reverseBridge, m.tun2socksPath, LinuxSandboxOptions{
|
||||
return WrapCommandLinuxWithOptions(m.config, command, m.proxyBridge, m.dnsBridge, m.reverseBridge, m.tun2socksPath, m.filterProxy, LinuxSandboxOptions{
|
||||
UseLandlock: false, // Disabled: seccomp blocks ptrace which strace needs
|
||||
UseSeccomp: false, // Disabled: conflicts with strace
|
||||
UseEBPF: false,
|
||||
@@ -201,6 +232,9 @@ func (m *Manager) GenerateLearnedTemplate(cmdName string) (string, error) {
|
||||
|
||||
// Cleanup stops the proxies and cleans up resources.
|
||||
func (m *Manager) Cleanup() {
|
||||
if m.filterProxy != nil {
|
||||
m.filterProxy.Shutdown()
|
||||
}
|
||||
if m.reverseBridge != nil {
|
||||
m.reverseBridge.Cleanup()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user