├── LICENSE.md ├── README.md ├── composer.json ├── config └── laravelcloudflare.php └── src ├── CloudflareProxies.php ├── Commands ├── Reload.php └── View.php ├── Facades └── CloudflareProxies.php ├── Http └── Middleware │ └── TrustProxies.php ├── LaravelCloudflare.php └── TrustedProxyServiceProvider.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Ogün Karakuş 4 | (c) 2018 Alexis Saettler 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trust Cloudflare's Proxies for Laravel 2 | 3 | Add Cloudflare ip addresses to trusted proxies for Laravel. 4 | 5 | [![Latest Version](https://img.shields.io/packagist/v/monicahq/laravel-cloudflare?style=flat-square&label=Latest%20Version)](https://github.com/monicahq/laravel-cloudflare/releases) 6 | [![Downloads](https://img.shields.io/packagist/dt/monicahq/laravel-cloudflare?style=flat-square&label=Downloads)](https://packagist.org/packages/monicahq/laravel-cloudflare) 7 | [![Workflow Status](https://img.shields.io/github/workflow/status/monicahq/laravel-cloudflare/Unit%20tests?style=flat-square&label=Workflow%20Status)](https://github.com/monicahq/laravel-cloudflare/actions?query=branch%3Amain) 8 | [![Quality Gate](https://img.shields.io/sonar/quality_gate/monicahq_laravel-cloudflare?server=https%3A%2F%2Fsonarcloud.io&style=flat-square&label=Quality%20Gate)](https://sonarcloud.io/dashboard?id=monicahq_laravel-cloudflare) 9 | [![Coverage Status](https://img.shields.io/sonar/coverage/monicahq_laravel-cloudflare?server=https%3A%2F%2Fsonarcloud.io&style=flat-square&label=Coverage%20Status)](https://sonarcloud.io/dashboard?id=monicahq_laravel-cloudflare) 10 | 11 | 12 | # Installation 13 | 14 | 1. Install package using composer: 15 | ``` 16 | composer require monicahq/laravel-cloudflare 17 | ``` 18 | 19 | 20 | 1. Configure Middleware 21 | 22 | Replace `TrustProxies` middleware in your `bootstrap/app.php` file: 23 | 24 | ```php 25 | ->withMiddleware(function (Middleware $middleware) { 26 | $middleware->replace( 27 | \Illuminate\Http\Middleware\TrustProxies::class, 28 | \Monicahq\Cloudflare\Http\Middleware\TrustProxies::class 29 | ); 30 | }) 31 | ``` 32 | 33 | ## Custom proxies callback 34 | 35 | You can define your own proxies callback by calling the `LaravelCloudflare::getProxiesUsing()` to change the behavior of the `LaravelCloudflare::getProxies()` method. 36 | This method should typically be called in the `boot` method of your `AppServiceProvider` class: 37 | 38 | ```php 39 | use Illuminate\Support\ServiceProvider; 40 | use Monicahq\Cloudflare\LaravelCloudflare; 41 | use Monicahq\Cloudflare\Facades\CloudflareProxies; 42 | 43 | class AppServiceProvider extends ServiceProvider 44 | { 45 | /** 46 | * Bootstrap any application services. 47 | */ 48 | public function boot(): void 49 | { 50 | LaravelCloudflare::getProxiesUsing(fn() => CloudflareProxies::load()); 51 | } 52 | } 53 | ``` 54 | 55 | 56 | # How it works 57 | 58 | The middleware uses [Illuminate\Http\Middleware\TrustProxies](https://github.com/laravel/framework/blob/8.x/src/Illuminate/Http/Middleware/TrustProxies.php) as a backend. 59 | 60 | When the cloudflare ips are detected, they are used as trusted proxies. 61 | 62 | 63 | # Refreshing the Cache 64 | 65 | This package retrieves Cloudflare's IP blocks, and stores them in cache. 66 | When request comes, the middleware will get Cloudflare's IP blocks from cache, and load them as trusted proxies. 67 | 68 | You'll need to refresh the cloudflare cache regularely to always have up to date proxy. 69 | 70 | Use the `cloudflare:reload` artisan command to refresh the IP blocks: 71 | 72 | ```sh 73 | php artisan cloudflare:reload 74 | ``` 75 | 76 | ## Suggestion: add the reload command in the schedule 77 | 78 | Add a schedule to your `routes/console.php` file to refresh the cache, for instance: 79 | 80 | ```php 81 | use Illuminate\Support\Facades\Schedule; 82 | 83 | Schedule::command('cloudflare:reload')->daily(); 84 | ``` 85 | 86 | # View current Cloudflare's IP blocks 87 | 88 | You can use the `cloudflare:view` artisan command to see the cached IP blocks: 89 | 90 | ```sh 91 | php artisan cloudflare:view 92 | ``` 93 | 94 | # Option: publish the package config file 95 | 96 | If you want, you can publish the package config file to `config/laravelcloudflare.php`: 97 | 98 | ```sh 99 | php artisan vendor:publish --provider="Monicahq\Cloudflare\TrustedProxyServiceProvider" 100 | ``` 101 | 102 | This file contains some configurations, but you may not need to change them normally. 103 | 104 | ## Running tests for your package 105 | 106 | When running tests for your package, you generally don't need to get Cloudflare's proxy addresses. 107 | You can deactivate the Laravel Cloudflare middleware by adding the following environment variable in 108 | your `.env` or `phpunit.xml` file: 109 | 110 | ``` 111 | LARAVEL_CLOUDFLARE_ENABLED=false 112 | ``` 113 | 114 | 115 | # Compatibility 116 | 117 | | Laravel | [monicahq/laravel-cloudflare](https://github.com/monicahq/laravel-cloudflare) | 118 | |----------|----------| 119 | | 5.x-6.x | <= 1.8.0 | 120 | | 7.x-8.53 | 2.0.0 | 121 | | >= 8.54 | >= 3.0.0 | 122 | 123 | 124 | # Citations 125 | 126 | This package was inspired by [lukasz-adamski/laravel-cloudflare](https://github.com/lukasz-adamski/laravel-cloudflare) and forked from [ogunkarakus/laravel-cloudflare](https://github.com/ogunkarakus/laravel-cloudflare). 127 | 128 | 129 | # License 130 | 131 | Author: [Alexis Saettler](https://github.com/asbiin) 132 | 133 | This project is part of [MonicaHQ](https://github.com/monicahq/). 134 | 135 | Copyright © 2019–2024. 136 | 137 | Licensed under the MIT License. [View license](LICENSE.md). 138 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monicahq/laravel-cloudflare", 3 | "description": "Add Cloudflare ip addresses to trusted proxies for Laravel.", 4 | "keywords": [ 5 | "laravel", 6 | "php", 7 | "cloudflare", 8 | "proxies" 9 | ], 10 | "type": "library", 11 | "license": "MIT", 12 | "support": { 13 | "issues": "https://github.com/monicahq/laravel-cloudflare/issues", 14 | "source": "https://github.com/monicahq/laravel-cloudflare" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Alexis Saettler", 19 | "email": "alexis@saettler.org" 20 | } 21 | ], 22 | "require": { 23 | "php": "^7.4 || ^8.0", 24 | "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" 25 | }, 26 | "require-dev": { 27 | "brainmaestro/composer-git-hooks": "^3.0", 28 | "guzzlehttp/guzzle": "^6.3 || ^7.0", 29 | "larastan/larastan": "^1.0 || ^2.4 || ^3.0", 30 | "laravel/pint": "^1.15", 31 | "mockery/mockery": "^1.4", 32 | "ocramius/package-versions": "^1.5 || ^2.1", 33 | "orchestra/testbench": "^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0", 34 | "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", 35 | "phpstan/phpstan-phpunit": "^1.0 || ^2.0", 36 | "phpstan/phpstan-strict-rules": "^1.0 || ^2.0", 37 | "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0", 38 | "vimeo/psalm": "^4.0 || ^5.6 || ^6.0" 39 | }, 40 | "autoload": { 41 | "psr-4": { 42 | "Monicahq\\Cloudflare\\": "src" 43 | } 44 | }, 45 | "autoload-dev": { 46 | "psr-4": { 47 | "Monicahq\\Cloudflare\\Tests\\": "tests" 48 | } 49 | }, 50 | "extra": { 51 | "laravel": { 52 | "providers": [ 53 | "Monicahq\\Cloudflare\\TrustedProxyServiceProvider" 54 | ] 55 | }, 56 | "hooks": { 57 | "config": { 58 | "stop-on-failure": [ 59 | "pre-commit" 60 | ] 61 | }, 62 | "pre-commit": [ 63 | "files=$(git diff --staged --name-only);\"$(dirname \"$0\")/../../vendor/bin/pint\" $files; git add $files" 64 | ] 65 | } 66 | }, 67 | "scripts": { 68 | "cghooks": "vendor/bin/cghooks", 69 | "post-install-cmd": "cghooks add --ignore-lock", 70 | "post-update-cmd": "cghooks update" 71 | }, 72 | "suggest": { 73 | "guzzlehttp/guzzle": "Required to get cloudflares ip addresses (^6.5.5|^7.0)." 74 | }, 75 | "config": { 76 | "sort-packages": true, 77 | "allow-plugins": { 78 | "composer/package-versions-deprecated": true 79 | } 80 | }, 81 | "minimum-stability": "dev", 82 | "prefer-stable": true 83 | } 84 | -------------------------------------------------------------------------------- /config/laravelcloudflare.php: -------------------------------------------------------------------------------- 1 | (bool) env('LARAVEL_CLOUDFLARE_ENABLED', true), 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Replace current remote addr with Cf-Connecting-Ip header 20 | |-------------------------------------------------------------------------- 21 | | 22 | | This replace the request ip with the value of the Cf-Connecting-Ip header. 23 | | 24 | */ 25 | 26 | 'replace_ip' => (bool) env('LARAVEL_CLOUDFLARE_REPLACE_IP', false), 27 | 28 | /* 29 | |-------------------------------------------------------------------------- 30 | | Name of the cache to store values of the proxies 31 | |-------------------------------------------------------------------------- 32 | | 33 | | This value is the key used in the cache (table, redis, etc.) to store the 34 | | values. 35 | | 36 | */ 37 | 38 | 'cache' => 'cloudflare.proxies', 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Cloudflare main url 43 | |-------------------------------------------------------------------------- 44 | | 45 | | This is the url for the cloudflare api. 46 | | 47 | */ 48 | 49 | 'url' => 'https://www.cloudflare.com', 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Cloudflare uri for ipv4 ips response 54 | |-------------------------------------------------------------------------- 55 | | 56 | | This is the path to get the values of ipv4 ips from Cloudflare. 57 | | 58 | */ 59 | 60 | 'ipv4-path' => 'ips-v4', 61 | 62 | /* 63 | |-------------------------------------------------------------------------- 64 | | Cloudflare uri for ipv6 ips response 65 | |-------------------------------------------------------------------------- 66 | | 67 | | This is the path to get the values of ipv6 ips from Cloudflare. 68 | | 69 | */ 70 | 71 | 'ipv6-path' => 'ips-v6', 72 | 73 | ]; 74 | -------------------------------------------------------------------------------- /src/CloudflareProxies.php: -------------------------------------------------------------------------------- 1 | retrieve($this->config->get('laravelcloudflare.ipv4-path')); 51 | } 52 | 53 | if ((bool) ($type & self::IP_VERSION_6)) { 54 | $proxies6 = $this->retrieve($this->config->get('laravelcloudflare.ipv6-path')); 55 | $proxies = array_merge($proxies, $proxies6); 56 | } 57 | 58 | return $proxies; 59 | } 60 | 61 | /** 62 | * Retrieve requested proxy list by name. 63 | */ 64 | protected function retrieve(string $name): array 65 | { 66 | try { 67 | $url = Str::of($this->config->get('laravelcloudflare.url', 'https://www.cloudflare.com/'))->finish('/').$name; 68 | 69 | $response = Http::get($url)->throw(); 70 | } catch (\Exception $e) { 71 | throw new UnexpectedValueException('Failed to load trust proxies from Cloudflare server.', 1, $e); 72 | } 73 | 74 | return array_filter(explode("\n", $response->body())); // @phpstan-ignore arrayFilter.strict 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Commands/Reload.php: -------------------------------------------------------------------------------- 1 | get('laravelcloudflare.enabled')) { 32 | return; 33 | } 34 | 35 | $proxies = LaravelCloudflare::getProxies(); 36 | 37 | $cache->store()->forever($config->get('laravelcloudflare.cache'), $proxies); 38 | 39 | $this->info('Cloudflare\'s IP blocks have been reloaded.'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Commands/View.php: -------------------------------------------------------------------------------- 1 | store()->get($config->get('laravelcloudflare.cache'), []); 31 | 32 | $rows = array_map(fn ($value): array => [$value], $proxies); 33 | 34 | $this->table(['Address'], $rows); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Facades/CloudflareProxies.php: -------------------------------------------------------------------------------- 1 | setRemoteAddr($request); 23 | } 24 | 25 | return parent::handle($request, $next); 26 | } 27 | 28 | /** 29 | * Set RemoteAddr server value using Cf-Connecting-Ip header. 30 | */ 31 | protected function setRemoteAddr(Request $request): void 32 | { 33 | if (($ip = $request->header('Cf-Connecting-Ip')) !== null) { 34 | $request->server->set('REMOTE_ADDR', $ip); 35 | } 36 | } 37 | 38 | /** 39 | * Sets the trusted proxies on the request. 40 | */ 41 | protected function setTrustedProxyIpAddresses(Request $request): void 42 | { 43 | if ((bool) Config::get('laravelcloudflare.enabled')) { 44 | $this->setTrustedProxyCloudflare($request); 45 | } 46 | 47 | parent::setTrustedProxyIpAddresses($request); 48 | } 49 | 50 | /** 51 | * Sets the trusted proxies on the request to the value of Cloudflare ips. 52 | */ 53 | protected function setTrustedProxyCloudflare(Request $request): void 54 | { 55 | $cacheKey = Config::get('laravelcloudflare.cache'); 56 | $cachedProxies = Cache::rememberForever($cacheKey, fn () => LaravelCloudflare::getProxies()); 57 | 58 | if (count($cachedProxies) > 0) { 59 | $this->proxies = array_merge((array) $this->proxies, $cachedProxies); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/LaravelCloudflare.php: -------------------------------------------------------------------------------- 1 | registerPublishing(); 15 | } 16 | 17 | /** 18 | * Register the package's publishable resources. 19 | */ 20 | private function registerPublishing(): void 21 | { 22 | if ($this->app->runningInConsole()) { 23 | $this->publishes([ 24 | __DIR__.'/../config/laravelcloudflare.php' => config_path('laravelcloudflare.php'), 25 | ], 'laravelcloudflare-config'); 26 | } 27 | } 28 | 29 | /** 30 | * Register any package services. 31 | */ 32 | #[\Override] 33 | public function register(): void 34 | { 35 | $this->mergeConfigFrom( 36 | __DIR__.'/../config/laravelcloudflare.php', 'laravelcloudflare' 37 | ); 38 | $this->app->singleton(\Monicahq\Cloudflare\Facades\CloudflareProxies::class, \Monicahq\Cloudflare\CloudflareProxies::class); 39 | 40 | if ($this->app->runningInConsole()) { 41 | $this->commands([ 42 | Commands\Reload::class, 43 | Commands\View::class, 44 | ]); 45 | } 46 | } 47 | } 48 | --------------------------------------------------------------------------------