Bref: /Prod/ path prefix ignored by frameworks (Laravel/Symfony)

Created on 17 Apr 2019  Â·  19Comments  Â·  Source: brefphp/bref

Hello,

I basically followed the tutorial about laravel integration: https://bref.sh/docs/frameworks/laravel.html

I'm stuck now because the redirection removes the "/Prod" or "/Stage" part, leading me to a 403 error.

For example, on the first call, the application should redirect me to the login page:
https://.execute-api.eu-west-1.amazonaws.com/Prod/login

but instead it removes the "/Prod" part:
https://.execute-api.eu-west-1.amazonaws.com/login

I'm not quite sure how to solve this issue. I tried to compare my configuration with the demo repository (https://github.com/mnapoli/bref-laravel-demo) but I could not found how to solve it.

Thanks for your help

bug

Most helpful comment

I indeed use that a low now, and it can be written even simpler:

functions:
    web:
        handler: public/index.php
        layers:
            - ${bref:layer.php-74-fpm}
        events:
            -   httpApi: '*'

However, I'm not sure it makes sense to update the docs for now: HTTP APIs still don't support HTTP -> HTTPS redirection when setting up custom domains. That makes them not suitable for websites (but OK for APIs). It's really too bad, I wish AWS would fix that.

All 19 comments

Thank you. I think this is related to this:

https://github.com/mnapoli/bref/blob/aa941f89cd66cd1643cf9bbc75525f58dc942736/src/Runtime/PhpFpm.php#L215

and this is a problem we've discussed in the past. The root of the problem is that the $event['path'] key does not contain the stage prefix (/Prod/).

Pro:

  • we can deploy an application to API Gateway and all the routes will work without needing to add the /Prod/ prefix in them

Con:

  • since the app is unaware of the prefix links and redirects can be broken

If we add the prefix back, then deploying an application to API Gateway will break because the prefix will break the routes -> this is really annoying.

Since most applications are deployed with a domain name this is even more annoying to dev locally without a prefix, test for a couple minutes (after the first deployment) with the path prefix (which means all route have to be changed) and then redeploy with the domain name (all routes are broken again).

I wish we could find a all-in-one solution to this to have routes and redirects work both with and without the prefix… Any idea is welcome!

Related: #67

@mnapoli I Was trying to think how we could do that but, how can you deploy an application to APIGW without the need to adding the prefix(stage) to it? If I Deploy to a stage, and try to enter without the stage in the URL, APIGW returns me Forbidden and don't even ping the Layer (I've just tested now).

The best thing i can think of is removing the stage from the path before sending to the application.


        $path = $event['path'] ?? '/';
        $path = explode("/",$path);
        array_shift($path); // Remove stage
        $path = "/" . implode('/', $path);

That way, the application will be Stage agnostic, but links must start with the stage.

With ELB and custom domains, stages are not needed in the URL.

Regarding @the-player777 question, unfortunately, i think you need to edit the application to append /Prod to all links, Because as i said, ApiGW does not gets to our layer without that..

If anyone knows how to ping the layer without the stage in the url then it might change things.

@atrope sorry I wasn't clear, you still have to use the URL with the stage. However the $event['path'] key doesn't contain the prefix. Because of that the PHP application doesn't need the routes to be changed.

However as you said it breaks links generated server-side.

I am afraid I don't see a solution other than having the prefix in the URL… This would be a BC break.

Where we could mitigate this is with documentation though: e.g. document how we can add a prefix to all routes in Laravel/Symfony/Slim/etc.

If you always deploy with a domain, you never have to switch route for anything. Since its serverless you can always test locally, deploy service.dev.domain.com to test and then deploy service.domain.com to production. Pretty much no extra cost unless you want a dev RDS (which serverless Aurora might also help save cost)

Maybe we should look into how RewriteBase works when using a .htaccess file. This is something commonly used to host a website in a subfolder without having to update all the routes of the application.

If we can put the route prefix in there, Symfony/Laravel/etc might be able to pick it up and make everything work out of the box!

In other words we should try to set $_SERVER variables so that this code picks it up correctly:

https://github.com/symfony/symfony/blob/fc6d6c90541ccd67ccc46cc6a716f36dc04bb90d/src/Symfony/Component/HttpFoundation/Request.php#L1781

It's true that you can learn about the stage prefix from the serverless.yml when deploying the application, right? Is it always true? It wouldn't work for people that use Bref to deploy their PHP application but then set up their API gateway manually, but I don't know if anybody does that.

@ondrejmirtes we can find the prefix in the variables provided by API Gateway. It's a bit tricky, but we shouldn't have to parse anything in serverless.yml, everything should be resolvable at runtime.

Cool, if it's available then it should be easy to populate $_SERVER :)

Hello,

I recently found this issue while testing my Symfony application with AWS Lambda.

My current issue is that I'm using the switch user functionality explained here. The problem I'm facing is after the Symfony\Component\Security\Http\Firewall\SwitchUserListener is activated and it sets the response here it redirects me to a url of this kind:

https://some-api-id.execute-api.eu-central-1.amazonaws.com/

I think this is because the method prepareBaseUrl mentioned in @mnapoli comment doesn't know that I'm using a Cloudfront with a custom domain name and it is redirecting me to the original execution path of the script and not my domain.

Any suggestions how this can be fixed are welcomed.

For example I'm trying to log in as a user from this url:

https://example.com/my_custom_path/?_switch_user={my_user_id}

But the response from the lambda is a redirect with location:

http://{some-api-id}.execute-api.eu-central-1.amazonaws.com/my_custom_path/

Sorry but still facing this issue, is there a quick fix for this ?

A way to get rid of the prefix is to switch from API Gateway’s REST API to v2’s HTTP API. While Bref supports the HTTP API (#602), the documentation still only mentions the previous-generation REST API.

@mnapoli What do you think about switching the current documentation from REST to the HTTP API? HTTP API may lack some features but most of these should be irrelevant for Bref users.

@g-balas A very simple example config would be:

functions:
    web:
        handler: public/index.php
        layers:
            - ${bref:layer.php-74-fpm}
        events:
            -   httpApi:
                    path: '*'
                    method: '*'

Thank you so much @ddeboer, looking good !

I indeed use that a low now, and it can be written even simpler:

functions:
    web:
        handler: public/index.php
        layers:
            - ${bref:layer.php-74-fpm}
        events:
            -   httpApi: '*'

However, I'm not sure it makes sense to update the docs for now: HTTP APIs still don't support HTTP -> HTTPS redirection when setting up custom domains. That makes them not suitable for websites (but OK for APIs). It's really too bad, I wish AWS would fix that.

As @ddeboer mentioned, another lack of feature is the use of resourcePolicy to implement a whitelist IPs for exemple.
Using access_control on symfony side does not seems to work neither, I guess it's because the framework receive the IP of the lambda, not the client

I managed to get around this issue within a day of trials & errors.

Using Symfony 4.4, I create a custom router (RouterInterface) & custom logout (LogoutSuccessHandlerInterface) services.

``` /**
* @inheritdoc
*/
public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
{
$url = $this->router->generate($name, $parameters, $referenceType);
if ($this->environment == 'prod') {
return '/prod'.$url;
}
return $url;
}


When in prod, I manually add the stage 'prod' inside the generate function.

The firewall, and redirection mecanism need to be customed a bit but overall everything is working fine.

I finally can use the resourcePolicy in serverless.yaml  :

resourcePolicy:
     - Effect: Allow
       Principal: '*'
       Action: execute-api:Invoke
       Resource:
         - execute-api:/*/*/*
       Condition:
         IpAddress:
           aws:SourceIp:
             - 'xx.xx.xxx.xxx'

functions:
website:
handler: public/index.php
timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
layers:
- ${bref:layer.php-74-fpm}
events:
- http: 'ANY /'
- http: 'ANY /{proxy+}'
```

I pretty new to symfony, so any comments are welcome !

@g-balas it is possible to get the IP of the visitor in PHP/Symfony by looking into the X-Forwarded-For header (see https://symfony.com/doc/current/deployment/proxies.html#but-what-if-the-ip-of-my-reverse-proxy-changes-constantly).

This will be fixed in v1.0, see #794 for more info.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mnapoli picture mnapoli  Â·  5Comments

nealio82 picture nealio82  Â·  6Comments

mnapoli picture mnapoli  Â·  3Comments

t-geindre picture t-geindre  Â·  5Comments

tlfbrito picture tlfbrito  Â·  8Comments