feat: add --learning mode, --template flag, and fix DNS relay
Learning mode (--learning) traces filesystem access with strace and generates minimal sandbox config templates. A background monitor kills strace when the main command exits so long-lived child processes (LSP servers, file watchers) don't cause hangs. Other changes: - Add 'greywall templates list/show' subcommand - Add --template flag to load specific learned templates - Fix DNS relay: use TCP DNS (options use-vc) instead of broken UDP relay through tun2socks - Filter O_DIRECTORY opens from learned read paths - Add docs/experience.md with development notes
This commit is contained in:
@@ -19,6 +19,9 @@ type Manager struct {
|
||||
debug bool
|
||||
monitor bool
|
||||
initialized bool
|
||||
learning bool // learning mode: permissive sandbox with strace
|
||||
straceLogPath string // host-side temp file for strace output
|
||||
commandName string // name of the command being learned
|
||||
}
|
||||
|
||||
// NewManager creates a new sandbox manager.
|
||||
@@ -35,6 +38,21 @@ func (m *Manager) SetExposedPorts(ports []int) {
|
||||
m.exposedPorts = ports
|
||||
}
|
||||
|
||||
// SetLearning enables or disables learning mode.
|
||||
func (m *Manager) SetLearning(enabled bool) {
|
||||
m.learning = enabled
|
||||
}
|
||||
|
||||
// SetCommandName sets the command name for learning mode template generation.
|
||||
func (m *Manager) SetCommandName(name string) {
|
||||
m.commandName = name
|
||||
}
|
||||
|
||||
// IsLearning returns whether learning mode is enabled.
|
||||
func (m *Manager) IsLearning() bool {
|
||||
return m.learning
|
||||
}
|
||||
|
||||
// Initialize sets up the sandbox infrastructure.
|
||||
func (m *Manager) Initialize() error {
|
||||
if m.initialized {
|
||||
@@ -128,12 +146,55 @@ func (m *Manager) WrapCommand(command string) (string, error) {
|
||||
case platform.MacOS:
|
||||
return WrapCommandMacOS(m.config, command, m.exposedPorts, 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)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported platform: %s", plat)
|
||||
}
|
||||
}
|
||||
|
||||
// wrapCommandLearning creates a permissive sandbox with strace for learning mode.
|
||||
func (m *Manager) wrapCommandLearning(command string) (string, error) {
|
||||
// Create host-side temp file for strace output
|
||||
tmpFile, err := os.CreateTemp("", "greywall-strace-*.log")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create strace log file: %w", err)
|
||||
}
|
||||
tmpFile.Close()
|
||||
m.straceLogPath = tmpFile.Name()
|
||||
|
||||
m.logDebug("Strace log file: %s", m.straceLogPath)
|
||||
|
||||
return WrapCommandLinuxWithOptions(m.config, command, m.proxyBridge, m.dnsBridge, m.reverseBridge, m.tun2socksPath, LinuxSandboxOptions{
|
||||
UseLandlock: false, // Disabled: seccomp blocks ptrace which strace needs
|
||||
UseSeccomp: false, // Disabled: conflicts with strace
|
||||
UseEBPF: false,
|
||||
Debug: m.debug,
|
||||
Learning: true,
|
||||
StraceLogPath: m.straceLogPath,
|
||||
})
|
||||
}
|
||||
|
||||
// GenerateLearnedTemplate generates a config template from the strace log collected during learning.
|
||||
func (m *Manager) GenerateLearnedTemplate(cmdName string) (string, error) {
|
||||
if m.straceLogPath == "" {
|
||||
return "", fmt.Errorf("no strace log available (was learning mode enabled?)")
|
||||
}
|
||||
|
||||
templatePath, err := GenerateLearnedTemplate(m.straceLogPath, cmdName, m.debug)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Clean up strace log since we've processed it
|
||||
os.Remove(m.straceLogPath)
|
||||
m.straceLogPath = ""
|
||||
|
||||
return templatePath, nil
|
||||
}
|
||||
|
||||
// Cleanup stops the proxies and cleans up resources.
|
||||
func (m *Manager) Cleanup() {
|
||||
if m.reverseBridge != nil {
|
||||
@@ -148,6 +209,10 @@ func (m *Manager) Cleanup() {
|
||||
if m.tun2socksPath != "" {
|
||||
os.Remove(m.tun2socksPath)
|
||||
}
|
||||
if m.straceLogPath != "" {
|
||||
os.Remove(m.straceLogPath)
|
||||
m.straceLogPath = ""
|
||||
}
|
||||
m.logDebug("Sandbox manager cleaned up")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user