1// Copyright (C) 2020 Storj Labs, Inc. 2// See LICENSE for copying information. 3 4package preflight_test 5 6import ( 7 "context" 8 "net" 9 "strconv" 10 "testing" 11 "time" 12 13 "github.com/stretchr/testify/require" 14 "go.uber.org/zap/zaptest" 15 "golang.org/x/sync/errgroup" 16 17 "storj.io/common/identity/testidentity" 18 "storj.io/common/pb" 19 "storj.io/common/peertls/extensions" 20 "storj.io/common/peertls/tlsopts" 21 "storj.io/common/rpc" 22 "storj.io/common/testcontext" 23 "storj.io/storj/private/server" 24 "storj.io/storj/private/testplanet" 25 "storj.io/storj/storagenode" 26 "storj.io/storj/storagenode/preflight" 27 "storj.io/storj/storagenode/trust" 28) 29 30type mockServer struct { 31 localTime time.Time 32 pb.DRPCNodeServer 33} 34 35func TestLocalTime_InSync(t *testing.T) { 36 testplanet.Run(t, testplanet.Config{ 37 SatelliteCount: 1, StorageNodeCount: 1, UplinkCount: 0, 38 Reconfigure: testplanet.Reconfigure{ 39 StorageNode: func(index int, config *storagenode.Config) { 40 config.Preflight.LocalTimeCheck = true 41 }, 42 }, 43 }, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) { 44 storagenode := planet.StorageNodes[0] 45 err := storagenode.Preflight.LocalTime.Check(ctx) 46 require.NoError(t, err) 47 }) 48} 49 50func TestLocalTime_OutOfSync(t *testing.T) { 51 ctx := testcontext.New(t) 52 defer ctx.Cleanup() 53 54 log := zaptest.NewLogger(t) 55 56 // set up mock satellite server configuration 57 mockSatID, err := testidentity.NewTestIdentity(ctx) 58 require.NoError(t, err) 59 config := server.Config{ 60 Address: "127.0.0.1:0", 61 PrivateAddress: "127.0.0.1:0", 62 63 Config: tlsopts.Config{ 64 PeerIDVersions: "*", 65 Extensions: extensions.Config{ 66 Revocation: false, 67 WhitelistSignedLeaf: false, 68 }, 69 }, 70 } 71 mockSatTLSOptions, err := tlsopts.NewOptions(mockSatID, config.Config, nil) 72 require.NoError(t, err) 73 74 t.Run("Less than 30m", func(t *testing.T) { 75 // register mock GetTime endpoint to mock server 76 var group errgroup.Group 77 defer ctx.Check(group.Wait) 78 79 contactServer, err := server.New(log, mockSatTLSOptions, config) 80 require.NoError(t, err) 81 defer ctx.Check(contactServer.Close) 82 83 err = pb.DRPCRegisterNode(contactServer.DRPC(), &mockServer{ 84 localTime: time.Now().Add(-25 * time.Minute), 85 }) 86 require.NoError(t, err) 87 88 group.Go(func() error { 89 return contactServer.Run(ctx) 90 }) 91 92 // get mock server address 93 _, portStr, err := net.SplitHostPort(contactServer.Addr().String()) 94 require.NoError(t, err) 95 port, err := strconv.Atoi(portStr) 96 require.NoError(t, err) 97 url := trust.SatelliteURL{ 98 ID: mockSatID.ID, 99 Host: "127.0.0.1", 100 Port: port, 101 } 102 require.NoError(t, err) 103 104 // set up storagenode client 105 source, err := trust.NewStaticURLSource(url.String()) 106 require.NoError(t, err) 107 108 identity, err := testidentity.NewTestIdentity(ctx) 109 require.NoError(t, err) 110 tlsOptions, err := tlsopts.NewOptions(identity, config.Config, nil) 111 require.NoError(t, err) 112 dialer := rpc.NewDefaultDialer(tlsOptions) 113 pool, err := trust.NewPool(log, trust.Dialer(dialer), trust.Config{ 114 Sources: []trust.Source{source}, 115 CachePath: ctx.File("trust-cache.json"), 116 }, nil) 117 require.NoError(t, err) 118 err = pool.Refresh(ctx) 119 require.NoError(t, err) 120 121 // should not return any error when node's clock is off no more than 30m 122 localtime := preflight.NewLocalTime(log, preflight.Config{ 123 LocalTimeCheck: true, 124 }, pool, dialer) 125 err = localtime.Check(ctx) 126 require.NoError(t, err) 127 128 }) 129 130 t.Run("More than 30m", func(t *testing.T) { 131 // register mock GetTime endpoint to mock server 132 var group errgroup.Group 133 defer ctx.Check(group.Wait) 134 135 contactServer, err := server.New(log, mockSatTLSOptions, config) 136 require.NoError(t, err) 137 defer ctx.Check(contactServer.Close) 138 139 err = pb.DRPCRegisterNode(contactServer.DRPC(), &mockServer{ 140 localTime: time.Now().Add(-31 * time.Minute), 141 }) 142 require.NoError(t, err) 143 144 group.Go(func() error { 145 return contactServer.Run(ctx) 146 }) 147 148 // get mock server address 149 _, portStr, err := net.SplitHostPort(contactServer.Addr().String()) 150 require.NoError(t, err) 151 port, err := strconv.Atoi(portStr) 152 require.NoError(t, err) 153 url := trust.SatelliteURL{ 154 ID: mockSatID.ID, 155 Host: "127.0.0.1", 156 Port: port, 157 } 158 require.NoError(t, err) 159 160 // set up storagenode client 161 source, err := trust.NewStaticURLSource(url.String()) 162 require.NoError(t, err) 163 164 identity, err := testidentity.NewTestIdentity(ctx) 165 require.NoError(t, err) 166 tlsOptions, err := tlsopts.NewOptions(identity, config.Config, nil) 167 require.NoError(t, err) 168 dialer := rpc.NewDefaultDialer(tlsOptions) 169 pool, err := trust.NewPool(log, trust.Dialer(dialer), trust.Config{ 170 Sources: []trust.Source{source}, 171 CachePath: ctx.File("trust-cache.json"), 172 }, nil) 173 require.NoError(t, err) 174 err = pool.Refresh(ctx) 175 require.NoError(t, err) 176 177 // should return an error when node's clock is off by more than 30m with all trusted satellites 178 localtime := preflight.NewLocalTime(log, preflight.Config{ 179 LocalTimeCheck: true, 180 }, pool, dialer) 181 err = localtime.Check(ctx) 182 require.Error(t, err) 183 }) 184} 185 186func (mock *mockServer) GetTime(ctx context.Context, req *pb.GetTimeRequest) (*pb.GetTimeResponse, error) { 187 return &pb.GetTimeResponse{ 188 Timestamp: mock.localTime, 189 }, nil 190} 191