Skip to content

Commit c122150

Browse files
author
root
committed
Update mikrotik-nat-sync plugin files
1 parent 308d646 commit c122150

5 files changed

Lines changed: 228 additions & 1 deletion

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
return [
4+
// Config values for MikroTik NAT Sync
5+
];

mikrotik-nat-sync/plugin.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
"status": "enabled",
1717
"status_message": null
1818
}
19-
}
19+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
namespace Avalon\MikroTikNATSync\Console\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Support\Facades\DB;
7+
use Illuminate\Support\Facades\Http;
8+
9+
class SyncMikrotikCommand extends Command
10+
{
11+
protected $signature = 'mikrotik:sync';
12+
protected $description = 'Синхронізація правил NAT з MikroTik з перевіркою заборонених портів';
13+
14+
public function handle()
15+
{
16+
$this->info('Starting MikroTik Sync...');
17+
18+
$mk_ip = str_replace(['http://', 'https://'], '', env('MIKROTIK_IP'));
19+
$mk_port = env('MIKROTIK_PORT', '9080');
20+
$mk_user = env('MIKROTIK_USER');
21+
$mk_pass = env('MIKROTIK_PASS');
22+
$mk_interface = env('MIKROTIK_INTERFACE', 'ether1');
23+
24+
// Отримуємо список заборонених портів та перетворюємо в масив
25+
$forbidden_string = env('MIKROTIK_FORBIDDEN_PORTS', '');
26+
$forbidden_ports = array_map('trim', explode(',', $forbidden_string));
27+
28+
if (!$mk_ip || !$mk_user || !$mk_pass) {
29+
$this->error('Налаштування MikroTik не заповнені!');
30+
return;
31+
}
32+
33+
if (str_contains($mk_ip, ':')) {
34+
$url = "http://" . $mk_ip . "/rest/ip/firewall/nat";
35+
} else {
36+
$url = "http://" . $mk_ip . ":" . $mk_port . "/rest/ip/firewall/nat";
37+
}
38+
39+
$active_servers = DB::table('servers')
40+
->join('allocations', 'allocations.server_id', '=', 'servers.id')
41+
->select('servers.uuid', 'servers.name', 'allocations.ip', 'allocations.port')
42+
->get();
43+
44+
$whitelist = [];
45+
foreach ($active_servers as $srv) {
46+
// ПЕРЕВІРКА: чи порт не в списку заборонених
47+
if (in_array((string)$srv->port, $forbidden_ports)) {
48+
$this->warn("Порт {$srv->port} для сервера {$srv->name} ЗАБОРОНЕНИЙ. Пропускаємо.");
49+
continue;
50+
}
51+
52+
$target_ip = ($srv->ip == '0.0.0.0') ? '192.168.70.231' : $srv->ip;
53+
$whitelist[$srv->port . '-tcp'] = ['ip' => $target_ip, 'name' => $srv->name, 'uuid' => $srv->uuid];
54+
$whitelist[$srv->port . '-udp'] = ['ip' => $target_ip, 'name' => $srv->name, 'uuid' => $srv->uuid];
55+
}
56+
57+
try {
58+
$response = Http::withBasicAuth($mk_user, $mk_pass)->timeout(10)->get($url);
59+
if (!$response->successful()) {
60+
$this->error('Помилка API: ' . $response->body());
61+
return;
62+
}
63+
64+
$existing_rules = [];
65+
foreach ($response->json() as $rule) {
66+
if (isset($rule['comment']) && str_contains($rule['comment'], 'Pelican:')) {
67+
$key = ($rule['dst-port'] ?? '') . '-' . ($rule['protocol'] ?? 'tcp');
68+
$existing_rules[$key] = $rule['.id'];
69+
}
70+
}
71+
72+
foreach ($existing_rules as $key => $id) {
73+
if (!isset($whitelist[$key])) {
74+
$this->warn("Deleting rule: $key");
75+
Http::withBasicAuth($mk_user, $mk_pass)->delete("$url/$id");
76+
}
77+
}
78+
79+
foreach ($whitelist as $key => $info) {
80+
if (!isset($existing_rules[$key])) {
81+
[$port, $proto] = explode('-', $key);
82+
$this->info("Adding rule: $key for {$info['name']}");
83+
Http::withBasicAuth($mk_user, $mk_pass)->put($url, [
84+
'chain' => 'dstnat',
85+
'action' => 'dst-nat',
86+
'to-addresses' => $info['ip'],
87+
'to-ports' => (string)$port,
88+
'protocol' => $proto,
89+
'dst-port' => (string)$port,
90+
'in-interface' => $mk_interface,
91+
'comment' => "Pelican: {$info['name']} ({$info['uuid']})"
92+
]);
93+
}
94+
}
95+
$this->info('Sync Complete.');
96+
} catch (\Exception $e) {
97+
$this->error('Error: ' . $e->getMessage());
98+
}
99+
}
100+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
namespace Avalon\MikroTikNATSync;
4+
5+
use Filament\Contracts\Plugin as FilamentPlugin;
6+
use Filament\Panel;
7+
use Filament\Forms\Components\TextInput;
8+
use Filament\Forms\Components\Select;
9+
use Filament\Notifications\Notification;
10+
use App\Contracts\Plugins\HasPluginSettings;
11+
use App\Traits\EnvironmentWriterTrait;
12+
use Illuminate\Console\Scheduling\Schedule;
13+
14+
class MikroTikNATSyncPlugin implements FilamentPlugin, HasPluginSettings
15+
{
16+
use EnvironmentWriterTrait;
17+
18+
public function getId(): string
19+
{
20+
return 'mikrotik-nat-sync';
21+
}
22+
23+
public function register(Panel $panel): void
24+
{
25+
//
26+
}
27+
28+
public function boot(Panel $panel): void
29+
{
30+
if (app()->runningInConsole()) {
31+
$this->commands([
32+
\Avalon\MikroTikNATSync\Console\Commands\SyncMikrotikCommand::class,
33+
]);
34+
}
35+
36+
app()->booted(function () {
37+
$schedule = app(Schedule::class);
38+
$interval = env('MIKROTIK_SYNC_INTERVAL', 'everyFiveMinutes');
39+
40+
$schedule->command('mikrotik:sync')
41+
->{$interval}()
42+
->withoutOverlapping();
43+
});
44+
}
45+
46+
public function getSettingsForm(): array
47+
{
48+
return [
49+
TextInput::make('mk_ip')
50+
->label('IP MikroTik')
51+
->default(env('MIKROTIK_IP'))
52+
->required(),
53+
TextInput::make('mk_port')
54+
->label('Порт REST API')
55+
->default(env('MIKROTIK_PORT', '9080'))
56+
->required(),
57+
TextInput::make('mk_user')
58+
->label('Користувач')
59+
->default(env('MIKROTIK_USER'))
60+
->required(),
61+
TextInput::make('mk_pass')
62+
->label('Пароль')
63+
->password()
64+
->revealable()
65+
->default(env('MIKROTIK_PASS')),
66+
TextInput::make('mk_interface')
67+
->label('Вхідний інтерфейс (WAN)')
68+
->default(env('MIKROTIK_INTERFACE', 'ether1'))
69+
->required(),
70+
TextInput::make('mk_forbidden_ports')
71+
->label('Заборонені порти (через кому)')
72+
->placeholder('22, 80, 443, 3306')
73+
->default(env('MIKROTIK_FORBIDDEN_PORTS')),
74+
Select::make('mk_interval')
75+
->label('Інтервал синхронізації')
76+
->options([
77+
'everyMinute' => 'Щохвилини',
78+
'everyFiveMinutes' => 'Кожні 5 хвилин',
79+
'everyTenMinutes' => 'Кожні 10 хвилин',
80+
'hourly' => 'Щогодини',
81+
])
82+
->default(env('MIKROTIK_SYNC_INTERVAL', 'everyFiveMinutes'))
83+
->required(),
84+
];
85+
}
86+
87+
public function saveSettings(array $data): void
88+
{
89+
$this->writeToEnvironment([
90+
'MIKROTIK_IP' => $data['mk_ip'],
91+
'MIKROTIK_PORT' => $data['mk_port'],
92+
'MIKROTIK_USER' => $data['mk_user'],
93+
'MIKROTIK_PASS' => $data['mk_pass'],
94+
'MIKROTIK_INTERFACE' => $data['mk_interface'],
95+
'MIKROTIK_SYNC_INTERVAL' => $data['mk_interval'],
96+
'MIKROTIK_FORBIDDEN_PORTS' => $data['mk_forbidden_ports'],
97+
]);
98+
99+
Notification::make()
100+
->title('Налаштування збережено')
101+
->success()
102+
->send();
103+
}
104+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Avalon\MikroTikNATSync\Providers;
4+
5+
use Illuminate\Support\ServiceProvider;
6+
7+
class MikroTikNATSyncPluginProvider extends ServiceProvider
8+
{
9+
public function register(): void
10+
{
11+
//
12+
}
13+
14+
public function boot(): void
15+
{
16+
//
17+
}
18+
}

0 commit comments

Comments
 (0)