Install from composer
composer require lyrasoft/firewall
Then copy files to project
php windwalker pkg:install lyrasoft/firewall -t routes -t migrations
Add this line to admin & front middleware if you don't want to override languages:
$this->lang->loadAllFromVendor('lyrasoft/firewall', 'ini');
// OR
$this->lang->loadAllFromVendor(\Lyrasoft\Firewall\FirewallPackage::class, 'ini');
Or run this command to copy languages files:
php windwalker pkg:install lyrasoft/firewall -t lang
Edit resources/menu/admin/sidemenu.menu.php
$menu->link($this->trans('unicorn.title.grid', title: $this->trans('firewall.redirect.title')))
->to($nav->to('redirect_list')->var('type', 'main'))
->icon('fal fa-angles-right');
$menu->link($this->trans('unicorn.title.grid', title: $this->trans('firewall.ip.rule.title')))
->to($nav->to('ip_rule_list')->var('type', 'main'))
->icon('fal fa-network-wired');
Add RedirectMiddleware
to etc/app/main.php
use Lyrasoft\Firewall\Middleware\RedirectMiddleware;
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
ignores: [
'admin/*'
]
),
// ...
],
Now you can add redirect records at admin:
- Add
/
at start, the path will compare from site base root (not domain root). - If you enable the
Regex
:- Add
*
will compare a path segment with any string. - Add
**
will compare cross-segments. - You can add custom regex rules, like:
/foo/(\d+)
- Add
Thr dest path can be relative path: foo/bar
or full URL: https://simular.co/foo/bar
.
If you enabne the Regex
, you may use variables start with $
to insert matched string. For example, a foo/*/edit/(\d+)
, can redirect to new/path/$1/edit/$2
Only 404
: Only redirect if a page is 404, if page URL exists, won't redirect.Handle Locale
: If this site is multi-language, this params will auto auto detect the starting ;anguage prefix and auto add it to dest path, you may use{lang}
in dest path to custom set lang alias position.
Redirect tables has type
colimn, you can use admin/redirect/list/{type}
to manage different types.
And if you want to choose types for middleware, you can do this:
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
type: 'other_type',
ignores: [
'admin/*'
]
),
// ...
],
The type supports string|Enum|array|null|false
, if you send NULL
into it, means all redirect records. If you send FALSE
, means don't use DB recods.
You can use custom redirect list, custom list will auto-enable the regex:
This settings will merge DB list and custom list.
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
type: 'flower',
list: [
'foo/bar' => 'hello/world',
'foo/yoo/*' => 'hello/mountain/$1',
],
ignores: [
'admin/*'
]
),
// ...
],
This settings will disable DB list and only use custom list.
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
type: false,
list: [
'foo/bar' => 'hello/world',
'foo/yoo/*' => 'hello/mountain/$1',
],
ignores: [
'admin/*'
]
),
// ...
],
Custom List can use Closure to generate list:
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
// ...
list: raw(function (FooService $fooService) {
return ...;
});
),
// ...
],
The custom list redirect status code default is 301
, if you want to use other status, set it to
REDIRECT_DEFAULT_STATUS
env varialbe.
If there has some reason you can not wait RedirectResponse
return, you may use instant redirect:
// ...
'middlewares' => [
\Windwalker\DI\create(
RedirectMiddleware::class,
// ...
instantRedirect: true,
),
// ...
],
If you wanr to disable this middleware in debug mode, add this options:
\Windwalker\DI\create(
RedirectMiddleware::class,
enabled: !WINDWALKER_DEBUG
),
Add afterHit
hook that you can do somthing or log if redirect hit.
\Windwalker\DI\create(
RedirectMiddleware::class,
afterHit: raw(function (string $dest, \Redirect $redirect) {
\Windwalker\Core\Manager\Logger::info('Redirect to: ' . $dest);
})
),
To enable IP Rules, add FirewallMiddleware
to front.route.php
use Lyrasoft\Firewall\Middleware\FirewallMiddleware;
// ...
->middleware(
FirewallMiddleware::class,
)
// ...
Select Allow or Block, and enter the IP Range format:
The supported formats:
Type | Syntax | Details |
---|---|---|
IPV6 | ::1 |
Short notation |
IPV4 | 192.168.0.1 |
|
Range | 192.168.0.0-192.168.1.60 |
Includes all IPs from 192.168.0.0 to 192.168.0.255 and from 192.168.1.0 to 198.168.1.60 |
Wild card | 192.168.0.* |
IPs starting with 192.168.0 Same as IP Range 192.168.0.0-192.168.0.255 |
Subnet mask | 192.168.0.0/255.255.255.0 |
IPs starting with 192.168.0 Same as 192.168.0.0-192.168.0.255 and 192.168.0.* |
CIDR Mask | 192.168.0.0/24 |
IPs starting with 192.168.0 Same as 192.168.0.0-192.168.0.255 and 192.168.0.* and 192.168.0.0/255.255.255.0 |
We use mlocati/ip-lib as IP Range parser.
You can also access different type from ip-rule/list/{type}
.
And set type name to middleware
->middleware(
FirewallMiddleware::class,
type: 'foo'
)
type can also supports string, array and enum. Use NULL
to select all, FALSE
to disable DB.
If you want to manually set ip list, FirewallMiddleware
custom list must use 2 lists, allowList
and blockList
.
->middleware(
FirewallMiddleware::class,
type: false,
allowList: [
'0.0.0.0',
'144.122.*.*',
],
blockList: [
'165.2.90.45',
'222.44.55.66',
],
)
If you wanr to disable this middleware in debug mode, add this options:
\Windwalker\DI\create(
FirewallMiddleware::class,
enabled: !WINDWALKER_DEBUG
),
Add afterHit
hook that you can do somthing or log if an IP was be blocked.
\Windwalker\DI\create(
FirewallMiddleware::class,
afterHit: raw(function (AppRequest $appRequest) {
\Windwalker\Core\Manager\Logger::info('Attack from: ' . $appRequest->getClientIp());
})
),
Both middlewares has a cacheTtl
param, default is 3600
seconds.
\Windwalker\DI\create(
FirewallMiddleware::class,
cacheTtl: 3600
),
Everytime you edit Redirect
or IpRule
will auto clear all caches.
The cache files is located at caches/firewall/
, and you can add firewall
to clear cache command in composer.json
"post-autoload-dump": [
...
"php windwalker cache:clear renderer html firewall" <-- Add firewall
],
Cache will disable in debug mode or when ttl
set to 0
.