Start work on auth fetch middleware
All checks were successful
/ docker (push) Successful in 4m12s

This commit is contained in:
Melody Becker 2025-04-16 16:43:57 +02:00
parent b00f5e9777
commit 7eac1db475
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
4 changed files with 111 additions and 5 deletions

View file

@ -77,7 +77,7 @@ func ImportRemoteAccount(targetName string) (string, error) {
} }
defer response.Body.Close() defer response.Body.Close()
body, _ := io.ReadAll(response.Body) body, _ := io.ReadAll(response.Body)
log.Trace(). log.Debug().
Int("status", response.StatusCode). Int("status", response.StatusCode).
Bytes("body", body). Bytes("body", body).
Any("headers", response.Header). Any("headers", response.Header).
@ -394,7 +394,7 @@ func ImportRemoteServer(host string) (uint, error) {
} }
} }
} }
if nodeAdmins, ok := data.Metadata["nodeAdmins"].([]map[string]any); ok { if nodeAdmins, ok := data.Metadata["nodeAdmins"].([]map[string]any); ok && len(nodeAdmins) > 0 {
log.Debug().Msg("Node admins url found") log.Debug().Msg("Node admins url found")
targets := sliceutils.Filter( targets := sliceutils.Filter(
existingEntry.Metadata, existingEntry.Metadata,
@ -530,7 +530,7 @@ func ImportRemoteServer(host string) (uint, error) {
} }
} }
} }
if staffAccounts, ok := data.Metadata["nodeAdmins"].([]any); ok { if staffAccounts, ok := data.Metadata["nodeAdmins"].([]any); ok && len(staffAccounts) > 0 {
log.Debug().Msg("Node admins url found") log.Debug().Msg("Node admins url found")
targets := sliceutils.Filter( targets := sliceutils.Filter(
existingEntry.Metadata, existingEntry.Metadata,

View file

@ -1,7 +1,7 @@
[general] [general]
protocol = "https" protocol = "https"
domain = "lhr.life" domain = "lhr.life"
subdomain = "47565bb39100de" subdomain = "3e4af10addc7d0"
private_port = 8080 private_port = 8080
public_port = 443 public_port = 443

View file

@ -12,12 +12,14 @@ var errorDescriptions = map[string]string{
"db-failure": "The database query for this request failed for an undisclosed reason. This is often caused by bad input data conflicting with existing information. Try to submit different data or wait for some time", "db-failure": "The database query for this request failed for an undisclosed reason. This is often caused by bad input data conflicting with existing information. Try to submit different data or wait for some time",
"bad-request-data": "The data provided in the request doesn't match the requirements, see problem details' detail field for more information", "bad-request-data": "The data provided in the request doesn't match the requirements, see problem details' detail field for more information",
"bad-page": "The provided page number was not valid. See response details for more information", "bad-page": "The provided page number was not valid. See response details for more information",
"bad-accept-mime-type": "The requested mime type is not supported for the target url",
"invalid-auth-signature": "The requested URL requires verification via authorized fetch. See https://swicg.github.io/activitypub-http-signature/ and https://datatracker.ietf.org/doc/html/rfc9421 (not implemented yet) for details on how to verify your request",
} }
func errorTypeHandler(w http.ResponseWriter, r *http.Request) { func errorTypeHandler(w http.ResponseWriter, r *http.Request) {
errName := r.PathValue("name") errName := r.PathValue("name")
if description, ok := errorDescriptions[errName]; ok { if description, ok := errorDescriptions[errName]; ok {
fmt.Fprint(w, description) _, _ = fmt.Fprint(w, description)
} else { } else {
webutils.ProblemDetailsStatusOnly(w, 404) webutils.ProblemDetailsStatusOnly(w, 404)
} }

View file

@ -0,0 +1,104 @@
package webmiddleware
import (
"net/http"
"net/url"
"regexp"
webutils "git.mstar.dev/mstar/goutils/http"
"git.mstar.dev/mstar/goutils/other"
)
const signatureRegexString = `keyId=([a-zA-Z0-9_\-\.:@/\?&=#%\+\[\]!$\(\)\*,;]+),headers="([a-z0-9-_\(\) ]+)",(?:algorithm="([a-z0-9-])+",)?signature="(.+)"`
var publicPaths = []*regexp.Regexp{
regexp.MustCompile(`/\.well-known/.+^`),
// regexp.MustCompile(``),
}
var signatureRegex = regexp.MustCompile(signatureRegexString)
// Builder for the authorized fetch check middleware.
// forNonGet determines whether the check should happen for non-GET requests.
// forGet determines whether the check should also happen for GET requests.
// forGet being true implicitly sets forNonGet true too.
// Requests to the server actor and other, required public resources
// will never be checked
func BuildAuthorizedFetchCheck(forNonGet bool, forGet bool) webutils.HandlerBuilder {
return func(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
// Check always open path first
for _, re := range publicPaths {
if re.Match([]byte(path)) {
h.ServeHTTP(w, r)
return
}
}
// Not an always open path, check methods
if r.Method == "GET" && !forGet {
h.ServeHTTP(w, r)
return
} else if !forGet && !forNonGet {
h.ServeHTTP(w, r)
return
}
// TODO: Perform check here
signatureHeader := r.Header.Get("Signature")
if signatureHeader == "" {
webutils.ProblemDetails(
w,
http.StatusUnauthorized,
"/errors/invalid-auth-signature",
"invalid authorization signature",
other.IntoPointer("Missing Signature header for authorization"),
nil,
)
return
}
match := signatureRegex.FindStringSubmatch(signatureHeader)
if len(match) <= 1 {
webutils.ProblemDetails(
w,
http.StatusUnauthorized,
"/errors/invalid-auth-signature",
"invalid authorization signature",
other.IntoPointer("Invalid signature header format"),
map[string]any{
"used signature regex": signatureRegexString,
"signature format": `keyId="<url to actor with used key>",headers="<list of headers used, including pseudo header (request-target)>",(optional: algorithm="<one of rsa-sha256, hs2019>", (defaults to rsa-sha256 if not set))signature="signed message"`,
},
)
return
}
// fullMatch = match[0]
rawKeyId := match[1]
rawHeaders := match[2]
rawAlgorithm := match[3]
signature := match[4]
_, err := url.Parse(rawKeyId)
if err != nil {
webutils.ProblemDetails(
w,
http.StatusUnauthorized,
"/errors/invalid-auth-signature",
"invalid authorization signature",
other.IntoPointer("keyId must be a valid url"),
nil,
)
return
}
if rawAlgorithm == "" {
rawAlgorithm = "hs2019"
// w.Header().Add("X-Algorithm-Hint", "")
}
_ = rawHeaders
_ = signature
_ = rawAlgorithm
panic("not implemented")
})
}
}