156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { SmartNftables } from '../ts/nft.manager.js';
|
|
|
|
tap.test('should create SmartNftables with default options', async () => {
|
|
const nft = new SmartNftables();
|
|
expect(nft.tableName).toEqual('smartnftables');
|
|
expect(nft.family).toEqual('ip');
|
|
expect(nft.nat).toBeDefined();
|
|
expect(nft.firewall).toBeDefined();
|
|
expect(nft.rateLimit).toBeDefined();
|
|
});
|
|
|
|
tap.test('should create SmartNftables with custom options', async () => {
|
|
const nft = new SmartNftables({
|
|
tableName: 'myapp',
|
|
family: 'inet',
|
|
});
|
|
expect(nft.tableName).toEqual('myapp');
|
|
expect(nft.family).toEqual('inet');
|
|
});
|
|
|
|
tap.test('should track rule groups when not root', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
// NAT port forwarding
|
|
await nft.nat.addPortForwarding('web', {
|
|
sourcePort: 443,
|
|
targetHost: '10.0.0.5',
|
|
targetPort: 8443,
|
|
});
|
|
|
|
const status = nft.status();
|
|
expect(status.initialized).toBeTrue();
|
|
expect(status.activeGroups).toBeGreaterThan(0);
|
|
expect(status.groups['nat:web']).toBeDefined();
|
|
expect(status.groups['nat:web'].ruleCount).toEqual(2); // DNAT + masquerade
|
|
|
|
await nft.cleanup();
|
|
const statusAfter = nft.status();
|
|
expect(statusAfter.initialized).toBeFalse();
|
|
expect(statusAfter.activeGroups).toEqual(0);
|
|
});
|
|
|
|
tap.test('should track firewall rules when not root', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
await nft.firewall.addRule('block-badguy', {
|
|
direction: 'input',
|
|
action: 'drop',
|
|
sourceIP: '192.168.1.100',
|
|
});
|
|
|
|
const status = nft.status();
|
|
expect(status.groups['fw:block-badguy']).toBeDefined();
|
|
expect(status.groups['fw:block-badguy'].ruleCount).toEqual(1);
|
|
|
|
await nft.firewall.removeRule('block-badguy');
|
|
const statusAfter = nft.status();
|
|
expect(statusAfter.groups['fw:block-badguy']).toBeUndefined();
|
|
|
|
await nft.cleanup();
|
|
});
|
|
|
|
tap.test('should track rate limit rules when not root', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
await nft.rateLimit.addRateLimit('api-throttle', {
|
|
port: 8080,
|
|
protocol: 'tcp',
|
|
rate: '100/second',
|
|
burst: 50,
|
|
perSourceIP: true,
|
|
});
|
|
|
|
const status = nft.status();
|
|
expect(status.groups['ratelimit:api-throttle']).toBeDefined();
|
|
|
|
await nft.rateLimit.removeRateLimit('api-throttle');
|
|
const statusAfter = nft.status();
|
|
expect(statusAfter.groups['ratelimit:api-throttle']).toBeUndefined();
|
|
|
|
await nft.cleanup();
|
|
});
|
|
|
|
tap.test('should handle blockIP convenience method', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
await nft.firewall.blockIP('1.2.3.4');
|
|
|
|
const status = nft.status();
|
|
expect(status.groups['fw:block-1_2_3_4']).toBeDefined();
|
|
|
|
await nft.cleanup();
|
|
});
|
|
|
|
tap.test('should handle stateful tracking convenience', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
await nft.firewall.enableStatefulTracking('input');
|
|
|
|
const status = nft.status();
|
|
expect(status.groups['fw:stateful:input']).toBeDefined();
|
|
expect(status.groups['fw:stateful:input'].ruleCount).toEqual(2); // established+related, invalid
|
|
|
|
await nft.cleanup();
|
|
});
|
|
|
|
tap.test('should handle connection rate limiting', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
await nft.rateLimit.addConnectionRateLimit('ssh-limit', {
|
|
port: 22,
|
|
protocol: 'tcp',
|
|
rate: '5/second',
|
|
perSourceIP: true,
|
|
});
|
|
|
|
const status = nft.status();
|
|
expect(status.groups['connrate:ssh-limit']).toBeDefined();
|
|
|
|
await nft.cleanup();
|
|
});
|
|
|
|
tap.test('should handle port range forwarding', async () => {
|
|
const nft = new SmartNftables({ tableName: 'test' });
|
|
await nft.initialize();
|
|
|
|
await nft.nat.addPortRange('gameservers', 27015, 27020, '10.0.0.10', 27015, 'udp');
|
|
|
|
const status = nft.status();
|
|
const group = status.groups['nat:range:gameservers'];
|
|
expect(group).toBeDefined();
|
|
// 6 ports * 2 commands each (DNAT + masquerade) = 12
|
|
expect(group.ruleCount).toEqual(12);
|
|
|
|
await nft.cleanup();
|
|
});
|
|
|
|
tap.test('should report correct status', async () => {
|
|
const nft = new SmartNftables({ tableName: 'statustest', family: 'inet' });
|
|
|
|
const status = nft.status();
|
|
expect(status.tableName).toEqual('statustest');
|
|
expect(status.family).toEqual('inet');
|
|
expect(status.initialized).toBeFalse();
|
|
expect(status.activeGroups).toEqual(0);
|
|
});
|
|
|
|
export default tap.start();
|