github.com/decred/politeia@v1.4.0/politeiawww/politeiawww.go (about) 1 // Copyright (c) 2017-2022 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "crypto/elliptic" 9 "crypto/tls" 10 "database/sql" 11 "fmt" 12 "net/http" 13 "os" 14 "os/signal" 15 "syscall" 16 "time" 17 18 pdclient "github.com/decred/politeia/politeiad/client" 19 "github.com/decred/politeia/politeiawww/config" 20 "github.com/decred/politeia/politeiawww/legacy" 21 "github.com/decred/politeia/politeiawww/logger" 22 plugin "github.com/decred/politeia/politeiawww/plugin/v1" 23 "github.com/decred/politeia/politeiawww/user" 24 "github.com/decred/politeia/util" 25 "github.com/gorilla/mux" 26 "github.com/gorilla/sessions" 27 ) 28 29 // politeiawww represents the politeiawww server. 30 type politeiawww struct { 31 cfg *config.Config 32 router *mux.Router // Unprotected router 33 protected *mux.Router // CSRF protected subrouter 34 35 // Database layer. The sql DB is used as the backing database for the 36 // following interfaces. 37 db *sql.DB 38 sessions sessions.Store 39 userDB user.DB 40 41 // pluginIDs contains the plugin IDs of all registered plugins, ordered in 42 // the same order that they were provided to the config in. This is the order 43 // that the plugin hooks are executed in. 44 pluginIDs []string 45 46 // plugins contains all registered plugins. 47 plugins map[string]plugin.Plugin // [pluginID]plugin 48 49 // userManager handles user database insertions and deletions. The plugin 50 // that is set as the cfg.UserPlugin must implement the UserManager 51 // interface. This is the only plugin that is allowed to make user database 52 // insertions and deletions, e.g. the NewUser route. A cfg.UserPlugin must 53 // be specified if the user layer is enabled. 54 userManager plugin.UserManager 55 56 // authManager handles user authorization. The plugin that is set as the 57 // cfg.AuthPlugin must implement the Authorizer interface. A cfg.AuthPlugin 58 // must be specified if the user layer is enabled. 59 authManager plugin.AuthManager 60 61 // legacy contains the legacy politeiawww server. 62 legacy *legacy.Politeiawww 63 } 64 65 func _main() error { 66 // Load the configuration and parse the command line. This 67 // also initializes logging and configures it accordingly. 68 cfg, _, err := config.Load() 69 if err != nil { 70 return fmt.Errorf("Could not load configuration file: %v", err) 71 } 72 defer func() { 73 logger.CloseLogRotator() 74 }() 75 76 log.Infof("Version : %v", cfg.Version) 77 log.Infof("Network : %v", cfg.ActiveNet.Name) 78 log.Infof("Home dir: %v", cfg.HomeDir) 79 80 // Create the data directory in case it does not exist. 81 err = os.MkdirAll(cfg.DataDir, 0700) 82 if err != nil { 83 return err 84 } 85 86 // Check if this command is being run to fetch the politeiad 87 // identity. 88 if cfg.FetchIdentity { 89 return getIdentity(cfg.RPCHost, cfg.RPCCert, 90 cfg.RPCIdentityFile, cfg.Interactive) 91 } 92 93 // Generate the TLS cert and key file if both don't already exist. 94 if !util.FileExists(cfg.HTTPSKey) && 95 !util.FileExists(cfg.HTTPSCert) { 96 log.Infof("Generating HTTPS keypair...") 97 98 err := util.GenCertPair(elliptic.P256(), "politeiadwww", 99 cfg.HTTPSCert, cfg.HTTPSKey) 100 if err != nil { 101 return fmt.Errorf("unable to create https keypair: %v", 102 err) 103 } 104 105 log.Infof("HTTPS keypair created") 106 } 107 108 // Setup the politeiad client 109 pdc, err := pdclient.New(cfg.RPCHost, cfg.RPCCert, 110 cfg.RPCUser, cfg.RPCPass, cfg.Identity) 111 if err != nil { 112 return err 113 } 114 115 // Setup application context 116 p := &politeiawww{ 117 cfg: cfg, 118 router: nil, // Set in setupRouter() 119 protected: nil, // Set in setupRouter() 120 121 // Not implemented yet 122 db: nil, 123 sessions: nil, 124 userDB: nil, 125 126 // The plugin fields are setup by setupPlugins() 127 pluginIDs: cfg.Plugins, 128 plugins: nil, 129 userManager: nil, 130 authManager: nil, 131 132 // Legacy fields 133 legacy: nil, // Set below 134 } 135 136 // Setup the HTTP router 137 err = p.setupRouter() 138 if err != nil { 139 return err 140 } 141 142 // Setup the API routes. The legacy routes are 143 // used by default. If the legacy routes have been 144 // disabled then the plugin routes will be setup. 145 if cfg.DisableLegacy { 146 // Legacy routes have been disabled 147 p.setupPluginRoutes() 148 err = p.setupPlugins() 149 if err != nil { 150 return err 151 } 152 } else { 153 // Legacy routes are not disabled 154 legacywww, err := legacy.NewPoliteiawww(p.cfg, 155 p.router, p.protected, cfg.ActiveNet.Params, 156 pdc) 157 if err != nil { 158 return err 159 } 160 p.legacy = legacywww 161 } 162 163 // Bind to a port and pass our router in 164 listenC := make(chan error) 165 for _, listener := range cfg.Listeners { 166 listen := listener 167 go func() { 168 tlsConfig := &tls.Config{ 169 MinVersion: tls.VersionTLS12, 170 CurvePreferences: []tls.CurveID{ 171 tls.CurveP256, // BLAME CHROME, NOT ME! 172 tls.CurveP521, 173 tls.X25519}, 174 PreferServerCipherSuites: true, 175 CipherSuites: []uint16{ 176 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 177 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 178 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 179 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 180 }, 181 } 182 srv := &http.Server{ 183 Handler: p.router, 184 Addr: listen, 185 ReadTimeout: time.Duration(cfg.ReadTimeout) * time.Second, 186 WriteTimeout: time.Duration(cfg.WriteTimeout) * time.Second, 187 TLSConfig: tlsConfig, 188 TLSNextProto: make(map[string]func(*http.Server, 189 *tls.Conn, http.Handler)), 190 } 191 192 log.Infof("Listen: %v", listen) 193 listenC <- srv.ListenAndServeTLS(cfg.HTTPSCert, 194 cfg.HTTPSKey) 195 }() 196 } 197 198 // Tell user we are ready to go. 199 log.Infof("Start of day") 200 201 // Setup OS signals 202 sigs := make(chan os.Signal, 1) 203 signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 204 signal.Notify(sigs, syscall.SIGINT, syscall.SIGINT) 205 for { 206 select { 207 case sig := <-sigs: 208 log.Infof("Terminating with %v", sig) 209 goto done 210 case err := <-listenC: 211 log.Errorf("%v", err) 212 goto done 213 } 214 } 215 216 done: 217 log.Infof("Exiting") 218 219 if p.legacy != nil { 220 p.legacy.Close() 221 } 222 223 return nil 224 } 225 226 func main() { 227 err := _main() 228 if err != nil { 229 fmt.Fprintf(os.Stderr, "%v\n", err) 230 os.Exit(1) 231 } 232 }