mirror of
https://github.com/Egida/EndGame0.git
synced 2025-08-15 17:40:33 -04:00
EndGame v3
This commit is contained in:
commit
9e36ba54ee
646 changed files with 271674 additions and 0 deletions
349
sourcecode/gobalance/pkg/onionbalance/manager.go
Normal file
349
sourcecode/gobalance/pkg/onionbalance/manager.go
Normal file
|
@ -0,0 +1,349 @@
|
|||
package onionbalance
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"math/rand"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func loadDefaults() {
|
||||
p := Params()
|
||||
// InitialCallbackDelay How long to wait for onionbalance to bootstrap before starting periodic
|
||||
// events (in nanoseconds). 10 base plus 2 seconds here for every single front you have. (10 instances = 10 + (10X2) = 30) times 1 billion.
|
||||
p.SetInitialCallbackDelay(75 * 1000000000) //time is in nanoseconds
|
||||
// FetchDescriptorFrequency Every how often we should be fetching instance descriptors (in seconds)
|
||||
p.SetFetchDescriptorFrequency(20 * 1000000000)
|
||||
// PublishDescriptorCheckFrequency Every how often we should be checking whether we should publish our frontend
|
||||
// descriptor (in nanoseconds). Triggering this callback doesn't mean we will actually upload a descriptor.
|
||||
// We only upload a descriptor if it has expired, the intro points have changed, etc. Default
|
||||
p.SetPublishDescriptorCheckFrequency(30 * 1000000000)
|
||||
// FrontendDescriptorLifetime How long should we keep a frontend descriptor before we expire it (in
|
||||
// nanoseconds)?
|
||||
p.SetFrontendDescriptorLifetime(40 * 1000000000)
|
||||
// InstanceDescriptorTooOld If we last received a descriptor for this instance more than
|
||||
// INSTANCE_DESCRIPTOR_TOO_OLD seconds ago, consider the instance to be down.
|
||||
p.SetInstanceDescriptorTooOld(120 * 1000000000)
|
||||
// HsdirNReplicas Number of replicas per descriptor (generally only use 2!)
|
||||
p.SetHsdirNReplicas(2)
|
||||
// NIntrosPerInstance How many intros should we use from each instance in the final frontend
|
||||
// descriptor? Default 2 but we use 1 here.
|
||||
p.SetNIntrosPerInstance(1)
|
||||
// NIntrosWanted The amount of introduction points wanted for each individual descriptor
|
||||
p.SetNIntrosWanted(20)
|
||||
// NEWNYM is a tor control port command which clears the descriptors. Tor has a rate limit on this to about 8 seconds.
|
||||
// In the event that changes this variable can be adjusted. Otherwise, don't touch.
|
||||
p.SetNewnymSleep(8 * time.Second)
|
||||
|
||||
// Below is the adaptive configuration area. Don't touch these!
|
||||
p.SetAdaptForcePublish(1)
|
||||
p.SetAdaptDistinctDescriptors(1)
|
||||
}
|
||||
|
||||
// Main This is the entry point of v3 functionality.
|
||||
// Initialize onionbalance, schedule future jobs and let the scheduler do its thing.
|
||||
func Main(c *cli.Context) {
|
||||
loadDefaults()
|
||||
p := Params()
|
||||
if p.NIntrosWanted() > 20 {
|
||||
logrus.Fatal("You need to reduce the NIntrosWanted param value to 20 or below. " +
|
||||
"While it's possible to push more than 20 introduction points; the Tor clients, " +
|
||||
"at this time, will reject the descriptor. See tor's HS_CONFIG_V3_MAX_INTRO_POINTS in hs_config.h and function " +
|
||||
"desc_decode_encrypted_v3 in hs_descriptor.c")
|
||||
}
|
||||
p.SetAdaptEnabled(c.Bool("adaptive"))
|
||||
p.SetAdaptStrict(c.Bool("strict"))
|
||||
p.SetTightTimings(c.Bool("tight"))
|
||||
config := c.String("config")
|
||||
ip := c.String("ip")
|
||||
port := c.Int("port")
|
||||
quick := c.Bool("quick")
|
||||
torPassword := c.String("torPassword")
|
||||
start, end, err := parseRange(c.String("dirsplit"))
|
||||
if err != nil {
|
||||
logrus.Errorf("Your dirsplit value is invalid! Error: " + err.Error())
|
||||
}
|
||||
p.SetDirStart(start)
|
||||
p.SetDirEnd(end)
|
||||
MyOnionBalance := OnionBalance()
|
||||
if err := MyOnionBalance.InitSubsystems(InitSubsystemsParams{
|
||||
ConfigPath: config,
|
||||
IP: ip,
|
||||
Port: port,
|
||||
TorPassword: torPassword,
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
initScheduler(quick)
|
||||
}
|
||||
|
||||
func initScheduler(quick bool) {
|
||||
p := Params()
|
||||
instance := OnionBalance()
|
||||
|
||||
// Tell Tor to be active and ready.
|
||||
go torActive(instance.Controller())
|
||||
|
||||
//Check if Tor has live consensus before doing anything.
|
||||
if !instance.Consensus().IsLive() {
|
||||
logrus.Fatal("No live consensus. Wait for Tor to grab the consensus and try again.")
|
||||
}
|
||||
|
||||
if p.AdaptEnabled() {
|
||||
adaptiveStart(*instance)
|
||||
} else {
|
||||
instance.FetchInstanceDescriptors()
|
||||
// Quick is a hack to quickly deploy a new descriptor. Used to fix a suck descriptor.
|
||||
if quick {
|
||||
time.Sleep(5 * time.Second)
|
||||
} else {
|
||||
time.Sleep(time.Duration(p.InitialCallbackDelay()))
|
||||
}
|
||||
instance.PublishAllDescriptors()
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
//individual async channel threads for both fetching and publishing descriptors.
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Duration(p.FetchDescriptorFrequency())):
|
||||
case <-p.FetchChannel:
|
||||
continue
|
||||
}
|
||||
run := adaptFetch()
|
||||
if run {
|
||||
//variate timings to reduce correlation attacks
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
millisecond := time.Duration(rand.Intn(2001)) * time.Millisecond
|
||||
time.Sleep(millisecond)
|
||||
instance.FetchInstanceDescriptors()
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Duration(p.PublishDescriptorCheckFrequency())):
|
||||
case <-p.PublishChannel:
|
||||
continue
|
||||
}
|
||||
|
||||
run := adaptPublish()
|
||||
if run {
|
||||
//variate timings to reduce correlation attacks
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
millisecond := time.Duration(rand.Intn(2001)) * time.Millisecond
|
||||
time.Sleep(millisecond)
|
||||
instance.PublishAllDescriptors()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func torActive(instance *Controller) {
|
||||
_, err := instance.Signal("ACTIVE")
|
||||
if err != nil {
|
||||
logrus.Panicf("Sending 'Active' signal failed. Check if your Tor control process is still alive and able to be connected to!")
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
func adaptiveStart(instance Onionbalance) {
|
||||
p := Params()
|
||||
logrus.Infof("[ADAPTIVE] Waiting for %d instance descriptors.", p.NInstances())
|
||||
p.SetAdaptWgEnabled(true)
|
||||
instance.FetchInstanceDescriptors()
|
||||
p.AdaptWg().Wait()
|
||||
//need to get the channel and see how many instances have returned within the InitialCallbackDelay time. Hoping for all of them. Warn if not.
|
||||
adaptStartTime := p.AdaptStartTime()
|
||||
p.SetAdaptDelay(time.Since(time.Unix(adaptStartTime, 0)).Nanoseconds())
|
||||
logrus.Info("[ADAPTIVE] Adaptive Configured! It took ", p.AdaptDelay()/1000000000, " seconds to get all descriptors. Optimizing performance!")
|
||||
if p.AdaptStrict() {
|
||||
strictTest(p.AdaptDelay())
|
||||
}
|
||||
//Prevent Waitgroup Recounting. Sanity check as well.
|
||||
p.SetAdaptWgEnabled(false)
|
||||
|
||||
logrus.Info("[ADAPTIVE] Adapting to network and instance conditions...")
|
||||
|
||||
//Make sure that newnym has a chance to clear descriptors
|
||||
if p.AdaptDelay() < 8000000000 { //8 seconds
|
||||
p.SetAdaptDelay(80000000000)
|
||||
}
|
||||
|
||||
adaptDelay := p.AdaptDelay()
|
||||
//We got all the descriptors within this timeframe so should be a good default.
|
||||
p.SetFetchDescriptorFrequency(adaptDelay)
|
||||
//If new descriptors are not received for 5 fetches (2 retries) count them as old.
|
||||
p.SetInstanceDescriptorTooOld(adaptDelay * 5)
|
||||
//Expire a descriptor after two fetches. This is not ideal for large amounts of instances.
|
||||
p.SetFrontendDescriptorLifetime(adaptDelay * 2)
|
||||
//Time the publishing checks with the fetch descriptors. Only publishes if needed.
|
||||
p.SetPublishDescriptorCheckFrequency(adaptDelay / 2)
|
||||
|
||||
adaptFetch()
|
||||
//adaptPublish()
|
||||
//force publishing on first start
|
||||
p.SetAdaptIntroChanged(1)
|
||||
}
|
||||
|
||||
// Adaptive Publish
|
||||
//
|
||||
// These functions changes the way onionbalance operates to prioritize introduction rotation
|
||||
// onto the network in the most ideal timings (to increase reachability). It responds to the amount of
|
||||
// active instances and changes the publishing timings to the network in hopes of not overloading
|
||||
// the attached Tor process. The point is to help tune in the default parameters, based on the amount
|
||||
// of instances, so that it maximizes the onion service uptime. It is heavily opinionated and is not
|
||||
// a perfect alternative to manual refinement.
|
||||
// However, it is far better than what the original python implementation does. Aka nothing.
|
||||
|
||||
func adaptPublish() bool {
|
||||
p := Params()
|
||||
if !p.AdaptEnabled() {
|
||||
return true
|
||||
}
|
||||
|
||||
//If there is equal to or less than 20 Introduction points active disable distinct descriptors. The benefits of distinct descriptors
|
||||
//are only shown with more than 20 instances. We increase reachability with tighter timings on descriptor
|
||||
//pushes and better introduction point selection.
|
||||
if p.NIntroduction() <= 20 {
|
||||
p.SetAdaptDistinctDescriptors(1)
|
||||
p.SetAdaptShuffle(1)
|
||||
p := Params()
|
||||
adaptDelay := p.AdaptDelay()
|
||||
descriptorCheckFrequency := p.publishDescriptorCheckFrequency
|
||||
//If there has been no change in any introduction point with all instances being active do not proceed.
|
||||
if p.AdaptIntroChanged() == 0 && p.AdaptUp() == p.AdaptCount() {
|
||||
if adaptDelay < 800000000000 { //800 seconds max limit change
|
||||
logrus.Info("[ADAPTIVE] Slowing down descriptor publishing")
|
||||
p.SetPublishDescriptorCheckFrequency(adaptDelay * 2)
|
||||
}
|
||||
logrus.Info("[ADAPTIVE] Skipping descriptor push as there has been no introduction point change.")
|
||||
return false
|
||||
} else {
|
||||
if adaptDelay == descriptorCheckFrequency {
|
||||
logrus.Info("[ADAPTIVE] Speeding up descriptor publishing")
|
||||
p.SetPublishDescriptorCheckFrequency(adaptDelay / 2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p.SetAdaptDistinctDescriptors(1)
|
||||
//If the number of introduction points are less than the amount it takes to fill all HSDIR descriptors,
|
||||
//configure the push of introduction points to prioritize the freshest descriptors received. Otherwise, treat all
|
||||
//introduction points as equal priority.
|
||||
maxintropoints := p.AdaptHSDirCount() * 20
|
||||
if maxintropoints > p.NIntroduction() {
|
||||
p.SetAdaptShuffle(1)
|
||||
} else {
|
||||
p.SetAdaptShuffle(0)
|
||||
}
|
||||
}
|
||||
|
||||
//ADAPT TIMING ADJUSTMENTS REMOVED (correlation attack potential)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Adaptive Fetching
|
||||
func adaptFetch() bool {
|
||||
p := Params()
|
||||
if !p.AdaptEnabled() {
|
||||
return true
|
||||
}
|
||||
//warn if some instances are down
|
||||
if p.AdaptCount() != p.NInstances() {
|
||||
if p.AdaptDownNoDescriptor() != 0 {
|
||||
logrus.Infof("[ADAPTIVE] There are %d instances who have no returned Descriptors. If you see this message a lot "+
|
||||
"stop gobalance and remove the offline instances for better performance.", p.AdaptDownNoDescriptor())
|
||||
}
|
||||
|
||||
if p.AdaptDownInstanceOld() != 0 {
|
||||
logrus.Infof("[ADAPTIVE] There are %d instances who have old descriptors. If you see this message a lot "+
|
||||
"stop gobalance, reset tor, and remove the offline instances for better performance.", p.AdaptDownInstanceOld())
|
||||
}
|
||||
}
|
||||
|
||||
//ADAPT TIMING ADJUSTMENTS REMOVED. (correlation attack potential)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// strictTest Returns if adaptive timings are reasonable giving the number of instances. Runs only on start.
|
||||
// Gracefully exits on failure. Configurable with the "strict" cli option. Defaults to true.
|
||||
func strictTest(timings int64) {
|
||||
p := Params()
|
||||
//Check if there are failed services within the config.yaml which returned with no descriptors exit with warning.
|
||||
//Best to clear out the downed instances or wait for their recovery before doing anything else
|
||||
nInstances := p.NInstances()
|
||||
if nInstances < p.AdaptCount() {
|
||||
logrus.Infof("[STRICT] Some instances are down at start of this process. Wait for their recovery or remove " +
|
||||
"the downed instances.")
|
||||
if logrus.GetLevel().String() != "debug" {
|
||||
logrus.Infof("[STRICT] Set '--verbosity bebug' to see downed instances.")
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
//Tor has a soft max of 32 general-purpose client circuits that can be pending.
|
||||
//If you go over that value it will wait until some finish. This means having more than 32 fronts will greatly limit
|
||||
//your circuit builds.
|
||||
if nInstances > 32 {
|
||||
logrus.Infof("[STRICT] You have over 32 active fronts. Tor has a soft limit of 32 general-purpose pending circuits." +
|
||||
"For the best performance split your fronts and descriptor push over multiple gobalance instances and Tor processes")
|
||||
logrus.Debugf("You have %d active fronts. You want under 32.", nInstances)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
//From many tests the tolerance for a series of instances on a single Tor process is
|
||||
//a simple base of 10 plus 3 per instance. It takes in account the delay of the Tor network circuit building
|
||||
//The timings here was calculated when the Tor network was under DDOS with extreme latency build issues.
|
||||
//This will be probably inaccurate in times of peace and should be tightened further.
|
||||
maxTimings := (10 + (5 * nInstances)) * 1000000000
|
||||
if maxTimings < timings {
|
||||
logrus.Infof("[STRICT] The Tor process is too slow to handle %d instances in current network conditions. "+
|
||||
"Reduce the amount of instances on an individual onionbalance and tor process to pass strict test checks or "+
|
||||
"disable with cli --strict false.", nInstances)
|
||||
logrus.Debugf("strictTimings=%d and reported timings=%d in seconds", maxTimings/1000000000, timings/1000000000)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func parseRange(input string) (int, int, error) {
|
||||
if input == "" {
|
||||
return 0, 0, nil
|
||||
}
|
||||
pattern := `^([1-8])(?:-([1-8]))?$`
|
||||
re := regexp.MustCompile(pattern)
|
||||
matches := re.FindStringSubmatch(input)
|
||||
|
||||
if matches == nil {
|
||||
logrus.Errorf("The dirsplit value is invalid. You need to have it within the range of 1-8!")
|
||||
}
|
||||
|
||||
start, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
end := start
|
||||
if matches[2] != "" {
|
||||
end, err = strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the end is greater than or equal to the start
|
||||
if end < start {
|
||||
logrus.Errorf("End number should be greater than or equal to the start number. It's a assending range!")
|
||||
}
|
||||
|
||||
return start, end, nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue