feat: deny-by-default filesystem isolation
Flip the sandbox from allow-by-default reads (--ro-bind / /) to deny-by-default (--tmpfs / with selective mounts). This makes the sandbox safer by default — only system paths, CWD, and explicitly allowed paths are accessible. - Config: DefaultDenyRead is now *bool (nil = true, deny-by-default) with IsDefaultDenyRead() helper; opt out via "defaultDenyRead": false - Linux: new buildDenyByDefaultMounts() using --tmpfs / + selective --ro-bind for system paths, --symlink for merged-usr distros (Arch), --bind for CWD, and --ro-bind for user tooling/shell configs/caches - macOS: generateReadRules() adds CWD subpath, ancestor traversal, home shell configs/caches; generateWriteRules() auto-allows CWD - Landlock: deny-by-default mode allows only specific user tooling paths instead of blanket home directory read access - Sensitive .env files masked within CWD via empty-file overlay on Linux and deny rules on macOS - Learning templates now include allowRead and .env deny patterns
This commit is contained in:
@@ -81,17 +81,55 @@ func ApplyLandlockFromConfig(cfg *config.Config, cwd string, socketPaths []strin
|
||||
}
|
||||
}
|
||||
|
||||
// Current working directory - read access (may be upgraded to write below)
|
||||
// Current working directory - read+write access (project directory)
|
||||
if cwd != "" {
|
||||
if err := ruleset.AllowRead(cwd); err != nil && debug {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add cwd read path: %v\n", err)
|
||||
if err := ruleset.AllowReadWrite(cwd); err != nil && debug {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add cwd read/write path: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Home directory - read access
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
if err := ruleset.AllowRead(home); err != nil && debug {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add home read path: %v\n", err)
|
||||
// Home directory - read access only when not in deny-by-default mode.
|
||||
// In deny-by-default mode, only specific user tooling paths are allowed,
|
||||
// not the entire home directory. Landlock can't selectively deny files
|
||||
// within an allowed directory, so we rely on bwrap mount overlays for
|
||||
// .env file masking.
|
||||
defaultDenyRead := cfg != nil && cfg.Filesystem.IsDefaultDenyRead()
|
||||
if !defaultDenyRead {
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
if err := ruleset.AllowRead(home); err != nil && debug {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add home read path: %v\n", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In deny-by-default mode, allow specific user tooling paths
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
for _, p := range GetDefaultReadablePaths() {
|
||||
if strings.HasPrefix(p, home) {
|
||||
if err := ruleset.AllowRead(p); err != nil && debug {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add user tooling path %s: %v\n", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Shell configs
|
||||
shellConfigs := []string{".bashrc", ".bash_profile", ".profile", ".zshrc", ".zprofile", ".zshenv", ".inputrc"}
|
||||
for _, f := range shellConfigs {
|
||||
p := filepath.Join(home, f)
|
||||
if err := ruleset.AllowRead(p); err != nil && debug {
|
||||
if !os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add shell config %s: %v\n", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Home caches
|
||||
homeCaches := []string{".cache", ".npm", ".cargo", ".rustup", ".local", ".config"}
|
||||
for _, d := range homeCaches {
|
||||
p := filepath.Join(home, d)
|
||||
if err := ruleset.AllowRead(p); err != nil && debug {
|
||||
if !os.IsNotExist(err) {
|
||||
fmt.Fprintf(os.Stderr, "[greywall:landlock] Warning: failed to add home cache %s: %v\n", p, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user