forked from TrueCloudLab/frostfs-s3-gw
[#72] authmate: Add session token
Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
This commit is contained in:
parent
0814bf3d01
commit
44da6cf70c
4 changed files with 124 additions and 28 deletions
31
README.md
31
README.md
|
@ -231,13 +231,18 @@ used to create new AWS credentials.
|
||||||
|
|
||||||
#### Issuance of a secret
|
#### Issuance of a secret
|
||||||
|
|
||||||
To issue a secret means to create a Bearer token and put it as an object into
|
To issue a secret means to create a Bearer and (optionally) Session tokens and
|
||||||
container on the NeoFS network. The token is encrypted by a set of gateway
|
put them as an object into container on the NeoFS network. The tokens are
|
||||||
keys, so you need to pass them as well.
|
encrypted by a set of gateway keys, so you need to pass them as well.
|
||||||
|
|
||||||
If a parameter `container-id` is not set, a new container will be created.
|
If a parameter `container-id` is not set, a new container will be created.
|
||||||
|
|
||||||
If a parameter `rules` is not set, it will be auto-generated with values:
|
Creation of the bearer token is mandatory, and creation of the session token is
|
||||||
|
optional. If you want to add the session token you need to add a parameter
|
||||||
|
`create-session-token`.
|
||||||
|
|
||||||
|
Rules for bearer token can be set via param `bearer-rules`, if it is not set,
|
||||||
|
it will be auto-generated with values:
|
||||||
|
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
|
@ -264,13 +269,27 @@ If a parameter `rules` is not set, it will be auto-generated with values:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Rules for session token can be set via param `session-rules`, default value is:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"verb": "PUT",
|
||||||
|
"wildcard": true,
|
||||||
|
"containerID": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If `session-rules` is set, but `create-session-token` is not, the session
|
||||||
|
token will not be created.
|
||||||
|
|
||||||
Example of a command to issue a secret with custom rules for multiple gates:
|
Example of a command to issue a secret with custom rules for multiple gates:
|
||||||
```
|
```
|
||||||
$ ./neofs-authmate issue-secret --neofs-key user.key \
|
$ ./neofs-authmate issue-secret --neofs-key user.key \
|
||||||
--peer 192.168.130.71:8080 \
|
--peer 192.168.130.71:8080 \
|
||||||
--rules '{"records":[{"operation":"PUT","action":"ALLOW","filters":[],"targets":[{"role":"OTHERS","keys":[]}]}]}' \
|
--bearer-rules '{"records":[{"operation":"PUT","action":"ALLOW","filters":[],"targets":[{"role":"OTHERS","keys":[]}]}]}' \
|
||||||
--gate-public-key dd34f6dce9a4ce0990869ec6bd33a40e102a5798881cfe61d03a5659ceee1a64 \
|
--gate-public-key dd34f6dce9a4ce0990869ec6bd33a40e102a5798881cfe61d03a5659ceee1a64 \
|
||||||
--gate-public-key 20453af9d7f245ff6fdfb1260eaa411ae3be9c519a2a9bf1c98233522cbd0156
|
--gate-public-key 20453af9d7f245ff6fdfb1260eaa411ae3be9c519a2a9bf1c98233522cbd0156 \
|
||||||
|
--create-session-token \
|
||||||
|
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'
|
||||||
|
|
||||||
{
|
{
|
||||||
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
|
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
|
||||||
|
|
|
@ -12,12 +12,14 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
"github.com/nspcc-dev/neofs-api-go/pkg/container"
|
||||||
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/netmap"
|
"github.com/nspcc-dev/neofs-api-go/pkg/netmap"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
"github.com/nspcc-dev/neofs-api-go/pkg/object"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||||
|
"github.com/nspcc-dev/neofs-api-go/pkg/session"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
||||||
|
@ -53,6 +55,8 @@ type (
|
||||||
OwnerPrivateKey hcs.PrivateKey
|
OwnerPrivateKey hcs.PrivateKey
|
||||||
GatesPublicKeys []hcs.PublicKey
|
GatesPublicKeys []hcs.PublicKey
|
||||||
EACLRules []byte
|
EACLRules []byte
|
||||||
|
ContextRules []byte
|
||||||
|
SessionTkn bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
||||||
|
@ -138,35 +142,64 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
a.log.Info("prepare eACL table")
|
box.SetOwnerPublicKey(options.OwnerPrivateKey.PublicKey())
|
||||||
|
|
||||||
table, err := buildEACLTable(cid, options.EACLRules)
|
oid, err := ownerIDFromNeoFSKey(&options.NeoFSKey.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.log.Info("prepare eACL table")
|
||||||
|
bearerRules, err := buildEACLTable(cid, options.EACLRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build eacl table: %w", err)
|
return fmt.Errorf("failed to build eacl table: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tkn, err := buildBearerToken(options.NeoFSKey, table)
|
bearerTkn, err := buildBearerToken(options.NeoFSKey, oid, bearerRules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to build bearer token: %w", err)
|
return fmt.Errorf("failed to build bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
box.SetOwnerPublicKey(options.OwnerPrivateKey.PublicKey())
|
err = box.AddBearerToken(bearerTkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
|
||||||
err = box.AddBearerToken(tkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to add token to accessbox: %w", err)
|
return fmt.Errorf("failed to add bearer token to accessbox: %w", err)
|
||||||
|
}
|
||||||
|
a.log.Info("store bearer token into NeoFS",
|
||||||
|
zap.Stringer("owner_tkn", bearerTkn.Issuer()))
|
||||||
|
|
||||||
|
if options.SessionTkn {
|
||||||
|
sessionRules, err := buildContext(options.ContextRules)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to build context for session token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionTkn, err := buildSessionToken(options.NeoFSKey, oid, sessionRules)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create session token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = box.AddSessionToken(sessionTkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add session token to accessbox: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.log.Info("store bearer token into NeoFS",
|
if !options.SessionTkn && len(options.ContextRules) > 0 {
|
||||||
zap.Stringer("owner_tkn", tkn.Issuer()))
|
_, err := w.Write([]byte("Warning: rules for session token were set but --create-session flag wasn't, " +
|
||||||
|
"so session token was not created\n"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
address, err := tokens.
|
address, err := tokens.
|
||||||
New(a.pool, options.OwnerPrivateKey).
|
New(a.pool, options.OwnerPrivateKey).
|
||||||
Put(ctx, cid, tkn.Issuer(), &box, options.GatesPublicKeys...)
|
Put(ctx, cid, oid, &box, options.GatesPublicKeys...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to put bearer token: %w", err)
|
return fmt.Errorf("failed to put bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secret, err := BearerToAccessKey(tkn)
|
secret, err := BearerToAccessKey(bearerTkn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get bearer token secret key: %w", err)
|
return fmt.Errorf("failed to get bearer token secret key: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -267,13 +300,23 @@ func buildEACLTable(cid *cid.ID, eaclTable []byte) (*eacl.Table, error) {
|
||||||
return table, nil
|
return table, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table) (*token.BearerToken, error) {
|
func buildContext(rules []byte) (*session.ContainerContext, error) {
|
||||||
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
|
sessionCtx := session.NewContainerContext() // wildcard == true on by default
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
oid := owner.NewIDFromNeo3Wallet(wallet)
|
|
||||||
|
|
||||||
|
if len(rules) != 0 {
|
||||||
|
// cast ToV2 temporary, because there is no method for unmarshalling in ContainerContext in api-go
|
||||||
|
err := sessionCtx.ToV2().UnmarshalJSON(rules)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read rules for session token: %w", err)
|
||||||
|
}
|
||||||
|
return sessionCtx, nil
|
||||||
|
}
|
||||||
|
sessionCtx.ForPut()
|
||||||
|
sessionCtx.ApplyTo(nil)
|
||||||
|
return sessionCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildBearerToken(key *ecdsa.PrivateKey, oid *owner.ID, table *eacl.Table) (*token.BearerToken, error) {
|
||||||
bearerToken := token.NewBearerToken()
|
bearerToken := token.NewBearerToken()
|
||||||
bearerToken.SetEACLTable(table)
|
bearerToken.SetEACLTable(table)
|
||||||
bearerToken.SetOwner(oid)
|
bearerToken.SetOwner(oid)
|
||||||
|
@ -282,6 +325,19 @@ func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table) (*token.BearerTo
|
||||||
return bearerToken, bearerToken.SignToken(key)
|
return bearerToken, bearerToken.SignToken(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildSessionToken(key *ecdsa.PrivateKey, oid *owner.ID, ctx *session.ContainerContext) (*session.Token, error) {
|
||||||
|
tok := session.NewToken()
|
||||||
|
tok.SetContext(ctx)
|
||||||
|
uid, err := uuid.New().MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tok.SetID(uid)
|
||||||
|
tok.SetOwnerID(oid)
|
||||||
|
|
||||||
|
return tok, tok.Sign(key)
|
||||||
|
}
|
||||||
|
|
||||||
// BearerToAccessKey returns secret access key generated from given BearerToken.
|
// BearerToAccessKey returns secret access key generated from given BearerToken.
|
||||||
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
|
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
|
||||||
data, err := tkn.Marshal()
|
data, err := tkn.Marshal()
|
||||||
|
@ -292,3 +348,11 @@ func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
|
||||||
hash := sha256.Sum256(data)
|
hash := sha256.Sum256(data)
|
||||||
return hex.EncodeToString(hash[:]), nil
|
return hex.EncodeToString(hash[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ownerIDFromNeoFSKey(key *ecdsa.PublicKey) (*owner.ID, error) {
|
||||||
|
wallet, err := owner.NEO3WalletFromPublicKey(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return owner.NewIDFromNeo3Wallet(wallet), nil
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ var (
|
||||||
neoFSKeyPathFlag string
|
neoFSKeyPathFlag string
|
||||||
peerAddressFlag string
|
peerAddressFlag string
|
||||||
eaclRulesFlag string
|
eaclRulesFlag string
|
||||||
|
contextRulesFlag string
|
||||||
gatePrivateKeyFlag string
|
gatePrivateKeyFlag string
|
||||||
accessKeyIDFlag string
|
accessKeyIDFlag string
|
||||||
ownerPrivateKeyFlag string
|
ownerPrivateKeyFlag string
|
||||||
|
@ -46,6 +47,7 @@ var (
|
||||||
gatesKeysCountFlag int
|
gatesKeysCountFlag int
|
||||||
logEnabledFlag bool
|
logEnabledFlag bool
|
||||||
logDebugEnabledFlag bool
|
logDebugEnabledFlag bool
|
||||||
|
sessionTokenFlag bool
|
||||||
)
|
)
|
||||||
|
|
||||||
var zapConfig = zap.Config{
|
var zapConfig = zap.Config{
|
||||||
|
@ -198,11 +200,17 @@ func issueSecret() *cli.Command {
|
||||||
Destination: &peerAddressFlag,
|
Destination: &peerAddressFlag,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "rules",
|
Name: "bearer-rules",
|
||||||
Usage: "eacl rules as plain json string",
|
Usage: "rules for bearer token as plain json string",
|
||||||
Required: false,
|
Required: false,
|
||||||
Destination: &eaclRulesFlag,
|
Destination: &eaclRulesFlag,
|
||||||
},
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "session-rules",
|
||||||
|
Usage: "rules for session token as plain json string",
|
||||||
|
Required: false,
|
||||||
|
Destination: &contextRulesFlag,
|
||||||
|
},
|
||||||
&cli.StringSliceFlag{
|
&cli.StringSliceFlag{
|
||||||
Name: "gate-public-key",
|
Name: "gate-public-key",
|
||||||
Usage: "public x25519 key of a gate (use flags repeatedly for multiple gates)",
|
Usage: "public x25519 key of a gate (use flags repeatedly for multiple gates)",
|
||||||
|
@ -228,6 +236,13 @@ func issueSecret() *cli.Command {
|
||||||
Destination: &containerFriendlyName,
|
Destination: &containerFriendlyName,
|
||||||
Value: "auth-container",
|
Value: "auth-container",
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "create-session-token",
|
||||||
|
Usage: "create session token",
|
||||||
|
Required: false,
|
||||||
|
Destination: &sessionTokenFlag,
|
||||||
|
Value: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) error {
|
Action: func(c *cli.Context) error {
|
||||||
ctx, log := prepare()
|
ctx, log := prepare()
|
||||||
|
@ -275,6 +290,8 @@ func issueSecret() *cli.Command {
|
||||||
OwnerPrivateKey: owner.PrivateKey(),
|
OwnerPrivateKey: owner.PrivateKey(),
|
||||||
GatesPublicKeys: gatesPublicKeys,
|
GatesPublicKeys: gatesPublicKeys,
|
||||||
EACLRules: []byte(eaclRulesFlag),
|
EACLRules: []byte(eaclRulesFlag),
|
||||||
|
ContextRules: []byte(contextRulesFlag),
|
||||||
|
SessionTkn: sessionTokenFlag,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = agent.IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {
|
if err = agent.IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -342,10 +342,6 @@ github.com/nspcc-dev/neofs-crypto v0.3.0 h1:zlr3pgoxuzrmGCxc5W8dGVfA9Rro8diFvVnB
|
||||||
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw=
|
||||||
github.com/nspcc-dev/neofs-node v1.22.0 h1:TJ4d5zopItYYWMEajegVWBgAw8HjZFe12IkNm3Tt+rk=
|
github.com/nspcc-dev/neofs-node v1.22.0 h1:TJ4d5zopItYYWMEajegVWBgAw8HjZFe12IkNm3Tt+rk=
|
||||||
github.com/nspcc-dev/neofs-node v1.22.0/go.mod h1:ecpXrzIe1vcp5FBjPsIaHKVIVvxsv4GVBCw21WYcY3c=
|
github.com/nspcc-dev/neofs-node v1.22.0/go.mod h1:ecpXrzIe1vcp5FBjPsIaHKVIVvxsv4GVBCw21WYcY3c=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210604112451-f16d38c7b92a h1:bVvyR+Y+UmElTFKY0ifjtvWteYSm93jihKV1rh4wW5s=
|
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210604112451-f16d38c7b92a/go.mod h1:1djNrOkpTTbNUlJM/MvTmohJUaWKUMy9JHSCCA8rJEc=
|
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210609143631-0d3c078a0d9b h1:2alc6tGPHScEATOxlrYuHCTl+DbhVaqigT5Bo1QXY90=
|
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210609143631-0d3c078a0d9b/go.mod h1:1djNrOkpTTbNUlJM/MvTmohJUaWKUMy9JHSCCA8rJEc=
|
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210615074944-86a9aa92599b h1:l99sCKR/mt+iFb4p1836qtoXUQEGYlEzqWAeAliEaE8=
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210615074944-86a9aa92599b h1:l99sCKR/mt+iFb4p1836qtoXUQEGYlEzqWAeAliEaE8=
|
||||||
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210615074944-86a9aa92599b/go.mod h1:1djNrOkpTTbNUlJM/MvTmohJUaWKUMy9JHSCCA8rJEc=
|
github.com/nspcc-dev/neofs-sdk-go v0.0.0-20210615074944-86a9aa92599b/go.mod h1:1djNrOkpTTbNUlJM/MvTmohJUaWKUMy9JHSCCA8rJEc=
|
||||||
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
|
||||||
|
|
Loading…
Reference in a new issue