2022-04-11 19:49:34 +08:00
|
|
|
package proxy
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2023-03-24 15:27:58 +08:00
|
|
|
"fmt"
|
2022-04-11 19:49:34 +08:00
|
|
|
"strings"
|
|
|
|
|
2023-03-23 16:47:57 +08:00
|
|
|
"go.uber.org/zap"
|
2023-11-03 11:00:17 +08:00
|
|
|
"google.golang.org/grpc/codes"
|
2023-03-21 11:37:56 +08:00
|
|
|
"google.golang.org/grpc/metadata"
|
2023-11-03 11:00:17 +08:00
|
|
|
"google.golang.org/grpc/status"
|
2023-04-06 19:14:32 +08:00
|
|
|
|
|
|
|
"github.com/milvus-io/milvus/pkg/log"
|
|
|
|
"github.com/milvus-io/milvus/pkg/metrics"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/crypto"
|
|
|
|
"github.com/milvus-io/milvus/pkg/util/merr"
|
2022-04-11 19:49:34 +08:00
|
|
|
)
|
|
|
|
|
2023-10-17 21:00:11 +08:00
|
|
|
func parseMD(rawToken string) (username, password string) {
|
2022-04-11 19:49:34 +08:00
|
|
|
secrets := strings.SplitN(rawToken, util.CredentialSeperator, 2)
|
2023-05-08 10:28:39 +08:00
|
|
|
if len(secrets) < 2 {
|
|
|
|
log.Warn("invalid token format, length of secrets less than 2")
|
|
|
|
return
|
|
|
|
}
|
2023-03-23 16:47:57 +08:00
|
|
|
username = secrets[0]
|
|
|
|
password = secrets[1]
|
|
|
|
return
|
2022-04-11 19:49:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func validSourceID(ctx context.Context, authorization []string) bool {
|
|
|
|
if len(authorization) < 1 {
|
2023-09-21 09:45:27 +08:00
|
|
|
// log.Warn("key not found in header", zap.String("key", util.HeaderSourceID))
|
2022-04-11 19:49:34 +08:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
// token format: base64<sourceID>
|
|
|
|
token := authorization[0]
|
|
|
|
sourceID, err := crypto.Base64Decode(token)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return sourceID == util.MemberCredID
|
|
|
|
}
|
|
|
|
|
|
|
|
// AuthenticationInterceptor verify based on kv pair <"authorization": "token"> in header
|
|
|
|
func AuthenticationInterceptor(ctx context.Context) (context.Context, error) {
|
|
|
|
// The keys within metadata.MD are normalized to lowercase.
|
|
|
|
// See: https://godoc.org/google.golang.org/grpc/metadata#New
|
|
|
|
md, ok := metadata.FromIncomingContext(ctx)
|
|
|
|
if !ok {
|
2023-03-24 15:27:58 +08:00
|
|
|
return nil, merr.WrapErrIoKeyNotFound("metadata", "auth check failure, due to occurs inner error: missing metadata")
|
2022-04-11 19:49:34 +08:00
|
|
|
}
|
2022-04-14 20:01:34 +08:00
|
|
|
if globalMetaCache == nil {
|
2023-03-24 15:27:58 +08:00
|
|
|
return nil, merr.WrapErrServiceUnavailable("internal: Milvus Proxy is not ready yet. please wait")
|
2022-04-14 20:01:34 +08:00
|
|
|
}
|
2022-04-11 19:49:34 +08:00
|
|
|
// check:
|
|
|
|
// 1. if rpc call from a member (like index/query/data component)
|
|
|
|
// 2. if rpc call from sdk
|
2022-12-07 18:01:19 +08:00
|
|
|
if Params.CommonCfg.AuthorizationEnabled.GetAsBool() {
|
2023-03-23 16:47:57 +08:00
|
|
|
if !validSourceID(ctx, md[strings.ToLower(util.HeaderSourceID)]) {
|
2023-10-17 21:00:11 +08:00
|
|
|
authStrArr := md[strings.ToLower(util.HeaderAuthorize)]
|
|
|
|
|
|
|
|
if len(authStrArr) < 1 {
|
|
|
|
log.Warn("key not found in header")
|
2023-11-15 14:40:26 +08:00
|
|
|
return nil, status.Error(codes.Unauthenticated, "missing authorization in header")
|
2023-10-17 21:00:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// token format: base64<username:password>
|
|
|
|
// token := strings.TrimPrefix(authorization[0], "Bearer ")
|
|
|
|
token := authStrArr[0]
|
|
|
|
rawToken, err := crypto.Base64Decode(token)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn("fail to decode the token", zap.Error(err))
|
2023-11-15 14:40:26 +08:00
|
|
|
return nil, status.Error(codes.Unauthenticated, "invalid token format")
|
2023-10-17 21:00:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if !strings.Contains(rawToken, util.CredentialSeperator) {
|
2023-10-18 16:36:12 +08:00
|
|
|
user, err := VerifyAPIKey(rawToken)
|
2023-10-17 21:00:11 +08:00
|
|
|
if err != nil {
|
2023-10-18 16:36:12 +08:00
|
|
|
log.Warn("fail to verify apikey", zap.Error(err))
|
2023-11-15 14:40:26 +08:00
|
|
|
return nil, status.Error(codes.Unauthenticated, "auth check failure, please check api key is correct")
|
2023-10-17 21:00:11 +08:00
|
|
|
}
|
|
|
|
metrics.UserRPCCounter.WithLabelValues(user).Inc()
|
2024-04-16 16:47:19 +08:00
|
|
|
userToken := fmt.Sprintf("%s%s%s", user, util.CredentialSeperator, util.PasswordHolder)
|
2023-10-17 21:00:11 +08:00
|
|
|
md[strings.ToLower(util.HeaderAuthorize)] = []string{crypto.Base64Encode(userToken)}
|
|
|
|
ctx = metadata.NewIncomingContext(ctx, md)
|
|
|
|
} else {
|
|
|
|
// username+password authentication
|
|
|
|
username, password := parseMD(rawToken)
|
|
|
|
if !passwordVerify(ctx, username, password, globalMetaCache) {
|
2023-11-15 14:40:26 +08:00
|
|
|
log.Warn("fail to verify password", zap.String("username", username))
|
2023-11-03 11:00:17 +08:00
|
|
|
// NOTE: don't use the merr, because it will cause the wrong retry behavior in the sdk
|
2023-11-15 14:40:26 +08:00
|
|
|
return nil, status.Error(codes.Unauthenticated, "auth check failure, please check username and password are correct")
|
2023-10-17 21:00:11 +08:00
|
|
|
}
|
|
|
|
metrics.UserRPCCounter.WithLabelValues(username).Inc()
|
2023-03-23 16:47:57 +08:00
|
|
|
}
|
2022-04-11 19:49:34 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ctx, nil
|
|
|
|
}
|