2020-07-06 09:18:16 +00:00
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
2020-07-15 20:16:27 +00:00
"crypto/rsa"
2020-07-06 09:18:16 +00:00
"fmt"
"io"
"os"
"strconv"
"strings"
"time"
2020-07-15 20:16:27 +00:00
s3auth "github.com/minio/minio/auth"
2020-07-07 11:25:13 +00:00
"github.com/minio/minio/neofs/pool"
2020-07-15 20:16:27 +00:00
"github.com/pkg/errors"
2020-07-07 11:25:13 +00:00
2020-07-06 09:18:16 +00:00
"github.com/minio/minio/misc"
crypto "github.com/nspcc-dev/neofs-crypto"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"
)
const (
devNull = empty ( 0 )
generated = "generated"
minimumTTLInMinutes = 5
defaultTTL = minimumTTLInMinutes * time . Minute
2020-07-12 23:00:47 +00:00
defaultRebalanceTimer = 15 * time . Second
defaultRequestTimeout = 15 * time . Second
defaultConnectTimeout = 30 * time . Second
defaultShutdownTimeout = 15 * time . Second
2020-07-06 09:18:16 +00:00
defaultKeepaliveTime = 10 * time . Second
defaultKeepaliveTimeout = 10 * time . Second
)
2020-07-07 11:25:13 +00:00
const ( // settings
// Logger:
cfgLoggerLevel = "logger.level"
cfgLoggerFormat = "logger.format"
cfgLoggerTraceLevel = "logger.trace_level"
cfgLoggerNoDisclaimer = "logger.no_disclaimer"
cfgLoggerSamplingInitial = "logger.sampling.initial"
cfgLoggerSamplingThereafter = "logger.sampling.thereafter"
// KeepAlive
cfgKeepaliveTime = "keepalive.time"
cfgKeepaliveTimeout = "keepalive.timeout"
cfgKeepalivePermitWithoutStream = "keepalive.permit_without_stream"
2020-07-15 20:16:27 +00:00
// Keys
cfgNeoFSPrivateKey = "neofs-ecdsa-key"
cfgUserAuthPrivateKey = "userauth-rsa-key"
// HTTPS/TLS
2020-07-13 15:50:11 +00:00
cfgTLSKeyFile = "tls.key_file"
cfgTLSCertFile = "tls.cert_file"
2020-07-07 11:25:13 +00:00
// Timeouts
cfgConnectionTTL = "con_ttl"
cfgConnectTimeout = "connect_timeout"
cfgRequestTimeout = "request_timeout"
cfgRebalanceTimer = "rebalance_timer"
// gRPC
2020-07-15 20:16:27 +00:00
cfgGRPCVerbose = "verbose"
2020-07-07 11:25:13 +00:00
// Metrics / Profiler / Web
cfgEnableMetrics = "metrics"
cfgEnableProfiler = "pprof"
cfgListenAddress = "listen_address"
// Application
cfgApplicationName = "app.name"
cfgApplicationVersion = "app.version"
cfgApplicationBuildTime = "app.build_time"
)
2020-07-15 20:16:27 +00:00
type empty int
2020-07-06 09:18:16 +00:00
func ( empty ) Read ( [ ] byte ) ( int , error ) { return 0 , io . EOF }
2020-07-15 20:16:27 +00:00
func fetchAuthCenter ( l * zap . Logger , v * viper . Viper ) ( * s3auth . Center , error ) {
var (
err error
neofsPrivateKey * ecdsa . PrivateKey
userAuthPrivateKey * rsa . PrivateKey
)
switch nfspk := v . GetString ( cfgNeoFSPrivateKey ) ; nfspk {
2020-07-06 09:18:16 +00:00
case generated :
2020-07-15 20:16:27 +00:00
neofsPrivateKey , err = ecdsa . GenerateKey ( elliptic . P256 ( ) , rand . Reader )
2020-07-06 09:18:16 +00:00
if err != nil {
2020-07-15 20:16:27 +00:00
return nil , errors . Wrap ( err , "could not generate NeoFS private key" )
2020-07-06 09:18:16 +00:00
}
default :
2020-07-15 20:16:27 +00:00
neofsPrivateKey , err = crypto . LoadPrivateKey ( nfspk )
2020-07-06 09:18:16 +00:00
if err != nil {
2020-07-15 20:16:27 +00:00
return nil , errors . Wrap ( err , "could not load NeoFS private key" )
2020-07-06 09:18:16 +00:00
}
}
2020-07-15 20:16:27 +00:00
uapk := v . GetString ( cfgUserAuthPrivateKey )
userAuthPrivateKey , err = s3auth . ReadRSAPrivateKeyFromPEMFile ( uapk )
if err != nil {
return nil , errors . Wrap ( err , "could not load UserAuth private key" )
}
2020-07-20 17:23:16 +00:00
center := s3auth . NewCenter ( l )
2020-07-15 20:16:27 +00:00
center . SetUserAuthKeys ( userAuthPrivateKey )
center . SetNeoFSKeys ( neofsPrivateKey )
return center , nil
2020-07-06 09:18:16 +00:00
}
2020-07-07 11:25:13 +00:00
func fetchPeers ( l * zap . Logger , v * viper . Viper ) [ ] pool . Peer {
peers := make ( [ ] pool . Peer , 0 )
for i := 0 ; ; i ++ {
key := "peers." + strconv . Itoa ( i ) + "."
address := v . GetString ( key + "address" )
weight := v . GetFloat64 ( key + "weight" )
if address == "" {
l . Warn ( "skip, empty address" )
break
}
peers = append ( peers , pool . Peer {
Address : address ,
Weight : weight ,
} )
}
return peers
}
2020-07-06 09:18:16 +00:00
func newSettings ( ) * viper . Viper {
v := viper . New ( )
v . AutomaticEnv ( )
v . SetEnvPrefix ( "S3" )
v . SetConfigType ( "yaml" )
v . SetEnvKeyReplacer ( strings . NewReplacer ( "." , "_" ) )
// flags setup:
flags := pflag . NewFlagSet ( "commandline" , pflag . ExitOnError )
flags . SortFlags = false
2020-07-07 11:25:13 +00:00
flags . Bool ( cfgEnableProfiler , false , "enable pprof" )
2020-07-15 20:16:27 +00:00
flags . Bool ( cfgEnableMetrics , false , "enable prometheus metrics" )
2020-07-06 09:18:16 +00:00
help := flags . BoolP ( "help" , "h" , false , "show help" )
version := flags . BoolP ( "version" , "v" , false , "show version" )
2020-07-15 20:16:27 +00:00
flags . String ( cfgNeoFSPrivateKey , generated , fmt . Sprintf ( ` set value to hex string, WIF string, or path to NeoFS private key file (use "%s" to generate key) ` , generated ) )
flags . String ( cfgUserAuthPrivateKey , "" , "set path to file with private key to use in auth scheme" )
2020-07-06 09:18:16 +00:00
2020-07-15 20:16:27 +00:00
flags . Bool ( cfgGRPCVerbose , false , "set debug mode of gRPC connections" )
flags . Duration ( cfgRequestTimeout , defaultRequestTimeout , "set gRPC request timeout" )
flags . Duration ( cfgConnectTimeout , defaultConnectTimeout , "set gRPC connect timeout" )
flags . Duration ( cfgRebalanceTimer , defaultRebalanceTimer , "set gRPC connection rebalance timer" )
2020-07-06 09:18:16 +00:00
2020-07-15 20:16:27 +00:00
ttl := flags . DurationP ( cfgConnectionTTL , "t" , defaultTTL , "set gRPC connection time to live" )
2020-07-06 09:18:16 +00:00
2020-07-15 20:16:27 +00:00
flags . String ( cfgListenAddress , "0.0.0.0:8080" , "set address to listen" )
peers := flags . StringArrayP ( "peers" , "p" , nil , "set NeoFS nodes" )
2020-07-06 09:18:16 +00:00
// set prefers:
2020-07-07 11:28:49 +00:00
v . Set ( cfgApplicationName , misc . ApplicationName )
2020-07-07 11:25:13 +00:00
v . Set ( cfgApplicationVersion , misc . Version )
v . Set ( cfgApplicationBuildTime , misc . Build )
2020-07-06 09:18:16 +00:00
// set defaults:
// logger:
2020-07-07 11:25:13 +00:00
v . SetDefault ( cfgLoggerLevel , "debug" )
v . SetDefault ( cfgLoggerFormat , "console" )
v . SetDefault ( cfgLoggerTraceLevel , "fatal" )
v . SetDefault ( cfgLoggerNoDisclaimer , true )
v . SetDefault ( cfgLoggerSamplingInitial , 1000 )
v . SetDefault ( cfgLoggerSamplingThereafter , 1000 )
2020-07-06 09:18:16 +00:00
// keepalive:
// If set below 10s, a minimum value of 10s will be used instead.
2020-07-07 11:25:13 +00:00
v . SetDefault ( cfgKeepaliveTime , defaultKeepaliveTime )
v . SetDefault ( cfgKeepaliveTimeout , defaultKeepaliveTimeout )
v . SetDefault ( cfgKeepalivePermitWithoutStream , true )
2020-07-06 09:18:16 +00:00
if err := v . BindPFlags ( flags ) ; err != nil {
panic ( err )
}
if err := v . ReadConfig ( devNull ) ; err != nil {
panic ( err )
}
if err := flags . Parse ( os . Args ) ; err != nil {
panic ( err )
}
switch {
case help != nil && * help :
fmt . Printf ( "NeoFS S3 Gateway %s (%s)\n" , misc . Version , misc . Build )
flags . PrintDefaults ( )
os . Exit ( 0 )
case version != nil && * version :
fmt . Printf ( "NeoFS S3 Gateway %s (%s)\n" , misc . Version , misc . Build )
os . Exit ( 0 )
case ttl != nil && ttl . Minutes ( ) < minimumTTLInMinutes :
fmt . Printf ( "connection ttl should not be less than %s" , defaultTTL )
}
if peers != nil && len ( * peers ) > 0 {
for i := range * peers {
v . SetDefault ( "peers." + strconv . Itoa ( i ) + ".address" , ( * peers ) [ i ] )
v . SetDefault ( "peers." + strconv . Itoa ( i ) + ".weight" , 1 )
}
}
return v
}