From 788c58699e89746864bdb554aff9f849d82a8bcf Mon Sep 17 00:00:00 2001 From: trujillo-adam <47586768+trujillo-adam@users.noreply.github.com> Date: Thu, 5 Oct 2023 07:33:44 -0700 Subject: [PATCH] Docs/ce 477 dataplanes on ecs (#19010) * updated architecture topic * fixed type in arch diagram filenames * fixed path to img file * updated index page - still need to add links * moved arch and tech specs to reference folder * moved other ref topics to ref folder * set up the Deploy folder and TF install topics * merged secure conf into TF deploy instructions * moved bind addr and route conf to their own topics * moved arch and tech specs back to main folder * update migrate-existing-tasks content * merged manual deploy content; added serv conf ref * fixed links * added procedure for upgrading to dataplanes * fixed linked reported by checker * added updates to dataplanes overview page * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Ganesh S * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Ganesh S * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Ganesh S * Apply suggestions from code review Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Ganesh S * updated links and added redirects * removed old architecture content --------- Co-authored-by: Jeff Boruszak <104028618+boruszak@users.noreply.github.com> Co-authored-by: Ganesh S --- .../content/docs/connect/dataplane/index.mdx | 71 ++- website/content/docs/ecs/architecture.mdx | 280 ++------- .../docs/ecs/deploy/bind-addresses.mdx | 47 ++ .../docs/ecs/deploy/configure-routes.mdx | 79 +++ website/content/docs/ecs/deploy/manual.mdx | 342 +++++++++++ .../ecs/deploy/migrate-existing-tasks.mdx | 99 ++++ website/content/docs/ecs/deploy/terraform.mdx | 478 +++++++++++++++ website/content/docs/ecs/enterprise.mdx | 4 +- website/content/docs/ecs/index.mdx | 58 +- .../docs/ecs/manual/acl-controller.mdx | 190 ------ website/content/docs/ecs/manual/install.mdx | 556 ------------------ .../docs/ecs/manual/secure-configuration.mdx | 545 ----------------- .../ecs/{ => reference}/compatibility.mdx | 0 .../configuration-reference.mdx | 2 +- .../docs/ecs/reference/consul-server-json.mdx | 31 + website/content/docs/ecs/requirements.mdx | 32 - .../content/docs/ecs/task-resource-usage.mdx | 37 -- website/content/docs/ecs/tech-specs.mdx | 52 ++ .../content/docs/ecs/terraform/install.mdx | 452 -------------- .../ecs/terraform/migrate-existing-tasks.mdx | 115 ---- .../ecs/terraform/secure-configuration.mdx | 177 ------ .../docs/ecs/upgrade-to-dataplanes.mdx | 68 +++ .../docs/release-notes/consul-ecs/v0_5_x.mdx | 4 +- website/data/docs-nav-data.json | 96 +-- ...ul-on-ecs-architecture-dataplanes-dark.jpg | Bin 0 -> 68588 bytes .../consul-on-ecs-architecture-dataplanes.jpg | Bin 0 -> 67505 bytes website/redirects.js | 50 ++ 27 files changed, 1448 insertions(+), 2417 deletions(-) create mode 100644 website/content/docs/ecs/deploy/bind-addresses.mdx create mode 100644 website/content/docs/ecs/deploy/configure-routes.mdx create mode 100644 website/content/docs/ecs/deploy/manual.mdx create mode 100644 website/content/docs/ecs/deploy/migrate-existing-tasks.mdx create mode 100644 website/content/docs/ecs/deploy/terraform.mdx delete mode 100644 website/content/docs/ecs/manual/acl-controller.mdx delete mode 100644 website/content/docs/ecs/manual/install.mdx delete mode 100644 website/content/docs/ecs/manual/secure-configuration.mdx rename website/content/docs/ecs/{ => reference}/compatibility.mdx (100%) rename website/content/docs/ecs/{ => reference}/configuration-reference.mdx (99%) create mode 100644 website/content/docs/ecs/reference/consul-server-json.mdx delete mode 100644 website/content/docs/ecs/requirements.mdx delete mode 100644 website/content/docs/ecs/task-resource-usage.mdx create mode 100644 website/content/docs/ecs/tech-specs.mdx delete mode 100644 website/content/docs/ecs/terraform/install.mdx delete mode 100644 website/content/docs/ecs/terraform/migrate-existing-tasks.mdx delete mode 100644 website/content/docs/ecs/terraform/secure-configuration.mdx create mode 100644 website/content/docs/ecs/upgrade-to-dataplanes.mdx create mode 100644 website/public/img/ecs/consul-on-ecs-architecture-dataplanes-dark.jpg create mode 100644 website/public/img/ecs/consul-on-ecs-architecture-dataplanes.jpg diff --git a/website/content/docs/connect/dataplane/index.mdx b/website/content/docs/connect/dataplane/index.mdx index 9c097d79db..9b8f1a7e87 100644 --- a/website/content/docs/connect/dataplane/index.mdx +++ b/website/content/docs/connect/dataplane/index.mdx @@ -2,28 +2,35 @@ layout: docs page_title: Simplified Service Mesh with Consul Dataplane description: >- - Consul Dataplane removes the need to a run client agent for service discovery and service mesh by leveraging orchestrator functions. Learn about Consul Dataplane, how it can lower latency for Consul on Kubernetes, and how it enables Consul support for AWS Fargate and GKE Autopilot. + Consul Dataplane removes the need to run a client agent for service discovery and service mesh by leveraging orchestrator functions. Learn about Consul Dataplane, how it can lower latency for Consul on Kubernetes and AWS ECS, and how it enables Consul support for AWS Fargate and GKE Autopilot. --- # Simplified Service Mesh with Consul Dataplane -This topic provides an overview of Consul Dataplane, a lightweight process for managing Envoy proxies introduced in Consul v1.14.0. Consul Dataplane removes the need to run client agents on every node in a cluster for service discovery and service mesh. Instead, Consul deploys sidecar proxies that provide lower latency, support additional runtimes, and integrate with cloud infrastructure providers. +This topic provides an overview of Consul Dataplane, a lightweight process for managing Envoy proxies. Consul Dataplane removes the need to run client agents on every node in a cluster for service discovery and service mesh. Instead, Consul deploys sidecar proxies that provide lower latency, support additional runtimes, and integrate with cloud infrastructure providers. + +## Supported environments + +- Dataplanes can connect to Consul servers v1.14.0 and newer. +- Dataplanes on Kubernetes requires Consul K8s v1.0.0 and newer. +- Dataplanes on AWS Elastic Container Services (ECS) requires Consul ECS v0.7.0 and newer. -Consul Dataplane requires servers running Consul v1.14.0+ and Consul K8s v1.0.0+. ## What is Consul Dataplane? -In standard deployments, Consul uses a control plane that contains both _server agents_ and _client agents_. Server agents maintain the service catalog and service mesh, including its security and consistency, while client agents manage communications between service instances, their sidecar proxies, and the servers. While this model is optimal for applications deployed on virtual machines or bare metal servers, orchestrators such as Kubernetes already include components called _kubelets_ that support health checking and service location functions typically provided by the client agent. +When deployed to virtual machines or bare metal environments, the Consul control plane requires _server agents_ and _client agents_. Server agents maintain the service catalog and service mesh, including its security and consistency, while client agents manage communications between service instances, their sidecar proxies, and the servers. While this model is optimal for applications deployed on virtual machines or bare metal servers, orchestrators such as Kubernetes and ECS have native components that support health checking and service location functions typically provided by the client agent. -Consul Dataplane manages Envoy proxies and leaves responsibility for other functions to the orchestrator. As a result, it removes the need to run client agents on every node. In addition, services no longer need to be reregistered to a local client agent after restarting a service instance, as a client agent’s lack of access to persistent data storage in Kubernetes deployments is no longer an issue. +Consul Dataplane manages Envoy proxies and leaves responsibility for other functions to the orchestrator. As a result, it removes the need to run client agents on every node. In addition, services no longer need to be reregistered to a local client agent after restarting a service instance, as a client agent’s lack of access to persistent data storage in container-orchestrated deployments is no longer an issue. + +The following diagram shows how Consul Dataplanes facilitate service mesh in a Kubernetes-orchestrated environment. ![Diagram of Consul Dataplanes in Kubernetes deployment](/img/k8s-dataplanes-architecture.png) ### Impact on performance -The most significant differences between traditional deployments and Consul Dataplane deployments result from the removal of node-level client agents with gossip communication. They are replaced by _dataplanes_, which are the sidecars injected alongside each service instance that handle communication between Consul servers and Envoy proxies. While dataplanes use fewer resources than client agents, Consul servers need to consume additional resources in order to generate xDS resources for Envoy proxies. +Consul Dataplanes replace node-level client agents and function as sidecars attached to each service instance. Dataplanes handle communication between Consul servers and Envoy proxies, using fewer resources than client agents. Consul servers need to consume additional resources in order to generate xDS resources for Envoy proxies. -As a result, small deployments require fewer resources overall. For deployments that are especially large or expected to experience high levels of churn, consider the following impacts to your network's performance: +As a result, small deployments require fewer overall resources. For especially large deployments or deployments that expect to experience high levels of churn, consider the following impacts to your network's performance: 1. In our internal tests, which used 5000 proxies and services flapping every 2 seconds, additional CPU utilization remained under 10% on the control plane. 1. As you deploy more services, the resource usage for dataplanes grows on a linear scale. @@ -37,7 +44,7 @@ As a result, small deployments require fewer resources overall. For deployments **Simplified set up**: Because there are no client agents to engage in gossip, you do not have to generate and distribute a gossip encryption key to agents during the initial bootstrapping process. Securing agent communication also becomes simpler, with fewer tokens to track, distribute, and rotate. -**Additional environment and runtime support**: Consul on Kubernetes versions *prior* to 1.0 (Consul 1.14) require using hostPorts and DaemonSets for client agents, which limits Consul’s ability to be deployed in environments where those features are not supported. +**Additional environment and runtime support**: Consul on Kubernetes versions _prior_ to v1.0 (Consul v1.14) require the use of hostPorts and DaemonSets for client agents, which limits Consul’s ability to be deployed in environments where those features are not supported. As of Consul on Kubernetes version 1.0 (Consul 1.14) with the new Consul Dataplane, `hostPorts` are no longer required and Consul now supports AWS Fargate and GKE Autopilot. **Easier upgrades**: With Consul Dataplane, updating Consul to a new version no longer requires upgrading client agents. Consul Dataplane also has better compatibility across Consul server versions, so the process to upgrade Consul servers becomes easier. @@ -49,9 +56,15 @@ To get started with Consul Dataplane, use the following reference resources: - For `consul-dataplane` commands and usage examples, including required flags for startup, refer to the [`consul-dataplane` CLI reference](/consul/docs/connect/dataplane/consul-dataplane). - For Helm chart information, refer to the [Helm Chart reference](/consul/docs/k8s/helm). - For Envoy, Consul, and Consul Dataplane version compatibility, refer to the [Envoy compatibility matrix](/consul/docs/connect/proxies/envoy). +- For Consul on ECS workloads, refer to [Consul on AWS Elastic Container Service (ECS) Overview](/consul/docs/ecs). + ### Installation + + + + To install Consul Dataplane, set `VERSION` to `1.0.0` and then follow the instructions to install a specific version of Consul [with the Helm Chart](/consul/docs/k8s/installation/install#install-consul) or [with the Consul-k8s CLI](/consul/docs/k8s/installation/install-cli#install-a-previous-version). #### Helm @@ -67,14 +80,41 @@ $ helm install consul hashicorp/consul --set global.name=consul --version ${VERS $ export VERSION=1.0.0 && \ curl --location "https://releases.hashicorp.com/consul-k8s/${VERSION}/consul-k8s_${VERSION}_darwin_amd64.zip" --output consul-k8s-cli.zip ``` + + + + +Refer to the following documentation for Consul on ECS workloads: + +- [Deploy Consul with the Terraform module](/consul/docs/ecs/deploy/terraform) +- [Deploy Consul manually](/consul/ecs/install-manul) + + + + + ### Upgrading + + + + Before you upgrade Consul to a version that uses Consul Dataplane, you must edit your Helm chart so that client agents are removed from your deployments. Refer to [upgrading to Consul Dataplane](/consul/docs/k8s/upgrade#upgrading-to-consul-dataplanes) for more information. + + + + +Refer to [Upgrade to dataplane architecture](/consul/docs/ecs/upgrade-to-dataplanes) for instructions. + + + + + ## Feature support -Consul Dataplane supports the following features: +Consul Dataplane on Kubernetes supports the following features: - Single and multi-cluster installations, including those with WAN federation, cluster peering, and admin partitions are supported. - Ingress, terminating, and mesh gateways are supported. @@ -82,10 +122,17 @@ Consul Dataplane supports the following features: - xDS load balancing is supported. - Servers running in Kubernetes and servers external to Kubernetes are both supported. - HCP Consul is supported. -- Consul API Gateway is supported. +- Consul API Gateway + +Consul Dataplane on ECS support the following features: + +- Single and multi-cluster installations, including those with WAN federation, cluster peering, and admin partitions +- Mesh gateways +- Running Consul service mesh in AWS Fargate and EC2 +- xDS load balancing +- Self-managed and HCP Consul managed servers ### Technical Constraints -Be aware of the following limitations and recommendations for Consul Dataplane: - - Consul Dataplane is not supported on Windows. + diff --git a/website/content/docs/ecs/architecture.mdx b/website/content/docs/ecs/architecture.mdx index a0c84805a6..0f35b11b6d 100644 --- a/website/content/docs/ecs/architecture.mdx +++ b/website/content/docs/ecs/architecture.mdx @@ -1,257 +1,85 @@ --- layout: docs -page_title: Consul on AWS Elastic Container Service (ECS) Architecture +page_title: Consul on AWS Elastic Container Service (ECS) architecture description: >- - Consul's architecture supports Amazon Web Services ECS deployments. Learn about how the two work together, including the order tasks and containers startup and shutdown, as well as requirements for the AWS IAM auth method, the ACL controller and tokens, and health check syncing. + Learn about the Consul architecture on Amazon Web Services ECS deployments. Learn about how the two work together, including the order tasks and containers startup and shutdown, as well as requirements for the AWS IAM auth method, the ACL controller and tokens, and health check syncing. --- -# Consul on AWS Elastic Container Service (ECS) Architecture +# Consul on AWS Elastic Container Service (ECS) architecture -The following diagram shows the main components of the Consul architecture when deployed to an ECS cluster: +This topic provides reference information about the Consul's deployment architecture on AWS ECS. The following diagram shows the main components of the Consul architecture when deployed to an ECS cluster. -![Consul on ECS Architecture](/img/consul-ecs-arch.png) +![Diagram that provides an overview of the Consul Architecture on ECS](/img/ecs/consul-on-ecs-architecture-dataplanes.jpg#light-theme-only) +![Diagram that provides an overview of the Consul Architecture on ECS ](/img/ecs/consul-on-ecs-architecture-dataplanes-dark.jpg#dark-theme-only) -1. **Consul servers:** Production-ready Consul server cluster -1. **Application tasks:** Runs user application containers along with two helper containers: - 1. **Consul client:** The Consul client container runs Consul. The Consul client communicates - with the Consul server and configures the Envoy proxy sidecar. This communication - is called _control plane_ communication. - 1. **Sidecar proxy:** The sidecar proxy container runs [Envoy](https://envoyproxy.io/). All requests - to and from the application container(s) run through the sidecar proxy. This communication - is called _data plane_ communication. -1. **Mesh Init:** Each task runs a short-lived container, called `mesh-init`, which sets up initial configuration - for Consul and Envoy. -1. **Health Syncing:** Optionally, an additional `health-sync` container can be included in a task to sync health statuses - from ECS into Consul. -1. **ACL Controller:** The ACL controller is responsible for automating configuration and cleanup in the Consul servers. - The ACL controller will automatically configure the [AWS IAM Auth Method](/consul/docs/security/acl/auth-methods/aws-iam), and cleanup - unused ACL tokens from Consul. When using Consul Enterprise namespaces, the ACL controller will automatically create Consul - namespaces for ECS tasks. +## Components +Consul starts several components and containers inside the ECS cluster. Refer to [Startup sequence](#startup-sequence) for details about the order of the startup procedure: + +### Control-plane container -For more information about how Consul works in general, see Consul's [Architecture Overview](/consul/docs/architecture). +The control-plane container performs the following actions: -## Task Startup +- Logs into Consul servers +- Communicates directly with Consul server +- Registers proxies and services +- Creates a bootstrap configuration file for Consul dataplane container and stores it in a shared volume +- Synchronizes ECS health checks +- Watches the Consul server for changes -This diagram shows the timeline of a task starting up and all its containers: +### Dataplane containers - +The dataplane process runs in the same container as the Envoy proxy and performs the following actions: -![Task Startup Timeline](/img/ecs-task-startup.svg) +- Consumes and configures itself according to the bootstrap configuration written by the control-plane container. +- Contains and starts up the Envoy sidecar. - +### ECS controller container -- **T0:** ECS starts the task. The `consul-client` and `mesh-init` containers start: - - `consul-client` does the following: - - If ACLs are enabled, a startup script runs a `consul login` command to obtain a - token from the AWS IAM auth method for the Consul client. This token has `node:write` - permissions. - - It uses the `retry-join` option to join the Consul cluster. - - `mesh-init` does the following: - - If ACLs are enabled, mesh-init runs a `consul login` command to obtain a token from - the AWS IAM auth method for the service registration. This token has `service:write` - permissions for the service and its sidecar proxy. This token is written to a shared - volume for use by the `health-sync` container. - - It registers the service for the current task and its sidecar proxy with Consul. - - It runs `consul connect envoy -bootstrap` to generate Envoy's bootstrap JSON file and - writes it to a shared volume. -- **T1:** The following containers start: - - `sidecar-proxy` starts using a custom entrypoint command, `consul-ecs envoy-entrypoint`. - The entrypoint command starts Envoy by running `envoy -c `. - - `health-sync` starts if ECS health checks are defined or if ACLs are enabled. It syncs health - checks from ECS to Consul (see [ECS Health Check Syncing](#ecs-health-check-syncing)). -- **T2:** The `sidecar-proxy` container is marked as healthy by ECS. It uses a health check that - detects if its public listener port is open. At this time, your application containers are started - since all Consul machinery is ready to service requests. +One ECS task in the cluster contains the controller container, which performs the following actions: -## Task Shutdown +- Creates AWS IAM auth methods +- Creates ACL policies and roles +- Maintains ACL state +- Removes tokens when services exit +- Deregisters services if the ECS task exits without deregistering them +- Registers a _synthetic node_ that enables Consul to register services to the catalog + +## Startup sequence -This diagram shows an example timeline of a task shutting down: +Deploying Consul to ECS starts the following process to build the architecture: - +1. The control-plane container starts and logs into Consul. +1. The control-plane container registers services and proxies with the Consul servers. +1. The control-plane container writes the bootstrap configuration for the Consul dataplane process and stores it in a shared volume. +1. The dataplane container starts and configures itself using the bootstrap configuration generated by the control-plane container. +1. The dataplane container starts the Envoy sidecar proxy. +1. The control-plane container starts listening for ECS health checks. +1. When the ECS task indicates that the application instance is healthy, the control-plane container marks the service as healthy and starts allowing traffic to flow. -![Task Shutdown Timeline](/img/ecs-task-shutdown.svg) +## Consul security components - +Consul leverages AWS components to facilitate its own security features. -- **T0**: ECS sends a TERM signal to all containers. Each container reacts to the TERM signal: - - `consul-client` begins to gracefully leave the Consul cluster. - - `health-sync` stops syncing health status from ECS into Consul checks. - - `sidecar-proxy` ignores the TERM signal and continues running until the `user-app` container - exits. The custom entrypoint command, `consul-ecs envoy-entrypoint`, monitors the local ECS task - metadata. It waits until the `user-app` container has exited before terminating Envoy. This - enables the application to continue making outgoing requests through the proxy to the mesh for - graceful shutdown. - - `user-app` exits if it is not configured to ignore the TERM signal. The `user-app` container - will continue running if it is configured to ignore the TERM signal. -- **T1**: - - `health-sync` does the following: - - It updates its Consul checks to critical status and exits. This ensures this service instance is marked unhealthy. - - If ACLs are enabled, it runs `consul logout` for the two tokens created by the `consul-client` and `mesh-init` containers. - This removes those tokens from Consul. If `consul logout` fails for some reason, the ACL controller will remove the tokens - after the task has stopped. - - `sidecar-proxy` notices the `user-app` container has stopped and exits. -- **T2**: `consul-client` finishes gracefully leaving the Consul datacenter and exits. -- **T3**: - - ECS notices all containers have exited, and will soon change the Task status to `STOPPED` - - Updates about this task have reached the rest of the Consul cluster, so downstream proxies have been updated to stopped sending traffic to this task. -- **T4**: At this point task shutdown should be complete. Otherwise, ECS will send a KILL signal to any containers still running. The KILL signal cannot be ignored and will forcefully stop containers. This will interrupt in-progress operations and possibly cause errors. +### Auth methods -## ACL Tokens +Consul on ECS uses the AWS IAM auth method so that ECS tasks can automatically obtain Consul ACL tokens during startup. -Two types of ACL tokens are required by ECS tasks: +When ACLs are enabled, the Terraform modules for Consul on ECS support AWS IAM auth methods by default. The ECS controller sets up the auth method on the Consul servers. The `mesh-task` module configures the ECS task definition to be compatible with the auth method. -* **Client tokens:** used by the `consul-client` containers to join the Consul cluster -* **Service tokens:** used by sidecar containers for service registration and health syncing +A unique task IAM role is required for each ECS task family. A task family represents only one Consul service and the task IAM role must encode the Consul service name. As a result, task IAM roles must not be shared by different task families. -With Consul on ECS, these tokens are obtained dynamically when a task starts up by logging -in via Consul's AWS IAM auth method. +By default, the mesh-task module creates and configures the task IAM role for you. -### Consul Client Token +To pass an existing IAM role to the mesh-task module using the `task_role` input variable, configure the IAM role as described in ECS Task Role Configuration to be compatible with the AWS IAM auth method. -Consul client tokens require `node:write` for any node name, which is necessary because the Consul node -names on ECS are not known until runtime. +### ECS task roles -### Service Token +The [ECS task role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) is an IAM role associated with an ECS task. -Service tokens are associated with a [service identity](/consul/docs/security/acl#service-identities). -The service identity includes `service:write` permissions for the service and sidecar proxy. +When an ECS task starts up, it runs a `consul login` command. The command obtains credentials for the task role from AWS and then uses those credentials to sign the login request to the AWS IAM auth method. The credentials prove the ECS task's identity to the Consul servers. -## AWS IAM Auth Method +You must configure the task role with the following details for it to be compatible with the AWS IAM auth method: -Consul's [AWS IAM Auth Method](/consul/docs/security/acl/auth-methods/aws-iam) is used by ECS tasks to -automatically obtain Consul ACL tokens. When a service mesh task on ECS starts up, it runs two -`consul login` commands to obtain a client token and a service token via the auth method. When the -task stops, it attempts two `consul logout` commands in order to destroy these tokens. - -During a `consul login`, the [task's IAM -role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) is presented -to the AWS IAM auth method on the Consul servers. The role is validated with AWS. If the role is -valid, and if the auth method trusts the IAM role, then the role is permitted to login. A new Consul -ACL token is created and [Binding Rules](/consul/docs/security/acl/auth-methods#binding-rules) associate -permissions with the newly created token. These permissions are mapped to the token based on the IAM -role details. For example, tags on the IAM role are used to specify the service name and the -Consul Enterprise namespace to be associated with a service token that is created by a successful -login to the auth method. - -### Task IAM Role - -The following configuration is required for the task IAM role in order to be compatible with the -auth method. When using Terraform, the `mesh-task` module creates the task role with this -configuration by default. - -* A scoped `iam:GetRole` permission must be included on the IAM role, enabling the role to fetch - details about itself. -* A `consul.hashicorp.com.service-name` tag on the IAM role must be set to the Consul service name. -* A consul.hashicorp.com.namespace tag must be set on the - IAM role to the Consul Enterprise namespace of the Consul service for the task. - -Task IAM roles should not typically be shared across task families. Since a task family represents a -single Consul service, and since the task role must include the Consul service name, one task role -is required for each task family when using the auth method. - -### Security - -The auth method relies on the configuration of AWS resources, such as IAM roles, IAM policies, and -ECS tasks. If these AWS resources are misconfigured or if the account has loose access controls, -then the security of your service mesh may be at risk. - -Any entity in your AWS account with the ability to obtain credentials for an IAM role could potentially -obtain a Consul ACL token and impersonate a Consul service. The `mesh-task` Terraform module -mitigates against this concern by creating the task role with an `AssumeRolePolicyDocument` that -allows only the AWS ECS service to assume the task role. By default, other entities are unable -to obtain credentials for task roles, and are unable to abuse the AWS IAM auth method to obtain -Consul ACL tokens. - -However, other entities in your AWS account with the ability to create or modify IAM roles can -potentially circumvent this. For example, if they are able to create an IAM role with the correct -tags, they can obtain a Consul ACL token for any service. Or, if they can pass a role to an ECS task -and start an ECS task, they can use the task to obtain a Consul ACL token via the auth method. - -The IAM policy actions `iam:CreateRole`, `iam:TagRole`, `iam:PassRole`, and `sts:AssumeRole` can be -used to restrict these capabilities in your AWS account and improve security when using the AWS IAM -auth method. See the [AWS -documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) to learn how -to restrict these permissions in your AWS account. - -## ACL Controller - -The ACL controller performs the following operations on the Consul servers: - -* Configures the Consul AWS IAM auth method. -* Monitors tasks in ECS cluster where the controller is running. -* Cleans up unused Consul ACL tokens created by tasks in this cluster. -* Manages Consul admin partitions and namespaces. - -### Auth Method Configuration - -The ACL controller is responsible for configuring the AWS IAM auth method. The following resources -are created by the ACL controller when it starts up: - -* **Client role**: The controller creates the Consul (not IAM) role and policy used for client - tokens if these do not exist. This policy has `node:write` permissions to enable Consul clients to - join the Consul cluster. -* **Auth method for client tokens**: One instance of the AWS IAM auth method is created for client - tokens, if it does not exist. A binding rule is configured that attaches the Consul client role to each - token created during a successful login to this auth method instance. -* **Auth method for service tokens**: One instance of the AWS IAM auth method is created for service - tokens, if it does not exist: - * A binding rule is configured to attach a [service identity](/consul/docs/security/acl#service-identities) - to each token created during a successful login to this auth method instance. The service name for - this service identity is taken from the tag, `consul.hashicorp.com.service-name`, on the IAM role - used to log in. - * A namespace binding rule is configured to create service tokens in - the namespace specified by the tag, consul.hashicorp.com.namespace, on the IAM - role used to log in. - -The ACL controller configures both instances of the auth method to permit only certain IAM roles to login, -by setting the [`BoundIAMPrincipalARNs`](/consul/docs/security/acl/auth-methods/aws-iam#boundiamprincipalarns) -field of the AWS IAM auth method as follows: - -* By default, the only IAM roles permitted to log in must have an ARN matching the pattern, - `arn:aws:iam:::role/consul-ecs/*`. This allows IAM roles at the role path `/consul-ecs/` - to log in, and only those IAM roles in the same AWS account where the ACL controller is running. -* The role path can be changed by setting the `iam_role_path` input variable for the `mesh-task` and - `acl-controller` modules, or by passing the `-iam-role-path` flag to the `consul-ecs - acl-controller` command. -* Each instance of the auth method is shared by ACL controllers in the same Consul datacenter. Each - controller updates the auth method, if necessary, to include additional entries in the - `BoundIAMPrincipalARNs` list. This enables the use of the auth method with ECS clusters in - different AWS accounts, for example. This does not apply when using Consul Enterprise admin - partitions because auth method instances are not shared by multiple controllers in that case. - -### Task Monitoring - -After startup, the ACL controller monitors tasks in the same ECS cluster where the ACL controller is -running in order to discover newly running tasks and tasks that have stopped. - -The ACL controller cleans up tokens created by `consul login` for tasks that are no longer running. -Normally, each task attempts `consul logout` commands when the task stops to destroy its tokens. -However, in unstable conditions the `consul logout` command may fail to clean up a token. -The ACL controller runs continually to ensure those unused tokens are soon removed. - -### Admin Partitions and Namespaces - -When [admin partitions and namespaces](/consul/docs/ecs/enterprise#admin-partitions-and-namespaces) are enabled, -the ACL controller is assigned to its configured admin partition. It supports one ACL controller instance per ECS -cluster. This results in an architecture with one admin partition per ECS cluster. - -When admin partitions and namespace are enabled, the ACL controller performs the following -additional actions: - -* At startup, creates its assigned admin partition if it does not exist. -* Inspects task tags for new ECS tasks to discover the task's intended partition - and namespace. The ACL controller ignores tasks with a partition tag that does not match the - controller's assigned partition. -* Creates namespaces when tasks start up. Namespaces are only created if they do not exist. -* Creates auth method instances for client and service tokens in controller's assigned admin partition. - -## ECS Health Check Syncing - -If the following conditions apply, ECS health checks automatically sync with Consul health checks for all application containers: - -* marked as `essential` -* have ECS `healthChecks` -* are not configured with native Consul health checks - -The `mesh-init` container creates a TTL health check for every container that fits these criteria -and the `health-sync` container ensures that the ECS and Consul health checks remain in sync. +- An `iam:GetRole` permission to fetch itself. Refer to [IAM Policies](/consul/docs/security/acl/auth-methods/aws-iam#iam-policies) for additional information. +- A `consul.hashicorp.com.service-name` tag on the task role which contains the Consul service name for the application in this task. +- When using Consul Enterprise, add a `consul.hashicorp.com.namespace` tag on the task role indicating the Consul Enterprise namespace where this service is registered. \ No newline at end of file diff --git a/website/content/docs/ecs/deploy/bind-addresses.mdx b/website/content/docs/ecs/deploy/bind-addresses.mdx new file mode 100644 index 0000000000..0eea916687 --- /dev/null +++ b/website/content/docs/ecs/deploy/bind-addresses.mdx @@ -0,0 +1,47 @@ +--- +layout: docs +page_title: Configure the ECS task bind address +description: >- + Learn how to bind workloads to the loopback address, also called localhost and 127.0.0.1, so that your applications only send and receive traffic through Consul service mesh. +--- + +# Configure the ECS task bind address + +This topic describes how to configure the bind address for your workloads to the loopback address. + +## Introduction + +Binding workloads to the loopback address ensures that your application only receives traffic through the dataplane container running in the same task, which limits requests to the service mesh. The loopback address is also called `localhost`, `lo`, and `127.0.0.1`. If your application is listening on all interfaces, such as `0.0.0.0`, then other applications can bypass the proxy and call it directly. + +## Requirements + +Consul service mesh must be deployed to ECS before you can bind a network address. For more information, refer to the following topics: + +- [Deploy Consul to ECS using the Terraform module](/consul/docs/ecs/deploy/install-terraform) +- [Deploy Consul to ECS manually](/consul/docs/ecs/deploy/install-manual) + +## Change the listening address + +Changing the listening address is specific to your language and framework, but binding the loopback address to a dynamic value, such as an environment variable, is a best practice: + +```bash +$ export BIND_ADDRESS="127.0.0.1:8080" +``` + +The following examples demonstrate how to bind the loopback address to an environment variable in golang and Django (Python): + + + +```go +s := &http.Server{ + Addr: os.Getenv("BIND_ADDRESS"), + ... +} +log.Fatal(s.ListenAndServe()) +``` + +```bash +python manage.py runserver "$BIND_ADDRESS" +``` + + diff --git a/website/content/docs/ecs/deploy/configure-routes.mdx b/website/content/docs/ecs/deploy/configure-routes.mdx new file mode 100644 index 0000000000..11014f99a3 --- /dev/null +++ b/website/content/docs/ecs/deploy/configure-routes.mdx @@ -0,0 +1,79 @@ +--- +layout: docs +page_title: Configure routes between ECS tasks +description: >- + Learn how to configure routes between tasks after deploying Consul service mesh to your ECS workloads. +--- + +# Configure routes between ECS tasks + +This topic describes how to configure routes between tasks after registering the tasks to Consul service mesh. + +## Overview + +To enable tasks to call through the service mesh, complete the following steps: + +1. Configure the sidecar proxy to listen on a different port for each upstream service your application needs to call. +1. Modify your application to make requests to the sidecar proxy on the specified port. + +## Requirements + +Consul service mesh must be deployed to ECS before you can bind a network address. For more information, refer to the following topics: + +- [Deploy Consul to ECS using the Terraform module](/consul/docs/ecs/deploy/install-terraform) +- [Deploy Consul to ECS manually](/consul/docs/ecs/deploy/install-manual) + +## Configure the sidecar proxy + +Add the `upstreams` block to your application configuration and specify the following fields: + +- `destinationName`: Specifies the name of the upstream service as it is registered in the Consul service catalog. +- `localBindPort`: Specifies the port that the proxy forwards requests to. You must specify an unused port but it does not need to match the upstream service port. + +In the following example, the route from an application named `web` to an application named `backend` goes through port `8080`: + +```hcl +module "web" { + family = "web" + upstreams = [ + { + destinationName = "backend" + localBindPort = 8080 + } + ] +} +``` + +You must include all upstream services in the `upstream` configuration. + +## Configure your application + +Use an appropriate environment variable in your container definition to configure your application to call the upstream service at the loopback address. + +In the following example, the `web` application calls the `backend` service by sending requests to the +`BACKEND_URL` environment variable: + +```hcl +module "web" { + family = "web" + upstreams = [ + { + destinationName = "backend" + localBindPort = 8080 + } + ] + container_definitions = [ + { + name = "web" + environment = [ + { + name = "BACKEND_URL" + value = "http://localhost:8080" + } + ] + ... + } + ] + ... +} +``` \ No newline at end of file diff --git a/website/content/docs/ecs/deploy/manual.mdx b/website/content/docs/ecs/deploy/manual.mdx new file mode 100644 index 0000000000..708a49213a --- /dev/null +++ b/website/content/docs/ecs/deploy/manual.mdx @@ -0,0 +1,342 @@ +--- +layout: docs +page_title: Deploy Consul to ECS manually +description: >- + Manually install Consul on Amazon Web Services ECS by using the Docker `consul-ecs` image to create task definitions that include required containers. Learn how to configure task definitions with example configurations. +--- + +# Deploy Consul to ECS manually + +The following instructions describe how to use the `consul-ecs` Docker image to manually create the ECS task definition without Terraform. If you intend to peer the service mesh to multiple Consul datacenters or partitions, you must use the Consul ECS Terraform module to install your service mesh on ECS. There is no manual process for deploying a mesh gateway to an ECS cluster. + +## Requirements + +You should have some familiarity with AWS ECS. Refer to [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for details. + +### Secure configuration requirements + +You must meet the following requirements and prerequisites to enable security features in Consul service mesh: + +- Enable [TLS encryption](https://developer.hashicorp.com/consul/docs/security/encryption#rpc-encryption-with-tls) on your Consul servers so that they can communicate security with Consul dataplane containers over gRPC. +- Enable [access control lists (ACLs)](/consul/docs/security/acl) on your Consul servers. ALCs provide authentication and authorization for access to Consul servers on the mesh. +- You should be familiar with specifying sensitive data on ECS. Refer to [Passing sensitive data to a container](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the AWS documentation for additional information. + +You should be familiar with configuring Consul's secure features, including how to create ACL tokens and policies. Refer to the following resources: +- [Create a service token](/consul/docs/security/acl/tokens/create/create-a-service-token) +- [Day 1: Security tutorial](https://developer.hashicorp.com/consul/tutorials/security) for additional information. + +Consul requires a unique IAM role for each ECS task family. Task IAM roles cannot be shared by different task families because the task family is unique to each Consul service. + +## Configure ECS task definition file + +Create a JSON file for the task definition. The task definition is the ECS blueprint for your software services on AWS. Refer to the [ECS task definitions in the AWS documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html) for additional information. + +In addition to your application container, add configurations to your task definition that creates the following Consul containers: + +- Dataplane container +- Control-plane container +- ECS controller container + +## Top-level fields + +The following table describes the top-level fields you must include in the task definition: + +| Field name | Description | Type | +| --- | --- | --- | +| `family` | The task family name. This is used as the Consul service name by default. | string | +| `networkMode` | Must be `awsvpc`, which is the only network mode supported by Consul on ECS. | string | +| `volumes` | Volumes on the host for sharing configuration between containers for initial task setup. You must define a `consul_data` and `consul_binary` bind mount. Bind mounts can be mounted into one or more containers in order to share files among containers. For Consul on ECS, certain binaries and configuration are shared among containers during task startup. | list | +| `containerDefinitions` | Defines the application container that runs in the task. Refer to [Define your application container](#define-your-application-container). | list | + +The following example shows the top-level fields: + +```json +{ + "family": "my-example-client-app", + "networkMode": "awsvpc", + "volumes": [ + { + "name": "consul_data" + }, + { + "name": "consul_binary" + } + ], + "containerDefinitions": [...], + "tags": [ + { + "key": "consul.hashicorp.com/mesh", + "value": "true" + }, + { + "key": "consul.hashicorp.com/service-name", + "value": "example-client-app" + } + ] +} +``` + +## Configure task tags + +The `tags` list must include the following tags if you are using the ECS controller in a [secure configuration](/consul/docs/ecs/deploy/manual#secure-configuration-requirements). +Without these tags, the ACL controller is unable to provision a service token for the task. + +| Tag | Description | Type | Default | +| --- | --- | --- | --- | +| `consul.hashicorp.com/mesh` | Enables the ECS controller. Set to `false` to disable the ECS controller. | String | `true` | +| `consul.hashicorp.com/service-name` | Specifies the name of the Consul service associated with this task. Required if the service name is different than the task `family`. | String | None | +| `consul.hashicorp.com/partition` | Specifies the Consul admin partition associated with this task. | String | `default` | +| `consul.hashicorp.com/namespace` | Specifies the name of the Consul namespace associated with this task. | String | `default` | + +## Define your application container + +Specify your application container configurations in the `containerDefinitions` field. The following table describes all `containerDefinitions` fields: + +| Field name | Description | Type | +| --- | --- | --- | +| `name` | The name of your application container. | string | +| `image` | The container image used to run your application. | string | +| `essential` | Must be `true` to ensure the health of your application container affects the health status of the task. | boolean | +| `dependsOn` | Specifies container dependencies that ensure your application container starts after service mesh setup is complete. Refer to [Application container dependency configuration](#application-container-dependency-configuration) for details. | list | + +Refer to the [ECS Task Definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html) documentation for a complete reference. + +### Application container dependency configuration + +The control-plane and the dataplane containers are dependencies that enforce a specific startup order. The settings ensure that your application container starts after the control-plane container finishes setting up the task and after the dataplane is ready to proxy traffic between this task and the service mesh. + +The `dependsOn` list must include the following maps: + +```json +{ + { + "containerName": "consul-ecs-control-plane", + "condition": "SUCCESS" + }, + { + "containerName": "consul-dataplane", + "condition": "HEALTHY" + } +} +``` + +## Configure the dataplane container + +The dataplane container runs Envoy proxy for Consul service mesh. Specify the fields described in the following table to declare a dataplane container: + +| Field name | Description | Type | +| --- | --- | --- | +| `name` | Specifies the name of the container. This must be `consul-dataplane`. | string | +| `image` | Specifies the Envoy image. This must be a [supported version of Envoy](/consul/docs/connect/proxies/envoy#supported-versions). | string | +| `dependsOn` | Specifies container dependencies that ensure the dataplane container starts after the control-pane container has written the Envoy bootstrap configuration file. Refer to [Dataplane container dependency configuration](#dataplane-container-dependency-configuration) for details. | list | +| `healthCheck` | Must be set as shown above to monitor the health of Envoy's primary listener port, which ties into container dependencies and startup ordering. | map | +| `mountPoints` | Specifies a shared volume so that the dataplane container can access and consume the Envoy configuration file that the control-plane container generates. The keys and values in this configuration must be defined as described in [Dataplane container dependency configuration](#dataplane-container-dependency-configuration). | list | +| `ulimits` | The nofile ulimit must be raised to a sufficiently high value so that Envoy does not fail to open sockets. | list | +| `entrypoint` | Must be set to the custom Envoy entrypoint `consul-ecs envoy-entrypoint` to facilitate graceful shutdown. | list | +| `command` | Specifies the startup command to pass the bootstrap configuration to Envoy. | list | + +### Dataplane container dependency configuration + +The `dependsOn` configuration ensures that the dataplane container starts after the control-plane container successfully writes the Envoy bootstrap configuration file to the shared volume. The `dependsOn` list must include the following map: + +```json +[ + { + "containerName": "consul-ecs-control-plane", + "condition": "SUCCESS" + } +] +``` + +### Dataplane container volume mount configuration + +The `mountPoints` configuration defines a volume and path where dataplane container can consume the Envoy bootstrap configuration file generated by the control-plane container. You must specify the following keys and values: + +```json +{ + "mountPoints": [ + { + "readOnly": true, + "containerPath": "/consul", + "sourceVolume": "consul_data" + } + ], +} +``` + +## Configure the control-plane container + +The control-plane container is the first Consul container to start and set up the instance for Consul service mesh. It registers the service and proxy for this task with Consul and writes the Envoy bootstrap configuration to a shared volume. + +Specify the fields described in the following table to declare the control-plane container: + +| Field name | Description | Type | +| --- | --- | --- | +| `name` | Specifies the name of the container. This must be `control-plane`. | string | +| `image` | Specifies the `consul-ecs` image. Specify the following public AWS registry to avoid rate limits: `public.ecr.aws/hashicorp/consul-ecs` | string | +| `mountPoints` | Specifies a shared volume to store the Envoy bootstrap configuration file that the dataplane container can access and consume. The keys and values in this configuration must be defined as described in [Control-plane shared volume configuration](#control-plane-shared-volume-configuration). | list | +| `command` | Set to `["control-plane"]` so that the container runs the `control-plane` command. | list | +| `environment` | Specifies the `CONSUL_ECS_CONFIG_JSON` environment variable, which configures the container to connect to the Consul servers. Refer to [Control-plane to Consul servers configuration](#control-plane-to-Consul-servers-configuration) for details. | list | + +### Control-plane shared volume configuration + +The `mountPoints` configuration defines a volume and path where the control-plane container stores the Envoy bootstrap configuration file required to start Envoy. You must specify the following keys and values: + +```json +"mountPoints": [ + { + "readOnly": false, + "containerPath": "/consul", + "sourceVolume": "consul_data" + }, + { + "readOnly": true, + "containerPath": "/bin/consul-inject", + "sourceVolume": "consul_binary" + } +], +``` + +### Control-plane to Consul servers configuration + +Provide Consul server connection settings to the mesh task module so that the module can configure the control-plane and ECS controller containers to connect to the servers. + +1. In your `variables.tf` file, define variables for the host URL and TLS settings for gRPC and HTTP traffic. Refer to the [mesh task module reference](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for information about the variables you can define. In the following example, the Consul server address is defined in the `consul_server_hosts` variable: + + ```hcl + variable "consul_server_hosts" { + description = "Address of Consul servers." + type = string + } + + ``` +1. Add an `environment` block to the control-plane and ECS controller containers definition +1. Set the `environment.name` field to the `CONSUL_ECS_CONFIG_JSON` environment variable and the value to `local.encoded_config`. + + ```hcl + environment = [ + { + name = "CONSUL_ECS_CONFIG_JSON", + value = local.encoded_config + } + ] + ``` + When you apply the configuration, the mesh task module interpolates the server configuration variables, builds a `config.tf` file, and injects the settings into the appropriate containers. + +For additional information about the `config.tf` file, refer to the [JSON schema reference documentation](/consul/docs/ecs/reference/config-json-schema). + +## Register the task definition configuration + +Register the task definition with your ECS cluster using the AWS Console, AWS CLI, or another method supported by AWS. You must also create an ECS Service to start tasks using the task definition. Refer to the following ECS documentation for information on how to register the task definition: + +- [Creating a task definition using the classic console](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-task-definition-classic.html) +- [Register task definition CLI](https://docs.aws.amazon.com/cli/latest/reference/ecs/register-task-definition.html) + +## Deploy the controller container + +The controller container runs in a separate ECS task and is responsible for Consul security features. The controller uses the AWS IAM auth method to enable ECS tasks to automatically obtain Consul ACL tokens when the task starts up. Refer to [Consul security components](/consul/docs/ecs/architecture#consul-security-components) for additional information. + +Verify that you have completed the prerequisites described in [Secure configuration requirements](#secure-configuration-requirements) and configure the following components to enable Consul security features. + +- ACL policy +- ECS task role +- Auth method for service tokens +### Create an ACL policy + +On the Consul server, create a policy that grants the following access for the controller: + +- `acl:write` +- `operator:write` +- `node:write` + +The policy allows Consul to generate a token linked to the policy. Refer to [Create a service token](/consul/docs/security/acl/tokens/create/create-a-service-token) for instructions. + +### ECS task role + +1. Create an ECS task role and configure an `iam:GetRole` permission so that it can fetch itself. Refer to [IAM Policies](/consul/docs/security/acl/auth-methods/aws-iam#iam-policies) for instructions. +1. Add an `consul.hashicorp.com.service-name` tag to the task role that contains the Consul service name for the application in the task. +1. When using Consul Enterprise, you must also include the `consul.hashicorp.com.namespace` tag to specify the namespace to register the service in. + +### Configure the auth method for service tokens + +Run the `consul acl auth-method create` command on a Consul server to create an instance of the auth method for service tokens. + +The following example command configures the auth method to associate a service identity +to each token created during login to this auth method instance. + +```shell-session +$ consul acl auth-method create \ + -type aws-iam \ + -name iam-ecs-service-token \ + -description="AWS IAM auth method for ECS service tokens" \ + -config '{ + "BoundIAMPrincipalArns": ["arn:aws:iam:::role/consul-ecs/*"], + "EnableIAMEntityDetails": true, + "IAMEntityTags": [ + "consul.hashicorp.com.service-name" + ] +}' +``` + +If you want to create these resources in a particular partition, include the `-partition ` option when creating Consul ACL roles, policies, auth methods, and binding rules with the Consul CLI. + +You must specify the following flags in the command: + +| Flag | Description | Type | +| --- | --- | --- | +| `-type` | Must be `aws-iam`. | String | +| `-name` | Specify a name for the auth method. Must be unique among all auth methods. | String | +| `-description` | Specify a description for the auth method. | String | +| `-config` | A JSON string containing the configuration for the auth method. Refer to [Auth method `-config` parameter](#auth-method–config-parameter) for details. | String | +| `-partition` | Specifies an admin partition that the auth method is valid for. | String | + +#### Auth method `-config` parameter + +You must specify the following configuration in the `-config` flag: + +| Flag | Description | Type | +| --- | --- | --- | +| `BoundIAMPrincipalArns` | Specifies a list of trusted IAM roles. We recommend using a wildcard to trust IAM roles at a particular path. | List | +| `EnableIAMEntityDetails` | Must be `true` so that the auth method can retrieve IAM role details, such as the role path and role tags. | Boolean | +| `IAMEntityTags` | Specifies a list of IAM role tags to make available to binding rules. Must include the service name tag. | List | + +Refer to the [auth method configuration parameters documentation](/consul/docs/security/acl/auth-methods/aws-iam#config-parameters) for additional information. + +### Create the binding rule + +Run the `consul acl binding-rule create` command on a Consul server to create a binding rule. The rule associates a service identity with each token created on successful login to this instance of the auth method. + +In the following example, Consul takes the service identity name from the `consul.hashicorp.com.service-name` tag specified for authenticating IAM role identity. + +```shell-session +$ consul acl binding-rule create \ + -method iam-ecs-service-token \ + -description 'Bind a service identity from IAM role tags for ECS service tokens' \ + -bind-type service \ + -bind-name '${entity_tags.consul.hashicorp.com.service-name}' +``` + +Note that you must include the `-partition ` option to the Consul CLI when creating Consul ACL roles, policies, auth methods, and binding rules, in order to create these resources in a particular partition. + +### Configure storage for secrets + +Secure and store Consul Server CA certificates so that they are available to ECS tasks. You may require more than one certificate for different Consul protocols. Refer to the following documentation for instructions on how to store and pass secrets to ECS tasks: + +- [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-parameters.html) +- [AWS Secrets Manager](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-secrets.html) + +You can reference stored secrets using their ARN. The examples show ARNs for secrets stored in AWS Secrets Manager: + +- **Consul Server CA Cert for RPC**: `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert` +- **Consul Server CA Cert for HTTPS**: `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-https-ca-cert` + +### Configure audit logging + +You can configure Consul servers connected to your ECS workloads to capture a log of authenticated events. Refer to [Audit Logging](/consul/docs/enterprise/audit-logging) for details. + +## Next steps + +After deploying the Consul service mesh infrastructure, you must still define routes between service instances as well as configure the bind address for your applications so that they only receive traffic through the mesh. Refer to the following topics: + +- [Configure routes between ECS tasks](/consul/docs/ecs/deploy/configure-routes) +- [Configure the ECS task bind address](/consul/docs/ecs/deploy/bind-addresses) diff --git a/website/content/docs/ecs/deploy/migrate-existing-tasks.mdx b/website/content/docs/ecs/deploy/migrate-existing-tasks.mdx new file mode 100644 index 0000000000..77ab4ded7f --- /dev/null +++ b/website/content/docs/ecs/deploy/migrate-existing-tasks.mdx @@ -0,0 +1,99 @@ +--- +layout: docs +page_title: Migrate existing tasks to Consul service mesh +description: >- + You can migrate tasks in existing Amazon Web Services ECS deployments to a service mesh deployed with Terraform. Learn how to convert a task specified as an ECS task definition into a `mesh-task` Terraform module. +--- + +# Migrate existing tasks to Consul on ECS with Terraform + +To migrate existing tasks to Consul, rewrite the existing Terraform code for your tasks so that the container definitions include the [`mesh-task` Terraform module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task). + +Your tasks must already be defined in Terraform using the `ecs_task_definition` resource so that they can then be converted to use the `mesh-task` module. + +## Example + +The following example shows an existing task definition configured in Terraform: + +```hcl +resource "aws_ecs_task_definition" "my_task" { + family = "my_task" + requires_compatibilities = ["FARGATE"] + network_mode = "awsvpc" + cpu = 256 + memory = 512 + execution_role_arn = "arn:aws:iam::111111111111:role/execution-role" + task_role_arn = "arn:aws:iam::111111111111:role/task-role" + container_definitions = jsonencode( + [{ + name = "example-client-app" + image = "docker.io/org/my_task:v0.0.1" + essential = true + portMappings = [ + { + containerPort = 9090 + hostPort = 9090 + protocol = "tcp" + } + ] + cpu = 0 + mountPoints = [] + volumesFrom = [] + }] + ) +} + +resource "aws_ecs_service" "my_task" { + name = "my_task" + cluster = "arn:aws:ecs:us-east-1:111111111111:cluster/my-cluster" + task_definition = aws_ecs_task_definition.my_task.arn + desired_count = 1 + network_configuration { + subnets = ["subnet-abc123"] + } + launch_type = "FARGATE" +} +``` + + +Replace the `aws_ecs_task_definition` resource with the `mesh-task` module so that Consul adds the necessary dataplane containers that enable your task to join the mesh. The `mesh-task` module uses inputs similar to your old ECS task definition but creates a new version of the task definition with additional containers. + +The following Terraform configuration uses the `mesh-task` module to replace the previous example's task definition: + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/mesh-task" + version = "" + + family = "my_task" + container_definitions = [ + { + name = "example-client-app" + image = "docker.io/org/my_task:v0.0.1" + essential = true + portMappings = [ + { + containerPort = 9090 + hostPort = 9090 + protocol = "tcp" + } + ] + cpu = 0 + mountPoints = [] + volumesFrom = [] + } + ] + + port = 9090 + consul_server_hosts = "
" +} + +``` + +Note the following differences: + +- The `execution_role_arn` and `task_role_arn` fields are removed. The `mesh-task` module creates the task and execution roles by default. If you need to use existing IAM roles, set the `task_role` and `execution_role` fields to pass in existing roles. +- The `port` field specifes the port that your application listens on. If your application has no listening port, set `outbound_only = true` and remove the `port` field. +- The `jsonencode()` function is removed from the `container_definitions` field. + +The `mesh-task` module creates a new version of your task definition with the necessary dataplane containers so you can delete your existing `aws_ecs_task_definition` resource. \ No newline at end of file diff --git a/website/content/docs/ecs/deploy/terraform.mdx b/website/content/docs/ecs/deploy/terraform.mdx new file mode 100644 index 0000000000..623fdb87e3 --- /dev/null +++ b/website/content/docs/ecs/deploy/terraform.mdx @@ -0,0 +1,478 @@ +--- +layout: docs +page_title: Deploy Consul to ECS using the Terraform module +description: >- + Terraform modules simplify the process to install Consul on Amazon Web Services ECS. Learn how to create task definitions, schedule tasks for your service mesh, and configure routes with example configurations so that you can Deploy Consul to ECS using Terraform. +--- + +# Deploy Consul to ECS using the Terraform module + +This topic describes how to create a Terraform configuration that deploys Consul service mesh to your ECS cluster workloads. Consul server agents do not run on ECS and must be deployed to another runtime, such as EKS, and connected to your ECS workloads. Refer [Consul on AWS Elastic Container Service overview](/consul/docs/ecs) for additional information. + +## Overview + +Create a Terraform configuration file that includes the ECS task definition and Terraform modules that build the Consul service mesh components. The task definition is the ECS blueprint for your software services on AWS. Refer to the [ECS task definitions documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definitions.html) for additional information. + +You can add the following modules and resources to your Terraform configuration: + +- `mesh-task` module: Adds the Consul ECS control-plane and Consul dataplane containers to the task definition along with your application container. Envoy runs as a subprocess within the Consul dataplane container. +- `aws_ecs_service` resource: Adds an ECS service to run and maintain your task instance. +- `gateway-task` module: Adds mesh gateway containers to the cluster. Mesh gateways enable service-to-service communication across different types of network areas. + +To enable Consul security features for your production workloads, you must also deploy the `controller` module, which provisions ACL tokens for service mesh tasks. + +After defining your Terraform configuration, use `terraform apply` to deploy Consul to your ECS cluster. + +## Requirements + +- You should be familiar with creating Terraform configuration files. Refer to the [Terraform documentation](/terraform/docs) for information about how to get started with Terraform. +- You should be familiar with AWS ECS. Refer to [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) in the Amazon AWS documentation for additional information. +- If you intend to use the `gateway-task` module to deploy mesh gateways, you must enable TLS. Refer to [Configure the ECS controller](#configure-the-ecs-controller) for additional information. + +### Secure configuration requirements + +You must meet the following requirements and prerequisites to enable security features in Consul service mesh: + +- Enable [TLS encryption](/consul/docs/security/encryption#rpc-encryption-with-tls) on your Consul servers so that they can communicate securely with Consul containers over gRPC. +- Enable [access control lists (ACLs)](/consul/docs/security/acl) on your Consul servers. ALCs provide authentication and authorization for access to Consul servers on the mesh. +- You should be familiar with specifying sensitive data on ECS. Refer to [Passing sensitive data to a container](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the AWS documentation for additional information. + +Additionally, Consul requires a unique IAM role for each ECS task family. Task IAM roles cannot be shared by different task families because the task family is unique to each Consul service. + +You should be familiar with configuring Consul's secure features, including how to create ACL tokens and policies. Refer to the following resources for additional information: + +- [Create a service token](/consul/docs/security/acl/tokens/create/create-a-service-token) +- [Day 1: Security tutorial](https://developer.hashicorp.com/consul/tutorials/security) + +## Create the task definition + +Create a Terraform configuration file and add your ECS task definition. The task definition includes your application containers, Consul control-plane container, dataplane container, and controller container. If you intend to peer the service mesh to multiple Consul datacenters or partitions, [add the gateway-task module](#configure-the-gateway-task-module), which deploys gateway containers that enable connectivity between network areas in your network. + +## Configure the mesh task module + +Add a `module` block to your Terraform configuration and specify the following fields: + +- `source`: Specifies the location of the `mesh-task` module. This field must be set to `hashicorp/consul-ecs/aws//modules/mesh-task`. The `mesh-task` module automatically adds the Consul service mesh infrastructure when you apply the Terraform configuration. +- `version`: Specifies the version of the `mesh-task` module to use. +- `family`: Specifies the [ECS task definition family](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family). Consul also uses the `family` value as the Consul service name by default. +- `container_definitions`: Specifies a list of [container definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions) for the task definition. This field is where you include your application containers. + +Refer to the [`mesh-task` module reference documentation in the Terraform registry](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for information about all options you can configure. + +You should also configure the ACL and encryption settings if they are enabled on your Consul servers. Refer to [Enable secure deployment](#enable-secure-deployment) for additional information. + +In the following example, the Terraform configuration file `mesh-task.tf` creates a task definition with an application container called `example-client-app`: + + + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/mesh-task" + version = "" + + family = "my_task" + container_definitions = [ + { + name = "example-client-app" + image = "docker.io/org/my_task:v0.0.1" + essential = true + portMappings = [ + { + containerPort = 9090 + hostPort = 9090 + protocol = "tcp" + } + ] + cpu = 0 + mountPoints = [] + volumesFrom = [] + } + ] + + port = 9090 +} +``` + + + +The following fields are required. Refer to the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for a complete reference. + +## Configure Consul server settings + +Provide Consul server connection settings to the mesh task module so that the module can configure the control-plane and ECS controller containers to connect to the servers. + +1. In your `variables.tf` file, define variables for the host URL and the TLS settings for gRPC and HTTP traffic. Refer to the [mesh task module reference](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/gateway-task?tab=inputs) for information about the variables you can define. In the following example, the Consul server address is defined in the `consul_server_hosts` variable: + + ```hcl + variable "consul_server_hosts" { + description = "Address of Consul servers." + type = string + } + ``` +1. Add an `environment` block to the control-plane and ECS controller containers definition. +1. Set the `environment.name` field to the `CONSUL_ECS_CONFIG_JSON` environment variable and the value to `local.encoded_config`. + + ```hcl + environment = [ + { + name = "CONSUL_ECS_CONFIG_JSON", + value = local.encoded_config + } + ] + ``` + + When you apply the configuration, the mesh task module interpolates the server configuration variables, builds a `config.tf` file, and injects the settings into the appropriate containers. For additional information about the `config.tf` file, refer to the [JSON schema reference documentation](/consul/docs/ecs/reference/consul-server-json). + +## Configure an ECS service to run your task instances + +To start a task using the task definition, add the `aws_ecs_service` resource to your configuration to create an ECS service. [ECS services](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html) are one of the most common ways to start tasks using a task definition. + +Reference the `mesh-task` module's `task_definition_arn` output value in your `aws_ecs_service` resource. The following example adds an ECS service for a task definition referenced in as `module.my_task.task_defintion_arn`: + + + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/mesh-task" + ... +} + +resource "aws_ecs_service" "my_task" { + name = "my_task_service" + task_definition = module.my_task.task_definition_arn + launch_type = "FARGATE" + propagate_tags = "TASK_DEFINITION" + ... +} +``` + + + +Refer to [`aws_ecs_service` in the Terraform registry](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) for a complete configuration reference. + +If you are deploying a test instance of your ECS application, you can apply your configuration in Terraform. Refer to [Run your configuration](#run-your-configuration) for instructions. To configure your deployment for a production environment, you must also deploy the ECS controller module. Refer to [Configure the ECS controller](#configure-the-ecs-controller) for instructions. + +If you intend to leverage multi-datacenter Consul features, such as WAN federation and cluster peering, then you must add the `gateway-task` module for each Consul datacenter in your network. Refer to [Configure the gateway task module](#configure-the-gateway-task-module) for instructions. + +## Configure the gateway task module + +The `gateway-task` module deploys a mesh gateway, which enables service-to-service communication across network areas. Mesh gateways detect the server name indication (SNI) header from the service mesh session and route the connection to the appropriate destination. + +Refer to the following documentation for additional information: + +- [WAN Federation via Mesh Gateways](/consul/docs/connect/gateways/mesh-gateway/wan-federation-via-mesh-gateways) +- [Service-to-service Traffic Across Datacenters](/consul/docs/connect/gateways/mesh-gateway/service-to-service-traffic-wan-datacenters) + +To use mesh gateways, TLS must be enabled in your cluster. Refer to the [requirements section](#requirements) for additional information. + +1. Add a `module` block to your Terraform configuration file and specify a label. The label is a unique identifier for the gateway. +1. Add a `source` to the `module` and specify the location of the `gateway-task`. The value must be `hashicorp/consul-ecs/aws//modules/gateway-task`. +1. Specify the following required inputs: + - `ecs_cluster_arn`: The ARN of the ECS cluster for the gateway. + - `family`: Specifies a name for multiple versions of the task. Refer to the [AWS documentation](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family) for details. + - `kind`: Set to `mesh-gateway` + - `subnets`: Specifies a list of subnet IDs where the gateway task should be deployed. +1. If you are deploying to a production environment, you must also add the `acl` and `tls` configurations. Refer to [Configure the ECS controller](#configure-the-ecs-controller) for details. +1. Configure any additional parameters necessary for your environment. Refer to the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/gateway-task?tab=inputs) for information about all parameters. + +The following example defines a mesh gateway task called `my-gateway`: + + + +```hcl +module "my_mesh_gateway" { + source = "hashicorp/consul-ecs/aws//modules/gateway-task" + version = "" + kind = "mesh-gateway" + + family = "my-gateway" + ecs_cluster_arn = "" + subnets = [""] + consul_server_hosts = "
" + tls = true + consul_ca_cert_arn = "" +} +``` + + + +Refer to [gateway-task module in the Terraform registry](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/gateway-task?tab=inputs) for a complete reference. + +Refer to the [gateway task configuration examples](#gateway-task-configuration-examples) for additional example configurations. + +## Configure the ECS controller + +Deploy the ECS controller container to its own ECS task in the cluster. Refer to [ECS controller container](/consul/docs/ecs/reference/architecture#ecs-controller) for details about the container. + +Verify that you have completed the prerequisites described in [Secure configuration requirements](#secure-configuration-requirements) and complete the following steps to configure the controller container. + +### Create a an ACL token for the controller + +1. On the Consul server, create a policy that grants the following access for the controller: + + - `acl:write` + - `operator:write` + - `node:write` + + The policy allows Consul to generate a token linked to the policy. Refer to [Create a service token](/consul/docs/security/acl/tokens/create/create-a-service-token) for instructions. +1. Create a token and link it to the ACL controller policy. Refer to the [ACL tokens documentation](/consul/docs/security/acl/tokens) for instructions. + +### Configure an AWS secrets manager secret + +Add the `aws_secretsmanager_secret` resource to your Terraform configuration and specify values for retrieving the CA and TLS certificates. The resource enables services to communicate over TLS and present ACL tokens. The ECS controller also uses the secret manager to retrieve the value of the bootstrap token. + +In the following example, Terraform creates the CA certificates for gRPC and HTTPS in the secrets manager. Consul retrieves the CA certificate PEM file from the secret manager so that the mesh task can use TLS for HTTP and gRPC traffic: + + + +```hcl +resource "tls_private_key" "ca" { + algorithm = "ECDSA" + ecdsa_curve = "P384" +} + +resource "tls_self_signed_cert" "ca" { + private_key_pem = tls_private_key.ca.private_key_pem + + subject { + common_name = "Consul Agent CA" + organization = "HashiCorp Inc." + } + + // 5 years. + validity_period_hours = 43800 + + is_ca_certificate = true + set_subject_key_id = true + + allowed_uses = [ + "digital_signature", + "cert_signing", + "crl_signing", + ] +} + +resource "aws_secretsmanager_secret" "ca_key" { + name = "${var.name}-${var.datacenter}-ca-key" + recovery_window_in_days = 0 +} + +resource "aws_secretsmanager_secret_version" "ca_key" { + secret_id = aws_secretsmanager_secret.ca_key.id + secret_string = tls_private_key.ca.private_key_pem +} + +resource "aws_secretsmanager_secret" "ca_cert" { + name = "${var.name}-${var.datacenter}-ca-cert" + recovery_window_in_days = 0 +} + +resource "aws_secretsmanager_secret_version" "ca_cert" { + secret_id = aws_secretsmanager_secret.ca_cert.id + secret_string = tls_self_signed_cert.ca.cert_pem +} +``` + + + +Note that you could use a single `CERT PEM` for both variables. The `consul_ca_cert_arn` is the default ARN applicable to both the protocols. You can also use protocol-specific certificate PEMs with the `consul_https_ca_cert_arn` and `consul_grpc_ca_cert_arn` variables. + +The following Terraform configuration passes the generated CA certificate ARN to the `mesh-task` module and ensures that the CA certificate and PEM variable are set for both HTTPS and gRPC communication. + + + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/mesh-task" + version = "" + + ... + + tls = true + consul_ca_cert_arn = aws_secretsmanager_secret.ca_cert.arn +} +``` + + + +### Enable secure deployment + +To enable secure deployment, add the following configuration to the task module. + + + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/mesh-task" + version = "" + + ... + + tls = true + consul_grpc_ca_cert_arn = aws_secretsmanager_secret.ca_cert.arn + + acls = true + consul_server_hosts = "https://consul-server.example.com" + consul_https_ca_cert_arn = aws_secretsmanager_secret.ca_cert.arn +} + +``` + + + +### Complete configuration examples + +The [terraform-aws-consul-ecs GitHub repository](https://github.com/hashicorp/terraform-aws-consul-ecs/tree/main/examples) contains examples that you can reference to help you deploy Consul service mesh to your ECS workloads. + +## Apply your Terraform configuration + +Run Terraform to create the task definition. + +Save the Terraform configuration for the task definition to a file, such as `mesh-task.tf`. +You should place this file in a directory alongside other Terraform configuration files for your project. + +The `mesh-task` module requires the AWS Terraform provider. The following example shows how to include +and configure the AWS provider in a file called `provider.tf`. Refer to [AWS provider in the Terraform registry](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) +for additional documentation and specifications. + + + +```hcl +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "" + } + } +} + +provider "aws" { + region = "" + ... +} +``` + + + +Specify any additional AWS resources for your project in Terraform configuration files +in the same directory. The following example shows a basic project directory: + +```shell-session +$ ls +mesh-task.tf +provider.tf +... +``` + +Issue the following commands to run the configuration: + +1. `terraform init`: This command downloads dependencies, such as Terraform providers. +1. `terraform apply`: This command directs Terraform to create the AWS resources, such as the task definition from the `mesh-task` module. + +Terraform reads all files in the current directory that have a `.tf` file extension. +Refer to the [Terraform documentation](/terraform/docs) for more information and Terraform best practices. + +## Next steps + +After deploying the Consul service mesh infrastructure, you must still define routes between service instances as well as configure the bind address for your applications so that they only receive traffic through the mesh. Refer to the following topics: + +- [Configure routes between ECS tasks](/consul/docs/ecs/deploy/configure-routes) +- [Configure the ECS task bind address](/consul/docs/ecs/deploy/bind-addresses) + +## Gateway task configuration examples + +The following examples illustrate how to configure the `gateway-task` for different use cases. + +### Ingress + +Mesh gateways need to be reachable over the WAN to route traffic between datacenters. Configure the following options in the `gateway-task` module to enable ingress through the mesh gateway. + +| Input variable | Type | Description | +| --- | --- | --- | +| `lb_enabled` | Boolean | Set to `true` to automatically deploy and configure a network load balancer for ingress to the mesh gateway. | +| `lb_vpc_id` | string | Specifies the VPC to launch the load balancer in. | +| `lb_subnets` | list of strings | Specifies one or more public subnets to associate with the load balancer. | + + + +```hcl +module "my_mesh_gateway" { + ... + + lb_enabled = true + lb_vpc_id = "" + lb_subnets = [""] +} +``` + + + +Alternatively, you can manually configure ingress to the mesh gateway and provide the `wan_address` and `wan_port` inputs to the `gateway-task` module. The `wan_port` field is optional. Port `8443` is used by default. + + + +```hcl +module "my_mesh_gateway" { + ... + + wan_address = "" + wan_port = +} +``` + + + +Mesh gateways route L4 TCP connections and do not terminate mTLS sessions. If you manually configure [AWS Elastic Load Balancing](https://aws.amazon.com/elasticloadbalancing/) for ingress to a mesh gateway, you must use an [AWS Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) or a [Classic Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/introduction.html). + +### ACLs + +When ACLs are enabled, configure the following options in the `gateway-task` module. + +| Option | Type | Description | +| --- | --- | --- | +| `acl` | Boolean | Set to `true` when ACLs are enabled. | +| `consul_server_hosts` | string | Specifies the HTTP `address` of the Consul server. Required for the mesh gateway task to log into Consul using the IAM auth method so that it can obtain its client and service tokens. | +| `consul_https_ca_cert_arn` | string | Specifies ARN of the Secrets Manager secret that contains the certificate for the Consul HTTPS API. | + + + +```hcl +module "my_mesh_gateway" { + ... + + acls = true + consul_server_hosts = "" + tls = true + consul_https_ca_cert_arn = "" +} +``` + + + +### WAN federation + +Configure the following options in the `gateway-task` to enable [WAN federation through mesh gateways](/consul/docs/connect/gateways/mesh-gateway/wan-federation-via-mesh-gateways). + +| Option | Type | Description | +| --- | --- | --- | +| `consul_datacenter` | string | Specifies the name of the local Consul datacenter. | +| `consul_primary_datacenter` | string | Specifies the name of the primary Consul datacenter. | +| `enable_mesh_gateway_wan_federation` | Boolean | Set to `true` to enable WAN federation. | +| `enable_acl_token_replication` | Boolean | Set to `true` to enable ACL token replication and allow the creation of local tokens secondary datacenters. | + +The following example shows how to configure the `gateway-task` module. + + + +```hcl +module "my_mesh_gateway" { + ... + + enable_mesh_gateway_wan_federation = true +} +``` + + + +When federating Consul datacenters over the WAN with ACLs enabled, [ACL Token replication](/consul/docs/security/acl/acl-federated-datacenters) must be enabled on all server and client agents in all datacenters. diff --git a/website/content/docs/ecs/enterprise.mdx b/website/content/docs/ecs/enterprise.mdx index d8ea4079c8..c07a41c58d 100644 --- a/website/content/docs/ecs/enterprise.mdx +++ b/website/content/docs/ecs/enterprise.mdx @@ -26,7 +26,7 @@ module "my_task" { ## Licensing -!> **Warning:** Consul Enterprise is currently only fully supported when [ACLs are enabled](/consul/docs/ecs/terraform/secure-configuration). +!> **Warning:** Consul Enterprise is currently only fully supported when [ACLs are enabled](/consul/docs/ecs/deploy/terraform#secure-configuration-requirements). Consul Enterprise [requires a license](/consul/docs/enterprise/license/overview). If running Consul on ECS with ACLs enabled, the license will be automatically pulled down from Consul servers. @@ -95,7 +95,7 @@ module "acl_controller" { -Services are assigned to admin partitions and namespaces through the use of [task tags](/consul/docs/ecs/manual/install#task-tags). +Services are assigned to admin partitions and namespaces through the use of [task tags](/consul/docs/ecs/deploy/manual#configure-task-tags). The `mesh-task` module automatically adds the necessary tags to the task definition. If the ACL controller is configured for admin partitions, services on the mesh will always be assigned to an admin partition and namespace. If the `mesh-task` does not define diff --git a/website/content/docs/ecs/index.mdx b/website/content/docs/ecs/index.mdx index 963604188a..7238765af2 100644 --- a/website/content/docs/ecs/index.mdx +++ b/website/content/docs/ecs/index.mdx @@ -2,39 +2,53 @@ layout: docs page_title: Consul on AWS Elastic Container Service (ECS) Overview description: >- - Consul's architecture adapts to Amazon Web Services ECS by running each task with an application container, a client agent, and an Envoy proxy. Learn how Consul service mesh works on ECS and find getting started tutorials for several scenarios. + You can deploy Consul service mesh applications to Amazon Web Services ECS by running each task with an application container, a client agent, and an Envoy proxy. Learn how Consul service mesh works on ECS and find getting started tutorials for several scenarios. --- -# Consul on AWS Elastic Container Service (ECS) Overview +# Consul on AWS Elastic Container Service (ECS) overview +This overview provides information about connecting your workloads managed by [AWS Elastic Container Service (ECS)](https://aws.amazon.com/ecs/) to a Consul service mesh. A Consul service mesh automates service-to-service authorization and encryption across your Consul services. You can use a service mesh in ECS networks to secure communication between ECS tasks and communication between tasks and external services. -You can deploy Consul service mesh applications to [AWS Elastic Container Service](https://aws.amazon.com/ecs/) (ECS) using either our official [Terraform modules](/consul/docs/ecs/terraform/install) or by [manually configuring the task definition](/consul/docs/ecs/manual/install). -## Service Mesh +## Workflow -Using Consul on AWS ECS enables you to add your ECS tasks to the service mesh and -take advantage of features such as zero-trust-security, intentions, observability, -traffic policy, and more. You can also connect service meshes so that services deployed across your infrastructure environments can communicate. +You can install Consul on ECS with the [HashiCorp Terraform modules](/consul/docs/ecs/deploy/terraform) or by [manually configuring the task definition](/consul/docs/ecs/deploy/manual). We strongly recommend using the Terraform modules and resources because Terraform automatically builds and enables Consul service mesh containers for your workloads. The Terraform module installation method also allows you to add your existing ECS task definitions to the Consul service mesh without additional configuration. -## Architecture +### Terraform module installation -![Consul on ECS Architecture](/img/consul-ecs-arch.png) +1. Create and run a Terraform configuration that includes the ECS task, modules, and resources. +1. Configure routes between ECS tasks in your cluster. Once the service mesh is built, you must define paths for traffic between services. +1. Configure the ECS bind address. Binding to the loopback address allows the sidecar proxy running in the same task to only make requests within the service mesh. -Consul on ECS follows an [architecture](/consul/docs/architecture) similar to other platforms, but each ECS task is a -Consul node. An ECS task runs the user application container(s), as well as a Consul client container for control plane -communication and an [Envoy](https://envoyproxy.io/) sidecar proxy container to facilitate data plane communication for -[Consul service mesh](/consul/docs/connect). -For a detailed architecture overview, see the [Architecture](/consul/docs/ecs/architecture) page. +### Manual installation -## Getting Started +To manually install Consul, you must create definitions for each container that operates in the ECS cluster. Refer to [Architecture](/consul/docs/ecs/architecture) for information about the Consul containers you must deploy. Note that there is no manual process for creating gateway task containers. Gateways enable you to connect multiple datacenters or admin partitions. You must use Terraform if you want to deploy gateways to your network. -There are several ways to get started with Consul with ECS. +## Guidance -- The [Integrate your AWS ECS services into Consul service mesh](/consul/tutorials/cloud-integrations/consul-ecs) tutorial shows how to use Terraform to run Consul service mesh applications on ECS with self-managed Consul or HCP-managed Consul. -- The [Consul with Dev Server on Fargate](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-fargate) example installation deploys a sample application in ECS using the Fargate launch type. -- The [Consul with Dev Server on EC2](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-ec2) example installation deploys a sample application in ECS using the EC2 launch type. +Refer to the following documentation and tutorials for additional guidance. -Refer to the [Requirements](/consul/docs/ecs/requirements) and use one of the following sets of instructions when you're ready to install Consul on an existing ECS cluster and add tasks to the service mesh: +### Tutorials -- [Install with Terraform](/consul/docs/ecs/terraform/install) -- [Install Manually](/consul/docs/ecs/manual/install) +- [Integrate your AWS ECS services into Consul service mesh](/consul/tutorials/cloud-integrations/consul-ecs): Shows how to use Terraform to run Consul service mesh applications on ECS with self-managed Consul or HCP-managed Consul. + +You can also refer to the following example configurations: + +- [Examples on GitHub](https://github.com/hashicorp/terraform-aws-consul-ecs/tree/main/examples) +- [Consul with dev server on ECS using the Fargate launch type](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-fargate) +- [Consul with dev server onn ECS using the EC2 launch type](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/examples/dev-server-ec2) + +### Documentation + +- [Install Consul on ECS with Terraform](/consul/docs/ecs/deploy/terraform) +- [Configure routes between ECS tasks](/consul/docs/ecs/deploy/configure-routes) +- [Configure the ECS task bind address](/consul/docs/ecs/deploy/bind-addresses) +- [Install Consul on ECS manually](/consul/docs/ecs/deploy/manual) + +### Reference + +- [Architecture](/consul/docs/ecs/architecture) +- [Technical specifications](/consul/docs/ecs/tech-specs) +- [Task configuration reference](/consul/docs/ecs/reference/configuration-reference) +- [Cross-compatibility reference](/consul/docs/ecs/reference/compatibility) +- [Consul server JSON schema reference](/consul/docs/ecs/reference/consul-server-json) \ No newline at end of file diff --git a/website/content/docs/ecs/manual/acl-controller.mdx b/website/content/docs/ecs/manual/acl-controller.mdx deleted file mode 100644 index bb7d88638a..0000000000 --- a/website/content/docs/ecs/manual/acl-controller.mdx +++ /dev/null @@ -1,190 +0,0 @@ ---- -layout: docs -page_title: ACL Controller Manual Installation - Consul on AWS Elastic Container Service (ECS) -description: >- - An ACL Controller is required to configure the AWS IAM auth method on Amazon Web Services ECS. Learn how to manually install ACL controllers by defining a task and a service and then configuring role policies. ---- - -# Manual Installation of ACL Controller for Consul on AWS Elastic Container Service (ECS) - -This topic describes how to manually deploy the ACL controller, which will automatically configure the [AWS IAM Auth Method](/consul/docs/security/acl/auth-methods/aws-iam). If you are using Terraform, refer to the [Terraform Secure Configuration](/consul/docs/ecs/terraform/secure-configuration) page to deploy the ACL controller. - -## Prerequisites - -* Your application tasks must include certain tags to be compatible with the ACL controller. -Refer to the [Task Tags](/consul/docs/ecs/manual/install#task-tags) section of the installation page. -* You should be familiar with configuring Consul's secure features, including how to create ACL tokens and policies. Refer to the [Consul Security tutorials](/consul/tutorials/security) for an introduction and the [ACL system](/consul/docs/security/acl) documentation for more information. -* If you are using Consul with multiple ECS clusters, each cluster requires its own instance of the ACL controller. - -## Set Up Secrets - -Before deploying the ACL controller for the first time, you must [store the following secrets](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) from Consul in AWS Secrets Manager. - -| Secret | Sample Secret Name | Description | -| --------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Consul server CA cert | `my-consul-ca-cert` | The Consul server CA Cert for the HTTPS interface. This is required if the Consul server uses a self-signed or internal CA. It is not required for Consul servers in HCP. | -| Bootstrap ACL Token | `my-consul-bootstrap-token` | A Consul ACL token with `acl:write` and `operator:write` permissions. | - -## Task Definition - -You must create a task definition to deploy the ACL controller in your ECS cluster. -The ACL controller must run in the same ECS cluster that hosts your service mesh application -tasks. - -The following example shows how the task definition should be configured for the ACL controller. - -```json -{ - "family": "my-consul-acl-controller", - "networkMode": "awsvpc", - "containerDefinitions": [ - { - "name": "acl-controller", - "image": "public.ecr.aws/hashicorp/consul-ecs:", - "essential": true, - "command": ["acl-controller", "-iam-role-path", "/consul-ecs/"], - "secrets": [ - { - "name": "CONSUL_HTTP_TOKEN", - "valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-bootstrap-token" - }, - { - "name": "CONSUL_CACERT_PEM", - "valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert" - } - ], - "environment": [ - { - "name": "CONSUL_HTTP_ADDR", - "value": "" - } - ] - } - ] -} -``` - -You must include the following top-level fields. - -| Field name | Type | Description | -| ----------- | ------- | ---------------------------------------------------------------------------- | -| `family` | string | The task family name of your choice. | -| `networkMode` | string | Must be `awsvpc`, which is the only network mode supported by Consul on ECS. | - -In the `containerDefinitions` list, include one container with the following fields. - -| Field name | Type | Description | -| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | The container name, which should be `acl-controller` | -| `image` | string | The `consul-ecs` image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul-ecs`, to avoid rate limits. | -| `command` | list | Should be set as shown. The startup command for the ACL controller. | -| `essential` | boolean | Must be `true` to ensure the health of your application container affects the health status of the task. | -| `secrets` | list | Should be set as shown. Configures the secrets the ECS service will retrieve and set as environment variables in the `acl-controller` container. | -| `environment` | string | Must be set as shown. Configures environment variables that the ECS service will set in the `acl-controller` container. Must set the `CONSUL_HTTP_ADDR` environment variable to the HTTP(S) address of the Consul servers. | - -The following CLI options are available in the `command` field of the container definition. - -| Flag | Type | Description | -| ------------------ | ------- | --------------------------------------------------------------------------------------------- | -| `-iam-role-path` | string | Specifies the path to IAM roles trusted by the AWS IAM auth method created by the controller. | -| `-log-level` | string | The log level for the ACL controller. Can be set to `DEBUG` for additional detail. | - -The following describes the entries to include in the `secrets` list. - -| Name | Description | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| `CONSUL_HTTP_TOKEN` | Must be set to the secret containing the bootstrap ACL token. | -| `CONSUL_CACERT_PEM` | If applicable, should be set to the secret containing the Consul server CA certificate. This must not be set when using Consul servers in HCP. | - -## ECS Service - -Once the task definition is created, define an ECS service in order to start an ACL controller task. - -The following example contains the recommended settings for the ACL controller. Refer to -the [ECS service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service_definition_parameters.html) documentation -to complete the remaining details for your use case. - -```json -{ - "cluster": "" - "desiredCount": 1, - "launchType": "FARGATE", - "serviceName": "my-acl-controller", - "taskDefinition": "", - ... -} -``` - -| Field name | Type | Description | -| ---------------- | ------- | ---------------------------------------------------------------------------------------------------------------- | -| `cluster` | string | Set to your ECS cluster name or ARN. This must be the same ECS cluster where your service mesh applications run. | -| `desiredCount` | integer | Must be `1`. Only one instance of the ACL controller should run per ECS cluster. | -| `launchType` | string | Consul on ECS supports both the `FARGATE` and `EC2` launch types. | -| `serviceName` | string | The service name of your choice. | -| `taskDefinition` | string | Must be set to the ACL controller [task definition](/consul/docs/ecs/manual/acl-controller#task-definition). | - -## AWS IAM Roles - -The ECS task and execution roles must be configured to allow the ACL controller access -to the ECS API and Secrets Manager API. - -### Task Role Policy - -The following example shows the policy needed for the ECS task role for the ACL controller. -This grants the ACL controller permission to list tasks, describe tasks, and read and update -secrets. - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "ecs:ListTasks", - "ecs:DescribeTasks" - ], - "Resource": ["*"] - } - ] -} -``` - -The following are the required permissions. - -| Action | Resource | Description | -| --------------------- | --------- | ------------------------------------------------------------ | -| `ecs:ListTasks` | `*` | Allow the ACL controller to watch for new tasks. | -| `ecs:DescribeTasks` | `*` | Allow the ACL controller to retrieve details for new tasks. | - -### Execution Role Policy - -The following IAM policy document allows ECS to retrieve secrets needed -to start the ACL controller task from AWS Secrets Manager, including the ACL -bootstrap token. - -The following example shows the policy needed for the execution role. - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "secretsmanager:GetSecretValue" - ], - "Resource": [ - "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-bootstrap-token", - "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert" - ] - } - ] -} -``` - -The following are the required permissions. - -| Action | Resource | Description | -| ------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------- | -| `secretsmanager:GetSecretValue` | `arn:aws:secretsmanager:us-west-2:000000000000:secret:` | Allow ECS to retrieve this secret and inject the secret into the task. | diff --git a/website/content/docs/ecs/manual/install.mdx b/website/content/docs/ecs/manual/install.mdx deleted file mode 100644 index f22560e64e..0000000000 --- a/website/content/docs/ecs/manual/install.mdx +++ /dev/null @@ -1,556 +0,0 @@ ---- -layout: docs -page_title: Install Manually - Consul on AWS Elastic Container Service (ECS) -description: >- - Manually install Consul on Amazon Web Services ECS by using the Docker `consul-ecs` image to create task definitions that include required containers. Learn how to configure task definitions with example configurations. ---- - -# Manual Installation of Consul on AWS Elastic Container Service (ECS) - -The following instructions describe how to use the [`consul-ecs` Docker image](https://gallery.ecr.aws/hashicorp/consul-ecs) to manually create the ECS task definition without Terraform. If you prefer to use Terraform, refer to [Consul ECS Terraform module](/consul/docs/ecs/terraform/install). - -If you intend to peer the service mesh to multiple Consul datacenters or partitions, you must use the Consul ECS Terraform module to install your service mesh on ECS. Manually configuring mesh gateways without using the `gateway-task` Terraform module is not supported. - -This topic does not include instructions for creating all AWS resources necessary to install Consul, such as a VPC or the ECS cluster. Refer to the linked guides in the [Getting Started](/consul/docs/ecs#getting-started) section for complete, runnable examples. - -## Prerequisites - -You should have some familiarity with AWS ECS. See [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for details. - -## Task Definition - -Configure a task definition that creates the containers: - -- Your application container -- An Envoy sidecar-proxy container -- A Consul client container -- A `consul-ecs-mesh-init` container for service mesh setup -- (Optional) A `consul-ecs-health-sync` container to sync ECS health checks into Consul - -## Top-level fields - -Your task definition must include the following top-level fields. - -The `volumes` list contains two [bind mounts](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/bind-mounts.html), -named `consul_data` and `consul_binary`. Bind mounts are directories on the host which can be mounted into one or more containers -in order to share files among containers. For Consul on ECS, certain binaries and configuration are shared among containers -during task startup. - -```json -{ - "family": "my-example-client-app", - "networkMode": "awsvpc", - "volumes": [ - { - "name": "consul_data" - }, - { - "name": "consul_binary" - } - ], - "containerDefinitions": [...], - "tags": [ - { - "key": "consul.hashicorp.com/mesh", - "value": "true" - }, - { - "key": "consul.hashicorp.com/service-name", - "value": "example-client-app" - } - ] -} -``` - -| Field name | Type | Description | -| ---------------------- | ------ | ------------------------------------------------------------------------------------------------------------------ | -| `family` | string | The task family name. This is used as the Consul service name by default. | -| `networkMode` | string | Must be `awsvpc`, which is the only network mode supported by Consul on ECS. | -| `volumes` | list | Must be defined as shown above. Volumes are used to share configuration between containers for initial task setup. | -| `containerDefinitions` | list | The list of containers to run in this task (see [Application container](#application-container)). | - -### Task Tags - -The `tags` list must include the following if you are using the ACL controller in a [secure configuration](/consul/docs/ecs/manual/secure-configuration). -Without these tags, the ACL controller will be unable to provision a service token for the task. - -| Tag Key | Tag Value | Description | -| ----------------------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `consul.hashicorp.com/mesh` | `true` (string) | The ACL controller ignores tasks without this tag set to `true`. | -| `consul.hashicorp.com/service-name` | Consul service name | Specifies the Consul service associated with this task. Required if the service name is different than the task `family`. | -| `consul.hashicorp.com/partition` | Consul admin partition | Specifies the Consul admin partition associated with this task. Defaults to the `default` admin partition if omitted. | -| `consul.hashicorp.com/namespace` | Consul namespace | Specifies the Consul namespace associated with this task. Defaults to the `default` namespace if omitted. | - -## Application container - -First, include your application container in the `containerDefinitions` list -in the task definition. - -Ensure that the `containerName` and `condition` fields in the `dependsOn` list -are specified as described in the following example. These are container dependencies, -which must be used to enforce a specific [startup order](/consul/docs/ecs/architecture#task-startup). -By using the following settings, your application container will start after `consul-ecs-mesh-init` -has completed task setup and after `sidecar-proxy` is ready to proxy traffic between -this task and the service mesh. - -```json -{ - "containerDefinitions": [ - { - "name": "example-client-app", - "image": "docker.io/org/my_task:v0.0.1", - "essential": true, - "dependsOn": [ - { - "containerName": "consul-ecs-mesh-init", - "condition": "SUCCESS" - }, - { - "containerName": "sidecar-proxy", - "condition": "HEALTHY" - } - ], - ... - } - ] -} -``` - -| Field name | Type | Description | -| ----------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | The name of your application container. | -| `image` | string | The container image used to run your application. | -| `essential` | boolean | Must be `true` to ensure the health of your application container affects the health status of the task. | -| `dependsOn` | list | Must be set as shown above. Container dependencies ensure your application container starts after service mesh setup is complete. | - -See the [ECS Task Definition](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html) documentation for a complete reference. - -## `sidecar-proxy` container - -The `sidecar-proxy` container runs [Envoy proxy](/consul/docs/connect/proxies/envoy) for Consul service mesh. In most cases, the container should contain the following parameters and values. - -The `mountPoints` list must be set as shown in the following example. This will mount the shared `consul_data` volume into the -`sidecar-proxy` container at the path `/consul`. This volume is where the `consul-ecs-mesh-init` container copies the `envoy-bootstrap.json` -file and the `consul-ecs` binary, which are required to start Envoy. The `dependsOn` list must also be defined as follows to ensure the -`sidecar-proxy` container starts after `consul-ecs-mesh-init` has successfully written these files to the shared volume. - - - -```json -{ - "containerDefinitions": [ - { - "name": "example-client-app", - "image": "docker.io/org/my_task:v0.0.1", - ... - }, - { - "name": "sidecar-proxy", - "image": "envoyproxy/envoy-alpine:", - "essential": false, - "dependsOn": [ - { - "containerName": "consul-ecs-mesh-init", - "condition": "SUCCESS" - } - ], - "healthCheck": { - "retries": 3, - "command": ["nc", "-z", "127.0.0.1", "20000"], - "timeout": 5, - "interval": 30 - }, - "mountPoints": [ - { - "readOnly": true, - "containerPath": "/consul", - "sourceVolume": "consul_data" - } - ], - "ulimits": [ - { - "name": "nofile", - "softLimit": 1048576, - "hardLimit": 1048576 - } - ], - "command": ["envoy", "--config-path", "/consul/envoy-bootstrap.json"], - "entryPoint": ["/consul/consul-ecs", "envoy-entrypoint"], - } - ] -} -``` - - - -The following table describes the necessary configuration settings. - -| Field name | Type | Description | -| ------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | The container name, which must be `sidecar-proxy`. | -| `image` | string | The Envoy image. This must be a [supported version of Envoy](/consul/docs/connect/proxies/envoy#supported-versions). | -| `dependsOn` | list | Must be set as shown above to ensure Envoy starts after the `consul-ecs-mesh-init` container has written the `envoy-bootstrap.json` config file for Envoy. | -| `healthCheck` | list | Must be set as shown above to monitor the health of Envoy's primary listener port, which ties into container dependencies and startup ordering. | -| `mountPoints` | list | Must be set as shown above to access the files shared in the `/consul` directory, like the Envoy bootstrap configuration file and the `consul-ecs` binary. | -| `ulimits` | list | The `nofile` ulimit must be raised to a sufficiently high value so that Envoy does not fail to open sockets. | -| `entrypoint` | list | Must be set to the custom Envoy entrypoint, `consul-ecs envoy-entrypoint`, to facilitate graceful shutdown. | -| `command` | list | The startup command. This passes the bootstrap configuration to Envoy. | - --> **NOTE**: Envoy and Consul must be compatible versions. See the [supported versions of Envoy](/consul/docs/connect/proxies/envoy#supported-versions) in the Consul documentation. - -## `consul-client` container - -Each task must include a Consul client container in order for the task to join your Consul cluster. - - - -```json -{ - "containerDefinitions": [ - { - "name": "example-client-app", - "image": "docker.io/org/my_task:v0.0.1", - ... - }, - { - "name": "sidecar-proxy", - "image": "envoyproxy/envoy-alpine:", - ... - } - { - "name": "consul-client", - "image": "public.ecr.aws/hashicorp/consul:", - "mountPoints": [ - { - "readOnly": false, - "containerPath": "/consul", - "sourceVolume": "consul_data" - }, - { - "containerPath": "/bin/consul-inject", - "sourceVolume": "consul_binary" - } - ], - "entryPoint": ["/bin/sh", "-ec"], - "command": [ - "cp /bin/consul /bin/consul-inject/consul\n\nECS_IPV4=$(curl -s $ECS_CONTAINER_METADATA_URI_V4 | jq -r '.Networks[0].IPv4Addresses[0]')\n\n\ncat << EOF > /consul/agent-defaults.hcl\naddresses = {\n dns = \"127.0.0.1\"\n grpc = \"127.0.0.1\"\n http = \"127.0.0.1\"\n}\nadvertise_addr = \"$ECS_IPV4\"\nadvertise_reconnect_timeout = \"15m\"\nclient_addr = \"0.0.0.0\"\ndatacenter = \"dc1\"\nenable_central_service_config = true\nleave_on_terminate = true\nports {\n grpc = 8502\n}\nretry_join = [\n \"\",\n]\ntelemetry {\n disable_compat_1.9 = true\n}\n\nEOF\n\ncat << EOF > /consul/agent-extra.hcl\naddresses = {\n dns = \"0.0.0.0\"\n}\nlog_level = \"debug\"\n\nEOF\n\nexec consul agent \\\n -data-dir /consul/data \\\n -config-file /consul/agent-defaults.hcl \\\n -config-file /consul/agent-extra.hcl\n" - ] - } - ] -} -``` - - - -| Field name | Type | Description | -| ------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | The container name, which should always be `consul-client`. | -| `image` | string | The Consul image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul`, to avoid rate limits. | -| `mountPoints` | list | Must be set as shown above. Volumes are mounted to share information with other containers for task setup. | -| `entrypoint` | list | Must be set to a plain shell so that the startup `command` works properly. | -| `command` | list | Specifies the contents of the [startup script](#consul-client-startup-script). Copy the script and format it into a JSON string. | - -### Consul client startup script - -The following script is used to start the Consul client for Consul on ECS. - -```shell -# Copy the consul binary to a shared volume for `consul-ecs-mesh-init` to use to generate Envoy configuration. -cp /bin/consul /bin/consul-inject/consul - -# At runtime, determine the IP address assigned to this ECS Task. -ECS_IPV4=$(curl -s $ECS_CONTAINER_METADATA_URI_V4 | jq -r '.Networks[0].IPv4Addresses[0]') - -# Write the Consul agent configuration file. -cat << EOF > /consul/agent-defaults.hcl -addresses = { - dns = "127.0.0.1" - grpc = "127.0.0.1" - http = "127.0.0.1" -} -advertise_addr = "$ECS_IPV4" -advertise_reconnect_timeout = "15m" -client_addr = "0.0.0.0" -datacenter = "dc1" -enable_central_service_config = true -leave_on_terminate = true -ports { - grpc = 8502 -} -retry_join = [""] -telemetry { - disable_compat_1.9 = true -} - -EOF - -# Start the consul agent. -exec consul agent \ - -data-dir /consul/data \ - -config-file /consul/agent-defaults.hcl -``` - -The following table describes the values that you should use to configure the `command` script: - -| Field name | Type | Description | -| -------------------- | ------- | ------------------------------------------------------------------------------------------------------------ | -| `addresses.*` | strings | Set the DNS, GRPC, and HTTP addresses to `127.0.0.1` to ensure these are not accessible outside of the task. | -| `advertise_addr` | string | Must be set to the task IP address so that other Consul agents know how to reach this agent. | -| `client_addr` | string | Must be set to an interface reachable by other Consul agents. | -| `datacenter` | string | Must be set to the Consul datacenter this task will join. | -| `leave_on_terminate` | boolean | Must be set to `true` so that the Consul agent leaves the cluster gracefully before exiting. | -| `retry_join` | string | Must be set to your Consul server location(s) so this agent can join the Consul cluster. | - --> **NOTE**: Use `exec` to start the Consul agent so that the Consul agent runs as PID 1. This ensures -the Consul agent directly receives signals from ECS, which is important for graceful shutdown of the Consul agent. - -Refer to the [Consul Agent documentation](/consul/docs/agent/config/config-files#configuration_files) for a complete reference of Consul agent -configuration options. - -## `consul-ecs-mesh-init` container - -The `consul-ecs-mesh-init` container runs at task startup to setup this instance for Consul service mesh. -It registers the service and proxy for this task with Consul and writes Envoy bootstrap -configuration to a shared volume. - - - -```json -{ - "containerDefinitions": [ - { - "name": "example-client-app", - "image": "docker.io/org/my_task:v0.0.1", - ... - }, - { - "name": "sidecar-proxy", - "image": "envoyproxy/envoy-alpine:", - ... - }, - { - "name": "consul-client" - "image": "public.ecr.aws/hashicorp/consul:", - ... - }, - { - "name": "consul-ecs-mesh-init", - "image": "public.ecr.aws/hashicorp/consul-ecs:", - "command": ["mesh-init"], - "essential": false, - "environment": [ - { - "name": "CONSUL_ECS_CONFIG_JSON", - "value": "{\"bootstrapDir\":\"/consul\",\"healthSyncContainers\":[],\"proxy\":{\"upstreams\":[{\"destinationName\":\"example-server-app\",\"localBindPort\":1234}]},\"service\":{\"checks\":[],\"meta\":{},\"name\":\"example-client-app\",\"port\":9090,\"tags\":[]}}" - } - ], - "mountPoints": [ - { - "readOnly": false, - "containerPath": "/consul", - "sourceVolume": "consul_data" - }, - { - "readOnly": true, - "containerPath": "/bin/consul-inject", - "sourceVolume": "consul_binary" - } - ] - } - ] -} -``` - - - -| Field name | Type | Description | -| ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| `name` | string | The container name should be `consul-ecs-mesh-init`. | -| `image` | string | The `consul-ecs` image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul-ecs`, to avoid rate limits. | -| `mountPoints` | list | Must be set as show above, so the `consul` and `consul-ecs` binaries can be shared among containers for task setup. | -| `command` | list | Set to `["mesh-init"]` so that the container runs the `consul-ecs mesh-init` command. | -| `environment` | list | This must include the [`CONSUL_ECS_CONFIG_JSON`](/consul/docs/ecs/manual/install#consul_ecs_config_json) variable. See below for details. | - -### `CONSUL_ECS_CONFIG_JSON` - -Consul uses the `CONSUL_ECS_CONFIG_JSON` environment variable to passed configurations to the `consul-ecs` binary in JSON format. - -The following example configures a service named `example-client-app` with one upstream -service name `example-server-app`. The `proxy` and `service` blocks include information used by `consul-ecs-mesh-init` to register the service with Consul during task start up. -The same configuration format is used for -the `consul-ecs-health-sync` container. - -```json -{ - "bootstrapDir": "/consul", - "healthSyncContainers": [], - "proxy": { - "upstreams": [ - { - "destinationName": "example-server-app", - "localBindPort": 1234 - } - ] - }, - "service": { - "checks": [], - "meta": {}, - "name": "example-client-app", - "port": 9090, - "tags": [] - } -} -``` - -| Field name | Type | Description | -| ---------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | -| `bootstrapDir` | string | This is the path of a shared volume that is mounted to other containers, where `consul-ecs-mesh-init` will write out Envoy configuration. | -| `healthSyncContainers` | list | Used for [health status syncing](/consul/docs/ecs/architecture#ecs-health-check-syncing) from ECS to Consul. See below for details. | -| `proxy.upstreams` | list | The upstream services that your application calls over the service mesh, if any. The `destinationName` and `localBindPort` fields are required. | -| `service.name` | string | The name used to register this service into the Consul service catalog. | -| `service.port` | integer | The port your application listens on. Set to `0` if your application does not listen on any port. | -| `service.checks` | list | Consul [checks](/consul/docs/services/usage/checks) to include so that Consul can run health checks against your application. | - -See the [Configuration Reference](/consul/docs/ecs/configuration-reference) for a complete reference of fields. - -## `consul-ecs-health-sync` container - -Optionally, Consul ECS can sync health checks for this task into Consul checks. -This allows you to configure a health check for your application in one place and -see a consistent health status in both ECS and Consul. - -For example, the following defines an ECS health check command that runs `curl localhost:9090/health`: - - - -```json -{ - "containerDefinitions": [ - { - "name": "example-client-app", - "image": "docker.io/org/my_task:v0.0.1", - "healthCheck": { - "retries": 3, - "command": ["CMD-SHELL", "curl localhost:9090/health"], - "timeout": 5, - "interval": 30 - }, - ... - }, - ... - ] -} -``` - - - -First, define which containers need their health status synced into Consul. To do this, -add the container name(s) to the `healthSyncContainers` list of the `CONSUL_ECS_CONFIG_JSON` variable, -as shown in the following example. This configuration must be passed to both the `consul-ecs-mesh-init` -and `consul-ecs-health-sync` containers. - - - -```json -{ - "bootstrapDir": "/consul", - "healthSyncContainers": ["example-client-app"], - ... -} -``` - - - -Next, set the `CONSUL_ECS_CONFIG_JSON` variable for the `consul-ecs-mesh-init` container. -The following example shows how the `CONSUL_ECS_CONFIG_JSON` variable should be formatted. -The JSON configuration is compacted down to a single line and escaped. - - - -```json -{ - "containerDefinitions": [ - { - "name": "consul-ecs-mesh-init", - "image": "public.ecr.aws/hashicorp/consul-ecs:", - "environment": [ - { - "name": "CONSUL_ECS_CONFIG_JSON", - "value": "{\"bootstrapDir\":\"/consul\",\"healthSyncContainers\":[\"example-client-app\"],\"proxy\":{\"upstreams\":[{\"destinationName\":\"example-server-app\",\"localBindPort\":1234}]},\"service\":{\"checks\":[],\"meta\":{},\"name\":\"example-client-app\",\"port\":9090,\"tags\":[]}}" - } - ], - ... - }, - ... - ] -} -``` - - - -Finally, include the `consul-ecs-health-sync` container in the `containerDefinitions` list. -Pass the same value for `CONSUL_ECS_CONFIG_JSON` for both the `consul-ecs-health-sync` -and `consul-ecs-mesh-init` containers. - - - -```json -{ - "containerDefinitions": [ - { - "name": "example-client-app", - "image": "docker.io/org/my_task:v0.0.1", - ... - }, - { - "name": "sidecar-proxy", - "image": "envoyproxy/envoy-alpine:", - ... - }, - { - "name": "consul-client" - "image": "public.ecr.aws/hashicorp/consul:", - ... - }, - { - "name": "consul-ecs-mesh-init", - "image": "public.ecr.aws/hashicorp/consul-ecs:", - ... - }, - { - "name": "consul-ecs-health-sync", - "image": "public.ecr.aws/hashicorp/consul-ecs:", - "command": ["health-sync"], - "essential": false, - "dependsOn": [ - { - "containerName": "consul-ecs-mesh-init", - "condition": "SUCCESS" - } - ], - "environment": [ - { - "name": "CONSUL_ECS_CONFIG_JSON", - "value": "{\"bootstrapDir\":\"/consul\",\"healthSyncContainers\":[\"example-client-app\"],\"proxy\":{\"upstreams\":[{\"destinationName\":\"example-server-app\",\"localBindPort\":1234}]},\"service\":{\"checks\":[],\"meta\":{},\"name\":\"example-client-app\",\"port\":9090,\"tags\":[]}}" - } - ] - } - ] -} -``` - - - -| Field name | Type | Description | -| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------- | -| `name` | string | The container name, which must be `consul-ecs-health-sync`. | -| `image` | string | The `consul-ecs` image. Use our public AWS registry, `public.ecr.aws/hashicorp/consul-ecs`, to avoid rate limits. | -| `command` | list | Must be set to `["health-sync"]` to run the `consul-ecs health-sync` command. | -| `dependsOn` | list | Must be set as shown above to ensure the `health-sync` container starts after service registration has completed. | -| `environment` | list | Must include the `CONSUL_ECS_CONFIG_JSON` variable to pass configuration to the `consul-ecs health-sync` command. | - -# Next Steps - -* Create the task definition using the [AWS Console](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create-task-definition-classic.html) or the [AWS CLI](https://docs.aws.amazon.com/cli/latest/reference/ecs/register-task-definition.html), or another method of your choice. -* Create an [ECS Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html) to start tasks using the task definition. -* Follow the [Secure Configuration](/consul/docs/ecs/manual/secure-configuration) to get production-ready. diff --git a/website/content/docs/ecs/manual/secure-configuration.mdx b/website/content/docs/ecs/manual/secure-configuration.mdx deleted file mode 100644 index ec374670bc..0000000000 --- a/website/content/docs/ecs/manual/secure-configuration.mdx +++ /dev/null @@ -1,545 +0,0 @@ ---- -layout: docs -page_title: Manual Secure Configuration - Consul on AWS Elastic Container Service (ECS) -description: >- - Securely configure Consul Service Mesh on Amazon Web Services ECS to protect the communication of sensitive data in production workloads. Learn how to manually configure auth methods to create tokens for clients and services and then apply tokens by role or policy. ---- - -# Manual Secure Configuration of Consul on AWS Elastic Container Service (ECS) - -This topic describes how to enable Consul security features for your production workloads. - -## Prerequisites - -The following features must be configured for your Consul server cluster: - -- [TLS encryption](/consul/docs/security/encryption#rpc-encryption-with-tls) for RPC communication between Consul clients and servers. -- [Gossip encryption](/consul/docs/security/encryption#gossip-encryption) for encrypting gossip traffic. -- [Access control lists (ACLs)](/consul/docs/security/acl) for authentication and authorization for Consul clients and services on the mesh. - -You should already have followed the [manual installation instructions](/consul/docs/ecs/manual/install) to define the necessary components of the task definition for Consul on ECS. - -You should be familiar with [specifying sensitive data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) on ECS. - -You should be familiar with configuring Consul's secure features, including how to create ACL tokens and policies. Refer to the [ACL system documentation](/consul/docs/security/acl) and [Day 1: Security tutorials](/consul/tutorials/security) for an introduction and additional information. - -## Auth Method - -Tokens are artifacts within the ACL system that authenticate users, services, and Consul agents. Tokens are linked to policies that specify the resources the token bearer has access to when making requests in the network. - -Auth Methods are a Consul server component that performs authentication against a trusted external party to authorize the creation of ACL tokens. The [AWS IAM auth method](/consul/docs/security/acl/auth-methods/aws-iam) is used to enable an ECS task to automatically obtain ACL tokens when the task starts up. - -There are two types of ACL tokens for Consul on ECS: - -* **Client tokens:** used by the `consul-client` containers to join the Consul cluster -* **Service tokens:** used by sidecar containers for service registration and health syncing - -This section describes how to manually configure the AWS IAM auth method for Consul on ECS. Alternatively, you can install the ACL controller to ease the burden of creating these resources. The ACL controller can automatically configure ACL resources for Consul on ECS. For additional details, refer to [ACL Controller](/consul/docs/ecs/manual/acl-controller) and [Architecture](/consul/docs/ecs/architecture). - -### ECS Task Role Configuration - -The ECS [task role](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) -is an IAM role associated with an ECS task. - -When an ECS task starts up, it runs a `consul login` command. The `consul login` command obtains -credentials for the task role role from AWS. It uses those credentials to sign the login request to -the AWS IAM auth method. This proves the ECS task's identity to the Consul servers. - -The task role must be configured with the following details to compatible with the AWS IAM auth -method. - -* An `iam:GetRole` permission to fetch itself. See - [IAM Policies](/consul/docs/security/acl/auth-methods/aws-iam#iam-policies) for details. -* A `consul.hashicorp.com.service-name` tag on the task role which contains the Consul service name - for the application in this task. -* A consul.hashicorp.com.namespace tag on the task role - indicating the Consul Enterprise namespace where this service is registering. - -The following sections describe how to configure the auth method to enable task roles to -successfully authenticate to the AWS IAM auth method and obtain tokens with the necessary -permissions. - -### Auth Method for Client Tokens - -The following steps configure an instance of the auth method that creates client tokens for tasks. - -1. Create the auth method instance -2. Create the client policy and role -3. Create the binding rule - -#### Create Auth Method for Client Tokens - -The following Consul CLI command creates an instance of the auth method for client tokens. - -```shell -consul acl auth-method create \ - -type aws-iam \ - -name iam-ecs-client-token \ - -description="AWS IAM auth method for ECS client tokens" \ - -config '{ - "BoundIAMPrincipalArns": ["arn:aws:iam:::role/consul-ecs/*"], - "EnableIAMEntityDetails": true -}' -``` - -The following flags are required: - -| Flag | Type | Description | -| --------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | -| `-type` | string | Must be `aws-iam`. | -| `-name` | string | A name of your choice. Must be unique among all auth methods. | -| `-description` | string | A description of your choice. | -| `-config` | string | A JSON string containing the [configuration](/consul/docs/security/acl/auth-methods/aws-iam#config-parameters) for the auth method. | - -In the `-config` option, the following fields are required: - -| Field | Type | Description | -| ------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | -| `BoundIAMPrincipalArns` | list | The list of trusted IAM roles. We recommend using a wildcard to trust IAM roles at a particular path. | -| `EnableIAMEntityDetails` | boolean | Must be true so that the auth method can retrieve IAM role details, such as the role path and role tags. | - -#### Create Client Policy and Role - -Configure the following ACL policy for Consul client tokens: - - - -```hcl -node_prefix "" { - policy = "write" -} -service_prefix "" { - policy = "read" -} -``` - - - -The policy allows `node:write` for any node name, which is necessary because the Consul node names on ECS are not known until runtime. - -You can add the policy in Consul using the [`consul acl policy create`](/consul/commands/acl/policy/create) command or the [`[PUT] /v1/acl/policy`](/consul/api-docs/acl/policies#create-a-policy) API endpoint. - -After the policy is created, create a Consul role associated with the policy by using the [`consul acl role create`](/consul/commands/acl/role/create) command or the [`[PUT] /v1/acl/role`](/consul/api-docs/acl/roles#create-a-role) API endpoint. - -The following example shows how to use the Consul CLI to create the client policy and role. - -```shell -consul acl policy create -name consul-ecs-client-policy -rules @client-token-policy.hcl - -consul acl role create -name consul-ecs-client-role -policy-name consul-ecs-client-policy -``` - -#### Create Binding Rule for Client Tokens - -The following creates a binding rule for the auth method for client tokens. The binding rule -associates the client role with each token created by a successful login to this auth -method instance. - -```shell -consul acl binding-rule create -method iam-ecs-client-token \ - -description 'Bind a role for Consul clients on ECS' \ - -bind-type role \ - -bind-name consul-ecs-client-role -``` - -### Auth Method for Service Tokens - - -The following steps configure an instance of the auth method that creates service tokens for tasks. - -* Create the auth method instance -* Create the binding rule - -#### Create Auth Method for Service Tokens - -The following uses the Consul CLI to create an instance of the auth method -for service tokens. This configures the auth method to associate a service identity -to each token created during login to this auth method instance. - -```shell -consul acl auth-method create \ - -type aws-iam \ - -name iam-ecs-service-token \ - -description="AWS IAM auth method for ECS service tokens" \ - -config '{ - "BoundIAMPrincipalArns": ["arn:aws:iam:::role/consul-ecs/*"], - "EnableIAMEntityDetails": true, - "IAMEntityTags": [ - "consul.hashicorp.com.service-name" - ] -}' -``` - -The following flags are required: - -| Flag | Type | Description | -| --------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- | -| `-type` | string | Must be `aws-iam`. | -| `-name` | string | A name of your choice. Must be unique among all auth methods. | -| `-description` | string | A description of your choice. | -| `-config` | string | A JSON string containing the [configuration](/consul/docs/security/acl/auth-methods/aws-iam#config-parameters) for the auth method. | - -In the `-config` option, the following fields are required: - -| Field | Type | Description | -| --- | --- | --- | -| `BoundIAMPrincipalArns` | list | The list of trusted IAM roles. We recommend using a wildcard to trust IAM roles at a particular path. | -| `EnableIAMEntityDetails` | boolean | Must be true so that the auth method can retrieve IAM role details, such as the role path and role tags. | -| `IAMEntityTags` | list | The list of IAM role tags to make available to binding rules. Must include the service name tag as shown. | - -The following binding rule is used to associate a service identity with each token created by -successful login to this instance of the auth method. The service identity name is taken from the -`consul.hashicorp.com.service-name` tag from the authenticating IAM role identity. - -#### Create Binding Rule - -```shell -consul acl binding-rule create \ - -method iam-ecs-service-token \ - -description 'Bind a service identity from IAM role tags for ECS service tokens' \ - -bind-type service \ - -bind-name '${entity_tags.consul.hashicorp.com.service-name}' -``` - -### Configuration for Consul Enterprise - -When using Consul Enterprise namespaces and admin partitions, pass the `-partition ` -option to the Consul CLI when creating Consul ACL roles, policies, auth methods, and binding rules, -in order to create these resources in a particular partition. - -The following shows how to create the ACL policy for client tokens. This ensures permissions for the -client token are scoped to a particular partition. - - - -```hcl -partition "" { - node_prefix "" { - policy = "write" - } - namespace_prefix "" { - service_prefix "" { - policy = "read" - } - } -} -``` - - - -The following commands show how to use the Consul CLI to create the policy and role. - -```shell -consul acl policy create -partition \ - -name consul-ecs-client-policy \ - -rules @client-token-policy-ent.hcl - -consul acl role create \ - -partition \ - -name consul-ecs-client-role \ - -policy-name consul-ecs-client-policy -``` - -The auth method for *service tokens* requires the following additional configuration to include a -namespace binding rule. This ensures the service tokens are created in the right namespace during -login. (The namespace binding rule must not be configured on the instance of the auth method -instance for *client tokens*.) - - - -```shell -consul acl auth-method create \ - -partition \ - -type aws-iam \ - -name iam-ecs-service-token \ - -description="AWS IAM auth method for ECS service tokens" \ - -namespace-rule-selector 'entity_tags["consul.hashicorp.com.namespace"] != ""' \ - -namespace-rule-bind-namespace '${entity_tags.consul.hashicorp.com.namespace}' \ - -config '{ - "BoundIAMPrincipalArns": ["arn:aws:iam:::role/consul-ecs/*"], - "EnableIAMEntityDetails": true, - "IAMEntityTags": [ - "consul.hashicorp.com.service-name", - "consul.hashicorp.com.namespace" - ] -}' -``` - - - -| Field | Type | Description | -| -------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `-partition` | string | The Consul Enterprise admin partition in which the auth method is created. | -| `-namespace-rule-selector` | string | When this expression evaluates to true during login, the `-namespace-rule-bind-namespace` is applied. As shown, it evaluates to true when the `consul.hashicorp.com.namespace` tag is non-empty on the task IAM role. | -| `-namespace-rule-bind-namespace` | string | This expression is evaluated to determine the namespace where the token is created during login. As shown, it uses the namespace from the `consul.hashicorp.com.namespace` tag on the task IAM role. | -| `IAMEntityTags` | list | Must include `consul.hashicorp.com.namespace` to enable use of this tag in binding rules. | - -## Secret storage - -You should securely store the following secrets in order to make them available to ECS tasks. - -1. Consul Server CA certificates. More than one may be required for different Consul protocols. -2. Consul gossip encryption key - -These secrets can be securely stored and passed to ECS tasks using either of the following AWS secret services: - -* [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-parameters.html) -* [AWS Secrets Manager](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data-secrets.html) - -Once the secrets are stored they can be referenced using their ARN. The following shows -example secret ARNs when using AWS Secrets Manager: - -| Secret | Sample Secret ARN | -| ---------------------- | ---------------------------------------------------------------------------------- | -| Consul Server CA Cert for RPC | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert` | -| Consul Server CA Cert for HTTPS | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-https-ca-cert` | -| Gossip encryption key | `arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-gossip-key` | - -## Configure `consul-client` - -The following secrets must be passed to the `consul-client` container: - -* Consul server CA certificates -* Gossip encryption key - -The following example shows how to include these secrets in the task definition. The `secrets` -list specifies environment variable `name`s that will be set to the secret values for this container. -ECS automatically fetches the secret values specified in the `valueFrom` fields during task provisioning. - -```json -{ - "containerDefinitions": [ - { - "name": "consul-client" - "image": "public.ecr.aws/hashicorp/consul:", - "secrets": [ - { - "name": "CONSUL_CACERT_PEM", - "valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-ca-cert" - }, - { - "name": "CONSUL_HTTPS_CACERT_PEM", - "valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-https-ca-cert" - }, - { - "name": "CONSUL_GOSSIP_ENCRYPTION_KEY", - "valueFrom": "arn:aws:secretsmanager:us-west-2:000000000000:secret:my-consul-gossip-key" - } - ] - }, - ... - ] -} -``` - -Next, update the Consul client startup script with the `consul login` command and additional Consul configuration -options for a secure configuration. - -The following is an example of the *additional* content to include in the `consul-client` startup script. Refer to the [install -page](/consul/docs/ecs/manual/install#consul-client-container) for the remainder of the startup script and how to pass this -script to the container. - - - -```shell -... - -# Obtain details from the task metadata -ECS_TASK_META=$(curl -s $ECS_CONTAINER_METADATA_URI_V4/task) -TASK_REGION=$(echo "$ECS_TASK_META" | jq --raw-output '.TaskARN / ":" | .[3]') -TASK_ID=$(echo "$ECS_TASK_META" | jq --raw-output '.TaskARN / "/" | .[2]') -CLUSTER_ARN=$(echo "$ECS_TASK_META" | jq --raw-output '.TaskARN | sub(":task/(?[^/]+).*"; ":cluster/\(.cluster)")') - -# Write the CA certs to a files in the shared volume -echo "$CONSUL_CACERT_PEM" > /consul/consul-ca-cert.pem -echo "$CONSUL_HTTPS_CACERT_PEM" > /consul/consul-https-ca-cert.pem - -consul_login() { - echo "Logging into auth method" - consul login \ - -http-addr "" \ - -ca-file /consul/consul-https-ca-cert.pem \ - -partition "" \ - -type aws-iam \ - -method iam-ecs-client-token \ - -meta "consul.hashicorp.com/task-id=$TASK_ID" \ - -meta "consul.hashicorp.com/cluster=$CLUSTER_ARN" \ - -aws-region "$TASK_REGION" \ - -aws-auto-bearer-token -aws-include-entity \ - -token-sink-file /consul/client-token -} - -read_token_stale() { - consul acl token read -http-addr ${ consul_http_addr } \ - -ca-file /consul/consul-https-ca-cert.pem \ - -stale -self -token-file /consul/client-token \ - > /dev/null -} - -# Retry in order to login successfully. -while ! consul_login; do - sleep 2 -done - -# Allow the health-sync container to read this token for consul logout. -chmod 0644 /consul/client-token - -# Wait for raft replication to hopefully occur. Without this, an "ACL not found" may be cached for a while. -COUNT=20 -while [ "$COUNT" -gt 0 ]; do - echo "Checking that the ACL token exists when reading it in the stale consistency mode ($COUNT attempts remaining)" - if read_token_stale; then - echo "Successfully read ACL token from the server" - break - fi - sleep 0.1 - COUNT=$((COUNT - 1)) -done -if [ "$COUNT" -eq 0 ]; then - echo "Unable to read ACL token from a Consul server; please check that your server cluster is healthy" - exit 1 -fi - -# This is interpolated into the agent-defaults.hcl -export AGENT_TOKEN=$(cat /consul/client-token) - -# Write the Consul agent configuration file. -cat << EOF > /consul/agent-defaults.hcl -... - -# Configure gossip encryption key -encrypt = "$CONSUL_GOSSIP_ENCRYPTION_KEY" - -# Configure TLS settings -auto_encrypt = { - tls = true - ip_san = ["$ECS_IPV4"] -} -tls { - defaults { - ca_file = "/consul/consul-ca-cert.pem" - verify_outgoing = true - } -} - -# Configure ACLs -acl { - enabled = true - default_policy = "deny" - down_policy = "async-cache" - tokens { - agent = "$AGENT_TOKEN" - } -} - -partition = "" - -EOF -``` - - - -The following describes the additional steps added to the startup script: - -* Fetch additional details from the task metadata: the AWS region, task id, and cluster arn. - These details are necessary for the `consul login` command used to obtain a Consul client token. -* Write CA certificates to files for Consul CLI and Consul client -* Run the `consul login` command in a retry loop. -* Wait for Raft replication to hopefully occur for this token. -* Configure the Consul client config file with additional fields necessary for secure configuration. - -The following flags are passed to the `consul login` command: - -| Field name | Type | Description | -| ------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | -| [`-http-addr`](/consul/commands/login#http-addr) | string | HTTP(S) address of the Consul server. | -| [`-ca-file`](/consul/commands/login#ca-file) | string | Path of the CA cert for Consul's HTTPS interface. Not required when using Consul servers on HCP. | -| `-partition` | string | The Consul Enterprise admin partition the auth method belongs to. | -| [`-type`](/consul/commands/login#type) | string | The auth method type. Must be `aws-iam`. | -| [`-method`](/consul/commands/login#type) | string | The auth method name. Must be the name of the auth method for ECS client tokens. | -| [`-meta`](/consul/commands/login#meta) | string | Metadata to set in description of the created token. Should include the task id and cluster as shown. | -| `-aws-region` | string | The AWS region where the task is running. | -| `-aws-auto-bearer-token` | | Must be set to login to the AWS IAM auth method. | -| `-aws-include-entity` | | Must be set to enable IAM role details. | - -The following table describes the additional fields that must be included in the Consul client configuration file. - -| Field name | Type | Description | -| ------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | -| [`encrypt`](/consul/docs/agent/config/cli-flags#_encrypt) | string | Gossip encryption key | -| [`tls.defaults.ca_file`](/consul/docs/agent/config/config-files#tls_defaults_ca_file) | string | Consul server CA cert for TLS verification. | -| [`acl.enabled`](/consul/docs/agent/config/config-files#acl_enabled) | boolean | Enable ACLs for this agent. | -| [`acl.tokens.agent`](/consul/docs/agent/config/config-files#acl_tokens_agent) | string | Consul client token which authorizes this agent with Consul servers. | -| [`partition`](/consul/docs/agent/config/config-files#partition-1) | string | The Consul Enterprise admin partition this agent belongs to. | - -### Configure Audit Logging -[Audit logging](/consul/docs/enterprise/audit-logging) is supported on clients running Consul Enterprise with ACLs enabled. -To enable audit logging, update the startup script to add an `audit` stanza to the Consul client configuration file. - -The following example modifies the `consul-client` startup script to configure audit logs to be written to the `stdout` of the `consul-client` container. - - - -```shell -... - -# Write the Consul agent configuration file. -cat << EOF > /consul/agent-defaults.hcl -... - -partition = "" - -audit { - enabled = true - sink "stdout" { - type = "file" - format = "json" - path = "/dev/stdout" - delivery_guarantee = "best-effort" - } -} - -EOF -``` - - - -The following table describes the fields that must be included to configure audit logging. - -| Field name | Type | Description | -| ------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | -| [`audit.enabled`](/consul/docs/agent/config/config-files#enabled) | boolean | Enable audit logging for this agent. | -| [`audit.sink`](/consul/docs/agent/config/config-files#sink) | object | The audit logging sink for this agent. | - -## Configure `consul-ecs-mesh-init` and `consul-ecs-health-sync` - -The following *additional* options should be set in the [`CONSUL_ECS_CONFIG_JSON`](/consul/docs/ecs/manual/install#consul_ecs_config_json) environment variable. When these options are specified, the `consul-ecs mesh-init` command will run the `consul login` command to obtain a service token from the Consul AWS IAM Auth method. The `consul-ecs health-sync` command is responsible for running a `consul logout` command for both the service and client tokens when the task shuts down. - - - -```json -{ - "consulHTTPAddr": "", - "consulCACertFile": "/consul/consul-https-ca-cert.pem", - "consulLogin": { - "enabled": true, - "method": "iam-ecs-service-token", - "extraLoginFlags": ["-partition", ""] - }, - "bootstrapDir": "/consul", - "healthSyncContainers": [], - ... -} -``` - - - -The following table explains the additional fields for the `CONSUL_ECS_CONFIG_JSON`: - -| Field name | Type | Description | -| ----------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `consulHTTPAddr` | string | HTTP(S) address for the Consul server. | -| `consulCACertFile` | string | Path of the CA cert file for Consul's HTTPS interface. Not required for Consul servers in HCP. | -| `consulLogin.enabled` | boolean | Must be set to true to log in to the auth method. | -| `consulLogin.method` | string | Must be set to the name of the auth method instance for service tokens. | -| `consulLogin.extraLoginFlags` | list | Additional flags passed to the `consul login` command. This shows how to pass the Consul Enterprise admin partition to the `consul login` command. | diff --git a/website/content/docs/ecs/compatibility.mdx b/website/content/docs/ecs/reference/compatibility.mdx similarity index 100% rename from website/content/docs/ecs/compatibility.mdx rename to website/content/docs/ecs/reference/compatibility.mdx diff --git a/website/content/docs/ecs/configuration-reference.mdx b/website/content/docs/ecs/reference/configuration-reference.mdx similarity index 99% rename from website/content/docs/ecs/configuration-reference.mdx rename to website/content/docs/ecs/reference/configuration-reference.mdx index 8d2aedd735..4c366d7795 100644 --- a/website/content/docs/ecs/configuration-reference.mdx +++ b/website/content/docs/ecs/reference/configuration-reference.mdx @@ -7,7 +7,7 @@ description: >- # Consul on AWS Elastic Container Service (ECS) Configuration Reference -This pages details the configuration options for the JSON config format used +This topic describes configuration options for the JSON configuration format used by the `consul-ecs` binary. This configuration is passed to the `consul-ecs` binary as a string using the `CONSUL_ECS_CONFIG_JSON` environment variable. diff --git a/website/content/docs/ecs/reference/consul-server-json.mdx b/website/content/docs/ecs/reference/consul-server-json.mdx new file mode 100644 index 0000000000..c884506469 --- /dev/null +++ b/website/content/docs/ecs/reference/consul-server-json.mdx @@ -0,0 +1,31 @@ +--- +layout: docs +page_title: Consul server configuration JSON schema reference +description: Learn about the fields available in the JSON scheme for configuring ECS task connections to Consul servers. +--- + +# Consul server configuration JSON schema reference + +This topic provides reference information about the JSON schema used to build the `config.tf` file. Refer to [Configure Consul server settings](/consul/docs/ecs/deploy/terraform#configure-consul-server-settings) for information about how Consul on ECS uses the JSON schema. + +```json +`consulServers` + `hosts`: string + `skipServerWatch`: boolean; set to true if the Consul server is already behind a load balancer + `defaults`: map - applies to both gRPC and HTTP + `caCertFile`: string path to the certificate .pem file + `tlsServerName`: string name of the TLS server + `tls`: boolean that enables TLS + `grpc`: map - overrides for gRPC traffic + `port`: number specifying the port for gRPC communication + `caCertFile`: string path to the certificate .pem file for authorizing gRPC + `tlsServerName`: string name of the TLS server + `tls`: boolean that enables TLS for gRPC + `http`: map - overrides for HTTP traffic + `https`: boolean that enables HTTPS + `port`: number specifying the port for HTTPS communication + `caCertFile`: string path to the certificate .pem file for authorizing HTTPS + `tlsServerName`: string name of the TLS server + `tls`: boolean that enables TLS for HTTPS + +``` diff --git a/website/content/docs/ecs/requirements.mdx b/website/content/docs/ecs/requirements.mdx deleted file mode 100644 index 4e4f997eac..0000000000 --- a/website/content/docs/ecs/requirements.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -layout: docs -page_title: Requirements - Consul on AWS Elastic Container Service (ECS) -description: >- - Consul has requirements to install and run on Amazon Web Services ECS. Learn about Consul's requirements for Fargate and EC2, including network mode and subnet information, as well as server, routing, and ACL controller considerations to keep in mind. ---- - -# Requirements for Consul on AWS Elastic Container Service (ECS) - -The following requirements must be met in order to install Consul on ECS: - -* **Launch Type:** Fargate and EC2 launch types are supported. -* **Network Mode:** Only `awsvpc` mode is supported. -* **Subnets:** ECS Tasks can run in private or public subnets. Tasks must have [network access](https://aws.amazon.com/premiumsupport/knowledge-center/ecs-pull-container-api-error-ecr/) to Amazon ECR or other public container registries to pull images. -* **Consul Servers:** You can use your own Consul servers running on virtual machines or use [HashiCorp Cloud Platform Consul](https://www.hashicorp.com/cloud-platform) to host the servers for you. For development purposes or testing, you may use the `dev-server` [Terraform module](https://github.com/hashicorp/terraform-aws-consul-ecs/tree/main) that runs the Consul server as an ECS task. The `dev-server` does not support persistent storage. -* **ACL Controller:** If you are running a secure Consul installation with ACLs enabled, configure the ACL controller. - * **Admin Partitions:** Consul on ECS supports [admin partitions](/consul/docs/enterprise/admin-partitions) when ACLs are enabled and the ACL controller is configured. - The ACL controller manages one admin partition and each ECS cluster requires an ACL controller. - Each `mesh-task` must also be configured to use a Consul Enterprise client. - * **Namespaces:** [Namespaces](/consul/docs/enterprise/namespaces) are supported when ACLs are enabled and the ACL controller is configured. - Each `mesh-task` must also be configured to use a Consul Enterprise client. -* **Sidecar containers:** Consul on ECS requires two sidecar containers to run in each ECS task: a - Consul agent container and a sidecar proxy container. These additional sidecar containers must - be included in the ECS task definition. The [Consul ECS Terraform module](/consul/docs/ecs/terraform/install) - will include these sidecar containers for you. If you do not use Terraform, you can construct - the task definition yourself by following [our documentation](/consul/docs/ecs/manual/install). -* **Routing:** With your application running in tasks as part of the mesh, you must specify the - upstream services that your application calls. You will also need to change the URLs your - application uses to ensure the application is making requests through the service mesh. -* **Bind Address:** Once all communication is flowing through the service mesh, you should change - the address your application is listening on to `127.0.0.1` so that it only receives requests - through the sidecar proxy. diff --git a/website/content/docs/ecs/task-resource-usage.mdx b/website/content/docs/ecs/task-resource-usage.mdx deleted file mode 100644 index 8a566ad016..0000000000 --- a/website/content/docs/ecs/task-resource-usage.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -layout: docs -page_title: Resource Usage - Consul on AWS Elastic Container Service (ECS) -description: >- - Learn about the CPU and memory resources Consul uses while running on Amazon Web Services ECS, including performance test procedures and results. ---- - -# Resource Usage for Consul on AWS Elastic Container Service (ECS) - -We ran performance tests of Consul on ECS to determine the resource usage of -the sidecar containers Consul on ECS injects along with the ACL controller. -[The architecture page](/consul/docs/ecs/architecture) describes each Consul on ECS -component in depth. - -We used the following procedure to measure resource usage: - -- Executed performance tests while deploying clusters of various sizes. We - ensured that deployment conditions stressed Consul on ESC components. -- After each performance test session, we recorded resource usage for each - component to determine worst-case scenario resource usage in a production - environment. -- We used Fargate's minimum allowed CPU (256 shares) and memory settings (512 - MB) on ECS during performance testing to demonstrate that Consul on ECS along - with application containers can run on the smallest ECS tasks. - -Here is the maximum resource usage we observed for each container: - -| Container | CPU | Memory | -| -------------- | --- | ------ | -| ACL Controller | 5% | 43 MB | -| Health Sync | 6% | 35 MB | -| Consul Client | 10% | 87 MB | -| Envoy | 18% | 55 MB | - -The containers added by Consul on ECS consume resources well below the minimum CPU and -memory limits for an ECS Task. Use the `memory` and `cpu` settings for the task definition -if additional resources are necessary for your application task. diff --git a/website/content/docs/ecs/tech-specs.mdx b/website/content/docs/ecs/tech-specs.mdx new file mode 100644 index 0000000000..2f70e5ed0d --- /dev/null +++ b/website/content/docs/ecs/tech-specs.mdx @@ -0,0 +1,52 @@ +--- +layout: docs +page_title: Technical specifications for Consul on AWS Elastic Container Service (ECS) +description: >- + Consul has requirements to install and run on Amazon Web Services ECS. Learn about Consul's requirements for Fargate and EC2, including network mode and subnet information, as well as server, routing, and ACL controller considerations. +--- + +# Technical specifications for Consul on ECS + +This topic describes the supported runtimes and environments for using Consul service mesh for your ECS workloads. + +For requirements associated with using the Terraform `mesh-task` module to deploy Consul service mesh, refer [Deploy Consul with the Terraform module](/consul/docs/ecs/deploy/terraform). For requirements associated with manually deploying Consul service mesh to your ECS cluster, refer [Deploy Consul manually](/consul/docs/ecs/deploy/manual). + +## Supported environments and runtimes + +Consul on ECS supports the following environments and runtimes: + +- **Launch Types**: Fargate and EC2 +- **Network Modes**: `awsvpc` +- **Subnets**: Private and public subnets. Tasks must have network access to Amazon ECR or other public container registries to pull images. +- **Consul servers**: You can use your own Consul servers running on virtual machines or [use HCP Consul to host the servers for you](/hcp/docs/consul/hcp-managed). +- **ECS controller**: The ECS controller assists with reconciling state back to Consul and facilitates Consul security features. +- **Admin partitions**: Enable ACLs and configure the ECS controller to use admin partitions. You must deploy one controller for each admin partition. +- **Namespaces**: Enable ACLs and configure the ECS controller to use namespaces. +- **Dataplane containers**: To manage proxies using Consul dataplane, you must use the Terraform `mesh-task` module to install Consul service mesh. + +## Resource usage + +We used the following procedure to measure resource usage: + +- Executed performance tests while deploying clusters of various sizes. We + ensured that deployment conditions stressed Consul on ESC components. +- After each performance test session, we recorded resource usage for each + component to determine worst-case scenario resource usage in a production + environment. +- We used Fargate's minimum allowed CPU (256 shares) and memory settings (512 + MB) on ECS during performance testing to demonstrate that Consul on ECS along + with application containers can run on the smallest ECS tasks. + +The following table describes the maximum resource usage we observed for each container under these testing conditions: + +| Container | CPU | Memory | +| -------------- | --- | ------ | +| ECS controller | 5% | 43 MB | +| Control plane | 6% | 35 MB | +| Dataplane | 10% | 87 MB | + +The containers added by Consul on ECS consume resources well below the minimum CPU and +memory limits for an ECS task. Use the `memory` and `cpu` settings for the task definition +if additional resources are necessary for your application task. + +Refer to [Architecture](/consul/docs/ecs/architecture) for details about each component. \ No newline at end of file diff --git a/website/content/docs/ecs/terraform/install.mdx b/website/content/docs/ecs/terraform/install.mdx deleted file mode 100644 index 3aa2dff4df..0000000000 --- a/website/content/docs/ecs/terraform/install.mdx +++ /dev/null @@ -1,452 +0,0 @@ ---- -layout: docs -page_title: Install with Terraform - Consul on AWS Elastic Container Service (ECS) -description: >- - When you install Consul on Amazon Web Services ECS, you can use Terraform modules to simplify the process. Learn how to create task definitions, schedule tasks for your service mesh, and configure routes with example configurations. ---- - -# Install Consul on AWS Elastic Container Service (ECS) with Terraform - -This topic describes how to use HashiCorp's Terraform modules to launch your application in AWS ECS as part of Consul service mesh. If you do not use Terraform, refer to the [Manual Installation](/consul/docs/ecs/manual/install) page to install Consul on ECS without Terraform. - -This topic does not include instructions for creating all AWS resources necessary to install Consul, such as a VPC or the ECS cluster. Refer to the guides in the [Getting Started](/consul/docs/ecs#getting-started) section for complete and runnable examples. - -## Overview - -The following procedure describes the general workflow: - -1. Create Terraform configuration files for the necessary components: - - - [ECS task definition](#create-the-task-definition): Use the HashiCorp Terraform modules to create the ECS task definition. - - [ECS service](#ecs-service): Use the `aws_ecs_service` resource to create an ECS service that schedules service mesh tasks to run on ECS. - -2. [Run Terraform](#running-terraform) to deploy the resources in AWS - -If you want to operate Consul in production environments, follow the instructions in the [Secure Configuration](/consul/docs/ecs/terraform/secure-configuration) documentation. The instructions describe how to enable ACLs and TLS and gossip encryption, which provide network security for production-grade deployments. - -## Requirements - -* You should have some familiarity with using Terraform. Refer to the [Terraform documentation](/terraform/docs) to learn about infrastructure as code and how to get started with Terraform. -* You should also be familiar with AWS ECS before following these instructions. See [What is Amazon Elastic Container Service](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html) for details. -* If you intend to [use the `gateway-task` module to deploy mesh gateways](#configure-the-gateway-task-module), all Consul server and client agents in all datacenters must have TLS and gossip encryption enabled. Refer to the [Secure Configuration](/consul/docs/ecs/terraform/secure-configuration) documentation for instructions. - -## Create the task definition - -To run an application in ECS with Consul service mesh, you must create an ECS task definition. The task definition includes your application containers and additional sidecar containers, such as the Consul agent container and the Envoy sidecar proxy container. - -Create a Terraform configuration file and include the `mesh-task` module. The module automatically adds the necessary sidecar containers. - -If you intend to peer the service mesh to multiple Consul datacenters or partitions, you can also include the `gateway-task` module. The module enables connectivity between datacenters across service meshes. - -### Configure the mesh task module - -Create a Terraform configuration file and specify the `mesh-task` module in the `source` field. The [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task) automatically includes the necessary sidecar containers. - -In the following example, the Terraform configuration file called `mesh-task.tf` creates a task definition with an application container called `example-client-app`: - - - -```hcl -module "my_task" { - source = "hashicorp/consul-ecs/aws//modules/mesh-task" - version = "" - - family = "my_task" - container_definitions = [ - { - name = "example-client-app" - image = "docker.io/org/my_task:v0.0.1" - essential = true - portMappings = [ - { - containerPort = 9090 - hostPort = 9090 - protocol = "tcp" - } - ] - cpu = 0 - mountPoints = [] - volumesFrom = [] - } - ] - - port = 9090 - retry_join = ["
"] - consul_datacenter = "" -} -``` - - - -The following fields are required. Refer to the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for a complete reference. - -| Input Variable | Type | Description | -| ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `source` | string | Must be set to the source location of the `mesh-task` module, `hashicorp/consul-ecs/aws//modules/mesh-task`. | -| `version` | string | Must be set to the version of the `mesh-task` module. | -| `family` | string | The [ECS task definition family](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family). The family is also used as the Consul service name by default. | -| `container_definitions` | list | This is the list of [container definitions](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definitions) for the task definition. This is where you include your application containers. | -| `essential` | boolean | Must be `true` to ensure the health of your application container affects the health status of the task. | -| `port` | integer | The port that your application listens on, if any. If your application does not listen on a port, set `outbound_only = true`. | -| `retry_join` | list | This is the [`retry_join`](/consul/docs/agent/config/config-files#retry_join) option for the Consul agent, which specifies the locations of your Consul servers. | -### Configure an ECS service for the mesh task module - -[ECS services](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html) are one of the most common -ways to start tasks using a task definition. - -To define an ECS service, reference the `mesh-task` module's `task_definition_arn` output value -in your `aws_ecs_service` resource. The following example shows how to include the service in the `mesh-task.tf` file. - - - -```hcl -module "my_task" { - source = "hashicorp/consul-ecs/aws//modules/mesh-task" - ... -} - -resource "aws_ecs_service" "my_task" { - name = "my_task_service" - task_definition = module.my_task.task_definition_arn - launch_type = "FARGATE" - propagate_tags = "TASK_DEFINITION" - ... -} -``` - - - -The example shows a partially configured ECS service to highlight significant fields. Refer to [`aws_ecs_service`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) for a complete configuration reference. - -| Input Variable | Type | Description | -| ----------------- | ------- | ------------------------------------------------------------------------------------------------------------------- | -| `name` | string | The name of the ECS service. This name is required by AWS but is not used by Consul service mesh. | -| `task_definition` | string | The task definition used to start tasks. Set this option to the task definition ARN returned by the `mesh-task` module. | -| `launch_type` | string | The launch type. Consul on ECS supports the `FARGATE` and `EC2` launch types. | -| `propagate_tags` | string | This option must be set to `TASK_DEFINITION` so that tags added by `mesh-task` to the task definition are copied to tasks. | - -After including the ECS service in your Terraform configuration, run `terraform apply` -from your project directory to create the ECS service resource. The ECS service -then starts your application in a task. The task automatically registers itself -into the Consul service catalog during startup. - --> **NOTE:** If your tasks run in a public subnet, they must have `assign_public_ip = true` -in their [`network_configuration`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service#network_configuration) block so that ECS can pull the Docker images. - -### Configure the gateway task module - -Add the `gateway-task` to your Terraform configuration if you want to deploy a mesh gateway. Mesh gateways enable service to service communication across the WAN, as well as federate service mesh traffic across Consul admin partitions and Consul datacenters over the WAN. Refer to the following documentation to learn more about mesh gateways: - -* [WAN Federation via Mesh Gateways](/consul/docs/connect/gateways/mesh-gateway/wan-federation-via-mesh-gateways) -* [Service-to-service Traffic Across Datacenters](/consul/docs/connect/gateways/mesh-gateway/service-to-service-traffic-wan-datacenters). - -You must add and configure a `gateway-task` for each Consul datacenter in your network. You must also enable TLS and gossip encryption on all server and client agents in all data centers per the [Requirements](#requirements). Mesh gateways operate by sniffing and extracting the server name indication (SNI) header from the service mesh session and routing the connection to the appropriate destination based on the server name requested. - -The module creates an ECS service and a task definition that includes the following containers: - -- Consul client -- Envoy gateway proxy -- Mesh init - -You will need to provide inputs for the artifacts created by the `gateway-task` module. The following example defines a mesh gateway task called `my-gateway` in a file called `mesh-gateway.tf`: - - - -```hcl -module "my_mesh_gateway" { - source = "hashicorp/consul-ecs/aws//modules/gateway-task" - version = "" - kind = "mesh-gateway" - - family = "my-gateway" - ecs_cluster_arn = "" - subnets = [""] - retry_join = ["
"] - tls = true - consul_server_ca_cert_arn = "" - gossip_key_secret_arn = "" -} -``` - - - -The following fields are required. Refer to the [module reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/gateway-task?tab=inputs) for a complete reference. - -| Input variable | Type | Description | -| --- | --- | --- | -| `source` | string | Specifies the source location of the `gateway-task` module. Must be set to `hashicorp/consul-ecs/aws//modules/gateway-task`. | -| `version` | string | Specifies the version of the `gateway-task` module. | -| `kind` | string | Declares the kind of gateway to create. Must be set to `mesh-gateway` to create a mesh-gateway. | -| `family` | string | Specifies the [ECS task definition family](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#family). The family is also used as the Consul service name by default. | -| `ecs_cluster_arn` | string | Specifies the ARN of the ECS cluster where the mesh gateway task should be launched. | -| `subnets` | list of strings | Specifies the subnet IDs where the task will be launched. | -| `retry_join` | list of strings | Defines a set of arguments to pass to the Consul agent [`-retry-join`](/consul/docs/agent/config/cli-flags#_retry_join) flag. The arguments specify locations of the Consul servers in the local datacenter that Consul client agents can connect to. | -| `tls` | boolean | Set to `true` to enable TLS. | -| `consul_server_ca_cert_arn` | string | Specifies the ARN of the Secrets Manager containing the Consul server CA certificate | -| `gossip_key_secret_arn` | string | Specifies the ARN of the Secrets Manager containing the Consul's gossip encryption key. | - -Refer to the [gateway task configuration examples](#gateway-task-configuration-examples) for additional example configurations. - -#### ECS service - -The ECS service is automatically created by the `gateway-task` module. The service can run one or more instances of the gateway. - -#### Mesh init - -The `mesh-init` container is a short-lived container that sets up the initial configurations for Consul and Envoy. The `gateway-task` module automatically configures the `mesh-init` container based on the inputs specified in the [task definition](#task-definition) and [ECS service](#ecs-service) configuration. - -For additional information, refer to [Task Startup](/consul/docs/ecs/architecture#task-startup) for additional information. - -#### Gateway task configuration examples - -The following examples illustrate how to configure the `gateway-task` for different use cases. - -##### Ingress - -Mesh gateways need to be reachable over the WAN to route traffic between datacenters. Configure the following options in the `gateway-task` to enable ingress through the mesh gateway. - -| Input variable | Type | Description | -| --- | --- | --- | -| `lb_enabled` | Boolean | Set to `true` to automatically deploy and configure a network load balancer for ingress to the mesh gateway. | -| `lb_vpc_id` | string | Specifies the VPC to launch the load balancer in. | -| `lb_subnets` | list of strings | Specifies one or more public subnets to associate with the load balancer. | - - - -```hcl -module "my_mesh_gateway" { - ... - - lb_enabled = true - lb_vpc_id = "" - lb_subnets = [""] -} -``` - - - -Alternatively, you can manually configure ingress to the mesh gateway and provide the `wan_address` and `wan_port` inputs to the `gateway-task` module. The `wan_port` field is optional. Port `8443` is used by default. - - - -```hcl -module "my_mesh_gateway" { - ... - - wan_address = "" - wan_port = -} -``` - - - -Mesh gateways route L4 TCP connections and do not terminate mTLS sessions. If you manually configure [AWS Elastic Load Balancing](https://aws.amazon.com/elasticloadbalancing/) for ingress to a mesh gateway, you must use an AWS [Network Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/introduction.html) or a [Classic Load Balancer](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/introduction.html). - - -##### ACLs - -Configure the following options in the `gateway-task` when ACLs are enabled. - -| Option | Type | Description | -| --- | --- | --- | -| `acl` | Boolean | Set to `true` if ACLs are enabled. | -| `consul_http_addr` | string | Specifies the HTTP `address:port` of the Consul server. Required for the mesh gateway task to log into Consul via the IAM Auth Method to obtain its client and service tokens. | -| `consul_https_ca_cert_arn` | string | Specifies ARN of the Secrets Manager secret that contains the certificate for the Consul HTTPS API. | - - - -```hcl -module "my_mesh_gateway" { - ... - - acls = true - consul_http_addr = "" - consul_https_ca_cert_arn = "" -} -``` - - - -##### WAN federation - -Configure the following options in the `gateway-task` to enable [WAN federation via mesh gateways](/consul/docs/connect/gateways/mesh-gateway/wan-federation-via-mesh-gateways). - -| Option | Type | Description | -| --- | --- | --- | -| `consul_datacenter` | string | Specifies the name of the local Consul datacenter. | -| `consul_primary_datacenter` | string | Specifies the name of the primary Consul datacenter. | -| `enable_mesh_gateway_wan_federation` | Boolean | Set to `true` to enable WAN federation. | -| `enable_acl_token_replication` | Boolean | Set to `true` to enable ACL token replication and allow the creation of local tokens secondary datacenters. | - -The following example shows how to configure the `gateway-task` module. - - - -```hcl -module "my_mesh_gateway" { - ... - - consul_datacenter = "" - consul_primary_datacenter = "" - enable_mesh_gateway_wan_federation = true - enable_acl_token_replication = true -} -``` - - - -When federating Consul datacenters over the WAN with ACLs enabled, [ACL Token replication](/consul/docs/security/acl/acl-federated-datacenters) must be enabled on all server and client agents in all datacenters. - -## Run Terraform - -You will need to run Terraform to create the task definition. - -Save the Terraform configuration for the task definition to a file, such as `mesh-task.tf`. -You should place this file in a directory alongside other Terraform configuration files for your project. - -The `mesh-task` module requires the AWS Terraform provider. The following example shows how to include -and configure the AWS provider in a file called `provider.tf`. Refer to the [AWS Terraform provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) -documentation for complete configuration details. - - - -```hcl -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "" - } - } -} - -provider "aws" { - region = "" - ... -} -``` - - - -Additional AWS resources for your project can be included in additional Terraform configuration files -in the same directory. The following example shows a basic project directory: - -```shell-session -$ ls -mesh-task.tf -provider.tf -... -``` - -Terraform should be run in your project directory as follows. - -* Run `terraform init` first to download dependencies, such as Terraform providers -* Run `terraform apply` to have Terraform create AWS resources, such as the task definition from the `mesh-task` module. - -Terraform automatically reads all files in the current directory that have a `.tf` file extension. -Refer to the [Terraform documentation](/terraform/docs) for more information and Terraform best practices. - -## Configure routes - -Now that your tasks are registered in the mesh, you're able to use the service -mesh to route between them. - -In order to make calls through the service mesh, you must configure the sidecar -proxy to listen on a different port for each upstream service your application -needs to call. You then must modify your application to make requests to the sidecar -proxy on that port. - -For example, if your application `web` makes calls to another application called `backend`, then you would first configure the `mesh-task` module's upstream(s): - -```hcl -module "web" { - family = "web" - upstreams = [ - { - destinationName = "backend" - localBindPort = 8080 - } - ] -} -``` - -| Input Variable | Type | Description | -| ----------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `destinationName` | string | The name of the upstream service, as it is registered in the Consul service catalog. | -| `localBindPort` | integer | Requests to this port will be forwarded by the proxy to the upstream service. This must be an unused port, but does not need to match the upstream service port. | - -If you have multiple upstream services they each need to be listed here. - -Next, configure your application to make requests to `localhost:8080` when -it wants to call the `backend` service. - -For example, if your service allows configuring the URL for `backend` via the -`BACKEND_URL` environment variable, you would set: - -```hcl -module "web" { - family = "web" - upstreams = [ - { - destinationName = "backend" - localBindPort = 8080 - } - ] - container_definitions = [ - { - name = "web" - environment = [ - { - name = "BACKEND_URL" - value = "http://localhost:8080" - } - ] - ... - } - ] - ... -} -``` - -## Configure the bind address - -To ensure that your application only receives traffic through the service mesh, -you must change the address that your application listens on to the loopback address. The loopback address is also called `localhost`, `lo`, and `127.0.0.1`. -Binding to the loopback address allows the sidecar proxy running in the same task to only make requests within the service mesh. - -If your application is listening on all interfaces, such as `0.0.0.0`, then other -applications can call it directly, bypassing its sidecar proxy. - -Changing the listening address is specific to the language and framework you're -using in your application. Regardless of which language or framework you're using, -binding the loopback address to a dynamic value, such as an environment variable, is a best practice: - -```bash -export BIND_ADDRESS="127.0.0.1:8080" -``` - -The following examples demonstrate how to bind the loopback address to an environment variable in golang and Django (Python): - - - -```go -s := &http.Server{ - Addr: os.Getenv("BIND_ADDRESS"), - ... -} -log.Fatal(s.ListenAndServe()) -``` - -```bash -python manage.py runserver "$BIND_ADDRESS" -``` - - - -## Next steps - -- Follow the [Secure Configuration](/consul/docs/ecs/terraform/secure-configuration) to get production-ready. -- Now that your applications are running in the service mesh, read about - other [Service Mesh features](/consul/docs/connect). -- View the [Architecture](/consul/docs/ecs/architecture) documentation to understand - what's going on under the hood. diff --git a/website/content/docs/ecs/terraform/migrate-existing-tasks.mdx b/website/content/docs/ecs/terraform/migrate-existing-tasks.mdx deleted file mode 100644 index 5dcfb31e92..0000000000 --- a/website/content/docs/ecs/terraform/migrate-existing-tasks.mdx +++ /dev/null @@ -1,115 +0,0 @@ ---- -layout: docs -page_title: Migrate Tasks with Terraform - Consul on AWS Elastic Container Service (ECS) -description: >- - You can migrate tasks in existing Amazon Web Services ECS deployments to a service mesh deployed with Terraform. Learn how to convert a task specified as an ECS task definition into a `mesh-task` Terraform module. ---- - -# Migrate Tasks to Consul on AWS Elastic Container Service (ECS) with Terraform - -This topic describes how to migrate your existing ECS Tasks to use our [`mesh-task` Terraform module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task). - -## Define Tasks in Terraform - -Your tasks must first be specified in Terraform using the [`ecs_task_definition`](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) -resource so that they can then be converted to use the [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task). - -For example, your tasks should be defined with Terraform similar to the following: - -```hcl -resource "aws_ecs_task_definition" "my_task" { - family = "my_task" - requires_compatibilities = ["FARGATE"] - network_mode = "awsvpc" - cpu = 256 - memory = 512 - execution_role_arn = "arn:aws:iam::111111111111:role/execution-role" - task_role_arn = "arn:aws:iam::111111111111:role/task-role" - container_definitions = jsonencode( - [{ - name = "example-client-app" - image = "docker.io/org/my_task:v0.0.1" - essential = true - portMappings = [ - { - containerPort = 9090 - hostPort = 9090 - protocol = "tcp" - } - ] - cpu = 0 - mountPoints = [] - volumesFrom = [] - }] - ) -} - -resource "aws_ecs_service" "my_task" { - name = "my_task" - cluster = "arn:aws:ecs:us-east-1:111111111111:cluster/my-cluster" - task_definition = aws_ecs_task_definition.my_task.arn - desired_count = 1 - network_configuration { - subnets = ["subnet-abc123"] - } - launch_type = "FARGATE" -} -``` - -## Convert to the `mesh-task` Module - -In order to add the necessary sidecar containers for your task to join the mesh, -you must use the [`mesh-task` module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task). - -The `mesh-task` module uses inputs similar to your old ECS task definition but -creates a new version of the task definition with additional containers. - -The `mesh-task` module is used as follows: - -```hcl -module "my_task" { - source = "hashicorp/consul-ecs/aws//modules/mesh-task" - version = "" - - family = "my_task" - container_definitions = [ - { - name = "example-client-app" - image = "docker.io/org/my_task:v0.0.1" - essential = true - portMappings = [ - { - containerPort = 9090 - hostPort = 9090 - protocol = "tcp" - } - ] - cpu = 0 - mountPoints = [] - volumesFrom = [] - } - ] - - port = 9090 - retry_join = ["
"] -} -``` - -The main differences are: - -- You should remove the `execution_role_arn` and `task_role_arn` fields. The `mesh-task` module creates the task and execution roles by default. If you need to use existing IAM role(s), set the `task_role` and `execution_role` fields to pass in existing roles. -- You must set the `port` field to the port that your application listens on. - If your application has no listening port, set `outbound_only = true` and remove the `port` field. -- You must add the `retry_join` field. This specifies the location of your Consul servers so that your task can join the mesh. -- You must remove the `jsonencode()` function from the `container_definitions` field. - -The `mesh-task` module will create a new version of your task definition with the -necessary sidecar containers added so you can delete your existing `aws_ecs_task_definition` -resource. - -## Next Steps - -Now that your task(s) are migrated to the `mesh-task` module, - -- Start at the [ECS Service section](/consul/docs/ecs/terraform/install#configure-an-ecs-service-for-the-mesh-task-module) of the Installation Guide to continue installing Consul on ECS. -- Refer to the [`mesh-task` reference documentation](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/mesh-task?tab=inputs) for all available inputs to your mesh tasks. diff --git a/website/content/docs/ecs/terraform/secure-configuration.mdx b/website/content/docs/ecs/terraform/secure-configuration.mdx deleted file mode 100644 index 93932b6fbf..0000000000 --- a/website/content/docs/ecs/terraform/secure-configuration.mdx +++ /dev/null @@ -1,177 +0,0 @@ ---- -layout: docs -page_title: Secure Configuration with Terraform - Consul on AWS Elastic Container Service (ECS) -description: >- - When running Consul on Amazon Web Services ECS, you can use Terraform to secure your service mesh with an auth method and ACL controller. Learn how to configure Terraform modules to enable secure deployment. ---- - -# Secure Configuration for Consul on AWS Elastic Container Service (ECS) with Terraform - -This topic describes how to enable Consul security features for your production workloads. - -## Overview - -To enable security in your production workloads, you must deploy the [ACL controller](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/acl-controller), which provisions tokens for other service mesh tasks. Refer to [Architecture](/consul/docs/ecs/architecture#acl-controller) to learn more about the ACL controller. - -The controller cannot provision tokens for itself, so you must create the token for the ACL controller. The following steps describe the overall process of enabling security features for your production workloads: - -1. Enable the security features on your Consul server cluster per the [Prerequisites](#prerequisites). -1. Create the ACL token for the ACL controller in the datacenter. -1. Create a Secrets Manager secret containing the ACL controller's token. -1. Create a Secrets Manager secret containing the Consul CA certificate. -1. Deploy the ACL controller -1. Deploy the other services on the mesh. - -## Prerequisites - -Implement the following security features for your Consul server clusters before applying them to your workloads: - -1. [TLS encryption](/consul/docs/security/encryption#rpc-encryption-with-tls) for RPC communication between Consul clients and servers. -1. [Gossip encryption](/consul/docs/security/encryption#gossip-encryption) for encrypting gossip traffic. -1. [Access control lists (ACLs)](/consul/docs/security/acl) for authentication and authorization for Consul clients and services on the mesh. - -## Auth Method - -Consul on ECS uses the [AWS IAM Auth Method](/consul/docs/ecs/architecture#aws-iam-auth-method) to enable -tasks to automatically obtain Consul ACL tokens during startup. - -With the Terraform modules for Consul on ECS, the auth method is supported by default when ACLs are -enabled. The ACL controller sets up the auth method on the Consul servers. The `mesh-task` module -configures the ECS task definition to be compatible with the auth method. - -A unique task IAM role is required for each ECS task family. Task IAM roles must not be shared by -different task families. This is because the task family represents only one Consul service and the -task IAM role must encode the Consul service name. - -By default, the `mesh-task` module will create and configure the task IAM role for you. - -~> **Note:** When passing an existing IAM role to the `mesh-task` module using the `task_role` input -variable, you must configure the IAM role as described in [ECS Task Role -Configuration](/consul/docs/ecs/manual/secure-configuration#ecs-task-role-configuration) to be compatible with -the AWS IAM auth method. - -## ACL controller - -1. Create a policy that grants `acl:write` and `operator:write` access for the controller. Refer to the [ACL policies documentation](/consul/docs/security/acl/acl-policies) for instructions. -1. Create a token and link it to the ACL controller policy. Refer to the [ACL tokens documentation](/consul/docs/security/acl/tokens) for instructions. -1. Create a Secrets Manager secret containing the ACL controller's token and a Secrets Manager secret containing the Consul CA cert. - -```hcl -resource "aws_secretsmanager_secret" "bootstrap_token" { - name = "bootstrap-token" -} - -resource "aws_secretsmanager_secret_version" "bootstrap_token" { - secret_id = aws_secretsmanager_secret.bootstrap_token.id - secret_string = "" -} - -resource "aws_secretsmanager_secret" "ca_cert" { - name = "server-ca-cert" -} - -resource "aws_secretsmanager_secret_version" "ca_cert" { - secret_id = aws_secretsmanager_secret.ca_cert.id - secret_string = "" -} -``` - -1. Use the [`acl-controller` terraform module](https://registry.terraform.io/modules/hashicorp/consul-ecs/aws/latest/submodules/acl-controller?tab=inputs) to deploy the controller. You must provide the ARN's for the token and CA cert in the `consul_bootstrap_token_secret_arn` and `consul_server_ca_cert_arn` fields, respectively. - - ```hcl - module "acl_controller" { - source = "hashicorp/consul-ecs/aws//modules/acl-controller" - version = "" - - consul_bootstrap_token_secret_arn = aws_secretsmanager_secret.bootstrap_token.arn - consul_server_http_addr = "https://consul-server.example.com:8501" - consul_server_ca_cert_arn = aws_secretsmanager_secret.ca_cert.arn - ecs_cluster_arn = "arn:aws:ecs:us-east-1:111111111111:cluster/consul-ecs" - region = "us-east-1" - subnets = ["subnet-abcdef123456789"] - name_prefix = "consul-ecs" - } - ``` - -The following table describes the required input variables for the `acl-controller` module. - -| Input Variable | Type | Description | -| ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `source` | string | The location of the `acl-controller` module source. | -| `version` | string | The version of the `acl-controller` module. | -| `consul_bootstrap_token_secret_arn` | string | The Secrets Manager secret containing an ACL token for the ACL controller. The ACL token must have `acl:write` and `operator:write` permissions. | -| `consul_server_http_addr` | string | The HTTP(S) address of the Consul servers. | -| `consul_server_ca_cert_arn` | string | (Optional) The Secrets Manager secret containing the CA cert for HTTPS communication with Consul servers. Required if the server's certificate is self-signed or signed by an internal CA. This is not required for Consul servers in HCP. | -| `ecs_cluster_arn` | string | The ECS cluster where the ACL controller will be deployed. | -| `region` | string | The AWS region where the AWS resources will be created. | -| `subnets` | list | The AWS VPC subnet ids where the ACL controller will be deployed. | -| `name_prefix` | string | AWS resources created by the `acl-controller` module will include this prefix in the resource name. | - - - -If you are using Consul Enterprise, see the [Admin Partitions and Namespaces requirements documentation](/consul/docs/ecs/requirements) for additional configuration required to support Consul Enterprise on ECS. - - - -## Deploy your services - -Follow the instructions described in [Create a task definition](/consul/docs/ecs/terraform/install#create-the-task-definition) to create the basic configuration for the task module. Add the following additional configurations to make the configuration production-ready. - -### Create an AWS Secrets Manager secret - -The secret stores the gossip encryption key that the Consul clients use. - - - -```hcl -resource "aws_secretsmanager_secret" "gossip_key" { - name = "gossip-encryption-key" -} - -resource "aws_secretsmanager_secret_version" "gossip_key" { - secret_id = aws_secretsmanager_secret.gossip_key.id - secret_string = "" -} -``` - - - -### Enable secure deployment - -To enable secure deployment, add the following configuration to the task module. - -```hcl -module "my_task" { - source = "hashicorp/consul-ecs/aws//modules/mesh-task" - version = "" - - ... - - tls = true - consul_server_ca_cert_arn = aws_secretsmanager_secret.ca_cert.arn - gossip_key_secret_arn = aws_secretsmanager_secret.gossip_key.arn - - acls = true - consul_http_addr = "https://consul-server.example.com:8501" - consul_https_ca_cert_arn = aws_secretsmanager_secret.ca_cert.arn - audit_logging = true -} -``` - -The following table explains the `mesh-task` input variables relevant to a secure configuration: - -| Input Variable | Type | Description | -| ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- | -| `tls` | boolean | If true, TLS is enabled for RPC communication with the Consul servers. | -| `consul_server_ca_cert_arn` | string | The Secrets Manager secret containing the CA certificate for RPC communication with the Consul servers when TLS is enabled. | -| `gossip_key_secret_arn` | string | The Secrets Manager secret containing Consul's gossip encryption key. | -| `acls` | boolean | If true, ACLs are enabled. | -| `consul_http_addr` | string | The Consul server address. Required when `acls = true` in order to log in to Consul's AWS IAM auth method to obtain ACL tokens. | -| `consul_https_ca_cert_arn` | string | (optional) The Secrets Manager secret containing the CA cert for HTTPS communication with Consul servers. Required if the server's certificate is self-signed or signed by an internal CA. This is not required for Consul servers in HCP. | -| `audit_logging` | boolean | (optional) If true, ACL audit logging is enabled. Consul client is configured to print audit logs to `stdout`. | - -Complete the following steps described in the Installation with Terraform chapter to deploy and connect your services: - -1. [Run Terraform](/consul/docs/ecs/terraform/install#run-terraform) -1. [Configure routes](/consul/docs/ecs/terraform/install#configure-routes) -1. [Configure the bind address](/consul/docs/ecs/terraform/install#configure-the-bind-address) diff --git a/website/content/docs/ecs/upgrade-to-dataplanes.mdx b/website/content/docs/ecs/upgrade-to-dataplanes.mdx new file mode 100644 index 0000000000..c0a788dc66 --- /dev/null +++ b/website/content/docs/ecs/upgrade-to-dataplanes.mdx @@ -0,0 +1,68 @@ +--- +layout: docs +page_title: Upgrade to Consul dataplane architecture +description: Learn how to upgrade your existing Consul service mesh on ECS workloads to the agentless dataplanes architecture. +--- + +# Upgrade to Consul dataplane architecture + +This topic describes how to manually upgrade a live installation of Consul on ECS to the dataplane-based architecture with zero downtime. Since v0.7.0, Consul service mesh on ECS uses [Consul dataplanes](/consul/docs/connect/dataplane), which are lightweight processes for managing Envoy proxies in containerized networks. Refer to the [release notes](/consul/docs/release-notes/consul-ecs/v0_7_x) for additional information about the switch to Consul dataplanes. + +## Requirements + +Before you upgrading to the dataplane-based architecture, you must upgrade your Consul servers to a version compatible with Consul ECS: + +- Consul 1.14.x and later +- Consul dataplane 1.3.x and later + +## Deploy the latest version of the ECS controller module + +In an ACL enabled cluster, deploy the latest version of the ECS controller module in `hashicorp/terraform-aws-consul-ecs` along with the older version of the ACL controller. Note that both the controllers should coexist until the upgrade is complete. The new version of the controller only tracks tasks that use dataplanes. + +## Upgrade workloads + +For application tasks, upgrade the individual task definitions to `v0.7.0` or later of the `mesh-task` module. You must upgrade each task one at a time. + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/mesh-task" + version = "v0.7.0" +} +``` + +For gateway tasks, upgrade the individual task definitions to `v0.7.0` or later of the `gateway-task` module. You must upgrade each task one by one independently. ECS creates new versions of tasks before shutting down the older tasks to support zero downtime deployments. + +```hcl +module "my_task" { + source = "hashicorp/consul-ecs/aws//modules/gateway-task" + version = "v0.7.0" +} +``` + +## Delete previous tasks + +After upgrading all tasks, you can destroy the `acl-controller` containers, which are replaced by the ECS controller. You can manually remove any artifacts related to the old architecture, including Consul clients and ACL controllers, by executing the following commands: + +1. Run `consul acl policy delete` to delete the client policy. You can pass either the ID of the policy or the name of the policy, for example: + + ```shell-session + $ consul acl policy delete -name="consul-ecs-client-policy" + ``` + + Refer to the [`consul acl policy delete`](/consul/commands/acl/policy/delete) documentation for additional information. + +1. Run the `consul acl role delete` command to delete the client role. You can pass either the ID of the role or the name of the role, for example: + + ```shell-session + $ consul acl role delete -name="consul-ecs-client-role" + ``` + + Refer to the [`consul acl role delete`](/consul/commands/acl/role/delete) documentation for additional information. + +1. Run the `consul acl auth-method delete` command and specify the auth method name to delete. + + ```shell-session + $ consul acl auth-method delete -name="iam-ecs-client-token" + ``` + + Refer to the [`consul acl auth-method delete`](/consul/commands/acl/auth-method/delete) documentation for additional information. \ No newline at end of file diff --git a/website/content/docs/release-notes/consul-ecs/v0_5_x.mdx b/website/content/docs/release-notes/consul-ecs/v0_5_x.mdx index 4aa6de68a1..c6282bffaf 100644 --- a/website/content/docs/release-notes/consul-ecs/v0_5_x.mdx +++ b/website/content/docs/release-notes/consul-ecs/v0_5_x.mdx @@ -11,9 +11,9 @@ description: >- - **Audit Logging (Enterprise) :** Consul on ECS now captures authentication events and processes them with the HTTP API. Audit logging provides insight into access and usage patterns. Refer to [Audit Logging](/consul/docs/ecs/enterprise#audit-logging) for usage information. -- **AWS IAM Auth Method :** This feature provides support for Consul's AWS IAM auth method. This allows AWS IAM roles and users to authenticate with Consul to obtain ACL tokens. Refer to [ECS Configuration Reference](/consul/docs/ecs/configuration-reference#consullogin) for configuration information. +- **AWS IAM Auth Method :** This feature provides support for Consul's AWS IAM auth method. This allows AWS IAM roles and users to authenticate with Consul to obtain ACL tokens. Refer to [ECS Configuration Reference](/consul/docs/ecs/reference/configuration-reference#consullogin) for configuration information. -- **Mesh Gateways :** This feature introduces support for running mesh gateways as ECS tasks. Mesh gateways enable service mesh communication across datacenter and admin partition boundaries. Refer to [ECS Installation with Terraform](/consul/docs/ecs/terraform/install#configure-the-gateway-task-module) for usage information. +- **Mesh Gateways :** This feature introduces support for running mesh gateways as ECS tasks. Mesh gateways enable service mesh communication across datacenter and admin partition boundaries. Refer to [ECS Installation with Terraform](/consul/docs/ecs/deploy/terraform#configure-the-gateway-task-module) for usage information. ## Supported Software Versions diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index d3591ada7e..49dc215b16 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -1539,63 +1539,63 @@ "title": "Overview", "path": "ecs" }, - { - "title": "Requirements", - "path": "ecs/requirements" - }, - { - "title": "Task Resource Usage", - "path": "ecs/task-resource-usage" - }, - { - "title": "Install with Terraform", - "routes": [ - { - "title": "Installation", - "path": "ecs/terraform/install" - }, - { - "title": "Secure Configuration", - "path": "ecs/terraform/secure-configuration" - }, - { - "title": "Migrate Existing Tasks", - "path": "ecs/terraform/migrate-existing-tasks" - } - ] - }, - { - "title": "Install Manually", - "routes": [ - { - "title": "Installation", - "path": "ecs/manual/install" - }, - { - "title": "Secure Configuration", - "path": "ecs/manual/secure-configuration" - }, - { - "title": "ACL Controller", - "path": "ecs/manual/acl-controller" - } - ] - }, { "title": "Architecture", "path": "ecs/architecture" }, + { + "title": "Technical specifications", + "path": "ecs/tech-specs" + }, + { + "title": "Deploy Consul to ECS", + "routes": [ + { + "title": "Deploy using the Terraform module", + "path": "ecs/deploy/terraform" + }, + { + "title": "Deploy manually", + "path": "ecs/deploy/manual" + }, + { + "title": "Configure the ECS task bind address", + "path": "ecs/deploy/bind-addresses" + }, + { + "title": "Configure routes between ECS tasks", + "path": "ecs/deploy/configure-routes" + }, + { + "title": "Migrate existing tasks", + "path": "ecs/deploy/migrate-existing-tasks" + } + ] + }, + { + "title": "Upgrade to Consul dataplane architecture", + "path": "ecs/upgrade-to-dataplanes" + }, { "title": "Consul Enterprise", "path": "ecs/enterprise" }, { - "title": "Configuration Reference", - "path": "ecs/configuration-reference" - }, - { - "title": "Compatibility Matrix", - "path": "ecs/compatibility" + "title": "Reference", + "routes": [ + { + "title": "ECS configuration reference", + "path": "ecs/reference/configuration-reference" + }, + { + "title": "ECS compatibility matrix", + "path": "ecs/reference/compatibility" + }, + { + "title": "ECS Consul server JSON schema", + "path": "ecs/reference/consul-server-json" + } + ] } ] }, diff --git a/website/public/img/ecs/consul-on-ecs-architecture-dataplanes-dark.jpg b/website/public/img/ecs/consul-on-ecs-architecture-dataplanes-dark.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc88e95145b52d3aaa976559f9cd355b1f533cda GIT binary patch literal 68588 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 SXc`zz1EXnRXr}@8|2F~XV77Ju literal 0 HcmV?d00001 diff --git a/website/redirects.js b/website/redirects.js index 1bf907feff..fa2b13bbb3 100644 --- a/website/redirects.js +++ b/website/redirects.js @@ -87,4 +87,54 @@ module.exports = [ destination: '/consul/docs/connect/proxies/deploy-sidecar-services', permanent: true, }, + { + source: '/consul/docs/ecs/terraform/install', + destination: '/consul/docs/ecs/deploy/terraform', + permanent: true, + }, + { + source: '/consul/docs/ecs/terraform/secure-configuration', + destination: '/consul/docs/ecs/deploy/terraform', + permanent: true, + }, + { + source: '/consul/docs/ecs/terraform/migrate-existing-tasks', + destination: '/consul/docs/ecs/deploy/migrate-existing-tasks', + permanent: true, + }, + { + source: '/consul/docs/ecs/manual/install', + destination: '/consul/docs/ecs/deploy/manual', + permanent: true, + }, + { + source: '/consul/docs/ecs/manual/secure-configuration', + destination: '/consul/docs/ecs/deploy/manual', + permanent: true, + }, + { + source: '/consul/docs/ecs/manual/acl-controller', + destination: '/consul/docs/ecs/deploy/manual', + permanent: true, + }, + { + source: '/consul/docs/ecs/task-resource-usage', + destination: '/consul/docs/ecs/tech-specs', + permanent: true, + }, + { + source: '/consul/docs/ecs/requirements', + destination: '/consul/docs/ecs/tech-specs', + permanent: true, + }, + { + source: '/consul/docs/ecs/configuration-reference', + destination: '/consul/docs/ecs/reference/configuration-reference', + permanent: true, + }, + { + source: '/consul/docs/ecs/compatibility', + destination: '/consul/docs/ecs/reference/compatibility', + permanent: true, + }, ]