Search code examples
node.jsmemcachedamazon-elasticache

Question about setting up a node js client into Elasticache memcached cluster with TLS enabled


I am in the midst of setting up a load generator into elasticache memcached cluster https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/WhatIs.html to test degenerate cases for distributed sts assumed role cache.

The way my load generator works is that

  1. I assume a single AWS STS role once in an ec2 instance in the same vpc where my elasticache cluster resides.
  2. I create a key value pair, where my key is a uuid and my value is the credentials serialized.
  3. I then generate n k,v pairs where n is a configuration I can provide as a command line argument.
  4. I put each k,v pair into a memcache cluster.

I use memjs https://www.npmjs.com/package/memjs to put the creds in there.

So far so good, but I am curious how I would get it to work with https://aws.amazon.com/about-aws/whats-new/2022/05/amazon-elasticache-memcached-supports-encryption-data-transit/

It looks like I do not need a custom cert chain : https://docs.aws.amazon.com/AmazonElastiCache/latest/mem-ug/in-transit-encryption-mc.html and I got the basic connection to work.

However I cannot figure out how to do this in node js. Can someone help me out with this?

The full code for my load generator is

// Script to fill up an elasticache cluster with credentials

const commandLineUsage = require('command-line-usage')
const commandLineArgs = require('command-line-args')
const optionDefinitions = [
    { name: 'clusterEndpoint', type: String, description: "Elasticache (Memcached) Cluster to set credentials into. In host:port format" },
    { name: 'clusterRegion', type: String, description: "Region where cluster lives" },
    { name: 'roleArnToAssume', type: String, description: "STS Role ARN to assume. Assumed role creds will be uploaded to Elastiache" },
    { name: 'cacheTtlInSeconds', type: Number, description: "Time to live in STS Cache" },
    { name: 'numCredsToUpload', type: Number, description: "Number of times to assume role and upload credentials" },
]

const options = commandLineArgs(optionDefinitions)
console.log("Seen args")
console.log(options)

if (options.clusterEndpoint === undefined ||
    options.roleArnToAssume === undefined ||
    options.cacheTtlInSeconds === undefined ||
    options.numCredsToUpload === undefined ||
    options.clusterRegion === undefined) {
    console.log("Invalid parameters. Please provide all options below")
    const usage = commandLineUsage([
        {
            header: 'Options',
            optionList: optionDefinitions
        }
    ])
    console.log(usage)
} else {
    const clusterEndpoint = options.clusterEndpoint
    const roleArnToAssume = options.roleArnToAssume
    const cacheTtlInSeconds = options.cacheTtlInSeconds
    const numCredsToUpload = options.numCredsToUpload
    const clusterRegion = options.clusterRegion

    // Setup memcached client
    const memjs = require('memjs')
    const memcachedClient = memjs.Client.create(clusterEndpoint)

    // Setup STS client
    const sts = require('@aws-sdk/client-sts')
    const stsClient = new sts.STSClient({ region: clusterRegion })

    const serialize = require('serialize-javascript')
    const { v4: uuidv4 } = require('uuid')
    const stsAssumeRoleCommand = new sts.AssumeRoleCommand({
        RoleArn: roleArnToAssume,
        RoleSessionName: "SessionName"
    })
    const stsAssumedRole = stsClient.send(stsAssumeRoleCommand)

    for (var i = 0; i < numCredsToUpload; i++) {
        const key = uuidv4()
        stsAssumedRole.then(
            (data) => {
                const dataToPutIn = serialize(data.Credentials)
                return memcachedClient.set(key, dataToPutIn, { expires:cacheTtlInSeconds }, (err, _) => {
                    if (err !== null) {
                        console.log("Caught error while setting creds " + err.toString())
                        throw err
                    }
                    console.log("Successfully put memcached key : " + key + " and value " + dataToPutIn)
                })
            }).catch((error) => {
                throw error
            }).finally(() => {
                // finally.
            }
        )
    }
}

and my dependency closure is

{
  "dependencies": {
    "@aws-sdk/client-sts": "^3.245.0",
    "command-line-args": "^5.2.1",
    "command-line-usage": "^6.1.3",
    "memcached": "^2.2.2",
    "memjs": "^1.3.0",
    "serialize-javascript": "^6.0.1",
    "uuid": "^8.3.2"
  }
}

I was half expecting some property in the memjs client init, but I cannot seem to find it. Is there an alternative way/mechanism to maybe listen in on this traffic and then encrypt it (if say this or any other library does not support tls).


Solution

  • There don't appear to be any nodeJS Memcached clients that support TLS at the moment, based on a review of the existing OSS clients (memjs, node-memcached, memcached, mc, nodejs-memcache).

    You can use nodeJS 'tls' module to connect to a Memcached server over TLS, but you will need to create and parse Memcached API requests/responses manually. Here's an example for nodeJS TLS client.

    If Java or PHP are applicable, you can use ElastiCache's Memcached clients with TLS.

    To download the ElastiCache Memcached cluster client:

    1. Sign in to the Amazon Web Services Management Console and open the ElastiCache console at https://console.amazonaws.cn/elasticache/.

    2. From the ElastiCache console, choose ElastiCache Cluster Client.

    3. From the Download ElastiCache Memcached Cluster Client list, choose the ElastiCache Cluster Client that matches your preferred language, version and AMI architecture, then choose the Download button.

    More related links: