Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add whitelist for custom headers that will be forwarded to the micros… #27

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
38 changes: 21 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Introductory blog post [in English](https://medium.com/@poweredlocal/developing-

## Running as a Docker container

Ideally, you want to run this as a stateless Docker container configured entirely by environment variables. Therefore, you don't even need to deploy
Ideally, you want to run this as a stateless Docker container configured entirely by environment variables. Therefore, you don't even need to deploy
this code anywhere yourself - just use our [public Docker Hub image](https://hub.docker.com/r/pwred/vrata).

Deploying it is as easy as:
Expand Down Expand Up @@ -66,7 +66,7 @@ See Laravel/Lumen documentation for the list of supported databases.

#### APP_KEY

Lumen application key
Lumen application key

### Gateway variables

Expand Down Expand Up @@ -111,9 +111,13 @@ JSON array of extra routes including any aggregate routes

JSON object with global settings

#### X_HEADER_WHITELIST

JSON object with custom headers that will be forwarded to the microservices

### Logging

Currently only LogEntries is supported out of the box. To send nginx and Lumen logs to LE, simply set two
Currently only LogEntries is supported out of the box. To send nginx and Lumen logs to LE, simply set two
environmetn variables:

#### LOGGING_ID
Expand All @@ -128,11 +132,11 @@ Your user key with LogEntries

- Built-in OAuth2 server to handle authentication for all incoming requests
- Aggregate queries (combine output from 2+ APIs)
- Output restructuring
- Output restructuring
- Aggregate Swagger documentation (combine Swagger docs from underlying services) *
- Automatic mount of routes based on Swagger JSON
- Sync and async outgoing requests
- DNS service discovery
- Sync and async outgoing requests
- DNS service discovery

## Installation

Expand Down Expand Up @@ -201,11 +205,11 @@ This endpoint may be auto-imported to API gateway during container start (or whe
Assuming this microservice is listed in *GATEWAY_SERVICES*, we can now run auto-import:

```bash
$ php artisan gateway:parse
** Parsing service1
Processing API action: http://localhost:8000/uploads
Dumping route data to JSON file
Finished!
$ php artisan gateway:parse
** Parsing service1
Processing API action: http://localhost:8000/uploads
Dumping route data to JSON file
Finished!
```

That's it - Vrata will now "proxy" all requests for `/uploads` to this microservice.
Expand All @@ -229,7 +233,7 @@ this Id or on token scopes (see below).

Token scopes extracted from the JSON web token. Comma separated (eg. ```read,write```)

Your microservice may use these for authorization purposes (restrict certain actions, etc).
Your microservice may use these for authorization purposes (restrict certain actions, etc).

*X-Client-Ip*

Expand All @@ -248,7 +252,7 @@ You can do basic JSON output mutation using ```output``` property of an action.
];
```

Response from *service1* will be included in the final output under *data* key.
Response from *service1* will be included in the final output under *data* key.

```output_key``` can be an array to allow further mutation:
```php
Expand Down Expand Up @@ -297,7 +301,7 @@ $ time curl http://gateway.local/devices/5/details
real 0m0.056s
```

And it's just 56ms for all 3 requests! Second and third requests were executed in parallel (in async mode).
And it's just 56ms for all 3 requests! Second and third requests were executed in parallel (in async mode).

This is pretty decent, we think!

Expand Down Expand Up @@ -483,7 +487,7 @@ Another simple route:

This will add a "/v1/history" endpoint that will request data from http://core.live.vrata.io/connections/history.
Notice the "raw" flag - this means Vrata won't do any JSON parsing at all (and therefore you won't be able to mutate
output as result). This is important for performance - PHP may choke if you json_decode() and then json_encode() a huge string
output as result). This is important for performance - PHP may choke if you json_decode() and then json_encode() a huge string
- arrays and objects are very memory expensive in PHP.

And finally our aggregate route:
Expand Down Expand Up @@ -531,13 +535,13 @@ First property marks it as an aggregate route - that's self explanatory. The rou
to microservices and two of them can be made in parallel - because they have the same sequence number of 1.

Vrata will first make a request to http://core.live.vrata.io/venues/{id} where {id} is the parameter from request.
This route action is marked as critical - therefore, if it fails the whole request is abandoned.
This route action is marked as critical - therefore, if it fails the whole request is abandoned.
All output from this action will be presented in the final JSON output as "venue" property.

Then, two requests will be launched simultaneously - to http://service1.live.vrata.io/connections/{id}
and another to http://service1.live.vrata.io/metadata/{id}. This time, {id} is taken from the output of
the previous action. Vrata will collect all outputs from all requests and make them available to all
following requests.
following requests.

Since these two requests always happen later than the first one (because of the sequence setting),
they can have access to its output. Notice {venue%data.id} in the paths - this refers to "venue" (name
Expand Down
34 changes: 23 additions & 11 deletions app/Services/RestClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,28 @@ public function __construct(Client $client, ServiceRegistryContract $services, R
*/
private function injectHeaders(Request $request)
{
$this->setHeaders(
[
'X-User' => $request->user()->id ?? self::USER_ID_ANONYMOUS,
'X-Token-Scopes' => $request->user() && ! empty($request->user()->token()) ? implode(',', $request->user()->token()->scopes) : '',
'X-Client-Ip' => $request->getClientIp(),
'User-Agent' => $request->header('User-Agent'),
'Content-Type' => 'application/json',
'Accept' => 'application/json'
]
);
// Set the default headers
$headers = [
'X-User' => $request->user()->id ?? self::USER_ID_ANONYMOUS,
'X-Token-Scopes' => $request->user() && ! empty($request->user()->token()) ? implode(',', $request->user()->token()->scopes) : '',
'X-Client-Ip' => $request->getClientIp(),
'User-Agent' => $request->header('User-Agent'),
'Content-Type' => 'application/json',
'Accept' => 'application/json'
];

// Check if there are whitelisted custom headers
$whiteList = env('X_HEADER_WHITELIST', '');

if ($whiteList != '') {
$whiteList = json_decode($whiteList, true);

foreach ($whiteList as $key => $value) {
$headers[$key] = $value;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm if I understand correctly, this is not really a white list (a list of allowed headers to pass through) but a pre-defined list of headers to pass? then it should be called something else, eg extra headers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok...you're right. I think then we should go with the suggestion of @MrDarkSkil

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the PR to match the real whitelist functionality.

}

$this->setHeaders($headers);
}

/**
Expand Down Expand Up @@ -289,4 +301,4 @@ private function buildUrl(ActionContract $action, $parametersJar)

return $this->services->resolveInstance($action->getService()) . $url;
}
}
}