[#81] Use impersonate bearer token

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2022-10-25 12:30:18 +03:00 committed by Denis Kirillov
parent e487ee5b7d
commit b366e75366
6 changed files with 39 additions and 11 deletions

View file

@ -20,6 +20,7 @@ This document outlines major changes between releases.
- Support string `Action` and `Resource` fields in `bucketPolicy.Statement` (TrueCloudLab#32) - Support string `Action` and `Resource` fields in `bucketPolicy.Statement` (TrueCloudLab#32)
- Add new `kludge.use_default_xmlns_for_complete_multipart` config param (TrueCloudLab#40) - Add new `kludge.use_default_xmlns_for_complete_multipart` config param (TrueCloudLab#40)
- Support dump metrics descriptions (#80) - Support dump metrics descriptions (#80)
- Support impersonate bearer token (#81)
### Changed ### Changed
- Update syncTree.sh due to recent renaming (#73) - Update syncTree.sh due to recent renaming (#73)

View file

@ -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) { 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 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 prm.BearerToken = bd.Gate.BearerToken
return return
} }

View file

@ -99,6 +99,7 @@ type (
FrostFSKey *keys.PrivateKey FrostFSKey *keys.PrivateKey
GatesPublicKeys []*keys.PublicKey GatesPublicKeys []*keys.PublicKey
EACLRules []byte EACLRules []byte
Impersonate bool
SessionTokenRules []byte SessionTokenRules []byte
SkipSessionRules bool SkipSessionRules bool
Lifetime time.Duration Lifetime time.Duration
@ -344,16 +345,21 @@ func restrictedRecords() (records []*eacl.Record) {
return 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 var ownerID user.ID
user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*gateKey)) user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*gateKey))
var bearerToken bearer.Token var bearerToken bearer.Token
if !impersonate {
bearerToken.SetEACLTable(*table) bearerToken.SetEACLTable(*table)
}
bearerToken.ForUser(ownerID) bearerToken.ForUser(ownerID)
bearerToken.SetExp(lifetime.Exp) bearerToken.SetExp(lifetime.Exp)
bearerToken.SetIat(lifetime.Iat) bearerToken.SetIat(lifetime.Iat)
bearerToken.SetNbf(lifetime.Iat) bearerToken.SetNbf(lifetime.Iat)
bearerToken.SetImpersonate(impersonate)
err := bearerToken.Sign(key.PrivateKey) err := bearerToken.Sign(key.PrivateKey)
if err != nil { if err != nil {
@ -363,10 +369,10 @@ func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, lifetime lifetime
return &bearerToken, nil 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)) bearerTokens := make([]*bearer.Token, 0, len(gatesKeys))
for _, gateKey := range gatesKeys { for _, gateKey := range gatesKeys {
tkn, err := buildBearerToken(key, table, lifetime, gateKey) tkn, err := buildBearerToken(key, impersonate, table, lifetime, gateKey)
if err != nil { if err != nil {
return nil, fmt.Errorf("build bearer token: %w", err) return nil, fmt.Errorf("build bearer token: %w", err)
} }
@ -413,7 +419,8 @@ func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*acc
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to build eacl table: %w", err) 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 { if err != nil {
return nil, fmt.Errorf("failed to build bearer tokens: %w", err) return nil, fmt.Errorf("failed to build bearer tokens: %w", err)
} }

View file

@ -56,6 +56,7 @@ var (
accountAddressFlag string accountAddressFlag string
peerAddressFlag string peerAddressFlag string
eaclRulesFlag string eaclRulesFlag string
disableImpersonateFlag bool
gateWalletPathFlag string gateWalletPathFlag string
gateAccountAddressFlag string gateAccountAddressFlag string
accessKeyIDFlag string accessKeyIDFlag string
@ -207,10 +208,16 @@ func issueSecret() *cli.Command {
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "bearer-rules", 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, Required: false,
Destination: &eaclRulesFlag, 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{ &cli.StringSliceFlag{
Name: "gate-public-key", Name: "gate-public-key",
Usage: "public 256r1 key of a gate (use flags repeatedly for multiple gates)", 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) 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) bearerRules, err := getJSONRules(eaclRulesFlag)
if err != nil { if err != nil {
return cli.Exit(fmt.Sprintf("couldn't parse 'bearer-rules' flag: %s", err.Error()), 7) 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, FrostFSKey: key,
GatesPublicKeys: gatesPublicKeys, GatesPublicKeys: gatesPublicKeys,
EACLRules: bearerRules, EACLRules: bearerRules,
Impersonate: !disableImpersonateFlag,
SessionTokenRules: sessionRules, SessionTokenRules: sessionRules,
SkipSessionRules: skipSessionRules, SkipSessionRules: skipSessionRules,
ContainerPolicies: policies, ContainerPolicies: policies,

View file

@ -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. 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 ```shell
$ frostfs-s3-authmate issue-secret --wallet wallet.json \ $ frostfs-s3-authmate issue-secret --wallet wallet.json \
--peer 192.168.130.71:8080 \ --peer 192.168.130.71:8080 \
--gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf \ --gate-public-key 0313b1ac3a8076e155a7e797b24f0b650cccad5941ea59d7cfd51a024a8b2a06bf \
--bearer-rules bearer-rules.json --bearer-rules bearer-rules.json \
--disable-impersonate
``` ```
where content of `bearer-rules.json`: where content of `bearer-rules.json`:
```json ```json

View file

@ -8,12 +8,13 @@ import (
"strings" "strings"
"sync/atomic" "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"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/data"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
grpcService "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/services/tree" 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-s3-gw/pkg/service/tree"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"go.uber.org/zap" "go.uber.org/zap"
"google.golang.org/grpc" "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 { 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, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil {
if bd.Gate.BearerToken != 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() return bd.Gate.BearerToken.Marshal()
} }
} }