From cd17eb0fa742612ecaed964b92ced447eaa5ddab Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sun, 4 Aug 2024 15:34:31 +0200 Subject: [PATCH] activitypub: Sign the Host header too Mastodon with `AUTHORIZED_FETCH` enabled requires the `Host` header to be signed too, add it to the default for `setting.Federation.GetHeaders` and `setting.Federation.PostHeaders`. For this to work, we need to sign the request later: not immediately after `NewRequest`, but just before sending them out with `client.Do`. Doing so also lets us use `setting.Federation.GetHeaders` (we were using `.PostHeaders` even for GET requests before). Signed-off-by: Gergely Nagy --- modules/activitypub/client.go | 39 +++++++++++++++++++++++++---------- modules/setting/federation.go | 4 ++-- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index 38ccc58eb5..f07d3bc7d6 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -36,16 +36,19 @@ func CurrentTime() string { } func containsRequiredHTTPHeaders(method string, headers []string) error { - var hasRequestTarget, hasDate, hasDigest bool + var hasRequestTarget, hasDate, hasDigest, hasHost bool for _, header := range headers { hasRequestTarget = hasRequestTarget || header == httpsig.RequestTarget hasDate = hasDate || header == "Date" hasDigest = hasDigest || header == "Digest" + hasHost = hasHost || header == "Host" } if !hasRequestTarget { return fmt.Errorf("missing http header for %s: %s", method, httpsig.RequestTarget) } else if !hasDate { return fmt.Errorf("missing http header for %s: Date", method) + } else if !hasHost { + return fmt.Errorf("missing http header for %s: Host", method) } else if !hasDigest && method != http.MethodGet { return fmt.Errorf("missing http header for %s: Digest", method) } @@ -99,29 +102,36 @@ func NewClient(ctx context.Context, user *user_model.User, pubID string) (c *Cli } // NewRequest function -func (c *Client) NewRequest(method string, b []byte, to string) (req *http.Request, err error) { +func (c *Client) newRequest(method string, b []byte, to string) (req *http.Request, err error) { buf := bytes.NewBuffer(b) req, err = http.NewRequest(method, to, buf) if err != nil { return nil, err } - req.Header.Add("Content-Type", ActivityStreamsContentType) + req.Header.Add("Accept", "application/json, "+ActivityStreamsContentType) req.Header.Add("Date", CurrentTime()) + req.Header.Add("Host", req.URL.Host) req.Header.Add("User-Agent", "Gitea/"+setting.AppVer) - signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) - if err != nil { - return nil, err - } - err = signer.SignRequest(c.priv, c.pubID, req, b) + req.Header.Add("Content-Type", ActivityStreamsContentType) + return req, err } // Post function func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) { var req *http.Request - if req, err = c.NewRequest(http.MethodPost, b, to); err != nil { + if req, err = c.newRequest(http.MethodPost, b, to); err != nil { return nil, err } + + signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) + if err != nil { + return nil, err + } + if err := signer.SignRequest(c.priv, c.pubID, req, b); err != nil { + return nil, err + } + resp, err = c.client.Do(req) return resp, err } @@ -129,10 +139,17 @@ func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) { // Create an http GET request with forgejo/gitea specific headers func (c *Client) Get(to string) (resp *http.Response, err error) { var req *http.Request - emptyBody := []byte{0} - if req, err = c.NewRequest(http.MethodGet, emptyBody, to); err != nil { + if req, err = c.newRequest(http.MethodGet, nil, to); err != nil { return nil, err } + signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.getHeaders, httpsig.Signature, httpsigExpirationTime) + if err != nil { + return nil, err + } + if err := signer.SignRequest(c.priv, c.pubID, req, nil); err != nil { + return nil, err + } + resp, err = c.client.Do(req) return resp, err } diff --git a/modules/setting/federation.go b/modules/setting/federation.go index 2bea900633..aeb30683ea 100644 --- a/modules/setting/federation.go +++ b/modules/setting/federation.go @@ -25,8 +25,8 @@ var ( MaxSize: 4, Algorithms: []string{"rsa-sha256", "rsa-sha512", "ed25519"}, DigestAlgorithm: "SHA-256", - GetHeaders: []string{"(request-target)", "Date"}, - PostHeaders: []string{"(request-target)", "Date", "Digest"}, + GetHeaders: []string{"(request-target)", "Date", "Host"}, + PostHeaders: []string{"(request-target)", "Date", "Host", "Digest"}, } )