Files
1Panel/core/utils/mcp/access_proof.go
T
蘭 d7355f3ccc feat(mcp): add 1Panel MCP and enhance MCP credential management (#14189)
* feat(mcp): add 1Panel MCP and enhance MCP credential management

* feat(i18n): implement MCP document localization and remove legacy code
2026-10-26 13:59:49 +08:00

97 lines
2.3 KiB
Go

package mcp
import (
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"net/netip"
"strings"
"sync"
"time"
)
const AccessProofHeader = "X-Panel-MCP-Access"
var accessProofKey struct {
sync.Once
value []byte
err error
}
type accessProof struct {
IP string `json:"ip"`
Token string `json:"token"`
Expires int64 `json:"expires"`
}
func proofKey() ([]byte, error) {
accessProofKey.Do(func() {
accessProofKey.value = make([]byte, 32)
_, accessProofKey.err = rand.Read(accessProofKey.value)
})
return accessProofKey.value, accessProofKey.err
}
func proofToken(token string) string {
value := sha256.Sum256([]byte(token))
return hex.EncodeToString(value[:])
}
func IssueAccessProof(token, source string, now time.Time) (string, error) {
address, err := netip.ParseAddr(source)
if err != nil || address.Zone() != "" || token == "" {
return "", errors.New("invalid MCP source proof")
}
key, err := proofKey()
if err != nil {
return "", err
}
data, err := json.Marshal(accessProof{IP: address.Unmap().String(), Token: proofToken(token), Expires: now.Add(400 * time.Second).Unix()})
if err != nil {
return "", err
}
mac := hmac.New(sha256.New, key)
mac.Write(data)
return base64.RawURLEncoding.EncodeToString(data) + "." + base64.RawURLEncoding.EncodeToString(mac.Sum(nil)), nil
}
func VerifyAccessProof(value, token string, now time.Time) (string, error) {
if len(value) > 512 {
return "", Forbidden()
}
parts := strings.Split(value, ".")
if len(parts) != 2 {
return "", Forbidden()
}
data, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
return "", Forbidden()
}
signature, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return "", Forbidden()
}
key, err := proofKey()
if err != nil {
return "", Forbidden()
}
mac := hmac.New(sha256.New, key)
mac.Write(data)
if !hmac.Equal(signature, mac.Sum(nil)) {
return "", Forbidden()
}
var proof accessProof
if json.Unmarshal(data, &proof) != nil || !hmac.Equal([]byte(proof.Token), []byte(proofToken(token))) || now.Unix() >= proof.Expires || proof.Expires > now.Add(401*time.Second).Unix() {
return "", Forbidden()
}
address, err := netip.ParseAddr(proof.IP)
if err != nil || address.Zone() != "" {
return "", Forbidden()
}
return address.Unmap().String(), nil
}