From d140358d672494bd509705027ca51c9cbcec1349 Mon Sep 17 00:00:00 2001 From: Wisdom Arerosuoghene Date: Wed, 4 Sep 2019 19:10:47 +0100 Subject: [PATCH] remove maxticketchallengeage config option, use constant value --- config.go | 199 ++++++++++++++++++--------------------- sample-dcrstakepool.conf | 4 - server.go | 2 +- system/api-auth.go | 22 ++--- system/core.go | 9 +- 5 files changed, 110 insertions(+), 126 deletions(-) diff --git a/config.go b/config.go index 1c0ad321..5175f58a 100644 --- a/config.go +++ b/config.go @@ -26,30 +26,27 @@ import ( ) const ( - defaultBaseURL = "http://127.0.0.1:8000" - defaultClosePoolMsg = "The voting service is temporarily closed to new signups." - defaultConfigFilename = "dcrstakepool.conf" - defaultLogLevel = "info" - defaultLogDirname = "logs" - defaultLogFilename = "dcrstakepool.log" - defaultTicketChallengeMaxAge = 600 // 10 minutes - defaultCookieSecure = false - defaultDBHost = "localhost" - defaultDBName = "stakepool" - defaultDBPort = "3306" - defaultDBUser = "stakepool" - defaultListen = ":8000" - defaultPoolEmail = "admin@example.com" - defaultPoolFees = 7.5 - defaultPoolLink = "https://forum.decred.org/threads/rfp-6-setup-and-operate-10-stake-pools.1361/" - defaultPublicPath = "public" - defaultTemplatePath = "views" - defaultSMTPHost = "" - defaultMaxVotedTickets = 1000 - defaultDescription = "" - defaultDesignation = "" - - MaxTicketChallengeAge = 60 * 30 // 30 minutes + defaultBaseURL = "http://127.0.0.1:8000" + defaultClosePoolMsg = "The voting service is temporarily closed to new signups." + defaultConfigFilename = "dcrstakepool.conf" + defaultLogLevel = "info" + defaultLogDirname = "logs" + defaultLogFilename = "dcrstakepool.log" + defaultCookieSecure = false + defaultDBHost = "localhost" + defaultDBName = "stakepool" + defaultDBPort = "3306" + defaultDBUser = "stakepool" + defaultListen = ":8000" + defaultPoolEmail = "admin@example.com" + defaultPoolFees = 7.5 + defaultPoolLink = "https://forum.decred.org/threads/rfp-6-setup-and-operate-10-stake-pools.1361/" + defaultPublicPath = "public" + defaultTemplatePath = "views" + defaultSMTPHost = "" + defaultMaxVotedTickets = 1000 + defaultDescription = "" + defaultDesignation = "" ) var ( @@ -68,59 +65,58 @@ var runServiceCommand func(string) error // // See loadConfig for details on the configuration load process. type config struct { - ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` - ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` - DataDir string `short:"b" long:"datadir" description:"Deprecated. Unused, do not set."` - LogDir string `long:"logdir" description:"Directory to log output."` - Listen string `long:"listen" description:"Listen for connections on the specified interface/port (default all interfaces port: 9113, testnet: 19113)"` - TestNet bool `long:"testnet" description:"Use the test network"` - SimNet bool `long:"simnet" description:"Use the simulation test network"` - Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` - CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` - MemProfile string `long:"memprofile" description:"Write mem profile to the specified file"` - DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` - APISecret string `long:"apisecret" description:"Secret string used to encrypt API tokens."` - TicketChallengeMaxAge int64 `long:"ticketchallengemaxage" description:"Max age (in seconds) for API v3 ticket authentication timestamps. Max allowed value is 1800 (30 minutes)."` - BaseURL string `long:"baseurl" description:"BaseURL to use when sending links via email"` - ColdWalletExtPub string `long:"coldwalletextpub" description:"The extended public key for addresses to which voting service user fees are sent."` - ClosePool bool `long:"closepool" description:"Disable user registration actions (sign-ups and submitting addresses)"` - ClosePoolMsg string `long:"closepoolmsg" description:"Message to display when closepool is set."` - CookieSecret string `long:"cookiesecret" description:"Secret string used to encrypt session data."` - CookieSecure bool `long:"cookiesecure" description:"Set whether cookies can be sent in clear text or not."` - DBHost string `long:"dbhost" description:"Hostname for database connection"` - DBUser string `long:"dbuser" description:"Username for database connection"` - DBPassword string `long:"dbpassword" description:"Password for database connection"` - DBPort string `long:"dbport" description:"Port for database connection"` - DBName string `long:"dbname" description:"Name of database"` - PublicPath string `long:"publicpath" description:"Path to the public folder which contains css/fonts/images/javascript."` - TemplatePath string `long:"templatepath" description:"Path to the views folder which contains html files."` - PoolEmail string `long:"poolemail" description:"Email address to for support inquiries"` - PoolFees float64 `long:"poolfees" description:"The per-ticket fees the user must send to the pool with their tickets"` - PoolLink string `long:"poollink" description:"URL for support inquiries such as forum, IRC, etc"` - RealIPHeader string `long:"realipheader" description:"The name of an HTTP request header containing the actual remote client IP address, typically set by a reverse proxy. An empty string (default) indicates to use net/Request.RemodeAddr."` - SMTPFrom string `long:"smtpfrom" description:"From address to use on outbound mail"` - SMTPHost string `long:"smtphost" description:"SMTP hostname/ip and port, e.g. mail.example.com:25"` - SMTPUsername string `long:"smtpusername" description:"SMTP username for authentication if required"` - SMTPPassword string `long:"smtppassword" description:"SMTP password for authentication if required"` - UseSMTPS bool `long:"usesmtps" description:"Connect to the SMTP server using smtps."` - SMTPSkipVerify bool `long:"smtpskipverify" description:"Skip SMTP TLS cert verification. Will only skip if SMTPCert is empty"` - SMTPCert string `long:"smtpcert" description:"Path for the smtp certificate file"` - SystemCerts *x509.CertPool - StakepooldHosts []string `long:"stakepooldhosts" description:"Hostnames for stakepoold servers"` - StakepooldCerts []string `long:"stakepooldcerts" description:"Certificate paths for stakepoold servers"` - WalletHosts []string `long:"wallethosts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - WalletUsers []string `long:"walletusers" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - WalletPasswords []string `long:"walletpasswords" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - WalletCerts []string `long:"walletcerts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` - VotingWalletExtPub string `long:"votingwalletextpub" description:"The extended public key of the default account of the voting wallet"` - AdminIPs []string `long:"adminips" description:"Expected admin host"` - AdminUserIDs []string `long:"adminuserids" description:"User IDs of users who are allowed to access administrative functions."` - MinServers int `long:"minservers" description:"Deprecated: Do not use. Minimum of 2 servers are required when running on mainnet. Testnet and simnet require minimum 1."` - EnableStakepoold bool `long:"enablestakepoold" description:"Deprecated: Do not use. Stakepoold is required."` - MaxVotedAge int64 `long:"maxvotedage" description:"Deprecated: Use maxvotedtickets instead"` - MaxVotedTickets int `long:"maxvotedtickets" description:"Maximum number of voted tickets to show on tickets page."` - Description string `long:"description" description:"Operators own description of their VSP"` - Designation string `long:"designation" description:"VSP designation (eg. Alpha, Bravo, etc)"` + ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` + ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"` + DataDir string `short:"b" long:"datadir" description:"Deprecated. Unused, do not set."` + LogDir string `long:"logdir" description:"Directory to log output."` + Listen string `long:"listen" description:"Listen for connections on the specified interface/port (default all interfaces port: 9113, testnet: 19113)"` + TestNet bool `long:"testnet" description:"Use the test network"` + SimNet bool `long:"simnet" description:"Use the simulation test network"` + Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"` + CPUProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"` + MemProfile string `long:"memprofile" description:"Write mem profile to the specified file"` + DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify =,=,... to set the log level for individual subsystems -- Use show to list available subsystems"` + APISecret string `long:"apisecret" description:"Secret string used to encrypt API tokens."` + BaseURL string `long:"baseurl" description:"BaseURL to use when sending links via email"` + ColdWalletExtPub string `long:"coldwalletextpub" description:"The extended public key for addresses to which voting service user fees are sent."` + ClosePool bool `long:"closepool" description:"Disable user registration actions (sign-ups and submitting addresses)"` + ClosePoolMsg string `long:"closepoolmsg" description:"Message to display when closepool is set."` + CookieSecret string `long:"cookiesecret" description:"Secret string used to encrypt session data."` + CookieSecure bool `long:"cookiesecure" description:"Set whether cookies can be sent in clear text or not."` + DBHost string `long:"dbhost" description:"Hostname for database connection"` + DBUser string `long:"dbuser" description:"Username for database connection"` + DBPassword string `long:"dbpassword" description:"Password for database connection"` + DBPort string `long:"dbport" description:"Port for database connection"` + DBName string `long:"dbname" description:"Name of database"` + PublicPath string `long:"publicpath" description:"Path to the public folder which contains css/fonts/images/javascript."` + TemplatePath string `long:"templatepath" description:"Path to the views folder which contains html files."` + PoolEmail string `long:"poolemail" description:"Email address to for support inquiries"` + PoolFees float64 `long:"poolfees" description:"The per-ticket fees the user must send to the pool with their tickets"` + PoolLink string `long:"poollink" description:"URL for support inquiries such as forum, IRC, etc"` + RealIPHeader string `long:"realipheader" description:"The name of an HTTP request header containing the actual remote client IP address, typically set by a reverse proxy. An empty string (default) indicates to use net/Request.RemodeAddr."` + SMTPFrom string `long:"smtpfrom" description:"From address to use on outbound mail"` + SMTPHost string `long:"smtphost" description:"SMTP hostname/ip and port, e.g. mail.example.com:25"` + SMTPUsername string `long:"smtpusername" description:"SMTP username for authentication if required"` + SMTPPassword string `long:"smtppassword" description:"SMTP password for authentication if required"` + UseSMTPS bool `long:"usesmtps" description:"Connect to the SMTP server using smtps."` + SMTPSkipVerify bool `long:"smtpskipverify" description:"Skip SMTP TLS cert verification. Will only skip if SMTPCert is empty"` + SMTPCert string `long:"smtpcert" description:"Path for the smtp certificate file"` + SystemCerts *x509.CertPool + StakepooldHosts []string `long:"stakepooldhosts" description:"Hostnames for stakepoold servers"` + StakepooldCerts []string `long:"stakepooldcerts" description:"Certificate paths for stakepoold servers"` + WalletHosts []string `long:"wallethosts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + WalletUsers []string `long:"walletusers" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + WalletPasswords []string `long:"walletpasswords" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + WalletCerts []string `long:"walletcerts" description:"Deprecated: dcrstakepool no longer connects to dcrwallet"` + VotingWalletExtPub string `long:"votingwalletextpub" description:"The extended public key of the default account of the voting wallet"` + AdminIPs []string `long:"adminips" description:"Expected admin host"` + AdminUserIDs []string `long:"adminuserids" description:"User IDs of users who are allowed to access administrative functions."` + MinServers int `long:"minservers" description:"Deprecated: Do not use. Minimum of 2 servers are required when running on mainnet. Testnet and simnet require minimum 1."` + EnableStakepoold bool `long:"enablestakepoold" description:"Deprecated: Do not use. Stakepoold is required."` + MaxVotedAge int64 `long:"maxvotedage" description:"Deprecated: Use maxvotedtickets instead"` + MaxVotedTickets int `long:"maxvotedtickets" description:"Maximum number of voted tickets to show on tickets page."` + Description string `long:"description" description:"Operators own description of their VSP"` + Designation string `long:"designation" description:"VSP designation (eg. Alpha, Bravo, etc)"` } // serviceOptions defines the configuration options for the daemon as a service @@ -317,28 +313,27 @@ func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *fl func loadConfig() (*config, []string, error) { // Default config. cfg := config{ - BaseURL: defaultBaseURL, - ClosePool: false, - ClosePoolMsg: defaultClosePoolMsg, - ConfigFile: defaultConfigFile, - DebugLevel: defaultLogLevel, - LogDir: defaultLogDir, - TicketChallengeMaxAge: defaultTicketChallengeMaxAge, - CookieSecure: defaultCookieSecure, - DBHost: defaultDBHost, - DBName: defaultDBName, - DBPort: defaultDBPort, - DBUser: defaultDBUser, - Listen: defaultListen, - PoolEmail: defaultPoolEmail, - PoolFees: defaultPoolFees, - PoolLink: defaultPoolLink, - PublicPath: defaultPublicPath, - TemplatePath: defaultTemplatePath, - SMTPHost: defaultSMTPHost, - MaxVotedTickets: defaultMaxVotedTickets, - Description: defaultDescription, - Designation: defaultDesignation, + BaseURL: defaultBaseURL, + ClosePool: false, + ClosePoolMsg: defaultClosePoolMsg, + ConfigFile: defaultConfigFile, + DebugLevel: defaultLogLevel, + LogDir: defaultLogDir, + CookieSecure: defaultCookieSecure, + DBHost: defaultDBHost, + DBName: defaultDBName, + DBPort: defaultDBPort, + DBUser: defaultDBUser, + Listen: defaultListen, + PoolEmail: defaultPoolEmail, + PoolFees: defaultPoolFees, + PoolLink: defaultPoolLink, + PublicPath: defaultPublicPath, + TemplatePath: defaultTemplatePath, + SMTPHost: defaultSMTPHost, + MaxVotedTickets: defaultMaxVotedTickets, + Description: defaultDescription, + Designation: defaultDesignation, } // Service options which are only added on Windows. @@ -475,12 +470,6 @@ func loadConfig() (*config, []string, error) { return nil, nil, err } - // Ensure ticket challenge max age is not greater than permitted maximum. - if cfg.TicketChallengeMaxAge > MaxTicketChallengeAge { - return nil, nil, fmt.Errorf("%s: Ticket challenge max age cannot be higher than %v", - funcName, MaxTicketChallengeAge) - } - // Validate profile port number if cfg.Profile != "" { profilePort, err := strconv.Atoi(cfg.Profile) diff --git a/sample-dcrstakepool.conf b/sample-dcrstakepool.conf index e8e380c8..bda3a279 100644 --- a/sample-dcrstakepool.conf +++ b/sample-dcrstakepool.conf @@ -11,10 +11,6 @@ adminips=127.0.0.1 ; Multiple values can be used and are separated by a comma. ;adminuserids=1,2,3 -; Max age (in seconds) for per-ticket api authentication timestamps. -; Max allowed value is 1800 (30 minutes). -; ticketchallengemaxage=600 - ; Secret string used to encrypt API and to generate CSRF tokens. ; Can use openssl rand -hex 32 to generate one. ;apisecret= diff --git a/server.go b/server.go index 5bbbe266..01f9e2b4 100644 --- a/server.go +++ b/server.go @@ -63,7 +63,7 @@ func runMain() error { var application = &system.Application{} - application.Init(cfg.APISecret, cfg.TicketChallengeMaxAge, cfg.BaseURL, cfg.CookieSecret, + application.Init(cfg.APISecret, cfg.BaseURL, cfg.CookieSecret, cfg.CookieSecure, cfg.DBHost, cfg.DBName, cfg.DBPassword, cfg.DBPort, cfg.DBUser) if application.DbMap == nil { diff --git a/system/api-auth.go b/system/api-auth.go index b440b944..c7b96509 100644 --- a/system/api-auth.go +++ b/system/api-auth.go @@ -3,6 +3,7 @@ package system import ( "encoding/base64" "fmt" + "math" "strconv" "strings" "time" @@ -12,6 +13,10 @@ import ( "github.com/dgrijalva/jwt-go" ) +// maxTicketChallengeAge is the maximum number of seconds into the past +// or future required for a ticket auth timestamp value to be considered valid. +const maxTicketChallengeAge = 60 + func (application *Application) validateToken(authHeader string) (int64, string) { apitoken := strings.TrimPrefix(authHeader, "Bearer ") @@ -51,16 +56,12 @@ func (application *Application) validateTicketOwnership(authHeader string) (mult return } - // Ensure that the auth timestamp - // - is not more than 5 minutes into the future - // - is not more than 30 seconds into the past + // Ensure that the auth timestamp is not more than + // the permitted number of seconds into the past and future. timestampDelta := time.Now().Unix() - int64(authTimestamp) - if timestampDelta < -300 { - // more than 5 minutes into the future - authValidationFailureReason = "invalid (future) timestamp" - return - } else if timestampDelta > application.TicketChallengeMaxAge { - authValidationFailureReason = fmt.Sprintf("expired ticket auth timestamp value %v", timestamp) + if math.Abs(float64(timestampDelta)) > maxTicketChallengeAge { + authValidationFailureReason = fmt.Sprintf("expired or invalid ticket auth timestamp value %v", + timestamp) return } @@ -72,8 +73,7 @@ func (application *Application) validateTicketOwnership(authHeader string) (mult } // Mark this timestamp signature as used to prevent subsequent reuse. - challengeExpiresIn := application.TicketChallengeMaxAge - timestampDelta - application.ProcessedTicketChallenges.AddChallenge(timestampSignature, challengeExpiresIn) + application.ProcessedTicketChallenges.AddChallenge(timestampSignature, maxTicketChallengeAge) // todo check if ticket belongs to this vsp diff --git a/system/core.go b/system/core.go index a18da209..f6a3828e 100644 --- a/system/core.go +++ b/system/core.go @@ -23,7 +23,6 @@ import ( type Application struct { APISecret string - TicketChallengeMaxAge int64 ProcessedTicketChallenges *ticketChallengesCache Template *template.Template TemplatesPath string @@ -40,9 +39,10 @@ func GojiWebHandlerFunc(h http.HandlerFunc) web.HandlerFunc { } } -func (application *Application) Init(APISecret string, ticketChallengeMaxAge int64, - baseURL string, cookieSecret string, cookieSecure bool, - DBHost string, DBName string, DBPassword string, DBPort string, DBUser string) { +func (application *Application) Init(APISecret string, baseURL string, + cookieSecret string, cookieSecure bool, DBHost string, DBName string, + DBPassword string, + DBPort string, DBUser string) { application.DbMap = models.GetDbMap( APISecret, @@ -65,7 +65,6 @@ func (application *Application) Init(APISecret string, ticketChallengeMaxAge int } application.APISecret = APISecret - application.TicketChallengeMaxAge = ticketChallengeMaxAge application.ProcessedTicketChallenges = newTicketChallengesCache() }