Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions cloudformation/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,39 @@
# CloudFormation templates
# CloudFormation — Socket Registry Firewall on AWS (EKS)

AWS CloudFormation deployment templates for the Socket Registry Firewall.
> **Status: DRAFT, untested.** These templates are a work in progress and have not been deployed yet. Validate in a non-production account before relying on them.

Status: in progress. An EKS template for the Vercel internal deployment is being scoped
(firewall + Redis, DNS-override routing). Pending requirements confirmation before authoring.
## Background

Reference CloudFormation for deploying the Socket Registry Firewall on **AWS EKS** (firewall + Redis, DNS-override routing, self-signed certs, fail-open). This is the EKS companion to the existing per-platform deployment templates (ECS Fargate, GCP Cloud Run, Azure Container Apps).

On EKS the firewall is the **Helm chart** (`../helm`), which already supports DNS-override, self-signed CA generation, and Redis/TLS — so most of the work is standing up the AWS infrastructure and installing the chart, not re-implementing the workload.

## Layout

| File | Purpose |
|------|---------|
| `eks-cluster.yaml` | **Greenfield wrapper** — VPC + EKS cluster + node group + OIDC provider. Skip if you already run a cluster. |
| `firewall-eks.yaml` | **Shared base** — ElastiCache Redis + Socket token (Secrets Manager) + IRSA role; emits the `helm upgrade --install` command. |
| `values/dns-override.values.yaml` | Example Helm values (DNS-override + Redis + self-signed certs). |

## Cases

- **Greenfield (no cluster):** deploy `eks-cluster.yaml`, then `firewall-eks.yaml` (wire its outputs in), then run the emitted Helm command.
- **Existing cluster:** deploy `firewall-eks.yaml` only (pass your existing `ClusterName` / `VpcId` / subnets / cluster security group / OIDC), then Helm.

The base is identical in both cases — the wrapper only adds the cluster/VPC layer.

## Helm install: handoff vs one-shot

The base template currently takes the **handoff** approach: CloudFormation provisions the infrastructure and outputs the exact `helm upgrade --install` command to run. The alternative is **one-shot**, where the stack runs Helm itself via a CodeBuild-backed custom resource (CodeBuild installs helm/kubectl, runs `aws eks update-kubeconfig`, then `helm upgrade --install`; a small Lambda signals CloudFormation). One-shot is a single-deploy experience but adds a moving part and requires the CodeBuild role be added to the cluster's EKS access entries.

## Config model

On EKS the Helm chart renders the firewall config into a **ConfigMap**. The stack only injects two install-time values: the ElastiCache endpoint (`redis.host`) and the Socket token (`socket.apiToken`, read from Secrets Manager).

## Known DRAFT caveats

- **Untested** — no template here has been deployed yet.
- `eks-cluster.yaml`'s OIDC `ThumbprintList` is a placeholder — confirm it for your issuer, or associate the provider with `eksctl utils associate-iam-oidc-provider`.
- ElastiCache transit encryption assumes the firewall image trusts Amazon's CA for `redis.ssl`; if not, mount the CA or set `redis.sslVerify: false`.
- The example values file enables npm + pypi only — adjust to your ecosystems.
205 changes: 205 additions & 0 deletions cloudformation/eks-cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: >
DRAFT — Greenfield EKS wrapper for the Socket Registry Firewall. Stands up a
VPC (2 public + 2 private subnets, NAT), an EKS cluster, a managed node group,
and the IAM OIDC provider for IRSA. Outputs feed firewall-eks.yaml.
Skip this template entirely if you already run an EKS cluster.
UNTESTED DRAFT — validate in a non-production account (see README.md).

Parameters:
ClusterName:
Type: String
Default: socket-firewall
KubernetesVersion:
Type: String
Default: "1.30"
NodeInstanceType:
Type: String
Default: t3.large
DesiredNodes:
Type: Number
Default: 2
MaxNodes:
Type: Number
Default: 6
VpcCidr:
Type: String
Default: 10.0.0.0/16

Mappings:
Subnets:
Public0: { Cidr: 10.0.0.0/20 }
Public1: { Cidr: 10.0.16.0/20 }
Private0: { Cidr: 10.0.32.0/20 }
Private1: { Cidr: 10.0.48.0/20 }

Resources:
# --- VPC ------------------------------------------------------------------
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsHostnames: true
EnableDnsSupport: true
Tags: [{ Key: Name, Value: !Sub "${ClusterName}-vpc" }]

InternetGateway:
Type: AWS::EC2::InternetGateway
VpcGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties: { VpcId: !Ref Vpc, InternetGatewayId: !Ref InternetGateway }

PublicSubnet0:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !FindInMap [Subnets, Public0, Cidr]
AvailabilityZone: !Select [0, !GetAZs ""]
MapPublicIpOnLaunch: true
Tags:
- { Key: Name, Value: !Sub "${ClusterName}-public-0" }
- { Key: kubernetes.io/role/elb, Value: "1" }
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !FindInMap [Subnets, Public1, Cidr]
AvailabilityZone: !Select [1, !GetAZs ""]
MapPublicIpOnLaunch: true
Tags:
- { Key: Name, Value: !Sub "${ClusterName}-public-1" }
- { Key: kubernetes.io/role/elb, Value: "1" }
PrivateSubnet0:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !FindInMap [Subnets, Private0, Cidr]
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- { Key: Name, Value: !Sub "${ClusterName}-private-0" }
- { Key: kubernetes.io/role/internal-elb, Value: "1" }
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !FindInMap [Subnets, Private1, Cidr]
AvailabilityZone: !Select [1, !GetAZs ""]
Tags:
- { Key: Name, Value: !Sub "${ClusterName}-private-1" }
- { Key: kubernetes.io/role/internal-elb, Value: "1" }

NatEip:
Type: AWS::EC2::EIP
Properties: { Domain: vpc }
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatEip.AllocationId
SubnetId: !Ref PublicSubnet0

PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties: { VpcId: !Ref Vpc }
PublicRoute:
Type: AWS::EC2::Route
DependsOn: VpcGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicAssoc0:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties: { RouteTableId: !Ref PublicRouteTable, SubnetId: !Ref PublicSubnet0 }
PublicAssoc1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties: { RouteTableId: !Ref PublicRouteTable, SubnetId: !Ref PublicSubnet1 }

PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties: { VpcId: !Ref Vpc }
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateAssoc0:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties: { RouteTableId: !Ref PrivateRouteTable, SubnetId: !Ref PrivateSubnet0 }
PrivateAssoc1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties: { RouteTableId: !Ref PrivateRouteTable, SubnetId: !Ref PrivateSubnet1 }

# --- EKS cluster ----------------------------------------------------------
ClusterRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: eks.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonEKSClusterPolicy

Cluster:
Type: AWS::EKS::Cluster
Properties:
Name: !Ref ClusterName
Version: !Ref KubernetesVersion
RoleArn: !GetAtt ClusterRole.Arn
ResourcesVpcConfig:
SubnetIds: [!Ref PrivateSubnet0, !Ref PrivateSubnet1, !Ref PublicSubnet0, !Ref PublicSubnet1]
EndpointPublicAccess: true
EndpointPrivateAccess: true

# IAM OIDC provider for IRSA is intentionally NOT created here — hardcoding an
# OIDC thumbprint is brittle. After the cluster is up, associate the provider
# (which fetches the correct thumbprint automatically) with:
# eksctl utils associate-iam-oidc-provider --cluster <ClusterName> --approve
# Then pass that provider's ARN and the OIDCProviderUrl output into firewall-eks.yaml.

# --- Managed node group ---------------------------------------------------
NodeRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal: { Service: ec2.amazonaws.com }
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly

NodeGroup:
Type: AWS::EKS::Nodegroup
Properties:
ClusterName: !Ref Cluster
NodeRole: !GetAtt NodeRole.Arn
Subnets: [!Ref PrivateSubnet0, !Ref PrivateSubnet1]
InstanceTypes: [!Ref NodeInstanceType]
ScalingConfig:
MinSize: !Ref DesiredNodes
DesiredSize: !Ref DesiredNodes
MaxSize: !Ref MaxNodes

Outputs:
ClusterName:
Value: !Ref Cluster
VpcId:
Value: !Ref Vpc
PrivateSubnetIds:
Value: !Join [",", [!Ref PrivateSubnet0, !Ref PrivateSubnet1]]
ClusterSecurityGroupId:
Description: Cluster security group (use as ClusterSecurityGroupId in firewall-eks.yaml).
Value: !GetAtt Cluster.ClusterSecurityGroupId
OIDCProviderUrl:
Description: >
Cluster OIDC issuer URL. Associate the IAM OIDC provider via
`eksctl utils associate-iam-oidc-provider`, then pass that provider's ARN
and this URL (without the https:// prefix) into firewall-eks.yaml.
Value: !GetAtt Cluster.OpenIdConnectIssuerUrl
Loading