2018-03-09 20:44:13 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
2020-07-14 00:46:05 +00:00
|
|
|
"crypto/ed25519"
|
|
|
|
"cwtch.im/cwtch/model"
|
2018-06-27 15:14:59 +00:00
|
|
|
"cwtch.im/cwtch/server/metrics"
|
2019-01-21 20:11:40 +00:00
|
|
|
"cwtch.im/cwtch/server/storage"
|
2020-11-02 23:53:13 +00:00
|
|
|
"fmt"
|
2021-04-09 01:22:08 +00:00
|
|
|
"git.openprivacy.ca/cwtch.im/tapir"
|
|
|
|
"git.openprivacy.ca/cwtch.im/tapir/applications"
|
|
|
|
tor2 "git.openprivacy.ca/cwtch.im/tapir/networks/tor"
|
|
|
|
"git.openprivacy.ca/cwtch.im/tapir/persistence"
|
|
|
|
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
|
|
|
"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
|
2020-02-10 22:09:24 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/connectivity"
|
|
|
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
|
|
|
"git.openprivacy.ca/openprivacy/log"
|
2020-11-02 23:53:13 +00:00
|
|
|
"path"
|
2020-11-05 21:26:03 +00:00
|
|
|
"sync"
|
2018-03-09 20:44:13 +00:00
|
|
|
)
|
|
|
|
|
2018-05-16 21:11:04 +00:00
|
|
|
// Server encapsulates a complete, compliant Cwtch server.
|
2018-03-09 20:44:13 +00:00
|
|
|
type Server struct {
|
2020-11-02 23:53:13 +00:00
|
|
|
service tapir.Service
|
|
|
|
config Config
|
|
|
|
metricsPack metrics.Monitors
|
|
|
|
tokenTapirService tapir.Service
|
|
|
|
tokenServer *privacypass.TokenServer
|
|
|
|
tokenService primitives.Identity
|
|
|
|
tokenServicePrivKey ed25519.PrivateKey
|
|
|
|
tokenServiceStopped bool
|
|
|
|
onionServiceStopped bool
|
|
|
|
running bool
|
|
|
|
existingMessageCount int
|
2020-12-17 01:40:03 +00:00
|
|
|
lock sync.RWMutex
|
2020-07-14 00:46:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup initialized a server from a given configuration
|
|
|
|
func (s *Server) Setup(serverConfig Config) {
|
|
|
|
s.config = serverConfig
|
2020-09-21 21:26:28 +00:00
|
|
|
bs := new(persistence.BoltPersistence)
|
2021-05-05 19:46:35 +00:00
|
|
|
bs.Open(path.Join(serverConfig.ConfigDir, "tokens.db"))
|
|
|
|
s.tokenServer = privacypass.NewTokenServerFromStore(&serverConfig.TokenServiceK, bs)
|
|
|
|
log.Infof("Y: %v", s.tokenServer.Y)
|
2020-09-21 21:26:28 +00:00
|
|
|
s.tokenService = s.config.TokenServiceIdentity()
|
|
|
|
s.tokenServicePrivKey = s.config.TokenServerPrivateKey
|
2018-03-09 20:44:13 +00:00
|
|
|
}
|
|
|
|
|
2020-11-02 23:53:13 +00:00
|
|
|
// Identity returns the main onion identity of the server
|
|
|
|
func (s *Server) Identity() primitives.Identity {
|
|
|
|
return s.config.Identity()
|
|
|
|
}
|
2018-03-09 20:44:13 +00:00
|
|
|
|
2020-11-02 23:53:13 +00:00
|
|
|
// Run starts a server with the given privateKey
|
|
|
|
func (s *Server) Run(acn connectivity.ACN) error {
|
2020-07-14 00:46:05 +00:00
|
|
|
addressIdentity := tor.GetTorV3Hostname(s.config.PublicKey)
|
|
|
|
identity := primitives.InitializeIdentity("", &s.config.PrivateKey, &s.config.PublicKey)
|
|
|
|
var service tapir.Service
|
|
|
|
service = new(tor2.BaseOnionService)
|
|
|
|
service.Init(acn, s.config.PrivateKey, &identity)
|
|
|
|
s.service = service
|
|
|
|
log.Infof("cwtch server running on cwtch:%s\n", addressIdentity+".onion:")
|
|
|
|
s.metricsPack.Start(service, s.config.ConfigDir, s.config.ServerReporting.LogMetricsToFile)
|
|
|
|
|
2018-03-09 20:44:13 +00:00
|
|
|
ms := new(storage.MessageStore)
|
2020-07-14 00:46:05 +00:00
|
|
|
err := ms.Init(s.config.ConfigDir, s.config.MaxBufferLines, s.metricsPack.MessageCounter)
|
2018-09-20 21:01:46 +00:00
|
|
|
if err != nil {
|
2020-11-02 23:53:13 +00:00
|
|
|
return err
|
2018-09-20 21:01:46 +00:00
|
|
|
}
|
2018-03-09 20:44:13 +00:00
|
|
|
|
2020-11-02 23:53:13 +00:00
|
|
|
// Needed because we only collect metrics on a per-session basis
|
|
|
|
// TODO fix metrics so they persist across sessions?
|
|
|
|
s.existingMessageCount = len(ms.FetchMessages())
|
|
|
|
|
|
|
|
s.tokenTapirService = new(tor2.BaseOnionService)
|
|
|
|
s.tokenTapirService.Init(acn, s.tokenServicePrivKey, &s.tokenService)
|
|
|
|
tokenApplication := new(applications.TokenApplication)
|
|
|
|
tokenApplication.TokenService = s.tokenServer
|
|
|
|
powTokenApp := new(applications.ApplicationChain).
|
|
|
|
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
|
|
|
|
ChainApplication(tokenApplication, applications.HasTokensCapability)
|
2020-07-14 00:46:05 +00:00
|
|
|
go func() {
|
|
|
|
s.tokenTapirService.Listen(powTokenApp)
|
2020-11-02 23:53:13 +00:00
|
|
|
s.tokenServiceStopped = true
|
2020-07-14 00:46:05 +00:00
|
|
|
}()
|
2020-11-02 23:53:13 +00:00
|
|
|
go func() {
|
2020-07-14 00:46:05 +00:00
|
|
|
s.service.Listen(NewTokenBoardServer(ms, s.tokenServer))
|
2020-11-02 23:53:13 +00:00
|
|
|
s.onionServiceStopped = true
|
|
|
|
}()
|
2020-11-05 21:26:03 +00:00
|
|
|
|
|
|
|
s.lock.Lock()
|
2020-11-02 23:53:13 +00:00
|
|
|
s.running = true
|
2020-11-05 21:26:03 +00:00
|
|
|
s.lock.Unlock()
|
2020-11-02 23:53:13 +00:00
|
|
|
return nil
|
2018-03-09 20:44:13 +00:00
|
|
|
}
|
2018-05-30 18:42:17 +00:00
|
|
|
|
2020-10-01 17:13:45 +00:00
|
|
|
// KeyBundle provides the signed keybundle of the server
|
|
|
|
func (s *Server) KeyBundle() *model.KeyBundle {
|
|
|
|
kb := model.NewKeyBundle()
|
2020-07-14 00:46:05 +00:00
|
|
|
identity := s.config.Identity()
|
2020-07-22 17:14:32 +00:00
|
|
|
kb.Keys[model.KeyTypeServerOnion] = model.Key(identity.Hostname())
|
2020-10-01 17:13:45 +00:00
|
|
|
kb.Keys[model.KeyTypeTokenOnion] = model.Key(s.tokenService.Hostname())
|
2020-07-14 00:46:05 +00:00
|
|
|
kb.Keys[model.KeyTypePrivacyPass] = model.Key(s.tokenServer.Y.String())
|
2020-10-01 17:13:45 +00:00
|
|
|
kb.Sign(identity)
|
2020-07-14 00:46:05 +00:00
|
|
|
return kb
|
|
|
|
}
|
|
|
|
|
2020-11-02 23:53:13 +00:00
|
|
|
// CheckStatus returns true if the server is running and/or an error if any part of the server needs to be restarted.
|
|
|
|
func (s *Server) CheckStatus() (bool, error) {
|
2020-11-05 21:26:03 +00:00
|
|
|
s.lock.RLock()
|
|
|
|
defer s.lock.RUnlock()
|
2020-11-02 23:53:13 +00:00
|
|
|
if s.onionServiceStopped == true || s.tokenServiceStopped == true {
|
|
|
|
return s.running, fmt.Errorf("one of more server components are down: onion:%v token service: %v", s.onionServiceStopped, s.tokenServiceStopped)
|
|
|
|
}
|
|
|
|
return s.running, nil
|
|
|
|
}
|
|
|
|
|
2018-06-15 16:21:07 +00:00
|
|
|
// Shutdown kills the app closing all connections and freeing all goroutines
|
2018-05-30 18:42:17 +00:00
|
|
|
func (s *Server) Shutdown() {
|
2020-11-05 21:26:03 +00:00
|
|
|
s.lock.Lock()
|
|
|
|
defer s.lock.Unlock()
|
2020-07-14 00:46:05 +00:00
|
|
|
s.service.Shutdown()
|
|
|
|
s.tokenTapirService.Shutdown()
|
2018-06-27 15:14:59 +00:00
|
|
|
s.metricsPack.Stop()
|
2020-11-05 21:26:03 +00:00
|
|
|
s.running = true
|
|
|
|
|
2020-11-02 23:53:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Statistics is an encapsulation of information about the server that an operator might want to know at a glance.
|
|
|
|
type Statistics struct {
|
|
|
|
TotalMessages int
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetStatistics is a stub method for providing some high level information about
|
|
|
|
// the server operation to bundling applications (e.g. the UI)
|
|
|
|
func (s *Server) GetStatistics() Statistics {
|
|
|
|
// TODO Statistics from Metrics is very awkward. Metrics needs an overhaul to make safe
|
|
|
|
total := s.existingMessageCount
|
|
|
|
if s.metricsPack.TotalMessageCounter != nil {
|
|
|
|
total += s.metricsPack.TotalMessageCounter.Count()
|
|
|
|
}
|
|
|
|
|
|
|
|
return Statistics{
|
|
|
|
TotalMessages: total,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConfigureAutostart sets whether this server should autostart (in the Cwtch UI/bundling application)
|
|
|
|
func (s *Server) ConfigureAutostart(autostart bool) {
|
|
|
|
s.config.AutoStart = autostart
|
|
|
|
s.config.Save(s.config.ConfigDir, s.config.FilePath)
|
2018-05-30 18:42:17 +00:00
|
|
|
}
|
2021-05-05 19:46:35 +00:00
|
|
|
|
2021-05-05 19:50:19 +00:00
|
|
|
// Close shuts down the cwtch server in a safe way.
|
2021-05-05 19:46:35 +00:00
|
|
|
func (s *Server) Close() {
|
|
|
|
log.Infof("Shutting down server")
|
|
|
|
s.lock.Lock()
|
|
|
|
defer s.lock.Unlock()
|
|
|
|
log.Infof("Closing Token Server Database...")
|
|
|
|
s.tokenServer.Close()
|
|
|
|
}
|