mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
91 lines
2.1 KiB
Go
91 lines
2.1 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
signingKeyVar = "NB_UPLOAD_SIGNING_KEY"
|
|
|
|
// signatureTTL matches the expiry the S3 backend puts on its presigned URLs.
|
|
signatureTTL = 15 * time.Minute
|
|
|
|
expiryParam = "exp"
|
|
signatureParam = "sig"
|
|
|
|
minSigningKeyLen = 32
|
|
)
|
|
|
|
type signer struct {
|
|
key []byte
|
|
}
|
|
|
|
func newSigner() (*signer, error) {
|
|
if env, ok := os.LookupEnv(signingKeyVar); ok {
|
|
if env == "" {
|
|
return nil, fmt.Errorf("%s is set but empty", signingKeyVar)
|
|
}
|
|
if len(env) < minSigningKeyLen {
|
|
return nil, fmt.Errorf("%s must be at least %d bytes", signingKeyVar, minSigningKeyLen)
|
|
}
|
|
return &signer{key: []byte(env)}, nil
|
|
}
|
|
|
|
key := make([]byte, 32)
|
|
if _, err := rand.Read(key); err != nil {
|
|
return nil, fmt.Errorf("generate signing key: %w", err)
|
|
}
|
|
log.Infof("%s not set, generated an ephemeral upload signing key", signingKeyVar)
|
|
|
|
return &signer{key: key}, nil
|
|
}
|
|
|
|
// sign returns the query parameters that authorize an upload of objectKey.
|
|
func (s *signer) sign(objectKey string, now time.Time) url.Values {
|
|
exp := now.Add(signatureTTL).Unix()
|
|
|
|
v := url.Values{}
|
|
v.Set(expiryParam, strconv.FormatInt(exp, 10))
|
|
v.Set(signatureParam, hex.EncodeToString(s.signature(objectKey, exp)))
|
|
|
|
return v
|
|
}
|
|
|
|
// verify reports whether query carries a still-valid signature over objectKey.
|
|
func (s *signer) verify(objectKey string, query url.Values, now time.Time) error {
|
|
exp, err := strconv.ParseInt(query.Get(expiryParam), 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("malformed %s parameter", expiryParam)
|
|
}
|
|
|
|
got, err := hex.DecodeString(query.Get(signatureParam))
|
|
if err != nil {
|
|
return fmt.Errorf("malformed %s parameter", signatureParam)
|
|
}
|
|
|
|
if !hmac.Equal(got, s.signature(objectKey, exp)) {
|
|
return fmt.Errorf("signature mismatch")
|
|
}
|
|
if now.Unix() >= exp {
|
|
return fmt.Errorf("upload URL expired")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *signer) signature(objectKey string, exp int64) []byte {
|
|
mac := hmac.New(sha256.New, s.key)
|
|
fmt.Fprintf(mac, "%s\n%d", objectKey, exp)
|
|
return mac.Sum(nil)
|
|
}
|