-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add example terraform to demonstrate TProxy on ECS EC2 (#265)
* Add example terraform to demonstrate TProxy on ECS EC2 * Terraform fmt * Fix comments
- Loading branch information
1 parent
0f432c6
commit 26b3edc
Showing
17 changed files
with
944 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
63
examples/dev-server-ec2-transparent-proxy/bastion/bastion.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
14
examples/dev-server-ec2-transparent-proxy/bastion/outputs.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
32
examples/dev-server-ec2-transparent-proxy/bastion/variables.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
14
examples/dev-server-ec2-transparent-proxy/consul-server.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.