git.iko.soy/typeable/oauth2-proxy
on master help sign in
Add ability to configure username for Redis cluster connections (#2381)
f3dbca6Ross Golder
CHANGELOG.md+1
@@ -8,6 +8,7 @@
8
8
9## Changes since v7.5.1
9## Changes since v7.5.1
10
10
11+- [#2381](https://github.com/oauth2-proxy/oauth2-proxy/pull/2381) Allow username authentication to Redis cluster (@rossigee)
11- [#2345](https://github.com/oauth2-proxy/oauth2-proxy/pull/2345) Log error details when failed loading CSRF cookie (@charvadzo)
12- [#2345](https://github.com/oauth2-proxy/oauth2-proxy/pull/2345) Log error details when failed loading CSRF cookie (@charvadzo)
12- [#2128](https://github.com/oauth2-proxy/oauth2-proxy/pull/2128) Update dependencies (@vllvll)
13- [#2128](https://github.com/oauth2-proxy/oauth2-proxy/pull/2128) Update dependencies (@vllvll)
13- [#2269](https://github.com/oauth2-proxy/oauth2-proxy/pull/2269) Added Azure China (and other air gaped cloud) support (@mblaschke)
14- [#2269](https://github.com/oauth2-proxy/oauth2-proxy/pull/2269) Added Azure China (and other air gaped cloud) support (@mblaschke)
pkg/apis/options/options.go+4−3
@@ -144,16 +144,17 @@ func NewFlagSet() *pflag.FlagSet {
144 flagSet.String("ready-path", "/ready", "the ready endpoint that can be used for deep health checks")
144 flagSet.String("ready-path", "/ready", "the ready endpoint that can be used for deep health checks")
145 flagSet.String("session-store-type", "cookie", "the session storage provider to use")
145 flagSet.String("session-store-type", "cookie", "the session storage provider to use")
146 flagSet.Bool("session-cookie-minimal", false, "strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only)")
146 flagSet.Bool("session-cookie-minimal", false, "strip OAuth tokens from cookie session stores if they aren't needed (cookie session store only)")
147− flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://HOST[:PORT])")
147+ flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://[USER[:PASSWORD]@]HOST[:PORT])")
148+ flagSet.String("redis-username", "", "Redis username. Applicable for Redis configurations where ACL has been configured. Will override any username set in `--redis-connection-url`")
148 flagSet.String("redis-password", "", "Redis password. Applicable for all Redis configurations. Will override any password set in `--redis-connection-url`")
149 flagSet.String("redis-password", "", "Redis password. Applicable for all Redis configurations. Will override any password set in `--redis-connection-url`")
149 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")
150 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")
150 flagSet.String("redis-sentinel-password", "", "Redis sentinel password. Used only for sentinel connection; any redis node passwords need to use `--redis-password`")
151 flagSet.String("redis-sentinel-password", "", "Redis sentinel password. Used only for sentinel connection; any redis node passwords need to use `--redis-password`")
151 flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
152 flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
152 flagSet.String("redis-ca-path", "", "Redis custom CA path")
153 flagSet.String("redis-ca-path", "", "Redis custom CA path")
153 flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
154 flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
154− flagSet.StringSlice("redis-sentinel-connection-urls", []string{}, "List of Redis sentinel connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
155+ flagSet.StringSlice("redis-sentinel-connection-urls", []string{}, "List of Redis sentinel connection URLs (eg redis://[USER[:PASSWORD]@]HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
155 flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
156 flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
156− flagSet.StringSlice("redis-cluster-connection-urls", []string{}, "List of Redis cluster connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-cluster")
157+ flagSet.StringSlice("redis-cluster-connection-urls", []string{}, "List of Redis cluster connection URLs (eg redis://[USER[:PASSWORD]@]HOST[:PORT]). Used in conjunction with --redis-use-cluster")
157 flagSet.Int("redis-connection-idle-timeout", 0, "Redis connection idle timeout seconds, if Redis timeout option is non-zero, the --redis-connection-idle-timeout must be less then Redis timeout option")
158 flagSet.Int("redis-connection-idle-timeout", 0, "Redis connection idle timeout seconds, if Redis timeout option is non-zero, the --redis-connection-idle-timeout must be less then Redis timeout option")
158 flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
159 flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
159 flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
160 flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
pkg/apis/options/sessions.go+1
@@ -23,6 +23,7 @@ type CookieStoreOptions struct {
23// RedisStoreOptions contains configuration options for the RedisSessionStore.
23// RedisStoreOptions contains configuration options for the RedisSessionStore.
24type RedisStoreOptions struct {
24type RedisStoreOptions struct {
25 ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
25 ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
26+ Username string `flag:"redis-username" cfg:"redis_username"`
26 Password string `flag:"redis-password" cfg:"redis_password"`
27 Password string `flag:"redis-password" cfg:"redis_password"`
27 UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
28 UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
28 SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
29 SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
pkg/sessions/redis/redis_store.go+19
@@ -100,6 +100,13 @@ func buildSentinelClient(opts options.RedisStoreOptions) (Client, error) {
100 return nil, fmt.Errorf("could not parse redis urls: %v", err)
100 return nil, fmt.Errorf("could not parse redis urls: %v", err)
101 }
101 }
102
102
103+ if opts.Password != "" {
104+ opt.Password = opts.Password
105+ }
106+ if opts.Username != "" {
107+ opt.Username = opts.Username
108+ }
109+
103 if err := setupTLSConfig(opts, opt); err != nil {
110 if err := setupTLSConfig(opts, opt); err != nil {
104 return nil, err
111 return nil, err
105 }
112 }
@@ -108,6 +115,7 @@ func buildSentinelClient(opts options.RedisStoreOptions) (Client, error) {
108 MasterName: opts.SentinelMasterName,
115 MasterName: opts.SentinelMasterName,
109 SentinelAddrs: addrs,
116 SentinelAddrs: addrs,
110 SentinelPassword: opts.SentinelPassword,
117 SentinelPassword: opts.SentinelPassword,
118+ Username: opts.Username,
111 Password: opts.Password,
119 Password: opts.Password,
112 TLSConfig: opt.TLSConfig,
120 TLSConfig: opt.TLSConfig,
113 ConnMaxIdleTime: time.Duration(opts.IdleTimeout) * time.Second,
121 ConnMaxIdleTime: time.Duration(opts.IdleTimeout) * time.Second,
@@ -122,12 +130,20 @@ func buildClusterClient(opts options.RedisStoreOptions) (Client, error) {
122 return nil, fmt.Errorf("could not parse redis urls: %v", err)
130 return nil, fmt.Errorf("could not parse redis urls: %v", err)
123 }
131 }
124
132
133+ if opts.Password != "" {
134+ opt.Password = opts.Password
135+ }
136+ if opts.Username != "" {
137+ opt.Username = opts.Username
138+ }
139+
125 if err := setupTLSConfig(opts, opt); err != nil {
140 if err := setupTLSConfig(opts, opt); err != nil {
126 return nil, err
141 return nil, err
127 }
142 }
128
143
129 client := redis.NewClusterClient(&redis.ClusterOptions{
144 client := redis.NewClusterClient(&redis.ClusterOptions{
130 Addrs: addrs,
145 Addrs: addrs,
146+ Username: opts.Username,
131 Password: opts.Password,
147 Password: opts.Password,
132 TLSConfig: opt.TLSConfig,
148 TLSConfig: opt.TLSConfig,
133 ConnMaxIdleTime: time.Duration(opts.IdleTimeout) * time.Second,
149 ConnMaxIdleTime: time.Duration(opts.IdleTimeout) * time.Second,
@@ -146,6 +162,9 @@ func buildStandaloneClient(opts options.RedisStoreOptions) (Client, error) {
146 if opts.Password != "" {
162 if opts.Password != "" {
147 opt.Password = opts.Password
163 opt.Password = opts.Password
148 }
164 }
165+ if opts.Username != "" {
166+ opt.Username = opts.Username
167+ }
149
168
150 if err := setupTLSConfig(opts, opt); err != nil {
169 if err := setupTLSConfig(opts, opt); err != nil {
151 return nil, err
170 return nil, err
pkg/sessions/redis/redis_store_test.go+51
@@ -18,6 +18,7 @@ import (
18 . "github.com/onsi/gomega"
18 . "github.com/onsi/gomega"
19)
19)
20
20
21+const redisUsername = "testuser"
21const redisPassword = "0123456789abcdefghijklmnopqrstuv"
22const redisPassword = "0123456789abcdefghijklmnopqrstuv"
22
23
23var (
24var (
@@ -231,6 +232,56 @@ var _ = Describe("Redis SessionStore Tests", func() {
231 })
232 })
232 })
233 })
233
234
235+ Context("with a redis username and password", func() {
236+ BeforeEach(func() {
237+ mr.RequireUserAuth(redisUsername, redisPassword)
238+ })
239+
240+ AfterEach(func() {
241+ mr.RequireUserAuth("", "")
242+ })
243+
244+ tests.RunSessionStoreTests(
245+ func(opts *options.SessionOptions, cookieOpts *options.Cookie) (sessionsapi.SessionStore, error) {
246+ // Set the connection URL
247+ opts.Type = options.RedisSessionStoreType
248+ opts.Redis.ConnectionURL = "redis://" + redisUsername + "@" + mr.Addr()
249+ opts.Redis.Password = redisPassword
250+
251+ // Capture the session store so that we can close the client
252+ var err error
253+ ss, err = NewRedisSessionStore(opts, cookieOpts)
254+ return ss, err
255+ },
256+ func(d time.Duration) error {
257+ mr.FastForward(d)
258+ return nil
259+ },
260+ )
261+
262+ Context("with cluster", func() {
263+ tests.RunSessionStoreTests(
264+ func(opts *options.SessionOptions, cookieOpts *options.Cookie) (sessionsapi.SessionStore, error) {
265+ clusterAddr := "redis://" + redisUsername + "@" + mr.Addr()
266+ opts.Type = options.RedisSessionStoreType
267+ opts.Redis.ClusterConnectionURLs = []string{clusterAddr}
268+ opts.Redis.UseCluster = true
269+ opts.Redis.Username = redisUsername
270+ opts.Redis.Password = redisPassword
271+
272+ // Capture the session store so that we can close the client
273+ var err error
274+ ss, err = NewRedisSessionStore(opts, cookieOpts)
275+ return ss, err
276+ },
277+ func(d time.Duration) error {
278+ mr.FastForward(d)
279+ return nil
280+ },
281+ )
282+ })
283+ })
284+
234 Context("with TLS connection", func() {
285 Context("with TLS connection", func() {
235 BeforeEach(func() {
286 BeforeEach(func() {
236 mr.Close()
287 mr.Close()