Skip to content

Commit ba0acfd

Browse files
authored
Merge pull request #45 from NHSDigital/feature/eja-eli-201-configure-vpc
Feature/eja-eli-201-configure-vpc
2 parents 4d5fa19 + f4e6d3e commit ba0acfd

File tree

19 files changed

+690
-0
lines changed

19 files changed

+690
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Networking stack
2+
3+
The networking stack contains the networking resources that are securing the Eligibility Signposting API application resources.
4+
5+
The stack is documented on [this Confluence page](https://nhsd-confluence.digital.nhs.uk/spaces/Vacc/pages/1054575846/VPC+structure)
6+
7+
## Traffic Flow Explanation
8+
9+
### Public HTTPS Request Flow
10+
11+
* External client makes HTTPS request → Internet Gateway
12+
* Request routes to Load Balancer or API Gateway in public subnet
13+
* Request forwards to Lambda (or other application) in private subnet
14+
* Lambda processes the request and returns response
15+
* Response returns to client through the same path
16+
17+
### Outbound Internet Access
18+
19+
* Lambda functions in private subnets can make outbound internet calls via NAT Gateways
20+
* This allows Lambda to call external APIs, download packages, etc.
21+
* No direct inbound access to Lambda from the internet
22+
23+
### Internal-Only Traffic
24+
25+
* Lambda functions access AWS services via VPC Endpoints:
26+
* Gateway Endpoints: S3, DynamoDB
27+
* Interface Endpoints: KMS, CloudWatch, SSM, Secrets Manager, Lambda, STS, SQS
28+
* All traffic between Lambda and AWS services stays within the AWS network
29+
* No internet transit required for AWS service access
30+
31+
### Security Controls
32+
33+
#### Network ACLs
34+
35+
Public subnets: Allow HTTP(80), HTTPS(443), ephemeral ports
36+
Private subnets: Allow VPC traffic and responses to outbound requests
37+
38+
#### Security Groups
39+
40+
Default security group: Deny all
41+
VPC Endpoint security group: Allow HTTPS(443) from within VPC
42+
43+
#### Route Tables
44+
45+
Public subnets: Route to Internet Gateway for external access
46+
Private subnets: Route to NAT Gateways for outbound-only access
47+
48+
## Deployment to AWS Development Environment
49+
50+
This stack should only ever be deployed once per account (e.g. the use of Terraform workspaces is explicitly not recommended beyond specifying `dev` as the environment).
51+
52+
Deployment to the Development environment is done through use of `make` commands
53+
54+
### Initialize Terraform and Plan
55+
56+
Run the following command to initialize Terraform and generate a plan. Replace `<env>` with the target environment:
57+
58+
```bash
59+
make terraform env=dev stack=networking tf-command=init workspace=<env>
60+
```
61+
62+
then
63+
64+
```bash
65+
make terraform env=dev stack=networking tf-command=plan workspace=<env>
66+
```
67+
68+
### 1.4 Apply Terraform Changes
69+
70+
Deploy the Terraform configuration using the following command:
71+
72+
```bash
73+
make terraform env=dev stack=networking tf-command=apply workspace=<env>
74+
```
75+
76+
## Release Deployment to AWS (Int, Ref and Prod)
77+
78+
Deployment to Int, Ref and Prod, as well as running the automated tests can be done via GitHub actions, when they are developed.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
bucket = "eligibility-signposting-api-dev-tfstate"
2+
key = "networking.tfstate"
3+
region = "eu-west-2"
4+
encrypt = true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
data "aws_caller_identity" "current" {}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../_shared/default_variables.tf
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
resource "aws_internet_gateway" "vpc_external_access" {
2+
vpc_id = aws_vpc.main.id
3+
tags = {
4+
Name = "internet-gateway",
5+
Stack = local.stack_name
6+
}
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
locals {
2+
any_ip_cidr = "0.0.0.0/0"
3+
vpc_cidr_block = "10.0.0.0/16"
4+
public_subnet_1_cidr = "10.0.3.0/24"
5+
public_subnet_2_cidr = "10.0.4.0/24"
6+
public_subnet_3_cidr = "10.0.5.0/24"
7+
private_subnet_1_cidr = "10.0.6.0/24"
8+
private_subnet_2_cidr = "10.0.7.0/24"
9+
private_subnet_3_cidr = "10.0.8.0/24"
10+
availability_zone_1 = "eu-west-2a"
11+
availability_zone_2 = "eu-west-2b"
12+
availability_zone_3 = "eu-west-2c"
13+
default_port = 443
14+
15+
region = "eu-west-2"
16+
stack_name = "Networking"
17+
18+
# VPC Interface Endpoints
19+
vpc_interface_endpoints = {
20+
kms = "com.amazonaws.${local.region}.kms"
21+
cloudwatch-logs = "com.amazonaws.${local.region}.logs"
22+
ssm = "com.amazonaws.${local.region}.ssm"
23+
secrets-manager = "com.amazonaws.${local.region}.secretsmanager"
24+
lambda = "com.amazonaws.${local.region}.lambda"
25+
sts = "com.amazonaws.${local.region}.sts"
26+
sqs = "com.amazonaws.${local.region}.sqs"
27+
}
28+
29+
# VPC Gateway Endpoints
30+
vpc_gateway_endpoints = {
31+
dynamodb = "com.amazonaws.${local.region}.dynamodb"
32+
s3 = "com.amazonaws.${local.region}.s3"
33+
}
34+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Network ACL for Private Subnets
2+
resource "aws_network_acl" "private" {
3+
vpc_id = aws_vpc.main.id
4+
subnet_ids = [
5+
aws_subnet.private_1.id,
6+
aws_subnet.private_2.id,
7+
aws_subnet.private_3.id
8+
]
9+
10+
# Allow all outbound traffic from private subnets
11+
egress {
12+
rule_no = 100
13+
action = "allow"
14+
cidr_block = local.vpc_cidr_block
15+
protocol = -1
16+
from_port = 0
17+
to_port = 0
18+
}
19+
20+
# Allow inbound traffic from within the VPC
21+
ingress {
22+
rule_no = 100
23+
action = "allow"
24+
cidr_block = local.vpc_cidr_block
25+
protocol = -1
26+
from_port = 0
27+
to_port = 0
28+
}
29+
30+
# Allow responses to outbound requests (ephemeral ports)
31+
ingress {
32+
rule_no = 200
33+
action = "allow"
34+
cidr_block = "0.0.0.0/0"
35+
protocol = "tcp"
36+
from_port = 1024
37+
to_port = 65535
38+
}
39+
40+
tags = {
41+
Name = "private-nacl",
42+
Stack = local.stack_name
43+
}
44+
}
45+
46+
# Network ACL for Public Subnets
47+
resource "aws_network_acl" "public" {
48+
vpc_id = aws_vpc.main.id
49+
subnet_ids = [
50+
aws_subnet.public_1.id,
51+
aws_subnet.public_2.id,
52+
aws_subnet.public_3.id
53+
]
54+
55+
# Allow all outbound traffic from public subnets
56+
egress {
57+
rule_no = 100
58+
action = "allow"
59+
cidr_block = "0.0.0.0/0"
60+
protocol = -1
61+
from_port = 0
62+
to_port = 0
63+
}
64+
65+
# Allow inbound HTTP
66+
ingress {
67+
rule_no = 100
68+
action = "allow"
69+
cidr_block = "0.0.0.0/0"
70+
protocol = "tcp"
71+
from_port = 80
72+
to_port = 80
73+
}
74+
75+
# Allow inbound HTTPS
76+
ingress {
77+
rule_no = 110
78+
action = "allow"
79+
cidr_block = "0.0.0.0/0"
80+
protocol = "tcp"
81+
from_port = 443
82+
to_port = 443
83+
}
84+
85+
# Allow responses to outbound requests (ephemeral ports)
86+
ingress {
87+
rule_no = 120
88+
action = "allow"
89+
cidr_block = "0.0.0.0/0"
90+
protocol = "tcp"
91+
from_port = 1024
92+
to_port = 65535
93+
}
94+
95+
# Allow inbound VPC traffic
96+
ingress {
97+
rule_no = 130
98+
action = "allow"
99+
cidr_block = local.vpc_cidr_block
100+
protocol = -1
101+
from_port = 0
102+
to_port = 0
103+
}
104+
105+
tags = {
106+
Name = "public-nacl",
107+
Stack = local.stack_name
108+
}
109+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
provider "aws" {
2+
region = "eu-west-2"
3+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Public Route Tables
2+
resource "aws_route_table" "public_1" {
3+
vpc_id = aws_vpc.main.id
4+
tags = {
5+
Name = "public-route-1",
6+
Stack = local.stack_name
7+
}
8+
}
9+
10+
resource "aws_route_table" "public_2" {
11+
vpc_id = aws_vpc.main.id
12+
tags = {
13+
Name = "public-route-2",
14+
Stack = local.stack_name
15+
}
16+
}
17+
18+
resource "aws_route_table" "public_3" {
19+
vpc_id = aws_vpc.main.id
20+
tags = {
21+
Name = "public-route-3",
22+
Stack = local.stack_name
23+
}
24+
}
25+
26+
# Associate Public Route Tables with Public Subnets
27+
resource "aws_route_table_association" "public_1" {
28+
subnet_id = aws_subnet.public_1.id
29+
route_table_id = aws_route_table.public_1.id
30+
}
31+
32+
resource "aws_route_table_association" "public_2" {
33+
subnet_id = aws_subnet.public_2.id
34+
route_table_id = aws_route_table.public_2.id
35+
}
36+
37+
resource "aws_route_table_association" "public_3" {
38+
subnet_id = aws_subnet.public_3.id
39+
route_table_id = aws_route_table.public_3.id
40+
}
41+
42+
# Private Route Tables
43+
resource "aws_route_table" "private_1" {
44+
vpc_id = aws_vpc.main.id
45+
tags = {
46+
Name = "private-route-1",
47+
Stack = local.stack_name
48+
}
49+
}
50+
51+
resource "aws_route_table" "private_2" {
52+
vpc_id = aws_vpc.main.id
53+
tags = {
54+
Name = "private-route-2",
55+
Stack = local.stack_name
56+
}
57+
}
58+
59+
resource "aws_route_table" "private_3" {
60+
vpc_id = aws_vpc.main.id
61+
tags = {
62+
Name = "private-route-3",
63+
Stack = local.stack_name
64+
}
65+
}
66+
67+
# Associate Private Route Tables with Private Subnets
68+
resource "aws_route_table_association" "private_association_1" {
69+
subnet_id = aws_subnet.private_1.id
70+
route_table_id = aws_route_table.private_1.id
71+
}
72+
73+
resource "aws_route_table_association" "private_association_2" {
74+
subnet_id = aws_subnet.private_2.id
75+
route_table_id = aws_route_table.private_2.id
76+
}
77+
78+
resource "aws_route_table_association" "private_association_3" {
79+
subnet_id = aws_subnet.private_3.id
80+
route_table_id = aws_route_table.private_3.id
81+
}
82+
83+
# Egress Internet Access
84+
resource "aws_route" "public_internet_access" {
85+
route_table_id = aws_route_table.public_1.id
86+
destination_cidr_block = local.any_ip_cidr
87+
gateway_id = aws_internet_gateway.vpc_external_access.id
88+
}
89+
90+
resource "aws_route" "public_internet_access_2" {
91+
route_table_id = aws_route_table.public_2.id
92+
destination_cidr_block = local.any_ip_cidr
93+
gateway_id = aws_internet_gateway.vpc_external_access.id
94+
}
95+
96+
resource "aws_route" "public_internet_access_3" {
97+
route_table_id = aws_route_table.public_3.id
98+
destination_cidr_block = local.any_ip_cidr
99+
gateway_id = aws_internet_gateway.vpc_external_access.id
100+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../_shared/locals.tf

0 commit comments

Comments
 (0)