73 lines
2.0 KiB
Go
73 lines
2.0 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const internalSignatureWindow = 5 * time.Minute
|
|
|
|
func (a *API) withInternalAdminAuth(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
body, err := io.ReadAll(io.LimitReader(r.Body, 10<<20))
|
|
if err != nil {
|
|
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", nil)
|
|
return
|
|
}
|
|
_ = r.Body.Close()
|
|
r.Body = io.NopCloser(bytes.NewReader(body))
|
|
if err := a.verifyInternalAdminSignature(r, body); err != nil {
|
|
writeError(w, r, http.StatusForbidden, 30041, reasonForbidden, "forbidden", nil)
|
|
return
|
|
}
|
|
next(w, r)
|
|
}
|
|
}
|
|
|
|
func (a *API) verifyInternalAdminSignature(r *http.Request, body []byte) error {
|
|
secret := strings.TrimSpace(a.authCfg.InternalSecret)
|
|
if secret == "" {
|
|
return ErrForbidden
|
|
}
|
|
serviceName := strings.TrimSpace(r.Header.Get("X-Service-Name"))
|
|
if expected := strings.TrimSpace(a.authCfg.InternalServiceName); expected != "" && serviceName != expected {
|
|
return ErrForbidden
|
|
}
|
|
timestampRaw := strings.TrimSpace(r.Header.Get("X-Timestamp"))
|
|
nonce := strings.TrimSpace(r.Header.Get("X-Nonce"))
|
|
signature := strings.TrimSpace(r.Header.Get("X-Signature"))
|
|
if timestampRaw == "" || nonce == "" || signature == "" {
|
|
return ErrForbidden
|
|
}
|
|
ts, err := strconv.ParseInt(timestampRaw, 10, 64)
|
|
if err != nil {
|
|
return ErrForbidden
|
|
}
|
|
when := time.Unix(ts, 0)
|
|
if time.Since(when) > internalSignatureWindow || time.Until(when) > internalSignatureWindow {
|
|
return ErrForbidden
|
|
}
|
|
bodyHash := sha256.Sum256(body)
|
|
signingText := strings.Join([]string{
|
|
r.Method,
|
|
r.URL.RequestURI(),
|
|
hex.EncodeToString(bodyHash[:]),
|
|
timestampRaw,
|
|
nonce,
|
|
}, "\n")
|
|
mac := hmac.New(sha256.New, []byte(secret))
|
|
_, _ = mac.Write([]byte(signingText))
|
|
expected := hex.EncodeToString(mac.Sum(nil))
|
|
if !hmac.Equal([]byte(expected), []byte(signature)) {
|
|
return ErrForbidden
|
|
}
|
|
return nil
|
|
}
|