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  }