Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

security: Encrypt region boundary keys, Part 4 - KMS #3141

Merged
merged 54 commits into from
Nov 13, 2020
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
20c682d
Encrypt region boundary keys, Part 2 - server changes
Sep 16, 2020
b6f7ba0
update errno and config template
Sep 17, 2020
0bf0304
fix typo
Sep 17, 2020
23954db
fix tests
Sep 17, 2020
2bad0b9
fix tests
Sep 17, 2020
f88d26a
fix tests
Sep 17, 2020
6f6d93a
fix tests
Sep 17, 2020
928de26
address comment in #2931
Sep 17, 2020
9b6bcfd
fix loadRegion
Sep 17, 2020
bdd5de4
rename encryption_key_manager package
Sep 17, 2020
bbdb8ef
Merge branch 'master' into enc_server
Sep 17, 2020
90de5ef
fix lint
Sep 17, 2020
c16ecd8
fix lint
Sep 17, 2020
2428248
fix comments
Sep 18, 2020
ce83a42
fix comment
Sep 18, 2020
073f13e
use option pattern
Sep 24, 2020
c35f948
move loadRegion and saveRegion
Sep 24, 2020
06d8963
Merge branch 'master' into enc_server
Sep 24, 2020
551b3f9
revert changes
Sep 24, 2020
7051407
fix doc
Sep 25, 2020
7201c37
address comment
Sep 28, 2020
c9eb341
Merge branch 'master' into enc_server
Sep 28, 2020
39f147e
Merge remote-tracking branch 'origin/master' into enc_server
Sep 28, 2020
12f03c4
Merge branch 'master' into enc_server
Sep 29, 2020
f951278
Merge branch 'master' into enc_server
Sep 30, 2020
ee2e0cd
key manager
Oct 2, 2020
9e753b4
add test and refactor
Oct 5, 2020
a6d1359
make EncryptionKeysPath a const
Oct 5, 2020
103a612
fix region_crypter key manager nil check
Oct 5, 2020
e9d810d
save conflict test
Oct 5, 2020
eb5b6b1
sanity check keys revision
Oct 5, 2020
c97b672
kms
Oct 5, 2020
bc3f35f
test set ciphertextKey
Oct 6, 2020
b5456df
test set ciphertextKey
Oct 6, 2020
fcf3fe2
clone region only when needed
Oct 9, 2020
4f2332f
add test for config
Oct 9, 2020
763bdb3
Merge branch 'enc_server' of github.com:yiwu-arbug/pd into enc_server
Oct 9, 2020
df6cb01
Merge branch 'master' into enc_server
Oct 9, 2020
8f444ac
Merge branch 'enc_kms' of github.com:yiwu-arbug/pd into enc_kms
Oct 9, 2020
1d42e57
update errors
Oct 9, 2020
af90759
Merge branch 'enc_server' into enc_ekm
Oct 10, 2020
39583d2
Merge branch 'enc_ekm' into enc_kms
Oct 10, 2020
079c299
Merge branch 'master' into enc_kms
Nov 2, 2020
9cbc5ff
fix lint
Nov 3, 2020
813a8d9
fix lint
Nov 3, 2020
d585204
Merge branch 'master' into enc_kms
Nov 3, 2020
7cf9888
make errdoc
Nov 3, 2020
9a6020f
address comment
Nov 9, 2020
5a69760
Merge branch 'master' into enc_kms
Nov 9, 2020
0321a80
fix error type
Nov 9, 2020
312bb48
fix test
Nov 9, 2020
e90b370
Merge branch 'master' into enc_kms
Nov 9, 2020
7480d5d
Merge branch 'master' into enc_kms
Nov 9, 2020
f1d3380
Merge branch 'master' into enc_kms
Nov 12, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# AUTOGENERATED BY github.com/pingcap/tiup/components/errdoc/errdoc-gen
# YOU CAN CHANGE THE 'description'/'workaround' FIELDS IF THEM ARE IMPROPER.

["PD:ErrEncryptionKMS"]
error = '''
KMS error
'''

["PD:apiutil:ErrRedirect"]
error = '''
redirect failed
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.13
require (
github.com/BurntSushi/toml v0.3.1
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/aws/aws-sdk-go v1.35.3
github.com/cakturk/go-netstat v0.0.0-20200220111822-e5b49efee7a5
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/coreos/go-semver v0.3.0
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS
github.com/appleboy/gofight/v2 v2.1.2/go.mod h1:frW+U1QZEdDgixycTj4CygQ48yLTUhplt43+Wczp3rw=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/asaskevich/EventBus v0.0.0-20180315140547-d46933a94f05/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/aws/aws-sdk-go v1.35.3 h1:r0puXncSaAfRt7Btml2swUo74Kao+vKhO3VLjwDjK54=
github.com/aws/aws-sdk-go v1.35.3/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
Expand Down Expand Up @@ -432,6 +434,10 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
Expand Down Expand Up @@ -985,6 +991,7 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
Expand Down
117 changes: 117 additions & 0 deletions pkg/encryption/kms.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,124 @@

package encryption

import (
"os"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/kms"
"github.com/aws/aws-sdk-go/service/sts"
"github.com/pingcap/kvproto/pkg/encryptionpb"
"github.com/tikv/pd/pkg/errs"
)

const (
// We only support AWS KMS right now.
kmsVendorAWS = "AWS"

// K8S IAM related environment variables.
envAwsRoleArn = "AWS_ROLE_ARN"
envAwsWebIdentityTokenFile = "AWS_WEB_IDENTITY_TOKEN_FILE"
envAwsRoleSessionName = "AWS_ROLE_SESSION_NAME"
)

func newMasterKeyFromKMS(
config *encryptionpb.MasterKeyKms,
ciphertextKey []byte,
) (masterKey *MasterKey, err error) {
if config == nil {
return nil, errs.ErrEncryptionNewMasterKey.GenWithStack("missing master key file config")
}
if config.Vendor != kmsVendorAWS {
return nil, errs.ErrEncryptionKMS.GenWithStack("unsupported KMS vendor: %s", config.Vendor)
}
credentials, err := newAwsCredentials()
if err != nil {
return nil, err
}
session, err := session.NewSession(&aws.Config{
Credentials: credentials,
Region: &config.Region,
Endpoint: &config.Endpoint,
})
if err != nil {
return nil, errs.ErrEncryptionKMS.Wrap(err).GenWithStack(
"fail to create AWS session to access KMS CMK")
}
client := kms.New(session)
if len(ciphertextKey) == 0 {
numberOfBytes := int64(masterKeyLength)
// Create a new data key.
output, err := client.GenerateDataKey(&kms.GenerateDataKeyInput{
KeyId: &config.KeyId,
NumberOfBytes: &numberOfBytes,
})
if err != nil {
return nil, errs.ErrEncryptionKMS.Wrap(err).GenWithStack(
"fail to generate data key from AWS KMS")
}
if len(output.Plaintext) != masterKeyLength {
return nil, errs.ErrEncryptionKMS.GenWithStack(
"unexpected data key length generated from AWS KMS, expectd %d vs actual %d",
masterKeyLength, len(output.Plaintext))
}
masterKey = &MasterKey{
key: output.Plaintext,
ciphertextKey: output.CiphertextBlob,
}
} else {
// Decrypt existing data key.
output, err := client.Decrypt(&kms.DecryptInput{
KeyId: &config.KeyId,
CiphertextBlob: ciphertextKey,
})
if err != nil {
return nil, errs.ErrEncryptionKMS.Wrap(err).GenWithStack(
"fail to decrypt data key from AWS KMS")
}
if len(output.Plaintext) != masterKeyLength {
return nil, errs.ErrEncryptionKMS.GenWithStack(
"unexpected data key length decrypted from AWS KMS, expected %d vs actual %d",
masterKeyLength, len(output.Plaintext))
}
masterKey = &MasterKey{
key: output.Plaintext,
ciphertextKey: ciphertextKey,
}
}
return
}

func newAwsCredentials() (*credentials.Credentials, error) {
var providers []credentials.Provider

// Credentials from K8S IAM role.
roleArn := os.Getenv(envAwsRoleArn)
tokenFile := os.Getenv(envAwsWebIdentityTokenFile)
sessionName := os.Getenv(envAwsRoleSessionName)
// Session name is optional.
if roleArn != "" && tokenFile != "" {
session, err := session.NewSession()
if err != nil {
return nil, errs.ErrEncryptionKMS.Wrap(err).GenWithStack(
"fail to create AWS session to create a WebIdentityRoleProvider")
}
webIdentityProvider := stscreds.NewWebIdentityRoleProvider(
sts.New(session), roleArn, sessionName, tokenFile)
providers = append(providers, webIdentityProvider)
}

// Credentials from AWS environment variables.
providers = append(providers, &credentials.EnvProvider{})

// Credentials from default AWS credentials file.
providers = append(providers, &credentials.SharedCredentialsProvider{
Filename: "",
Profile: "",
})

credentials := credentials.NewChainCredentials(providers)
return credentials, nil
}
37 changes: 25 additions & 12 deletions pkg/encryption/master_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"io/ioutil"
"strings"

"github.com/pingcap/errors"
"github.com/pingcap/kvproto/pkg/encryptionpb"
"github.com/tikv/pd/pkg/errs"
)
Expand All @@ -33,11 +32,13 @@ type MasterKey struct {
// Encryption key in plaintext. If it is nil, encryption is no-op.
// Never output it to info log or persist it on disk.
key []byte
// Key in ciphertext form. Used by KMS key type.
ciphertextKey []byte
}

// NewMasterKey obtains a master key from backend specified by given config.
// The config may be altered to fill in metadata generated when initializing the master key.
func NewMasterKey(config *encryptionpb.MasterKey) (*MasterKey, error) {
func NewMasterKey(config *encryptionpb.MasterKey, ciphertextKey []byte) (*MasterKey, error) {
if config == nil {
return nil, errs.ErrEncryptionNewMasterKey.GenWithStack("master key config is empty")
}
Expand All @@ -47,15 +48,21 @@ func NewMasterKey(config *encryptionpb.MasterKey) (*MasterKey, error) {
}, nil
}
if file := config.GetFile(); file != nil {
key, err := newMasterKeyFromFile(file)
if err != nil {
return nil, err
}
return &MasterKey{
key: key,
}, nil
return newMasterKeyFromFile(file)
}
if kms := config.GetKms(); kms != nil {
return newMasterKeyFromKMS(kms, ciphertextKey)
}
return nil, errs.ErrEncryptionNewMasterKey.GenWithStack("unrecognized master key type")
}

// NewCustomMasterKeyForTest construct a master key instance from raw key and ciphertext key bytes.
// Used for test only.
func NewCustomMasterKeyForTest(key []byte, ciphertextKey []byte) *MasterKey {
return &MasterKey{
key: key,
ciphertextKey: ciphertextKey,
}
return nil, errors.New("unrecognized master key type")
}

// Encrypt encrypts given plaintext using the master key.
Expand Down Expand Up @@ -84,10 +91,16 @@ func (k *MasterKey) IsPlaintext() bool {
return k.key == nil
}

// CiphertextKey returns the key in encrypted form.
// KMS key type recover the key by decrypting the ciphertextKey from KMS.
func (k *MasterKey) CiphertextKey() []byte {
return k.ciphertextKey
}

// newMasterKeyFromFile reads a hex-string from file specified in the config, and construct a
// MasterKey object. The key must be of 256 bits (32 bytes). The file can contain leading and
// tailing spaces.
func newMasterKeyFromFile(config *encryptionpb.MasterKeyFile) ([]byte, error) {
func newMasterKeyFromFile(config *encryptionpb.MasterKeyFile) (*MasterKey, error) {
if config == nil {
return nil, errs.ErrEncryptionNewMasterKey.GenWithStack("missing master key file config")
}
Expand All @@ -110,5 +123,5 @@ func newMasterKeyFromFile(config *encryptionpb.MasterKeyFile) ([]byte, error) {
"unexpected key length from master key file, expected %d vs actual %d",
masterKeyLength, len(key))
}
return key, nil
return &MasterKey{key: key}, nil
}
12 changes: 6 additions & 6 deletions pkg/encryption/master_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (s *testMasterKeySuite) TestPlaintextMasterKey(c *C) {
Plaintext: &encryptionpb.MasterKeyPlaintext{},
},
}
masterKey, err := NewMasterKey(config)
masterKey, err := NewMasterKey(config, nil)
c.Assert(err, IsNil)
c.Assert(masterKey, Not(IsNil))
c.Assert(len(masterKey.key), Equals, 0)
Expand Down Expand Up @@ -91,7 +91,7 @@ func (s *testMasterKeySuite) TestNewFileMasterKeyMissingPath(c *C) {
},
},
}
_, err := NewMasterKey(config)
_, err := NewMasterKey(config, nil)
c.Assert(err, Not(IsNil))
}

Expand All @@ -106,7 +106,7 @@ func (s *testMasterKeySuite) TestNewFileMasterKeyMissingFile(c *C) {
},
},
}
_, err = NewMasterKey(config)
_, err = NewMasterKey(config, nil)
c.Assert(err, Not(IsNil))
}

Expand All @@ -122,7 +122,7 @@ func (s *testMasterKeySuite) TestNewFileMasterKeyNotHexString(c *C) {
},
},
}
_, err = NewMasterKey(config)
_, err = NewMasterKey(config, nil)
c.Assert(err, Not(IsNil))
}

Expand All @@ -138,7 +138,7 @@ func (s *testMasterKeySuite) TestNewFileMasterKeyLengthMismatch(c *C) {
},
},
}
_, err = NewMasterKey(config)
_, err = NewMasterKey(config, nil)
c.Assert(err, Not(IsNil))
}

Expand All @@ -155,7 +155,7 @@ func (s *testMasterKeySuite) TestNewFileMasterKey(c *C) {
},
},
}
masterKey, err := NewMasterKey(config)
masterKey, err := NewMasterKey(config, nil)
c.Assert(err, IsNil)
c.Assert(hex.EncodeToString(masterKey.key), Equals, key)
}
1 change: 1 addition & 0 deletions pkg/errs/errno.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,4 +285,5 @@ var (
ErrEncryptionLoadKeys = errors.Normalize("load data keys error", errors.RFCCodeText("PD:encryption:ErrEncryptionLoadKeys"))
ErrEncryptionRotateDataKey = errors.Normalize("failed to rotate data key", errors.RFCCodeText("PD:encryption:ErrEncryptionRotateDataKey"))
ErrEncryptionSaveDataKeys = errors.Normalize("failed to save data keys", errors.RFCCodeText("PD:encryption:ErrEncryptionSaveDataKeys"))
ErrEncryptionKMS = errors.Normalize("KMS error", errors.RFCCodeText("PD:ErrEncryptionKMS"))
)
25 changes: 16 additions & 9 deletions server/encryptionkm/key_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ func saveKeys(
leadership *election.Leadership,
masterKeyMeta *encryptionpb.MasterKey,
keys *encryptionpb.KeyDictionary,
) error {
helper keyManagerHelper,
) (err error) {
// Get master key.
masterKey, err := encryption.NewMasterKey(masterKeyMeta)
masterKey, err := helper.newMasterKey(masterKeyMeta, nil)
if err != nil {
return err
}
Expand All @@ -98,9 +99,10 @@ func saveKeys(
return err
}
content := &encryptionpb.EncryptedContent{
Content: ciphertextContent,
MasterKey: masterKeyMeta,
Iv: iv,
Content: ciphertextContent,
MasterKey: masterKeyMeta,
Iv: iv,
CiphertextKey: masterKey.CiphertextKey(),
}
value, err := proto.Marshal(content)
if err != nil {
Expand All @@ -124,7 +126,10 @@ func saveKeys(
}

// extractKeysFromKV unpack encrypted keys from etcd KV.
func extractKeysFromKV(kv *mvccpb.KeyValue) (*encryptionpb.KeyDictionary, error) {
func extractKeysFromKV(
kv *mvccpb.KeyValue,
helper keyManagerHelper,
) (*encryptionpb.KeyDictionary, error) {
content := &encryptionpb.EncryptedContent{}
err := content.Unmarshal(kv.Value)
if err != nil {
Expand All @@ -136,7 +141,7 @@ func extractKeysFromKV(kv *mvccpb.KeyValue) (*encryptionpb.KeyDictionary, error)
return nil, errs.ErrEncryptionLoadKeys.GenWithStack(
"no master key config found with encryption keys")
}
masterKey, err := encryption.NewMasterKey(masterKeyConfig)
masterKey, err := helper.newMasterKey(masterKeyConfig, content.CiphertextKey)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -272,7 +277,7 @@ func (m *KeyManager) loadKeysFromKVImpl(
if kv.ModRevision <= m.mu.keysRevision {
return m.getKeys(), nil
}
keys, err := extractKeysFromKV(kv)
keys, err := extractKeysFromKV(kv, m.helper)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -396,7 +401,7 @@ func (m *KeyManager) rotateKeyIfNeeded(forceUpdate bool) error {
return nil
}
// Store updated keys in etcd.
err = saveKeys(m.mu.leadership, m.masterKeyMeta, keys)
err = saveKeys(m.mu.leadership, m.masterKeyMeta, keys, m.helper)
if err != nil {
m.helper.eventSaveKeysFailure()
log.Error("failed to save keys", zap.Error(err))
Expand Down Expand Up @@ -488,6 +493,7 @@ func (m *KeyManager) SetLeadership(leadership *election.Leadership) error {
type keyManagerHelper struct {
now func() time.Time
tick func(ticker *time.Ticker) <-chan time.Time
newMasterKey func(*encryptionpb.MasterKey, []byte) (*encryption.MasterKey, error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to add this to the helper?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is used in the unit test to fake and check ciphertext_key.

eventAfterReloadByWatcher func()
eventAfterTicker func()
eventAfterLeaderCheckSuccess func()
Expand All @@ -498,6 +504,7 @@ func defaultKeyManagerHelper() keyManagerHelper {
return keyManagerHelper{
now: func() time.Time { return time.Now() },
tick: func(ticker *time.Ticker) <-chan time.Time { return ticker.C },
newMasterKey: encryption.NewMasterKey,
eventAfterReloadByWatcher: func() {},
eventAfterTicker: func() {},
eventAfterLeaderCheckSuccess: func() {},
Expand Down
Loading