feat(api): enhance host firewall handling and validation
- Introduced a new `sanitizeHostFirewall` function to filter and validate host firewall rules and listeners, ensuring only valid entries are processed. - Updated the `apply-report` endpoint to prevent overwriting existing host firewall snapshots with empty payloads, improving data integrity. - Enhanced the `applyReportHostFirewallSchema` to define the expected structure for host firewall data, allowing for better validation and error handling. - Added tests to verify the behavior of the new sanitization logic and the preservation of existing snapshots, ensuring robustness in the API's handling of firewall data. These changes improve the reliability and accuracy of host firewall data management within the API, enhancing overall monitoring capabilities.
This commit is contained in:
@@ -278,6 +278,113 @@ describe('port ACL + host firewall snapshot', () => {
|
||||
expect(body.listeners[0]?.port).toBe(22)
|
||||
})
|
||||
|
||||
it('does not wipe host_firewall snapshot with empty apply-report payload', async () => {
|
||||
const app = await appPromise
|
||||
await app.ready()
|
||||
|
||||
const { agentId, token } = await enrollApprovedLinux(
|
||||
app,
|
||||
'host-fw-empty',
|
||||
'evofw_host_fw_empty_token_ab',
|
||||
)
|
||||
|
||||
const filled = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/v1/agent/apply-report',
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
payload: {
|
||||
status: 'ok',
|
||||
host_firewall: {
|
||||
rules: [
|
||||
{
|
||||
ownership: 'evofw',
|
||||
backend: 'nft',
|
||||
action: 'drop',
|
||||
raw: 'ip saddr @deny_v4 drop',
|
||||
},
|
||||
],
|
||||
listeners: [{ protocol: 'tcp', port: 22, address: '0.0.0.0' }],
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(filled.statusCode).toBe(200)
|
||||
|
||||
const empty = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/v1/agent/apply-report',
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
payload: {
|
||||
status: 'ok',
|
||||
host_firewall: { rules: [], listeners: [] },
|
||||
},
|
||||
})
|
||||
expect(empty.statusCode).toBe(200)
|
||||
|
||||
const snap = await app.inject({
|
||||
method: 'GET',
|
||||
url: `/api/v1/agents/${agentId}/host-firewall`,
|
||||
})
|
||||
const body = snap.json() as {
|
||||
rules: unknown[]
|
||||
listeners: unknown[]
|
||||
}
|
||||
expect(body.rules).toHaveLength(1)
|
||||
expect(body.listeners).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('filters invalid host_firewall rules without failing apply-report', async () => {
|
||||
const app = await appPromise
|
||||
await app.ready()
|
||||
|
||||
const { agentId, token } = await enrollApprovedLinux(
|
||||
app,
|
||||
'host-fw-filter',
|
||||
'evofw_host_fw_filter_token_a',
|
||||
)
|
||||
|
||||
const report = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/v1/agent/apply-report',
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
payload: {
|
||||
status: 'ok',
|
||||
host_firewall: {
|
||||
rules: [
|
||||
{ ownership: 'evofw', backend: 'nft', raw: 'ok rule' },
|
||||
{ ownership: 'nope', backend: 'nft', raw: 'bad ownership' },
|
||||
{ not: 'a rule' },
|
||||
],
|
||||
listeners: [
|
||||
{ protocol: 'tcp', port: 443, address: '::' },
|
||||
{ protocol: 'tcp', port: 99999, address: '1.1.1.1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(report.statusCode).toBe(200)
|
||||
|
||||
const snap = await app.inject({
|
||||
method: 'GET',
|
||||
url: `/api/v1/agents/${agentId}/host-firewall`,
|
||||
})
|
||||
const body = snap.json() as {
|
||||
rules: unknown[]
|
||||
listeners: { port: number }[]
|
||||
}
|
||||
expect(body.rules).toHaveLength(1)
|
||||
expect(body.listeners).toHaveLength(1)
|
||||
expect(body.listeners[0]?.port).toBe(443)
|
||||
})
|
||||
|
||||
it('rejects port ACL on non-linux agents', async () => {
|
||||
const app = await appPromise
|
||||
await app.ready()
|
||||
|
||||
Reference in New Issue
Block a user