Why this article ?
I recently had to inject a password into an app, which was already defined inside my Secret Manager. I though: do I really need to copy this inside a .env file? Or even as a Symfony Secret?Well, the short answer is: no! Let's find out how we can manage this with Symfony's environment variables processor.
What is an environment variable processor ?
Basically, a Symfony's Environment Variable Processors is a service that allows to transform the original content of a given environment variable into something else: a new string reformated, an int, ...By implementing the
EnvVarProcessorInterface, everyone is able to define their own service / their own processor.
An example is given in the
official documentation,
where the service is used to always render the lower-case version of a string. Well, isn't this nice? :)
But how does that help me?
Processing variable through a service
Since we can create a service that will implementEnvVarProcessorInterface,
we can also beneficiate of services injection.
This means we can provide a service that will make an API call to an exterior service!
Example below.
# src/Service/GetMySecretProcessor.php
use App\Service\Secrets\Secret;
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
class GetMySecretProcessor implements EnvVarProcessorInterface
{
public function __construct(private VaultApiService $vaultApiService)
{
}
public function getEnv(string $prefix, string $name, \Closure $getEnv): string
{
// Do something to retrieve your secret value
// Probably something like :
// return $this->vaultApiService->resolve($name);
}
public static function getProvidedTypes(): array
{
return ['get_my_secret' => 'string'];
}
}
In this example:
VaultApiServiceis your service, your logic, that will do what needs to be done to interrogate your secret manager (typically an API call).getProvidedTypes()defines which 'tag' is resolved by your service. We'll see this in action in a second.getEnv()defines the logic to retrieve the value of the password.
Using this service to get a secret's value
Now that we have created our own environment variable processor, how to use it? Simple!Simply use Symfony autowiring to
inject your variable, as an environment variable, in a service that requires this sensitive value.
On the plus side, this will work both in
PHP files ... And in yaml files too!
For example:
# src/Controller/Controller.php
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class Controller extends AbstractController
{
#[Route('/', name: 'index')]
public function index(#[Autowire(env: 'get_my_secret:my_github_access_token')] string $myGithubAccessToken): Response
{
// $myGithubAccessToken now contains the value of your token, automatically fetched.
}
}
# packages/framework.yaml
framework:
http_client:
scoped_clients:
github.client:
scope: 'https://api\.github\.com'
headers:
Authorization: 'token %env(get_my_secret:my_github_access_token)%'
A quick precision on what has been defined for the example above:
get_my_secretis the 'tag' used to link your newly defined environment variable processor. It is referenced in thegetProvidedTypes()method.my_github_access_tokenis the$namethat will be passed to your service that implements the EnvVarProcessorInterface, to thegetEnv()function.
Don't forget however that:
- This approach still requires you to store credentials to your Secret Manager somewhere in your Symfony app.
- API requests (in most cases) will be sent every time you need a password injected to your services. Try implementing some cache for your secret value.
Socials
Github
Stack
Overflow
LinkedIn