I'm trying to get visitor IP on my Laravel application that uses Nginx on Google Cloud Kubernetes Engine, under load balancer.
I have set up TrustProxies.php like this:
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Fideloper\Proxy\TrustProxies as Middleware;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* #var array
*/
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* #var int
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}
I have also tried
protected $proxies = '**';
And
protected $proxies = ['loadbalancer_ip_here'];
No matter what I have tried, it will always return load balancer ip.
Might this be caused by Nginx configuration? Help appreciated.
You have to set traffic policy in your nginx service
externalTrafficPolicy: "Local"
and also
healthCheckNodePort: "numeric port number for the service"
More details in Preserving the client source IP doc
Related
I am deploying my laravel application using docker + kubernetes and aws load balancer.
There is one middleware 'EventLogger' which is run after user logs in to the system. This logger logs the user's IP address and saves into mongo db.
Here is that middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
use App\Models\EventLogger as Logger;
use App\Models\OauthClient;
use Jenssegers\Agent\Agent;
use App\Models\Advertisement;
use App\Jobs\ProcessEventLogger;
class EventLogger
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $event = null)
{
//$request->request->add(["event" => $event]);
$request->merge(["event" => $event]);
return $next($request);
}
public function terminate($request, $response)
{
if($response->getStatusCode() != 200){
return;
}
$response_contents = $response->getContent();
//allow only status = true responses
if(false == json_decode($response_contents)->status){
return;
}
if (env('APP_ENV') === 'testing') {
$client_id = 3;
} else{
if ($request->event == Logger::LOGIN) {
$client_id = $request->client_id;
} else {
$client_id = Auth::user()->token()->client_id;
}
}
$agent = new Agent();
$agent->browser();
$agent->device();
$agent->platform();
ProcessEventLogger::dispatch($request->all(),$response, Auth::user()->id, $request->ip(),$client_id, $response_contents, $request->coin_id,$agent->browser(),$agent->device(),$agent->platform());
}
}
ProcessEventLogger is the background queued job, which is working fine.
The problem is $request->ip() always returns 127.0.0.1
I have tried most of the solutions which are answered here but there is one solution which uses trusted proxies of laravel middleware.
How can I use that feature, as I do not want protected $proxies = '*' and also I can't provide proxy ip address as everytime I restart the pod, new ip address is generated.
Please guide me in this situation.
as I do not want protected $proxies = '*'
Sorry, looks like you don't have many options. Either you will have to know the IP Range to allow. OR Configure your web server accordingly. Quoting from Symfony Doc which Laravel uses.
Some reverse proxies (like AWS Elastic Load Balancing) don’t have a
static IP address or even a range that you can target with the CIDR
notation. In this case, you’ll need to - very carefully - trust all
proxies.
Configure your web server(s) to not respond to traffic from any
clients other than your load balancers. For AWS, this can be done with
security groups.
Once you’ve guaranteed that traffic will only come from your trusted
reverse proxies, configure Symfony to always trust incoming request:
https://symfony.com/doc/current/deployment/proxies.html#but-what-if-the-ip-of-my-reverse-proxy-changes-constantly
Normally everything works fine on localhost. When I throw the project to the server, only the homepage works.
When I add manually to this section from the database, I can pull the data.
Web site here: http://elsetea.com/public (running)
but when I send a message from the contact section, my routes don't work (http://elsetea.com/public/#messages). What can I do in this situation.
Contact routing is as follows.
Route::namespace('Frontend')->group(function () {
Route::post('contact', 'ContactPageController#store')->name('contact-page.store');
});
I have taken the action as below and sending the message has been successful. But there was no informative message that should come out later.
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* #var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
//
'/contact'
];
}
With my new project, when I deploy my app to my https:// domain, every {{ asset() }} and every {{ route() }} is being served over http (which causes "mixed content" security issues in browsers).
I'm using AWS with a load-balanced Elastic Beanstalk application.
I've tried ensuring APP_URL is correctly set to https, and I understand I can use secure_asset or forceScheme, however I didn't have to do this with my previous project and I want to understand why.
How can I see where Laravel is making a decision about protocol? I want to get to the root of the problem rather than plaster over it.
This is an easy gotcha. If you're using AWS you need to change your config. It's very simple and, as usual, Laravel's documentation has the solution. You can read more here:
https://laravel.com/docs/5.6/requests#configuring-trusted-proxies
All I had to do (as an AWS Elastic Beanstalk user) was edit app/Http/Middleware/TrustProxies.php:
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* #var array
*/
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* #var int
*/
protected $headers = Request::HEADER_X_FORWARDED_AWS_ELB;
}
Now everything is fine. Easy to miss when setting up a new project.
I believe secure_asset is what you're looking for.
Here an example:
<link href="{{ secure_asset('assets/mdi/css/materialdesignicons.min.css') }}" media="all" rel="stylesheet" type="text/css" />
Update:
A better solution to do it right (tested in laravel 5.4):
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
if(env('APP_ENV') == 'production') {
\URL::forceScheme('https');
}
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
I have a Laravel 5.5 App where I have a Service Provider which I use to put some stuff in the request->attributes to access it everywhere (simplified):
namespace App\Providers;
use App\Models\Domain;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
class GlobalVarsServiceProvider extends ServiceProvider
{
/**
* Register the application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap the application services.
*
* #param Request $request
*
* #return void
*/
public function boot(Request $request)
{
$domain = .. get domain with language and some logic and cache because of multiple domains ..
$request->attributes->add(['domain' => $domain]);
}
}
I do this in a Service Provider, because then I can already use it in other Service Providers like my ViewComposerServiceProvider, where I compose some stuff for the Views. I'm able to access $domain everywhere like this:
$this->domain = $request->attributes->get('domain');
It works great. BUT not in testing. When I want to access $domain in a Unit Test in a middleware the $request->attributes are empty (In UnitTests as in DuskTests either).
It looks like the testing environment uses a different Request Lifecycle? If yes, what else is different in the testing environment?
What am I doing wrong?
-- Edit --
Test Example:
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
TestCase uses trait MakesHttpRequests which has method call. When you use get method in your tests, it's simply a shortcut to this.
In your test you can use it like this:
$this->call('GET', '/url/here', $yourRequestParametersHere);
To implement the security system in my application. I want to keep the history of visitors and users IP addresses and browser identity. In my application, visitors are those who do not sign up with my application and can access certain parts of my application.
What is the best way to implement it in laravel 5? Is there method like boot() which could serve my requirement.
You can use middleware.
Create middleware:
artisan make:middleware StoreUserInfo
This command will create class App\Http\Middleware\StoreUserInfo
Edit StoreUserInfo class:
<?php
namespace App\Http\Middleware;
use DB;
use Session;
class StoreUserInfo
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (!Session::get('user-info-stored')) {
// save IP address and browser to DB
// set session flag to prevent DB duplicates
Session::put('user-info-stored', true);
}
return $next($request);
}
}
Keep in mind that your software should be privacy-compliant. In some countries like Germany it's illegal to store user's IP address. Read more: https://github.com/piwik/piwik/issues/692