From 84f1cee808f7bbaa3a558cf99001f751b9787603 Mon Sep 17 00:00:00 2001 From: guojunding Date: Tue, 3 Sep 2024 14:27:06 +0800 Subject: [PATCH] ignore raft messages if member id mismatch and Update the compaction log --- ...-raft-messages-if-member-id-mismatch.patch | 144 ++++++++++++++++++ ...tstrap-and-update-compacts-signature.patch | 96 ++++++++++++ etcd.spec | 11 +- 3 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 0012-backport-ignore-raft-messages-if-member-id-mismatch.patch create mode 100644 0013-backport-Update-the-compaction-log-when-bootstrap-and-update-compacts-signature.patch diff --git a/0012-backport-ignore-raft-messages-if-member-id-mismatch.patch b/0012-backport-ignore-raft-messages-if-member-id-mismatch.patch new file mode 100644 index 0000000..d978d37 --- /dev/null +++ b/0012-backport-ignore-raft-messages-if-member-id-mismatch.patch @@ -0,0 +1,144 @@ +From 1b53a26cae96b7834b117a787ab666fa22c36deb Mon Sep 17 00:00:00 2001 +From: Chun-Hung Tseng +Date: Wed, 17 Apr 2024 13:50:08 +0200 +Subject: [PATCH] [backport-3.4] server: ignore raft messages if member id + mismatch #17078 + +Signed-off-by: Chun-Hung Tseng +--- + etcdserver/server.go | 12 +++++++ + etcdserver/server_test.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 92 insertions(+) + +diff --git a/etcdserver/server.go b/etcdserver/server.go +index a341625..e57b89f 100644 +--- a/etcdserver/server.go ++++ b/etcdserver/server.go +@@ -895,6 +895,18 @@ func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error { + } + return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message from removed member") + } ++ if s.ID() != types.ID(m.To) { ++ if lg := s.getLogger(); lg != nil { ++ lg.Warn( ++ "rejected Raft message to mismatch member", ++ zap.String("local-member-id", s.ID().String()), ++ zap.String("mismatch-member-id", types.ID(m.To).String()), ++ ) ++ } else { ++ plog.Warningf("rejected message to mismatch member %s", types.ID(m.From).String()) ++ } ++ return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message to mismatch member") ++ } + if m.Type == raftpb.MsgApp { + s.stats.RecvAppendReq(types.ID(m.From).String(), m.Size()) + } +diff --git a/etcdserver/server_test.go b/etcdserver/server_test.go +index 405fae8..b49312c 100644 +--- a/etcdserver/server_test.go ++++ b/etcdserver/server_test.go +@@ -19,6 +19,7 @@ import ( + "encoding/json" + "fmt" + "io/ioutil" ++ "math" + "net/http" + "os" + "path" +@@ -48,6 +49,7 @@ import ( + "go.etcd.io/etcd/raft" + "go.etcd.io/etcd/raft/raftpb" + "go.uber.org/zap" ++ "go.uber.org/zap/zaptest" + ) + + // TestDoLocalAction tests requests which do not need to go through raft to be applied, +@@ -1317,7 +1319,85 @@ func TestAddMember(t *testing.T) { + t.Errorf("member with id 1234 is not added") + } + } ++func realisticRaftNode(lg *zap.Logger, id uint64, snap *raftpb.Snapshot) *raftNode { ++ storage := raft.NewMemoryStorage() ++ storage.SetHardState(raftpb.HardState{Commit: 0, Term: 0}) ++ if snap != nil { ++ err := storage.ApplySnapshot(*snap) ++ if err != nil { ++ panic(err) ++ } ++ } ++ c := &raft.Config{ ++ ID: id, ++ ElectionTick: 10, ++ HeartbeatTick: 1, ++ Storage: storage, ++ MaxSizePerMsg: math.MaxUint64, ++ MaxInflightMsgs: 256, ++ } ++ n := raft.RestartNode(c) ++ r := newRaftNode(raftNodeConfig{ ++ lg: lg, ++ Node: n, ++ transport: newNopTransporter(), ++ }) ++ return r ++} + ++// TestProcessIgnoreMismatchMessage tests Process must ignore messages to ++// mismatch member. ++func TestProcessIgnoreMismatchMessage(t *testing.T) { ++ lg := zaptest.NewLogger(t) ++ cl := newTestCluster(nil) ++ st := v2store.New() ++ cl.SetStore(st) ++ ++ // Bootstrap a 3-node cluster, member IDs: 1 2 3. ++ cl.AddMember(&membership.Member{ID: 1}) ++ cl.AddMember(&membership.Member{ID: 2}) ++ cl.AddMember(&membership.Member{ID: 3}) ++ // r is initialized with ID 1. ++ r := realisticRaftNode(lg, 1, &raftpb.Snapshot{ ++ Metadata: raftpb.SnapshotMetadata{ ++ Index: 11, // Magic number. ++ Term: 11, // Magic number. ++ ConfState: raftpb.ConfState{ ++ // Member ID list. ++ Voters: []uint64{1, 2, 3}, ++ }, ++ }, ++ }) ++ ++ var cindex consistentIndex ++ cindex.setConsistentIndex(0) ++ s := &EtcdServer{ ++ lgMu: new(sync.RWMutex), ++ lg: lg, ++ id: 1, ++ r: *r, ++ v2store: st, ++ cluster: cl, ++ reqIDGen: idutil.NewGenerator(0, time.Time{}), ++ SyncTicker: &time.Ticker{}, ++ consistIndex: cindex, ++ } ++ // Mock a mad switch dispatching messages to wrong node. ++ m := raftpb.Message{ ++ Type: raftpb.MsgHeartbeat, ++ To: 2, // Wrong ID, s.MemberId() is 1. ++ From: 3, ++ Term: 11, ++ Commit: 42, // Commit is larger than the last index 11. ++ } ++ if types.ID(m.To) == s.ID() { ++ t.Fatalf("m.To (%d) is expected to mismatch s.MemberId (%d)", m.To, s.ID()) ++ } ++ err := s.Process(context.Background(), m) ++ if err == nil { ++ t.Fatalf("Must ignore the message and return an error") ++ } ++} + // TestRemoveMember tests RemoveMember can propose and perform node removal. + func TestRemoveMember(t *testing.T) { + n := newNodeConfChangeCommitterRecorder() +-- +2.9.3.windows.1 + diff --git a/0013-backport-Update-the-compaction-log-when-bootstrap-and-update-compacts-signature.patch b/0013-backport-Update-the-compaction-log-when-bootstrap-and-update-compacts-signature.patch new file mode 100644 index 0000000..eb6f9d0 --- /dev/null +++ b/0013-backport-Update-the-compaction-log-when-bootstrap-and-update-compacts-signature.patch @@ -0,0 +1,96 @@ +From f8f186490bbd2f404f47a50a4dab92355c3485f4 Mon Sep 17 00:00:00 2001 +From: Benjamin Wang +Date: Sun, 21 Apr 2024 13:23:59 +0100 +Subject: [PATCH] Update the compaction log when bootstrap and update compact's + signature + +Actually the compact() never return an error, so remove the second return +parameter. + +Signed-off-by: Benjamin Wang +--- + mvcc/kvstore.go | 39 +++++++++++++++++++++++++-------------- + 1 file changed, 25 insertions(+), 14 deletions(-) + +diff --git a/mvcc/kvstore.go b/mvcc/kvstore.go +index 6752038..198db96 100644 +--- a/mvcc/kvstore.go ++++ b/mvcc/kvstore.go +@@ -274,7 +274,7 @@ func (s *store) updateCompactRev(rev int64) (<-chan struct{}, error) { + return nil, nil + } + +-func (s *store) compact(trace *traceutil.Trace, rev int64) (<-chan struct{}, error) { ++func (s *store) compact(trace *traceutil.Trace, rev int64) <-chan struct{} { + ch := make(chan struct{}) + var j = func(ctx context.Context) { + if ctx.Err() != nil { +@@ -293,7 +293,7 @@ func (s *store) compact(trace *traceutil.Trace, rev int64) (<-chan struct{}, err + + s.fifoSched.Schedule(j) + trace.Step("schedule compaction") +- return ch, nil ++ return ch + } + + func (s *store) compactLockfree(rev int64) (<-chan struct{}, error) { +@@ -302,7 +302,7 @@ func (s *store) compactLockfree(rev int64) (<-chan struct{}, error) { + return ch, err + } + +- return s.compact(traceutil.TODO(), rev) ++ return s.compact(traceutil.TODO(), rev), nil + } + + func (s *store) Compact(trace *traceutil.Trace, rev int64) (<-chan struct{}, error) { +@@ -316,7 +316,7 @@ func (s *store) Compact(trace *traceutil.Trace, rev int64) (<-chan struct{}, err + } + s.mu.Unlock() + +- return s.compact(trace, rev) ++ return s.compact(trace, rev), nil + } + + // DefaultIgnores is a map of keys to ignore in hash checking. +@@ -447,17 +447,28 @@ func (s *store) restore() error { + tx.Unlock() + + if scheduledCompact != 0 { +- s.compactLockfree(scheduledCompact) +- +- if s.lg != nil { +- s.lg.Info( +- "resume scheduled compaction", +- zap.String("meta-bucket-name", string(metaBucketName)), +- zap.String("meta-bucket-name-key", string(scheduledCompactKeyName)), +- zap.Int64("scheduled-compact-revision", scheduledCompact), +- ) ++ if _, err := s.compactLockfree(scheduledCompact); err != nil { ++ if s.lg != nil { ++ s.lg.Warn("compaction encountered error", ++ zap.String("meta-bucket-name", string(metaBucketName)), ++ zap.String("meta-bucket-name-key", string(scheduledCompactKeyName)), ++ zap.Int64("scheduled-compact-revision", scheduledCompact), ++ zap.Error(err), ++ ) ++ } else { ++ plog.Printf("compaction encountered error, scheduled-compact-revision: %d", scheduledCompact) ++ } + } else { +- plog.Printf("resume scheduled compaction at %d", scheduledCompact) ++ if s.lg != nil { ++ s.lg.Info( ++ "resume scheduled compaction", ++ zap.String("meta-bucket-name", string(metaBucketName)), ++ zap.String("meta-bucket-name-key", string(scheduledCompactKeyName)), ++ zap.Int64("scheduled-compact-revision", scheduledCompact), ++ ) ++ } else { ++ plog.Printf("resume scheduled compaction at %d", scheduledCompact) ++ } + } + } + +-- +2.9.3.windows.1 + diff --git a/etcd.spec b/etcd.spec index 4007b93..b25400c 100644 --- a/etcd.spec +++ b/etcd.spec @@ -31,7 +31,7 @@ system.} %global gosupfiles integration/fixtures/* etcdserver/api/v2http/testdata/* Name: etcd -Release: 14 +Release: 15 Summary: Distributed reliable key-value store for the most critical data of a distributed system # Upstream license specification: Apache-2.0 @@ -55,7 +55,8 @@ Patch8: 0008-fix-CVE-2023-32082.patch Patch9: 0009-fix-CVE-2021-28235.patch Patch10: 0010-backport-Suppress-noisy-basic-auth-token-deletion-log.patch Patch11: 0011-backport-Fix-wait-time-docs-tests-to-indicate-trigger-deadline.patch - +Patch12: 0012-backport-ignore-raft-messages-if-member-id-mismatch.patch +Patch13: 0013-backport-Update-the-compaction-log-when-bootstrap-and-update-compacts-signature.patch BuildRequires: golang BuildRequires: python3-devel %{?systemd_requires} @@ -80,6 +81,8 @@ Requires(pre): shadow-utils %patch9 -p1 %patch10 -p1 %patch11 -p1 +%patch12 -p1 +%patch13 -p1 %ifarch sw_64 %patch3 -p1 %endif @@ -168,6 +171,10 @@ getent passwd %{name} >/dev/null || useradd -r -g %{name} -d %{_sharedstatedir}/ %endif %changelog +* Tue Sep 3 2024 guojunding - 3.4.14-15 +- Fix server: ignore raft messages if member id +- Update the compaction log when bootstrap and update compact's signature + * Tue Sep 3 2024 guojunding - 3.4.14-14 - Fix wait_time docs, tests for trigger deadline greater than - or equal to wait deadline -- Gitee