Skip to content

Commit

Permalink
Add Redis AUTH, in-transit and at-rest encryption (#2090)
Browse files Browse the repository at this point in the history
* add AUTH, at-rest and in-transit encryption to Elasticache replication groups

* add _enabled to transit/at_rest encyrption parameters

* added one more _enabled

* move validateAwsElastiCacheReplicationGroupAuthToken to aws/validators.go, as well as tests

* set auth_token to nil during Reads

* update Replication Group encryption acceptance tests to use config functions instead of vars

* Fix whitespacing (tabs -> spaces)
  • Loading branch information
nathanielks authored and radeksimko committed Dec 11, 2017
1 parent 0c0887c commit f4800f6
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 0 deletions.
40 changes: 40 additions & 0 deletions aws/resource_aws_elasticache_replication_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,28 @@ func resourceAwsElasticacheReplicationGroup() *schema.Resource {
resourceSchema["engine"].Default = "redis"
resourceSchema["engine"].ValidateFunc = validateAwsElastiCacheReplicationGroupEngine

resourceSchema["at_rest_encryption_enabled"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
}

resourceSchema["transit_encryption_enabled"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
}

resourceSchema["auth_token"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Sensitive: true,
ForceNew: true,
ValidateFunc: validateAwsElastiCacheReplicationGroupAuthToken,
}

return &schema.Resource{
Create: resourceAwsElasticacheReplicationGroupCreate,
Read: resourceAwsElasticacheReplicationGroupRead,
Expand Down Expand Up @@ -168,6 +190,18 @@ func resourceAwsElasticacheReplicationGroupCreate(d *schema.ResourceData, meta i
params.SnapshotName = aws.String(v.(string))
}

if _, ok := d.GetOk("transit_encryption_enabled"); ok {
params.TransitEncryptionEnabled = aws.Bool(d.Get("transit_encryption_enabled").(bool))
}

if _, ok := d.GetOk("at_rest_encryption_enabled"); ok {
params.AtRestEncryptionEnabled = aws.Bool(d.Get("at_rest_encryption_enabled").(bool))
}

if v, ok := d.GetOk("auth_token"); ok {
params.AuthToken = aws.String(v.(string))
}

clusterMode, clusterModeOk := d.GetOk("cluster_mode")
cacheClusters, cacheClustersOk := d.GetOk("number_cache_clusters")

Expand Down Expand Up @@ -313,6 +347,12 @@ func resourceAwsElasticacheReplicationGroupRead(d *schema.ResourceData, meta int
}

d.Set("auto_minor_version_upgrade", c.AutoMinorVersionUpgrade)
d.Set("at_rest_encryption_enabled", c.AtRestEncryptionEnabled)
d.Set("transit_encryption_enabled", c.TransitEncryptionEnabled)

if c.AuthTokenEnabled != nil && !*c.AuthTokenEnabled {
d.Set("auth_token", nil)
}
}

return nil
Expand Down
147 changes: 147 additions & 0 deletions aws/resource_aws_elasticache_replication_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,44 @@ func TestAccAWSElasticacheReplicationGroup_enableSnapshotting(t *testing.T) {
})
}

func TestAccAWSElasticacheReplicationGroup_enableAuthTokenTransitEncryption(t *testing.T) {
var rg elasticache.ReplicationGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSElasticacheReplicationGroup_EnableAuthTokenTransitEncryptionConfig(acctest.RandInt(), acctest.RandString(10), acctest.RandString(16)),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists("aws_elasticache_replication_group.bar", &rg),
resource.TestCheckResourceAttr(
"aws_elasticache_replication_group.bar", "transit_encryption_enabled", "true"),
),
},
},
})
}

func TestAccAWSElasticacheReplicationGroup_enableAtRestEncryption(t *testing.T) {
var rg elasticache.ReplicationGroup
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSElasticacheReplicationDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSElasticacheReplicationGroup_EnableAtRestEncryptionConfig(acctest.RandInt(), acctest.RandString(10)),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSElasticacheReplicationGroupExists("aws_elasticache_replication_group.bar", &rg),
resource.TestCheckResourceAttr(
"aws_elasticache_replication_group.bar", "at_rest_encryption_enabled", "true"),
),
},
},
})
}

func TestResourceAWSElastiCacheReplicationGroupIdValidation(t *testing.T) {
cases := []struct {
Value string
Expand Down Expand Up @@ -1002,3 +1040,112 @@ resource "aws_elasticache_replication_group" "bar" {
}
}`, rInt, rInt, rInt, rInt, rName)
}

func testAccAWSElasticacheReplicationGroup_EnableAtRestEncryptionConfig(rInt int, rString string) string {
return fmt.Sprintf(`
resource "aws_vpc" "foo" {
cidr_block = "192.168.0.0/16"
tags {
Name = "tf-test"
}
}
resource "aws_subnet" "foo" {
vpc_id = "${aws_vpc.foo.id}"
cidr_block = "192.168.0.0/20"
availability_zone = "us-west-2a"
tags {
Name = "tf-test-%03d"
}
}
resource "aws_elasticache_subnet_group" "bar" {
name = "tf-test-cache-subnet-%03d"
description = "tf-test-cache-subnet-group-descr"
subnet_ids = [
"${aws_subnet.foo.id}",
]
}
resource "aws_security_group" "bar" {
name = "tf-test-security-group-%03d"
description = "tf-test-security-group-descr"
vpc_id = "${aws_vpc.foo.id}"
ingress {
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_elasticache_replication_group" "bar" {
replication_group_id = "tf-%s"
replication_group_description = "test description"
node_type = "cache.t2.micro"
number_cache_clusters = "1"
port = 6379
subnet_group_name = "${aws_elasticache_subnet_group.bar.name}"
security_group_ids = ["${aws_security_group.bar.id}"]
parameter_group_name = "default.redis3.2"
availability_zones = ["us-west-2a"]
engine_version = "3.2.6"
at_rest_encryption_enabled = true
}
`, rInt, rInt, rInt, rString)
}

func testAccAWSElasticacheReplicationGroup_EnableAuthTokenTransitEncryptionConfig(rInt int, rString10 string, rString16 string) string {
return fmt.Sprintf(`
resource "aws_vpc" "foo" {
cidr_block = "192.168.0.0/16"
tags {
Name = "tf-test"
}
}
resource "aws_subnet" "foo" {
vpc_id = "${aws_vpc.foo.id}"
cidr_block = "192.168.0.0/20"
availability_zone = "us-west-2a"
tags {
Name = "tf-test-%03d"
}
}
resource "aws_elasticache_subnet_group" "bar" {
name = "tf-test-cache-subnet-%03d"
description = "tf-test-cache-subnet-group-descr"
subnet_ids = [
"${aws_subnet.foo.id}",
]
}
resource "aws_security_group" "bar" {
name = "tf-test-security-group-%03d"
description = "tf-test-security-group-descr"
vpc_id = "${aws_vpc.foo.id}"
ingress {
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_elasticache_replication_group" "bar" {
replication_group_id = "tf-%s"
replication_group_description = "test description"
node_type = "cache.t2.micro"
number_cache_clusters = "1"
port = 6379
subnet_group_name = "${aws_elasticache_subnet_group.bar.name}"
security_group_ids = ["${aws_security_group.bar.id}"]
parameter_group_name = "default.redis3.2"
availability_zones = ["us-west-2a"]
engine_version = "3.2.6"
transit_encryption_enabled = true
auth_token = "%s"
}
`, rInt, rInt, rInt, rString10, rString16)
}
13 changes: 13 additions & 0 deletions aws/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -1984,3 +1984,16 @@ func validateDxConnectionBandWidth(v interface{}, k string) (ws []string, errors
errors = append(errors, fmt.Errorf("expected %s to be one of %v, got %s", k, validBandWidth, val))
return
}

func validateAwsElastiCacheReplicationGroupAuthToken(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if (len(value) < 16) || (len(value) > 128) {
errors = append(errors, fmt.Errorf(
"%q must contain from 16 to 128 alphanumeric characters or symbols (excluding @, \", and /)", k))
}
if !regexp.MustCompile(`^[^@"\/]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf(
"only alphanumeric characters or symbols (excluding @, \", and /) allowed in %q", k))
}
return
}
40 changes: 40 additions & 0 deletions aws/validators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2799,3 +2799,43 @@ func TestValidateCognitoUserPoolReplyEmailAddress(t *testing.T) {
}
}
}

func TestResourceAWSElastiCacheReplicationGroupAuthTokenValidation(t *testing.T) {
cases := []struct {
Value string
ErrCount int
}{
{
Value: "this-is-valid!#%()^",
ErrCount: 0,
},
{
Value: "this-is-not",
ErrCount: 1,
},
{
Value: "this-is-not-valid\"",
ErrCount: 1,
},
{
Value: "this-is-not-valid@",
ErrCount: 1,
},
{
Value: "this-is-not-valid/",
ErrCount: 1,
},
{
Value: randomString(129),
ErrCount: 1,
},
}

for _, tc := range cases {
_, errors := validateAwsElastiCacheReplicationGroupAuthToken(tc.Value, "aws_elasticache_replication_group_auth_token")

if len(errors) != tc.ErrCount {
t.Fatalf("Expected the ElastiCache Replication Group AuthToken to trigger a validation error")
}
}
}

0 comments on commit f4800f6

Please sign in to comment.