Eve Security Installer 2.2.37

Eve Guard on AWS ECS Fargate

Deploy Eve Guard into your own AWS account with a single CloudFormation stack. No Kubernetes, no cluster to build, no servers to patch — ECS Fargate runs the containers and AWS manages the hosts.

Before you start: request these two secrets from the Eve Customer Success team and have them in hand before installing — both are required:

The template itself needs no request. It is public — it contains no secrets, only infrastructure — so you can launch it directly; see Section 4.

Running Kubernetes instead? See the EKS guide.


Quick install

  1. Create the ACM certificate for your gateway domain, in the same region you will deploy into.

  2. Open the AWS CloudFormation console with this release’s Eve Guard template (new tab). Change the console region if you are not deploying to us-west-2 — the certificate must be in that same region:

    Launch Eve Guard in AWS CloudFormation

  3. Fill in the parameters — there are seven you must supply.

  4. Tick the IAM acknowledgement and create the stack.

Roughly 10 minutes later the stack outputs a GatewayUrl. Point your MCP clients at it.

There is nothing to download, no CLI to install, and no files to edit.


Quick update

Nothing to do, if you accept the default. Eve Guard checks weekly for new releases and emails the address you gave it. Set AutoUpdateChannel to patch at install time and it applies them for you, with automatic rollback if live traffic starts failing.

See Section 6: Updates for all three postures.


Quick uninstall

Delete the CloudFormation stack. Some things are kept on purpose:

Both are listed in Section 9: Uninstall with the commands to remove them once you are sure. Neither blocks reinstalling.


1. What gets deployed

Five containers, in your VPC, in your account. Traffic never leaves your network except to reach the Eve platform for policy configuration.

Service Purpose Tasks
kong The gateway itself. Terminates MCP traffic and enforces policy. 1
orchestrator Policy decision point. Analyses every request. 4–8, autoscaled
redis-state OAuth session state and pending policy evaluations. In-memory (same as EKS redis-auth). 1
redis-cache Prompt analysis cache. Disposable. 1
rule-improver Optional. Proposes policy improvements from observed traffic. 0 or 1

Alongside them the stack creates an Application Load Balancer, one Secrets Manager secret, CloudWatch log groups, and two small Lambda functions — one that validates your environment before anything else is built, and one that checks weekly for new releases.

Clients reach the ALB over HTTPS on 443. TLS terminates there using your ACM certificate; inside the VPC, services talk to each other over ECS Service Connect.

What it costs

Rough monthly estimate for the default sizing in us-west-2, on-demand pricing, excluding data transfer:

Item Estimate
Orchestrator, 4 tasks × 1 vCPU / 2 GB ~$120
Kong, 1 task × 1 vCPU / 2 GB ~$30
Redis, 2 tasks × 0.5 vCPU / 1 GB ~$30
Application Load Balancer ~$20
CloudWatch Logs, Lambda, Secrets Manager ~$5
Total ~$205/month

A NAT gateway, if you do not already have one, adds roughly $32/month per availability zone. rule-improver adds roughly $30/month when enabled. These are estimates from public pricing, not a quote.


2. Prerequisites

An existing VPC with two subnets in different availability zones

The stack does not create a VPC. It uses yours. You need:

Outbound internet access from the task subnets

This is the single most common install failure, so it is worth checking before you start. The containers must reach two internet endpoints:

Both are on the public internet, so VPC endpoints alone are not sufficient. There are two supported shapes:

Shape AssignPublicIp Notes
Private subnets with a NAT gateway DISABLED Recommended. Tasks have no public IP.
Public subnets ENABLED No NAT cost, but tasks get public IPs.

The stack checks this for you before creating anything, and fails in about 20 seconds with a readable message rather than 15 minutes later with CannotPullContainerError.

An ACM certificate in the deployment region

The load balancer needs a certificate for your gateway domain, and ACM certificates for an ALB must live in the same region as the load balancer. A certificate in us-east-1 will not work for a load balancer in eu-west-1. This is also checked before install.

If you do not have one yet:

aws acm request-certificate \
  --domain-name gateway.example.com \
  --validation-method DNS \
  --region us-west-2

Complete the DNS validation and wait for status ISSUED before installing.

Keeping the gateway private

By default the load balancer is reachable from the internet. If your clients are all inside your own network, set LoadBalancerScheme to internal and the gateway never gets a public address.

What this needs:

Setting Value
LoadBalancerScheme internal
LoadBalancerSubnetIds Private subnets, at least two, in different AZs
LoadBalancerAllowedCidr The range your clients come from
DNS for GatewayDomainName A private hosted zone

Two things catch people out.

Private subnets alone are not enough. AWS decides where a load balancer lives from the scheme, not from the subnets you hand it, so passing private subnet IDs to an internet-facing load balancer does not make it private — it fails to create instead.

LoadBalancerAllowedCidr is the network your clients reach the load balancer from, which is not always this VPC. If your users come in over a VPN that lives in another account and peers into this one, the range you want is that VPN’s VPC, not the one hosting the gateway. Getting this wrong locks out the people who need it, which at least fails loudly.

Also note that DNS validation for your ACM certificate happens over the public internet even when the gateway itself is private. The certificate proves the name, it does not expose the endpoint.

Switching an existing stack between the two schemes replaces the load balancer and changes its DNS name, so plan a DNS cutover rather than treating it as a routine parameter change.

Putting a WAF in front

You can, and the stack does not need to know about it. The load balancer is a normal ALB, so attach a WAFv2 web ACL to it whenever you like:

aws wafv2 associate-web-acl \
  --web-acl-arn "arn:aws:wafv2:us-west-2:111122223333:regional/webacl/my-acl/abc" \
  --resource-arn "$(aws cloudformation describe-stack-resources \
    --stack-name eve-guard --logical-resource-id LoadBalancer \
    --query 'StackResources[0].PhysicalResourceId' --output text)"

The ACL must be REGIONAL scope and in this region; a CLOUDFRONT scope ACL cannot attach to a load balancer.

This is deliberately outside the template. Your web ACL is yours — your rules, your rate limits, your logging, on your own change cadence — and putting the association in the stack would only add a way for a stack operation to detach it. It is also not a prerequisite: install first, add the WAF when you are ready.

Worth adding alongside LoadBalancerAllowedCidr rather than instead of it. The security group decides who can open a connection; the WAF inspects the requests of those allowed through. Neither substitutes for the other.

Protecting a live gateway

EnableDeletionProtection makes the load balancer refuse to be deleted. It is off by default, and worth turning on once the gateway is actually serving traffic, because the ALB is the one resource in this stack whose loss is immediately visible to every client.

It is off by default because it also blocks rollback. If a create or update fails after the load balancer exists, CloudFormation tries to remove it, cannot, and leaves the stack in ROLLBACK_FAILED for you to clear by hand. First installs are when failures are most likely, so the protection costs more than it saves until the stack is known good.

The preflight check catches the usual first-install failures — a certificate in the wrong region, subnets with no egress, a bad license key — before any resource exists, so most failures never reach the load balancer. It cannot predict every one.

Turn it on with an ordinary stack update once you are serving:

aws cloudformation update-stack --stack-name eve-guard \
  --use-previous-template --capabilities CAPABILITY_NAMED_IAM \
  --parameters ParameterKey=EnableDeletionProtection,ParameterValue=true \
    ParameterKey=EveLicenseKey,UsePreviousValue=true \
    ParameterKey=GhcrToken,UsePreviousValue=true

Remember to clear it before an uninstall — Section 9 has the command.

Permissions to create the stack

The stack creates IAM roles, so whoever runs it needs CAPABILITY_IAM/CAPABILITY_NAMED_IAM. If your organisation does not allow that, see Section 8 — Eve can run the install with a narrowly scoped role instead.


3. Parameters

Required

Parameter Description
EveLicenseKey Your Eve license key (evk_...). Stored in Secrets Manager, never logged.
GhcrToken Registry token from Eve, for pulling the images.
VpcId The VPC to deploy into.
TaskSubnetIds Two or more subnets, in different AZs, for the containers.
LoadBalancerSubnetIds Two or more subnets for the load balancer. Public ones unless you set LoadBalancerScheme to internal.
CertificateArn ACM certificate ARN, in this region.
GatewayDomainName The DNS name clients will use, e.g. gateway.example.com.

Commonly changed

Parameter Default Description
EveVersion 2.2.37 Version to deploy. Drives all four image tags together.
OpenAiApiKey empty Your own OpenAI key. Recommended — see the note below.
AutoUpdateChannel notify-only off, notify-only, patch, or minor. See Section 6.
NotificationEmail empty Where update and error notifications go. Strongly recommended.
AssignPublicIp DISABLED Set to ENABLED only when using public task subnets with no NAT.
LoadBalancerScheme internet-facing internal keeps the gateway off the public internet. See Keeping the gateway private.
LoadBalancerAllowedCidr 0.0.0.0/0 Narrows who may reach the load balancer on 80 and 443.
EnableDeletionProtection false Blocks deletion of the load balancer. Turn on once the gateway is serving — see Protecting a live gateway.
OrchestratorMinTasks 4 Minimum orchestrator tasks.
OrchestratorMaxTasks 8 Maximum orchestrator tasks. Scales on CPU 70% / memory 80%.
EnableRuleImprover false Deploy the optional policy improvement service.
LogRetentionDays 30 CloudWatch Logs retention.

Rarely changed

Parameter Default Description
KongDesiredCount 1 Kong task count. Read the warning below before raising.
ExistingRedisCacheEndpoint empty Use an existing ElastiCache cluster for the prompt cache instead of an in-stack task.
KongCustomServicesYaml empty Extra MCP routes. See Section 7.
EvePlatformUrl Eve’s endpoint Override only if Eve tells you to.
LogLevel INFO DEBUG is very verbose; use it only while diagnosing.
GhcrUsername eve-installer Override only if Eve tells you to.

About OpenAiApiKey. Supplying your own key is the setup we recommend: LLM usage is then billed to your OpenAI account, and the key is held in Secrets Manager in your account rather than anywhere else. Set it at install and you are done.

It is optional because Eve Guard can instead resolve a key from your license payload, which is worth knowing about for two reasons. First, if Eve has provisioned a key for your guard, that key wins — the license payload is read before the environment, so this parameter has no effect and changing it will not appear to do anything. Second, if you leave this empty and Eve has not provisioned one, the gateway installs and starts normally and then fails on the first request that needs an LLM, because there is no key to use. If you are unsure which applies to your guard, ask Eve before leaving it empty.

About KongDesiredCount. Kong’s rate limiting counts per task, so raising this multiplies your effective rate limit by the task count. One Kong task handles substantial throughput because it only proxies — the analysis work happens in the orchestrator, which is the service that autoscales. Talk to Eve before raising this.


4. Install

Open the Launch Eve Guard in AWS CloudFormation button in Quick install. The template is public — nothing in it is a secret — and it pre-fills the template URL, so you land directly on the parameter form.

  1. Stack name — eve-guard. Keep the eve-guard prefix if Eve will manage updates for you, because their access is scoped by stack name.
  2. Parameters — fill in the seven required values from Section 3.
  3. Next through the options page. No changes needed.
  4. Acknowledge that the stack creates IAM resources.
  5. Submit.

Watch the Events tab. Custom::Preflight runs first and completes within a minute; if your environment has a problem, this is where you find out, with a message naming the specific subnet, certificate or license issue.

If you supplied a NotificationEmail, confirm the SNS subscription email that arrives shortly after — unconfirmed subscriptions receive nothing.

CLI

The template URL below is public — no credentials or account registration needed to fetch it, only the secrets in --parameters are yours to keep. Confirm the current version at https://install.eve.security/versions.json first (.ecs.template_url), since versioned keys are immutable and there is no latest.

aws cloudformation create-stack \
  --stack-name eve-guard \
  --template-url https://eve-cfn-templates.s3.us-west-2.amazonaws.com/v2.2.37/ecs/eve-guard-ecs.yaml \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --parameters \
    ParameterKey=EveLicenseKey,ParameterValue=evk_xxx \
    ParameterKey=GhcrToken,ParameterValue=ghp_xxx \
    ParameterKey=VpcId,ParameterValue=vpc-0abc \
    ParameterKey=TaskSubnetIds,ParameterValue=\"subnet-0aaa,subnet-0bbb\" \
    ParameterKey=LoadBalancerSubnetIds,ParameterValue=\"subnet-0ccc,subnet-0ddd\" \
    ParameterKey=CertificateArn,ParameterValue=arn:aws:acm:us-west-2:111122223333:certificate/abc \
    ParameterKey=GatewayDomainName,ParameterValue=gateway.example.com \
    ParameterKey=NotificationEmail,ParameterValue=platform@example.com

aws cloudformation wait stack-create-complete --stack-name eve-guard

Note the escaped quotes around the subnet lists — CloudFormation needs the comma-separated list as a single value.

After install

Read the outputs:

aws cloudformation describe-stacks --stack-name eve-guard \
  --query 'Stacks[0].Outputs' --output table

Point your domain at the load balancer. Create a CNAME (or a Route 53 alias) from your GatewayDomainName to the LoadBalancerDnsName output. Until you do, HTTPS requests will fail certificate validation, because the certificate is for your domain and not for the ALB’s own hostname.

Verify the gateway is healthy:

curl -sS https://gateway.example.com/status

Point your MCP clients at the GatewayUrl output.


5. Verify

# All five services should show runningCount matching desiredCount.
aws ecs list-services --cluster eve-guard --query 'serviceArns' --output table

aws ecs describe-services --cluster eve-guard \
  --services kong orchestrator redis-state redis-cache \
  --query 'services[].{name:serviceName,desired:desiredCount,running:runningCount}' \
  --output table

# The load balancer's view. Kong should be "healthy".
aws elbv2 describe-target-health \
  --target-group-arn "$(aws elbv2 describe-target-groups \
    --query "TargetGroups[?contains(TargetGroupName,'eve')].TargetGroupArn" \
    --output text)" \
  --query 'TargetHealthDescriptions[].TargetHealth.State'

Logs, per service:

aws logs tail /ecs/eve-guard/kong --follow
aws logs tail /ecs/eve-guard/orchestrator --follow

A healthy Kong startup logs its resolved policy flags:

🔧 Eve Kong runtime flags: mcp_only=false interrogation_enabled=false
✅ Kong ready - starting in foreground mode

Those values come from your license. If you instead see a line about the config bootstrap failing, Kong could not reach the Eve platform — see Troubleshooting.


6. Updates

Eve ships new features every few weeks. You never need to uninstall and reinstall. The stack updates in place: the load balancer, its DNS name, your secret and your persisted state all survive.

Three postures, set by AutoUpdateChannel:

Posture What happens
notify-only (default) Weekly check; you get an email when a release is available. You apply it when ready.
patch Patch releases (2.2.0 → 2.2.1) apply automatically.
minor Minor and patch releases (2.2.0 → 2.3.0) apply automatically.
off No checks, no notifications. You track releases yourself.

notify-only is the default because automatically updating a security gateway is a change-management decision that is yours to make, not ours. If your process allows it, patch is the lowest-effort safe option.

How automatic updates work

A small Lambda in your stack runs weekly. It fetches https://install.eve.security/versions.json, compares the latest version to what you are running, and if an update is due it calls CloudFormation’s UpdateStack on its own stack with the new version.

Three properties worth knowing:

That second role also cannot change any IAM role or policy, including its own. An update that only moves the version never needs to, and refusing it means a scheduled function can never mint or widen a role. If a future release does change a role, the automatic update fails and rolls back, and you apply that release manually — the same escalation you get for a release that needs a new template.

You are notified by email in every case: update available, update started, update failed.

One consequence to know about. Once an automatic update has run, CloudFormation associates that execution role with your stack and reuses it for later operations unless you say otherwise. Because the role cannot change IAM, a later manual upgrade that does change a role will fail with AccessDenied. Pass your own role explicitly to override it: aws cloudformation update-stack --role-arn <your-admin-or-deployment-role> ..., or pick IAM role on the console’s update screen. The same applies to delete-stack.

Manual update

Two ways. From the console: Update stack → Use current template → change EveVersion → next, next, submit.

Or from the CLI, which is the same operation:

aws cloudformation update-stack \
  --stack-name eve-guard \
  --use-previous-template \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --parameters \
    ParameterKey=EveVersion,ParameterValue=2.2.37 \
    ParameterKey=EveLicenseKey,UsePreviousValue=true \
    ParameterKey=GhcrToken,UsePreviousValue=true \
    ParameterKey=VpcId,UsePreviousValue=true \
    ParameterKey=TaskSubnetIds,UsePreviousValue=true \
    ParameterKey=LoadBalancerSubnetIds,UsePreviousValue=true \
    ParameterKey=CertificateArn,UsePreviousValue=true \
    ParameterKey=GatewayDomainName,UsePreviousValue=true

aws cloudformation wait stack-update-complete --stack-name eve-guard

--use-previous-template matters: it changes only the image tags and never touches your parameters or the template.

Updates are rolling. ECS starts new tasks, waits for them to pass health checks, then drains the old ones. The deployment circuit breaker rolls back automatically if the new tasks fail to become healthy.

Occasionally, a new template

Most releases only move image tags. Every so often a release changes the infrastructure itself, and then you need the new template rather than the one already in your stack. Eve tells you when this applies, and the automatic updater will email you rather than attempting it:

aws cloudformation update-stack \
  --stack-name eve-guard \
  --template-url https://eve-cfn-templates.s3.us-west-2.amazonaws.com/v2.2.37/ecs/eve-guard-ecs.yaml \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --parameters ParameterKey=EveVersion,ParameterValue=2.2.37 # ... plus UsePreviousValue for the rest

Create a change set first if you want to see exactly what will change:

aws cloudformation create-change-set \
  --stack-name eve-guard --change-set-name review-new-template \
  --template-url https://eve-cfn-templates.s3.us-west-2.amazonaws.com/v2.2.37/ecs/eve-guard-ecs.yaml \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --parameters ParameterKey=EveVersion,ParameterValue=2.2.37

aws cloudformation describe-change-set \
  --stack-name eve-guard --change-set-name review-new-template \
  --query 'Changes[].ResourceChange.{Action:Action,Resource:LogicalResourceId,Replace:Replacement}' \
  --output table

Rotating your credentials

Rotate through the stack, not through Secrets Manager. Editing the secret directly works until the next stack update, which will overwrite it from the stack parameters.

aws cloudformation update-stack \
  --stack-name eve-guard --use-previous-template \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --parameters \
    ParameterKey=EveLicenseKey,ParameterValue=evk_new_value \
    ParameterKey=GhcrToken,UsePreviousValue=true \
    ParameterKey=EveVersion,UsePreviousValue=true # ... plus the rest

The tasks pick up the new value when they next restart. To apply it immediately:

aws ecs update-service --cluster eve-guard --service orchestrator --force-new-deployment
aws ecs update-service --cluster eve-guard --service kong --force-new-deployment

7. Custom MCP routes

By default Eve Guard proxies MCP traffic through its built-in routes. To add your own upstreams, pass Kong declarative YAML in KongCustomServicesYaml:

services:
  - name: internal-jira
    url: https://jira.internal.example.com
    routes:
      - name: internal-jira-route
        paths:
          - /jira

The parameter is capped at 4096 characters. Multi-line values are awkward in the console form, so for anything non-trivial use a parameters file:

aws cloudformation update-stack \
  --stack-name eve-guard --use-previous-template \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --parameters file://params.json

Your services are merged with Eve’s built-in routes; they never replace them.


8. Letting Eve run the install

Optional, and only if you would rather Eve drove installs and upgrades than have a Lambda in your account with UpdateStack rights.

Eve provides a second, much smaller template that creates two IAM roles. It fits in the console paste box, so there is no bucket access involved and the full template is available for review before deployment.

Before you start — gather these values

Collect everything in the table below and share it with the Eve Customer Success team before deploying the roles stack. Eve needs these values to run the install on your behalf and cannot look them up without access to your account.

What Example Where to find it
VPC ID vpc-0aaabbbbccccdddd VPC console → Your VPCs
Task subnet IDs (≥ 2, private, different AZs) subnet-0aaa,subnet-0bbb VPC console → Subnets
Task subnets have NAT gateway? Yes / No VPC console → Route Tables — check if the route table for the task subnets has a 0.0.0.0/0 → nat-… route. If not, containers need public IPs (AssignPublicIp=ENABLED)
Load balancer subnet IDs (≥ 2, public, different AZs) subnet-0ccc,subnet-0ddd VPC console → Subnets
ACM certificate ARN arn:aws:acm:us-east-2:…:certificate/… ACM console → Certificates
Gateway domain name gateway.example.com The DNS name you will point at the load balancer
External ID (generated by you) Run openssl rand -hex 16, store securely, share with Eve CS over a verified channel — prevents the confused deputy attack
Eve AWS Account ID (provided by Eve) Request from Eve Customer Success — verify out of band, do not rely on email alone

Launch the installer roles stack

Once you have the Eve AWS Account ID and your External ID ready, launch the roles stack:

Launch Eve Guard installer roles in AWS CloudFormation

You enter the External ID on the parameter form, acknowledge IAM capabilities, and create the stack.

Share the stack outputs with Eve

Once the stack is in CREATE_COMPLETE, go to the Outputs tab and send the following values to the Eve Customer Success team. Without these, Eve cannot proceed with the install.

Output Description Required
InstallerRoleArn The role Eve will assume to operate in your account ✅
DeploymentRoleArn The role CloudFormation will use to deploy resources ✅
StackNamePrefix The stack name prefix Eve’s access is scoped to ✅

Share these over a verified channel — the same one you used to exchange the External ID.

CLI equivalent (replace placeholders):

EVE_ACCOUNT_ID=XXXXXXXXXXXX   # verify with Eve Customer Success
EXTERNAL_ID='from-eve-cs'

aws cloudformation create-stack \
  --stack-name eve-guard-roles \
  --template-url "$(curl -s https://install.eve.security/versions.json | jq -r .ecs.roles_template_url)" \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameters \
    ParameterKey=EveAccountId,ParameterValue="$EVE_ACCOUNT_ID" \
    ParameterKey=ExternalId,ParameterValue="$EXTERNAL_ID" \
    ParameterKey=GrantScope,ParameterValue=UpdateOnly \
    ParameterKey=StackNamePrefix,ParameterValue=eve-guard

The two roles exist separately for a specific reason. A role that can run CloudFormation with IAM capabilities is effectively account administrator, because it can create a role with any policy attached. So:

Three controls make that split real:

  1. cloudformation:RoleArn — Eve may only run CloudFormation as the deployment role, never as some more privileged role already in the account.
  2. cloudformation:TemplateUrl — create/update/changeset must use Eve’s published eve-guard-ecs.yaml on eve-cfn-templates (no free-form TemplateBody). That stops a compromised Eve identity from substituting a template that mints eve-guard-backdoor with admin rights.
  3. iam:PermissionsBoundary — the deployment role may only create or mutate stack IAM roles when they carry the stack’s <stack>-permissions-boundary managed policy. Prefix-scoped PutRolePolicy alone is not enough to mint unbound admin roles.

There is also an explicit Deny on secretsmanager:GetSecretValue for the installer role, and the deployment role may only read secrets named under the stack prefix, so dynamic references cannot exfiltrate unrelated secrets.

Parameter Default Description
EveAccountId required Eve’s AWS account. Verify this out of band before installing.
ExternalId required Secret Eve must present when assuming the role. Generate with openssl rand -hex 16 and share with Eve Customer Success over a verified channel. Minimum 16 characters.
GrantScope UpdateOnly UpdateOnly lets Eve keep you current but not create the stack. InstallAndUpdate allows the initial install too.
StackNamePrefix eve-guard Eve’s access is limited to the stack named exactly this and to stacks named <prefix>-*. A separately named stack that merely starts with the same letters — eve-guardian, say — is out of reach.

Send Eve the InstallerRoleArn stack output. Most enterprises install themselves and use UpdateOnly.

Restricting the install to a single region

The template’s IAM policies use ${AWS::Region} wildcards rather than a literal region, because the same template is published once and installed by customers in different regions. That means the template alone cannot promise that Eve only ever operates in one region — it inherits whichever region the install runs in.

If you need a single region enforced, do it with a Service Control Policy. An SCP is strictly stronger than anything the template could assert, for three reasons: it applies to every principal in the account including your own administrators, it cannot be altered by a stack update, and Eve has no permission to detach or edit it.

Attach the following to the account (or the OU) that hosts Eve Guard, replacing us-east-1 with your approved region:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyRegionalOperationsOutsideApprovedRegion",
      "Effect": "Deny",
      "NotAction": [
        "iam:*",
        "sts:*",
        "organizations:*",
        "account:*",
        "route53:*",
        "cloudfront:*",
        "globalaccelerator:*",
        "shield:*",
        "waf:*",
        "wafv2:*",
        "support:*",
        "health:*",
        "trustedadvisor:*",
        "budgets:*",
        "servicequotas:*"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": "us-east-1"
        }
      }
    }
  ]
}

Two notes before you apply it:

Independently of the SCP, the stack’s preflight check validates that every task and load balancer subnet belongs to the VPC you selected, and that the ACM certificate resolves in the deployment region. Both fail the install before any resource is created.

Applying your own permissions boundary

If your organisation mandates a standard permissions boundary on every role, note that IAM allows a role to carry exactly one. A boundary you supply would replace the stack’s, not add to it — so the ceiling the stack relies on would become yours to maintain, and any permission it omits surfaces as an AccessDenied mid-install rather than as a validation error. For that reason the template does not accept a boundary ARN as a parameter.

Use an SCP here as well, for the same three reasons it is the right answer for regions: it applies to every principal including your own administrators, a stack update cannot alter it, and Eve cannot detach or edit it. An SCP also composes with the stack’s boundaries instead of displacing them, so you keep both ceilings rather than trading one for the other.

Every role the stack creates lives under the IAM path /<stack>/ and carries a <stack>-*permissions-boundary managed policy, both of which an SCP can condition on. The boundaries themselves are in the published template, so you can read exactly what each role is capped at before you install.

The path also separates one install from another. If you run two Eve Guard stacks in the same account — eve-guard and eve-guard-prod, say — their roles sit under /eve-guard/ and /eve-guard-prod/ respectively, and each stack’s own grants address only its own path. Neither install can pass or modify the other’s roles, which a shared eve-guard-* name prefix would otherwise allow.


9. Uninstall

# Only needed if you turned EnableDeletionProtection on. It is off by default.
# Skipping it when protection is on leaves the stack in DELETE_FAILED; clear
# the attribute and delete again to recover.
aws elbv2 modify-load-balancer-attributes \
  --load-balancer-arn "$(aws cloudformation describe-stack-resources \
    --stack-name eve-guard --logical-resource-id LoadBalancer \
    --query 'StackResources[0].PhysicalResourceId' --output text)" \
  --attributes Key=deletion_protection.enabled,Value=false

aws cloudformation delete-stack --stack-name eve-guard
aws cloudformation wait stack-delete-complete --stack-name eve-guard

Deliberately retained, so an accidental delete is not a license-key-loss event:

Resource Why Remove with
Credentials secret Holds your license key aws secretsmanager delete-secret --secret-id eve-guard/credentials --force-delete-without-recovery
CloudWatch log groups Audit trail aws logs delete-log-group --log-group-name /ecs/eve-guard/kong

Reinstalling with the same stack name while the old secret still exists will fail, because the secret name is already taken. Either delete it as above, or use a different stack name.

Two IAM resources are left behind

If automatic updates were ever enabled, the stack deletes cleanly but leaves two IAM resources in your account:

Resource Name
The updater’s CloudFormation execution role eve-guard-UpdaterExecutionRole-<generated>
Its permissions boundary eve-guard-<stack uid>-update-execution-permissions-boundary

This is a CloudFormation limitation rather than a choice. When the updater applies a release it calls UpdateStack with that role, and CloudFormation then keeps the role associated with the stack for every later operation, including the delete. The role therefore runs its own deletion, and the moment it deletes itself its credentials stop working — anything still in flight fails with “The security token included in the request is invalid”. Retaining the two is what lets everything else, including the stack’s other ten IAM resources, delete cleanly.

Neither resource can do anything on its own: the role is only assumable by CloudFormation, and nothing references it once the stack is gone. Removing them is optional, and leaving them does not block reinstalling — the boundary name carries the stack uid and the role name is generated, so a new install under the same stack name will not collide.

To remove them, delete the role first. IAM refuses to delete a managed policy while it is still attached to a role as a permissions boundary, so the reverse order fails with DeleteConflict.

STACK=eve-guard   # the stack name you deleted

# 1. The role. Its name is generated, so find it by the stack's IAM path.
ROLE=$(aws iam list-roles --path-prefix "/${STACK}/" \
  --query "Roles[?contains(RoleName,'UpdaterExecutionRole')].RoleName" \
  --output text)
aws iam delete-role-policy --role-name "$ROLE" --policy-name apply-stack-update
aws iam delete-role --role-name "$ROLE"

# 2. Only now the boundary, which is free once no role carries it.
POLICY=$(aws iam list-policies --scope Local \
  --query "Policies[?ends_with(PolicyName,'-update-execution-permissions-boundary')].Arn" \
  --output text)
aws iam delete-policy --policy-arn "$POLICY"

If you have installed Eve Guard more than once, each install leaves its own pair and the two queries above return several results. Match them up by the stack uid in the boundary name before deleting, or you will remove the boundary belonging to an install that is still running.

The same retention applies if you turn automatic updates off without deleting the stack. Setting AutoUpdateChannel to off removes both resources from the stack, and Retain leaves the boundary in your account. Because its name is derived from the stack id rather than the install date, turning automatic updates back on later tries to create the same name again and the update fails with EntityAlreadyExists.

So if you are re-enabling automatic updates on a stack that had them turned off, delete the leftover boundary first. Nothing is attached to it at that point, so it deletes on its own:

aws iam delete-policy --policy-arn "$(aws iam list-policies --scope Local \
  --query "Policies[?PolicyName=='${STACK}-<uid>-update-execution-permissions-boundary'].Arn" \
  --output text)"

The <uid> is the fifth dash-separated field of your stack id, which you can read from the stack’s Overview tab, or with aws cloudformation describe-stacks --stack-name "$STACK" --query 'Stacks[0].StackId'.


10. Troubleshooting

CannotPullContainerError during install

The task subnets have no route to the internet, or the registry token is wrong.

# Which is it? This shows the actual error.
aws ecs describe-tasks --cluster eve-guard \
  --tasks "$(aws ecs list-tasks --cluster eve-guard --desired-status STOPPED \
    --query 'taskArns[0]' --output text)" \
  --query 'tasks[0].{stopped:stoppedReason,containers:containers[].reason}'

Preflight failed before anything was created

This is the stack telling you what to fix, and the message names the specific problem. Common ones:

Message contains Fix
“has no route to a NAT gateway” Add a NAT gateway, or use public subnets with AssignPublicIp=ENABLED.
“span only 1 availability zone” Choose task subnets in two different AZs.
“was not found in this region” The ACM certificate is in a different region. Request one in the deployment region.
“rejected the license key” Check for a typo; if it looks right, ask Eve whether the key is active.
“missing supabase_url, …” Your license is valid but the guard is not fully configured on Eve’s side. Contact Eve.

The orchestrator keeps restarting

The orchestrator is deliberately fail-closed: it refuses to start rather than serve traffic without a valid policy configuration. Check its logs first.

aws logs tail /ecs/eve-guard/orchestrator --since 10m
aws ecs describe-services --cluster eve-guard --services redis-state \
  --query 'services[0].{running:runningCount,events:events[:3].message}'

ALB returns 503

No healthy Kong tasks behind the load balancer.

aws logs tail /ecs/eve-guard/kong --since 10m

The most common cause is Kong failing to start because of a configuration error, which shows in its logs as a kong prepare failure. The second most common is simply that the tasks have not finished starting yet — Kong gets a 120-second grace period before the ALB starts failing it.

Kong is running but ignoring my policy

The important thing to know: Kong is fail-open by design. If it cannot reach the Eve platform it keeps serving traffic with default settings rather than going down. That is the right behaviour for an inline gateway, and it means this failure is quiet. Look for it explicitly:

aws logs tail /ecs/eve-guard/kong --since 30m | grep -E "Eve config|runtime flags"

An update failed and rolled back

Expected behaviour, and nothing is broken — the previous version is still serving. Find out why:

aws cloudformation describe-stack-events --stack-name eve-guard \
  --query 'StackEvents[?ResourceStatus==`UPDATE_FAILED`].[LogicalResourceId,ResourceStatusReason]' \
  --output table

A UPDATE_FAILED on an ECS service usually means the new task version could not pass its health checks. Its logs will say why:

aws logs tail /ecs/eve-guard/orchestrator --since 30m

Getting a shell in a running task

Enabled on Kong and the orchestrator for diagnostics:

aws ecs execute-command --cluster eve-guard \
  --task "$(aws ecs list-tasks --cluster eve-guard --service-name kong \
    --query 'taskArns[0]' --output text)" \
  --container kong --interactive --command "/bin/bash"

Requires the Session Manager plugin for the AWS CLI.


11. Getting help

Contact Eve Customer Success with:

Please do not include your license key, registry token or any secret value.