diff --git a/auth/center.go b/auth/center.go index 7821707..984162f 100644 --- a/auth/center.go +++ b/auth/center.go @@ -1,11 +1,13 @@ package auth import ( + "bytes" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" + "encoding/hex" "encoding/pem" "io/ioutil" "net/http" @@ -16,12 +18,16 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/pkg/errors" + "go.uber.org/zap" ) const authorizationFieldPattern = `AWS4-HMAC-SHA256 Credential=(?P[^/]+)/(?P[^/]+)/(?P[^/]*)/(?P[^/]+)/aws4_request, SignedHeaders=(?P.*), Signature=(?P.*)` +const emptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855` + // Center is a central app's authentication/authorization management unit. type Center struct { + log *zap.Logger submatcher *regexpSubmatcher zstdEncoder *zstd.Encoder zstdDecoder *zstd.Decoder @@ -38,10 +44,11 @@ type Center struct { } // NewCenter creates an instance of AuthCenter. -func NewCenter() *Center { +func NewCenter(log *zap.Logger) *Center { zstdEncoder, _ := zstd.NewWriter(nil) zstdDecoder, _ := zstd.NewReader(nil) return &Center{ + log: log, submatcher: ®expSubmatcher{re: regexp.MustCompile(authorizationFieldPattern)}, zstdEncoder: zstdEncoder, zstdDecoder: zstdDecoder, @@ -86,68 +93,72 @@ func (center *Center) SetUserAuthKeys(key *rsa.PrivateKey) { center.userAuthKeys.PublicKey = &key.PublicKey } -func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) ([]byte, error) { +func (center *Center) packBearerToken(bearerToken *service.BearerTokenMsg) (string, string, error) { data, err := bearerToken.Marshal() if err != nil { - return nil, errors.Wrap(err, "failed to marshal bearer token") + return "", "", errors.Wrap(err, "failed to marshal bearer token") } encryptedKeyID, err := encrypt(center.userAuthKeys.PublicKey, center.compress(data)) if err != nil { - return nil, errors.Wrap(err, "") + return "", "", errors.Wrap(err, "failed to encrypt bearer token bytes") } - return append(sha256Hash(data), encryptedKeyID...), nil + accessKeyID := hex.EncodeToString(encryptedKeyID) + secretAccessKey := hex.EncodeToString(sha256Hash(data)) + return accessKeyID, secretAccessKey, nil } -func (center *Center) unpackBearerToken(packedBearerToken []byte) (*service.BearerTokenMsg, error) { - compressedKeyID := packedBearerToken[32:] - encryptedKeyID, err := center.decompress(compressedKeyID) +func (center *Center) unpackBearerToken(accessKeyID string) (*service.BearerTokenMsg, error) { + encryptedKeyID, err := hex.DecodeString(accessKeyID) if err != nil { - return nil, errors.Wrap(err, "failed to decompress key ID") + return nil, errors.Wrap(err, "failed to decode HEX string") } - keyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) + compressedKeyID, err := decrypt(center.userAuthKeys.PrivateKey, encryptedKeyID) if err != nil { return nil, errors.Wrap(err, "failed to decrypt key ID") } + data, err := center.decompress(compressedKeyID) + if err != nil { + return nil, errors.Wrap(err, "failed to decompress key ID") + } bearerToken := new(service.BearerTokenMsg) - if err := bearerToken.Unmarshal(keyID); err != nil { + if err := bearerToken.Unmarshal(data); err != nil { return nil, errors.Wrap(err, "failed to unmarshal embedded bearer token") } return bearerToken, nil } -func (center *Center) AuthenticationPassed(header http.Header) (*service.BearerTokenMsg, error) { - authHeaderField := header["Authorization"] +func (center *Center) AuthenticationPassed(request *http.Request) (*service.BearerTokenMsg, error) { + authHeaderField := request.Header["Authorization"] if len(authHeaderField) != 1 { - return nil, errors.New("wrong length of Authorization header field") + return nil, errors.New("unsupported request: wrong length of Authorization header field") } sms := center.submatcher.getSubmatches(authHeaderField[0]) if len(sms) != 6 { return nil, errors.New("bad Authorization header field") } - akid := sms["access_key_id"] - bt, err := center.unpackBearerToken([]byte(akid)) + bt, err := center.unpackBearerToken(sms["access_key_id"]) if err != nil { - return nil, errors.Wrap(err, "failed to unpack bearer token") + center.log.Warn("Failed to unpack bearer token", zap.Error(err)) + //return nil, errors.Wrap(err, "failed to unpack bearer token") } - // v4sig := sms["v4_signature"] - // TODO: Validate V4 signature. return bt, nil } +func readAndReplaceBody(request *http.Request) []byte { + if request.Body == nil { + return []byte{} + } + payload, _ := ioutil.ReadAll(request.Body) + request.Body = ioutil.NopCloser(bytes.NewReader(payload)) + return payload +} + func (center *Center) compress(data []byte) []byte { - center.zstdEncoder.Reset(nil) - var compressedData []byte - center.zstdEncoder.EncodeAll(data, compressedData) - return compressedData + return center.zstdEncoder.EncodeAll(data, make([]byte, 0, len(data))) } func (center *Center) decompress(data []byte) ([]byte, error) { - center.zstdDecoder.Reset(nil) - var decompressedData []byte - if _, err := center.zstdDecoder.DecodeAll(data, decompressedData); err != nil { - return nil, err - } - return decompressedData, nil + return center.zstdDecoder.DecodeAll(data, nil) } func encrypt(key *rsa.PublicKey, data []byte) ([]byte, error) { diff --git a/cmd/gate/app-new-auth.go b/cmd/gate/app-new-auth.go index 5e77184..066092c 100644 --- a/cmd/gate/app-new-auth.go +++ b/cmd/gate/app-new-auth.go @@ -11,7 +11,7 @@ import ( func attachNewUserAuth(router *mux.Router, center *s3auth.Center, log *zap.Logger) { uamw := func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - _, err := center.AuthenticationPassed(r.Header) + _, err := center.AuthenticationPassed(r) if err != nil { log.Error("failed to pass authentication", zap.Error(err)) } diff --git a/cmd/gate/app-settings.go b/cmd/gate/app-settings.go index 9842293..e989505 100644 --- a/cmd/gate/app-settings.go +++ b/cmd/gate/app-settings.go @@ -110,7 +110,7 @@ func fetchAuthCenter(l *zap.Logger, v *viper.Viper) (*s3auth.Center, error) { if err != nil { return nil, errors.Wrap(err, "could not load UserAuth private key") } - center := s3auth.NewCenter() + center := s3auth.NewCenter(l) center.SetUserAuthKeys(userAuthPrivateKey) center.SetNeoFSKeys(neofsPrivateKey) return center, nil diff --git a/cmd/gate/app.go b/cmd/gate/app.go index a99c93e..a94c9f9 100644 --- a/cmd/gate/app.go +++ b/cmd/gate/app.go @@ -120,7 +120,7 @@ func newApp(l *zap.Logger, v *viper.Viper) *App { l.Info("used credentials", zap.String("AccessKey", uid.String()), zap.String("SecretKey", wif)) } - if obj, err = layer.NewLayer(cli, l, center); err != nil { + if obj, err = layer.NewLayer(l, cli, center); err != nil { l.Fatal("could not prepare ObjectLayer", zap.Error(err)) } } diff --git a/legacy/auth-handler.go b/legacy/auth-handler.go index 0d66d76..29c9803 100644 --- a/legacy/auth-handler.go +++ b/legacy/auth-handler.go @@ -274,13 +274,18 @@ func checkRequestAuthType(ctx context.Context, r *http.Request, action policy.Ac return s3Err } +// FIXME: Remove this temporary stub to by-pass Minio auth procedure. +func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (string, bool, APIErrorCode) { + return "", true, ErrNone +} + // Check request auth type verifies the incoming http request // - validates the request signature // - validates the policy action if anonymous tests bucket policies if any, // for authenticated requests validates IAM policies. // returns APIErrorCode if any to be replied to the client. // Additionally returns the accessKey used in the request, and if this request is by an admin. -func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (accessKey string, owner bool, s3Err APIErrorCode) { +func _checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, action policy.Action, bucketName, objectName string) (accessKey string, owner bool, s3Err APIErrorCode) { var cred auth.Credentials switch getRequestAuthType(r) { case authTypeUnknown, authTypeStreamingSigned: diff --git a/neofs/layer/gateway-neofs.go b/neofs/layer/gateway-neofs.go index 05898fe..71824dd 100644 --- a/neofs/layer/gateway-neofs.go +++ b/neofs/layer/gateway-neofs.go @@ -21,11 +21,12 @@ type ( neofsObject struct { minio.GatewayUnsupported // placeholder for unimplemented functions - cli pool.Client - log *zap.Logger - key *ecdsa.PrivateKey - owner refs.OwnerID - token *service.Token + log *zap.Logger + cli pool.Client + key *ecdsa.PrivateKey + owner refs.OwnerID + token *service.Token + bearerToken *service.BearerTokenMsg // Concurrency must be resolved by creating one lock per object, but // it may be unnecessary in neofs, because objects are immutable. So @@ -40,7 +41,7 @@ type ( // NewGatewayLayer creates instance of neofsObject. It checks credentials // and establishes gRPC connection with node. -func NewLayer(cli pool.Client, log *zap.Logger, center *s3auth.Center) (minio.ObjectLayer, error) { +func NewLayer(log *zap.Logger, cli pool.Client, center *s3auth.Center) (minio.ObjectLayer, error) { // setup gRPC connection // todo: think about getting timeout parameters from cli args ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) diff --git a/neofs/layer/neofs-container.go b/neofs/layer/neofs-container.go index e6a60bb..fb43031 100644 --- a/neofs/layer/neofs-container.go +++ b/neofs/layer/neofs-container.go @@ -16,6 +16,7 @@ func (n *neofsObject) containerList(ctx context.Context) ([]refs.CID, error) { req.OwnerID = n.owner req.SetTTL(service.SingleForwardingTTL) req.SetVersion(APIVersion) + req.SetBearer(nil) err := service.SignRequestData(n.key, req) if err != nil {