forked from TrueCloudLab/frostfs-s3-gw
[#81] Use impersonate bearer token
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
e487ee5b7d
commit
b366e75366
6 changed files with 39 additions and 11 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue