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:
@@ -594,8 +594,8 @@ func isSystemMountPoint(path string) bool {
|
||||
|
||||
// WrapCommandLinux wraps a command with Linux bubblewrap sandbox.
|
||||
// It uses available security features (Landlock, seccomp) with graceful fallback.
|
||||
func WrapCommandLinux(cfg *config.Config, command string, proxyBridge *ProxyBridge, dnsBridge *DnsBridge, reverseBridge *ReverseBridge, tun2socksPath string, debug bool) (string, error) {
|
||||
return WrapCommandLinuxWithOptions(cfg, command, proxyBridge, dnsBridge, reverseBridge, tun2socksPath, LinuxSandboxOptions{
|
||||
func WrapCommandLinux(cfg *config.Config, command string, proxyBridge *ProxyBridge, dnsBridge *DnsBridge, reverseBridge *ReverseBridge, tun2socksPath string, filterProxy *FilteringProxy, debug bool) (string, error) {
|
||||
return WrapCommandLinuxWithOptions(cfg, command, proxyBridge, dnsBridge, reverseBridge, tun2socksPath, filterProxy, LinuxSandboxOptions{
|
||||
UseLandlock: true, // Enabled by default, will fall back if not available
|
||||
UseSeccomp: true, // Enabled by default
|
||||
UseEBPF: true, // Enabled by default if available
|
||||
@@ -604,7 +604,7 @@ func WrapCommandLinux(cfg *config.Config, command string, proxyBridge *ProxyBrid
|
||||
}
|
||||
|
||||
// WrapCommandLinuxWithOptions wraps a command with configurable sandbox options.
|
||||
func WrapCommandLinuxWithOptions(cfg *config.Config, command string, proxyBridge *ProxyBridge, dnsBridge *DnsBridge, reverseBridge *ReverseBridge, tun2socksPath string, opts LinuxSandboxOptions) (string, error) {
|
||||
func WrapCommandLinuxWithOptions(cfg *config.Config, command string, proxyBridge *ProxyBridge, dnsBridge *DnsBridge, reverseBridge *ReverseBridge, tun2socksPath string, filterProxy *FilteringProxy, opts LinuxSandboxOptions) (string, error) {
|
||||
if _, err := exec.LookPath("bwrap"); err != nil {
|
||||
return "", fmt.Errorf("bubblewrap (bwrap) is required on Linux but not found: %w", err)
|
||||
}
|
||||
@@ -636,11 +636,18 @@ func WrapCommandLinuxWithOptions(cfg *config.Config, command string, proxyBridge
|
||||
bwrapArgs = append(bwrapArgs, "--die-with-parent")
|
||||
|
||||
// Always use --unshare-net when available (network namespace isolation)
|
||||
// Inside the namespace, tun2socks will provide transparent proxy access
|
||||
if features.CanUnshareNet {
|
||||
// Inside the namespace, tun2socks will provide transparent proxy access.
|
||||
// Skip network namespace when domain filtering with wildcard allow is active
|
||||
// (the filtering proxy handles domain enforcement via env vars).
|
||||
skipUnshareNet := filterProxy != nil && cfg != nil && cfg.Network.IsWildcardAllow()
|
||||
if features.CanUnshareNet && !skipUnshareNet {
|
||||
bwrapArgs = append(bwrapArgs, "--unshare-net") // Network namespace isolation
|
||||
} else if opts.Debug {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:linux] Skipping --unshare-net (network namespace unavailable in this environment)\n")
|
||||
if skipUnshareNet {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:linux] Skipping --unshare-net (wildcard allow with domain filtering)\n")
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:linux] Skipping --unshare-net (network namespace unavailable in this environment)\n")
|
||||
}
|
||||
}
|
||||
|
||||
bwrapArgs = append(bwrapArgs, "--unshare-pid") // PID namespace isolation
|
||||
@@ -1042,6 +1049,23 @@ export no_proxy=localhost,127.0.0.1
|
||||
`, proxyBridge.SocketPath))
|
||||
}
|
||||
|
||||
// Set up domain filtering proxy env vars inside the sandbox.
|
||||
// When filterProxy is active, skip tun2socks and use env-var-based proxying
|
||||
// through a socat bridge to the host-side filtering proxy.
|
||||
if filterProxy != nil && proxyBridge == nil {
|
||||
filterProxyAddr := filterProxy.Addr()
|
||||
innerScript.WriteString(fmt.Sprintf(`
|
||||
# Domain filtering proxy: bridge to host-side filtering proxy
|
||||
export HTTP_PROXY=http://%s
|
||||
export HTTPS_PROXY=http://%s
|
||||
export http_proxy=http://%s
|
||||
export https_proxy=http://%s
|
||||
export NO_PROXY=localhost,127.0.0.1
|
||||
export no_proxy=localhost,127.0.0.1
|
||||
|
||||
`, filterProxyAddr, filterProxyAddr, filterProxyAddr, filterProxyAddr))
|
||||
}
|
||||
|
||||
// Set up reverse (inbound) socat listeners inside the sandbox
|
||||
if reverseBridge != nil && len(reverseBridge.Ports) > 0 {
|
||||
innerScript.WriteString("\n# Start reverse bridge listeners for inbound connections\n")
|
||||
|
||||
Reference in New Issue
Block a user