diff --git a/.env.example b/.env.example index d50429c..340f59b 100644 --- a/.env.example +++ b/.env.example @@ -59,7 +59,7 @@ FREERADIUS_MACADDR_CASE=lower CODIGO_ACESSO='ABCD-1234' SOMENTE_SENHAUNICA=false -USP_THEME_SKIN=uspdev +USP_THEME_SKIN=fflch # Dusk APP_URL=http://copaco diff --git a/app/Http/Controllers/Api/KeaController.php b/app/Http/Controllers/Api/KeaController.php new file mode 100644 index 0000000..c669f21 --- /dev/null +++ b/app/Http/Controllers/Api/KeaController.php @@ -0,0 +1,46 @@ +keaService = $keaService; + } + + public function kea(Request $request) + { + if ($request->consumer_deploy_key != config('copaco.consumer_deploy_key')) { + return response()->json(['error' => 'Unauthorized action.'], 403); + } + + try { + $config = $this->keaService->generateKeaConfig(); + return response()->json($config, 200, ['Content-Type' => 'application/json']); + } catch (\Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } + + + public function uniquekeab(Request $request) + { + if ($request->consumer_deploy_key != config('copaco.consumer_deploy_key')) { + return response()->json(['error' => 'Unauthorized action.'], 403); + } + + try { + $config = $this->keaService->generateUniqueKeaConfig(); + return response()->json($config, 200, ['Content-Type' => 'application/json']); + } catch (\Exception $e) { + return response()->json(['error' => $e->getMessage()], 500); + } + } +} \ No newline at end of file diff --git a/app/Http/Services/KeaService.php b/app/Http/Services/KeaService.php new file mode 100644 index 0000000..1d2f08d --- /dev/null +++ b/app/Http/Services/KeaService.php @@ -0,0 +1,223 @@ + [ + 'interfaces-config' => [ + 'interfaces' => ['*'], // pode vir de config futuramente + ], + 'lease-database' => [ + 'type' => 'memfile', + 'name' => '/var/lib/kea/kea-leases4.csv', + ], + 'control-socket' => [ + 'socket-type' => 'unix', + 'socket-name' => '/tmp/kea4-ctrl-socket', + ], + 'loggers' => [ + [ + 'name' => 'kea-dhcp4', + 'severity' => 'INFO', + 'output_options' => [ + ['output' => '/var/log/kea/kea-dhcp4.log'], + ], + ], + ], + 'option-data' => [], // será preenchido depois + ], + ]; + } + + public function generateKeaConfig(): array + { + $config = $this->baseConfig(); + $config['Dhcp4']['option-data'] = $this->parseGlobalOptions(); + + $sharedNetworkConfig = Config::where('key', 'shared_network')->first(); + + if (empty($sharedNetworkConfig)) { + $redes = Rede::where('active_dhcp', 1)->get(); + if ($redes->isNotEmpty()) { + $config['Dhcp4']['shared-networks'][] = $this->buildSharedNetwork('default', $redes); + } + } else { + $sharedNetworksList = array_map('trim', explode(',', $sharedNetworkConfig->value)); + if (!in_array('default', $sharedNetworksList)) { + $sharedNetworksList[] = 'default'; + } + foreach ($sharedNetworksList as $sn) { + $redes = Rede::where('shared_network', $sn)->where('active_dhcp', 1)->get(); + if ($redes->isNotEmpty()) { + $config['Dhcp4']['shared-networks'][] = $this->buildSharedNetwork($sn, $redes); + } + } + } + + return $config; + } + + + public function generateUniqueKeaConfig(): array + { + $config = $this->baseConfig(); + $config['Dhcp4']['option-data'] = $this->parseGlobalOptions(); + + $iprede = Config::where('key', 'unique_iprede')->value('value'); + $gateway = Config::where('key', 'unique_gateway')->value('value'); + $cidr = Config::where('key', 'unique_cidr')->value('value'); + + if (!$iprede || !$gateway || !$cidr) { + throw new \Exception('Missing network data for unique configuration'); + } + + $broadcast = NetworkOps::findBroadcast($iprede, $cidr); + $rangeBegin = NetworkOps::findFirstIP($iprede, $cidr); + $rangeEnd = NetworkOps::findLastIP($iprede, $cidr); + + // Opções específicas da subnet + $subnetOptions = [ + ['name' => 'routers', 'data' => $gateway], + ['name' => 'broadcast-address', 'data' => $broadcast], + ]; + + // Reservas para equipamentos (alocação automática) + $reservations = $this->buildReservationsForUnique($iprede, $cidr, $gateway); + + $config['Dhcp4']['subnet4'] = [ + [ + 'subnet' => "{$iprede}/{$cidr}", + 'pools' => [ + ['pool' => "{$rangeBegin} - {$rangeEnd}"], + ], + 'option-data' => $subnetOptions, + 'reservations' => $reservations, + ], + ]; + + return $config; + } + + + private function buildSharedNetwork(string $name, $redes): array + { + $sharedNetwork = [ + 'name' => $name, + 'subnet4' => [], + 'option-data' => [], // opções comuns a todas subnets (se houver) + ]; + + foreach ($redes as $rede) { + $sharedNetwork['subnet4'][] = $this->buildSubnet($rede); + } + + return $sharedNetwork; + } + + private function buildSubnet(Rede $rede): array + { + $subnetCidr = "{$rede->iprede}/{$rede->cidr}"; + $rangeBegin = NetworkOps::findFirstIP($rede->iprede, $rede->cidr); + $rangeEnd = NetworkOps::findLastIP($rede->iprede, $rede->cidr); + $broadcast = NetworkOps::findBroadcast($rede->iprede, $rede->cidr); + + // Opções DHCP para esta subnet + $options = [ + ['name' => 'routers', 'data' => $rede->gateway], + ['name' => 'broadcast-address', 'data' => $broadcast], + ]; + + if (!empty($rede->netbios)) { + $options[] = ['name' => 'netbios-name-servers', 'data' => $rede->netbios]; + } + if (!empty($rede->ntp)) { + $options[] = ['name' => 'ntp-servers', 'data' => $rede->ntp]; + } + if (!empty($rede->dns)) { + $options[] = ['name' => 'domain-name-servers', 'data' => $rede->dns]; + } + if (!empty($rede->ad_domain)) { + $options[] = ['name' => 'domain-name', 'data' => $rede->ad_domain]; + } + + + $reservations = []; + foreach ($rede->equipamentos as $equip) { + if (!empty($equip->macaddress) && !empty($equip->ip)) { + $reservations[] = [ + 'hw-address' => $equip->macaddress, + 'ip-address' => $equip->ip, + ]; + } + } + + return [ + 'subnet' => $subnetCidr, + 'pools' => [ + ['pool' => "{$rangeBegin} - {$rangeEnd}"], + ], + 'option-data' => $options, + 'reservations' => $reservations, + ]; + } + + private function buildReservationsForUnique(string $iprede, string $cidr, string $gateway): array + { + $reservedIpsConfig = Config::where('key', 'ips_reservados')->first(); + $reservedIps = $reservedIpsConfig ? array_map('trim', explode(',', $reservedIpsConfig->value)) : []; + + $equipamentos = Equipamento::all(); + $reservations = []; + $ipsAlocados = $reservedIps; + + foreach ($equipamentos as $equip) { + if (empty($equip->macaddress)) { + continue; + } + $ip = NetworkOps::nextIpAvailable($ipsAlocados, $iprede, $cidr, $gateway); + if ($ip) { + $reservations[] = [ + 'hw-address' => $equip->macaddress, + 'ip-address' => $ip, + ]; + $ipsAlocados[] = $ip; + } + } + + return $reservations; + } + + private function parseGlobalOptions(): array + { + $dhcpGlobal = Config::where('key', 'dhcp_global')->first(); + if (!$dhcpGlobal || empty($dhcpGlobal->value)) { + return []; + } + + $options = []; + $lines = explode("\n", $dhcpGlobal->value); + foreach ($lines as $line) { + if (preg_match('/^\s*option\s+([a-z0-9\-]+)\s+(.+?);\s*$/i', $line, $matches)) { + $options[] = [ + 'name' => $matches[1], + 'data' => trim($matches[2]), + ]; + } + } + return $options; + } +} \ No newline at end of file diff --git a/resources/views/config/index.blade.php b/resources/views/config/index.blade.php index 81736e6..5886370 100644 --- a/resources/views/config/index.blade.php +++ b/resources/views/config/index.blade.php @@ -5,6 +5,26 @@ @stop @section('content') +
+
+ {{csrf_field()}} + + +
+
+ +
+ +
+
+ {{csrf_field()}} + + +
+
+ +
+
{{csrf_field()}} diff --git a/routes/api.php b/routes/api.php index 98b205a..dfe4503 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,7 +2,11 @@ use App\Http\Controllers\Api\DhcpController; use App\Http\Controllers\Api\FreeradiusController; +use App\Http\Controllers\Api\EquipamentoController; +use App\Http\Controllers\Api\KeaController; Route::post('/dhcpd.conf', [DhcpController::class, 'dhcpd']); Route::post('/uniquedhcpd.conf', [DhcpController::class, 'uniquedhcpd']); Route::post('/freeradius/authorize_file', [FreeradiusController::class, 'authorize_file']); +Route::post('/kea', [KeaController::class, 'kea']); +Route::post('/kea/unique', [KeaController::class, 'uniquekeab']); diff --git a/tests/Browser/RedeCrudTest.php b/tests/Browser/RedeCrudTest.php index 2c3b587..a702cf8 100644 --- a/tests/Browser/RedeCrudTest.php +++ b/tests/Browser/RedeCrudTest.php @@ -53,6 +53,12 @@ public function test_rede_crud() ->pause(1000) ->assertSee('Rede Teste Editada'); + // Gerar Keadhcp + $browser->visit("/config") + ->press('Gerar configuração Kea (JSON) - Redes segmentadas') + ->pause(2000) + ->assertSee('141.232.67.1'); + // Delete $browser->visit('/redes') ->click("form[action$='/redes/{$rede->id}'] button.delete-item")