Auth fetch verification (cavage) works now
All checks were successful
/ docker (push) Successful in 4m14s

- Verifying inbound requests signed with Cavage are now checked as
  expected
- Fixed a bug where the signature header is not generated correctly
- Extended config to include settings for what requests to verify
- Fixed new server in main not using internal port from config
This commit is contained in:
Melody Becker 2025-04-22 15:27:24 +02:00
parent 271acc8d29
commit 627926460c
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
8 changed files with 90 additions and 36 deletions

View file

@ -6,6 +6,7 @@ import (
webutils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/web/public/api/activitypub"
webmiddleware "git.mstar.dev/mstar/linstrom/web/public/middleware"
)
@ -18,7 +19,10 @@ func BuildApiRouter() http.Handler {
"/activitypub",
webutils.ChainMiddlewares(
activitypub.BuildActivitypubRouter(),
webmiddleware.BuildAuthorizedFetchCheck(true, true),
webmiddleware.BuildAuthorizedFetchCheck(
config.GlobalConfig.Admin.AuthFetchForNonGet,
config.GlobalConfig.Admin.AuthFetchForGet,
),
),
),
)

View file

@ -5,10 +5,12 @@ import (
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"net/http"
"net/url"
"regexp"
"strings"
"time"
webutils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/goutils/other"
@ -17,12 +19,13 @@ import (
"git.mstar.dev/mstar/linstrom/activitypub"
"git.mstar.dev/mstar/linstrom/config"
"git.mstar.dev/mstar/linstrom/storage-new"
"git.mstar.dev/mstar/linstrom/storage-new/dbgen"
"git.mstar.dev/mstar/linstrom/storage-new/models"
webshared "git.mstar.dev/mstar/linstrom/web/shared"
)
const signatureRegexString = `keyId="([a-zA-Z0-9_\-\.:@\/\?&=#%\+\[\]!$\(\)\*,;]+)",(?:algorithm="([a-z0-9-]+)",)?headers="([a-z0-9-_\(\) ]+)",(?:algorithm="([a-z0-9-]+)",)?signature="(.+)"`
const signatureRegexString = `keyId="([a-zA-Z0-9_\-\.:@\/\?&=#%\+\[\]!$\(\)\*,;]+)",(?:algorithm="([a-z0-9-]+)",)?headers="([a-z0-9\-_\(\) ]+)",(?:algorithm="([a-z0-9-]+)",)?signature="(.+)"`
var publicPaths = []*regexp.Regexp{
regexp.MustCompile(`/\.well-known/.+^`),
@ -61,12 +64,12 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil
// So either you get the server actor, which is intended behaviour, or access to
// one specific note that most likely won't even exist
// FIXME: Re-enable once implementation of actual verification is stable
// if strings.Contains(path, storage.ServerActorId) {
// log.Info().Msg("Server actor requested, no auth")
// h.ServeHTTP(w, r)
// return
// }
if !config.GlobalConfig.Experimental.AuthFetchForServerActor &&
strings.Contains(path, storage.ServerActorId) {
log.Info().Msg("Server actor requested, no auth")
h.ServeHTTP(w, r)
return
}
// Not an always open path, check methods
if r.Method == "GET" && !forGet {
log.Info().Msg("Get request to AP resources don't need signature")
@ -78,6 +81,30 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil
return
}
log.Info().Msg("Need signature for AP request")
rawDate := r.Header.Get("Date")
date, err := http.ParseTime(rawDate)
if err != nil {
webutils.ProblemDetails(
w,
http.StatusBadRequest,
"/errors/bad-date",
"no or invalid date header",
nil,
nil,
)
return
}
if time.Since(date) > time.Hour+time.Minute*5 {
webutils.ProblemDetails(
w,
http.StatusUnauthorized,
"/errors/invalid-auth-signature",
"invalid authorization signature",
other.IntoPointer("Request is outdated"),
nil,
)
return
}
signatureHeader := r.Header.Get("Signature")
if signatureHeader == "" {
log.Info().Msg("Received AP request without signature header where one is required")
@ -117,7 +144,20 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil
rawAlgorithm1 := match[2]
rawHeaders := match[3]
rawAlgorithm2 := match[4]
signature := match[5]
signature, err := base64.StdEncoding.DecodeString(match[5])
if err != nil {
webutils.ProblemDetails(
w,
http.StatusUnauthorized,
"/errors/invalid-auth-signature",
"invalid authorization signature",
other.IntoPointer(
"Signature not decodable as bas64",
),
nil,
)
return
}
var rawAlgorithm string
if rawAlgorithm2 != "" {
@ -127,7 +167,7 @@ func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuil
} else {
rawAlgorithm = "hs2019"
}
_, err := url.Parse(rawKeyId)
_, err = url.Parse(rawKeyId)
if err != nil {
log.Warn().Err(err).Msg("Key id is not an url")
webutils.ProblemDetails(