-
Notifications
You must be signed in to change notification settings - Fork 390
/
localtime.go
140 lines (116 loc) · 3.94 KB
/
localtime.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package preflight
import (
"context"
"math"
"time"
"github.com/zeebo/errs"
"go.uber.org/zap"
"golang.org/x/sync/errgroup"
"storj.io/common/pb"
"storj.io/common/rpc"
"storj.io/common/storj"
"storj.io/storj/storagenode/trust"
)
// ErrClockOutOfSyncMinor is the error class for system clock is off by more than 10m.
var ErrClockOutOfSyncMinor = errs.Class("system clock is off")
// ErrClockOutOfSyncMajor is the error class for system clock is out of sync by more than 30m.
var ErrClockOutOfSyncMajor = errs.Class("system clock is out of sync")
// LocalTime checks local system clock against all trusted satellites.
type LocalTime struct {
log *zap.Logger
config Config
trust *trust.Pool
dialer rpc.Dialer
}
// NewLocalTime creates a new localtime instance.
func NewLocalTime(log *zap.Logger, config Config, trust *trust.Pool, dialer rpc.Dialer) *LocalTime {
return &LocalTime{
log: log,
config: config,
trust: trust,
dialer: dialer,
}
}
// Check compares local system clock with all trusted satellites' system clock.
// it returns an error when local system clock is out of sync by more than 24h with all trusted satellites' clock.
func (localTime *LocalTime) Check(ctx context.Context) (err error) {
defer mon.Task()(&ctx)(&err)
if !localTime.config.LocalTimeCheck {
localTime.log.Debug("local system clock check is not enabled")
return nil
}
localTime.log.Info("start checking local system clock with trusted satellites' system clock.")
group, ctx := errgroup.WithContext(ctx)
// get trusted satellites
satellites := localTime.trust.GetSatellites(ctx)
results := make([]error, len(satellites))
for i, satellite := range satellites {
i := i
satellite := satellite
group.Go(func() error {
// get a current timestamp
currentLocalTime := time.Now().UTC()
satelliteTime, err := localTime.getSatelliteTime(ctx, satellite)
if err != nil {
localTime.log.Error("unable to get satellite system time", zap.Stringer("Satellite ID", satellite), zap.Error(err))
results[i] = ErrClockOutOfSyncMajor.Wrap(err)
return nil
}
err = localTime.checkSatelliteTime(ctx, satelliteTime.GetTimestamp(), currentLocalTime)
if err != nil {
localTime.log.Error("system clock is out of sync with satellite", zap.Stringer("Satellite ID", satellite), zap.Error(err))
if ErrClockOutOfSyncMinor.Has(err) {
return nil
}
results[i] = err
}
return nil
})
}
_ = group.Wait()
errsCounter := 0
for _, result := range results {
if ErrClockOutOfSyncMajor.Has(result) {
errsCounter++
}
}
if errsCounter == len(satellites) {
return ErrClockOutOfSyncMajor.New("system clock is out of sync with all trusted satellites")
}
localTime.log.Info("local system clock is in sync with trusted satellites' system clock.")
return nil
}
func (localTime *LocalTime) getSatelliteTime(ctx context.Context, satelliteID storj.NodeID) (_ *pb.GetTimeResponse, err error) {
defer mon.Task()(&ctx)(&err)
nodeurl, err := localTime.trust.GetNodeURL(ctx, satelliteID)
if err != nil {
return nil, err
}
conn, err := localTime.dialer.DialNodeURL(ctx, nodeurl)
if err != nil {
return nil, err
}
defer func() {
err = errs.Combine(err, conn.Close())
}()
resp, err := pb.NewDRPCNodeClient(conn).GetTime(ctx, &pb.GetTimeRequest{})
if err != nil {
return nil, err
}
return resp, nil
}
func (localTime *LocalTime) checkSatelliteTime(ctx context.Context, satelliteTime time.Time, systemTime time.Time) (err error) {
defer mon.Task()(&ctx)(&err)
diff := math.Abs(satelliteTime.Sub(systemTime).Minutes())
// check to see if the timestamp received from satellites are off by more than 30m
if diff > 30 {
return ErrClockOutOfSyncMajor.New("clock off by %f minutes", diff)
}
// check to see if the timestamp received from satellites are off by more than 10m
if diff > 10 {
return ErrClockOutOfSyncMinor.New("clock off by %f minutes", diff)
}
return nil
}