%PDF- %PDF-
| Direktori : /www/varak.net/wiki.varak.net/includes/shell/ |
| Current File : /www/varak.net/wiki.varak.net/includes/shell/FirejailCommand.php |
<?php
/**
* Copyright (C) 2017 Kunal Mehta <legoktm@member.fsf.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
namespace MediaWiki\Shell;
use RuntimeException;
/**
* Restricts execution of shell commands using firejail
*
* @see https://firejail.wordpress.com/
* @since 1.31
*/
class FirejailCommand extends Command {
/**
* @var string Path to firejail
*/
private $firejail;
/**
* @var string[]
*/
private $whitelistedPaths = [];
/**
* @param string $firejail Path to firejail
*/
public function __construct( $firejail ) {
parent::__construct();
$this->firejail = $firejail;
}
/**
* @inheritDoc
*/
public function whitelistPaths( array $paths ) {
$this->whitelistedPaths = array_merge( $this->whitelistedPaths, $paths );
return $this;
}
/**
* @inheritDoc
*/
protected function buildFinalCommand( $command ) {
// If there are no restrictions, don't use firejail
if ( $this->restrictions === 0 ) {
$splitCommand = explode( ' ', $command, 2 );
$this->logger->debug(
"firejail: Command {$splitCommand[0]} {params} has no restrictions",
[ 'params' => $splitCommand[1] ?? '' ]
);
return parent::buildFinalCommand( $command );
}
if ( $this->firejail === false ) {
throw new RuntimeException( 'firejail is enabled, but cannot be found' );
}
// quiet has to come first to prevent firejail from adding
// any output.
$cmd = [ $this->firejail, '--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=' . __DIR__ . '/firejail.profile';
// By default firejail hides all other user directories, so if
// MediaWiki is inside a home directory (/home) but not the
// current user's home directory, pass --allusers to whitelist
// the home directories again.
static $useAllUsers = null;
if ( $useAllUsers === null ) {
global $IP;
// In case people are doing funny things with symlinks
// or relative paths, resolve them all.
$realIP = realpath( $IP );
$currentUser = posix_getpwuid( posix_geteuid() );
$useAllUsers = ( strpos( $realIP, '/home/' ) === 0 )
&& ( strpos( $realIP, $currentUser['dir'] ) !== 0 );
if ( $useAllUsers ) {
$this->logger->warning( 'firejail: MediaWiki is located ' .
'in a home directory that does not belong to the ' .
'current user, so allowing access to all home ' .
'directories (--allusers)' );
}
}
if ( $useAllUsers ) {
$cmd[] = '--allusers';
}
if ( $this->whitelistedPaths ) {
// Always whitelist limit.sh
$cmd[] = '--whitelist=' . __DIR__ . '/limit.sh';
foreach ( $this->whitelistedPaths as $whitelistedPath ) {
$cmd[] = "--whitelist={$whitelistedPath}";
}
}
if ( $this->hasRestriction( Shell::NO_LOCALSETTINGS ) ) {
$cmd[] = '--blacklist=' . realpath( MW_CONFIG_FILE );
}
if ( $this->hasRestriction( Shell::NO_ROOT ) ) {
$cmd[] = '--noroot';
}
$useSeccomp = $this->hasRestriction( Shell::SECCOMP );
$extraSeccomp = [];
if ( $this->hasRestriction( Shell::NO_EXECVE ) ) {
$extraSeccomp[] = 'execve';
// 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 ( $this->hasRestriction( Shell::PRIVATE_DEV ) ) {
$cmd[] = '--private-dev';
}
if ( $this->hasRestriction( Shell::NO_NETWORK ) ) {
$cmd[] = '--net=none';
}
$builtCmd = implode( ' ', $cmd );
// Prefix the firejail command in front of the wanted command
return parent::buildFinalCommand( "$builtCmd -- {$command}" );
}
}