Add native traffic inspection to Harbor Connect and Gateway
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import test from 'node:test';
|
||||
|
||||
const singboxUrl = pathToFileURL(path.resolve('dist/server/singbox.js')).href;
|
||||
const subscriptionConfig = {
|
||||
outbounds: [{
|
||||
type: 'vless',
|
||||
tag: 'vpn',
|
||||
server: 'vpn.example.test',
|
||||
server_port: 443,
|
||||
uuid: '00000000-0000-4000-8000-000000000000',
|
||||
tls: { enabled: true },
|
||||
}],
|
||||
};
|
||||
|
||||
function run(source, { component = 'dataplane', socket = '/tmp/harbor-test.sock' } = {}) {
|
||||
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'harbor-gateway-native-config-'));
|
||||
const script = `
|
||||
const { buildGatewayConfig, buildDualChannelGatewayConfig } = await import(${JSON.stringify(singboxUrl)});
|
||||
const subscription = ${JSON.stringify(subscriptionConfig)};
|
||||
process.stdout.write(JSON.stringify({
|
||||
single: buildGatewayConfig(subscription, 'vpn'),
|
||||
dual: buildDualChannelGatewayConfig({
|
||||
primary: { subscriptionConfig: subscription, selectedServerId: 'vpn' },
|
||||
reserve: { subscriptionConfig: subscription, selectedServerId: 'vpn' },
|
||||
}),
|
||||
}));
|
||||
`;
|
||||
try {
|
||||
return spawnSync(process.execPath, ['--input-type=module', '--eval', script], {
|
||||
cwd: path.resolve('.'),
|
||||
encoding: 'utf8',
|
||||
env: {
|
||||
...process.env,
|
||||
APP_MODE: 'gateway',
|
||||
APP_COMPONENT: component,
|
||||
DATA_DIR: directory,
|
||||
SING_BOX_CACHE: path.join(directory, 'cache.db'),
|
||||
SING_BOX_TRAFFIC_SOURCE: source,
|
||||
DATAPLANE_SOCKET: socket,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
fs.rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
test('Gateway snapshot keeps the 1.13-compatible config and Clash traffic API', () => {
|
||||
const result = run('snapshot', { component: '', socket: '' });
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
const configs = JSON.parse(result.stdout);
|
||||
for (const config of Object.values(configs)) {
|
||||
assert.equal(config.services, undefined);
|
||||
assert.deepEqual(config.dns, { independent_cache: true });
|
||||
assert.deepEqual(config.experimental.clash_api, { external_controller: '127.0.0.1:19090' });
|
||||
}
|
||||
});
|
||||
|
||||
test('Gateway shadow and native add one secret-free loopback API to single and dual configs', () => {
|
||||
for (const source of ['shadow', 'native']) {
|
||||
const result = run(source);
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
const configs = JSON.parse(result.stdout);
|
||||
for (const config of Object.values(configs)) {
|
||||
assert.deepEqual(config.services, [{
|
||||
type: 'api',
|
||||
listen: '127.0.0.1',
|
||||
listen_port: 19091,
|
||||
dashboard: false,
|
||||
}]);
|
||||
assert.equal(JSON.stringify(config).includes('secret'), false);
|
||||
assert.deepEqual(config.dns, {});
|
||||
assert.deepEqual(config.experimental.clash_api, { external_controller: '127.0.0.1:19090' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('Gateway shadow and native reject combined or socket-less topology', () => {
|
||||
for (const options of [
|
||||
{ component: '', socket: '' },
|
||||
{ component: 'control', socket: '' },
|
||||
{ component: 'dataplane', socket: '' },
|
||||
]) {
|
||||
const result = run('native', options);
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(result.stderr, /require split control\/dataplane topology/);
|
||||
}
|
||||
});
|
||||
|
||||
test('Gateway rejects unknown traffic sources', () => {
|
||||
const result = run('disabled');
|
||||
assert.notEqual(result.status, 0);
|
||||
assert.match(result.stderr, /must be snapshot, shadow or native/);
|
||||
});
|
||||
Reference in New Issue
Block a user