symfony: run-time json encoded environment variable string does not work in security.yml
Symfony Version 3.4, PHP 7, Apache 2.4
What we are trying to do?
We are trying to restrict a route (/anon/foo) to a set of IPs via Symfony’s security access_control. The IPs will be read as a runtime environment variable from Apache. To group the IPs together, we will use a json encoded string containing multiple IPs and then have symfony decode them using Symfony’s Json environment variable processor.
STEP 1: Set up an environment variable as a json encoded string in Apache as below:
<VirtualHost *:443>
...
SetEnv whitelisted_ip "[\"127.0.0.1\", \"127.0.0.2\"]
</VirtualHost>
STEP 2: Set up your application’s parameter.yml to decode that environment variable as below:
whitelisted_ips_for_access_control: '%env(json:whitelisted_ip)%'
Step 3:
Observe the parameter in \Symfony\Component\HttpKernel\Kernel::handle after $this->boot(); The values have been decoded into an array perfectly.
$this->boot();
$foo = $this->getContainer()->getParameter( 'whitelisted_ips_for_access_control');
Step 4: Set security.yml so that the route is only accessibly via certain IPs as below:
security:
...
firewalls:
anon_area:
pattern: ^/anon
anonymous: true
...
access_control:
- { path: ^/anon/foo, roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: '%whitelisted_ips_for_access_control%' }
- { path: ^/anon/foo, roles: ROLE_NO_ACCESS}
Step 5:
Clear cache and observe the container file var/cache/test/ContainerP59owun/getSecurity_AccessMapService.php. Notice that the variable has been passed to RequestMatcher as an array array(0 => $this->getEnv('json:whitelisted_ip'))
<?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
// Returns the private 'security.access_map' shared service.
$this->services['security.access_map'] = $instance = new \Symfony\Component\Security\Http\AccessMap();
$a = new \Symfony\Component\HttpFoundation\RequestMatcher('^/anon/foo');
$instance->add(new \Symfony\Component\HttpFoundation\RequestMatcher('^/anon/foo', NULL, array(), array(0 => $this->getEnv('json:whitelisted_ip'))), array(0 => 'IS_AUTHENTICATED_ANONYMOUSLY'), NULL);
Step 6: Go back to your application’s parameters.yml that you changed in Step 2. Now change that parameter as below:
whitelisted_ips_for_access_control: ["127.0.01", "127.0.02"]
Step 7:
Clear cache again and observe the same container file Tests/_app/var/cache/test/ContainerAjyqbl9/getSecurity_AccessMapService.php
<?php
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
$this->services['security.access_map'] = $instance = new \Symfony\Component\Security\Http\AccessMap();
$a = new \Symfony\Component\HttpFoundation\RequestMatcher('^/anon/foo');
$instance->add(new \Symfony\Component\HttpFoundation\RequestMatcher('^/anon/foo', NULL, array(), $this->parameters['whitelisted_ips_for_access_control']), array(0 => 'IS_AUTHENTICATED_ANONYMOUSLY'), NULL);
Step 8: Compare the following two lines from Step 7 and Step 5
with parameter set as whitelisted_ips_for_access_control: '%env(json:whitelisted_ip)%'
$instance->add(new \Symfony\Component\HttpFoundation\RequestMatcher('^/anon/foo', NULL, array(), array(0 => $this->getEnv('json:whitelisted_ip'))), array(0 => 'IS_AUTHENTICATED_ANONYMOUSLY'), NULL);
with parameter set as whitelisted_ips_for_access_control: ["127.0.01", "127.0.02"]
$instance->add(new \Symfony\Component\HttpFoundation\RequestMatcher('^/anon/foo', NULL, array(), $this->parameters['whitelisted_ips_for_access_control']), array(0 => 'IS_AUTHENTICATED_ANONYMOUSLY'), NULL);
When the parameter is being read at runtime, a ContextErrorException is thrown at vendor\symfony\symfony\src\Symfony\Component\HttpFoundation\IpUtils.php (line 66) since the run time environment variable was wrapped in an array rather than be passed directly.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 4
- Comments: 19 (6 by maintainers)
Still a issue.
Not every config value can be set using an environment variable. For example, if the value is used at compile time to influence how the container is built (like that’s the case here), an environment variable cannot be used (see the more detailed explanations in #45868 for example).
Yes. We need to do something here, esp. if beforeNormalization() is converting envs to array/non-strings making the value incompatible too early.
Any normalizer is allowed to modify/concat the env string (this is the only case we where we “leak” its placeholder value).
but we can make the ExprBuilder aware of placeholder values, so e.g. in closures you could ask for a 2nd arg
bool $isEnvor so. Will look into this 👍The
csvprocesssor is also affected by this problem I’m afraidWhat about a new
list:prefix to use, which will validate to be a flat array of scalar values ([1,2,3,...]). And thus can be made compatible with prototyped array nodes likesecurity.access_control.N.ips(e.g. would also solve the trusted hosts node).Alternatively and maybe more pragmatic; make the
json:prefix compatible here. Kinda like we did for envs vs. cannotBeEmpty() in #26799