fix(api/proxmox): add websocket validation to journalctl and tail endpoints

Add required websocket check at the beginning of both journalctl and tail endpoint handlers to ensure these endpoints only accept websocket connections.
This commit is contained in:
yusing
2026-02-22 19:54:09 +08:00
parent 2305eca90b
commit 4e5ded13fb
2 changed files with 20 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/yusing/godoxy/internal/proxmox" "github.com/yusing/godoxy/internal/proxmox"
"github.com/yusing/goutils/apitypes" "github.com/yusing/goutils/apitypes"
"github.com/yusing/goutils/http/httpheaders"
"github.com/yusing/goutils/http/websocket" "github.com/yusing/goutils/http/websocket"
) )
@@ -15,10 +16,10 @@ import (
// e.g. ws://localhost:8889/api/v1/proxmox/journalctl/pve/127?service=pveproxy&service=pvedaemon&limit=10 // e.g. ws://localhost:8889/api/v1/proxmox/journalctl/pve/127?service=pveproxy&service=pvedaemon&limit=10
type JournalctlRequest struct { type JournalctlRequest struct {
Node string `form:"node" uri:"node" binding:"required"` // Node name Node string `form:"node" uri:"node" binding:"required"` // Node name
VMID *int `form:"vmid" uri:"vmid"` // Container VMID (optional - if not provided, streams node journalctl) VMID *int `form:"vmid" uri:"vmid"` // Container VMID (optional - if not provided, streams node journalctl)
Services []string `form:"service" uri:"service"` // Service names Services []string `form:"service" uri:"service"` // Service names
Limit *int `form:"limit" uri:"limit" default:"100" binding:"min=1,max=1000"` // Limit output lines (1-1000) Limit *int `form:"limit" uri:"limit" default:"100" binding:"omitempty,min=1,max=1000"` // Limit output lines (1-1000)
} // @name ProxmoxJournalctlRequest } // @name ProxmoxJournalctlRequest
// @x-id "journalctl" // @x-id "journalctl"
@@ -40,6 +41,11 @@ type JournalctlRequest struct {
// @Router /proxmox/journalctl/{node}/{vmid} [get] // @Router /proxmox/journalctl/{node}/{vmid} [get]
// @Router /proxmox/journalctl/{node}/{vmid}/{service} [get] // @Router /proxmox/journalctl/{node}/{vmid}/{service} [get]
func Journalctl(c *gin.Context) { func Journalctl(c *gin.Context) {
if !httpheaders.IsWebsocket(c.Request.Header) {
c.JSON(http.StatusBadRequest, apitypes.Error("websocket required"))
return
}
var request JournalctlRequest var request JournalctlRequest
uriErr := c.ShouldBindUri(&request) uriErr := c.ShouldBindUri(&request)
queryErr := c.ShouldBindQuery(&request) queryErr := c.ShouldBindQuery(&request)
@@ -48,6 +54,10 @@ func Journalctl(c *gin.Context) {
return return
} }
if request.Limit == nil {
request.Limit = new(100)
}
node, ok := proxmox.Nodes.Get(request.Node) node, ok := proxmox.Nodes.Get(request.Node)
if !ok { if !ok {
c.JSON(http.StatusNotFound, apitypes.Error("node not found")) c.JSON(http.StatusNotFound, apitypes.Error("node not found"))

View File

@@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/yusing/godoxy/internal/proxmox" "github.com/yusing/godoxy/internal/proxmox"
"github.com/yusing/goutils/apitypes" "github.com/yusing/goutils/apitypes"
"github.com/yusing/goutils/http/httpheaders"
"github.com/yusing/goutils/http/websocket" "github.com/yusing/goutils/http/websocket"
) )
@@ -34,6 +35,11 @@ type TailRequest struct {
// @Failure 500 {object} apitypes.ErrorResponse "Internal server error" // @Failure 500 {object} apitypes.ErrorResponse "Internal server error"
// @Router /proxmox/tail [get] // @Router /proxmox/tail [get]
func Tail(c *gin.Context) { func Tail(c *gin.Context) {
if !httpheaders.IsWebsocket(c.Request.Header) {
c.JSON(http.StatusBadRequest, apitypes.Error("websocket required"))
return
}
var request TailRequest var request TailRequest
if err := c.ShouldBindQuery(&request); err != nil { if err := c.ShouldBindQuery(&request); err != nil {
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err)) c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))