Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 53 |
FirejailWrapper | |
0.00% |
0 / 1 |
|
0.00% |
0 / 3 |
240 | |
0.00% |
0 / 53 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
wrap | |
0.00% |
0 / 1 |
182 | |
0.00% |
0 / 47 |
|||
getPriority | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
<?php | |
namespace Shellbox\Command; | |
use Shellbox\Shellbox; | |
/** | |
* A wrapper that restricts the command using firejail | |
*/ | |
class FirejailWrapper extends Wrapper { | |
/** | |
* Firejail is a setuid-root executable which naturally goes inside systemd | |
* but outside BashWrapper, since it inherits and preserves most aspects of | |
* the system. | |
*/ | |
public const PRIORITY = 40; | |
/** | |
* @var string The path to firejail | |
*/ | |
private $binaryPath; | |
/** | |
* @var string The path to the profile file | |
*/ | |
private $profilePath; | |
/** | |
* @param string $binaryPath The path to firejail | |
* @param string $profilePath The path to the profile file | |
*/ | |
public function __construct( $binaryPath, $profilePath ) { | |
parent::__construct(); | |
$this->binaryPath = $binaryPath; | |
$this->profilePath = $profilePath; | |
} | |
public function wrap( Command $command ) { | |
// If there are no restrictions, don't use firejail | |
if ( $command->getDisableSandbox() ) { | |
$splitCommand = explode( ' ', $command->getCommandString(), 2 ); | |
$this->logger->debug( | |
"firejail: Command {$splitCommand[0]} {params} has no restrictions", | |
[ 'params' => $splitCommand[1] ?? '' ] | |
); | |
return; | |
} | |
// quiet has to come first to prevent firejail from adding | |
// any output. | |
$cmd = [ $this->binaryPath, '--quiet' ]; | |
// Use a profile that allows people to add local overrides | |
// if their system is setup in an incompatible manner. Also it | |
// prevents any default profiles from running. | |
// FIXME: Doesn't actually override command-line switches? | |
$cmd[] = '--profile=' . $this->profilePath; | |
foreach ( $command->getAllowedPaths() as $path ) { | |
if ( $path === '/home' ) { | |
$cmd[] = '--allusers'; | |
} else { | |
$cmd[] = "--whitelist={$path}"; | |
} | |
} | |
foreach ( $command->getDisallowedPaths() as $path ) { | |
$cmd[] = '--blacklist=' . $path; | |
} | |
if ( $command->getPrivateUserNamespace() ) { | |
$cmd[] = '--noroot'; | |
} | |
$extraSeccomp = $command->getDisabledSyscalls(); | |
$useSeccomp = $extraSeccomp || $command->getFirejailDefaultSeccomp(); | |
if ( in_array( 'execve', $extraSeccomp ) ) { | |
// Normally firejail will run commands in a bash shell, | |
// but that won't work if we ban the execve syscall, so | |
// run the command without a shell. | |
$cmd[] = '--shell=none'; | |
} | |
if ( $useSeccomp ) { | |
$seccomp = '--seccomp'; | |
if ( $extraSeccomp ) { | |
// The "@default" seccomp group will always be enabled | |
$seccomp .= '=' . implode( ',', $extraSeccomp ); | |
} | |
$cmd[] = $seccomp; | |
} | |
if ( $command->getPrivateDev() ) { | |
$cmd[] = '--private-dev'; | |
} | |
if ( $command->getDisableNetwork() ) { | |
$cmd[] = '--net=none'; | |
} | |
foreach ( $command->getEnvironment() as $name => $value ) { | |
$cmd[] = "--env=$name=$value"; | |
} | |
$builtCmd = Shellbox::escape( $cmd ); | |
// Prefix the firejail command in front of the wanted command | |
$command->unsafeCommand( "$builtCmd -- " . $command->getCommandString() ); | |
} | |
public function getPriority() { | |
return self::PRIORITY; | |
} | |
} |