feat: add ! kubectl command to terminal + restrict RBAC (no secrets/configmaps)
Build & Push Portfolio Image / build-push (push) Successful in 3m29s
Build & Push Portfolio Image / build-push (push) Successful in 3m29s
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
// Blocked resources (no read access from terminal)
|
||||
const BLOCKED_RESOURCES = [
|
||||
'secret',
|
||||
'secrets',
|
||||
'configmap',
|
||||
'configmaps',
|
||||
'certificate',
|
||||
'certificates',
|
||||
'key',
|
||||
'keys',
|
||||
'token',
|
||||
'tokens',
|
||||
'serviceaccount',
|
||||
'serviceaccounts',
|
||||
]
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { command } = await request.json()
|
||||
|
||||
if (!command || typeof command !== 'string') {
|
||||
return NextResponse.json({ error: 'Invalid command' }, { status: 400 })
|
||||
}
|
||||
|
||||
// Must start with kubectl (enforced by terminal, but double-check)
|
||||
if (!command.trim().startsWith('kubectl ')) {
|
||||
return NextResponse.json({ error: 'Only kubectl commands allowed' }, { status: 403 })
|
||||
}
|
||||
|
||||
// Check for blocked resources
|
||||
const cmdLower = command.toLowerCase()
|
||||
for (const blocked of BLOCKED_RESOURCES) {
|
||||
if (cmdLower.includes(blocked)) {
|
||||
return NextResponse.json(
|
||||
{ error: `Access denied: cannot read '${blocked}'` },
|
||||
{ status: 403 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute kubectl (uses homelab-agent read-only context)
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
timeout: 10000, // 10s timeout
|
||||
maxBuffer: 1024 * 1024, // 1MB max output
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
output: stdout || stderr,
|
||||
status: 'success',
|
||||
})
|
||||
} catch (error) {
|
||||
const err = error as Error & { code?: number }
|
||||
|
||||
// Timeout or execution error
|
||||
if (err.message.includes('ETIMEDOUT')) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Command timeout (10s limit)' },
|
||||
{ status: 504 }
|
||||
)
|
||||
}
|
||||
|
||||
return NextResponse.json(
|
||||
{ error: err.message || 'Command failed' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user