mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-10-27 08:00:14 +00:00
* feat(mcp): add 1Panel MCP and enhance MCP credential management * feat(i18n): implement MCP document localization and remove legacy code
80 lines
2.2 KiB
Go
80 lines
2.2 KiB
Go
package mcpapi
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
InvocationHeader = "X-Panel-MCP-Invocation"
|
|
NodeHeader = "X-Panel-MCP-Node"
|
|
MethodHeader = "X-Panel-MCP-Method"
|
|
PathHeader = "X-Panel-MCP-Path"
|
|
CapabilitiesPath = "/api/v2/internal/mcp/capabilities"
|
|
ContractVersion = 4
|
|
)
|
|
|
|
type verifiedNodeTransportKey struct{}
|
|
|
|
func BindVerifiedNodeTransport(request *http.Request) *http.Request {
|
|
if request.TLS == nil || !request.TLS.HandshakeComplete || len(request.TLS.VerifiedChains) == 0 {
|
|
return request
|
|
}
|
|
return request.WithContext(context.WithValue(request.Context(), verifiedNodeTransportKey{}, request.TLS))
|
|
}
|
|
|
|
func validNodeID(id string) bool {
|
|
if id == "local" {
|
|
return true
|
|
}
|
|
value, err := strconv.ParseUint(id, 10, 64)
|
|
return err == nil && value != 0 && strconv.FormatUint(value, 10) == id
|
|
}
|
|
|
|
func TrustedTransport(request *http.Request) bool {
|
|
if global.IsMaster {
|
|
local, ok := request.Context().Value(http.LocalAddrContextKey).(net.Addr)
|
|
return ok && local.Network() == "unix"
|
|
}
|
|
verified := request.Context().Value(verifiedNodeTransportKey{})
|
|
return request.TLS != nil && verified == request.TLS
|
|
}
|
|
|
|
func Check(request *http.Request) (present, trusted bool) {
|
|
present = false
|
|
for _, name := range []string{InvocationHeader, NodeHeader, MethodHeader, PathHeader} {
|
|
if len(request.Header.Values(name)) != 0 {
|
|
present = true
|
|
}
|
|
}
|
|
if !present {
|
|
return false, false
|
|
}
|
|
for _, name := range []string{InvocationHeader, NodeHeader, MethodHeader, PathHeader} {
|
|
if len(request.Header.Values(name)) != 1 {
|
|
return true, false
|
|
}
|
|
}
|
|
if _, err := uuid.Parse(request.Header.Get(InvocationHeader)); err != nil {
|
|
return true, false
|
|
}
|
|
nodeID := request.Header.Get(NodeHeader)
|
|
if !validNodeID(nodeID) || request.Header.Get(MethodHeader) != request.Method || request.Header.Get(PathHeader) != request.URL.Path || request.URL.RawQuery != "" {
|
|
return true, false
|
|
}
|
|
return true, TrustedTransport(request) && ((nodeID == "local") == global.IsMaster)
|
|
}
|
|
|
|
func NodeID(request *http.Request) (string, bool) {
|
|
_, trusted := Check(request)
|
|
if !trusted {
|
|
return "", false
|
|
}
|
|
return request.Header.Get(NodeHeader), true
|
|
}
|