Skip to content

Commit

Permalink
Merge pull request #259 from hashicorp/NET-6931-tls-tgw
Browse files Browse the repository at this point in the history
[NET-6931]
  • Loading branch information
kkavish authored Jan 11, 2024
2 parents c681e04 + 01b7146 commit 00703ba
Show file tree
Hide file tree
Showing 19 changed files with 1,450 additions and 6 deletions.
Binary file added _docs/terminating-gateway-external-server-tls.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
130 changes: 130 additions & 0 deletions examples/terminating-gateway-tls/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Terminating Gateways with mTLS on ECS.

This example demonstrates accessing non mesh destinations services from mesh tasks with the help of terminating gateways deployed as ECS tasks over TLS/mTLS.

For the terminating gateway to be able to use TLS/mTLS, certificates trusted by the external server application must be uploaded to the gateway volume.
After the volume mount is successful, a `consul_config_entry` needs to be created to tell the gateway when to use the certs.
If CA cert is configured in the `consul_config_entry`, then the gateway will use mTLS to communicate with the external server application otherwise it will use TLS.
The certs need to be present in the external server application's task as well. So, that it can verify the gateway's identity.
After this setup is complete, any http calls originating from the client application (inside the mesh) to external service will go through the terminating gateway.
The terminating gateway will then forward the request to the external server application's task over TLS/mTLS, using the certs configured earlier.

[Terminating Gateway & TLS](https://developer.hashicorp.com/consul/docs/connect/gateways/terminating-gateway)

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

## Requirements

* `jq`
* `curl`
* Terraform >= 1.2.2

## 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/terminating-gateway-tls
```

This module contains everything needed to spin up the example. The only
requirement is to pass in the IP address of your workstation via the `lb_ingress_ip`
variable. This is used for the security groups on the application load balancers to ensure
only you have access to them.

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 passing in a name and your IP:

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

The plan should look similar to:

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

Changes to Outputs:
+ consul_server_bootstrap_token = (known after apply)
+ consul_server_lb_address = (known after apply)
+ mesh_client_lb_address = (known after apply)
+ certs_efs_file_system_id = (known after apply)
+ non_mesh_server_lb_dns_name = (known after apply)
```

Type `yes` to apply the changes.

Then apply the Terraform passing in a name and your IP again.

```shell

~> **Warning:** These resources will cost money. Be sure to run `terraform destroy`
when you've finished testing.
The apply should take 7-10 minutes. When complete, the URLs of the three load
balancers should be in the output, along with the bootstrap token for the Consul servers:
```shell
Apply complete! Resources: 96 added, 0 changed, 0 destroyed.
Outputs:
consul_server_bootstrap_token = <sensitive>
consul_server_lb_address = "http://consul-ecs-consul-server-1772347952.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"
certs_efs_file_system_id = "fs-12345678"
non_mesh_server_lb_dns_name = "consul-ecs-external-server-app-111111111.us-east-1.elb.amazonaws.com"
```
### Explore
Get the bootstrap token for the Consul cluster from the Terraform output:
```console
$ terraform output -json | jq -r .consul_server_bootstrap_token.value
e2cb39e2-b9fd-18af-025f-86f6da6889a7
```
If you click on the URL of the `consul_server_lb_address`, you should be able
to view the Consul UI and log in using the `consul_server_bootstrap_token` above:
![Consul dc1 UI](https://github.com/hashicorp/terraform-aws-consul-ecs/blob/main/_docs/terminating-gateway-dc1.png?raw=true)
If you browse to the URL of the `mesh_client_lb_address`, you should see the following raw output in your browser
[Application UI](https://github.com/hashicorp/terraform-aws-consul-ecs/blob/main/_docs/terminating-gateway-client-ui.png)
This indicates that the request from the client application that is part of the mesh was able to reach the external server application's task that is not part of the mesh with the help of the terminating gateway workload.

## Cleanup

Once you've finished 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.
161 changes: 161 additions & 0 deletions examples/terminating-gateway-tls/certs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

# generate ca cert and key for tgw <-> external app communication
resource "tls_private_key" "external_app_ca_key" {
algorithm = "ECDSA"
ecdsa_curve = "P256"
}

resource "tls_self_signed_cert" "external_app_ca_cert" {
private_key_pem = tls_private_key.external_app_ca_key.private_key_pem
validity_period_hours = 43800
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
"cert_signing",
"crl_signing",
"client_auth",
]
dns_names = ["*"]
subject {
common_name = "*"
organization = "HashiCorp Inc."
}

is_ca_certificate = true
set_subject_key_id = true
}

resource "aws_secretsmanager_secret" "external_app_ca_key" {
name = "${var.name}-external-app-ca-key"
recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "external_app_ca_key" {
secret_id = aws_secretsmanager_secret.external_app_ca_key.id
secret_string = tls_private_key.external_app_ca_key.private_key_pem
}

resource "aws_secretsmanager_secret" "external_app_ca_cert" {
name = "${var.name}-external-app-ca-cert"
recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "external_app_ca_cert" {
secret_id = aws_secretsmanager_secret.external_app_ca_cert.id
secret_string = tls_self_signed_cert.external_app_ca_cert.cert_pem
}

# generate cert and key for the external app
resource "tls_private_key" "external_app_private_key" {
algorithm = "ECDSA"
ecdsa_curve = "P256"
}

resource "tls_cert_request" "csr_external_app" {

private_key_pem = tls_private_key.external_app_private_key.private_key_pem

dns_names = ["*"]

subject {
common_name = "*"
organization = "HashiCorp Inc."
}
}

resource "tls_locally_signed_cert" "external_app_cert" {
validity_period_hours = 168
early_renewal_hours = 24
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
"client_auth",
]

// CSR by the development servers
cert_request_pem = tls_cert_request.csr_external_app.cert_request_pem
// CA Private key
ca_private_key_pem = tls_private_key.external_app_ca_key.private_key_pem
// CA certificate
ca_cert_pem = tls_self_signed_cert.external_app_ca_cert.cert_pem
}

resource "aws_secretsmanager_secret" "external_app_private_key" {
name = "${var.name}-external-private-key"
recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "external_private_key" {
secret_id = aws_secretsmanager_secret.external_app_private_key.id
secret_string = tls_private_key.external_app_private_key.private_key_pem
}

resource "aws_secretsmanager_secret" "external_cert" {
name = "${var.name}-external-cert"
recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "external_cert" {
secret_id = aws_secretsmanager_secret.external_cert.id
secret_string = tls_locally_signed_cert.external_app_cert.cert_pem
}

# generate cert and key for the gateway
resource "tls_private_key" "tgw_private_key" {
algorithm = "ECDSA"
ecdsa_curve = "P256"
}

resource "tls_cert_request" "csr_tgw" {

private_key_pem = tls_private_key.tgw_private_key.private_key_pem

dns_names = ["*"]

subject {
common_name = "*"
organization = "HashiCorp Inc."
}
}

resource "tls_locally_signed_cert" "tgw_cert" {
validity_period_hours = 168
early_renewal_hours = 24
allowed_uses = [
"key_encipherment",
"digital_signature",
"server_auth",
"client_auth",
]

// CSR by the development servers
cert_request_pem = tls_cert_request.csr_tgw.cert_request_pem
// CA Private key
ca_private_key_pem = tls_private_key.external_app_ca_key.private_key_pem
// CA certificate
ca_cert_pem = tls_self_signed_cert.external_app_ca_cert.cert_pem
}

resource "aws_secretsmanager_secret" "tgw_private_key" {
name = "${var.name}-tgw-private-key"
recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "tgw_private_key" {
secret_id = aws_secretsmanager_secret.tgw_private_key.id
secret_string = tls_private_key.tgw_private_key.private_key_pem
}

resource "aws_secretsmanager_secret" "tgw_cert" {
name = "${var.name}-tgw-cert"
recovery_window_in_days = 0
}

resource "aws_secretsmanager_secret_version" "tgw_cert" {
secret_id = aws_secretsmanager_secret.tgw_cert.id
secret_string = tls_locally_signed_cert.tgw_cert.cert_pem
}
Loading

0 comments on commit 00703ba

Please sign in to comment.