diff --git a/cmd/s3-authmate/modules/root.go b/cmd/s3-authmate/modules/root.go index fa34ade..5d29fed 100644 --- a/cmd/s3-authmate/modules/root.go +++ b/cmd/s3-authmate/modules/root.go @@ -62,4 +62,7 @@ GoVersion: {{ runtimeVersion }} rootCmd.AddCommand(generatePresignedURLCmd) initGeneratePresignedURLCmd() + + rootCmd.AddCommand(updateSecretCmd) + initUpdateSecretCmd() } diff --git a/cmd/s3-authmate/modules/update-secret.go b/cmd/s3-authmate/modules/update-secret.go new file mode 100644 index 0000000..a978cd9 --- /dev/null +++ b/cmd/s3-authmate/modules/update-secret.go @@ -0,0 +1,108 @@ +package modules + +import ( + "context" + "fmt" + "os" + "strings" + + "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate" + "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet" + oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +var updateSecretCmd = &cobra.Command{ + Use: "update-secret", + Short: "Update a secret in FrostFS network", + Long: `Creates new access box that will be available for extend list of s3 gates, preserve all timeout from initial credentials. +After using this command you can use initial access-key-id to interact with newly added gates`, + Example: `To extend list of s3 gates that can use existing credentials run: +frostfs-s3-authmate update-secret --wallet wallet.json --peer s01.neofs.devenv:8080 --gate-wallet s3-wallet.json \ + --gate-public-key 031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a \ + --gate-public-key 021dc56fc6d81d581ae7605a8e00e0e0bab6cbad566a924a527339475a97a8e38e \ + --acces-key-id EC3tyWpTEKfGNS888PFBpwQzZTrnwDXReGjgAxa8Em1h037VoWktUZCAk1LVA5SvVbVd2NHHb2NQm9jhcd5WFU5VD`, + RunE: runUpdateSecretCmd, +} + +func initUpdateSecretCmd() { + updateSecretCmd.Flags().String(walletFlag, "", "Path to the wallet that will be owner of the credentials") + updateSecretCmd.Flags().String(addressFlag, "", "Address of the wallet account") + updateSecretCmd.Flags().String(peerFlag, "", "Address of a frostfs peer to connect to") + updateSecretCmd.Flags().String(gateWalletFlag, "", "Path to the s3 gateway wallet to decrypt accessbox") + updateSecretCmd.Flags().String(gateAddressFlag, "", "Address of the s3 gateway wallet account") + updateSecretCmd.Flags().String(accessKeyIDFlag, "", "Access key id of s3 credential for which secret must be obtained") + updateSecretCmd.Flags().StringSlice(gatePublicKeyFlag, nil, "Public 256r1 key of a gate (use flags repeatedly for multiple gates or separate them by comma)") + updateSecretCmd.Flags().Duration(poolDialTimeoutFlag, defaultPoolDialTimeout, "Timeout for connection to the node in pool to be established") + 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(poolStreamTimeoutFlag, defaultPoolStreamTimeout, "Timeout for individual operation in streaming RPC") + + _ = updateSecretCmd.MarkFlagRequired(walletFlag) + _ = updateSecretCmd.MarkFlagRequired(peerFlag) + _ = updateSecretCmd.MarkFlagRequired(gateWalletFlag) + _ = updateSecretCmd.MarkFlagRequired(accessKeyIDFlag) + _ = updateSecretCmd.MarkFlagRequired(gatePublicKeyFlag) +} + +func runUpdateSecretCmd(cmd *cobra.Command, _ []string) error { + ctx, cancel := context.WithTimeout(cmd.Context(), viper.GetDuration(timeoutFlag)) + defer cancel() + + log := getLogger() + + password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg) + key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password) + if err != nil { + return fmt.Errorf("failed to load frostfs private key: %s", err) + } + + gatePassword := wallet.GetPassword(viper.GetViper(), walletGatePassphraseCfg) + gateKey, err := wallet.GetKeyFromPath(viper.GetString(gateWalletFlag), viper.GetString(gateAddressFlag), gatePassword) + if err != nil { + return fmt.Errorf("failed to load s3 gate private key: %s", err) + } + + var accessBoxAddress oid.Address + credAddr := strings.Replace(viper.GetString(accessKeyIDFlag), "0", "/", 1) + if err = accessBoxAddress.DecodeString(credAddr); err != nil { + return fmt.Errorf("failed to parse creds address: %w", err) + } + + var gatesPublicKeys []*keys.PublicKey + for _, keyStr := range viper.GetStringSlice(gatePublicKeyFlag) { + gpk, err := keys.NewPublicKeyFromString(keyStr) + if err != nil { + return fmt.Errorf("failed to load gate's public key: %s", err) + } + gatesPublicKeys = append(gatesPublicKeys, gpk) + } + + poolCfg := PoolConfig{ + Key: &key.PrivateKey, + Address: viper.GetString(peerFlag), + DialTimeout: viper.GetDuration(poolDialTimeoutFlag), + HealthcheckTimeout: viper.GetDuration(poolHealthcheckTimeoutFlag), + StreamTimeout: viper.GetDuration(poolStreamTimeoutFlag), + RebalanceInterval: viper.GetDuration(poolRebalanceIntervalFlag), + } + + frostFS, err := createFrostFS(ctx, log, poolCfg) + if err != nil { + return fmt.Errorf("failed to create FrostFS component: %s", err) + } + + updateSecretOptions := &authmate.UpdateSecretOptions{ + Address: accessBoxAddress, + FrostFSKey: key, + GatesPublicKeys: gatesPublicKeys, + GatePrivateKey: gateKey, + } + + if err = authmate.New(log, frostFS).UpdateSecret(ctx, os.Stdout, updateSecretOptions); err != nil { + return fmt.Errorf("failed to update secret: %s", err) + } + return nil +}