cfn-modules / elasticache-redis

ElastiCache redis cluster with secure firewall configuration, encryption, multi AZ, backup enabled, and alerting
Apache License 2.0
3 stars 1 forks source link

Export Auth Information #5

Open ambsw-technology opened 4 years ago

ambsw-technology commented 4 years ago

In theory, a user of Redis will need both the AuthToken (if used) and state of TransitEncryption to connect. I originally hardcoded some of the values in my template (vs. all of the conditions below), but eventually needed to build a URI like:

  Value: !Join
    - ''
    - - 'redis'
      - !If [HasTransitEncryption, 's', '']
      - '://'
      # No username needed for Redis AUTH
      - !If [HasAuthToken, !Sub ':${RedisAuthToken}@', '']
      - !GetAtt 'ReplicationGroup.PrimaryEndPoint.Address'
      - ':'
      - !GetAtt 'ReplicationGroup.PrimaryEndPoint.Port'
      - '/'

It seems like this module (and the aws-cf-templates equivalent) needs to more aggressively export information. The AuthToken is already visible in the parameters and the StackName is pseudorandom so I'm not clear if there's any reason not to build and export the full URI (above). I'd certainly do the same with AuthToken and TransitEncryption for consumers who want/need to pass those to the application separately for processing.

ambsw-technology commented 4 years ago

Some additional thoughts:

For example, this module could accept a [SSM] ParameterModule instead of an AuthToken and export the SSM path at ${RedisModule}-AuthToken. But I assume you can't chain an !ImportValue into a dynamic reference.

edit; cfn-modules might pull something like this off since it could plausibly nest templates so a wrapper extracts the parameter and the nested template resolves it. I think it's literally impossible for aws-cf-templates to use this pattern without forcing the user to do the unwrapping for it.

michaelwittig commented 4 years ago

I agree, AuthToken should use NoEcho. I pushd the change already.

Regarding the connection URL: see #7

It would be even better if we would leverage the secret module instead of AuthToken as we do here https://github.com/cfn-modules/rds-aurora-serverless/blob/master/module.yml#L194. But I suggest we track this in a separate issue/feature request?

ambsw-technology commented 4 years ago

Thanks. The PR will definitely work for us. For the sake of completeness, however, I'll point out that:

We're a HIPAA-compliant organization so we must use both. However, I could see this combination being attractive:

michaelwittig commented 4 years ago

If I understand you correctly you suggest to always use encrypted communication and start the URL with rediss://, right?

michaelwittig commented 4 years ago

ok... I see. Only if TransitEncryption := true we should use rediss:// ... sorry ... I will fix that

ambsw-technology commented 4 years ago

^ exactly.

michaelwittig commented 4 years ago

After one night of sleep I'm not sure if we should Output/Export a secret as a plain text string. That feels wrong to me. If the value is a secret we should threat it that way. Let's see what @andreaswittig adds in #7

ambsw-technology commented 4 years ago

In theory I 100% agree. That was what I was trying to say in the second part of my NoEcho comment (emphasis added):

  • AuthToken should use NoEcho; in that case, exposing the URI exposes secure info.

But, in practice, how else does cfn-modules pass information like this to e.g. ECS services? The easiest one is to pass it as environment variable... but then it's plaintext in a different not-NoEcho parameter and you've had to go to all of the trouble to create it outside the RedisModule.

In theory, dynamic references are the "right" way, but they're a design nightmare right now. You have to specify the version of the SSM parameter so they're virtually useless.

Instead of passing ENV variables to my service stack, I could create an SSM parameter in a place that my application picks up, but the behavior isn't really generalizable. In fact, I need to put it in two different places since I have two applications (and, in the future, more) that need to use it.

My bottom line is that this value is going to get passed plaintext somewhere in the current cfn-modules world. It would be great to develop a standardized (and secure) way to handle these parameters, but this is an acceptable stopgap.

michaelwittig commented 4 years ago

you are right about the dynamic references and SSM parameters. But luckily, you can reference latest if the value comes from Secrets Manager :) I think that's the best way to go. But still, you would need to "construct" the connection string outside of CloudFormation / inside your application based on environment variables.

ambsw-technology commented 4 years ago

I was confused how you intended to pass that environment variable to the service without exposing it in e.g. AppEnvironmentKey1Value (which does not use NoEcho). I recalled but had never looked into the AppEnvironment1SecretModule param in CFN modules.

That actually brought me to the documentation for the ECS Container Definition Secrets key. While CF requires a version number this feature does not e.g.:

"secrets": [{
  "name": "environment_variable_name",
  "valueFrom": "arn:aws:ssm:region:aws_account_id:parameter/parameter_name"
}]

So, in theory, the Redis module could construct the entire URI, create a (secure) parameter store entry (avoiding the cost of the Secrets Manager), and expose either (1) the ARN for the secrets module or (2) stack names for one or more secrets modules (e.g. one for just AuthToken and another for the full URI).

Building the URI in an application is virtually unacceptable to me because you need to hard code (or pass) too many values:

When the ECS Service templates only support a half dozen (or fewer) ENV variables, that chews up WAY too many of them.

ambsw-technology commented 4 years ago

Nevermind... per the docs,

AWS CloudFormation doesn't support creating a SecureString parameter type.