Merge pull request #1896 from jsievenpiper/main

feat: add acl auth support for sentinels
This commit is contained in:
Vladimir Mihailenco 2021-09-22 14:02:54 +03:00 committed by GitHub
commit fddf086cbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 1 deletions

View File

@ -23,7 +23,13 @@ type FailoverOptions struct {
MasterName string MasterName string
// A seed list of host:port addresses of sentinel nodes. // A seed list of host:port addresses of sentinel nodes.
SentinelAddrs []string SentinelAddrs []string
// Sentinel password from "requirepass <password>" (if enabled) in Sentinel configuration
// If specified with SentinelPassword, enables ACL-based authentication (via
// AUTH <user> <pass>).
SentinelUsername string
// Sentinel password from "requirepass <password>" (if enabled) in Sentinel
// configuration, or, if SentinelUsername is also supplied, used for ACL-based
// authentication.
SentinelPassword string SentinelPassword string
// Allows routing read-only commands to the closest master or slave node. // Allows routing read-only commands to the closest master or slave node.
@ -109,6 +115,7 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
OnConnect: opt.OnConnect, OnConnect: opt.OnConnect,
DB: 0, DB: 0,
Username: opt.SentinelUsername,
Password: opt.SentinelPassword, Password: opt.SentinelPassword,
MaxRetries: opt.MaxRetries, MaxRetries: opt.MaxRetries,

View File

@ -212,3 +212,76 @@ var _ = Describe("NewFailoverClusterClient", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
}) })
}) })
var _ = Describe("SentinelAclAuth", func() {
const (
aclSentinelUsername = "sentinel-user"
aclSentinelPassword = "sentinel-pass"
)
var client *redis.Client
var sentinel *redis.SentinelClient
var sentinels = func() []*redisProcess {
return []*redisProcess{ sentinel1, sentinel2, sentinel3 }
}
BeforeEach(func() {
authCmd := redis.NewStatusCmd(ctx, "ACL", "SETUSER", aclSentinelUsername, "ON",
">" + aclSentinelPassword, "-@all", "+auth", "+client|getname", "+client|id", "+client|setname",
"+command", "+hello", "+ping", "+role", "+sentinel|get-master-addr-by-name", "+sentinel|master",
"+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels")
for _, process := range sentinels() {
err := process.Client.Process(ctx, authCmd)
Expect(err).NotTo(HaveOccurred())
}
client = redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: sentinelName,
SentinelAddrs: sentinelAddrs,
MaxRetries: -1,
SentinelUsername: aclSentinelUsername,
SentinelPassword: aclSentinelPassword,
})
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
sentinel = redis.NewSentinelClient(&redis.Options{
Addr: sentinelAddrs[0],
MaxRetries: -1,
Username: aclSentinelUsername,
Password: aclSentinelPassword,
})
_, err := sentinel.GetMasterAddrByName(ctx, sentinelName).Result()
Expect(err).NotTo(HaveOccurred())
// Wait until sentinels are picked up by each other.
for _, process := range sentinels() {
Eventually(func() string {
return process.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("sentinels=3"))
}
})
AfterEach(func() {
unauthCommand := redis.NewStatusCmd(ctx, "ACL", "DELUSER", aclSentinelUsername)
for _, process := range sentinels() {
err := process.Client.Process(ctx, unauthCommand)
Expect(err).NotTo(HaveOccurred())
}
_ = client.Close()
_ = sentinel.Close()
})
It("should still facilitate operations", func() {
err := client.Set(ctx, "wow", "acl-auth", 0).Err()
Expect(err).NotTo(HaveOccurred())
val, err := client.Get(ctx, "wow").Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("acl-auth"))
})
})