MediaWiki  master
FirejailCommand.php
Go to the documentation of this file.
1 <?php
21 namespace MediaWiki\Shell;
22 
24 
31 class FirejailCommand extends Command {
32 
36  private $firejail;
37 
41  private $whitelistedPaths = [];
42 
46  public function __construct( string $firejail ) {
47  parent::__construct();
48  $this->firejail = $firejail;
49  }
50 
54  public function whitelistPaths( array $paths ): Command {
55  $this->whitelistedPaths = array_merge( $this->whitelistedPaths, $paths );
56  return $this;
57  }
58 
62  protected function buildFinalCommand( string $command ): array {
63  // If there are no restrictions, don't use firejail
64  if ( $this->restrictions === 0 ) {
65  $splitCommand = explode( ' ', $command, 2 );
66  $this->logger->debug(
67  "firejail: Command {$splitCommand[0]} {params} has no restrictions",
68  [ 'params' => $splitCommand[1] ?? '' ]
69  );
70  return parent::buildFinalCommand( $command );
71  }
72 
73  if ( $this->firejail === false ) {
74  throw new RuntimeException( 'firejail is enabled, but cannot be found' );
75  }
76  // quiet has to come first to prevent firejail from adding
77  // any output.
78  $cmd = [ $this->firejail, '--quiet' ];
79  // Use a profile that allows people to add local overrides
80  // if their system is setup in an incompatible manner. Also it
81  // prevents any default profiles from running.
82  // FIXME: Doesn't actually override command-line switches?
83  $cmd[] = '--profile=' . __DIR__ . '/firejail.profile';
84 
85  // By default firejail hides all other user directories, so if
86  // MediaWiki is inside a home directory (/home) but not the
87  // current user's home directory, pass --allusers to whitelist
88  // the home directories again.
89  static $useAllUsers = null;
90  if ( $useAllUsers === null ) {
91  global $IP;
92  // In case people are doing funny things with symlinks
93  // or relative paths, resolve them all.
94  $realIP = realpath( $IP );
95  $currentUser = posix_getpwuid( posix_geteuid() );
96  $useAllUsers = ( strpos( $realIP, '/home/' ) === 0 )
97  && ( strpos( $realIP, $currentUser['dir'] ) !== 0 );
98  if ( $useAllUsers ) {
99  $this->logger->warning( 'firejail: MediaWiki is located ' .
100  'in a home directory that does not belong to the ' .
101  'current user, so allowing access to all home ' .
102  'directories (--allusers)' );
103  }
104  }
105 
106  if ( $useAllUsers ) {
107  $cmd[] = '--allusers';
108  }
109 
110  if ( $this->whitelistedPaths ) {
111  // Always whitelist limit.sh
112  $cmd[] = '--whitelist=' . __DIR__ . '/limit.sh';
113  foreach ( $this->whitelistedPaths as $whitelistedPath ) {
114  $cmd[] = "--whitelist={$whitelistedPath}";
115  }
116  }
117 
118  if ( $this->hasRestriction( Shell::NO_LOCALSETTINGS ) ) {
119  $cmd[] = '--blacklist=' . realpath( MW_CONFIG_FILE );
120  }
121 
122  if ( $this->hasRestriction( Shell::NO_ROOT ) ) {
123  $cmd[] = '--noroot';
124  }
125 
126  $useSeccomp = $this->hasRestriction( Shell::SECCOMP );
127  $extraSeccomp = [];
128 
129  if ( $this->hasRestriction( Shell::NO_EXECVE ) ) {
130  $extraSeccomp[] = 'execve';
131  // Normally firejail will run commands in a bash shell,
132  // but that won't work if we ban the execve syscall, so
133  // run the command without a shell.
134  $cmd[] = '--shell=none';
135  }
136 
137  if ( $useSeccomp ) {
138  $seccomp = '--seccomp';
139  if ( $extraSeccomp ) {
140  // The "@default" seccomp group will always be enabled
141  $seccomp .= '=' . implode( ',', $extraSeccomp );
142  }
143  $cmd[] = $seccomp;
144  }
145 
146  if ( $this->hasRestriction( Shell::PRIVATE_DEV ) ) {
147  $cmd[] = '--private-dev';
148  }
149 
150  if ( $this->hasRestriction( Shell::NO_NETWORK ) ) {
151  $cmd[] = '--net=none';
152  }
153 
154  $builtCmd = implode( ' ', $cmd );
155 
156  // Prefix the firejail command in front of the wanted command
157  return parent::buildFinalCommand( "$builtCmd -- {$command}" );
158  }
159 
160 }
$IP
Definition: WebStart.php:41
const NO_ROOT
Disallow any root access.
Definition: Shell.php:52
string $firejail
Path to firejail.
Class used for executing shell commands.
Definition: Command.php:36
const NO_LOCALSETTINGS
Deny access to LocalSettings.php (MW_CONFIG_FILE)
Definition: Shell.php:90
const NO_EXECVE
Deny execve syscall with seccomp.
Definition: Shell.php:83
hasRestriction(int $restriction)
Bitfield helper on whether a specific restriction is enabled.
Definition: Command.php:263
const SECCOMP
Use seccomp to block dangerous syscalls.
Definition: Shell.php:60
const NO_NETWORK
Restrict the request to have no network access.
Definition: Shell.php:75
Restricts execution of shell commands using firejail.
const PRIVATE_DEV
Create a private /dev.
Definition: Shell.php:67
Copyright (C) 2017 Kunal Mehta legoktm@member.fsf.org
Definition: Command.php:21