Filter container interfaces from device discovery and policies
Build and Deploy Gateway / build-and-push (push) Successful in 11s
Build and Deploy Gateway / deploy (push) Successful in 14s

This commit is contained in:
2026-08-07 17:16:36 +03:00
parent 53e6cf2146
commit 608f8cfcf2
8 changed files with 68 additions and 16 deletions
@@ -2,6 +2,7 @@ import crypto from 'node:crypto';
import fs from 'node:fs';
import net from 'node:net';
import { HarborError } from '../../shared/errors.js';
import { isDeviceInterface } from '../adapters/neighbors.js';
import { fingerprintDirectDevices } from './devicePolicyService.js';
export const DEVICE_INVENTORY_SCHEMA_VERSION = 2;
@@ -12,7 +13,6 @@ const COUNTER_PATTERN = /^\d+$/;
const DEVICE_ID_PATTERN = /^dev_[a-f0-9]{16}$/;
const MAC_PATTERN = /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/;
const FINGERPRINT_PATTERN = /^[a-f0-9]{64}$/;
const INTERFACE_PATTERN = /^[a-z0-9_.:-]{1,15}$/i;
const POLICY_MODES = new Set(['vpn', 'direct']);
const POLICY_STATUSES = new Set(['applied', 'applying', 'pending', 'failed']);
const PROXY_RECOVERY_ERROR = 'Повреждённый proxy traffic checkpoint восстановлен из корректных данных';
@@ -214,7 +214,9 @@ export function migrateDeviceInventoryState(value) {
const traffic = state.traffic && typeof state.traffic === 'object' && !Array.isArray(state.traffic)
? state.traffic
: {};
const devices = Array.isArray(state.devices) ? state.devices : [];
const devices = Array.isArray(state.devices)
? state.devices.filter((device) => isDeviceInterface(device?.interface))
: [];
const proxyTraffic = normalizeProxyTraffic(traffic.proxy, devices);
const rebaselineMacs = new Set((Array.isArray(traffic.rebaselineMacs) ? traffic.rebaselineMacs : [])
.map(normalizeMac).filter((mac) => MAC_PATTERN.test(mac)));
@@ -314,8 +316,7 @@ export function createDeviceInventoryService({
function policyIdentity(device) {
return device?.pinned && device.confidence !== 'ambiguous'
&& net.isIPv4(String(device.ip || '')) && MAC_PATTERN.test(device.mac)
&& INTERFACE_PATTERN.test(String(device.interface || ''))
&& !String(device.interface).startsWith('br-');
&& isDeviceInterface(device.interface);
}
function directDevices(state) {
@@ -523,7 +524,8 @@ export function createDeviceInventoryService({
: null,
]);
const observedAt = result?.observedAt || now().toISOString();
const observations = Array.isArray(result?.observations) ? result.observations : [];
const observations = (Array.isArray(result?.observations) ? result.observations : [])
.filter((observation) => isDeviceInterface(observation?.interface));
const identitiesByMac = new Map();
for (const observation of observations) {
const mac = normalizeMac(observation.mac);
+3 -3
View File
@@ -1,11 +1,11 @@
import crypto from 'node:crypto';
import net from 'node:net';
import { spawnSync } from 'node:child_process';
import { isDeviceInterface } from '../adapters/neighbors.js';
const COMMAND_OPTIONS = { encoding: 'utf8', timeout: 2_000, killSignal: 'SIGKILL' };
const DEVICE_ID_PATTERN = /^dev_[a-f0-9]{16}$/;
const MAC_PATTERN = /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/;
const INTERFACE_PATTERN = /^[a-z0-9_.:-]{1,15}$/i;
const CHAIN_PATTERN = /^[a-z0-9_-]{1,26}$/i;
const MARK_PATTERN = /^(?:0x)?[0-9a-f]+$/i;
const MAX_DEVICES = 512;
@@ -36,8 +36,8 @@ export function normalizeDirectDevices(value) {
};
const tuple = `${normalized.ip}|${normalized.mac}|${normalized.interface}`;
if (!DEVICE_ID_PATTERN.test(normalized.id) || !net.isIPv4(normalized.ip)
|| !MAC_PATTERN.test(normalized.mac) || !INTERFACE_PATTERN.test(normalized.interface)
|| normalized.interface.startsWith('br-') || ids.has(normalized.id) || tuples.has(tuple)) {
|| !MAC_PATTERN.test(normalized.mac) || !isDeviceInterface(normalized.interface)
|| ids.has(normalized.id) || tuples.has(tuple)) {
throw new Error('Некорректная или повторяющаяся device policy identity');
}
ids.add(normalized.id);
+2 -3
View File
@@ -1,10 +1,10 @@
import crypto from 'node:crypto';
import net from 'node:net';
import { spawn } from 'node:child_process';
import { isDeviceInterface } from '../adapters/neighbors.js';
const COMMAND_OPTIONS = { encoding: 'utf8', timeout: 2_000, killSignal: 'SIGKILL' };
const MAC_PATTERN = /^[0-9a-f]{2}(?::[0-9a-f]{2}){5}$/i;
const INTERFACE_PATTERN = /^[a-z0-9_.:-]{1,15}$/i;
const CHAIN_PATTERN = /^[a-z0-9_]{1,24}$/i;
const COUNTERS = [
['upload', 'upload', 'uploadBytes'],
@@ -85,8 +85,7 @@ export function selectTrafficDevices(observations) {
const ip = String(observation?.ip || '');
const mac = String(observation?.mac || '').toLowerCase();
const deviceInterface = String(observation?.interface || '');
if (!net.isIPv4(ip) || !MAC_PATTERN.test(mac) || !INTERFACE_PATTERN.test(deviceInterface)
|| deviceInterface.startsWith('br-')) continue;
if (!net.isIPv4(ip) || !MAC_PATTERN.test(mac) || !isDeviceInterface(deviceInterface)) continue;
const location = `${mac}|${deviceInterface}`;
candidates.set(`${ip}|${location}`, { ip, mac, interface: deviceInterface });