diff --git a/doc/faqs/how-to-install-untrusted-packages-safely.md b/doc/faqs/how-to-install-untrusted-packages-safely.md index 27a8b7efd..6048d1523 100644 --- a/doc/faqs/how-to-install-untrusted-packages-safely.md +++ b/doc/faqs/how-to-install-untrusted-packages-safely.md @@ -39,3 +39,10 @@ Also note that the `exec` command will always run third party code as the user w See the [COMPOSER_ALLOW_SUPERUSER](../03-cli.md#composer-allow-superuser) environment variable for more info on how to disable the warnings. + +## Running Composer inside Docker/Podman containers + +Composer makes a best effort attempt to detect that it runs inside a container and if so it will +allow running as root without any further issues. If that detection fails however you will +see warnings and plugins will be disabled unless you set the [COMPOSER_ALLOW_SUPERUSER](../03-cli.md#composer-allow-superuser) +environment variable. diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 2f3e2c4b1..217919bc0 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -218,11 +218,7 @@ class Application extends BaseApplication $needsSudoCheck = !Platform::isWindows() && function_exists('exec') && !Platform::getEnv('COMPOSER_ALLOW_SUPERUSER') - && (ini_get('open_basedir') || ( - !file_exists('/.dockerenv') - && !file_exists('/run/.containerenv') - && !file_exists('/var/run/.containerenv') - )); + && !Platform::isDocker(); $isNonAllowedRoot = false; // Clobber sudo credentials if COMPOSER_ALLOW_SUPERUSER is not set before loading plugins diff --git a/src/Composer/Util/Platform.php b/src/Composer/Util/Platform.php index 6266b0413..dd41904a0 100644 --- a/src/Composer/Util/Platform.php +++ b/src/Composer/Util/Platform.php @@ -25,6 +25,8 @@ class Platform private static $isVirtualBoxGuest = null; /** @var ?bool */ private static $isWindowsSubsystemForLinux = null; + /** @var ?bool */ + private static $isDocker = null; /** * getcwd() equivalent which always returns a string @@ -153,12 +155,10 @@ class Platform } if ( - !ini_get('open_basedir') + !(bool) ini_get('open_basedir') && is_readable('/proc/version') && false !== stripos((string)Silencer::call('file_get_contents', '/proc/version'), 'microsoft') - && !file_exists('/.dockerenv') // Docker and Podman running inside WSL should not be seen as WSL - && !file_exists('/run/.containerenv') - && !file_exists('/var/run/.containerenv') + && !self::isDocker() // Docker and Podman running inside WSL should not be seen as WSL ) { return self::$isWindowsSubsystemForLinux = true; } @@ -175,6 +175,40 @@ class Platform return \defined('PHP_WINDOWS_VERSION_BUILD'); } + public static function isDocker(): bool + { + if (null !== self::$isDocker) { + return self::$isDocker; + } + + // cannot check so assume no + if ((bool) ini_get('open_basedir')) { + return self::$isDocker = false; + } + + // .dockerenv and .containerenv are present in some cases but not reliably + if (file_exists('/.dockerenv') || file_exists('/run/.containerenv') || file_exists('/var/run/.containerenv')) { + return self::$isDocker = true; + } + + // see https://www.baeldung.com/linux/is-process-running-inside-container + $cgroups = [ + '/proc/self/mountinfo', // cgroup v2 + '/proc/1/cgroup', // cgroup v1 + ]; + foreach($cgroups as $cgroup) { + if (!is_readable($cgroup)) { + continue; + } + $data = file_get_contents($cgroup); + if (is_string($data) && str_contains($data, '/var/lib/docker/')) { + return self::$isDocker = true; + } + } + + return self::$isDocker = false; + } + /** * @return int return a guaranteed binary length of the string, regardless of silly mbstring configs */