feat: add macOS daemon support with group-based pf routing

- Add daemon CLI subcommand (install/uninstall/status/run)
- Download tun2socks for darwin platforms in Makefile
- Export ExtractTun2Socks and add darwin embed support
- Use group-based pf filtering instead of user-based for transparent
proxying
- Install sudoers rule for passwordless sandbox-exec with _greywall
group
- Add nolint directives for gosec false positives on sudoers 0440 perms
- Fix lint issues: lowercase errors, fmt.Fprintf, nolint comments
This commit is contained in:
2026-02-26 09:46:33 -06:00
parent cfe29d2c0b
commit cb474b2d99
12 changed files with 91 additions and 36 deletions

View File

@@ -21,7 +21,7 @@ const (
InstallBinaryPath = "/usr/local/bin/greywall"
InstallLibDir = "/usr/local/lib/greywall"
SandboxUserName = "_greywall"
SandboxUserUID = "399" // System user range on macOS
SandboxUserUID = "399" // System user range on macOS
SandboxGroupName = "_greywall" // Group used for pf routing (same name as user)
SudoersFilePath = "/etc/sudoers.d/greywall"
DefaultSocketPath = "/var/run/greywall.sock"
@@ -446,7 +446,7 @@ func installSudoersRule(debug bool) error {
// Write to a temp file first, then validate with visudo.
tmpFile := SudoersFilePath + ".tmp"
if err := os.WriteFile(tmpFile, []byte(rule), 0o440); err != nil {
if err := os.WriteFile(tmpFile, []byte(rule), 0o440); err != nil { //nolint:gosec // sudoers files require 0440 per sudo(8)
return fmt.Errorf("failed to write sudoers temp file: %w", err)
}
@@ -467,7 +467,7 @@ func installSudoersRule(debug bool) error {
if err := runCmd(debug, "chown", "root:wheel", SudoersFilePath); err != nil {
return fmt.Errorf("failed to set sudoers ownership: %w", err)
}
if err := os.Chmod(SudoersFilePath, 0o440); err != nil {
if err := os.Chmod(SudoersFilePath, 0o440); err != nil { //nolint:gosec // sudoers files require 0440 per sudo(8)
return fmt.Errorf("failed to set sudoers permissions: %w", err)
}

View File

@@ -24,10 +24,10 @@ type Request struct {
// Response from daemon to CLI.
type Response struct {
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
SessionID string `json:"session_id,omitempty"`
TunDevice string `json:"tun_device,omitempty"`
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
SessionID string `json:"session_id,omitempty"`
TunDevice string `json:"tun_device,omitempty"`
SandboxUser string `json:"sandbox_user,omitempty"`
SandboxGroup string `json:"sandbox_group,omitempty"`
// Status response fields.