[#414] authmate: Add register-user command
All checks were successful
/ Builds (1.20) (pull_request) Successful in 14m30s
/ Builds (1.21) (pull_request) Successful in 14m25s
/ DCO (pull_request) Successful in 6m47s
/ Vulncheck (pull_request) Successful in 6m59s
/ Lint (pull_request) Successful in 11m52s
/ Tests (1.20) (pull_request) Successful in 8m24s
/ Tests (1.21) (pull_request) Successful in 8m19s
All checks were successful
/ Builds (1.20) (pull_request) Successful in 14m30s
/ Builds (1.21) (pull_request) Successful in 14m25s
/ DCO (pull_request) Successful in 6m47s
/ Vulncheck (pull_request) Successful in 6m59s
/ Lint (pull_request) Successful in 11m52s
/ Tests (1.20) (pull_request) Successful in 8m24s
/ Tests (1.21) (pull_request) Successful in 8m19s
New command allows register user in frostfsid and set allowed rules in policy contract Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
This commit is contained in:
parent
f86b82351a
commit
70eedfc077
12 changed files with 284 additions and 93 deletions
|
@ -16,6 +16,7 @@ This document outlines major changes between releases.
|
||||||
- Support tag checks in policies (#357, #365, #392)
|
- Support tag checks in policies (#357, #365, #392)
|
||||||
- Support IAM-MFA checks (#367)
|
- Support IAM-MFA checks (#367)
|
||||||
- More docs (#334, #353)
|
- More docs (#334, #353)
|
||||||
|
- Add `register-user` command to `authmate` (#414)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Update dependencies noted by govulncheck (#368)
|
- Update dependencies noted by govulncheck (#368)
|
||||||
|
|
|
@ -16,6 +16,10 @@ type (
|
||||||
frostFSIDInitError struct {
|
frostFSIDInitError struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
policyInitError struct {
|
||||||
|
err error
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func wrapPreparationError(e error) error {
|
func wrapPreparationError(e error) error {
|
||||||
|
@ -50,6 +54,14 @@ func (e frostFSIDInitError) Error() string {
|
||||||
return e.err.Error()
|
return e.err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func wrapPolicyInitError(e error) error {
|
||||||
|
return policyInitError{e}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e policyInitError) 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 {
|
||||||
|
@ -62,6 +74,8 @@ func ExitCode(e error) int {
|
||||||
return 4
|
return 4
|
||||||
case frostFSIDInitError:
|
case frostFSIDInitError:
|
||||||
return 4
|
return 4
|
||||||
|
case policyInitError:
|
||||||
|
return 5
|
||||||
}
|
}
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ 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/contract"
|
|
||||||
"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"
|
||||||
|
@ -36,16 +35,10 @@ const (
|
||||||
lifetimeFlag = "lifetime"
|
lifetimeFlag = "lifetime"
|
||||||
containerPolicyFlag = "container-policy"
|
containerPolicyFlag = "container-policy"
|
||||||
awsCLICredentialFlag = "aws-cli-credentials"
|
awsCLICredentialFlag = "aws-cli-credentials"
|
||||||
frostfsIDFlag = "frostfsid"
|
|
||||||
frostfsIDProxyFlag = "frostfsid-proxy"
|
|
||||||
frostfsIDNamespaceFlag = "frostfsid-namespace"
|
|
||||||
rpcEndpointFlag = "rpc-endpoint"
|
|
||||||
attributesFlag = "attributes"
|
attributesFlag = "attributes"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const walletPassphraseCfg = "wallet.passphrase"
|
||||||
walletPassphraseCfg = "wallet.passphrase"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultAccessBoxLifetime = 30 * 24 * time.Hour
|
defaultAccessBoxLifetime = 30 * 24 * time.Hour
|
||||||
|
@ -79,10 +72,6 @@ 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(frostfsIDProxyFlag, "", "Proxy contract hash (LE) or name in NNS to use when interact with frostfsid contract")
|
|
||||||
issueSecretCmd.Flags().String(frostfsIDNamespaceFlag, "", "Namespace to register public key in frostfsid contract")
|
|
||||||
issueSecretCmd.Flags().String(rpcEndpointFlag, "", "NEO node RPC address")
|
|
||||||
issueSecretCmd.Flags().String(attributesFlag, "", "User attributes in form of Key1=Value1,Key2=Value2 (note: you cannot override system attributes)")
|
issueSecretCmd.Flags().String(attributesFlag, "", "User attributes in form of Key1=Value1,Key2=Value2 (note: you cannot override system attributes)")
|
||||||
|
|
||||||
_ = issueSecretCmd.MarkFlagRequired(walletFlag)
|
_ = issueSecretCmd.MarkFlagRequired(walletFlag)
|
||||||
|
@ -148,29 +137,6 @@ 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 := contract.Config{
|
|
||||||
RPCAddress: rpcAddress,
|
|
||||||
Contract: frostFSID,
|
|
||||||
ProxyContract: viper.GetString(frostfsIDProxyFlag),
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
frostfsIDClient, err := createFrostFSID(ctx, log, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return wrapFrostFSIDInitError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = registerPublicKey(frostfsIDClient, viper.GetString(frostfsIDNamespaceFlag), key.PublicKey()); err != nil {
|
|
||||||
return wrapBusinessLogicError(fmt.Errorf("failed to register key in frostfsid: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customAttrs, err := parseObjectAttrs(viper.GetString(attributesFlag))
|
customAttrs, err := parseObjectAttrs(viper.GetString(attributesFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapPreparationError(fmt.Errorf("failed to parse attributes: %s", err))
|
return wrapPreparationError(fmt.Errorf("failed to parse attributes: %s", err))
|
||||||
|
|
202
cmd/s3-authmate/modules/register-user.go
Normal file
202
cmd/s3-authmate/modules/register-user.go
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-contract/policy"
|
||||||
|
ffsidContract "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/frostfsid/contract"
|
||||||
|
policyContact "git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/frostfs/policy/contract"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/logs"
|
||||||
|
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
|
||||||
|
"git.frostfs.info/TrueCloudLab/policy-engine/pkg/chain"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var registerUserCmd = &cobra.Command{
|
||||||
|
Use: "register-user",
|
||||||
|
Short: "Register user and add allowed policy to him",
|
||||||
|
Long: "Register user in FrostFSID contract and add allowed policies. This is need to get access to s3-gw operations.",
|
||||||
|
Example: `frostfs-s3-authmate register-user --wallet wallet.json --rpc-endpoint http://morph-chain.frostfs.devenv:30333
|
||||||
|
frostfs-s3-authmate register-user --wallet wallet.json --contract-wallet contract-wallet.json --rpc-endpoint http://morph-chain.frostfs.devenv:30333 --namespace namespace --frostfsid-name devenv --frostfsid-contract frostfsid.frostfs --proxy-contract proxy.frostfs --policy-contract policy.frostfs`,
|
||||||
|
RunE: runRegisterUserCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
frostfsIDContractFlag = "frostfsid-contract"
|
||||||
|
proxyContractFlag = "proxy-contract"
|
||||||
|
usernameFlag = "username"
|
||||||
|
namespaceFlag = "namespace"
|
||||||
|
policyContractFlag = "policy-contract"
|
||||||
|
contractWalletFlag = "contract-wallet"
|
||||||
|
contractWalletAddressFlag = "contract-wallet-address"
|
||||||
|
rpcEndpointFlag = "rpc-endpoint"
|
||||||
|
)
|
||||||
|
|
||||||
|
const walletContractPassphraseCfg = "wallet.contract.passphrase"
|
||||||
|
|
||||||
|
func initRegisterUserCmd() {
|
||||||
|
registerUserCmd.Flags().String(walletFlag, "", "Path to the wallet with account of the user that will be registered in FrostFS ID contract")
|
||||||
|
registerUserCmd.Flags().String(addressFlag, "", "Address of the user wallet that will be registered in FrostFS ID contract")
|
||||||
|
registerUserCmd.Flags().String(frostfsIDContractFlag, "frostfsid.frostfs", "FrostfsID contract hash (LE) or name in NNS to register public key in contract")
|
||||||
|
registerUserCmd.Flags().String(proxyContractFlag, "proxy.frostfs", "Proxy contract hash (LE) or name in NNS to use when interact with frostfsid contract")
|
||||||
|
registerUserCmd.Flags().String(namespaceFlag, "", "Namespace to register public key in frostfsid contract and add policy chains")
|
||||||
|
registerUserCmd.Flags().String(usernameFlag, "", "Username to set for public key in frostfsid contract")
|
||||||
|
registerUserCmd.Flags().String(contractWalletFlag, "", "Path to wallet that will be used to interact with contracts (if missing key from wallet flag be used)")
|
||||||
|
registerUserCmd.Flags().String(contractWalletAddressFlag, "", "Address of the contract wallet account")
|
||||||
|
registerUserCmd.Flags().String(policyContractFlag, "policy.frostfs", "Policy contract hash (LE) or name in NNS to save allowed chains for key")
|
||||||
|
registerUserCmd.Flags().String(rpcEndpointFlag, "", "NEO node RPC address")
|
||||||
|
|
||||||
|
_ = registerUserCmd.MarkFlagRequired(walletFlag)
|
||||||
|
_ = registerUserCmd.MarkFlagRequired(rpcEndpointFlag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runRegisterUserCmd(cmd *cobra.Command, _ []string) error {
|
||||||
|
ctx, cancel := context.WithTimeout(cmd.Context(), viper.GetDuration(timeoutFlag))
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
log := getLogger()
|
||||||
|
|
||||||
|
key, contractKey, err := parseKeys()
|
||||||
|
if err != nil {
|
||||||
|
return wrapPreparationError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
frostfsIDClient, err := initFrostFSIDContract(ctx, log, contractKey)
|
||||||
|
if err != nil {
|
||||||
|
return wrapFrostFSIDInitError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = registerPublicKey(log, frostfsIDClient, key.PublicKey()); err != nil {
|
||||||
|
return wrapBusinessLogicError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
policyClient, err := initPolicyContract(ctx, log, contractKey)
|
||||||
|
if err != nil {
|
||||||
|
return wrapPolicyInitError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = addAllowedPolicyChains(cmd, log, policyClient, key.PublicKey()); err != nil {
|
||||||
|
return wrapBusinessLogicError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKeys() (userKey *keys.PrivateKey, contractKey *keys.PrivateKey, err error) {
|
||||||
|
password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg)
|
||||||
|
key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to load frostfs private key: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
contractKey = key
|
||||||
|
if contractWallet := viper.GetString(contractWalletFlag); contractWallet != "" {
|
||||||
|
password = wallet.GetPassword(viper.GetViper(), walletContractPassphraseCfg)
|
||||||
|
contractKey, err = wallet.GetKeyFromPath(contractWallet, viper.GetString(contractWalletAddressFlag), password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to load contract private key: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, contractKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFrostFSIDContract(ctx context.Context, log *zap.Logger, key *keys.PrivateKey) (*ffsidContract.FrostFSID, error) {
|
||||||
|
log.Debug(logs.PrepareFrostfsIDClient)
|
||||||
|
|
||||||
|
cfg := ffsidContract.Config{
|
||||||
|
RPCAddress: viper.GetString(rpcEndpointFlag),
|
||||||
|
Contract: viper.GetString(frostfsIDContractFlag),
|
||||||
|
ProxyContract: viper.GetString(proxyContractFlag),
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := ffsidContract.New(ctx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create frostfsid client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cli, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerPublicKey(log *zap.Logger, cli *ffsidContract.FrostFSID, key *keys.PublicKey) error {
|
||||||
|
namespace := viper.GetString(namespaceFlag)
|
||||||
|
|
||||||
|
log.Debug(logs.CreateSubjectInFrostFSID)
|
||||||
|
err := cli.Wait(cli.CreateSubject(namespace, key))
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "subject already exists") {
|
||||||
|
log.Debug(logs.SubjectAlreadyExistsInFrostFSID, zap.String("address", key.Address()))
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("create subject in frostfsid: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name := viper.GetString(usernameFlag)
|
||||||
|
if name == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug(logs.SetSubjectNameInFrostFSID)
|
||||||
|
if err = cli.Wait(cli.SetSubjectName(key, name)); err != nil {
|
||||||
|
return fmt.Errorf("set subject name in frostfsid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initPolicyContract(ctx context.Context, log *zap.Logger, key *keys.PrivateKey) (*policyContact.Client, error) {
|
||||||
|
log.Debug(logs.PreparePolicyClient)
|
||||||
|
|
||||||
|
cfg := policyContact.Config{
|
||||||
|
RPCAddress: viper.GetString(rpcEndpointFlag),
|
||||||
|
Contract: viper.GetString(policyContractFlag),
|
||||||
|
ProxyContract: viper.GetString(proxyContractFlag),
|
||||||
|
Key: key,
|
||||||
|
}
|
||||||
|
|
||||||
|
cli, err := policyContact.New(ctx, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("create policy client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cli, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addAllowedPolicyChains(cmd *cobra.Command, log *zap.Logger, cli *policyContact.Client, key *keys.PublicKey) error {
|
||||||
|
log.Debug(logs.AddPolicyChainRules)
|
||||||
|
namespace := viper.GetString(namespaceFlag)
|
||||||
|
|
||||||
|
allowAllRule := chain.Rule{
|
||||||
|
Status: chain.Allow,
|
||||||
|
Actions: chain.Actions{Names: []string{"*"}},
|
||||||
|
Resources: chain.Resources{Names: []string{"*"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
chains := []*chain.Chain{
|
||||||
|
{ID: chain.ID(chain.S3 + ":authmate"), Rules: []chain.Rule{allowAllRule}},
|
||||||
|
{ID: chain.ID(chain.Ingress + ":authmate"), Rules: []chain.Rule{allowAllRule}},
|
||||||
|
}
|
||||||
|
|
||||||
|
kind := policy.Kind(policy.User)
|
||||||
|
entity := namespace + ":" + key.Address()
|
||||||
|
tx := cli.StartTx()
|
||||||
|
for _, ch := range chains {
|
||||||
|
tx.AddChain(kind, entity, ch.ID, ch.Bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cli.SendTx(tx); err != nil {
|
||||||
|
return fmt.Errorf("add policy chain: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.Printf("Added policy rules:\nkind: '%c'\nentity: '%s'\nchains:\n", kind, entity)
|
||||||
|
enc := json.NewEncoder(os.Stdout)
|
||||||
|
return enc.Encode(chains)
|
||||||
|
}
|
|
@ -65,4 +65,7 @@ GoVersion: {{ runtimeVersion }}
|
||||||
|
|
||||||
rootCmd.AddCommand(updateSecretCmd)
|
rootCmd.AddCommand(updateSecretCmd)
|
||||||
initUpdateSecretCmd()
|
initUpdateSecretCmd()
|
||||||
|
|
||||||
|
rootCmd.AddCommand(registerUserCmd)
|
||||||
|
initRegisterUserCmd()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ 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/contract"
|
|
||||||
"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"
|
||||||
|
@ -40,10 +39,6 @@ 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(frostfsIDProxyFlag, "", "Proxy contract hash (LE) or name in NNS to use when interact with frostfsid contract")
|
|
||||||
updateSecretCmd.Flags().String(frostfsIDNamespaceFlag, "", "Namespace to register public key in frostfsid contract")
|
|
||||||
updateSecretCmd.Flags().String(rpcEndpointFlag, "", "NEO node RPC address")
|
|
||||||
updateSecretCmd.Flags().String(attributesFlag, "", "User attributes in form of Key1=Value1,Key2=Value2 (note: you cannot override system attributes)")
|
updateSecretCmd.Flags().String(attributesFlag, "", "User attributes in form of Key1=Value1,Key2=Value2 (note: you cannot override system attributes)")
|
||||||
|
|
||||||
_ = updateSecretCmd.MarkFlagRequired(walletFlag)
|
_ = updateSecretCmd.MarkFlagRequired(walletFlag)
|
||||||
|
@ -100,29 +95,6 @@ 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 := contract.Config{
|
|
||||||
RPCAddress: rpcAddress,
|
|
||||||
Contract: frostFSID,
|
|
||||||
ProxyContract: viper.GetString(frostfsIDProxyFlag),
|
|
||||||
Key: key,
|
|
||||||
}
|
|
||||||
|
|
||||||
frostfsIDClient, err := createFrostFSID(ctx, log, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return wrapFrostFSIDInitError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = registerPublicKey(frostfsIDClient, viper.GetString(frostfsIDNamespaceFlag), key.PublicKey()); err != nil {
|
|
||||||
return wrapBusinessLogicError(fmt.Errorf("failed to register key in frostfsid: %w", err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customAttrs, err := parseObjectAttrs(viper.GetString(attributesFlag))
|
customAttrs, err := parseObjectAttrs(viper.GetString(attributesFlag))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return wrapPreparationError(fmt.Errorf("failed to parse attributes: %s", err))
|
return wrapPreparationError(fmt.Errorf("failed to parse attributes: %s", err))
|
||||||
|
|
|
@ -11,7 +11,6 @@ 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/contract"
|
|
||||||
"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/object"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
||||||
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
||||||
|
@ -145,26 +144,6 @@ func getLogger() *zap.Logger {
|
||||||
return log
|
return log
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFrostFSID(ctx context.Context, log *zap.Logger, cfg contract.Config) (*contract.FrostFSID, error) {
|
|
||||||
log.Debug(logs.PrepareFrostfsIDClient)
|
|
||||||
|
|
||||||
cli, err := contract.New(ctx, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("create frostfsid client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cli, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func registerPublicKey(cli *contract.FrostFSID, namespace string, key *keys.PublicKey) error {
|
|
||||||
err := cli.Wait(cli.CreateSubject(namespace, key))
|
|
||||||
if err != nil && !strings.Contains(err.Error(), "subject already exists") {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseObjectAttrs(attributes string) ([]object.Attribute, error) {
|
func parseObjectAttrs(attributes string) ([]object.Attribute, error) {
|
||||||
if len(attributes) == 0 {
|
if len(attributes) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -146,12 +146,6 @@ the secret. Format of `access_key_id`: `%cid0%oid`, where 0(zero) is a delimiter
|
||||||
24h). Default value is `720h` (30 days). It will be ceil rounded to the nearest amount of epoch
|
24h). Default value is `720h` (30 days). It will be ceil rounded to the nearest amount of epoch
|
||||||
* `--aws-cli-credentials` - path to the aws cli credentials file, where authmate will write `access_key_id` and
|
* `--aws-cli-credentials` - path to the aws cli credentials file, where authmate will write `access_key_id` and
|
||||||
`secret_access_key` to
|
`secret_access_key` to
|
||||||
* `--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
|
|
||||||
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
|
||||||
|
|
||||||
|
@ -212,6 +206,57 @@ can be set via parameter `--container-policy` (json-string and file path allowed
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## User registration
|
||||||
|
|
||||||
|
To be able to interact with FrostFS Storage using s3-gw the user (whose credential is used) must be registered in
|
||||||
|
`frostfsid` contract and also must have allowed chain rules in `policy` contract.
|
||||||
|
|
||||||
|
### CLI parameters
|
||||||
|
|
||||||
|
**Required parameters:**
|
||||||
|
|
||||||
|
* `--wallet` is a path to a wallet `.json` file. You can provide a passphrase to decrypt
|
||||||
|
a wallet via environment variable `AUTHMATE_WALLET_PASSPHRASE`, or you will be asked to enter a passphrase
|
||||||
|
interactively. You can also specify an account address to use from a wallet using the `--address` parameter. Key from
|
||||||
|
this wallet will be registered in `frostfsid` contract and for this key allowed rules will be added.
|
||||||
|
* `--rpc-endpoint` -- NEO node RPC address.
|
||||||
|
|
||||||
|
**Optional parameters:**
|
||||||
|
|
||||||
|
* `--frostfsid-contract` -- FrostfsID contract hash (LE) or name in NNS to register public key in contract (
|
||||||
|
default: `frostfsid.frostfs`).
|
||||||
|
* `--username` -- Username to set for public key in frostfsid contract.
|
||||||
|
* `--namespace` -- Namespace to register public key in frostfsid contract and add allowed rules (default: `""`).
|
||||||
|
* `--contract-wallet` -- is a path to a contract wallet `.json` file. This wallet will be used to sign transactions
|
||||||
|
(if missing key from wallet flag be used). You can provide a passphrase to decrypt
|
||||||
|
a wallet via environment variable `AUTHMATE_WALLET_CONTRACT_PASSPHRASE`, or you will be asked to enter a passphrase
|
||||||
|
interactively. You can also specify an account address to use from a wallet using the `--contract-wallet-address`
|
||||||
|
parameter.
|
||||||
|
* `--policy-contract` -- Policy contract hash (LE) or name in NNS to save allowed chains for key (
|
||||||
|
default: `policy.frostfs`).
|
||||||
|
* `--proxy-contract` -- Proxy contract hash (LE) or name in NNS to use when interact with frostfsid contract (
|
||||||
|
default: `proxy.frostfs`).
|
||||||
|
|
||||||
|
Example command that not only create access keys for key, but also register key in frostfsid contract and
|
||||||
|
create allowed rules in policy contract:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ frostfs-s3-authmate register-user --wallet subject-wallet.json \
|
||||||
|
--contract-wallet wallet-registered-in-proxy-contract.json \
|
||||||
|
--frostfsid-contract frostfsid.frostfs \
|
||||||
|
--namespace "" \
|
||||||
|
--usrername devenv \
|
||||||
|
--proxy-contract proxy.frostfs \
|
||||||
|
--policy-contract policy.frostfs \
|
||||||
|
--rpc-endpoint http://morph-chain.frostfs.devenv:30333
|
||||||
|
|
||||||
|
Added policy rules:
|
||||||
|
kind: 'u'
|
||||||
|
entity: ':NbUgTSFvPmsRxmGeWpuuGeJUoRoi6PErcM'
|
||||||
|
chains:
|
||||||
|
[{"ID":"czM6YXV0aG1hdGU=","Rules":[{"Status":"Allow","Actions":{"Inverted":false,"Names":["*"]},"Resources":{"Inverted":false,"Names":["*"]},"Any":false,"Condition":null}],"MatchType":"DenyPriority"},{"ID":"aW5ncmVzczphdXRobWF0ZQ==","Rules":[{"Status":"Allow","Actions":{"Inverted":false,"Names":["*"]},"Resources":{"Inverted":false,"Names":["*"]},"Any":false,"Condition":null}],"MatchType":"DenyPriority"}]
|
||||||
|
```
|
||||||
|
|
||||||
## Obtaining credential secrets
|
## Obtaining credential secrets
|
||||||
|
|
||||||
You can get a secret access key and bearer token associated with an access key ID by obtaining a
|
You can get a secret access key and bearer token associated with an access key ID by obtaining a
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -4,7 +4,7 @@ go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240417080107-db361318009c
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f
|
git.frostfs.info/TrueCloudLab/frostfs-sdk-go v0.0.0-20240531132048-ebd8fcd1685f
|
||||||
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240611102930-ac965e8d176a
|
git.frostfs.info/TrueCloudLab/policy-engine v0.0.0-20240611102930-ac965e8d176a
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 h1:H5GvrVlowIMWfzqQkhY0p0myooJxQ1sMRVSFfXawwWg=
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3 h1:H5GvrVlowIMWfzqQkhY0p0myooJxQ1sMRVSFfXawwWg=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
|
git.frostfs.info/TrueCloudLab/frostfs-api-go/v2 v2.16.1-0.20240530152826-2f6d3209e1d3/go.mod h1:OBDSr+DqV1z4VDouoX3YMleNc4DPBVBWTG3WDT2PK1o=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd h1:fujTUMMn0wnpEKNDWLejFL916EPuaYD1MdZpk1ZokU8=
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240417080107-db361318009c h1:V58j1eg12wxbl4fUbjWtBOexl3zFt4w0EGHpCPkWJhQ=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240409115729-6eb492025bdd/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
|
git.frostfs.info/TrueCloudLab/frostfs-contract v0.19.3-0.20240417080107-db361318009c/go.mod h1:F/fe1OoIDKr5Bz99q4sriuHDuf3aZefZy9ZsCqEtgxc=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0 h1:FxqFDhQYYgpe41qsIHVOcdzSVCB8JNSfPG7Uk4r2oSk=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
git.frostfs.info/TrueCloudLab/frostfs-crypto v0.6.0/go.mod h1:RUIKZATQLJ+TaYQa60X2fTDwfuhMfm8Ar60bQ5fr+vU=
|
||||||
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
|
git.frostfs.info/TrueCloudLab/frostfs-observability v0.0.0-20230531082742-c97d21411eb6 h1:aGQ6QaAnTerQ5Dq5b2/f9DUQtSqPkZZ/bkMx/HKuLCo=
|
||||||
|
|
|
@ -78,6 +78,10 @@ func (f *FrostFSID) CreateSubject(namespace string, key *keys.PublicKey) (util.U
|
||||||
return f.cli.CreateSubject(namespace, key)
|
return f.cli.CreateSubject(namespace, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FrostFSID) SetSubjectName(key *keys.PublicKey, name string) (util.Uint256, uint32, error) {
|
||||||
|
return f.cli.SetSubjectName(key.GetScriptHash(), name)
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FrostFSID) Wait(tx util.Uint256, vub uint32, err error) error {
|
func (f *FrostFSID) Wait(tx util.Uint256, vub uint32, err error) error {
|
||||||
_, err = f.cli.Wait(tx, vub, err)
|
_, err = f.cli.Wait(tx, vub, err)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -51,6 +51,11 @@ const (
|
||||||
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
|
PrepareFrostfsIDClient = "prepare frostfsid client" // Debug in ../../cmd/s3-authmate/modules/utils.go
|
||||||
|
PreparePolicyClient = "prepare policy client" // Debug in ../../cmd/s3-authmate/modules/utils.go
|
||||||
|
CreateSubjectInFrostFSID = "create subject in frostfsid" // Debug in ../../cmd/s3-authmate/modules/utils.go
|
||||||
|
SubjectAlreadyExistsInFrostFSID = "subject already exists in frostfsid" // Debug in ../../cmd/s3-authmate/modules/utils.go
|
||||||
|
SetSubjectNameInFrostFSID = "set subject name in frostfsid" // Debug in ../../cmd/s3-authmate/modules/utils.go
|
||||||
|
AddPolicyChainRules = "add policy chain rules" // 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
|
||||||
|
|
Loading…
Reference in a new issue