Files
csf-web/src/app/api/logs/route.ts
ale 8a4fde51c7 feat: Complete CSF web interface with all features
- Fixed TypeScript errors and build issues
- Added comprehensive README.md with documentation
- Created .env.example with all configuration options
- Improved .gitignore with CSF-specific entries
- Added VS Code configuration for development
- Fixed next.config.mjs configuration
- Corrected API route type issues
- Added CHANGELOG.md with version history
- All components now compile without errors
- Ready for production deployment

Features included:
- Modern web interface for CSF firewall management
- Real-time monitoring with WebSockets
- JWT authentication system
- Complete API for CSF control
- Responsive UI with Tailwind CSS
- TypeScript support throughout
- Docker-ready configuration

Signed-off-by: ale <ale@manalejandro.com>
2025-09-20 18:48:53 +02:00

328 líneas
9.1 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server'
import { exec } from 'child_process'
import { promisify } from 'util'
import fs from 'fs/promises'
const execAsync = promisify(exec)
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const type = searchParams.get('type')
const limit = parseInt(searchParams.get('limit') || '100')
const since = searchParams.get('since') || undefined // timestamp
try {
switch (type) {
case 'firewall':
return await getFirewallLogs(limit, since)
case 'lfd':
return await getLfdLogs(limit, since)
case 'system':
return await getSystemLogs(limit, since)
case 'blocked':
return await getBlockedIPs(limit)
case 'connections':
return await getActiveConnections()
default:
return await getAllLogs(limit, since)
}
} catch (error: any) {
console.error('Logs API error:', error)
return NextResponse.json({
success: false,
error: `Failed to retrieve logs: ${error.message}`
}, { status: 500 })
}
}
async function getFirewallLogs(limit: number, since?: string) {
try {
// Get iptables logs from syslog
let command = `grep "CSF\\|iptables" /var/log/syslog | tail -${limit}`
if (since) {
const sinceDate = new Date(since).toISOString().split('T')[0]
command = `grep "CSF\\|iptables" /var/log/syslog | grep "${sinceDate}" | tail -${limit}`
}
const { stdout } = await execAsync(command)
const logs = parseFirewallLogs(stdout)
return NextResponse.json({
success: true,
data: {
logs,
total: logs.length,
type: 'firewall'
}
})
} catch (error: any) {
return NextResponse.json({
success: false,
error: `Failed to get firewall logs: ${error.message}`
}, { status: 500 })
}
}
async function getLfdLogs(limit: number, since?: string) {
try {
let command = `grep "lfd" /var/log/lfd.log 2>/dev/null | tail -${limit} || echo ""`
if (since) {
const sinceDate = new Date(since).toISOString().split('T')[0]
command = `grep "lfd" /var/log/lfd.log 2>/dev/null | grep "${sinceDate}" | tail -${limit} || echo ""`
}
const { stdout } = await execAsync(command)
const logs = parseLfdLogs(stdout)
return NextResponse.json({
success: true,
data: {
logs,
total: logs.length,
type: 'lfd'
}
})
} catch (error: any) {
return NextResponse.json({
success: false,
error: `Failed to get LFD logs: ${error.message}`
}, { status: 500 })
}
}
async function getSystemLogs(limit: number, since?: string) {
try {
let command = `grep "kernel\\|iptables" /var/log/syslog | tail -${limit}`
if (since) {
const sinceDate = new Date(since).toISOString().split('T')[0]
command = `grep "kernel\\|iptables" /var/log/syslog | grep "${sinceDate}" | tail -${limit}`
}
const { stdout } = await execAsync(command)
const logs = parseSystemLogs(stdout)
return NextResponse.json({
success: true,
data: {
logs,
total: logs.length,
type: 'system'
}
})
} catch (error: any) {
return NextResponse.json({
success: false,
error: `Failed to get system logs: ${error.message}`
}, { status: 500 })
}
}
async function getBlockedIPs(limit: number) {
try {
// Get current blocked IPs from iptables
const { stdout } = await execAsync('/usr/local/csf/bin/csf --status | grep DROP | head -' + limit)
const blockedIPs = parseBlockedIPs(stdout)
return NextResponse.json({
success: true,
data: {
blocked_ips: blockedIPs,
total: blockedIPs.length,
type: 'blocked'
}
})
} catch (error: any) {
return NextResponse.json({
success: false,
error: `Failed to get blocked IPs: ${error.message}`
}, { status: 500 })
}
}
async function getActiveConnections() {
try {
// Get active network connections
const { stdout } = await execAsync('netstat -tn | grep ESTABLISHED | wc -l')
const activeConnections = parseInt(stdout.trim()) || 0
// Get connection details
const { stdout: connections } = await execAsync('netstat -tn | grep ESTABLISHED | head -20')
const connectionList = parseConnections(connections)
return NextResponse.json({
success: true,
data: {
active_connections: activeConnections,
connections: connectionList,
type: 'connections'
}
})
} catch (error: any) {
return NextResponse.json({
success: false,
error: `Failed to get active connections: ${error.message}`
}, { status: 500 })
}
}
async function getAllLogs(limit: number, since?: string) {
try {
const [firewallResult, lfdResult, systemResult] = await Promise.allSettled([
getFirewallLogs(Math.floor(limit / 3), since),
getLfdLogs(Math.floor(limit / 3), since),
getSystemLogs(Math.floor(limit / 3), since)
])
const allLogs = []
if (firewallResult.status === 'fulfilled') {
const response = await firewallResult.value.json()
if (response.success) allLogs.push(...response.data.logs)
}
if (lfdResult.status === 'fulfilled') {
const response = await lfdResult.value.json()
if (response.success) allLogs.push(...response.data.logs)
}
if (systemResult.status === 'fulfilled') {
const response = await systemResult.value.json()
if (response.success) allLogs.push(...response.data.logs)
}
// Sort by timestamp
allLogs.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
return NextResponse.json({
success: true,
data: {
logs: allLogs.slice(0, limit),
total: allLogs.length,
type: 'all'
}
})
} catch (error: any) {
return NextResponse.json({
success: false,
error: `Failed to get all logs: ${error.message}`
}, { status: 500 })
}
}
// Parsing functions
function parseFirewallLogs(output: string) {
const logs = []
const lines = output.split('\\n').filter(line => line.trim())
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const logEntry = parseLogLine(line, 'firewall')
if (logEntry) logs.push(logEntry)
}
return logs
}
function parseLfdLogs(output: string) {
const logs = []
const lines = output.split('\\n').filter(line => line.trim())
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const logEntry = parseLogLine(line, 'lfd')
if (logEntry) logs.push(logEntry)
}
return logs
}
function parseSystemLogs(output: string) {
const logs = []
const lines = output.split('\\n').filter(line => line.trim())
for (let i = 0; i < lines.length; i++) {
const line = lines[i]
const logEntry = parseLogLine(line, 'system')
if (logEntry) logs.push(logEntry)
}
return logs
}
function parseLogLine(line: string, type: string) {
// Basic log parsing - adjust regex patterns based on actual log format
const patterns = {
timestamp: /^(\\w{3}\\s+\\d{1,2}\\s+\\d{2}:\\d{2}:\\d{2})/,
ip: /(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})/,
port: /DPT=(\\d+)/,
protocol: /(TCP|UDP|ICMP)/i,
action: /(BLOCK|DROP|ACCEPT|ALLOW|DENY)/i
}
const timestampMatch = line.match(patterns.timestamp)
const ipMatch = line.match(patterns.ip)
const portMatch = line.match(patterns.port)
const protocolMatch = line.match(patterns.protocol)
const actionMatch = line.match(patterns.action)
if (!timestampMatch) return null
const currentYear = new Date().getFullYear()
const timestamp = new Date(`${currentYear} ${timestampMatch[1]}`).toISOString()
return {
id: `${type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp,
level: actionMatch && (actionMatch[1].toLowerCase() === 'block' || actionMatch[1].toLowerCase() === 'drop') ? 'block' : 'info',
message: line,
ip: ipMatch ? ipMatch[1] : undefined,
port: portMatch ? portMatch[1] : undefined,
protocol: protocolMatch ? protocolMatch[1].toUpperCase() : undefined,
action: actionMatch ? actionMatch[1].toUpperCase() : undefined,
type
}
}
function parseBlockedIPs(output: string) {
const blockedIPs = []
const lines = output.split('\\n').filter(line => line.trim())
for (const line of lines) {
const ipMatch = line.match(/(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})/)
if (ipMatch) {
blockedIPs.push({
ip: ipMatch[1],
reason: 'Firewall rule',
timestamp: new Date().toISOString()
})
}
}
return blockedIPs
}
function parseConnections(output: string) {
const connections = []
const lines = output.split('\\n').filter(line => line.trim())
for (const line of lines) {
const parts = line.trim().split(/\\s+/)
if (parts.length >= 4) {
const [protocol, , , localAddress, foreignAddress] = parts
connections.push({
protocol,
local_address: localAddress,
foreign_address: foreignAddress,
state: 'ESTABLISHED'
})
}
}
return connections
}