feat(diagnostics): UI-Tools — ping/traceroute/dig/curl/tcp
Operator-Diagnose ohne SSH. Sidebar → System → Diagnose.
internal/services/diagnostics/:
- Ping — ping -c4 -W2 (12s timeout)
- Traceroute — traceroute -n -w2 -q1 -m20 (60s timeout)
- Dig — dig +timeout=3 +tries=2 <TYPE> <NAME> (Types A/AAAA/
CNAME/MX/TXT/NS/SOA/PTR/SRV/CAA whitelisted)
- Curl — curl -IsSv --max-time 10 (nur http(s)://, kein
file:// / smb:// / data://)
- TCPProbe — nc -zv -w5 (8s timeout)
Sicherheit: validTarget() prüft jeden Input gegen Allow-List
[a-zA-Z0-9.:/_-]; verhindert Shell-Metachar-Injection. exec.Command
mit nackten Argument-Slices (kein /bin/sh, kein Glob-Expansion).
internal/handlers/diagnostics.go: POST /api/v1/diagnostics/<tool>
hinter requireAuth.
UI (pages/Diagnostics): 5 Tool-Cards, jede mit eigenem Input + Run-
Button + monospace-Output-Pane (dunkel, scrollbar, max-height 320px).
Pro Tool ein Status-Tag (OK / exit N) + Dauer-ms. info-Alert oben
erklärt dass Tools auf der Box laufen, nicht im Browser; security-
Alert unten erklärt die Restrictions.
control: iputils-ping, traceroute, dnsutils, curl, netcat-openbsd
als Depends. Auf Test-Box bereits da (waren Distro-defaults).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
108
internal/handlers/diagnostics.go
Normal file
108
internal/handlers/diagnostics.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/handlers/response"
|
||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/diagnostics"
|
||||
)
|
||||
|
||||
// DiagnosticsHandler exposes /api/v1/diagnostics/{ping,traceroute,
|
||||
// dig,curl,tcp}. Authentifiziert hinter requireAuth — wir wollen das
|
||||
// keine externen Scanner als Reflection-Service missbrauchen können.
|
||||
type DiagnosticsHandler struct{}
|
||||
|
||||
func NewDiagnosticsHandler() *DiagnosticsHandler { return &DiagnosticsHandler{} }
|
||||
|
||||
func (h *DiagnosticsHandler) Register(rg *gin.RouterGroup) {
|
||||
g := rg.Group("/diagnostics")
|
||||
g.POST("/ping", h.Ping)
|
||||
g.POST("/traceroute", h.Traceroute)
|
||||
g.POST("/dig", h.Dig)
|
||||
g.POST("/curl", h.Curl)
|
||||
g.POST("/tcp", h.TCP)
|
||||
}
|
||||
|
||||
type targetReq struct {
|
||||
Target string `json:"target" binding:"required"`
|
||||
}
|
||||
|
||||
type digReq struct {
|
||||
Target string `json:"target" binding:"required"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type tcpReq struct {
|
||||
Target string `json:"target" binding:"required"`
|
||||
Port int `json:"port" binding:"required"`
|
||||
}
|
||||
|
||||
func (h *DiagnosticsHandler) Ping(c *gin.Context) {
|
||||
var req targetReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := diagnostics.Ping(c.Request.Context(), req.Target)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, res)
|
||||
}
|
||||
|
||||
func (h *DiagnosticsHandler) Traceroute(c *gin.Context) {
|
||||
var req targetReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := diagnostics.Traceroute(c.Request.Context(), req.Target)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, res)
|
||||
}
|
||||
|
||||
func (h *DiagnosticsHandler) Dig(c *gin.Context) {
|
||||
var req digReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := diagnostics.Dig(c.Request.Context(), req.Target, req.Type)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, res)
|
||||
}
|
||||
|
||||
func (h *DiagnosticsHandler) Curl(c *gin.Context) {
|
||||
var req targetReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := diagnostics.Curl(c.Request.Context(), req.Target)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, res)
|
||||
}
|
||||
|
||||
func (h *DiagnosticsHandler) TCP(c *gin.Context) {
|
||||
var req tcpReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
res, err := diagnostics.TCPProbe(c.Request.Context(), req.Target, req.Port)
|
||||
if err != nil {
|
||||
response.BadRequest(c, err)
|
||||
return
|
||||
}
|
||||
response.OK(c, res)
|
||||
}
|
||||
Reference in New Issue
Block a user