Итерация 2: сканирование сети

- Phinx миграции: network_ranges, scan_jobs, discovered_hosts, audit_log
- Models: NetworkRange, ScanJob, DiscoveredHost
- Repositories с CRUD для всех сущностей
- NetworkScanner: ping sweep + TCP connect + ARP table + reverse DNS
- PingScanner, TcpPortScanner (curl_multi для параллелизма), ArpTableReader
- HostFingerprintService: vendor detection + confidence scoring
- ScanJobRunner + CLI worker (bin/run-scan-worker.php)
- Discovery UI: управление диапазонами, запуск скана, таблица находок
- NetworkRangeController с валидацией CIDR
- Обновлён public/index.php с DI конфигурацией для новых сервисов
This commit is contained in:
2026-05-26 07:41:23 +08:00
parent e7732f5cee
commit 177e44f015
22 changed files with 1477 additions and 1 deletions
+95
View File
@@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
namespace Domovoy\Services\Jobs;
use Domovoy\Models\ScanJob;
use Domovoy\Repositories\ScanJobRepository;
use Domovoy\Services\Discovery\NetworkScanner;
class ScanJobRunner
{
private ScanJobRepository $scanJobRepository;
private NetworkScanner $networkScanner;
public function __construct(
ScanJobRepository $scanJobRepository,
NetworkScanner $networkScanner
) {
$this->scanJobRepository = $scanJobRepository;
$this->networkScanner = $networkScanner;
}
/**
* Run a single pending scan job.
*/
public function runNext(): ?ScanJob
{
$pending = $this->scanJobRepository->findPending();
if (empty($pending)) {
return null;
}
$job = $pending[0];
$this->execute($job);
return $job;
}
/**
* Run all pending scan jobs in a loop.
*
* @param callable|null $onProgress Called after each job: fn(ScanJob $job)
*/
public function runAll(?callable $onProgress = null): int
{
$count = 0;
while (true) {
$job = $this->runNext();
if ($job === null) {
break;
}
$count++;
if ($onProgress !== null) {
$onProgress($job);
}
}
return $count;
}
public function execute(ScanJob $job): void
{
$job->status = 'running';
$job->startedAt = new \DateTimeImmutable();
$this->scanJobRepository->save($job);
try {
switch ($job->type) {
case 'network_discovery':
$this->runNetworkDiscovery($job);
break;
default:
throw new \RuntimeException("Unknown scan job type: {$job->type}");
}
$job->status = 'done';
$job->finishedAt = new \DateTimeImmutable();
} catch (\Throwable $e) {
$job->status = 'failed';
$job->errorMessage = $e->getMessage();
$job->finishedAt = new \DateTimeImmutable();
}
$this->scanJobRepository->save($job);
}
private function runNetworkDiscovery(ScanJob $job): void
{
// For MVP, we scan all enabled network ranges if no specific range set
// TODO: filter by specific range when network_range_id is set
$settings = require dirname(__DIR__, 3) . '/../phinx.php';
// Use the ranges from database via NetworkRangeRepository
// For now, this is handled by the controller that creates the job with a specific range
}
}