Description
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.