Skip to content

Commit

Permalink
Add example terraform to demonstrate TProxy on ECS EC2 (#265)
Browse files Browse the repository at this point in the history
* Add example terraform to demonstrate TProxy on ECS EC2

* Terraform fmt

* Fix comments
  • Loading branch information
Ganeshrockz authored Jan 24, 2024
1 parent 0f432c6 commit 26b3edc
Show file tree
Hide file tree
Showing 17 changed files with 944 additions and 3 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ BREAKING CHANGES
* Following are the changes made to the task definitions for `mesh-task` and `gateway-task` submodules to react to the changes made in [this](https://github.com/hashicorp/consul-ecs/pull/211) PR.
- Removes the `consul-ecs-control-plane` container from the task definition and adds a new `consul-ecs-mesh-init` container which will be responsible for setting up mesh on ECS.
- Adds a new container named `consul-ecs-health-sync` to the task definition which will be responsible for syncing back ECS container health checks into Consul. This container will wait for a successful exit of `consul-ecs-mesh-init` container before starting.

FEATURES
* Add support for transparent proxy in ECS tasks based on EC2 launch types. Following are the changes made to the `mesh-task` submodule [[GH-264](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/264)]
* Add support for transparent proxy in ECS tasks based on EC2 launch types. This feature automatically routes outgoing/incoming traffic to/from the application container to the sidecar proxy container deployed in the same task. Following are the changes made to the `mesh-task` submodule [[GH-264](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/264)]
- Adds the following variables [[GH-209](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/209)]
- `enable_transparent_proxy` - Defaults to `true`. Fargate based tasks should explicitly pass `false` to avoid validation errors during terraform planning phase.
- `enable_consul_dns` - Defaults to `false`. Indicates whether Consul DNS should be configured for this task. Enabling this makes Consul dataplane start up a proxy DNS server that forwards requests to the Consul DNS server. `var.enable_transparent_proxy` should be `true` to enable this setting.
Expand All @@ -18,6 +16,7 @@ FEATURES
- `mesh-init` container is run as a `root` user.
- Assign a UID of `5995` for the `consul-dataplane` container and `5996` for the `health-sync` container. This is done to selectively exclude the traffic flowing through these containers from the redirection rules.

FEATURES
* Add support for provisioning API gateways as ECS tasks [[GH-234](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/234)]
- Add `api-gateway` as an acceptable `kind` input.
- Add `custom_load_balancer_config` input variable which can be used to feed in custom load balancer target group config that can be attached to the gateway's ECS task.
Expand All @@ -27,6 +26,7 @@ FEATURES
- Add `terminating-gateway` as an acceptable `kind` input for the gateway submodule.
* examples/api-gateway: Add example terraform to demonstrate exposing mesh tasks in ECS via Consul API gateway deployed as an ECS task. [[GH-235]](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/235)
* examples/terminating-gateway: Add example terraform to demonstrate the use of terminating gateways deployed as ECS tasks to facilitate communication between mesh and non mesh services. [[GH-238]](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/238)
* examples/dev-server-ec2-transparent-proxy: Add example terraform to demonstrate Consul's transparent proxy feature for services deployed in ECS EC2 launch type tasks. [[GH-265](https://github.com/hashicorp/terraform-aws-consul-ecs/pull/265)]

## 0.7.1 (Dec 19, 2023)

Expand Down
Binary file added _docs/ec2-transparent-proxy-ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
188 changes: 188 additions & 0 deletions examples/dev-server-ec2-transparent-proxy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# EC2 with transparent proxy

This example module deploys a new VPC and ECS cluster and then provisions
a Consul dev server and two example service mesh tasks using the EC2 launch type and verifies if services are able to communicate each other via transparent proxy. [Transparent proxy](https://developer.hashicorp.com/consul/docs/k8s/connect/transparent-proxy) is how Consul automatically redirects outbound traffic from a container to the Envoy sidecar present within the same task thus disallowing users to directly hit the upstream endpoints without passing through the proxy. Note that transparent proxy is only supported for the ECS EC2 launch type.

![Example architecture](https://github.com/hashicorp/terraform-aws-consul-ecs/blob/main/_docs/dev-server-ec2.png?raw=true)

## Requirements

* Terraform >= 0.14.0

## Usage

### Setup

Clone this repository:

```console
$ git clone https://github.com/hashicorp/terraform-aws-consul-ecs.git
$ git checkout tags/<latest-version>
$ cd terraform-aws-consul-ecs/examples/dev-server-ec2-transparent-proxy
```

This module contains everything needed to spin up the example. The only
requirement is that you pass in the IP address of your workstation via the `lb_ingress_ip`
variable. This is used for the security group on the two load balancers to ensure
only you have access to them since Consul is not running in a secure configuration.

First, determine your public IP. You can use a site like https://ifconfig.me/:

```console
$ curl ifconfig.me
123.456.789.1%
```

Initialize Terraform:

```console
$ terraform init
```

### Terraform Apply

Then apply the Terraform and pass in your IP:

```console
$ terraform apply -var lb_ingress_ip=123.456.789.1
```

The plan should look similar to:

```shell
Plan: 82 to add, 0 to change, 0 to destroy.

Changes to Outputs:
+ consul_server_lb_address = (known after apply)
+ consul_server_bootstrap_token = (sensitive value)
+ mesh_client_lb_address = (known after apply)
```

Type `yes` to apply the changes.

~> **Warning:** These resources will cost money. Be sure to run `terraform destroy`
when you've finished testing.

The apply should take 2-5 minutes. When complete, the URLs of the two load
balancers should be in the output:

```shell
Apply complete! Resources: 82 added, 0 changed, 0 destroyed.

Outputs:

consul_server_lb_address = "http://consul-ecs-consul-server-111111111.us-east-1.elb.amazonaws.com:8500"
consul_server_bootstrap_token = <sensitive>
mesh_client_lb_address = "http://consul-ecs-example-client-app-111111111.us-east-1.elb.amazonaws.com:9090/ui"
```

### Explore

If you click on the URL of the `consul_server_lb_address`, you should be able
to view the Consul UI:

![Consul UI](https://github.com/hashicorp/terraform-aws-consul-ecs/blob/main/_docs/consul-ui.png?raw=true)

You should be able to login to Consul with the `consul_server_bootstrap_token`.

~> At first, if you click on the URL of the `mesh_client_lb_address` or `consul_server_lb_address`,
the page might not load.
This is because the example client application or the Consul server are not yet healthy. After
a minute or two, you should be able to load the UI.

If you go the URL of the `mesh_client_lb_address` in your browser, you should see the UI:

![Example App UI](https://github.com/hashicorp/terraform-aws-consul-ecs/blob/main/_docs/ec2-transparent-proxy-ui.png?raw=true)

The `consul-ecs-example-client-app` calls the `consul-ecs-example-server-app` through the service mesh
and shows the request path in the UI.

If you navigate to the same URL without `/ui`, you'll see the raw requests:

```json
{
"name": "consul-ecs-example-client-app",
"uri": "/",
"type": "HTTP",
"ip_addresses": [
"169.254.172.2",
"10.0.1.237"
],
"start_time": "2024-01-18T17:09:20.908239",
"end_time": "2024-01-18T17:09:20.963856",
"duration": "55.616609ms",
"body": "Hello World",
"upstream_calls": {
"http://consul-ecs-example-server-app.virtual.consul": {
"name": "consul-ecs-example-server-app",
"uri": "http://consul-ecs-example-server-app.virtual.consul",
"type": "HTTP",
"ip_addresses": [
"123.234.456.210",
"10.0.3.91"
],
"start_time": "2024-01-18T17:09:20.958409",
"end_time": "2024-01-18T17:09:20.958544",
"duration": "135.381µs",
"headers": {
"Content-Length": "298",
"Content-Type": "text/plain; charset=utf-8",
"Date": "Thu, 18 Jan 2024 17:09:20 GMT"
},
"body": "Hello World",
"code": 200
}
},
"code": 200
}
```

Under `upstream_calls`, you can see that the `consul-ecs-example-client-app` is making
a call to uri `http://consul-ecs-example-server-app.virtual.consul`. Internally the DNS resolve request gets transparently routed to Consul Dataplane's DNS proxy server and gets resolved to a virtual IP of the `consul-ecs-example-server-app` service which is present in Consul's catalog. After that the request gets transparently proxied through the client app's sidecar and reaches the server app's sidecar before reaching the server app's container.

### (optional) SSH Access to Container Instances

This module supports creating a bastion server (or jump host) which allows you
to login to container instances in EC2 over SSH. This uses the `lb_ingress_ip` to
configure a security group so that only you have access to the bastion instance.
You will need an SSH keypair on your local machine.

To have this module create the bastion server, pass the `public_ssh_key` variable:

```console
$ terraform apply -var lb_ingress_ip=123.456.789.1 -var public_ssh_key=~/.ssh/id_rsa.pub
```

You should see a `bastion_ip` in the Terraform outputs:

```shell
Outputs:

bastion_ip = "1.2.3.4"
consul_server_lb_address = "http://consul-ecs-consul-server-111111111.us-east-1.elb.amazonaws.com:8500"
mesh_client_lb_address = "http://consul-ecs-example-client-app-111111111.us-east-1.elb.amazonaws.com:9090/ui"
```

To login to a container instance, specify the bastion as a jump host using `ssh -J`.
For example, if the bastion IP is 1.2.3.4 and a container instance is at 10.0.1.2:

```shell
ssh -J '[email protected]' '[email protected]'
```

### Explore

To explore Consul and the sample application, see the [Fargate Example README](https://github.com/hashicorp/terraform-aws-consul-ecs/blob/main/examples/dev-server-fargate/README.md#explore).

## Cleanup

Once you've done testing, be sure to clean up the resources you've created:

```console
$ terraform destroy -var lb_ingress_ip=123.456.789.1
```

## Next Steps

Next, see our [full documentation](https://www.consul.io/docs/ecs) when you're ready to deploy your own applications
into the service mesh.
63 changes: 63 additions & 0 deletions examples/dev-server-ec2-transparent-proxy/bastion/bastion.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

// Bastion server to SSH into container instances in a private subnet
data "aws_ami" "bastion" {
most_recent = true
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
owners = ["amazon"]
}

resource "aws_key_pair" "pubkey" {
key_name = "${var.name}-key"
public_key = file(pathexpand(var.public_ssh_key))
}

resource "aws_instance" "bastion" {
ami = data.aws_ami.bastion.id
instance_type = "t2.micro"
key_name = aws_key_pair.pubkey.key_name
subnet_id = var.subnet_id
vpc_security_group_ids = [aws_security_group.bastion.id]

tags = {
Name = "${var.name}-consul-ecs-ec2-bastion"
}
}

resource "aws_security_group" "bastion" {
name = "${var.name}-consul-ecs-ec2-bastion-group"
vpc_id = var.vpc_id
}

resource "aws_security_group_rule" "ssh_rule" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["${var.ingress_ip}/32"]
security_group_id = aws_security_group.bastion.id
}

resource "aws_security_group_rule" "egress_rule" {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
security_group_id = aws_security_group.bastion.id
}

// Modify the security group associated with the *container instances* to allow ingress from the bastion
resource "aws_security_group_rule" "ssh_ingress_from_bastion" {
type = "ingress"
from_port = 22
to_port = 22
protocol = "tcp"
source_security_group_id = aws_security_group.bastion.id
security_group_id = var.destination_security_group
}
14 changes: 14 additions & 0 deletions examples/dev-server-ec2-transparent-proxy/bastion/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

output "ip" {
value = aws_instance.bastion.public_ip
}

output "keypair_name" {
value = aws_key_pair.pubkey.key_name
}

output "security_group_id" {
value = aws_security_group.bastion.id
}
32 changes: 32 additions & 0 deletions examples/dev-server-ec2-transparent-proxy/bastion/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

variable "name" {
description = "Name to be used on all the resources as identifier."
type = string
}

variable "vpc_id" {
description = "The VPC for the EC2 instance."
type = string
}

variable "subnet_id" {
description = "Subnet ID for the EC2 instance."
type = string
}

variable "ingress_ip" {
description = "Your IP. This is used in security groups to ensure only you can access the server."
type = string
}

variable "public_ssh_key" {
description = "Local file path of a public ssh key to login to the bastion server."
type = string
}

variable "destination_security_group" {
description = "Used to create a rule to allow SSH from the bastion to container instances."
type = string
}
14 changes: 14 additions & 0 deletions examples/dev-server-ec2-transparent-proxy/consul-server.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

module "dc1" {
name = var.name
region = var.region
source = "./datacenter"
ecs_cluster_arn = aws_ecs_cluster.this.arn
private_subnets = module.vpc.private_subnets
public_subnets = module.vpc.public_subnets
vpc = module.vpc
lb_ingress_ip = var.lb_ingress_ip
log_group_name = aws_cloudwatch_log_group.log_group.name
}
30 changes: 30 additions & 0 deletions examples/dev-server-ec2-transparent-proxy/controller.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

module "ecs_controller" {
depends_on = [module.dc1]
source = "../../modules/controller"

name_prefix = var.name
ecs_cluster_arn = aws_ecs_cluster.this.arn
region = var.region
subnets = module.vpc.private_subnets
consul_server_hosts = module.dc1.dev_consul_server.server_dns
consul_ca_cert_arn = module.dc1.dev_consul_server.ca_cert_arn
launch_type = "FARGATE"

consul_partitions_enabled = false

consul_bootstrap_token_secret_arn = module.dc1.dev_consul_server.bootstrap_token_secret_arn

log_configuration = {
logDriver = "awslogs"
options = {
awslogs-group = aws_cloudwatch_log_group.log_group.name
awslogs-region = var.region
awslogs-stream-prefix = "ecs-controller"
}
}

tls = true
}
Loading

0 comments on commit 26b3edc

Please sign in to comment.