frostfs-s3-gw/cmd/s3-authmate/modules/issue-secret.go
Denis Kirillov 18878b66d3
Some checks failed
/ DCO (pull_request) Failing after 3s
/ Vulncheck (pull_request) Failing after 2s
/ Builds (1.19) (pull_request) Failing after 3s
/ Builds (1.20) (pull_request) Successful in 7m47s
/ Lint (pull_request) Failing after 4s
/ Tests (1.19) (pull_request) Successful in 8m8s
/ Tests (1.20) (pull_request) Failing after 3s
[#175] Use gate owner as object owner
This is required because node check session token owner
TrueCloudLab/frostfs-node#528
For client cut TrueCloudLab/frostfs-sdk-go#114
such owner will be gate owner

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2023-08-08 12:22:33 +03:00

176 lines
7.6 KiB
Go

package modules
import (
"context"
"errors"
"fmt"
"os"
"time"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/authmate"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var issueSecretCmd = &cobra.Command{
Use: "issue-secret",
Short: "Issue a secret in FrostFS network",
Long: "Creates new s3 credentials to use with frostfs-s3-gw",
Example: `frostfs-s3-authmate issue-secret --wallet wallet.json --peer s01.frostfs.devenv:8080 --gate-public-key 031a6c6fbbdf02ca351745fa86b9ba5a9452d785ac4f7fc2b7548ca2a46c4fcf4a`,
RunE: runIssueSecretCmd,
}
const (
walletFlag = "wallet"
addressFlag = "address"
peerFlag = "peer"
bearerRulesFlag = "bearer-rules"
disableImpersonateFlag = "disable-impersonate"
gatePublicKeyFlag = "gate-public-key"
containerIDFlag = "container-id"
containerFriendlyNameFlag = "container-friendly-name"
containerPlacementPolicyFlag = "container-placement-policy"
sessionTokensFlag = "session-tokens"
lifetimeFlag = "lifetime"
containerPolicyFlag = "container-policy"
awsCLICredentialFlag = "aws-cli-credentials"
)
const (
walletPassphraseCfg = "wallet.passphrase"
)
const (
defaultAccessBoxLifetime = 30 * 24 * time.Hour
defaultPoolDialTimeout = 5 * time.Second
defaultPoolHealthcheckTimeout = 5 * time.Second
defaultPoolRebalanceInterval = 30 * time.Second
defaultPoolStreamTimeout = 10 * time.Second
)
const (
poolDialTimeoutFlag = "pool-dial-timeout"
poolHealthcheckTimeoutFlag = "pool-healthcheck-timeout"
poolRebalanceIntervalFlag = "pool-rebalance-interval"
poolStreamTimeoutFlag = "pool-stream-timeout"
)
func initIssueSecretCmd() {
issueSecretCmd.Flags().String(walletFlag, "", "Path to the wallet that will be owner of the credentials")
issueSecretCmd.Flags().String(addressFlag, "", "Address of the wallet account")
issueSecretCmd.Flags().String(peerFlag, "", "Address of a frostfs peer to connect to")
issueSecretCmd.Flags().String(bearerRulesFlag, "", "Rules for bearer token (filepath or a plain json string are allowed, can be used only with --disable-impersonate)")
issueSecretCmd.Flags().Bool(disableImpersonateFlag, false, "Mark token as not impersonate to don't consider token signer as request owner (must be provided to use --bearer-rules flag)")
issueSecretCmd.Flags().StringSlice(gatePublicKeyFlag, nil, "Public 256r1 key of a gate (use flags repeatedly for multiple gates or separate them by comma)")
issueSecretCmd.Flags().String(containerIDFlag, "", "Auth container id to put the secret into (if not provided new container will be created)")
issueSecretCmd.Flags().String(containerFriendlyNameFlag, "", "Friendly name of auth container to put the secret into (flag value will be used only if --container-id is missed)")
issueSecretCmd.Flags().String(containerPlacementPolicyFlag, "REP 2 IN X CBF 3 SELECT 2 FROM * AS X", "Placement policy of auth container to put the secret into (flag value will be used only if --container-id is missed)")
issueSecretCmd.Flags().String(sessionTokensFlag, "", "create session tokens with rules, if the rules are set as 'none', no session tokens will be created")
issueSecretCmd.Flags().Duration(lifetimeFlag, defaultAccessBoxLifetime, "Lifetime of tokens. For example 50h30m (note: max time unit is an hour so to set a day you should use 24h).\nIt will be ceil rounded to the nearest amount of epoch.")
issueSecretCmd.Flags().String(containerPolicyFlag, "", "Mapping AWS storage class to FrostFS storage policy as plain json string or path to json file")
issueSecretCmd.Flags().String(awsCLICredentialFlag, "", "Path to the aws cli credential file")
issueSecretCmd.Flags().Duration(poolDialTimeoutFlag, defaultPoolDialTimeout, "Timeout for connection to the node in pool to be established")
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(poolStreamTimeoutFlag, defaultPoolStreamTimeout, "Timeout for individual operation in streaming RPC")
_ = issueSecretCmd.MarkFlagRequired(walletFlag)
_ = issueSecretCmd.MarkFlagRequired(peerFlag)
_ = issueSecretCmd.MarkFlagRequired(gatePublicKeyFlag)
}
func runIssueSecretCmd(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)
}
var cnrID cid.ID
containerID := viper.GetString(containerIDFlag)
if len(containerID) > 0 {
if err = cnrID.DecodeString(containerID); err != nil {
return fmt.Errorf("failed to parse auth container id: %s", 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)
}
lifetime := viper.GetDuration(lifetimeFlag)
if lifetime <= 0 {
return fmt.Errorf("lifetime must be greater 0, current value: %d", lifetime)
}
policies, err := parsePolicies(viper.GetString(containerPolicyFlag))
if err != nil {
return fmt.Errorf("couldn't parse container policy: %s", err.Error())
}
disableImpersonate := viper.GetBool(disableImpersonateFlag)
eaclRules := viper.GetString(bearerRulesFlag)
if !disableImpersonate && eaclRules != "" {
return errors.New("--bearer-rules flag can be used only with --disable-impersonate")
}
bearerRules, err := getJSONRules(eaclRules)
if err != nil {
return fmt.Errorf("couldn't parse 'bearer-rules' flag: %s", err.Error())
}
sessionRules, skipSessionRules, err := getSessionRules(viper.GetString(sessionTokensFlag))
if err != nil {
return fmt.Errorf("couldn't parse 'session-tokens' flag: %s", err.Error())
}
poolCfg := PoolConfig{
Key: key,
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)
}
issueSecretOptions := &authmate.IssueSecretOptions{
Container: authmate.ContainerOptions{
ID: cnrID,
FriendlyName: viper.GetString(containerFriendlyNameFlag),
PlacementPolicy: viper.GetString(containerPlacementPolicyFlag),
},
FrostFSKey: key,
GatesPublicKeys: gatesPublicKeys,
EACLRules: bearerRules,
Impersonate: !disableImpersonate,
SessionTokenRules: sessionRules,
SkipSessionRules: skipSessionRules,
ContainerPolicies: policies,
Lifetime: lifetime,
AwsCliCredentialsFile: viper.GetString(awsCLICredentialFlag),
}
if err = authmate.New(log, frostFS).IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {
return fmt.Errorf("failed to issue secret: %s", err)
}
return nil
}