adrien-mallet/secured-container-bundle
Symfony bundle that helps secure your app by removing selected commands/services from the dependency injection container. Configure a list of unauthorized service IDs (e.g., Doctrine drop commands) to prevent dangerous operations in certain environments.
Installation Add the bundle via Composer:
composer require adrien-mallet/secured-container-bundle
Enable it in config/bundles.php:
return [
// ...
AdrienMallet\SecuredContainerBundle\SecuredContainerBundle::class => ['all' => true],
];
Basic Configuration
Define unauthorized commands in config/packages/secured_container.yaml:
secured_container:
unauthorized:
- "doctrine:database:drop"
- "doctrine:schema:drop"
- "app:custom_command"
First Use Case
Immediately test by running a restricted command (e.g., php bin/console doctrine:database:drop). The command should fail with a CommandNotFoundException if properly configured.
Command Whitelisting/Blacklisting
doctrine:database:drop, cache:clear) in production.doctrine commands except doctrine:schema:update:
secured_container:
unauthorized:
- "doctrine:*"
- "!doctrine:schema:update"
Environment-Specific Restrictions
Override config per environment (e.g., config/packages/dev/secured_container.yaml):
secured_container:
unauthorized: [] # Allow all commands in dev
Dynamic Command Filtering
Extend the bundle by implementing CommandFilterInterface to add runtime checks (e.g., user roles):
// src/Command/SecuredCommandFilter.php
use AdrienMallet\SecuredContainerBundle\Command\CommandFilterInterface;
class SecuredCommandFilter implements CommandFilterInterface {
public function isAuthorized(string $commandName): bool {
return in_array($commandName, ['app:safe-command']);
}
}
Register the filter in services.yaml:
services:
App\Command\SecuredCommandFilter:
tags: [secured_container.command_filter]
Command Alias Handling
Use the command.identifier format (e.g., command.identifier:doctrine:database:drop) to restrict aliases explicitly.
Case Sensitivity
Command names are case-sensitive. Ensure exact matches (e.g., doctrine:database:drop vs. doctrine:database:drop:all).
Wildcard Overuse
Wildcards (*) are greedy. Use them sparingly to avoid unintended restrictions:
# ❌ Blocks ALL commands starting with "doctrine:"
unauthorized:
- "doctrine:*"
# ✅ Explicitly list commands
unauthorized:
- "doctrine:database:drop"
- "doctrine:schema:drop"
Caching Issues
Clear Symfony’s cache after modifying secured_container.yaml:
php bin/console cache:clear
Debugging Unauthorized Commands
php bin/console list --raw
php bin/console debug:config secured_container
Combine with Symfony’s Security
Use the bundle alongside security.yaml for layered protection:
# security.yaml
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
Restrict CLI commands for non-admin users via SecuredContainerBundle.
Log Unauthorized Attempts Extend the bundle to log blocked commands:
// src/EventListener/SecuredCommandListener.php
use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
class SecuredCommandListener extends ExceptionListener {
public function onKernelException(GetResponseForExceptionEvent $event) {
$exception = $event->getThrowable();
if ($exception instanceof CommandNotFoundException) {
// Log the blocked command
}
}
}
Test Coverage Write tests to verify command restrictions:
public function testCommandIsBlocked() {
$this->expectException(CommandNotFoundException::class);
$this->executeCommand('doctrine:database:drop');
}
Integration with Deployment
Automate config updates during deployment (e.g., restrict commands in prod but not staging). Example in deploy.php:
task('configure-secured-commands', function () {
if (env('APP_ENV') === 'prod') {
putenv('SECURED_COMMANDS=doctrine:database:drop,doctrine:schema:drop');
}
})->run();
How can I help you explore Laravel packages today?