forked from TrueCloudLab/frostfs-s3-gw
[#131] authmate: Add agent.UpdateSecret
Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
dea7b39805
commit
f74ab12f91
1 changed files with 141 additions and 19 deletions
|
@ -11,6 +11,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
||||||
|
sessionv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/cache"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/accessbox"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/creds/tokens"
|
||||||
|
@ -108,6 +110,21 @@ type (
|
||||||
UpdateCreds *UpdateOptions
|
UpdateCreds *UpdateOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateSecretOptions contains options for passing to Agent.UpdateSecret method.
|
||||||
|
UpdateSecretOptions struct {
|
||||||
|
FrostFSKey *keys.PrivateKey
|
||||||
|
GatesPublicKeys []*keys.PublicKey
|
||||||
|
Address oid.Address
|
||||||
|
GatePrivateKey *keys.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenUpdateOptions struct {
|
||||||
|
frostFSKey *keys.PrivateKey
|
||||||
|
gatesPublicKeys []*keys.PublicKey
|
||||||
|
lifetime lifetimeOptions
|
||||||
|
box *accessbox.Box
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerOptions groups parameters of auth container to put the secret into.
|
// ContainerOptions groups parameters of auth container to put the secret into.
|
||||||
ContainerOptions struct {
|
ContainerOptions struct {
|
||||||
ID cid.ID
|
ID cid.ID
|
||||||
|
@ -136,8 +153,8 @@ type lifetimeOptions struct {
|
||||||
|
|
||||||
type (
|
type (
|
||||||
issuingResult struct {
|
issuingResult struct {
|
||||||
AccessKeyID string `json:"access_key_id"`
|
|
||||||
InitialAccessKeyID string `json:"initial_access_key_id"`
|
InitialAccessKeyID string `json:"initial_access_key_id"`
|
||||||
|
AccessKeyID string `json:"access_key_id"`
|
||||||
SecretAccessKey string `json:"secret_access_key"`
|
SecretAccessKey string `json:"secret_access_key"`
|
||||||
OwnerPrivateKey string `json:"owner_private_key"`
|
OwnerPrivateKey string `json:"owner_private_key"`
|
||||||
WalletPublicKey string `json:"wallet_public_key"`
|
WalletPublicKey string `json:"wallet_public_key"`
|
||||||
|
@ -237,12 +254,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return fmt.Errorf("create tokens: %w", err)
|
return fmt.Errorf("create tokens: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var secret []byte
|
box, secrets, err := accessbox.PackTokens(gatesData, nil)
|
||||||
if options.UpdateCreds != nil {
|
|
||||||
secret = options.UpdateCreds.SecretAccessKey
|
|
||||||
}
|
|
||||||
|
|
||||||
box, secrets, err := accessbox.PackTokens(gatesData, secret)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("pack tokens: %w", err)
|
return fmt.Errorf("pack tokens: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -261,24 +273,15 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
|
|
||||||
creds := tokens.New(a.frostFS, secrets.EphemeralKey, cache.DefaultAccessBoxConfig(a.log))
|
creds := tokens.New(a.frostFS, secrets.EphemeralKey, cache.DefaultAccessBoxConfig(a.log))
|
||||||
|
|
||||||
var addr oid.Address
|
addr, err := creds.Put(ctx, id, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
||||||
var oldAddr oid.Address
|
|
||||||
if options.UpdateCreds != nil {
|
|
||||||
oldAddr = options.UpdateCreds.Address
|
|
||||||
addr, err = creds.Update(ctx, oldAddr, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
|
||||||
} else {
|
|
||||||
addr, err = creds.Put(ctx, id, idOwner, box, lifetime.Exp, options.GatesPublicKeys...)
|
|
||||||
oldAddr = addr
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to put creds: %w", err)
|
return fmt.Errorf("failed to put creds: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
accessKeyID := addr.Container().EncodeToString() + "0" + addr.Object().EncodeToString()
|
accessKeyID := accessKeyIDFromAddr(addr)
|
||||||
|
|
||||||
ir := &issuingResult{
|
ir := &issuingResult{
|
||||||
|
InitialAccessKeyID: accessKeyID,
|
||||||
AccessKeyID: accessKeyID,
|
AccessKeyID: accessKeyID,
|
||||||
InitialAccessKeyID: oldAddr.Container().EncodeToString() + "0" + oldAddr.Object().EncodeToString(),
|
|
||||||
SecretAccessKey: secrets.AccessKey,
|
SecretAccessKey: secrets.AccessKey,
|
||||||
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
||||||
WalletPublicKey: hex.EncodeToString(options.FrostFSKey.PublicKey().Bytes()),
|
WalletPublicKey: hex.EncodeToString(options.FrostFSKey.PublicKey().Bytes()),
|
||||||
|
@ -309,6 +312,73 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateSecret updates an auth token (change list of gates that can use credential), puts new cred version to the FrostFS network and writes to io.Writer a result.
|
||||||
|
func (a *Agent) UpdateSecret(ctx context.Context, w io.Writer, options *UpdateSecretOptions) error {
|
||||||
|
creds := tokens.New(a.frostFS, options.GatePrivateKey, cache.DefaultAccessBoxConfig(a.log))
|
||||||
|
|
||||||
|
box, err := creds.GetBox(ctx, options.Address)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get accessbox: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
secret, err := hex.DecodeString(box.Gate.AccessKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to decode secret key access box: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lifetime := getLifetimeFromGateData(box.Gate)
|
||||||
|
tokenOptions := tokenUpdateOptions{
|
||||||
|
frostFSKey: options.FrostFSKey,
|
||||||
|
gatesPublicKeys: options.GatesPublicKeys,
|
||||||
|
lifetime: lifetime,
|
||||||
|
box: box,
|
||||||
|
}
|
||||||
|
|
||||||
|
gatesData, err := formTokensToUpdate(tokenOptions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create tokens: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedBox, secrets, err := accessbox.PackTokens(gatesData, secret)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("pack tokens: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var idOwner user.ID
|
||||||
|
user.IDFromKey(&idOwner, options.FrostFSKey.PrivateKey.PublicKey)
|
||||||
|
a.log.Info("update access cred object into FrostFS",
|
||||||
|
zap.Stringer("owner_tkn", idOwner))
|
||||||
|
|
||||||
|
oldAddr := options.Address
|
||||||
|
addr, err := creds.Update(ctx, oldAddr, idOwner, updatedBox, lifetime.Exp, options.GatesPublicKeys...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update creds: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ir := &issuingResult{
|
||||||
|
AccessKeyID: accessKeyIDFromAddr(addr),
|
||||||
|
InitialAccessKeyID: accessKeyIDFromAddr(oldAddr),
|
||||||
|
SecretAccessKey: secrets.AccessKey,
|
||||||
|
OwnerPrivateKey: hex.EncodeToString(secrets.EphemeralKey.Bytes()),
|
||||||
|
WalletPublicKey: hex.EncodeToString(options.FrostFSKey.PublicKey().Bytes()),
|
||||||
|
ContainerID: addr.Container().EncodeToString(),
|
||||||
|
}
|
||||||
|
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetIndent("", " ")
|
||||||
|
return enc.Encode(ir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLifetimeFromGateData(gateData *accessbox.GateData) lifetimeOptions {
|
||||||
|
var btokenv2 acl.BearerToken
|
||||||
|
gateData.BearerToken.WriteToV2(&btokenv2)
|
||||||
|
|
||||||
|
return lifetimeOptions{
|
||||||
|
Iat: btokenv2.GetBody().GetLifetime().GetIat(),
|
||||||
|
Exp: btokenv2.GetBody().GetLifetime().GetExp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ObtainSecret receives an existing secret access key from FrostFS and
|
// ObtainSecret receives an existing secret access key from FrostFS and
|
||||||
// writes to io.Writer the secret access key.
|
// writes to io.Writer the secret access key.
|
||||||
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
|
||||||
|
@ -467,3 +537,55 @@ func createTokens(options *IssueSecretOptions, lifetime lifetimeOptions) ([]*acc
|
||||||
|
|
||||||
return gates, nil
|
return gates, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formTokensToUpdate(options tokenUpdateOptions) ([]*accessbox.GateData, error) {
|
||||||
|
btoken := options.box.Gate.BearerToken
|
||||||
|
table := btoken.EACLTable()
|
||||||
|
|
||||||
|
bearerTokens, err := buildBearerTokens(options.frostFSKey, btoken.Impersonate(), &table, options.lifetime, options.gatesPublicKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to build bearer tokens: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
gates := make([]*accessbox.GateData, len(options.gatesPublicKeys))
|
||||||
|
for i, gateKey := range options.gatesPublicKeys {
|
||||||
|
gates[i] = accessbox.NewGateData(gateKey, bearerTokens[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionRules := make([]sessionTokenContext, len(options.box.Gate.SessionTokens))
|
||||||
|
for i, token := range options.box.Gate.SessionTokens {
|
||||||
|
var stoken sessionv2.Token
|
||||||
|
token.WriteToV2(&stoken)
|
||||||
|
|
||||||
|
sessionCtx, ok := stoken.GetBody().GetContext().(*sessionv2.ContainerSessionContext)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("get context from session token: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cnrID cid.ID
|
||||||
|
if cnrIDv2 := sessionCtx.ContainerID(); cnrIDv2 != nil {
|
||||||
|
if err = cnrID.ReadFromV2(*cnrIDv2); err != nil {
|
||||||
|
return nil, fmt.Errorf("read from v2 container id: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionRules[i] = sessionTokenContext{
|
||||||
|
verb: session.ContainerVerb(sessionCtx.Verb()),
|
||||||
|
containerID: cnrID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionTokens, err := buildSessionTokens(options.frostFSKey, options.lifetime, sessionRules, options.gatesPublicKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to biuild session token: %w", err)
|
||||||
|
}
|
||||||
|
for i, sessionTkns := range sessionTokens {
|
||||||
|
gates[i].SessionTokens = sessionTkns
|
||||||
|
}
|
||||||
|
|
||||||
|
return gates, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func accessKeyIDFromAddr(addr oid.Address) string {
|
||||||
|
return addr.Container().EncodeToString() + "0" + addr.Object().EncodeToString()
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue