184 lines
5.3 KiB
TypeScript
184 lines
5.3 KiB
TypeScript
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import { NFTablesManager } from '../ts/proxies/smart-proxy/nftables-manager.js';
|
|
import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js';
|
|
import type { ISmartProxyOptions } from '../ts/proxies/smart-proxy/models/interfaces.js';
|
|
import * as child_process from 'child_process';
|
|
import { promisify } from 'util';
|
|
|
|
const exec = promisify(child_process.exec);
|
|
|
|
// Check if we have root privileges
|
|
async function checkRootPrivileges(): Promise<boolean> {
|
|
try {
|
|
const { stdout } = await exec('id -u');
|
|
return stdout.trim() === '0';
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Skip tests if not root
|
|
const isRoot = await checkRootPrivileges();
|
|
if (!isRoot) {
|
|
console.log('');
|
|
console.log('========================================');
|
|
console.log('NFTablesManager tests require root privileges');
|
|
console.log('Skipping NFTablesManager tests');
|
|
console.log('========================================');
|
|
console.log('');
|
|
// Skip tests when not running as root - tests are marked with tap.skip.test
|
|
}
|
|
|
|
/**
|
|
* Tests for the NFTablesManager class
|
|
*/
|
|
|
|
// Sample route configurations for testing
|
|
const sampleRoute: IRouteConfig = {
|
|
name: 'test-nftables-route',
|
|
match: {
|
|
ports: 8080,
|
|
domains: 'test.example.com'
|
|
},
|
|
action: {
|
|
type: 'forward',
|
|
target: {
|
|
host: 'localhost',
|
|
port: 8000
|
|
},
|
|
forwardingEngine: 'nftables',
|
|
nftables: {
|
|
protocol: 'tcp',
|
|
preserveSourceIP: true,
|
|
useIPSets: true
|
|
}
|
|
}
|
|
};
|
|
|
|
// Sample SmartProxy options
|
|
const sampleOptions: ISmartProxyOptions = {
|
|
routes: [sampleRoute],
|
|
enableDetailedLogging: true
|
|
};
|
|
|
|
// Instance of NFTablesManager for testing
|
|
let manager: NFTablesManager;
|
|
|
|
// Skip these tests by default since they require root privileges to run NFTables commands
|
|
// When running as root, change this to false
|
|
const SKIP_TESTS = true;
|
|
|
|
tap.skip.test('NFTablesManager setup test', async () => {
|
|
// Test will be skipped if not running as root due to tap.skip.test
|
|
|
|
// Create a new instance of NFTablesManager
|
|
manager = new NFTablesManager(sampleOptions);
|
|
|
|
// Verify the instance was created successfully
|
|
expect(manager).toBeTruthy();
|
|
});
|
|
|
|
tap.skip.test('NFTablesManager route provisioning test', async () => {
|
|
// Test will be skipped if not running as root due to tap.skip.test
|
|
|
|
// Provision the sample route
|
|
const result = await manager.provisionRoute(sampleRoute);
|
|
|
|
// Verify the route was provisioned successfully
|
|
expect(result).toEqual(true);
|
|
|
|
// Verify the route is listed as provisioned
|
|
expect(manager.isRouteProvisioned(sampleRoute)).toEqual(true);
|
|
});
|
|
|
|
tap.skip.test('NFTablesManager status test', async () => {
|
|
// Test will be skipped if not running as root due to tap.skip.test
|
|
|
|
// Get the status of the managed rules
|
|
const status = await manager.getStatus();
|
|
|
|
// Verify status includes our route
|
|
const keys = Object.keys(status);
|
|
expect(keys.length).toBeGreaterThan(0);
|
|
|
|
// Check the status of the first rule
|
|
const firstStatus = status[keys[0]];
|
|
expect(firstStatus.active).toEqual(true);
|
|
expect(firstStatus.ruleCount.added).toBeGreaterThan(0);
|
|
});
|
|
|
|
tap.skip.test('NFTablesManager route updating test', async () => {
|
|
// Test will be skipped if not running as root due to tap.skip.test
|
|
|
|
// Create an updated version of the sample route
|
|
const updatedRoute: IRouteConfig = {
|
|
...sampleRoute,
|
|
action: {
|
|
...sampleRoute.action,
|
|
target: {
|
|
host: 'localhost',
|
|
port: 9000 // Different port
|
|
},
|
|
nftables: {
|
|
...sampleRoute.action.nftables,
|
|
protocol: 'all' // Different protocol
|
|
}
|
|
}
|
|
};
|
|
|
|
// Update the route
|
|
const result = await manager.updateRoute(sampleRoute, updatedRoute);
|
|
|
|
// Verify the route was updated successfully
|
|
expect(result).toEqual(true);
|
|
|
|
// Verify the old route is no longer provisioned
|
|
expect(manager.isRouteProvisioned(sampleRoute)).toEqual(false);
|
|
|
|
// Verify the new route is provisioned
|
|
expect(manager.isRouteProvisioned(updatedRoute)).toEqual(true);
|
|
});
|
|
|
|
tap.skip.test('NFTablesManager route deprovisioning test', async () => {
|
|
// Test will be skipped if not running as root due to tap.skip.test
|
|
|
|
// Create an updated version of the sample route from the previous test
|
|
const updatedRoute: IRouteConfig = {
|
|
...sampleRoute,
|
|
action: {
|
|
...sampleRoute.action,
|
|
target: {
|
|
host: 'localhost',
|
|
port: 9000 // Different port from original test
|
|
},
|
|
nftables: {
|
|
...sampleRoute.action.nftables,
|
|
protocol: 'all' // Different protocol from original test
|
|
}
|
|
}
|
|
};
|
|
|
|
// Deprovision the route
|
|
const result = await manager.deprovisionRoute(updatedRoute);
|
|
|
|
// Verify the route was deprovisioned successfully
|
|
expect(result).toEqual(true);
|
|
|
|
// Verify the route is no longer provisioned
|
|
expect(manager.isRouteProvisioned(updatedRoute)).toEqual(false);
|
|
});
|
|
|
|
tap.skip.test('NFTablesManager cleanup test', async () => {
|
|
// Test will be skipped if not running as root due to tap.skip.test
|
|
|
|
// Stop all NFTables rules
|
|
await manager.stop();
|
|
|
|
// Get the status of the managed rules
|
|
const status = await manager.getStatus();
|
|
|
|
// Verify there are no active rules
|
|
expect(Object.keys(status).length).toEqual(0);
|
|
});
|
|
|
|
export default tap.start(); |