2021-05-18 18:49:09 +00:00
package main
import (
"context"
2021-05-28 20:48:23 +00:00
"crypto/ecdsa"
2023-06-13 09:35:40 +00:00
"encoding/hex"
2021-07-16 12:35:07 +00:00
"encoding/json"
2021-05-18 18:49:09 +00:00
"fmt"
"os"
2021-05-25 10:30:54 +00:00
"os/signal"
2022-07-13 12:27:05 +00:00
"runtime"
2021-06-02 18:53:20 +00:00
"strings"
2021-05-25 10:30:54 +00:00
"syscall"
2021-05-18 18:49:09 +00:00
"time"
2023-03-07 14:38:08 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api"
2023-06-01 11:30:49 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/api/auth"
2023-03-07 14:38:08 +00:00
"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/version"
"git.frostfs.info/TrueCloudLab/frostfs-s3-gw/internal/wallet"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
2023-06-13 09:35:40 +00:00
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
2023-03-07 14:38:08 +00:00
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
2022-06-14 15:15:29 +00:00
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
2021-06-24 15:21:34 +00:00
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/spf13/viper"
2021-05-18 18:49:09 +00:00
"github.com/urfave/cli/v2"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
const (
2022-12-08 10:20:09 +00:00
poolDialTimeout = 5 * time . Second
poolHealthcheckTimeout = 5 * time . Second
poolRebalanceInterval = 30 * time . Second
poolStreamTimeout = 10 * time . Second
2021-11-19 08:01:03 +00:00
// a month.
2022-06-14 15:15:29 +00:00
defaultLifetime = 30 * 24 * time . Hour
defaultPresignedLifetime = 12 * time . Hour
2021-05-18 18:49:09 +00:00
)
2022-12-08 10:20:09 +00:00
type PoolConfig struct {
Key * ecdsa . PrivateKey
Address string
DialTimeout time . Duration
HealthcheckTimeout time . Duration
StreamTimeout time . Duration
RebalanceInterval time . Duration
}
2021-05-18 18:49:09 +00:00
var (
2022-03-03 12:43:56 +00:00
walletPathFlag string
accountAddressFlag string
peerAddressFlag string
eaclRulesFlag string
2022-10-25 09:30:18 +00:00
disableImpersonateFlag bool
2022-03-03 12:43:56 +00:00
gateWalletPathFlag string
gateAccountAddressFlag string
accessKeyIDFlag string
containerIDFlag string
containerFriendlyName string
containerPlacementPolicy string
gatesPublicKeysFlag cli . StringSlice
logEnabledFlag bool
logDebugEnabledFlag bool
sessionTokenFlag string
lifetimeFlag time . Duration
2022-06-14 15:15:29 +00:00
endpointFlag string
bucketFlag string
objectFlag string
methodFlag string
profileFlag string
regionFlag string
secretAccessKeyFlag string
2022-03-03 12:43:56 +00:00
containerPolicies string
awcCliCredFile string
timeoutFlag time . Duration
2022-12-08 10:20:09 +00:00
// pool timeouts flag.
poolDialTimeoutFlag time . Duration
poolHealthcheckTimeoutFlag time . Duration
poolRebalanceIntervalFlag time . Duration
poolStreamTimeoutFlag time . Duration
2021-06-24 15:21:34 +00:00
)
const (
envWalletPassphrase = "wallet.passphrase"
envWalletGatePassphrase = "wallet.gate.passphrase"
2023-06-13 09:35:40 +00:00
envSecretAccessKey = "secret.access.key"
2021-05-18 18:49:09 +00:00
)
var zapConfig = zap . Config {
Development : true ,
Encoding : "console" ,
Level : zap . NewAtomicLevelAt ( zapcore . FatalLevel ) ,
OutputPaths : [ ] string { "stdout" } ,
EncoderConfig : zapcore . EncoderConfig {
MessageKey : "message" ,
LevelKey : "level" ,
EncodeLevel : zapcore . CapitalLevelEncoder ,
TimeKey : "time" ,
EncodeTime : zapcore . ISO8601TimeEncoder ,
CallerKey : "caller" ,
EncodeCaller : zapcore . ShortCallerEncoder ,
} ,
}
func prepare ( ) ( context . Context , * zap . Logger ) {
var (
2021-05-25 10:30:54 +00:00
err error
log = zap . NewNop ( )
ctx , _ = signal . NotifyContext ( context . Background ( ) , syscall . SIGINT , syscall . SIGTERM , syscall . SIGHUP )
2021-05-18 18:49:09 +00:00
)
if ! logEnabledFlag {
2021-05-25 10:30:54 +00:00
return ctx , log
2021-05-18 18:49:09 +00:00
} else if logDebugEnabledFlag {
zapConfig . Level = zap . NewAtomicLevelAt ( zapcore . DebugLevel )
}
if log , err = zapConfig . Build ( ) ; err != nil {
2022-06-22 19:40:52 +00:00
panic ( fmt . Errorf ( "create logger: %w" , err ) )
2021-05-18 18:49:09 +00:00
}
2021-05-25 10:30:54 +00:00
return ctx , log
2021-05-18 18:49:09 +00:00
}
func main ( ) {
app := & cli . App {
2022-12-15 14:08:25 +00:00
Name : "FrostFS S3 Authmate" ,
Usage : "Helps manage delegated access via gates to data stored in FrostFS network" ,
2021-05-20 12:41:32 +00:00
Version : version . Version ,
2021-05-18 18:49:09 +00:00
Flags : appFlags ( ) ,
Commands : appCommands ( ) ,
}
2022-07-13 12:27:05 +00:00
cli . VersionPrinter = func ( c * cli . Context ) {
fmt . Printf ( "%s\nVersion: %s\nGoVersion: %s\n" , c . App . Name , c . App . Version , runtime . Version ( ) )
}
2021-05-18 18:49:09 +00:00
2021-06-24 15:21:34 +00:00
viper . AutomaticEnv ( )
viper . SetEnvPrefix ( "AUTHMATE" )
viper . SetEnvKeyReplacer ( strings . NewReplacer ( "." , "_" ) )
viper . AllowEmptyEnv ( true )
2021-05-18 18:49:09 +00:00
if err := app . Run ( os . Args ) ; err != nil {
_ , _ = fmt . Fprintf ( os . Stderr , "error: %s\n" , err )
os . Exit ( 100 )
}
}
func appFlags ( ) [ ] cli . Flag {
return [ ] cli . Flag {
& cli . BoolFlag {
Name : "with-log" ,
Usage : "Enable logger" ,
Destination : & logEnabledFlag ,
} ,
& cli . BoolFlag {
Name : "debug" ,
Usage : "Enable debug logger level" ,
Destination : & logDebugEnabledFlag ,
} ,
2021-12-26 20:38:16 +00:00
& cli . DurationFlag {
Name : "timeout" ,
Usage : "timeout of processing of the command, for example 2m " +
"(note: max time unit is an hour so to set a day you should use 24h)" ,
Destination : & timeoutFlag ,
Value : 1 * time . Minute ,
} ,
2021-05-18 18:49:09 +00:00
}
}
func appCommands ( ) [ ] * cli . Command {
return [ ] * cli . Command {
issueSecret ( ) ,
obtainSecret ( ) ,
2022-06-14 15:15:29 +00:00
generatePresignedURL ( ) ,
2021-05-18 18:49:09 +00:00
}
}
func issueSecret ( ) * cli . Command {
return & cli . Command {
Name : "issue-secret" ,
2022-12-20 08:38:58 +00:00
Usage : "Issue a secret in FrostFS network" ,
2021-05-18 18:49:09 +00:00
Flags : [ ] cli . Flag {
& cli . StringFlag {
2021-06-24 15:21:34 +00:00
Name : "wallet" ,
2021-05-18 18:49:09 +00:00
Value : "" ,
2021-06-24 15:21:34 +00:00
Usage : "path to the wallet" ,
2021-05-18 18:49:09 +00:00
Required : true ,
2021-06-24 15:21:34 +00:00
Destination : & walletPathFlag ,
} ,
& cli . StringFlag {
Name : "address" ,
Value : "" ,
Usage : "address of wallet account" ,
Required : false ,
Destination : & accountAddressFlag ,
2021-05-18 18:49:09 +00:00
} ,
& cli . StringFlag {
Name : "peer" ,
Value : "" ,
2022-12-20 08:38:58 +00:00
Usage : "address of a frostfs peer to connect to" ,
2021-05-18 18:49:09 +00:00
Required : true ,
Destination : & peerAddressFlag ,
} ,
& cli . StringFlag {
2021-06-16 20:09:51 +00:00
Name : "bearer-rules" ,
2022-10-25 09:30:18 +00:00
Usage : "rules for bearer token (filepath or a plain json string are allowed, can be used only with --disable-impersonate)" ,
2021-05-18 18:49:09 +00:00
Required : false ,
Destination : & eaclRulesFlag ,
} ,
2022-10-25 09:30:18 +00:00
& cli . BoolFlag {
Name : "disable-impersonate" ,
Usage : "mark token as not impersonate to don't consider token signer as request owner (must be provided to use --bearer-rules flag)" ,
Required : false ,
Destination : & disableImpersonateFlag ,
} ,
2021-05-18 18:49:09 +00:00
& cli . StringSliceFlag {
Name : "gate-public-key" ,
2021-06-16 14:07:31 +00:00
Usage : "public 256r1 key of a gate (use flags repeatedly for multiple gates)" ,
2021-05-18 18:49:09 +00:00
Required : true ,
Destination : & gatesPublicKeysFlag ,
} ,
& cli . StringFlag {
Name : "container-id" ,
Usage : "auth container id to put the secret into" ,
Required : false ,
Destination : & containerIDFlag ,
} ,
2023-06-13 09:35:40 +00:00
& cli . StringFlag {
Name : "access-key-id" ,
Usage : "access key id for s3 (use this flag to update existing creds, if this flag is provided '--container-id', '--container-friendly-name' and '--container-placement-policy' are ineffective)" ,
Required : false ,
Destination : & accessKeyIDFlag ,
} ,
2021-05-18 18:49:09 +00:00
& cli . StringFlag {
Name : "container-friendly-name" ,
Usage : "friendly name of auth container to put the secret into" ,
Required : false ,
Destination : & containerFriendlyName ,
} ,
2022-03-03 12:43:56 +00:00
& cli . StringFlag {
Name : "container-placement-policy" ,
Usage : "placement policy of auth container to put the secret into" ,
Required : false ,
Destination : & containerPlacementPolicy ,
Value : "REP 2 IN X CBF 3 SELECT 2 FROM * AS X" ,
} ,
2022-01-31 18:40:00 +00:00
& cli . StringFlag {
2022-04-14 15:09:57 +00:00
Name : "session-tokens" ,
Usage : "create session tokens with rules, if the rules are set as 'none', no session tokens will be created" ,
2021-06-16 20:09:51 +00:00
Required : false ,
Destination : & sessionTokenFlag ,
2022-01-31 18:40:00 +00:00
Value : "" ,
2021-06-16 20:09:51 +00:00
} ,
2021-10-26 12:03:51 +00:00
& cli . DurationFlag {
2021-11-19 08:01:03 +00:00
Name : "lifetime" ,
2023-03-24 12:49:23 +00:00
Usage : ` Lifetime of tokens . For example 50 h30m ( note : max time unit is an hour so to set a day you should use 24 h ) .
2021-11-19 08:01:03 +00:00
It will be ceil rounded to the nearest amount of epoch . ` ,
2021-06-28 13:20:11 +00:00
Required : false ,
Destination : & lifetimeFlag ,
Value : defaultLifetime ,
} ,
2021-07-16 12:35:07 +00:00
& cli . StringFlag {
Name : "container-policy" ,
2022-12-20 08:38:58 +00:00
Usage : "mapping AWS storage class to FrostFS storage policy as plain json string or path to json file" ,
2021-07-16 12:35:07 +00:00
Required : false ,
Destination : & containerPolicies ,
} ,
2021-09-01 11:30:15 +00:00
& cli . StringFlag {
Name : "aws-cli-credentials" ,
Usage : "path to the aws cli credential file" ,
Required : false ,
Destination : & awcCliCredFile ,
} ,
2022-12-08 10:20:09 +00:00
& cli . DurationFlag {
Name : "pool-dial-timeout" ,
Usage : ` Timeout for connection to the node in pool to be established ` ,
Required : false ,
Destination : & poolDialTimeoutFlag ,
Value : poolDialTimeout ,
} ,
& cli . DurationFlag {
Name : "pool-healthcheck-timeout" ,
Usage : ` Timeout for request to node to decide if it is alive ` ,
Required : false ,
Destination : & poolHealthcheckTimeoutFlag ,
Value : poolHealthcheckTimeout ,
} ,
& cli . DurationFlag {
Name : "pool-rebalance-interval" ,
Usage : ` Interval for updating nodes health status ` ,
Required : false ,
Destination : & poolRebalanceIntervalFlag ,
Value : poolRebalanceInterval ,
} ,
& cli . DurationFlag {
Name : "pool-stream-timeout" ,
Usage : ` Timeout for individual operation in streaming RPC ` ,
Required : false ,
Destination : & poolStreamTimeoutFlag ,
Value : poolStreamTimeout ,
} ,
2021-05-18 18:49:09 +00:00
} ,
Action : func ( c * cli . Context ) error {
ctx , log := prepare ( )
2021-06-24 15:21:34 +00:00
password := wallet . GetPassword ( viper . GetViper ( ) , envWalletPassphrase )
key , err := wallet . GetKeyFromPath ( walletPathFlag , accountAddressFlag , password )
2021-05-18 18:49:09 +00:00
if err != nil {
2022-12-20 08:38:58 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to load frostfs private key: %s" , err ) , 1 )
2021-05-18 18:49:09 +00:00
}
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
2022-12-08 10:20:09 +00:00
poolCfg := PoolConfig {
Key : & key . PrivateKey ,
Address : peerAddressFlag ,
DialTimeout : poolDialTimeoutFlag ,
HealthcheckTimeout : poolHealthcheckTimeoutFlag ,
StreamTimeout : poolStreamTimeoutFlag ,
RebalanceInterval : poolRebalanceIntervalFlag ,
}
frostFS , err := createFrostFS ( ctx , log , poolCfg )
2021-05-18 18:49:09 +00:00
if err != nil {
2022-12-20 08:38:58 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to create FrostFS component: %s" , err ) , 2 )
2021-05-18 18:49:09 +00:00
}
2022-12-20 08:38:58 +00:00
agent := authmate . New ( log , frostFS )
2022-06-27 09:08:26 +00:00
var containerID cid . ID
2021-05-18 18:49:09 +00:00
if len ( containerIDFlag ) > 0 {
2022-04-25 09:57:58 +00:00
if err = containerID . DecodeString ( containerIDFlag ) ; err != nil {
2021-05-18 18:49:09 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to parse auth container id: %s" , err ) , 3 )
}
}
2023-06-13 09:35:40 +00:00
var credsToUpdate * authmate . UpdateOptions
if len ( accessKeyIDFlag ) > 0 {
secretAccessKeyStr := wallet . GetPassword ( viper . GetViper ( ) , envSecretAccessKey )
if secretAccessKeyStr == nil {
return fmt . Errorf ( "you must provide AUTHMATE_SECRET_ACCESS_KEY env to update existing creds" )
}
secretAccessKey , err := hex . DecodeString ( * secretAccessKeyStr )
if err != nil {
return fmt . Errorf ( "access key must be hex encoded" )
}
var addr oid . Address
credAddr := strings . Replace ( accessKeyIDFlag , "0" , "/" , 1 )
if err = addr . DecodeString ( credAddr ) ; err != nil {
return fmt . Errorf ( "failed to parse creds address: %w" , err )
}
// we can create new creds version only in the same container
containerID = addr . Container ( )
credsToUpdate = & authmate . UpdateOptions {
Address : addr ,
SecretAccessKey : secretAccessKey ,
}
}
2021-06-24 15:21:34 +00:00
var gatesPublicKeys [ ] * keys . PublicKey
2021-05-18 18:49:09 +00:00
for _ , key := range gatesPublicKeysFlag . Value ( ) {
2021-06-24 15:21:34 +00:00
gpk , err := keys . NewPublicKeyFromString ( key )
2021-05-18 18:49:09 +00:00
if err != nil {
2021-06-28 13:20:11 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to load gate's public key: %s" , err ) , 4 )
2021-05-18 18:49:09 +00:00
}
gatesPublicKeys = append ( gatesPublicKeys , gpk )
}
2021-06-28 13:20:11 +00:00
if lifetimeFlag <= 0 {
2021-10-26 12:03:51 +00:00
return cli . Exit ( fmt . Sprintf ( "lifetime must be greater 0, current value: %d" , lifetimeFlag ) , 5 )
2021-06-28 13:20:11 +00:00
}
2021-07-16 12:35:07 +00:00
policies , err := parsePolicies ( containerPolicies )
if err != nil {
return cli . Exit ( fmt . Sprintf ( "couldn't parse container policy: %s" , err . Error ( ) ) , 6 )
}
2022-10-25 09:30:18 +00:00
if ! disableImpersonateFlag && eaclRulesFlag != "" {
return cli . Exit ( "--bearer-rules flag can be used only with --disable-impersonate" , 6 )
}
2022-03-11 09:59:52 +00:00
bearerRules , err := getJSONRules ( eaclRulesFlag )
if err != nil {
return cli . Exit ( fmt . Sprintf ( "couldn't parse 'bearer-rules' flag: %s" , err . Error ( ) ) , 7 )
}
2022-03-30 12:23:00 +00:00
sessionRules , skipSessionRules , err := getSessionRules ( sessionTokenFlag )
2022-03-11 09:59:52 +00:00
if err != nil {
2022-04-14 15:09:57 +00:00
return cli . Exit ( fmt . Sprintf ( "couldn't parse 'session-tokens' flag: %s" , err . Error ( ) ) , 8 )
2022-03-11 09:59:52 +00:00
}
2021-05-19 09:28:17 +00:00
issueSecretOptions := & authmate . IssueSecretOptions {
2022-03-03 12:43:56 +00:00
Container : authmate . ContainerOptions {
2022-04-28 14:21:56 +00:00
ID : containerID ,
2022-03-03 12:43:56 +00:00
FriendlyName : containerFriendlyName ,
PlacementPolicy : containerPlacementPolicy ,
} ,
2022-12-20 08:38:58 +00:00
FrostFSKey : key ,
2021-05-18 18:49:09 +00:00
GatesPublicKeys : gatesPublicKeys ,
2022-03-11 09:59:52 +00:00
EACLRules : bearerRules ,
2022-10-25 09:30:18 +00:00
Impersonate : ! disableImpersonateFlag ,
2022-03-11 09:59:52 +00:00
SessionTokenRules : sessionRules ,
2022-03-30 12:23:00 +00:00
SkipSessionRules : skipSessionRules ,
2021-07-16 12:35:07 +00:00
ContainerPolicies : policies ,
2021-06-28 13:20:11 +00:00
Lifetime : lifetimeFlag ,
2021-09-01 11:30:15 +00:00
AwsCliCredentialsFile : awcCliCredFile ,
2023-06-13 09:35:40 +00:00
UpdateCreds : credsToUpdate ,
2021-05-18 18:49:09 +00:00
}
2021-12-26 20:38:16 +00:00
var tcancel context . CancelFunc
ctx , tcancel = context . WithTimeout ( ctx , timeoutFlag )
defer tcancel ( )
2021-05-18 18:49:09 +00:00
if err = agent . IssueSecret ( ctx , os . Stdout , issueSecretOptions ) ; err != nil {
2021-12-26 20:38:16 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to issue secret: %s" , err ) , 7 )
2021-05-18 18:49:09 +00:00
}
return nil
} ,
}
}
2022-06-14 15:15:29 +00:00
func generatePresignedURL ( ) * cli . Command {
return & cli . Command {
Name : "generate-presigned-url" ,
Description : ` Generate presigned url using AWS credentials . Credentials must be placed in ~ / . aws / credentials .
You provide profile to load using -- profile flag or explicitly provide credentials and region using
-- aws - access - key - id , -- aws - secret - access - key , -- region .
Note to override credentials you must provide both access key and secret key . ` ,
2022-12-20 08:38:58 +00:00
Usage : "generate-presigned-url --endpoint http://s3.frostfs.devenv:8080 --bucket bucket-name --object object-name --method get --profile aws-profile" ,
2022-06-14 15:15:29 +00:00
Flags : [ ] cli . Flag {
& cli . DurationFlag {
Name : "lifetime" ,
2023-03-24 12:49:23 +00:00
Usage : ` Lifetime of presigned URL . For example 50 h30m ( note : max time unit is an hour so to set a day you should use 24 h ) .
2022-06-14 15:15:29 +00:00
It will be ceil rounded to the nearest amount of epoch . ` ,
Required : false ,
Destination : & lifetimeFlag ,
Value : defaultPresignedLifetime ,
} ,
& cli . StringFlag {
Name : "endpoint" ,
Usage : ` Endpoint of s3-gw ` ,
Required : true ,
Destination : & endpointFlag ,
} ,
& cli . StringFlag {
Name : "bucket" ,
Usage : ` Bucket name to perform action ` ,
Required : true ,
Destination : & bucketFlag ,
} ,
& cli . StringFlag {
Name : "object" ,
Usage : ` Object name to perform action ` ,
Required : true ,
Destination : & objectFlag ,
} ,
& cli . StringFlag {
Name : "method" ,
Usage : ` HTTP method to perform action ` ,
Required : true ,
Destination : & methodFlag ,
} ,
& cli . StringFlag {
Name : "profile" ,
Usage : ` AWS profile to load ` ,
Required : false ,
Destination : & profileFlag ,
} ,
& cli . StringFlag {
Name : "region" ,
Usage : ` AWS region to use in signature (default is taken from ~/.aws/config) ` ,
Required : false ,
Destination : & regionFlag ,
} ,
& cli . StringFlag {
Name : "aws-access-key-id" ,
Usage : ` AWS access key id to sign the URL (default is taken from ~/.aws/credentials) ` ,
Required : false ,
Destination : & accessKeyIDFlag ,
} ,
& cli . StringFlag {
Name : "aws-secret-access-key" ,
Usage : ` AWS access secret access key to sign the URL (default is taken from ~/.aws/credentials) ` ,
Required : false ,
Destination : & secretAccessKeyFlag ,
} ,
} ,
Action : func ( c * cli . Context ) error {
var cfg aws . Config
if regionFlag != "" {
cfg . Region = & regionFlag
}
if accessKeyIDFlag != "" && secretAccessKeyFlag != "" {
cfg . Credentials = credentials . NewStaticCredentialsFromCreds ( credentials . Value {
AccessKeyID : accessKeyIDFlag ,
SecretAccessKey : secretAccessKeyFlag ,
} )
}
sess , err := session . NewSessionWithOptions ( session . Options {
Config : cfg ,
Profile : profileFlag ,
SharedConfigState : session . SharedConfigEnable ,
} )
if err != nil {
return fmt . Errorf ( "couldn't get credentials: %w" , err )
}
2023-06-01 11:30:49 +00:00
reqData := auth . RequestData {
Method : methodFlag ,
Endpoint : endpointFlag ,
Bucket : bucketFlag ,
Object : objectFlag ,
}
presignData := auth . PresignData {
Service : "s3" ,
Region : * sess . Config . Region ,
Lifetime : lifetimeFlag ,
SignTime : time . Now ( ) . UTC ( ) ,
2022-06-14 15:15:29 +00:00
}
2023-06-01 11:30:49 +00:00
req , err := auth . PresignRequest ( sess . Config . Credentials , reqData , presignData )
if err != nil {
return err
2022-06-14 15:15:29 +00:00
}
res := & struct { URL string } {
URL : req . URL . String ( ) ,
}
enc := json . NewEncoder ( os . Stdout )
enc . SetIndent ( "" , " " )
enc . SetEscapeHTML ( false )
return enc . Encode ( res )
} ,
}
}
2021-07-16 12:35:07 +00:00
func parsePolicies ( val string ) ( authmate . ContainerPolicies , error ) {
if val == "" {
return nil , nil
}
2022-07-06 21:28:16 +00:00
var (
2021-07-16 12:35:07 +00:00
data = [ ] byte ( val )
2022-07-06 21:28:16 +00:00
err error
)
if ! json . Valid ( data ) {
if data , err = os . ReadFile ( val ) ; err != nil {
2022-07-07 15:16:59 +00:00
return nil , fmt . Errorf ( "coudln't read json file or provided json is invalid" )
2022-07-06 21:28:16 +00:00
}
2021-07-16 12:35:07 +00:00
}
var policies authmate . ContainerPolicies
if err = json . Unmarshal ( data , & policies ) ; err != nil {
2022-06-22 19:40:52 +00:00
return nil , fmt . Errorf ( "unmarshal policies: %w" , err )
2021-07-16 12:35:07 +00:00
}
2022-05-30 11:33:46 +00:00
if _ , ok := policies [ api . DefaultLocationConstraint ] ; ok {
return nil , fmt . Errorf ( "config overrides %s location constraint" , api . DefaultLocationConstraint )
2022-05-30 10:36:42 +00:00
}
2021-07-16 12:35:07 +00:00
return policies , nil
}
2022-03-11 09:59:52 +00:00
func getJSONRules ( val string ) ( [ ] byte , error ) {
if val == "" {
return nil , nil
}
data := [ ] byte ( val )
if json . Valid ( data ) {
return data , nil
}
2021-06-29 12:24:44 +00:00
if data , err := os . ReadFile ( val ) ; err == nil {
2022-03-11 09:59:52 +00:00
if json . Valid ( data ) {
return data , nil
}
2021-06-29 12:24:44 +00:00
}
2022-07-07 15:16:59 +00:00
return nil , fmt . Errorf ( "coudln't read json file or provided json is invalid" )
2021-06-29 12:24:44 +00:00
}
2022-03-30 12:23:00 +00:00
// getSessionRules reads json session rules.
2022-04-13 16:56:58 +00:00
// It returns true if rules must be skipped.
2022-03-30 12:23:00 +00:00
func getSessionRules ( r string ) ( [ ] byte , bool , error ) {
2022-01-31 18:40:00 +00:00
if r == "none" {
2022-03-30 12:23:00 +00:00
return nil , true , nil
2022-01-31 18:40:00 +00:00
}
2022-03-30 12:23:00 +00:00
data , err := getJSONRules ( r )
return data , false , err
2022-01-31 18:40:00 +00:00
}
2021-05-18 18:49:09 +00:00
func obtainSecret ( ) * cli . Command {
command := & cli . Command {
Name : "obtain-secret" ,
2022-12-20 08:38:58 +00:00
Usage : "Obtain a secret from FrostFS network" ,
2021-05-18 18:49:09 +00:00
Flags : [ ] cli . Flag {
& cli . StringFlag {
2021-06-24 15:21:34 +00:00
Name : "wallet" ,
2021-05-18 18:49:09 +00:00
Value : "" ,
2021-06-24 15:21:34 +00:00
Usage : "path to the wallet" ,
2021-05-18 18:49:09 +00:00
Required : true ,
2021-06-24 15:21:34 +00:00
Destination : & walletPathFlag ,
} ,
& cli . StringFlag {
Name : "address" ,
Value : "" ,
Usage : "address of wallet account" ,
Required : false ,
Destination : & accountAddressFlag ,
2021-05-18 18:49:09 +00:00
} ,
& cli . StringFlag {
Name : "peer" ,
Value : "" ,
2022-12-20 08:38:58 +00:00
Usage : "address of frostfs peer to connect to" ,
2021-05-18 18:49:09 +00:00
Required : true ,
Destination : & peerAddressFlag ,
} ,
& cli . StringFlag {
2021-06-24 15:21:34 +00:00
Name : "gate-wallet" ,
Value : "" ,
Usage : "path to the wallet" ,
2021-05-18 18:49:09 +00:00
Required : true ,
2021-06-24 15:21:34 +00:00
Destination : & gateWalletPathFlag ,
} ,
& cli . StringFlag {
Name : "gate-address" ,
Value : "" ,
Usage : "address of wallet account" ,
Required : false ,
Destination : & gateAccountAddressFlag ,
2021-05-18 18:49:09 +00:00
} ,
& cli . StringFlag {
2021-06-02 18:53:20 +00:00
Name : "access-key-id" ,
Usage : "access key id for s3" ,
2021-05-18 18:49:09 +00:00
Required : true ,
2021-06-02 18:53:20 +00:00
Destination : & accessKeyIDFlag ,
2021-05-18 18:49:09 +00:00
} ,
2022-12-08 10:20:09 +00:00
& cli . DurationFlag {
Name : "pool-dial-timeout" ,
Usage : ` Timeout for connection to the node in pool to be established ` ,
Required : false ,
Destination : & poolDialTimeoutFlag ,
Value : poolDialTimeout ,
} ,
& cli . DurationFlag {
Name : "pool-healthcheck-timeout" ,
Usage : ` Timeout for request to node to decide if it is alive ` ,
Required : false ,
Destination : & poolHealthcheckTimeoutFlag ,
Value : poolHealthcheckTimeout ,
} ,
& cli . DurationFlag {
Name : "pool-rebalance-interval" ,
Usage : ` Interval for updating nodes health status ` ,
Required : false ,
Destination : & poolRebalanceIntervalFlag ,
Value : poolRebalanceInterval ,
} ,
& cli . DurationFlag {
Name : "pool-stream-timeout" ,
Usage : ` Timeout for individual operation in streaming RPC ` ,
Required : false ,
Destination : & poolStreamTimeoutFlag ,
Value : poolStreamTimeout ,
} ,
2021-05-18 18:49:09 +00:00
} ,
Action : func ( c * cli . Context ) error {
ctx , log := prepare ( )
2021-06-24 15:21:34 +00:00
password := wallet . GetPassword ( viper . GetViper ( ) , envWalletPassphrase )
key , err := wallet . GetKeyFromPath ( walletPathFlag , accountAddressFlag , password )
2021-05-18 18:49:09 +00:00
if err != nil {
2022-12-20 08:38:58 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to load frostfs private key: %s" , err ) , 1 )
2021-05-18 18:49:09 +00:00
}
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
2022-12-08 10:20:09 +00:00
poolCfg := PoolConfig {
Key : & key . PrivateKey ,
Address : peerAddressFlag ,
DialTimeout : poolDialTimeoutFlag ,
HealthcheckTimeout : poolHealthcheckTimeoutFlag ,
StreamTimeout : poolStreamTimeoutFlag ,
RebalanceInterval : poolRebalanceIntervalFlag ,
}
frostFS , err := createFrostFS ( ctx , log , poolCfg )
2021-05-18 18:49:09 +00:00
if err != nil {
2022-12-20 08:38:58 +00:00
return cli . Exit ( fmt . Sprintf ( "failed to create FrostFS component: %s" , err ) , 2 )
2021-05-18 18:49:09 +00:00
}
2022-12-20 08:38:58 +00:00
agent := authmate . New ( log , frostFS )
2021-05-18 18:49:09 +00:00
var _ = agent
2021-06-24 15:21:34 +00:00
password = wallet . GetPassword ( viper . GetViper ( ) , envWalletGatePassphrase )
gateCreds , err := wallet . GetKeyFromPath ( gateWalletPathFlag , gateAccountAddressFlag , password )
2021-05-18 18:49:09 +00:00
if err != nil {
return cli . Exit ( fmt . Sprintf ( "failed to create owner's private key: %s" , err ) , 4 )
}
2021-12-26 20:53:57 +00:00
secretAddress := strings . Replace ( accessKeyIDFlag , "0" , "/" , 1 )
2021-06-02 18:53:20 +00:00
2021-05-19 09:28:17 +00:00
obtainSecretOptions := & authmate . ObtainSecretOptions {
2021-06-02 18:53:20 +00:00
SecretAddress : secretAddress ,
2021-06-16 14:07:31 +00:00
GatePrivateKey : gateCreds ,
2021-05-18 18:49:09 +00:00
}
2021-12-26 20:38:16 +00:00
var tcancel context . CancelFunc
ctx , tcancel = context . WithTimeout ( ctx , timeoutFlag )
defer tcancel ( )
2021-05-18 18:49:09 +00:00
if err = agent . ObtainSecret ( ctx , os . Stdout , obtainSecretOptions ) ; err != nil {
return cli . Exit ( fmt . Sprintf ( "failed to obtain secret: %s" , err ) , 5 )
}
return nil
} ,
}
return command
}
2022-12-08 10:20:09 +00:00
func createFrostFS ( ctx context . Context , log * zap . Logger , cfg PoolConfig ) ( authmate . FrostFS , error ) {
2021-05-18 18:49:09 +00:00
log . Debug ( "prepare connection pool" )
2022-04-07 14:36:44 +00:00
var prm pool . InitParameters
2022-12-08 10:20:09 +00:00
prm . SetKey ( cfg . Key )
prm . SetNodeDialTimeout ( cfg . DialTimeout )
prm . SetHealthcheckTimeout ( cfg . HealthcheckTimeout )
prm . SetNodeStreamTimeout ( cfg . StreamTimeout )
prm . SetClientRebalanceInterval ( cfg . RebalanceInterval )
prm . AddNode ( pool . NewNodeParam ( 1 , cfg . Address , 1 ) )
2021-05-26 16:48:27 +00:00
2022-04-07 14:36:44 +00:00
p , err := pool . NewPool ( prm )
2022-03-01 19:02:24 +00:00
if err != nil {
2022-06-22 19:40:52 +00:00
return nil , fmt . Errorf ( "create pool: %w" , err )
2022-03-01 19:02:24 +00:00
}
2022-04-07 14:36:44 +00:00
if err = p . Dial ( ctx ) ; err != nil {
2022-06-22 19:40:52 +00:00
return nil , fmt . Errorf ( "dial pool: %w" , err )
2022-04-07 14:36:44 +00:00
}
2022-12-20 08:38:58 +00:00
return frostfs . NewAuthmateFrostFS ( p ) , nil
2021-05-18 18:49:09 +00:00
}