diff --git a/README.md b/README.md index 3321425..7d2bfc8 100644 --- a/README.md +++ b/README.md @@ -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: ``` { @@ -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", diff --git a/authmate/authmate.go b/authmate/authmate.go index dd90c31..d7c65bf 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -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" @@ -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. @@ -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", 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", - zap.Stringer("owner_tkn", tkn.Issuer())) + 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) } @@ -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 - } - oid := owner.NewIDFromNeo3Wallet(wallet) +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 + } + 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) @@ -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() @@ -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 +} diff --git a/cmd/authmate/main.go b/cmd/authmate/main.go index 37318df..28b0d89 100644 --- a/cmd/authmate/main.go +++ b/cmd/authmate/main.go @@ -37,6 +37,7 @@ var ( neoFSKeyPathFlag string peerAddressFlag string eaclRulesFlag string + contextRulesFlag string gatePrivateKeyFlag string accessKeyIDFlag string ownerPrivateKeyFlag string @@ -46,6 +47,7 @@ var ( gatesKeysCountFlag int logEnabledFlag bool logDebugEnabledFlag bool + sessionTokenFlag bool ) var zapConfig = zap.Config{ @@ -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)", @@ -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() @@ -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 { diff --git a/go.sum b/go.sum index 62e15dd..3228e9c 100644 --- a/go.sum +++ b/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-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=