Skip to content

Commit

Permalink
pkg/linksharing/sharing: add support for Signature Version 4-signed URLs
Browse files Browse the repository at this point in the history
This change adds support for Signature Version 4-signed linksharing URLs
(only for signed and not for pre-signed). The code that implements the
new feature introduced a new package:
pkg/linksharing/sharing/internal/signed, as it's orthogonal to the rest
of the existing packages.

Signed URLs will allow users to use non-public access grants after
signing the URL using Secret Access Key corresponding to non-public
access grant. Furthermore, the key reason to implement this feature is
to allow satellite UI to use linksharing for object and nodes storing
the object-map previews (the reason we can't just use Gateway-MT), but
not make the credentials public.

Signature Version 4 verification/re-signing is implemented as in
https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html,
but a few assumptions were made for linksharing only. Specifically: we
always assume that the URL is non-pre-signed, we don't escape paths
Amazon-style (AWS also doesn't do this for S3), and we always assume an
empty request body (we only allow HEAD and GET requests).

Hosting requests are currently not supported due to shared cache per
custom hostname that successfully verified signed request would update
and make non-public access grant public from the linksharing
perspective.

Closes #113

Change-Id: I18cb4896ae36c48cf62fbabf6813f9aeff56622c
  • Loading branch information
amwolff committed Jan 12, 2022
1 parent 70c8236 commit 981f92a
Show file tree
Hide file tree
Showing 5 changed files with 876 additions and 14 deletions.
34 changes: 24 additions & 10 deletions pkg/linksharing/sharing/access.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,33 @@ package sharing
import (
"context"
"net/http"
"time"

"github.com/btcsuite/btcutil/base58"
"github.com/zeebo/errs"

"storj.io/gateway-mt/pkg/authclient"
"storj.io/gateway-mt/pkg/errdata"
"storj.io/gateway-mt/pkg/linksharing/sharing/internal/signed"
"storj.io/uplink"
)

// parseAccess parses access to identify if it's a valid access grant otherwise
// identifies as being an access key and request the Auth services to resolve
// it. clientIP is the IP of the client that originated the request and it's
// required to be sent to the Auth Service.
//
// It returns an error if the access grant is correctly encoded but it doesn't
// parse or if the Auth Service responds with an error.
func parseAccess(ctx context.Context, access string, cfg *authclient.AuthClient, clientIP string) (_ *uplink.Access, err error) {
// parseAccess guesses whether access is an access grant or Access Key ID. If
// latter, it contacts authservice to resolve it. If the resolved access grant
// isn't public, it will assume r is AWS Signature Version 4-signed if it
// contains a valid signature. signedAccessValidityTolerance is how much r's
// signature time can be skewed. clientIP is the IP of the client that
// originated the request and cannot be empty.
func parseAccess(
ctx context.Context,
r *http.Request,
access string,
signedAccessValidityTolerance time.Duration,
cfg *authclient.AuthClient,
clientIP string,
) (_ *uplink.Access, err error) {
defer mon.Task()(&ctx)(&err)

wrappedParse := func(access string) (*uplink.Access, error) {
parsed, err := uplink.ParseAccess(access)
if err != nil {
Expand All @@ -42,8 +51,13 @@ func parseAccess(ctx context.Context, access string, cfg *authclient.AuthClient,
if err != nil {
return nil, err
}
if !authResp.Public {
return nil, errdata.WithStatus(errs.New("non-public access key id"), http.StatusForbidden)
if !authResp.Public { // If credentials aren't public, assume signed request.
if err = signed.VerifySigningInfo(r, authResp.SecretKey, time.Now(), signedAccessValidityTolerance); err != nil {
if errs.Is(err, signed.ErrMissingAuthorizationHeader) {
return nil, errdata.WithStatus(errs.New("non-public Access Key ID"), http.StatusForbidden)
}
return nil, errdata.WithStatus(err, http.StatusForbidden)
}
}

return wrappedParse(authResp.AccessGrant)
Expand Down
Loading

0 comments on commit 981f92a

Please sign in to comment.