Files
fleet/tools/redis-tests/elasticache/iam_auth.go
T
602f5a470b Feat 1817 add iam auth to mysql and redis (#32488)
for #1817 

# Details

This PR gives Fleet servers the ability to connect to RDS MySQL and
Elasticache Redis via AWS [Identity and Access Management
(IAM)](https://aws.amazon.com/iam/). It is based almost entirely on the
work of @titanous, branched from his [original pull
request](https://github.com/fleetdm/fleet/pull/31075). The main
differences between his branch and this are:

1. Removal of auto-detection of AWS region (and cache name for
Elasticache) in favor of specifying these values in configuration. The
auto-detection is admittedly handy but parsing AWS host URLs is not
considered a best practice.
2. Relying on the existence of these new configs to determine whether or
not to connect via IAM. This sidesteps a thorny issue of whether to try
an IAM-based Elasticache connection when a password is not supplied,
since this is technically a valid setup.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [X] Added/updated automated tests
- [X] QA'd all new/changed functionality manually - besides using
@titanous's excellent test tool, I verified the following end-to-end:
  - [X] regular (non RDS) MySQL connection
  - [X] RDS MySQL connection using username/password
  - [X] RDS MySQL connection using IAM (no role)
  - [X] RDS MySQL connection using IAM (assuming role)
  - [X] regular (non Elasticache) Redis connection
  - [X] Elasticache Redis connection using username/password
  - [X] Elasticache Redis connection using NO password (without IAM)
  - [X] Elasticache Redis connection using IAM (no role)
  - [X] Elasticache Redis connection using IAM (assuming role)

---------

Co-authored-by: Jonathan Rudenberg <jonathan@titanous.com>
Co-authored-by: Noah Talerman <47070608+noahtalerman@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-09-04 10:08:47 -05:00

75 lines
2.0 KiB
Go

//nolint:gocritic // Test tool, not production code
package main
import (
"flag"
"log"
"github.com/fleetdm/fleet/v4/server/datastore/redis"
redigo "github.com/gomodule/redigo/redis"
)
var (
addrFlag = flag.String("addr", "", "ElastiCache endpoint address, including port")
userFlag = flag.String("user", "", "Username for authentication")
passwordFlag = flag.String("pass", "", "Password for authentication")
useTLS = flag.Bool("tls", false, "Whether or not to use TLS")
assumeRoleFlag = flag.String("assume-role", "", "STS assume role ARN (optional)")
externalIDFlag = flag.String("external-id", "", "STS external ID (optional)")
regionFlag = flag.String("region", "", "AWS region")
cacheNameFlag = flag.String("cache-name", "", "ElastiCache cluster name")
)
func main() {
flag.Parse()
if *addrFlag == "" {
log.Fatal("ElastiCache address is required (-addr flag)")
}
log.Printf("Connecting to ElastiCache at %s with IAM auth for user %s", *addrFlag, *userFlag)
if *assumeRoleFlag != "" {
log.Printf("Using assume role: %s", *assumeRoleFlag)
}
config := redis.PoolConfig{
Server: *addrFlag,
// UseTLS: true,
StsAssumeRoleArn: *assumeRoleFlag,
StsExternalID: *externalIDFlag,
}
if userFlag != nil && *userFlag != "" {
config.Username = *userFlag
}
if passwordFlag != nil && *passwordFlag != "" {
config.Password = *passwordFlag
}
if useTLS != nil && *useTLS {
config.UseTLS = true
}
if regionFlag != nil && *regionFlag != "" {
config.Region = *regionFlag
}
if cacheNameFlag != nil && *cacheNameFlag != "" {
config.CacheName = *cacheNameFlag
}
pool, err := redis.NewPool(config)
if err != nil {
log.Fatalf("Failed to create Redis pool: %v", err)
}
defer pool.Close()
// Test basic connection
conn := pool.Get()
defer conn.Close()
// Execute PING command
reply, err := redigo.String(conn.Do("PING"))
if err != nil {
log.Fatalf("PING failed: %v", err)
}
log.Printf("✅ PING successful: %s", reply)
}