Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#72] Session token in authmate #86

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,18 @@ used to create new AWS credentials.

#### Issuance of a secret

To issue a secret means to create a Bearer token and put it as an object into
container on the NeoFS network. The token is encrypted by a set of gateway
keys, so you need to pass them as well.
To issue a secret means to create a Bearer and (optionally) Session tokens and
put them as an object into container on the NeoFS network. The tokens are
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 `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:

```
{
Expand All @@ -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:
```
$ ./neofs-authmate issue-secret --neofs-key user.key \
--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 20453af9d7f245ff6fdfb1260eaa411ae3be9c519a2a9bf1c98233522cbd0156
--gate-public-key 20453af9d7f245ff6fdfb1260eaa411ae3be9c519a2a9bf1c98233522cbd0156 \
--create-session-token \
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'

{
"access_key_id": "5g933dyLEkXbbAspouhPPTiyLZRg4axBW1axSPD87eVT_AiXsH4AjYy1iTJ4C1WExzjBrSobJsQFWEyKLREe5sQYM",
Expand Down
94 changes: 79 additions & 15 deletions authmate/authmate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import (
"strconv"
"time"

"github.com/google/uuid"
"github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl"
"github.com/nspcc-dev/neofs-api-go/pkg/container"
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/object"
"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-node/pkg/policy"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
Expand Down Expand Up @@ -53,6 +55,8 @@ type (
OwnerPrivateKey hcs.PrivateKey
GatesPublicKeys []hcs.PublicKey
EACLRules []byte
ContextRules []byte
SessionTkn bool
}

// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
Expand Down Expand Up @@ -138,35 +142,64 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
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 {
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 {
return fmt.Errorf("failed to build bearer token: %w", err)
}

box.SetOwnerPublicKey(options.OwnerPrivateKey.PublicKey())
err = box.AddBearerToken(tkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
err = box.AddBearerToken(bearerTkn, options.OwnerPrivateKey, options.GatesPublicKeys...)
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", tkn.Issuer()))
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)
}
}

if !options.SessionTkn && len(options.ContextRules) > 0 {
_, 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.
New(a.pool, options.OwnerPrivateKey).
Put(ctx, cid, tkn.Issuer(), &box, options.GatesPublicKeys...)
Put(ctx, cid, oid, &box, options.GatesPublicKeys...)
if err != nil {
return fmt.Errorf("failed to put bearer token: %w", err)
}

secret, err := BearerToAccessKey(tkn)
secret, err := BearerToAccessKey(bearerTkn)
if err != nil {
return fmt.Errorf("failed to get bearer token secret key: %w", err)
}
Expand Down Expand Up @@ -267,13 +300,23 @@ func buildEACLTable(cid *cid.ID, eaclTable []byte) (*eacl.Table, error) {
return table, nil
}

func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table) (*token.BearerToken, error) {
wallet, err := owner.NEO3WalletFromPublicKey(&key.PublicKey)
if err != nil {
return nil, err
func buildContext(rules []byte) (*session.ContainerContext, error) {
sessionCtx := session.NewContainerContext() // wildcard == true on by default

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
}
oid := owner.NewIDFromNeo3Wallet(wallet)
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.SetEACLTable(table)
bearerToken.SetOwner(oid)
Expand All @@ -282,6 +325,19 @@ func buildBearerToken(key *ecdsa.PrivateKey, table *eacl.Table) (*token.BearerTo
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.
func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
data, err := tkn.Marshal()
Expand All @@ -292,3 +348,11 @@ func BearerToAccessKey(tkn *token.BearerToken) (string, error) {
hash := sha256.Sum256(data)
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
}
21 changes: 19 additions & 2 deletions cmd/authmate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
neoFSKeyPathFlag string
peerAddressFlag string
eaclRulesFlag string
contextRulesFlag string
gatePrivateKeyFlag string
accessKeyIDFlag string
ownerPrivateKeyFlag string
Expand All @@ -46,6 +47,7 @@ var (
gatesKeysCountFlag int
logEnabledFlag bool
logDebugEnabledFlag bool
sessionTokenFlag bool
)

var zapConfig = zap.Config{
Expand Down Expand Up @@ -198,11 +200,17 @@ func issueSecret() *cli.Command {
Destination: &peerAddressFlag,
},
&cli.StringFlag{
Name: "rules",
Usage: "eacl rules as plain json string",
Name: "bearer-rules",
Usage: "rules for bearer token as plain json string",
Required: false,
Destination: &eaclRulesFlag,
},
&cli.StringFlag{
Name: "session-rules",
Usage: "rules for session token as plain json string",
Required: false,
Destination: &contextRulesFlag,
},
&cli.StringSliceFlag{
Name: "gate-public-key",
Usage: "public x25519 key of a gate (use flags repeatedly for multiple gates)",
Expand All @@ -228,6 +236,13 @@ func issueSecret() *cli.Command {
Destination: &containerFriendlyName,
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 {
ctx, log := prepare()
Expand Down Expand Up @@ -275,6 +290,8 @@ func issueSecret() *cli.Command {
OwnerPrivateKey: owner.PrivateKey(),
GatesPublicKeys: gatesPublicKeys,
EACLRules: []byte(eaclRulesFlag),
ContextRules: []byte(contextRulesFlag),
SessionTkn: sessionTokenFlag,
}

if err = agent.IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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-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-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/go.mod h1:1djNrOkpTTbNUlJM/MvTmohJUaWKUMy9JHSCCA8rJEc=
github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso=
Expand Down