[#108] authmate: Add lifetime for tokens

Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
This commit is contained in:
Angira Kekteeva 2021-06-28 16:20:11 +03:00
parent ce65a47d1b
commit f97739898a
2 changed files with 63 additions and 15 deletions

View file

@ -53,6 +53,7 @@ type (
EACLRules []byte EACLRules []byte
ContextRules []byte ContextRules []byte
SessionTkn bool SessionTkn bool
Lifetime uint64
} }
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method. // ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
@ -62,6 +63,12 @@ type (
} }
) )
// lifetimeOptions holds NeoFS epochs, iat -- epoch, which a token was issued at, exp -- epoch, when the token expires.
type lifetimeOptions struct {
Iat uint64
Exp uint64
}
type ( type (
issuingResult struct { issuingResult struct {
AccessKeyID string `json:"access_key_id"` AccessKeyID string `json:"access_key_id"`
@ -104,20 +111,42 @@ func (a *Agent) checkContainer(ctx context.Context, cid *cid.ID, friendlyName st
return cid, nil return cid, nil
} }
func (a *Agent) getCurrentEpoch(ctx context.Context) (uint64, error) {
if conn, _, err := a.pool.Connection(); err != nil {
return 0, err
} else if networkInfo, err := conn.NetworkInfo(ctx); err != nil {
return 0, err
} else {
return networkInfo.CurrentEpoch(), nil
}
}
// IssueSecret creates an auth token, puts it in the NeoFS network and writes to io.Writer a new secret access key. // IssueSecret creates an auth token, puts it in the NeoFS network and writes to io.Writer a new secret access key.
func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecretOptions) error { func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecretOptions) error {
var ( var (
err error err error
cid *cid.ID cid *cid.ID
box *accessbox.AccessBox box *accessbox.AccessBox
lifetime lifetimeOptions
) )
lifetime.Iat, err = a.getCurrentEpoch(ctx)
if err != nil {
return err
}
if options.Lifetime >= math.MaxUint64-lifetime.Iat {
lifetime.Exp = math.MaxUint64
} else {
lifetime.Exp = lifetime.Iat + options.Lifetime
}
a.log.Info("check container", zap.Stringer("cid", options.ContainerID)) a.log.Info("check container", zap.Stringer("cid", options.ContainerID))
if cid, err = a.checkContainer(ctx, options.ContainerID, options.ContainerFriendlyName); err != nil { if cid, err = a.checkContainer(ctx, options.ContainerID, options.ContainerFriendlyName); err != nil {
return err return err
} }
gatesData, err := createTokens(options, cid) gatesData, err := createTokens(options, lifetime, cid)
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)
} }
@ -257,7 +286,7 @@ func buildContext(rules []byte) (*session.ContainerContext, error) {
return sessionCtx, nil return sessionCtx, nil
} }
func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, gateKey *keys.PublicKey) (*token.BearerToken, error) { func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, lifetime lifetimeOptions, gateKey *keys.PublicKey) (*token.BearerToken, error) {
oid, err := ownerIDFromNeoFSKey(gateKey) oid, err := ownerIDFromNeoFSKey(gateKey)
if err != nil { if err != nil {
return nil, err return nil, err
@ -266,15 +295,15 @@ func buildBearerToken(key *keys.PrivateKey, table *eacl.Table, gateKey *keys.Pub
bearerToken := token.NewBearerToken() bearerToken := token.NewBearerToken()
bearerToken.SetEACLTable(table) bearerToken.SetEACLTable(table)
bearerToken.SetOwner(oid) bearerToken.SetOwner(oid)
bearerToken.SetLifetime(math.MaxUint64, 0, 0) bearerToken.SetLifetime(lifetime.Exp, lifetime.Iat, lifetime.Iat)
return bearerToken, bearerToken.SignToken(&key.PrivateKey) return bearerToken, bearerToken.SignToken(&key.PrivateKey)
} }
func buildBearerTokens(key *keys.PrivateKey, table *eacl.Table, gatesKeys []*keys.PublicKey) ([]*token.BearerToken, error) { func buildBearerTokens(key *keys.PrivateKey, table *eacl.Table, lifetime lifetimeOptions, gatesKeys []*keys.PublicKey) ([]*token.BearerToken, error) {
bearerTokens := make([]*token.BearerToken, 0, len(gatesKeys)) bearerTokens := make([]*token.BearerToken, 0, len(gatesKeys))
for _, gateKey := range gatesKeys { for _, gateKey := range gatesKeys {
tkn, err := buildBearerToken(key, table, gateKey) tkn, err := buildBearerToken(key, table, lifetime, gateKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -283,7 +312,7 @@ func buildBearerTokens(key *keys.PrivateKey, table *eacl.Table, gatesKeys []*key
return bearerTokens, nil return bearerTokens, nil
} }
func buildSessionToken(key *keys.PrivateKey, oid *owner.ID, ctx *session.ContainerContext, gateKey *keys.PublicKey) (*session.Token, error) { func buildSessionToken(key *keys.PrivateKey, oid *owner.ID, lifetime lifetimeOptions, ctx *session.ContainerContext, gateKey *keys.PublicKey) (*session.Token, error) {
tok := session.NewToken() tok := session.NewToken()
tok.SetContext(ctx) tok.SetContext(ctx)
uid, err := uuid.New().MarshalBinary() uid, err := uuid.New().MarshalBinary()
@ -294,13 +323,17 @@ func buildSessionToken(key *keys.PrivateKey, oid *owner.ID, ctx *session.Contain
tok.SetOwnerID(oid) tok.SetOwnerID(oid)
tok.SetSessionKey(gateKey.Bytes()) tok.SetSessionKey(gateKey.Bytes())
tok.SetIat(lifetime.Iat)
tok.SetNbf(lifetime.Iat)
tok.SetExp(lifetime.Exp)
return tok, tok.Sign(&key.PrivateKey) return tok, tok.Sign(&key.PrivateKey)
} }
func buildSessionTokens(key *keys.PrivateKey, oid *owner.ID, ctx *session.ContainerContext, gatesKeys []*keys.PublicKey) ([]*session.Token, error) { func buildSessionTokens(key *keys.PrivateKey, oid *owner.ID, lifetime lifetimeOptions, ctx *session.ContainerContext, gatesKeys []*keys.PublicKey) ([]*session.Token, error) {
sessionTokens := make([]*session.Token, 0, len(gatesKeys)) sessionTokens := make([]*session.Token, 0, len(gatesKeys))
for _, gateKey := range gatesKeys { for _, gateKey := range gatesKeys {
tkn, err := buildSessionToken(key, oid, ctx, gateKey) tkn, err := buildSessionToken(key, oid, lifetime, ctx, gateKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -309,14 +342,14 @@ func buildSessionTokens(key *keys.PrivateKey, oid *owner.ID, ctx *session.Contai
return sessionTokens, nil return sessionTokens, nil
} }
func createTokens(options *IssueSecretOptions, cid *cid.ID) ([]*accessbox.GateData, error) { func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions, cid *cid.ID) ([]*accessbox.GateData, error) {
gates := make([]*accessbox.GateData, len(options.GatesPublicKeys)) gates := make([]*accessbox.GateData, len(options.GatesPublicKeys))
table, err := buildEACLTable(cid, options.EACLRules) table, err := buildEACLTable(cid, options.EACLRules)
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.NeoFSKey, table, options.GatesPublicKeys) bearerTokens, err := buildBearerTokens(options.NeoFSKey, 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)
} }
@ -334,7 +367,7 @@ func createTokens(options *IssueSecretOptions, cid *cid.ID) ([]*accessbox.GateDa
return nil, err return nil, err
} }
sessionTokens, err := buildSessionTokens(options.NeoFSKey, oid, sessionRules, options.GatesPublicKeys) sessionTokens, err := buildSessionTokens(options.NeoFSKey, oid, lifetime, sessionRules, options.GatesPublicKeys)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -25,6 +25,8 @@ import (
const ( const (
poolConnectTimeout = 5 * time.Second poolConnectTimeout = 5 * time.Second
poolRequestTimeout = 5 * time.Second poolRequestTimeout = 5 * time.Second
// a number of 15-second blocks in a month.
defaultLifetime = 172800
) )
var ( var (
@ -42,6 +44,7 @@ var (
logEnabledFlag bool logEnabledFlag bool
logDebugEnabledFlag bool logDebugEnabledFlag bool
sessionTokenFlag bool sessionTokenFlag bool
lifetimeFlag uint64
) )
const ( const (
@ -191,6 +194,13 @@ func issueSecret() *cli.Command {
Destination: &sessionTokenFlag, Destination: &sessionTokenFlag,
Value: false, Value: false,
}, },
&cli.Uint64Flag{
Name: "lifetime",
Usage: "Lifetime of tokens in NeoFS epoch (number of blocks in sidechain)",
Required: false,
Destination: &lifetimeFlag,
Value: defaultLifetime,
},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
ctx, log := prepare() ctx, log := prepare()
@ -222,11 +232,15 @@ func issueSecret() *cli.Command {
for _, key := range gatesPublicKeysFlag.Value() { for _, key := range gatesPublicKeysFlag.Value() {
gpk, err := keys.NewPublicKeyFromString(key) gpk, err := keys.NewPublicKeyFromString(key)
if err != nil { if err != nil {
return cli.Exit(fmt.Sprintf("failed to load gate's public key: %s", err), 5) return cli.Exit(fmt.Sprintf("failed to load gate's public key: %s", err), 4)
} }
gatesPublicKeys = append(gatesPublicKeys, gpk) gatesPublicKeys = append(gatesPublicKeys, gpk)
} }
if lifetimeFlag <= 0 {
return cli.Exit(fmt.Sprintf("lifetime must be at least 1, current value: %d", lifetimeFlag), 5)
}
issueSecretOptions := &authmate.IssueSecretOptions{ issueSecretOptions := &authmate.IssueSecretOptions{
ContainerID: containerID, ContainerID: containerID,
ContainerFriendlyName: containerFriendlyName, ContainerFriendlyName: containerFriendlyName,
@ -235,6 +249,7 @@ func issueSecret() *cli.Command {
EACLRules: []byte(eaclRulesFlag), EACLRules: []byte(eaclRulesFlag),
ContextRules: []byte(contextRulesFlag), ContextRules: []byte(contextRulesFlag),
SessionTkn: sessionTokenFlag, SessionTkn: sessionTokenFlag,
Lifetime: lifetimeFlag,
} }
if err = agent.IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil { if err = agent.IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {