- Added support for managing desired L4 port ACL rules for Linux agents, allowing for open/close actions on specified ports. - Introduced a new endpoint for CRUD operations on port rules, enhancing the API's capabilities for agent management. - Implemented functionality to collect and report host firewall snapshots, capturing observed rules and listeners for better monitoring. - Updated the agent detail view to include tabs for managing port ACLs and viewing host firewall data, improving user experience. - Enhanced documentation to reflect the new features and API changes, ensuring clarity for users and developers. These changes significantly improve the management and visibility of firewall rules and port access control for agents.
141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import { describe, it, expect, afterAll } from 'vitest'
|
|
import { buildApp } from '../../app.js'
|
|
import type { AppConfig } from '../../config.js'
|
|
|
|
const testConfig: AppConfig = {
|
|
databaseUrl: 'sqlite::memory:',
|
|
jwtSecret: 'test',
|
|
jwtTtlHours: 24,
|
|
serverPort: 8080,
|
|
staticDir: null,
|
|
logLevel: 'error',
|
|
authRequired: false,
|
|
authIssuer: 'https://auth.test',
|
|
authPortalUrl: 'http://localhost:5175',
|
|
publicBaseUrl: 'https://fw.example.com',
|
|
enrollSeed: 'test-seed',
|
|
}
|
|
|
|
async function createAgent(
|
|
app: Awaited<ReturnType<typeof buildApp>>,
|
|
name: string,
|
|
) {
|
|
const link = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/v1/install-links',
|
|
payload: { name, platform: 'linux' },
|
|
})
|
|
expect(link.statusCode).toBe(201)
|
|
const agentId = (link.json() as { agent_id: string }).agent_id
|
|
await app.inject({
|
|
method: 'POST',
|
|
url: `/api/v1/agents/${agentId}/approve`,
|
|
})
|
|
return agentId
|
|
}
|
|
|
|
describe('classic policy default_action', () => {
|
|
const appPromise = buildApp({ memory: true, config: testConfig })
|
|
|
|
afterAll(async () => {
|
|
const app = await appPromise
|
|
await app.close()
|
|
})
|
|
|
|
it('mixed deny/allow; deny wins exact; preview has default_action', async () => {
|
|
const app = await appPromise
|
|
await app.ready()
|
|
|
|
const created = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/v1/policy-sets',
|
|
payload: { name: 'mixed-set' },
|
|
})
|
|
expect(created.statusCode).toBe(200)
|
|
const setId = (created.json() as { id: string }).id
|
|
|
|
for (const payload of [
|
|
{ set_id: setId, action: 'deny', cidr: '10.0.0.1/32' },
|
|
{ set_id: setId, action: 'allow', cidr: '10.0.0.1/32' },
|
|
{ set_id: setId, action: 'allow', cidr: '10.0.0.2/32' },
|
|
]) {
|
|
const r = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/v1/rules',
|
|
payload,
|
|
})
|
|
expect(r.statusCode).toBe(200)
|
|
}
|
|
|
|
const agentId = await createAgent(app, 'pol-agent')
|
|
|
|
const assign = await app.inject({
|
|
method: 'PUT',
|
|
url: `/api/v1/agents/${agentId}/policy-sets`,
|
|
payload: { set_ids: [setId] },
|
|
})
|
|
expect(assign.statusCode).toBe(200)
|
|
|
|
const patch = await app.inject({
|
|
method: 'PATCH',
|
|
url: `/api/v1/agents/${agentId}`,
|
|
payload: { default_action: 'drop' },
|
|
})
|
|
expect(patch.statusCode).toBe(200)
|
|
expect((patch.json() as { default_action: string }).default_action).toBe(
|
|
'drop',
|
|
)
|
|
|
|
const preview = await app.inject({
|
|
method: 'GET',
|
|
url: `/api/v1/agents/${agentId}/preview`,
|
|
})
|
|
expect(preview.statusCode).toBe(200)
|
|
const body = preview.json() as {
|
|
default_action: string
|
|
apply_version: number
|
|
deny_cidrs: string[]
|
|
allow_cidrs: string[]
|
|
summary: { conflicts_dropped: number }
|
|
chain: unknown[]
|
|
}
|
|
expect(body.default_action).toBe('drop')
|
|
expect(body.apply_version).toBe(3)
|
|
expect(body.deny_cidrs).toContain('10.0.0.1/32')
|
|
expect(body.allow_cidrs).not.toContain('10.0.0.1/32')
|
|
expect(body.allow_cidrs).toContain('10.0.0.2/32')
|
|
expect(body.summary.conflicts_dropped).toBeGreaterThanOrEqual(1)
|
|
expect(body.chain.length).toBeGreaterThanOrEqual(3)
|
|
})
|
|
|
|
it('allows assigning sets without same-mode lock', async () => {
|
|
const app = await appPromise
|
|
await app.ready()
|
|
|
|
const a = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/v1/policy-sets',
|
|
payload: { name: 'set-a', policy_mode: 'blacklist' },
|
|
})
|
|
const b = await app.inject({
|
|
method: 'POST',
|
|
url: '/api/v1/policy-sets',
|
|
payload: { name: 'set-b', policy_mode: 'whitelist' },
|
|
})
|
|
expect(a.statusCode).toBe(200)
|
|
expect(b.statusCode).toBe(200)
|
|
const setA = (a.json() as { id: string }).id
|
|
const setB = (b.json() as { id: string }).id
|
|
|
|
const agentId = await createAgent(app, 'multi-mode-agent')
|
|
|
|
const assign = await app.inject({
|
|
method: 'PUT',
|
|
url: `/api/v1/agents/${agentId}/policy-sets`,
|
|
payload: { set_ids: [setA, setB] },
|
|
})
|
|
expect(assign.statusCode).toBe(200)
|
|
expect((assign.json() as { items: unknown[] }).items).toHaveLength(2)
|
|
})
|
|
})
|