79f679d87bdcdcc87a87c8805fc8aeb4c2a05893
3 * Copyright (C) 2017 Kunal Mehta <legoktm@member.fsf.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 namespace MediaWiki\Shell
;
26 * Restricts execution of shell commands using firejail
28 * @see https://firejail.wordpress.com/
31 class FirejailCommand
extends Command
{
34 * @var string Path to firejail
41 private $whitelistedPaths = [];
44 * @param string $firejail Path to firejail
46 public function __construct( $firejail ) {
47 parent
::__construct();
48 $this->firejail
= $firejail;
54 public function whitelistPaths( array $paths ) {
55 $this->whitelistedPaths
= array_merge( $this->whitelistedPaths
, $paths );
62 protected function buildFinalCommand() {
63 // If there are no restrictions, don't use firejail
64 if ( $this->restrictions
=== 0 ) {
65 return parent
::buildFinalCommand();
68 if ( $this->firejail
=== false ) {
69 throw new RuntimeException( 'firejail is enabled, but cannot be found' );
71 // quiet has to come first to prevent firejail from adding
73 $cmd = [ $this->firejail
, '--quiet' ];
74 // Use a profile that allows people to add local overrides
75 // if their system is setup in an incompatible manner. Also it
76 // prevents any default profiles from running.
77 // FIXME: Doesn't actually override command-line switches?
78 $cmd[] = '--profile=' . __DIR__
. '/firejail.profile';
80 // By default firejail hides all other user directories, so if
81 // MediaWiki is inside a home directory (/home) but not the
82 // current user's home directory, pass --allusers to whitelist
83 // the home directories again.
84 static $useAllUsers = null;
85 if ( $useAllUsers === null ) {
87 // In case people are doing funny things with symlinks
88 // or relative paths, resolve them all.
89 $realIP = realpath( $IP );
90 $currentUser = posix_getpwuid( posix_geteuid() );
91 $useAllUsers = ( strpos( $realIP, '/home/' ) === 0 )
92 && ( strpos( $realIP, $currentUser['dir'] ) !== 0 );
94 $this->logger
->warning( 'firejail: MediaWiki is located ' .
95 'in a home directory that does not belong to the ' .
96 'current user, so allowing access to all home ' .
97 'directories (--allusers)' );
101 if ( $useAllUsers ) {
102 $cmd[] = '--allusers';
105 if ( $this->whitelistedPaths
) {
106 // Always whitelist limit.sh
107 $cmd[] = '--whitelist=' . __DIR__
. '/limit.sh';
108 foreach ( $this->whitelistedPaths
as $whitelistedPath ) {
109 $cmd[] = "--whitelist={$whitelistedPath}";
113 if ( $this->hasRestriction( Shell
::NO_ROOT
) ) {
119 if ( $this->hasRestriction( Shell
::SECCOMP
) ) {
120 $seccomp[] = '@default';
123 if ( $this->hasRestriction( Shell
::NO_EXECVE
) ) {
124 $seccomp[] = 'execve';
128 $cmd[] = '--seccomp=' . implode( ',', $seccomp );
131 if ( $this->hasRestriction( Shell
::PRIVATE_DEV
) ) {
132 $cmd[] = '--private-dev';
135 if ( $this->hasRestriction( Shell
::NO_NETWORK
) ) {
136 $cmd[] = '--net=none';
139 list( $fullCommand, $useLogPipe ) = parent
::buildFinalCommand();
141 $builtCmd = implode( ' ', $cmd );
143 return [ "$builtCmd -- $fullCommand", $useLogPipe ];