Files
harbor-net/test/server/device-policy.test.js
dokril 34d8b681ad
Build and Deploy Gateway / build-and-push (push) Failing after 2s
Build and Deploy Gateway / deploy (push) Has been skipped
Refactor VPN proxy client implementation
2026-08-09 00:41:52 +03:00

128 lines
5.0 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildDevicePolicyRestore,
createDevicePolicyService,
fingerprintDirectDevices,
normalizeDirectDevices,
} from '../../dist/server/services/devicePolicyService.js';
const directDevice = {
id: 'dev_0011223344556677',
ip: '192.168.50.7',
mac: '00:11:22:33:44:55',
interface: 'eth0',
};
test('device policy rules match the full identity before the TPROXY fallback', () => {
assert.deepEqual(normalizeDirectDevices([{ ...directDevice, mac: directDevice.mac.toUpperCase() }]), [directDevice]);
assert.throws(() => normalizeDirectDevices([directDevice, directDevice]), /повторяющаяся/);
assert.throws(() => normalizeDirectDevices([{ ...directDevice, interface: 'br-user' }]), /identity/);
assert.throws(() => normalizeDirectDevices([{ ...directDevice, interface: 'docker0' }]), /identity/);
assert.throws(() => normalizeDirectDevices([{ ...directDevice, interface: 'veth1234' }]), /identity/);
const restore = buildDevicePolicyRestore({
devices: [directDevice],
chain: 'VPN_PROXY_DEVICE_POLICY',
slot: 'B',
tproxyPort: 7895,
tproxyMark: '1',
});
assert.equal(restore, [
'*mangle',
'-F VPN_PROXY_DEVICE_POLICY_B',
'-A VPN_PROXY_DEVICE_POLICY_B -i eth0 -s 192.168.50.7 -m mac --mac-source 00:11:22:33:44:55 -m comment --comment harbor-policy:dev_0011223344556677:direct -j RETURN',
'-A VPN_PROXY_DEVICE_POLICY_B -p tcp -j TPROXY --on-port 7895 --tproxy-mark 1/1',
'-A VPN_PROXY_DEVICE_POLICY_B -p udp -j TPROXY --on-port 7895 --tproxy-mark 1/1',
'COMMIT',
'',
].join('\n'));
assert.match(fingerprintDirectDevices([directDevice]), /^[a-f0-9]{64}$/);
});
test('device policy swaps one prepared slot and preserves the active slot on failure', async () => {
const commands = [];
let failSwap = false;
const generations = ['epoch-a', 'rules-b', 'rules-a'];
const service = createDevicePolicyService({
chain: 'VPN_PROXY_DEVICE_POLICY',
tproxyPort: 7895,
tproxyMark: '1',
nextGeneration: () => generations.shift(),
now: () => new Date('2026-08-07T12:00:00.000Z'),
run: (command, args, options) => {
commands.push({ command, args, options });
if (failSwap && args.includes('-R')) return { status: 1, stderr: 'swap failed' };
return { status: 0, stdout: '', stderr: '' };
},
});
const initial = service.snapshot();
assert.equal((await service.apply([])).generation, initial.generation);
assert.equal(commands.length, 0);
const applied = await service.apply([directDevice]);
assert.deepEqual(applied.appliedIds, [directDevice.id]);
assert.equal(applied.changed, true);
assert.deepEqual(commands.at(-1).args, [
'-w', '1', '-t', 'mangle', '-R', 'VPN_PROXY_DEVICE_POLICY', '1', '-j', 'VPN_PROXY_DEVICE_POLICY_B',
]);
assert.equal(commands[0].command, 'iptables-restore');
assert.match(commands[0].options.input, /--mac-source 00:11:22:33:44:55/);
assert.equal(commands.length, 2);
assert.equal(commands.every(({ options }) => options.timeout === 2_000), true);
const commandCount = commands.length;
assert.equal((await service.apply([directDevice])).changed, false);
assert.equal(commands.length, commandCount);
failSwap = true;
await assert.rejects(service.apply([]), /swap failed/);
assert.deepEqual(service.snapshot().appliedIds, [directDevice.id]);
assert.equal(service.snapshot().generation, applied.generation);
const prepareCommands = [];
const prepareFailure = createDevicePolicyService({
chain: 'VPN_PROXY_DEVICE_POLICY',
tproxyPort: 7895,
tproxyMark: '1',
nextGeneration: () => 'prepare-epoch',
run: (command, args, options) => {
prepareCommands.push({ command, args, options });
return command === 'iptables-restore'
? { status: 1, stderr: 'prepare failed' }
: { status: 0, stdout: '', stderr: '' };
},
});
await assert.rejects(prepareFailure.apply([directDevice]), /prepare failed/);
assert.equal(prepareCommands.some(({ args }) => args.includes('-R')), false);
assert.deepEqual(prepareFailure.snapshot().appliedIds, []);
});
test('the maximum policy set stays inside the control request deadline', async () => {
const devices = Array.from({ length: 512 }, (_, index) => {
const mac = index.toString(16).padStart(12, '0').match(/../g).join(':');
return {
id: `dev_${index.toString(16).padStart(16, '0')}`,
ip: `192.168.${50 + Math.floor(index / 254)}.${(index % 254) + 1}`,
mac,
interface: 'eth0',
};
});
const calls = [];
const service = createDevicePolicyService({
chain: 'VPN_PROXY_DEVICE_POLICY',
tproxyPort: 7895,
tproxyMark: '1',
nextGeneration: () => 'generation',
run: (command, args, options) => {
calls.push({ command, args, options });
return { status: 0, stdout: '', stderr: '' };
},
});
await service.apply(devices);
assert.deepEqual(calls.map(({ command }) => command), ['iptables-restore', 'iptables']);
assert.equal(calls.reduce((sum, { options }) => sum + options.timeout, 0), 4_000);
});