damienfern/vault-symfony-bundle
composer require damienfern/vault-symfony-bundle
config/bundles.php:
return [
// ...
DamienFern\VaultBundle\VaultBundle::class => ['all' => true],
];
config/packages/vault.yaml (choose token or AppRole auth):
vault:
address: 'https://vault.example.com:8200'
path: 'myapp/production' # Secrets path in Vault
token: '%env(Vault_TOKEN)%' # Or use AppRole below
For AppRole:
vault:
address: 'https://vault.example.com:8200'
path: 'myapp/production'
app_role:
role_id: '%env(VAULT_ROLE_ID)%'
secret_id: '%env(VAULT_SECRET_ID)%'
The bundle automatically injects Vault secrets as environment variables (e.g., DB_PASSWORD from Vault → $_ENV['DB_PASSWORD']). Use them in Symfony services or .env:
# config/services.yaml
services:
App\Service\Database:
arguments:
$dsn: '%env(DATABASE_URL)%' # Populated from Vault
vault kv put myapp/production DB_PASSWORD="s3cr3t" DB_USER="admin"
%env() or $_ENV:
$password = $_ENV['DB_PASSWORD']; // Auto-populated
vault kv put myapp/production DB_PASSWORD="new_s3cr3t").ParameterBag to load secrets at runtime:
$container->get('vault.client')->get('myapp/production');
path per environment (e.g., myapp/development vs. myapp/production) in vault.yaml..env:
Combine with Symfony’s .env.local for local overrides:
# .env.local
DB_PASSWORD=local_override # Overrides Vault if needed
# config/services.yaml
services:
Doctrine\DBAL\Connection:
factory: ['@App\Factory\DatabaseFactory']
arguments:
$dsn: '%env(DATABASE_URL)%' # Vault-injected
$username: '%env(DB_USER)%'
$password: '%env(DB_PASSWORD)%'
No Auto-Refresh:
php-fpm).VaultClient service to manually refresh secrets:
$client = $container->get('vault.client');
$client->refresh(); // Hypothetical method (not yet implemented)
AppRole Token Leak Risk:
secret_id is long-lived. Rotate it via Vault’s auth/approle/rotate-secret-id API after use.secret_ids or Vault’s periodic token renewal.Missing Error Handling:
RuntimeException for now:
try {
$client->get('nonexistent/path');
} catch (RuntimeException $e) {
// Handle Vault errors (e.g., 404, auth failure)
}
Path Permissions:
vault kv get myapp/production
debug: true to vault.yaml to log HTTP requests:
vault:
debug: true
php bin/console debug:container vault.client
Custom Secret Processing:
Override the DamienFern\VaultBundle\Service\VaultClient to transform secrets (e.g., base64 decode):
// src/Service/CustomVaultClient.php
class CustomVaultClient extends VaultClient {
public function get(string $path): array {
$data = parent::get($path);
return array_map('base64_decode', $data);
}
}
Register it in services.yaml:
services:
App\Service\CustomVaultClient: ~
vault.client: '@App\Service\CustomVaultClient'
Add Integration Tests: Mock the Vault HTTP client in tests:
use Symfony\Component\HttpClient\MockHttpClient;
$client = new VaultClient(
new MockHttpClient([new Response(200, [], json_encode(['DB_PASSWORD' => 'test']))])
);
APCu Caching (Future): Watch for the planned APCu integration to reduce Vault API calls.
http:// only for local/dev.kv/v2/ paths (e.g., myapp/production) for KV v2 secrets. The bundle defaults to v2.transit engine to encrypt secrets, then rotate keys via:
vault write -f transit/keys/mykey rotate
php bin/console cache:warmup --env=prod
// src/Controller/VaultHealthCheck.php
class VaultHealthCheck {
public function __invoke(VaultClient $client) {
try {
$client->get('sys/health');
return new Response('OK');
} catch (Exception $e) {
return new Response('Vault Unavailable', 500);
}
}
}
Route it in config/routes.yaml:
_vault_health:
path: /_health/vault
controller: App\Controller\VaultHealthCheck
How can I help you explore Laravel packages today?