forked from TrueCloudLab/frostfs-s3-gw
[#108] authmate: Add lifetime for tokens
Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
This commit is contained in:
parent
ce65a47d1b
commit
f97739898a
2 changed files with 63 additions and 15 deletions
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue