@@ -59,6 +59,16 @@ const (
5959 linuxBootstrapLogPath = linuxBootstrapDir + "/bootstrap.log"
6060)
6161
62+ var linuxMinimalCoreDevicePaths = []string {
63+ "/dev/null" ,
64+ "/dev/zero" ,
65+ "/dev/full" ,
66+ "/dev/random" ,
67+ "/dev/urandom" ,
68+ "/dev/tty" ,
69+ "/dev/ptmx" ,
70+ }
71+
6272// NewLinuxBridge creates Unix socket bridges to the proxy servers.
6373// This allows sandboxed processes to communicate with the host's proxy (outbound).
6474func NewLinuxBridge (httpProxyPort , socksProxyPort int , debug bool ) (* LinuxBridge , error ) {
@@ -222,6 +232,35 @@ func fileExists(path string) bool {
222232 return err == nil
223233}
224234
235+ func appendLinuxDevicePassthrough (bwrapArgs []string , path string , bound map [string ]bool , debug bool , reason string ) []string {
236+ normalized := filepath .Clean (path )
237+ if bound [normalized ] {
238+ return bwrapArgs
239+ }
240+ if fileExists (normalized ) {
241+ bound [normalized ] = true
242+ return append (bwrapArgs , "--dev-bind" , normalized , normalized )
243+ }
244+ if debug {
245+ fmt .Fprintf (os .Stderr , "[fence:linux] Skipping missing %s device passthrough: %s\n " , reason , normalized )
246+ }
247+ return bwrapArgs
248+ }
249+
250+ func insertLinuxArgsBeforeSpecialMounts (args []string , insert []string ) []string {
251+ for i := 0 ; i < len (args ); i ++ {
252+ if args [i ] == "--dev" || args [i ] == "--proc" ||
253+ (args [i ] == "--dev-bind" && i + 2 < len (args ) && args [i + 1 ] == "/dev" && args [i + 2 ] == "/dev" ) {
254+ updated := make ([]string , 0 , len (args )+ len (insert ))
255+ updated = append (updated , args [:i ]... )
256+ updated = append (updated , insert ... )
257+ updated = append (updated , args [i :]... )
258+ return updated
259+ }
260+ }
261+ return append (args , insert ... )
262+ }
263+
225264// isDirectory returns true if the path exists and is a directory.
226265func isDirectory (path string ) bool {
227266 info , err := os .Stat (path )
@@ -758,20 +797,16 @@ func WrapCommandLinuxWithOptions(cfg *config.Config, command string, bridge *Lin
758797 bwrapArgs = append (bwrapArgs , "--dev-bind" , "/dev" , "/dev" )
759798 default :
760799 // Prefer a fresh minimal /dev for predictable sandbox behavior.
800+ // Rebind core devices from the outer environment so they remain usable
801+ // even if the synthetic /dev tmpfs inherits restrictive mount flags.
761802 bwrapArgs = append (bwrapArgs , "--dev" , "/dev" )
803+ boundDevicePaths := make (map [string ]bool , len (linuxMinimalCoreDevicePaths ))
804+ for _ , path := range linuxMinimalCoreDevicePaths {
805+ bwrapArgs = appendLinuxDevicePassthrough (bwrapArgs , path , boundDevicePaths , opts .Debug , "core" )
806+ }
762807 if cfg != nil && len (cfg .Devices .Allow ) > 0 {
763- boundDevicePaths := make (map [string ]bool , len (cfg .Devices .Allow ))
764808 for _ , path := range cfg .Devices .Allow {
765- normalized := filepath .Clean (path )
766- if boundDevicePaths [normalized ] {
767- continue
768- }
769- if fileExists (normalized ) {
770- bwrapArgs = append (bwrapArgs , "--dev-bind" , normalized , normalized )
771- boundDevicePaths [normalized ] = true
772- } else if opts .Debug {
773- fmt .Fprintf (os .Stderr , "[fence:linux] Skipping missing device passthrough: %s\n " , normalized )
774- }
809+ bwrapArgs = appendLinuxDevicePassthrough (bwrapArgs , path , boundDevicePaths , opts .Debug , "custom" )
775810 }
776811 }
777812 }
@@ -861,6 +896,11 @@ func WrapCommandLinuxWithOptions(cfg *config.Config, command string, bridge *Lin
861896 }
862897
863898 // Make writable paths actually writable (override read-only root)
899+ if writablePaths ["/" ] {
900+ delete (writablePaths , "/" )
901+ bwrapArgs = insertLinuxArgsBeforeSpecialMounts (bwrapArgs , []string {"--bind" , "/" , "/" })
902+ }
903+
864904 for p := range writablePaths {
865905 if fileExists (p ) {
866906 bwrapArgs = append (bwrapArgs , "--bind" , p , p )
@@ -1124,16 +1164,17 @@ func WrapCommandLinuxWithOptions(cfg *config.Config, command string, bridge *Lin
11241164
11251165 // Build the final command
11261166 bwrapCmd := ShellQuote (bwrapArgs )
1167+ finalCmd := bwrapCmd
11271168
11281169 // If seccomp filter is enabled, wrap with fd redirection
11291170 // bwrap --seccomp expects the filter on the specified fd
11301171 if seccompFilterPath != "" {
11311172 // Open filter file on fd 3, then run bwrap
11321173 // The filter file will be cleaned up after the sandbox exits
1133- return fmt .Sprintf ("exec 3<%s; %s" , ShellQuoteSingle (seccompFilterPath ), bwrapCmd ), nil
1174+ finalCmd = fmt .Sprintf ("exec 3<%s; %s" , ShellQuoteSingle (seccompFilterPath ), bwrapCmd )
11341175 }
11351176
1136- return bwrapCmd , nil
1177+ return finalCmd , nil
11371178}
11381179
11391180// StartLinuxMonitor starts violation monitoring for a Linux sandbox.
0 commit comments