This commit is contained in:
Shakar Bakr 2025-10-01 22:37:45 +02:00 committed by GitHub
commit e0d5332b42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 214 additions and 3 deletions

View File

@ -8,6 +8,8 @@
## Changes since v7.12.0 ## Changes since v7.12.0
- [#3175](https://github.com/oauth2-proxy/oauth2-proxy/pull/3175) test: add comprehensive tests for Redis sentinel DB configuration (@5h4k4r)
# V7.12.0 # V7.12.0
## Release Highlights ## Release Highlights
@ -119,7 +121,7 @@ For detailed information, migration guidance, and security implications, see the
- 🕵️‍♀️ Vulnerabilities have been addressed - 🕵️‍♀️ Vulnerabilities have been addressed
- [CVE-2025-22871](https://github.com/advisories/GHSA-g9pc-8g42-g6vq) - [CVE-2025-22871](https://github.com/advisories/GHSA-g9pc-8g42-g6vq)
- 🐛 Squashed some bugs - 🐛 Squashed some bugs
## Important Notes ## Important Notes
## Breaking Changes ## Breaking Changes

View File

@ -86,8 +86,7 @@ When using the redis store, specify `--session-store-type=redis` as well as the
`--redis-connection-url=redis://host[:port][/db-number]`. `--redis-connection-url=redis://host[:port][/db-number]`.
You may also configure the store for Redis Sentinel. In this case, you will want to use the You may also configure the store for Redis Sentinel. In this case, you will want to use the
`--redis-use-sentinel=true` flag, as well as configure the flags `--redis-sentinel-master-name` `--redis-use-sentinel=true` flag, as well as configure the flags `--redis-sentinel-master-name`, `--redis-sentinel-db` and `--redis-sentinel-connection-urls` appropriately.
and `--redis-sentinel-connection-urls` appropriately.
Redis Cluster is available to be the backend store as well. To leverage it, you will need to set the Redis Cluster is available to be the backend store as well. To leverage it, you will need to set the
`--redis-use-cluster=true` flag, and configure the flags `--redis-cluster-connection-urls` appropriately. `--redis-use-cluster=true` flag, and configure the flags `--redis-cluster-connection-urls` appropriately.

View File

@ -151,6 +151,7 @@ func NewFlagSet() *pflag.FlagSet {
flagSet.String("redis-username", "", "Redis username. Applicable for Redis configurations where ACL has been configured. Will override any username set in `--redis-connection-url`") flagSet.String("redis-username", "", "Redis username. Applicable for Redis configurations where ACL has been configured. Will override any username set in `--redis-connection-url`")
flagSet.String("redis-password", "", "Redis password. Applicable for all Redis configurations. Will override any password set in `--redis-connection-url`") flagSet.String("redis-password", "", "Redis password. Applicable for all Redis configurations. Will override any password set in `--redis-connection-url`")
flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature") flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
flagSet.Int("redis-sentinel-db", 0, "Redis sentinel database number. Used in conjunction with --redis-use-sentinel")
flagSet.String("redis-sentinel-password", "", "Redis sentinel password. Used only for sentinel connection; any redis node passwords need to use `--redis-password`") flagSet.String("redis-sentinel-password", "", "Redis sentinel password. Used only for sentinel connection; any redis node passwords need to use `--redis-password`")
flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel") flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
flagSet.String("redis-ca-path", "", "Redis custom CA path") flagSet.String("redis-ca-path", "", "Redis custom CA path")

View File

@ -26,6 +26,7 @@ type RedisStoreOptions struct {
Username string `flag:"redis-username" cfg:"redis_username"` Username string `flag:"redis-username" cfg:"redis_username"`
Password string `flag:"redis-password" cfg:"redis_password"` Password string `flag:"redis-password" cfg:"redis_password"`
UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"` UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
SentinelDB int `flag:"redis-sentinel-db" cfg:"redis_sentinel_db"`
SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"` SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"` SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"`
SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"` SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"`

View File

@ -119,6 +119,7 @@ func buildSentinelClient(opts options.RedisStoreOptions) (Client, error) {
SentinelAddrs: addrs, SentinelAddrs: addrs,
SentinelPassword: opts.SentinelPassword, SentinelPassword: opts.SentinelPassword,
Username: opts.Username, Username: opts.Username,
DB: opts.SentinelDB,
Password: opts.Password, Password: opts.Password,
TLSConfig: opt.TLSConfig, TLSConfig: opt.TLSConfig,
ConnMaxIdleTime: time.Duration(opts.IdleTimeout) * time.Second, ConnMaxIdleTime: time.Duration(opts.IdleTimeout) * time.Second,

View File

@ -93,6 +93,29 @@ var _ = Describe("Redis SessionStore Tests", func() {
return nil return nil
}, },
) )
Context("with custom sentinel DB", func() {
tests.RunSessionStoreTests(
func(opts *options.SessionOptions, cookieOpts *options.Cookie) (sessionsapi.SessionStore, error) {
// Set the sentinel connection URL with custom DB
sentinelAddr := redisProtocol + ms.Addr()
opts.Type = options.RedisSessionStoreType
opts.Redis.SentinelConnectionURLs = []string{sentinelAddr}
opts.Redis.UseSentinel = true
opts.Redis.SentinelMasterName = ms.MasterInfo().Name
opts.Redis.SentinelDB = 1
// Capture the session store so that we can close the client
var err error
ss, err = NewRedisSessionStore(opts, cookieOpts)
return ss, err
},
func(d time.Duration) error {
mr.FastForward(d)
return nil
},
)
})
}) })
Context("with cluster", func() { Context("with cluster", func() {
@ -170,6 +193,30 @@ var _ = Describe("Redis SessionStore Tests", func() {
return nil return nil
}, },
) )
Context("with custom sentinel DB and password", func() {
tests.RunSessionStoreTests(
func(opts *options.SessionOptions, cookieOpts *options.Cookie) (sessionsapi.SessionStore, error) {
// Set the sentinel connection URL with custom DB and password
sentinelAddr := redisProtocol + ms.Addr()
opts.Type = options.RedisSessionStoreType
opts.Redis.SentinelConnectionURLs = []string{sentinelAddr}
opts.Redis.UseSentinel = true
opts.Redis.SentinelMasterName = ms.MasterInfo().Name
opts.Redis.Password = redisPassword
opts.Redis.SentinelDB = 2
// Capture the session store so that we can close the client
var err error
ss, err = NewRedisSessionStore(opts, cookieOpts)
return ss, err
},
func(d time.Duration) error {
mr.FastForward(d)
return nil
},
)
})
}) })
Context("with cluster", func() { Context("with cluster", func() {
@ -271,4 +318,114 @@ var _ = Describe("Redis SessionStore Tests", func() {
Expect(opts).To(BeNil()) Expect(opts).To(BeNil())
}) })
}) })
Describe("Sentinel DB Configuration", func() {
var mr *miniredis.Miniredis
var ms *minisentinel.Sentinel
BeforeEach(func() {
var err error
mr, err = miniredis.Run()
Expect(err).ToNot(HaveOccurred())
ms = minisentinel.NewSentinel(mr)
Expect(ms.Start()).To(Succeed())
})
AfterEach(func() {
mr.Close()
})
It("should use default DB 0 when SentinelDB is not set", func() {
opts := options.RedisStoreOptions{
SentinelConnectionURLs: []string{"redis://" + ms.Addr()},
UseSentinel: true,
SentinelMasterName: ms.MasterInfo().Name,
SentinelDB: 0, // Default value
}
client, err := NewRedisClient(opts)
Expect(err).ToNot(HaveOccurred())
Expect(client).ToNot(BeNil())
// Verify we can create a session store successfully
sessionStore := &SessionStore{Client: client}
Expect(sessionStore).ToNot(BeNil())
// Clean up
if closer, ok := client.(interface{ Close() error }); ok {
Expect(closer.Close()).To(Succeed())
}
})
It("should use custom SentinelDB value when set", func() {
testDB := 5
opts := options.RedisStoreOptions{
SentinelConnectionURLs: []string{"redis://" + ms.Addr()},
UseSentinel: true,
SentinelMasterName: ms.MasterInfo().Name,
SentinelDB: testDB,
}
client, err := NewRedisClient(opts)
Expect(err).ToNot(HaveOccurred())
Expect(client).ToNot(BeNil())
// Verify we can create a session store successfully
sessionStore := &SessionStore{Client: client}
Expect(sessionStore).ToNot(BeNil())
// Clean up
if closer, ok := client.(interface{ Close() error }); ok {
Expect(closer.Close()).To(Succeed())
}
})
It("should work with maximum valid DB number", func() {
maxDB := 15 // Redis supports DB 0-15 by default
opts := options.RedisStoreOptions{
SentinelConnectionURLs: []string{"redis://" + ms.Addr()},
UseSentinel: true,
SentinelMasterName: ms.MasterInfo().Name,
SentinelDB: maxDB,
}
client, err := NewRedisClient(opts)
Expect(err).ToNot(HaveOccurred())
Expect(client).ToNot(BeNil())
// Verify we can create a session store successfully
sessionStore := &SessionStore{Client: client}
Expect(sessionStore).ToNot(BeNil())
// Clean up
if closer, ok := client.(interface{ Close() error }); ok {
Expect(closer.Close()).To(Succeed())
}
})
It("should handle SentinelDB with authentication", func() {
testDB := 3
testPassword := "test-password"
opts := options.RedisStoreOptions{
SentinelConnectionURLs: []string{"redis://" + ms.Addr()},
UseSentinel: true,
SentinelMasterName: ms.MasterInfo().Name,
SentinelDB: testDB,
Password: testPassword,
}
client, err := NewRedisClient(opts)
Expect(err).ToNot(HaveOccurred())
Expect(client).ToNot(BeNil())
// Verify we can create a session store successfully
sessionStore := &SessionStore{Client: client}
Expect(sessionStore).ToNot(BeNil())
// Clean up
if closer, ok := client.(interface{ Close() error }); ok {
Expect(closer.Close()).To(Succeed())
}
})
})
}) })

View File

@ -346,6 +346,56 @@ var _ = Describe("Sessions", func() {
}, },
errStrings: []string{}, errStrings: []string{},
}), }),
Entry("connect successfully to sentinel redis with custom DB", &redisStoreTableInput{
useSentinel: true,
setSentinelAddr: true,
setMasterName: true,
opts: &options.Options{
Session: options.SessionOptions{
Type: options.RedisSessionStoreType,
Redis: options.RedisStoreOptions{
UseSentinel: true,
SentinelDB: 1,
},
},
},
errStrings: []string{},
}),
Entry("connect successfully to sentinel redis with custom DB and password", &redisStoreTableInput{
password: "abcdef123",
useSentinel: true,
setSentinelAddr: true,
setMasterName: true,
opts: &options.Options{
Session: options.SessionOptions{
Type: options.RedisSessionStoreType,
Redis: options.RedisStoreOptions{
Password: "abcdef123",
UseSentinel: true,
SentinelDB: 5,
},
},
},
errStrings: []string{},
}),
Entry("connect successfully to sentinel redis with maximum DB number", &redisStoreTableInput{
useSentinel: true,
setSentinelAddr: true,
setMasterName: true,
opts: &options.Options{
Session: options.SessionOptions{
Type: options.RedisSessionStoreType,
Redis: options.RedisStoreOptions{
UseSentinel: true,
SentinelDB: 15,
},
},
},
errStrings: []string{},
}),
Entry("failed connection to sentinel redis with wrong password", &redisStoreTableInput{ Entry("failed connection to sentinel redis with wrong password", &redisStoreTableInput{
password: "abcdef123", password: "abcdef123",
useSentinel: true, useSentinel: true,