> ## Documentation Index
> Fetch the complete documentation index at: https://docs.formal.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Formal Endpoint for Amazon ECS

> Run the Formal Endpoint as a sidecar in Amazon ECS tasks on EC2, Managed Instances, and Fargate

## Overview

The Formal Endpoint runs as a sidecar in each ECS task. It intercepts the
outbound traffic your [network rules](https://docs.formal.ai/docs/guides/network-rules.md) select,
applies your Formal [policies](https://docs.formal.ai/docs/guides/policies/introduction.md), and logs
each request.

The [`ecs-endpoint` Terraform module](https://github.com/formalco/terraform-provider-formal/tree/main/examples/deployments/aws/ecs-endpoint)
takes your container definitions and returns them with the sidecar added. It
creates no AWS resources of its own.

```mermaid theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
flowchart LR
    subgraph task["ECS task (awsvpc)"]
        app["Application container"]
        sidecar["Formal Endpoint<br/>sidecar container"]
    end

    internet["Public internet"]

    app --> sidecar --> internet

    classDef formal fill:#3D6EFF,stroke:#2847A6,color:#FFFFFF
    class app,sidecar,internet formal
    style task fill:#2847A6,stroke:#2847A6,color:#FFFFFF
```

## Choose a launch type

| | EC2 and Managed Instances | Fargate |
| - | - | - |
| Traffic | The Endpoint intercepts all TCP connections | Applications must proxy HTTP and HTTPS traffic through the Endpoint |
| Required capabilities | `NET_ADMIN`, `NET_BIND_SERVICE` | None |
| Protocols | HTTP, Postgres, MySQL, SSH, and others | HTTP, HTTPS |

Fargate does not allow `NET_ADMIN`, so the Endpoint runs there as a local
proxy. A client that ignores the proxy settings connects directly. Use EC2 or
Managed Instances when Formal must enforce all egress.

## Sign in with the task role

The Endpoint signs in to Formal with OIDC. It exchanges a short-lived token
from the task role for Formal credentials, so you have no API key to store or
rotate.

**1. Turn on outbound identity federation**

Run this once per AWS account:

```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
aws iam enable-outbound-web-identity-federation
aws iam get-outbound-web-identity-federation-info
```

The second command prints your account's issuer URL. Keep it for the next
step.

**2. Create the OIDC integration**

Go to [OIDC Integrations](https://app.formal.ai/oidc-integrations) and
click **Create Integration**. Set **Issuer** to the URL from the previous
step and select the machine user the Endpoint signs in as.

Restrict the claim condition to your task role:

```cel theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
claims.sub == "arn:aws:iam::<ACCOUNT_ID>:role/<TASK_ROLE_NAME>"
```

Copy the generated `integrationoidc_...` ID.

**3. Let the task role request tokens**

```hcl theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
resource "aws_iam_role_policy" "formal_endpoint_oidc" {
  role = aws_iam_role.task.id
  policy = jsonencode({
    Version   = "2012-10-17"
    Statement = [{ Effect = "Allow", Action = "sts:GetWebIdentityToken", Resource = "*" }]
  })
}
```

> **Use an API key instead**
>
> Create an API key for a [machine user](https://app.formal.ai/users) and store
> it in Secrets Manager:
>
> ```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
> aws secretsmanager create-secret --name formal-endpoint-api-key \
>   --secret-string <YOUR_API_KEY>
> ```
>
> Pass its ARN as `api_key_secret_arn` instead of `oidc_integration_id`. The
> task execution role must be able to read it.

## Add the Endpoint to your task

Pass your application containers to the module. Use its output as the task's
container definitions:

```hcl theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
module "formal_endpoint" {
  source = "github.com/formalco/terraform-provider-formal//examples/deployments/aws/ecs-endpoint"

  launch_type         = "EC2" # or "FARGATE"
  oidc_integration_id = "integrationoidc_<id>"
  containers = [{
    name      = "app"
    image     = "my-app:1.2.3"
    essential = true
  }]
}

resource "aws_ecs_task_definition" "app" {
  family                   = "app"
  requires_compatibilities = ["EC2"] # or ["FARGATE"], with task-level cpu and memory
  network_mode             = "awsvpc"
  execution_role_arn       = aws_iam_role.execution.arn
  task_role_arn            = aws_iam_role.task.arn
  container_definitions    = jsonencode(module.formal_endpoint.container_definitions)

  volume {
    name = module.formal_endpoint.volume_name
  }
}
```

Use `launch_type = "EC2"` for ECS Managed Instances too. The task must use the
`awsvpc` network mode, so all its containers share one network namespace.

The Endpoint reserves 1 vCPU and 2 GiB by default. Change this with `cpu` and
`memory_reservation`. On Fargate, size the task for your application plus the
Endpoint.

Your application containers start only once the Endpoint is healthy and has
loaded your policies. Their first request is already inspected.

**Verify:**

```bash theme={"languages":{"custom":["/languages/cel.json","/languages/rego.json"]}}
aws ecs describe-tasks --cluster <CLUSTER> --tasks <TASK_ID> \
  --query 'tasks[0].containers[].[name,healthStatus]' --output text
# Expected: formal-endpoint  HEALTHY
```

Requests from your application then appear in [Logs](https://app.formal.ai/logs).

## TLS trust

The Endpoint decrypts TLS for the hosts your policies inspect, with its own
CA. The module mounts a bundle of the system CAs plus that CA at
`/formal-ca/ca-bundle.pem`. It points `SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`,
`NODE_EXTRA_CA_CERTS`, `AWS_CA_BUNDLE`, `CURL_CA_BUNDLE`, and similar
variables at it. Python, Node.js, Go, curl, and the AWS SDKs need no change.

## Proxy settings on Fargate

With `FARGATE`, the module points each application container at the Endpoint
on `127.0.0.1:3128`. It sets `HTTPS_PROXY`, `HTTP_PROXY`, and `NO_PROXY` for
most clients, `NODE_USE_ENV_PROXY=1` for Node.js, and the proxy properties in
`JAVA_TOOL_OPTIONS` for Java.

## Troubleshooting

> **Application containers never start**
>
> They wait for `formal-endpoint` to be healthy. Check its logs:
>
> * Sign-in errors with OIDC: the claim condition does not match the task
>   role, or the task role lacks `sts:GetWebIdentityToken`.
> * Sign-in errors with an API key: the task execution role cannot read the
>   secret, or the key was revoked.

> **The Endpoint is healthy but nothing appears in Logs**
>
> The Endpoint only intercepts hosts that a
> [network rule](https://docs.formal.ai/docs/guides/network-rules.md) selects. Other traffic passes
> through untouched and is not logged. Add a rule for the hosts you want to
> inspect.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.