feat: harden firewall policies telemetry and operations
quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped
quality / changes (push) Successful in 7s
quality / commitlint (push) Skipped
quality / openapi (push) Successful in 30s
quality / web (push) Successful in 1m3s
quality / api (push) Successful in 1m15s
quality / linux-agent-smoke (push) Failing after 27s
quality / docker-check (push) Failing after 2m28s
CD / quality (push) Failing after 5m52s
CD / publish (push) Skipped
Implement reliable agent apply, telemetry v2, analytics retention, guarded rollouts, enrollment, labels and webhooks. Improve ReUI frontend data safety and bounded list loading, and add Docker smoke, recovery and resource checks. Document local verification and pending native acceptance.
This commit is contained in:
1 parent
aa475848fd
commit
9fa6269174
242 files changed
+19872
-3057
No files matched your search
+19
-16
@@ -9,6 +9,7 @@ import { AsyncTask, CronJob } from 'toad-scheduler'
|
||||
import type { AppConfig } from './config.js'
|
||||
import { loadConfig } from './config.js'
|
||||
import authPlugin from './plugins/auth.js'
|
||||
import operationalPlugin from './plugins/operational.js'
|
||||
import corsPlugin from './plugins/cors.js'
|
||||
import dbPlugin from './plugins/db.js'
|
||||
import errorHandlerPlugin from './plugins/error-handler.js'
|
||||
@@ -16,7 +17,11 @@ import { healthRoutes } from './routes/health.js'
|
||||
import { controlRoutes } from './routes/control.js'
|
||||
import { auditRoutes } from './routes/audit.js'
|
||||
import { agentRoutes } from './routes/agent.js'
|
||||
import { enrollmentRoutes } from './routes/enrollment.js'
|
||||
import { policyRolloutsRoutes } from './routes/policy-rollouts.js'
|
||||
import { webhooksRoutes } from './routes/webhooks.js'
|
||||
import { refreshAllLists } from './services/lists/refresh.js'
|
||||
import { runStatsRetention } from './services/analytics.js'
|
||||
import { repos } from '@evofw/db'
|
||||
import {
|
||||
isValidInstallSlug,
|
||||
@@ -32,6 +37,7 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
||||
const config = opts.config ?? loadConfig()
|
||||
|
||||
const app = Fastify({
|
||||
trustProxy: config.trustProxy ?? false,
|
||||
logger: { level: config.logLevel },
|
||||
genReqId: () => crypto.randomUUID(),
|
||||
}).withTypeProvider<ZodTypeProvider>()
|
||||
@@ -66,14 +72,11 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
||||
}
|
||||
: false,
|
||||
})
|
||||
await app.register(import('@fastify/rate-limit'), {
|
||||
max: 300,
|
||||
timeWindow: '1 minute',
|
||||
})
|
||||
await app.register(corsPlugin, { config })
|
||||
await app.register(errorHandlerPlugin)
|
||||
await app.register(dbPlugin, { config, memory: opts.memory })
|
||||
await app.register(authPlugin, { config })
|
||||
await app.register(operationalPlugin, { config, memory: opts.memory })
|
||||
|
||||
// Seed enroll_seed into settings if empty
|
||||
if (!repos.getSetting(app.db, 'enroll_seed')) {
|
||||
@@ -82,12 +85,15 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
||||
|
||||
await app.register(healthRoutes)
|
||||
await app.register(agentRoutes, { config })
|
||||
await app.register(enrollmentRoutes, { config })
|
||||
|
||||
await app.register(
|
||||
async (protectedApi) => {
|
||||
protectedApi.addHook('onRequest', app.requireAuth)
|
||||
await protectedApi.register(controlRoutes, { config })
|
||||
await protectedApi.register(auditRoutes)
|
||||
await protectedApi.register(policyRolloutsRoutes, { config })
|
||||
await protectedApi.register(webhooksRoutes, { config, memory: opts.memory })
|
||||
},
|
||||
{ prefix: '/api/v1' },
|
||||
)
|
||||
@@ -102,6 +108,9 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
||||
|
||||
app.setNotFoundHandler(async (request, reply) => {
|
||||
const path = request.url.split('?')[0] ?? ''
|
||||
if (path === '/api' || path.startsWith('/api/') || path === '/v1' || path.startsWith('/v1/')) {
|
||||
return reply.code(404).send({ error: { code: 'NOT_FOUND', message: 'Not Found', request_id: request.id } })
|
||||
}
|
||||
const segment = path.startsWith('/') ? path.slice(1) : path
|
||||
if (
|
||||
request.method === 'GET' &&
|
||||
@@ -117,7 +126,7 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
||||
config.publicBaseUrl,
|
||||
config.enrollSeed,
|
||||
)
|
||||
return reply.type(contentType).send(body)
|
||||
return reply.header('Cache-Control', 'no-store').type(contentType).send(body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,21 +158,15 @@ export async function buildApp(opts: BuildAppOptions = {}) {
|
||||
|
||||
// Raw stats samples grow ~1 row per agent per sync interval; drop old
|
||||
// ones daily. Lifetime totals live on agents; aggregates are kept.
|
||||
const retentionAbort = new AbortController()
|
||||
app.addHook('onClose', async () => { retentionAbort.abort() })
|
||||
const runRetention = async () => {
|
||||
const cutoff = new Date(
|
||||
Date.now() - config.statsRetentionDays * 86_400_000,
|
||||
).toISOString()
|
||||
const deleted = repos.deleteStatsSamplesBefore(app.db, cutoff)
|
||||
if (deleted > 0) {
|
||||
app.log.info(
|
||||
{ deleted, cutoff },
|
||||
`stats retention: raw samples older than ${config.statsRetentionDays}d removed`,
|
||||
)
|
||||
}
|
||||
const deleted = await runStatsRetention(app.db, new Date().toISOString(), 1000, retentionAbort.signal)
|
||||
if (deleted > 0) app.log.info({ deleted }, 'stats retention completed')
|
||||
}
|
||||
app.scheduler.addCronJob(
|
||||
new CronJob(
|
||||
{ cronExpression: '17 3 * * *' },
|
||||
{ cronExpression: '17 * * * *' },
|
||||
new AsyncTask('stats-retention', runRetention, (err) => {
|
||||
app.log.warn({ err }, 'stats retention failed')
|
||||
}),
|
||||
|
||||
Reference in new issue
Block a user