diff --git a/CHANGELOG.md b/CHANGELOG.md index 767cdb0..6c1ac12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This document outlines major changes between releases. - Support string `Action` and `Resource` fields in `bucketPolicy.Statement` (TrueCloudLab#32) - Add new `kludge.use_default_xmlns_for_complete_multipart` config param (TrueCloudLab#40) - Support dump metrics descriptions (#80) +- Support impersonate bearer token (#81) ### Changed - Update syncTree.sh due to recent renaming (#73) diff --git a/api/layer/layer.go b/api/layer/layer.go index 508505a..e66f362 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -329,7 +329,7 @@ func (n *layer) Owner(ctx context.Context) user.ID { func (n *layer) prepareAuthParameters(ctx context.Context, prm *PrmAuth, bktOwner user.ID) { if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil { - if bktOwner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) { + if bd.Gate.BearerToken.Impersonate() || bktOwner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) { prm.BearerToken = bd.Gate.BearerToken return } diff --git a/authmate/authmate.go b/authmate/authmate.go index 2d20cb1..ca0633d 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -99,6 +99,7 @@ type ( FrostFSKey *keys.PrivateKey GatesPublicKeys []*keys.PublicKey EACLRules []byte + Impersonate bool SessionTokenRules []byte SkipSessionRules bool Lifetime time.Duration @@ -344,16 +345,21 @@ func restrictedRecords() (records []*eacl.Record) { return } -func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, lifetime lifetimeOptions, gateKey *keys.PublicKey) (*bearer.Token, error) { +func buildBearerToken(key *keys.PrivateKey, impersonate bool, table *eacl.Table, lifetime lifetimeOptions, gateKey *keys.PublicKey) (*bearer.Token, error) { var ownerID user.ID user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*gateKey)) var bearerToken bearer.Token - bearerToken.SetEACLTable(*table) + + if !impersonate { + bearerToken.SetEACLTable(*table) + } + bearerToken.ForUser(ownerID) bearerToken.SetExp(lifetime.Exp) bearerToken.SetIat(lifetime.Iat) bearerToken.SetNbf(lifetime.Iat) + bearerToken.SetImpersonate(impersonate) err := bearerToken.Sign(key.PrivateKey) if err != nil { @@ -363,10 +369,10 @@ func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, lifetime lifetime return &bearerToken, nil } -func buildBearerTokens(key *keys.PrivateKey, table *eacl.Table, lifetime lifetimeOptions, gatesKeys []*keys.PublicKey) ([]*bearer.Token, error) { +func buildBearerTokens(key *keys.PrivateKey, impersonate bool, table *eacl.Table, lifetime lifetimeOptions, gatesKeys []*keys.PublicKey) ([]*bearer.Token, error) { bearerTokens := make([]*bearer.Token, 0, len(gatesKeys)) for _, gateKey := range gatesKeys { - tkn, err := buildBearerToken(key, table, lifetime, gateKey) + tkn, err := buildBearerToken(key, impersonate, table, lifetime, gateKey) if err != nil { return nil, fmt.Errorf("build bearer token: %w", err) } @@ -413,7 +419,8 @@ func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*acc if err != nil { return nil, fmt.Errorf("failed to build eacl table: %w", err) } - bearerTokens, err := buildBearerTokens(options.FrostFSKey, table, lifetime, options.GatesPublicKeys) + + bearerTokens, err := buildBearerTokens(options.FrostFSKey, options.Impersonate, table, lifetime, options.GatesPublicKeys) if err != nil { return nil, fmt.Errorf("failed to build bearer tokens: %w", err) } diff --git a/cmd/s3-authmate/main.go b/cmd/s3-authmate/main.go index a1629ad..3ac0f4c 100644 --- a/cmd/s3-authmate/main.go +++ b/cmd/s3-authmate/main.go @@ -56,6 +56,7 @@ var ( accountAddressFlag string peerAddressFlag string eaclRulesFlag string + disableImpersonateFlag bool gateWalletPathFlag string gateAccountAddressFlag string accessKeyIDFlag string @@ -207,10 +208,16 @@ func issueSecret() *cli.Command { }, &cli.StringFlag{ Name: "bearer-rules", - Usage: "rules for bearer token (filepath or a plain json string are allowed)", + Usage: "rules for bearer token (filepath or a plain json string are allowed, can be used only with --disable-impersonate)", Required: false, Destination: &eaclRulesFlag, }, + &cli.BoolFlag{ + Name: "disable-impersonate", + Usage: "mark token as not impersonate to don't consider token signer as request owner (must be provided to use --bearer-rules flag)", + Required: false, + Destination: &disableImpersonateFlag, + }, &cli.StringSliceFlag{ Name: "gate-public-key", Usage: "public 256r1 key of a gate (use flags repeatedly for multiple gates)", @@ -345,6 +352,10 @@ It will be ceil rounded to the nearest amount of epoch.`, return cli.Exit(fmt.Sprintf("couldn't parse container policy: %s", err.Error()), 6) } + if !disableImpersonateFlag && eaclRulesFlag != "" { + return cli.Exit("--bearer-rules flag can be used only with --disable-impersonate", 6) + } + bearerRules, err := getJSONRules(eaclRulesFlag) if err != nil { return cli.Exit(fmt.Sprintf("couldn't parse 'bearer-rules' flag: %s", err.Error()), 7) @@ -364,6 +375,7 @@ It will be ceil rounded to the nearest amount of epoch.`, FrostFSKey: key, GatesPublicKeys: gatesPublicKeys, EACLRules: bearerRules, + Impersonate: !disableImpersonateFlag, SessionTokenRules: sessionRules, SkipSessionRules: skipSessionRules, ContainerPolicies: policies, diff --git a/docs/authmate.md b/docs/authmate.md index 4e33f7a..e9cb8cc 100644 --- a/docs/authmate.md +++ b/docs/authmate.md @@ -139,12 +139,19 @@ the secret. Format of `access_key_id`: `%cid0%oid`, where 0(zero) is a delimiter Creation of bearer tokens is mandatory. -Rules for a bearer token can be set via parameter `--bearer-rules` (json-string and file path allowed): +By default, bearer token will be created with `impersonate` flag and won't have eACL table. It means that gate which will use such token +to interact with node can have access to your private containers or to containers in which eACL grants access to you +by public key. + +Rules for a bearer token can be set via parameter `--bearer-rules` (json-string and file path allowed). +But you must provide `--disable-impersonate` flag: + ```shell $ frostfs-s3-authmate issue-secret --wallet wallet.json \ --peer 192.168.130.71:8080 \ --gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf \ ---bearer-rules bearer-rules.json +--bearer-rules bearer-rules.json \ +--disable-impersonate ``` where content of `bearer-rules.json`: ```json diff --git a/internal/frostfs/services/tree_client_grpc.go b/internal/frostfs/services/tree_client_grpc.go index 11acc05..a6a3c99 100644 --- a/internal/frostfs/services/tree_client_grpc.go +++ b/internal/frostfs/services/tree_client_grpc.go @@ -8,12 +8,13 @@ import ( "strings" "sync/atomic" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" + "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" grpcService "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/services/tree" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/pkg/service/tree" - "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "go.uber.org/zap" "google.golang.org/grpc" @@ -385,7 +386,7 @@ func metaToKV(meta map[string]string) []*grpcService.KeyValue { func getBearer(ctx context.Context, bktInfo *data.BucketInfo) []byte { if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil { if bd.Gate.BearerToken != nil { - if bktInfo.Owner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) { + if bd.Gate.BearerToken.Impersonate() || bktInfo.Owner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) { return bd.Gate.BearerToken.Marshal() } }