diff --git a/CHANGELOG.md b/CHANGELOG.md
index f2c9b441..2683b090 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@
## Changes since v7.12.0
+- [#3029](https://github.com/oauth2-proxy/oauth2-proxy/pull/3029) feat: IAM auth for AWS redis (@willwill96)
+
# V7.12.0
## Release Highlights
@@ -119,7 +121,7 @@ For detailed information, migration guidance, and security implications, see the
- 🕵️♀️ Vulnerabilities have been addressed
- [CVE-2025-22871](https://github.com/advisories/GHSA-g9pc-8g42-g6vq)
- 🐛 Squashed some bugs
-
+
## Important Notes
## Breaking Changes
diff --git a/docs/docs/configuration/alpha_config.md b/docs/docs/configuration/alpha_config.md
index 28645ceb..a8cd024f 100644
--- a/docs/docs/configuration/alpha_config.md
+++ b/docs/docs/configuration/alpha_config.md
@@ -150,6 +150,18 @@ You must remove these options before starting OAuth2 Proxy with `--alpha-config`
| ----- | ---- | ----------- |
| `skipScope` | _bool_ | Skip adding the scope parameter in login request
Default value is 'false' |
+### AWSIAMConfig
+
+(**Appears on:** [AlphaOptions](#alphaoptions))
+
+
+
+| Field | Type | Description |
+| ----- | ---- | ----------- |
+| `serviceName` | _string_ | AWS service redis service being used. "elasticache" or "memorydb" |
+| `clusterName` | _string_ | AWS Cluster name |
+| `userName` | _string_ | AWS Username |
+
### AlphaOptions
AlphaOptions contains alpha structured configuration options.
@@ -169,6 +181,7 @@ They may change between releases without notice.
| `server` | _[Server](#server)_ | Server is used to configure the HTTP(S) server for the proxy application.
You may choose to run both HTTP and HTTPS servers simultaneously.
This can be done by setting the BindAddress and the SecureBindAddress simultaneously.
To use the secure server you must configure a TLS certificate and key. |
| `metricsServer` | _[Server](#server)_ | MetricsServer is used to configure the HTTP(S) server for metrics.
You may choose to run both HTTP and HTTPS servers simultaneously.
This can be done by setting the BindAddress and the SecureBindAddress simultaneously.
To use the secure server you must configure a TLS certificate and key. |
| `providers` | _[Providers](#providers)_ | Providers is used to configure your provider. **Multiple-providers is not
yet working.** [This feature is tracked in
#925](https://github.com/oauth2-proxy/oauth2-proxy/issues/926) |
+| `redisSessionAWSIAMConfig` | _[AWSIAMConfig](#awsiamconfig)_ | AWS IAM Options is used to configure IAM authentication for your redis instance. |
### AzureOptions
diff --git a/go.mod b/go.mod
index 24f316e4..31c9412a 100644
--- a/go.mod
+++ b/go.mod
@@ -45,6 +45,20 @@ require (
require (
cloud.google.com/go/auth v0.16.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
+ github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
+ github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
+ github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
+ github.com/aws/smithy-go v1.22.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
diff --git a/go.sum b/go.sum
index caa8e2a0..3da39083 100644
--- a/go.sum
+++ b/go.sum
@@ -14,6 +14,42 @@ github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGn
github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE=
github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI=
github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
+github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
+github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
+github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0=
+github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U=
+github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM=
+github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
+github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY=
+github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
+github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
+github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
diff --git a/pkg/apis/options/alpha_options.go b/pkg/apis/options/alpha_options.go
index a438518c..2f54d73d 100644
--- a/pkg/apis/options/alpha_options.go
+++ b/pkg/apis/options/alpha_options.go
@@ -45,6 +45,9 @@ type AlphaOptions struct {
// yet working.** [This feature is tracked in
// #925](https://github.com/oauth2-proxy/oauth2-proxy/issues/926)
Providers Providers `json:"providers,omitempty"`
+
+ // AWS IAM Options is used to configure IAM authentication for your redis instance.
+ RedisSessionAWSIAMConfig *AWSIAMConfig `json:"redisSessionAWSIAMConfig,omitempty"`
}
// MergeInto replaces alpha options in the Options struct with the values
@@ -56,6 +59,7 @@ func (a *AlphaOptions) MergeInto(opts *Options) {
opts.Server = a.Server
opts.MetricsServer = a.MetricsServer
opts.Providers = a.Providers
+ opts.Session.Redis.AWSIAMConfig = a.RedisSessionAWSIAMConfig
}
// ExtractFrom populates the fields in the AlphaOptions with the values from
@@ -67,4 +71,5 @@ func (a *AlphaOptions) ExtractFrom(opts *Options) {
a.Server = opts.Server
a.MetricsServer = opts.MetricsServer
a.Providers = opts.Providers
+ a.RedisSessionAWSIAMConfig = opts.Session.Redis.AWSIAMConfig
}
diff --git a/pkg/apis/options/aws_iam.go b/pkg/apis/options/aws_iam.go
new file mode 100644
index 00000000..534ba718
--- /dev/null
+++ b/pkg/apis/options/aws_iam.go
@@ -0,0 +1,10 @@
+package options
+
+type AWSIAMConfig struct {
+ // AWS service redis service being used. "elasticache" or "memorydb"
+ ServiceName string `json:"serviceName,omitempty"`
+ // AWS Cluster name
+ ClusterName string `json:"clusterName,omitempty"`
+ // AWS Username
+ Username string `json:"userName,omitempty"`
+}
diff --git a/pkg/apis/options/options.go b/pkg/apis/options/options.go
index 8fa72c7c..9e4b6369 100644
--- a/pkg/apis/options/options.go
+++ b/pkg/apis/options/options.go
@@ -159,6 +159,7 @@ func NewFlagSet() *pflag.FlagSet {
flagSet.Bool("redis-use-cluster", false, "Connect to redis cluster. Must set --redis-cluster-connection-urls to use this feature")
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")
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")
+
flagSet.String("signature-key", "", "GAP-Signature request signature key (algorithm:secretkey)")
flagSet.Bool("gcp-healthchecks", false, "Enable GCP/GKE healthcheck endpoints")
diff --git a/pkg/apis/options/sessions.go b/pkg/apis/options/sessions.go
index c90c0ac2..d0085ff5 100644
--- a/pkg/apis/options/sessions.go
+++ b/pkg/apis/options/sessions.go
@@ -22,18 +22,19 @@ type CookieStoreOptions struct {
// RedisStoreOptions contains configuration options for the RedisSessionStore.
type RedisStoreOptions struct {
- ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
- Username string `flag:"redis-username" cfg:"redis_username"`
- Password string `flag:"redis-password" cfg:"redis_password"`
- UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
- SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
- SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"`
- SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"`
- UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster"`
- ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls"`
- CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path"`
- InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify"`
- IdleTimeout int `flag:"redis-connection-idle-timeout" cfg:"redis_connection_idle_timeout"`
+ ConnectionURL string `flag:"redis-connection-url" cfg:"redis_connection_url"`
+ Username string `flag:"redis-username" cfg:"redis_username"`
+ Password string `flag:"redis-password" cfg:"redis_password"`
+ UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel"`
+ SentinelPassword string `flag:"redis-sentinel-password" cfg:"redis_sentinel_password"`
+ SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name"`
+ SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls"`
+ UseCluster bool `flag:"redis-use-cluster" cfg:"redis_use_cluster"`
+ ClusterConnectionURLs []string `flag:"redis-cluster-connection-urls" cfg:"redis_cluster_connection_urls"`
+ CAPath string `flag:"redis-ca-path" cfg:"redis_ca_path"`
+ InsecureSkipTLSVerify bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify"`
+ IdleTimeout int `flag:"redis-connection-idle-timeout" cfg:"redis_connection_idle_timeout"`
+ AWSIAMConfig *AWSIAMConfig `cfg:",internal"`
}
func sessionOptionsDefaults() SessionOptions {
diff --git a/pkg/sessions/redis/aws-iam/auth.go b/pkg/sessions/redis/aws-iam/auth.go
new file mode 100644
index 00000000..8d86a1f2
--- /dev/null
+++ b/pkg/sessions/redis/aws-iam/auth.go
@@ -0,0 +1,105 @@
+package auth
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/url"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/aws/aws-sdk-go-v2/aws"
+ v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
+ "github.com/aws/aws-sdk-go-v2/config"
+)
+
+// This code was largely copied from this repo: https://github.com/build-on-aws/aws-redis-iam-auth-golang/tree/main
+
+const (
+ // "The IAM authentication token is valid for 15 minutes"
+ // https://docs.aws.amazon.com/memorydb/latest/devguide/auth-iam.html#auth-iam-limits
+ tokenValiditySeconds = 900
+
+ connectAction = "connect"
+
+ // If the request has no payload you should use the hex encoded SHA-256 of an empty string as the payloadHash value.
+ hexEncodedSHA256EmptyString = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+)
+
+type TokenGenerator interface {
+ GenerateToken() (string, error)
+}
+
+// IAMTokenGenerator generates an IAM token for AWS Redis authentication.
+type iamTokenGenerator struct {
+ serviceName string
+ region string
+ req *http.Request
+
+ credentialsProvider aws.CredentialsProvider
+ signer *v4.Signer
+}
+
+// New creates a new IAMTokenGenerator instance
+func New(serviceName, clusterName, userName string) (TokenGenerator, error) {
+
+ ctx := context.Background()
+ cfg, err := config.LoadDefaultConfig(ctx)
+
+ if err != nil {
+ return nil, err
+ }
+ queryParams := url.Values{
+ "Action": {connectAction},
+ "User": {userName},
+ "X-Amz-Expires": {strconv.FormatInt(int64(tokenValiditySeconds), 10)},
+ }
+
+ authURL := url.URL{
+ Host: clusterName,
+ Scheme: "http",
+ Path: "/",
+ RawQuery: queryParams.Encode(),
+ }
+
+ req, err := http.NewRequest(http.MethodGet, authURL.String(), nil)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return &iamTokenGenerator{
+ serviceName: serviceName,
+ region: cfg.Region,
+ req: req,
+ credentialsProvider: cfg.Credentials,
+ signer: v4.NewSigner(),
+ }, nil
+}
+
+func (atg iamTokenGenerator) GenerateToken() (string, error) {
+ ctx := context.Background()
+ credentials, err := atg.credentialsProvider.Retrieve(ctx)
+ if err != nil {
+ return "", fmt.Errorf("AWS IAM credentials retrieval failed - %v", err)
+ }
+ signedURL, _, err := atg.signer.PresignHTTP(
+ ctx,
+ credentials,
+ atg.req,
+ hexEncodedSHA256EmptyString,
+ atg.serviceName,
+ atg.region,
+ time.Now().UTC(),
+ )
+
+ if err != nil {
+ return "", fmt.Errorf("AWS IAM request signing failed - %v", err)
+ }
+ // AWS expects the scheme to be removed before using as an auth token
+ // https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/auth-iam.html#auth-iam-Connecting
+ signedURL = strings.Replace(signedURL, "http://", "", 1)
+
+ return signedURL, nil
+}
diff --git a/pkg/sessions/redis/aws-iam/auth_test.go b/pkg/sessions/redis/aws-iam/auth_test.go
new file mode 100644
index 00000000..3b19ff20
--- /dev/null
+++ b/pkg/sessions/redis/aws-iam/auth_test.go
@@ -0,0 +1,35 @@
+package auth
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestAWSIAMTokenGenerator(t *testing.T) {
+ // Set up the environment, so we don't make any external calls to AWS
+ t.Setenv("AWS_CONFIG_FILE", "file_not_exists")
+ t.Setenv("AWS_SHARED_CREDENTIALS_FILE", "file_not_exists")
+ t.Setenv("AWS_ENDPOINT_URL", "http://localhost:9999/aws")
+ t.Setenv("AWS_ACCESS_KEY_ID", "access_key")
+ t.Setenv("AWS_SECRET_ACCESS_KEY", "secret_key")
+ t.Setenv("AWS_REGION", "us-east-1")
+
+ tokenGenerator, err := New("elasticache", "test-cluster", "test-user")
+ require.NotNil(t, tokenGenerator)
+ require.NoError(t, err)
+
+ token, err := tokenGenerator.GenerateToken()
+ require.NoError(t, err)
+ require.NotEmpty(t, token)
+ require.Contains(t, token, "X-Amz-Algorithm", "signed token should contain algorithm attribute")
+ require.Contains(t, token, "User=test-user", "signed token should contain user parameter")
+ require.Contains(t, token, "X-Amz-Credential", "signed token should contain credential attribute")
+ require.Contains(t, token, "X-Amz-Date", "signed token should contain date attribute")
+ require.Contains(t, token, "X-Amz-Expires", "signed token should contain expires attribute")
+ require.Contains(t, token, "X-Amz-SignedHeaders", "signed token should contain signed headers attribute")
+ require.Contains(t, token, "X-Amz-Signature", "signed token should contain signature attribute")
+ require.Contains(t, token, "Action=connect", "signed token should contain connect action")
+ require.False(t, strings.HasPrefix(token, "http://"), "token should not have http:// scheme")
+}
diff --git a/pkg/sessions/redis/redis_store.go b/pkg/sessions/redis/redis_store.go
index 4e846e9b..a9775740 100644
--- a/pkg/sessions/redis/redis_store.go
+++ b/pkg/sessions/redis/redis_store.go
@@ -12,6 +12,7 @@ import (
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/persistence"
+ auth "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/redis/aws-iam"
"github.com/redis/go-redis/v9"
)
@@ -173,12 +174,39 @@ func buildStandaloneClient(opts options.RedisStoreOptions) (Client, error) {
return nil, err
}
+ if err := setupAWSIAMAuth(opts, opt); err != nil {
+ return nil, err
+ }
+
opt.ConnMaxIdleTime = time.Duration(opts.IdleTimeout) * time.Second
client := redis.NewClient(opt)
return newClient(client), nil
}
+func setupAWSIAMAuth(opts options.RedisStoreOptions, opt *redis.Options) error {
+ if opts.AWSIAMConfig == nil {
+ return nil
+ }
+ if opts.AWSIAMConfig.ServiceName != "elasticache" && opts.AWSIAMConfig.ServiceName != "memorydb" {
+ return fmt.Errorf("AWS IAM auth is only supported for elasticache and memorydb")
+ }
+ generator, err := auth.New(opts.AWSIAMConfig.ServiceName, opts.AWSIAMConfig.ClusterName, opts.AWSIAMConfig.Username)
+ if err != nil {
+ return fmt.Errorf("error creating AWS IAM auth token generator: %v", err)
+ }
+ opt.CredentialsProvider = func() (username string, password string) {
+ token, err := generator.GenerateToken()
+ if err != nil {
+ logger.Errorf("error generating AWS IAM auth token: %v", err)
+ }
+ return opts.AWSIAMConfig.Username, token
+ }
+ // AWS services has a max connection lifetime of 12 hours. This is set to 11 hours to give some buffer time
+ opt.ConnMaxLifetime = 11 * time.Hour
+ return nil
+}
+
// setupTLSConfig sets the TLSConfig if the TLS option is given in redis.Options
func setupTLSConfig(opts options.RedisStoreOptions, opt *redis.Options) error {
if opts.InsecureSkipTLSVerify {
diff --git a/pkg/sessions/redis/redis_store_test.go b/pkg/sessions/redis/redis_store_test.go
index 1bff6855..ca0af0ef 100644
--- a/pkg/sessions/redis/redis_store_test.go
+++ b/pkg/sessions/redis/redis_store_test.go
@@ -11,6 +11,7 @@ import (
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/sessions/tests"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
+ "github.com/redis/go-redis/v9"
)
const (
@@ -271,4 +272,39 @@ var _ = Describe("Redis SessionStore Tests", func() {
Expect(opts).To(BeNil())
})
})
+
+ Describe("AWSIAMAuth", func() {
+ Context("with AWS IAM options", func() {
+ It("should initialize CredentialsProvider when AWSIAMConfig is present", func() {
+ redisOpts := options.RedisStoreOptions{
+ AWSIAMConfig: &options.AWSIAMConfig{
+ ServiceName: "elasticache",
+ ClusterName: "test-cluster",
+ Username: "test-user",
+ },
+ }
+
+ var opt = &redis.Options{}
+
+ err := setupAWSIAMAuth(redisOpts, opt)
+ Expect(err).ToNot(HaveOccurred())
+
+ Expect(opt.CredentialsProvider).ToNot(BeNil())
+
+ username, _ := opt.CredentialsProvider()
+ Expect(username).To(Equal("test-user"))
+ })
+ It("should not initialize CredentialsProvider when AWSIAMConfig is nil", func() {
+ redisOpts := options.RedisStoreOptions{
+ AWSIAMConfig: nil,
+ }
+
+ var opt = &redis.Options{}
+
+ err := setupAWSIAMAuth(redisOpts, opt)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opt.CredentialsProvider).To(BeNil())
+ })
+ })
+ })
})