Skip to content

Commit fddf086

Browse files
authored
Merge pull request #1896 from jsievenpiper/main
feat: add acl auth support for sentinels
2 parents c575887 + f74ce7d commit fddf086

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

sentinel.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ type FailoverOptions struct {
2323
MasterName string
2424
// A seed list of host:port addresses of sentinel nodes.
2525
SentinelAddrs []string
26-
// Sentinel password from "requirepass <password>" (if enabled) in Sentinel configuration
26+
27+
// If specified with SentinelPassword, enables ACL-based authentication (via
28+
// AUTH <user> <pass>).
29+
SentinelUsername string
30+
// Sentinel password from "requirepass <password>" (if enabled) in Sentinel
31+
// configuration, or, if SentinelUsername is also supplied, used for ACL-based
32+
// authentication.
2733
SentinelPassword string
2834

2935
// Allows routing read-only commands to the closest master or slave node.
@@ -109,6 +115,7 @@ func (opt *FailoverOptions) sentinelOptions(addr string) *Options {
109115
OnConnect: opt.OnConnect,
110116

111117
DB: 0,
118+
Username: opt.SentinelUsername,
112119
Password: opt.SentinelPassword,
113120

114121
MaxRetries: opt.MaxRetries,

sentinel_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,3 +212,76 @@ var _ = Describe("NewFailoverClusterClient", func() {
212212
Expect(err).NotTo(HaveOccurred())
213213
})
214214
})
215+
216+
var _ = Describe("SentinelAclAuth", func() {
217+
const (
218+
aclSentinelUsername = "sentinel-user"
219+
aclSentinelPassword = "sentinel-pass"
220+
)
221+
222+
var client *redis.Client
223+
var sentinel *redis.SentinelClient
224+
var sentinels = func() []*redisProcess {
225+
return []*redisProcess{ sentinel1, sentinel2, sentinel3 }
226+
}
227+
228+
BeforeEach(func() {
229+
authCmd := redis.NewStatusCmd(ctx, "ACL", "SETUSER", aclSentinelUsername, "ON",
230+
">" + aclSentinelPassword, "-@all", "+auth", "+client|getname", "+client|id", "+client|setname",
231+
"+command", "+hello", "+ping", "+role", "+sentinel|get-master-addr-by-name", "+sentinel|master",
232+
"+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels")
233+
234+
for _, process := range sentinels() {
235+
err := process.Client.Process(ctx, authCmd)
236+
Expect(err).NotTo(HaveOccurred())
237+
}
238+
239+
client = redis.NewFailoverClient(&redis.FailoverOptions{
240+
MasterName: sentinelName,
241+
SentinelAddrs: sentinelAddrs,
242+
MaxRetries: -1,
243+
SentinelUsername: aclSentinelUsername,
244+
SentinelPassword: aclSentinelPassword,
245+
})
246+
247+
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
248+
249+
sentinel = redis.NewSentinelClient(&redis.Options{
250+
Addr: sentinelAddrs[0],
251+
MaxRetries: -1,
252+
Username: aclSentinelUsername,
253+
Password: aclSentinelPassword,
254+
})
255+
256+
_, err := sentinel.GetMasterAddrByName(ctx, sentinelName).Result()
257+
Expect(err).NotTo(HaveOccurred())
258+
259+
// Wait until sentinels are picked up by each other.
260+
for _, process := range sentinels() {
261+
Eventually(func() string {
262+
return process.Info(ctx).Val()
263+
}, "15s", "100ms").Should(ContainSubstring("sentinels=3"))
264+
}
265+
})
266+
267+
AfterEach(func() {
268+
unauthCommand := redis.NewStatusCmd(ctx, "ACL", "DELUSER", aclSentinelUsername)
269+
270+
for _, process := range sentinels() {
271+
err := process.Client.Process(ctx, unauthCommand)
272+
Expect(err).NotTo(HaveOccurred())
273+
}
274+
275+
_ = client.Close()
276+
_ = sentinel.Close()
277+
})
278+
279+
It("should still facilitate operations", func() {
280+
err := client.Set(ctx, "wow", "acl-auth", 0).Err()
281+
Expect(err).NotTo(HaveOccurred())
282+
283+
val, err := client.Get(ctx, "wow").Result()
284+
Expect(err).NotTo(HaveOccurred())
285+
Expect(val).To(Equal("acl-auth"))
286+
})
287+
})

0 commit comments

Comments
 (0)