[#260] authmate: Support key registration in frostfsid contract

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
Denis Kirillov 2023-11-16 14:39:58 +03:00
parent 6304d7bfda
commit a61ff3b8cb
8 changed files with 101 additions and 1 deletions

View file

@ -81,6 +81,11 @@ type FrostFS interface {
TimeToEpoch(context.Context, time.Time) (uint64, uint64, error) TimeToEpoch(context.Context, time.Time) (uint64, uint64, error)
} }
// FrostFSID represents interface to interact with frostfsid contract.
type FrostFSID interface {
RegisterPublicKey(*keys.PublicKey) error
}
// Agent contains client communicating with FrostFS and logger. // Agent contains client communicating with FrostFS and logger.
type Agent struct { type Agent struct {
frostFS FrostFS frostFS FrostFS

View file

@ -12,6 +12,10 @@ type (
businessLogicError struct { businessLogicError struct {
err error err error
} }
frostFSIDInitError struct {
err error
}
) )
func wrapPreparationError(e error) error { func wrapPreparationError(e error) error {
@ -38,6 +42,14 @@ func (e businessLogicError) Error() string {
return e.err.Error() return e.err.Error()
} }
func wrapFrostFSIDInitError(e error) error {
return frostFSIDInitError{e}
}
func (e frostFSIDInitError) Error() string {
return e.err.Error()
}
// ExitCode picks corresponding error code depending on the type of error provided. // ExitCode picks corresponding error code depending on the type of error provided.
// Returns 1 if error type is unknown. // Returns 1 if error type is unknown.
func ExitCode(e error) int { func ExitCode(e error) int {
@ -48,6 +60,8 @@ func ExitCode(e error) int {
return 3 return 3
case businessLogicError: case businessLogicError:
return 4 return 4
case frostFSIDInitError:
return 4
} }
return 1 return 1
} }

View file

@ -8,6 +8,7 @@ import (
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -37,6 +38,8 @@ const (
lifetimeFlag = "lifetime" lifetimeFlag = "lifetime"
containerPolicyFlag = "container-policy" containerPolicyFlag = "container-policy"
awsCLICredentialFlag = "aws-cli-credentials" awsCLICredentialFlag = "aws-cli-credentials"
frostfsIDFlag = "frostfsid"
rpcEndpointFlag = "rpc-endpoint"
) )
const ( const (
@ -77,6 +80,8 @@ func initIssueSecretCmd() {
issueSecretCmd.Flags().Duration(poolHealthcheckTimeoutFlag, defaultPoolHealthcheckTimeout, "Timeout for request to node to decide if it is alive") issueSecretCmd.Flags().Duration(poolHealthcheckTimeoutFlag, defaultPoolHealthcheckTimeout, "Timeout for request to node to decide if it is alive")
issueSecretCmd.Flags().Duration(poolRebalanceIntervalFlag, defaultPoolRebalanceInterval, "Interval for updating nodes health status") issueSecretCmd.Flags().Duration(poolRebalanceIntervalFlag, defaultPoolRebalanceInterval, "Interval for updating nodes health status")
issueSecretCmd.Flags().Duration(poolStreamTimeoutFlag, defaultPoolStreamTimeout, "Timeout for individual operation in streaming RPC") issueSecretCmd.Flags().Duration(poolStreamTimeoutFlag, defaultPoolStreamTimeout, "Timeout for individual operation in streaming RPC")
issueSecretCmd.Flags().String(frostfsIDFlag, "", "FrostfsID contract hash (LE) or name in NNS to register public key in contract (rpc-endpoint flag also must be provided)")
issueSecretCmd.Flags().String(rpcEndpointFlag, "", "NEO node RPC address")
_ = issueSecretCmd.MarkFlagRequired(walletFlag) _ = issueSecretCmd.MarkFlagRequired(walletFlag)
_ = issueSecretCmd.MarkFlagRequired(peerFlag) _ = issueSecretCmd.MarkFlagRequired(peerFlag)
@ -152,6 +157,28 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
return wrapFrostFSInitError(fmt.Errorf("failed to create FrostFS component: %s", err)) return wrapFrostFSInitError(fmt.Errorf("failed to create FrostFS component: %s", err))
} }
frostFSID := viper.GetString(frostfsIDFlag)
if frostFSID != "" {
rpcAddress := viper.GetString(rpcEndpointFlag)
if rpcAddress == "" {
return wrapPreparationError(fmt.Errorf("you can use '%s' flag only along with '%s'", frostfsIDFlag, rpcEndpointFlag))
}
cfg := frostfsid.Config{
RPCAddress: rpcAddress,
Contract: frostFSID,
Key: key,
}
frostfsIDClient, err := createFrostFSID(ctx, log, cfg)
if err != nil {
return wrapFrostFSIDInitError(err)
}
if err = frostfsIDClient.RegisterPublicKey(key.PublicKey()); err != nil {
return wrapBusinessLogicError(fmt.Errorf("failed to register key in frostfsid: %w", err))
}
}
issueSecretOptions := &authmate.IssueSecretOptions{ issueSecretOptions := &authmate.IssueSecretOptions{
Container: authmate.ContainerOptions{ Container: authmate.ContainerOptions{
ID: cnrID, ID: cnrID,

View file

@ -7,6 +7,7 @@ import (
"strings" "strings"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -39,6 +40,8 @@ func initUpdateSecretCmd() {
updateSecretCmd.Flags().Duration(poolHealthcheckTimeoutFlag, defaultPoolHealthcheckTimeout, "Timeout for request to node to decide if it is alive") updateSecretCmd.Flags().Duration(poolHealthcheckTimeoutFlag, defaultPoolHealthcheckTimeout, "Timeout for request to node to decide if it is alive")
updateSecretCmd.Flags().Duration(poolRebalanceIntervalFlag, defaultPoolRebalanceInterval, "Interval for updating nodes health status") updateSecretCmd.Flags().Duration(poolRebalanceIntervalFlag, defaultPoolRebalanceInterval, "Interval for updating nodes health status")
updateSecretCmd.Flags().Duration(poolStreamTimeoutFlag, defaultPoolStreamTimeout, "Timeout for individual operation in streaming RPC") updateSecretCmd.Flags().Duration(poolStreamTimeoutFlag, defaultPoolStreamTimeout, "Timeout for individual operation in streaming RPC")
updateSecretCmd.Flags().String(frostfsIDFlag, "", "FrostfsID contract hash (LE) or name in NNS to register public key in contract (rpc-endpoint flag also must be provided)")
updateSecretCmd.Flags().String(rpcEndpointFlag, "", "NEO node RPC address")
_ = updateSecretCmd.MarkFlagRequired(walletFlag) _ = updateSecretCmd.MarkFlagRequired(walletFlag)
_ = updateSecretCmd.MarkFlagRequired(peerFlag) _ = updateSecretCmd.MarkFlagRequired(peerFlag)
@ -94,6 +97,28 @@ func runUpdateSecretCmd(cmd *cobra.Command, _ []string) error {
return wrapFrostFSInitError(fmt.Errorf("failed to create FrostFS component: %s", err)) return wrapFrostFSInitError(fmt.Errorf("failed to create FrostFS component: %s", err))
} }
frostFSID := viper.GetString(frostfsIDFlag)
if frostFSID != "" {
rpcAddress := viper.GetString(rpcEndpointFlag)
if rpcAddress == "" {
return wrapPreparationError(fmt.Errorf("you can use '%s' flag only along with '%s'", frostfsIDFlag, rpcEndpointFlag))
}
cfg := frostfsid.Config{
RPCAddress: rpcAddress,
Contract: frostFSID,
Key: key,
}
frostfsIDClient, err := createFrostFSID(ctx, log, cfg)
if err != nil {
return wrapFrostFSIDInitError(err)
}
if err = frostfsIDClient.RegisterPublicKey(key.PublicKey()); err != nil {
return wrapBusinessLogicError(fmt.Errorf("failed to register key in frostfsid: %w", err))
}
}
updateSecretOptions := &authmate.UpdateSecretOptions{ updateSecretOptions := &authmate.UpdateSecretOptions{
Address: accessBoxAddress, Address: accessBoxAddress,
FrostFSKey: key, FrostFSKey: key,

View file

@ -10,6 +10,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -141,3 +142,14 @@ func getLogger() *zap.Logger {
return log return log
} }
func createFrostFSID(ctx context.Context, log *zap.Logger, cfg frostfsid.Config) (authmate.FrostFSID, error) {
log.Debug(logs.PrepareFrostfsIDClient)
cli, err := frostfsid.New(ctx, cfg)
if err != nil {
return nil, fmt.Errorf("create frostfsid client: %w", err)
}
return cli, nil
}

View file

@ -143,6 +143,9 @@ the secret. Format of `access_key_id`: `%cid0%oid`, where 0(zero) is a delimiter
* `--access-key-id` -- credentials that you want to update (e.g. to add more gates that can use your creds) * `--access-key-id` -- credentials that you want to update (e.g. to add more gates that can use your creds)
without changing values of `aws_access_key_id` and `aws_secret_access_key`. If you want to update credential you MUST without changing values of `aws_access_key_id` and `aws_secret_access_key`. If you want to update credential you MUST
provide also secret key using `AUTHMATE_SECRET_ACCESS_KEY` env variable. provide also secret key using `AUTHMATE_SECRET_ACCESS_KEY` env variable.
* `--frostfsid` -- FrostfsID contract hash (LE) or name in NNS to register public key in contract
(`--rpc-endpoint` flag also must be provided).
* `--rpc-endpoint` -- NEO node RPC address.
### Bearer tokens ### Bearer tokens

View file

@ -7,6 +7,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client" "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware" "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/middleware"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/ns"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -31,7 +32,10 @@ type Config struct {
Key *keys.PrivateKey Key *keys.PrivateKey
} }
var _ middleware.FrostFSID = (*FrostFSID)(nil) var (
_ middleware.FrostFSID = (*FrostFSID)(nil)
_ authmate.FrostFSID = (*FrostFSID)(nil)
)
// New creates new FrostfsID contract wrapper that implements auth.FrostFSID interface. // New creates new FrostfsID contract wrapper that implements auth.FrostFSID interface.
func New(ctx context.Context, cfg Config) (*FrostFSID, error) { func New(ctx context.Context, cfg Config) (*FrostFSID, error) {
@ -67,6 +71,15 @@ func (f *FrostFSID) ValidatePublicKey(key *keys.PublicKey) error {
return err return err
} }
func (f *FrostFSID) RegisterPublicKey(key *keys.PublicKey) error {
_, err := f.cli.Wait(f.cli.CreateSubject(key))
if err != nil && !strings.Contains(err.Error(), "subject already exists") {
return err
}
return nil
}
func fetchContractHash(cfg Config) (util.Uint160, error) { func fetchContractHash(cfg Config) (util.Uint160, error) {
if hash, err := util.Uint160DecodeStringLE(cfg.Contract); err == nil { if hash, err := util.Uint160DecodeStringLE(cfg.Contract); err == nil {
return hash, nil return hash, nil

View file

@ -45,6 +45,7 @@ const (
SkipEmptyAddress = "skip, empty address" // Warn in ../../cmd/s3-gw/app_settings.go SkipEmptyAddress = "skip, empty address" // Warn in ../../cmd/s3-gw/app_settings.go
AddedStoragePeer = "added storage peer" // Info in ../../cmd/s3-gw/app_settings.go AddedStoragePeer = "added storage peer" // Info in ../../cmd/s3-gw/app_settings.go
PrepareConnectionPool = "prepare connection pool" // Debug in ../../cmd/s3-authmate/modules/utils.go PrepareConnectionPool = "prepare connection pool" // Debug in ../../cmd/s3-authmate/modules/utils.go
PrepareFrostfsIDClient = "prepare frostfsid client" // Debug in ../../cmd/s3-authmate/modules/utils.go
InvalidCacheEntryType = "invalid cache entry type" // Warn in ../../api/cache/* InvalidCacheEntryType = "invalid cache entry type" // Warn in ../../api/cache/*
InvalidCacheKeyType = "invalid cache key type" // Warn in ../../api/cache/objectslist.go InvalidCacheKeyType = "invalid cache key type" // Warn in ../../api/cache/objectslist.go
ObjectIsCopied = "object is copied" // Info in ../../api/handler/copy.go ObjectIsCopied = "object is copied" // Info in ../../api/handler/copy.go