Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Redis AUTH, in-transit and at-rest encryption #2090

Merged
merged 7 commits into from
Dec 11, 2017
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")
}
}
}