Guide · AWS MemoryDB · Access Control
MemoryDB ACL User Management for MCP Servers
MemoryDB requires ACL authentication for every cluster — there is no unauthenticated mode. ACL users combine an authentication string (password) with an access string (permission grammar that scopes commands and keyspaces). This guide walks through creating minimum-privilege users for MCP server probe writers, session readers, and admin operations, the CDK constructs to manage them, and the failure modes that trip up most teams.
How MemoryDB ACLs work
MemoryDB implements the Redis 6 ACL model, but with important differences from self-managed Redis ACLs: ACL users are defined as AWS resources (CfnUser), grouped into an ACL (CfnACL), and the ACL is associated with a cluster (CfnCluster). Changes to users propagate through the ACL to the cluster — you don't configure ACLs on the cluster directly.
Authentication string
The authentication string controls whether the user can log in and what password they use. The format follows Redis 6 ACL SETUSER syntax but is passed to AWS as a JSON field:
"on resetpassword >MyS3cr3tP@ssword"
on— enables the user (without this, the user exists but cannot authenticate)resetpassword— clears any previously set passwords before applying the new one (prevents password accumulation on updates)>MyS3cr3tP@ssword— sets the password (the>prefix is Redis ACL syntax for "add this password")
The on flag is the most commonly missed element. A user created with an authentication string that omits on will be in ACTIVE state in the AWS console but will return WRONGPASS on every login attempt — the cluster rejects the user because the account is disabled.
Access string
The access string controls what the user can do after login. It uses Redis ACL permission grammar:
# Full access (for admin/migration user only)
"on resetpassword >AdminP@ss ~* &* +@all"
# Probe writer: HSET, HGET, EXPIRE, TTL on probe keyspace only
"on resetpassword >ProbeWriterP@ss ~{*}:probe:* ~{*}:session:* +hset +hget +hmget +hmset +hgetall +del +expire +ttl +pttl +exists"
# Probe reader: read-only on probe keyspace
"on resetpassword >ProbeReaderP@ss ~{*}:probe:* +hget +hmget +hgetall +ttl +pttl +exists"
Access string grammar rules:
~pattern— allows access to keys matching this glob pattern. Multiple~patterns are ORed together.~*allows all keys.+command— allows a specific command.+@allallows all commands.+@read,+@write,+@string,+@hashare category aliases.-command— denies a command. Useful to deny dangerous commands after a broad allow:+@all -@dangerous.&pattern— allows access to Pub/Sub channels matching the pattern. Required if your MCP server uses SUBSCRIBE/PUBLISH.- Commands not in the access string are denied by default — the model is allowlist, not denylist.
CDK CfnUser and CfnACL setup
Store authentication strings (passwords) in Secrets Manager, not hardcoded in CDK. The CDK construct takes the authentication string as a plain string, so construct it at deploy time by reading from Secrets Manager via a CDK custom resource or pass it via SSM Parameter Store.
import { aws_memorydb as memorydb, aws_secretsmanager as sm } from 'aws-cdk-lib';
// Store the password in Secrets Manager
const probeWriterSecret = new sm.Secret(this, 'ProbeWriterSecret', {
secretName: '/mcp-probes/memorydb/probe-writer',
generateSecretString: {
excludePunctuation: true, // Redis passwords can't contain spaces
passwordLength: 32,
},
});
// MemoryDB user: probe writer
// Authentication string is the Redis ACL SETUSER format
// AWS requires the full string including 'on resetpassword >'
const probeWriterUser = new memorydb.CfnUser(this, 'ProbeWriterUser', {
userName: 'mcp-probe-writer',
accessString: '~{*}:probe:* ~{*}:session:* +hset +hget +hmget +hmset +hgetall +del +expire +ttl +pttl +exists +incr +incrby +decr +zadd +zrangebyscore',
authenticationMode: {
type: 'password',
passwords: [probeWriterSecret.secretValue.unsafeUnwrap()],
},
});
// MemoryDB user: probe reader (dashboard / analytics service)
const probeReaderSecret = new sm.Secret(this, 'ProbeReaderSecret', {
secretName: '/mcp-probes/memorydb/probe-reader',
generateSecretString: {
excludePunctuation: true,
passwordLength: 32,
},
});
const probeReaderUser = new memorydb.CfnUser(this, 'ProbeReaderUser', {
userName: 'mcp-probe-reader',
accessString: '~{*}:probe:* ~{*}:session:* +hget +hmget +hgetall +ttl +pttl +exists +zrangebyscore +zcard +zcount',
authenticationMode: {
type: 'password',
passwords: [probeReaderSecret.secretValue.unsafeUnwrap()],
},
});
// MemoryDB ACL: group users
const acl = new memorydb.CfnACL(this, 'MemoryDbAcl', {
aclName: 'mcp-probes-acl',
userNames: [probeWriterUser.userName, probeReaderUser.userName],
// Note: 'default' user is NOT added here — removing it from the ACL
// is how you disable unauthenticated access
});
// Reference the ACL in your cluster
// cluster.aclName = acl.aclName (set in CfnCluster definition)
The default user trap
Every MemoryDB cluster starts with a built-in default user. This user has no password and full access. If your ACL includes the default user, anyone who can reach port 6379 can read and write without credentials. You must either:
- Option A (recommended): Do not include the
defaultuser in your ACL'suserNameslist. Thedefaultuser exists at the AWS account level but is inert when not included in an active ACL. - Option B: Modify the
defaultuser's access string tooff nopass ~* +@all— theoffflag disables the user while keeping it in the ACL structure.
The default user trap is the most common MemoryDB security misconfiguration in production. The AWS MemoryDB default ACL (open-access) includes the default user and is intentionally insecure — it's provided for development and must be replaced before any production workload.
Password rotation
MemoryDB supports up to two active passwords per user. The rotation strategy uses this dual-password window:
- Generate new password, add it to the user alongside the existing password:
authenticationMode.passwords = [oldPassword, newPassword] - Deploy the new password to all MCP server instances (update Secrets Manager, trigger a rolling restart or connection pool refresh)
- Remove the old password:
authenticationMode.passwords = [newPassword]
This ensures no connection interruption during rotation — during the transition window both passwords are valid. The Lambda rotation function in Secrets Manager can implement this pattern via the MemoryDB UpdateUser API.
# AWS CLI: add new password to user (dual-password window)
aws memorydb update-user \
--user-name mcp-probe-writer \
--authentication-mode '{"Type":"password","Passwords":["oldPassword","newPassword"]}'
# After deploying new password to all app instances, remove old password
aws memorydb update-user \
--user-name mcp-probe-writer \
--authentication-mode '{"Type":"password","Passwords":["newPassword"]}'
After updating a user, MemoryDB propagates the change to the cluster asynchronously. The user status transitions to MODIFYING briefly; existing connections are not dropped during this window. New connections using the new password are accepted as soon as propagation completes (typically under 30 seconds).
TLS enforcement
ACL authentication without TLS is insecure — passwords transmit in plaintext. Always set tlsEnabled: true in your CfnCluster. MemoryDB enforces TLS at the cluster level, not the user level, so a single cluster setting protects all ACL users.
The MemoryDB TLS certificate is signed by Amazon's internal CA. Most Redis clients handle this transparently with tls: {} or ssl: True options. If your client raises certificate validation errors, it may be trying to validate against the public CA bundle — configure it to accept the Amazon CA or disable hostname verification only as a temporary debug step (never in production).
TLS port is still 6379 in MemoryDB (not 6380 as in some self-managed Redis setups). The security group rule does not change between TLS and non-TLS configurations.
| tlsEnabled setting | Effect | Use case |
|---|---|---|
| true (required) | All connections must use TLS; plaintext connections rejected | Any workload where ACL passwords are sensitive |
| false (not recommended) | Plaintext connections accepted; ACL auth still required but password exposed on network | Development/local VPC testing only |
ACL update lifecycle
When you add or remove users from a CfnACL, MemoryDB propagates the change to associated clusters. The ACL transitions through states:
- CREATING → being set up (cluster cannot use it yet)
- ACTIVE → healthy, associated with one or more clusters
- MODIFYING → a user was added/removed; propagating to clusters (existing connections work; new connections get updated permissions)
- DELETING → being removed (must dissociate from all clusters first)
The MODIFYING state is not an error — it's expected during routine user rotation. CloudFormation waits for ACTIVE before marking the resource update complete. If an ACL stays in MODIFYING for more than 15 minutes, check the cluster's status: a cluster in UPDATING state can hold an ACL in MODIFYING.
Monitoring ACL authentication failures
MemoryDB emits a CloudWatch metric AuthenticationErrors per cluster. A non-zero baseline is normal during deployments when old pods reconnect with outdated credentials. A sustained non-zero value after a deployment completes indicates a misconfiguration — a pod was not restarted, a password in Secrets Manager doesn't match the ACL user, or the ACL update hasn't propagated.
// CDK CloudWatch alarm for sustained authentication errors
import { aws_cloudwatch as cloudwatch } from 'aws-cdk-lib';
new cloudwatch.Alarm(this, 'MemoryDbAuthErrorAlarm', {
metric: new cloudwatch.Metric({
namespace: 'AWS/MemoryDB',
metricName: 'AuthenticationErrors',
dimensionsMap: { ClusterName: 'mcp-probes' },
statistic: 'Sum',
period: cdk.Duration.minutes(5),
}),
threshold: 10,
evaluationPeriods: 3, // 15 minutes of sustained errors
datapointsToAlarm: 3,
comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD,
alarmDescription: 'MemoryDB authentication errors — check ACL user credentials in Secrets Manager',
treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
});
How AliveMCP uses MemoryDB ACLs in its own probe infrastructure
AliveMCP runs its probe state store on MemoryDB with three ACL user tiers, mirroring the pattern above:
- probe-writer: restricted to
~{*}:probe:*keyspace with HSET, EXPIRE, INCR — each probe worker writes its verdict and increments counters but cannot read other probes' state. - probe-reader: read-only on
~{*}:probe:*— dashboard queries and alert evaluation use this user; a compromised read path cannot corrupt probe state. - archiver: read-only on
~{*}:probe:*plus SCAN permission — the archiver worker drains probe state to Postgres and needs key discovery; the write path explicitly denies it to prevent accidental state corruption during archival.
AliveMCP monitors your MemoryDB ACL health via the AuthenticationErrors metric — if your probe writer starts failing authentication, AliveMCP detects the degraded write path before it affects your uptime data accuracy.