Executive summary
To run LILT in your own AWS account, LILT’s deployment automation provisions a standard set of AWS resources — a managed Amazon EKS cluster with EKS-managed node groups, an S3 bucket, an RDS MySQL database, an optional SQS queue, the supporting network, and the encryption keys that protect them — and then runs the LILT platform on that cluster. AWS manages the Kubernetes control plane and the worker-node instance lifecycle; this is not a self-managed cluster running on hand-operated virtual machines. This document is the access request: it lists exactly what your security team needs to grant, why each item is needed, how it is scoped, and how long it is required. There are two grants:1 · Deployment role
2 · Runtime roles
What you are granting, at a glance
Security posture
What your team can rely on when granting this:- Least privilege. Every policy lists only the actions the component needs. Runtime roles are additionally scoped to the specific resource LILT owns (its bucket, queue, key, or DNS zone) — they cannot touch anything else in the account.
- No long-lived keys. Access is brokered by short-lived, federated credentials (EKS Pod Identity for workloads; your choice of federation for the deployment role). LILT does not require static IAM access keys.
- Time-boxed deployment access. The broad create/delete permissions live on the deployment role, which is only needed during install or upgrade and can be detached the rest of the time.
- Encryption by default. S3 and RDS are encrypted with dedicated KMS keys;
the RDS database is private (no public endpoint) and reachable only from within
the VPC. The DB master password is managed by AWS Secrets Manager
(
manage_master_user_password) — no password is stored in Terraform state. Application workloads retrieve it via the External Secrets Operator or directly from Secrets Manager. - Auditable. The runtime policies are fixed and enumerated in this document, so a granted deployment produces a known, reviewable set of standing roles.
<ACCOUNT_ID>, <REGION>,
<CLUSTER_NAME>, <PREFIX> (an environment name prefix, e.g. prod), and the
<*_ARN> / <HOSTED_ZONE_ID> values — substitute the ones for your environment.
This request assumes a single-region deployment; multi-region follows the same
model per region.Grant 1 · Deployment role
Create one IAM role for LILT’s deployment identity and attach the permissions below. This is the complete set ofcreate, describe, modify, tag, and
delete actions required to stand up and later tear down the resources LILT
manages. It is needed only while provisioning; you may detach it between changes.
Why each service is needed:
Policy 1 — STS, ECR, Secrets Manager, S3
Policy 1 — STS, ECR, Secrets Manager, S3
Policy 2 — SQS, RDS, EKS
Policy 2 — SQS, RDS, EKS
Policy 3 — EC2 / VPC, IAM, KMS
Policy 3 — EC2 / VPC, IAM, KMS
Policy 4 — Route 53, extended EC2, CloudWatch Logs, EKS Pod Identity, EventBridge, SSM
Policy 4 — Route 53, extended EC2, CloudWatch Logs, EKS Pod Identity, EventBridge, SSM
- The
eks:*PodIdentityAssociationactions (EKSPodIdentity) are required — LILT binds every workload to its IAM role via EKS Pod Identity, so the deployment creates these associations. Do not drop them. EventBridgeRulesand the EKS-servicessm:GetParameterread are only needed for node auto-provisioning (Karpenter); they can be dropped here.- The instance-lifecycle actions in
EC2Extended—ec2:RunInstances,ec2:TerminateInstances,ec2:StopInstances,ec2:StartInstances— are only needed for self-managed nodes or Karpenter. With EKS-managed node groups the EKS service manages instance lifecycle, so the deployment role does not use them; the remainingEC2Extendednetwork-interface / ACL / volume actions are still required.
Grant 2 · Runtime roles
These roles are created for you by the deployment (using Grant 1) and are assumed by the cluster and its workloads at runtime. You do not hand-craft them; they are enumerated here so your team can review the standing access that will exist in the account after install.EKS service roles (AWS-managed policies)
The cluster and its worker nodes use roles built entirely from AWS’s own managed policies — AWS’s least-privilege definitions for these service roles. No custom permissions are involved.AmazonEKSServicePolicy to the
cluster role. On EKS 1.24+ its permissions are folded into
AmazonEKSClusterPolicy, so it is optional on new clusters.min/max/desired config). There are no
self-managed EC2 instances or Auto Scaling groups to operate. As a result the
Cluster Autoscaler and Karpenter roles below, and the “auto-provisioning”
node-role variant above (AmazonSSMManagedInstanceCore), are not enabled by
default — Karpenter is a future release; they are documented here for
deployments that opt into node auto-provisioning.Application & add-on roles (custom, least-privilege)
Each of these is a role with a small inline policy, scoped to the specific resource LILT owns and assumed by one named Kubernetes workload via short-lived federated credentials (EKS IRSA or Pod Identity). None of them can access resources outside the ones LILT created.LILT application — its own S3 bucket, SQS queue, KMS key
LILT application — its own S3 bucket, SQS queue, KMS key
AllowSQSAccess
statement shown above is always present in the app workload role — it is not
optional. The queue ARN is surfaced as the queue_arn output.ExternalDNS — the LILT DNS zone only
ExternalDNS — the LILT DNS zone only
cert-manager — TLS certificate issuance (DNS-01)
cert-manager — TLS certificate issuance (DNS-01)
Cluster Autoscaler — scaling the LILT cluster's node groups
Cluster Autoscaler — scaling the LILT cluster's node groups
Node auto-provisioning (Karpenter) — cluster-tagged EC2 only — future release, disabled by default
Node auto-provisioning (Karpenter) — cluster-tagged EC2 only — future release, disabled by default
ec2:CreateFleet / RunInstances / CreateLaunchTemplate / CreateTags /
TerminateInstances and the iam:*InstanceProfile actions by the
kubernetes.io/cluster/<CLUSTER_NAME>: owned and karpenter.sh/nodepool
tags — use the current published
Karpenter controller policy
for your Karpenter version.AWS Load Balancer Controller — load balancers it creates
AWS Load Balancer Controller — load balancers it creates
External Secrets Operator (optional) — read LILT-managed secrets
External Secrets Operator (optional) — read LILT-managed secrets
<PREFIX>/* in Secrets Manager. It cannot
read secrets outside that prefix. Assumed by the external-secrets-sa service
account in the external-secrets namespace via EKS Pod Identity.Harbor container registry (optional, future release) — S3 backend access
Harbor container registry (optional, future release) — S3 backend access
harbor-core, harbor-registry, harbor-jobservice)
in the harbor namespace — all three assume the same role.AmazonEC2ContainerRegistryReadOnly.ECR private registry (optional) — push/pull access for app workloads
ECR private registry (optional) — push/pull access for app workloads
app_workload role push and pull permissions
scoped to that repository. Node groups always receive
AmazonEC2ContainerRegistryReadOnly (pull-only) regardless of this toggle.Bastion host (optional) — describe the cluster + Session Manager
Bastion host (optional) — describe the cluster + Session Manager
aws eks describe-cluster /
update-kubeconfig against this one cluster and reach the private bastion
over AWS Systems Manager Session Manager (no public IP or open SSH port
required). It attaches the AWS-managed AmazonSSMManagedInstanceCore policy
plus the inline statement below.Cluster service account & Pod Identity wiring
The runtime roles above are not attached to pods directly. Each is bound to a named Kubernetes service account (SA), and an EKS Pod Identity association maps that SA to the IAM role. This section describes how LILT’s terraform sets that up so your team can see exactly which in-cluster identity receives which AWS permissions.The LILT application service accounts
LILT’s application runs under one or more service accounts in thelilt
namespace. The module variable app_workload_service_accounts (default:
["app-workload-sa"]) controls which service accounts receive Pod Identity
associations to the app workload IAM role. Each named SA gets its own
aws_eks_pod_identity_association. The service accounts themselves are created
by your cluster’s application deployment (e.g. Helm chart), not by this
Terraform module — the Pod Identity association is the AWS-side binding only.
How the pieces fit together
Three objects have to agree for a pod to receive AWS permissions:- The Kubernetes service account — the identity the pod runs as
(
namespace+name). - The IAM role — carries the least-privilege policy (e.g. the S3 + KMS
permissions) and has a trust policy that allows
pods.eks.amazonaws.comto assume it (sts:AssumeRole+sts:TagSession). This is what makes it a Pod Identity role (sts:AssumeRole+sts:TagSessionfrompods.eks.amazonaws.com). Under IRSA (iam_auth_method = "irsa"), the trust policy usessts:AssumeRoleWithWebIdentitywith a service-account-scoped condition against the cluster OIDC provider instead. - The Pod Identity association (Pod Identity mode only) — the AWS-side record that says “SA namespace/name on this cluster may assume this role.” Under IRSA, the Kubernetes SA carries an
eks.amazonaws.com/role-arnannotation in place of this association.
Service-account-to-role map
Role/RoleBindings for the application and Argo Workflows components). That
RBAC governs access to the Kubernetes API inside the cluster and is entirely
separate from the AWS IAM permissions granted through the Pod Identity
association described here — the same SA simply serves both roles.How access is brokered
No component uses a long-lived IAM access key. Each principal obtains short-lived credentials via a federated trust:iam_auth_method:pod_identity(default, recommended) — all workload roles use EKS Pod Identity. No OIDC provider is provisioned. Every role trustspods.eks.amazonaws.comviasts:AssumeRole+sts:TagSession. Pod Identity associations wire each Kubernetes service account to its role on the AWS side.irsa(backward compat) — an OIDC identity provider is provisioned for the cluster, and each role usessts:AssumeRoleWithWebIdentitywith a service-account-scoped condition. Use this for environments already running IRSA that are not yet ready to migrate.
Helm chart sourcing for constrained environments
By default, LILT’s deployment pulls community Helm charts from their upstream HTTPS repositories (Artifact Hub,kubernetes-sigs, aws.github.io, etc.).
In environments with restricted egress, two variables let you redirect chart
sources without changing any other module configuration.
Option A — OCI registry mirror (recommended)
Mirror all charts to a private OCI registry (e.g. Amazon ECR) and point the module at the base URL. All add-ons pull from that registry; no public internet access is required. The ECR VPC endpoint is included when VPC endpoints are enabled.Option B — per-chart OCI override
Override individual charts while leaving others at their defaults (or at thehelm_chart_registry base). Useful when only a subset of charts has been
mirrored.
cluster-autoscaler, metrics-server, prometheus-node-exporter,
external-dns, cert-manager, kube-state-metrics, datadog-operator,
aws-load-balancer-controller, external-secrets.
Option C — break-the-glass (local .tgz, no network)
For fully isolated environments where even ECR is unreachable. Pre-fetch chart
tarballs onto the machine running terraform apply, then reference them by
absolute path. Setting repository = null tells the Helm provider to skip all
registry lookups.

