Skip to content

Commit 93f8c66

Browse files
authored
Merge pull request #69 from zama-ai/ghislain/feat/support-additional-labels-selector
feat: support additional labels selector
2 parents 33ee63f + 3ab784e commit 93f8c66

File tree

7 files changed

+242
-8
lines changed

7 files changed

+242
-8
lines changed

modules/vpc-endpoint-consumer/README.md

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,86 @@
11
# VPC Endpoint Consumer Module
22

3+
This module creates VPC interface endpoints to connect to partner MPC services and optionally creates Kubernetes services for easy consumption within an EKS cluster.
4+
5+
## Features
6+
7+
- **VPC Interface Endpoints**: Create VPC endpoints to privately connect to partner services
8+
- **Kubernetes Service Integration**: Automatically create Kubernetes ExternalName services pointing to VPC endpoints
9+
- **Security Group Management**: Option to create a security group with default ingress rules or use existing security groups
10+
- **Custom DNS**: Optional Route53 private hosted zone records for custom DNS names
11+
- **S3 Bucket Sync**: Sync public buckets between partners for data exchange
12+
13+
## Usage
14+
15+
### Basic Usage with Auto-Created Security Group
16+
17+
```terraform
18+
module "vpc_endpoint_consumer" {
19+
source = "./modules/vpc-endpoint-consumer"
20+
21+
cluster_name = "my-eks-cluster"
22+
23+
# Create a security group with default ingress rules
24+
create_security_group = true
25+
security_group_name = "mpc-vpc-endpoint-sg"
26+
27+
# Default ingress rules allow traffic on gRPC (50100), peer (50001), and metrics (9646) ports
28+
# You can customize these rules using security_group_ingress_rules variable
29+
30+
party_services = [
31+
{
32+
name = "partner-kms-core"
33+
region = "us-east-1"
34+
party_id = "partner1"
35+
vpc_endpoint_service_name = "com.amazonaws.vpce.us-east-1.vpce-svc-xxxxx"
36+
}
37+
]
38+
}
39+
```
40+
41+
### Using Existing Security Groups
42+
43+
```terraform
44+
module "vpc_endpoint_consumer" {
45+
source = "./modules/vpc-endpoint-consumer"
46+
47+
vpc_id = "vpc-xxxxx"
48+
subnet_ids = ["subnet-xxxxx", "subnet-yyyyy"]
49+
security_group_ids = ["sg-xxxxx"]
50+
51+
# Don't create a new security group (default behavior)
52+
create_security_group = false
53+
54+
party_services = [
55+
{
56+
name = "partner-kms-core"
57+
region = "us-east-1"
58+
party_id = "partner1"
59+
vpc_endpoint_service_name = "com.amazonaws.vpce.us-east-1.vpce-svc-xxxxx"
60+
}
61+
]
62+
}
63+
```
64+
65+
## Security Group Configuration
66+
67+
When `create_security_group = true`, the module automatically creates ingress rules based on the `default_mpc_ports` configuration:
68+
69+
- **gRPC Port (50100)**: Created only if `enable_grpc_port = true` (default)
70+
- **Peer Port (50001)**: Always created
71+
- **Metrics Port (9646)**: Always created
72+
73+
The module automatically adds a default egress rule that allows all outbound traffic (0.0.0.0/0).
74+
75+
### Customizing Ingress Rules
76+
77+
You can customize who can access the VPC endpoints using:
78+
79+
- **`security_group_ingress_cidr_blocks`**: List of CIDR blocks to allow (default: `["0.0.0.0/0"]`)
80+
- **`security_group_ingress_source_sg_id`**: Source security group ID (takes precedence over CIDR blocks)
81+
82+
The ports used for ingress rules are automatically derived from `default_mpc_ports`, ensuring consistency between your Kubernetes services and security group rules.
83+
384
<!-- BEGIN_TF_DOCS -->
485
## Requirements
586

@@ -27,7 +108,10 @@ No modules.
27108
| Name | Type |
28109
|------|------|
29110
| [aws_route53_record.partner_dns](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
111+
| [aws_security_group.vpc_endpoint](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
30112
| [aws_vpc_endpoint.party_interface_endpoints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource |
113+
| [aws_vpc_security_group_egress_rule.vpc_endpoint_default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_egress_rule) | resource |
114+
| [aws_vpc_security_group_ingress_rule.vpc_endpoint](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |
31115
| [kubernetes_namespace.partner_namespace](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource |
32116
| [kubernetes_service.party_services](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service) | resource |
33117
| [null_resource.sync_s3_bucket](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
@@ -43,18 +127,23 @@ No modules.
43127
| <a name="input_cluster_name"></a> [cluster\_name](#input\_cluster\_name) | Name of the EKS cluster to lookup VPC, subnet, and security group details (Mode 1). If provided, vpc\_id, subnet\_ids, and security\_group\_ids will be ignored. | `string` | `null` | no |
44128
| <a name="input_create_custom_dns_records"></a> [create\_custom\_dns\_records](#input\_create\_custom\_dns\_records) | Whether to create custom DNS records for the VPC interface endpoints | `bool` | `false` | no |
45129
| <a name="input_create_namespace"></a> [create\_namespace](#input\_create\_namespace) | Whether to create the namespace if it doesn't exist | `bool` | `false` | no |
130+
| <a name="input_create_security_group"></a> [create\_security\_group](#input\_create\_security\_group) | Whether to create a security group for VPC endpoints with default ingress rules | `bool` | `true` | no |
46131
| <a name="input_default_mpc_ports"></a> [default\_mpc\_ports](#input\_default\_mpc\_ports) | Default port configurations for MPC services. These can be overridden per service in party\_services configuration. | <pre>object({<br/> grpc = object({<br/> name = string<br/> port = number<br/> target_port = number<br/> protocol = string<br/> })<br/> peer = object({<br/> name = string<br/> port = number<br/> target_port = number<br/> protocol = string<br/> })<br/> metrics = object({<br/> name = string<br/> port = number<br/> target_port = number<br/> protocol = string<br/> })<br/> })</pre> | <pre>{<br/> "grpc": {<br/> "name": "grpc",<br/> "port": 50100,<br/> "protocol": "TCP",<br/> "target_port": 50100<br/> },<br/> "metrics": {<br/> "name": "metrics",<br/> "port": 9646,<br/> "protocol": "TCP",<br/> "target_port": 9646<br/> },<br/> "peer": {<br/> "name": "peer",<br/> "port": 50001,<br/> "protocol": "TCP",<br/> "target_port": 50001<br/> }<br/>}</pre> | no |
47132
| <a name="input_dns_domain"></a> [dns\_domain](#input\_dns\_domain) | DNS domain for custom DNS records | `string` | `"mpc-partners.local"` | no |
48133
| <a name="input_enable_grpc_port"></a> [enable\_grpc\_port](#input\_enable\_grpc\_port) | Whether to enable and expose the gRPC port in the load balancer service | `bool` | `true` | no |
49134
| <a name="input_endpoint_create_timeout"></a> [endpoint\_create\_timeout](#input\_endpoint\_create\_timeout) | Timeout for creating VPC interface endpoints | `string` | `"10m"` | no |
50135
| <a name="input_endpoint_delete_timeout"></a> [endpoint\_delete\_timeout](#input\_endpoint\_delete\_timeout) | Timeout for deleting VPC interface endpoints | `string` | `"10m"` | no |
51136
| <a name="input_name_prefix"></a> [name\_prefix](#input\_name\_prefix) | Prefix for naming VPC interface endpoint resources | `string` | `"mpc-partner"` | no |
52137
| <a name="input_namespace"></a> [namespace](#input\_namespace) | Kubernetes namespace where partner services will be created | `string` | `"kms-decentralized"` | no |
53-
| <a name="input_party_services"></a> [party\_services](#input\_party\_services) | List of partner MPC services to connect to via VPC interface endpoints | <pre>list(object({<br/> name = string<br/> region = string<br/> party_id = string<br/> account_id = optional(string, null)<br/> partner_name = optional(string, null)<br/> vpc_endpoint_service_name = string<br/> public_bucket_url = optional(string, null)<br/> ports = optional(list(object({<br/> name = string<br/> port = number<br/> target_port = number<br/> protocol = string<br/> })), null)<br/> availability_zones = optional(list(string), null)<br/> create_kube_service = optional(bool, true)<br/> kube_service_config = optional(object({<br/> additional_annotations = optional(map(string), {})<br/> labels = optional(map(string), {})<br/> session_affinity = optional(string, "None")<br/> }), {})<br/> }))</pre> | n/a | yes |
138+
| <a name="input_party_services"></a> [party\_services](#input\_party\_services) | List of partner MPC services to connect to via VPC interface endpoints | <pre>list(object({<br/> name = string<br/> region = string<br/> party_id = string<br/> account_id = optional(string, null)<br/> partner_name = optional(string, null)<br/> vpc_endpoint_service_name = string<br/> enable_consumer_sync = optional(bool, true)<br/> public_bucket_url = optional(string, null)<br/> ports = optional(list(object({<br/> name = string<br/> port = number<br/> target_port = number<br/> protocol = string<br/> })), null)<br/> availability_zones = optional(list(string), null)<br/> create_kube_service = optional(bool, true)<br/> kube_service_config = optional(object({<br/> additional_annotations = optional(map(string), {})<br/> labels = optional(map(string), {})<br/> session_affinity = optional(string, "None")<br/> }), {})<br/> }))</pre> | n/a | yes |
54139
| <a name="input_private_dns_enabled"></a> [private\_dns\_enabled](#input\_private\_dns\_enabled) | Whether to enable private DNS for the VPC interface endpoints | `bool` | `false` | no |
55140
| <a name="input_private_zone_id"></a> [private\_zone\_id](#input\_private\_zone\_id) | Route53 private hosted zone ID for custom DNS records | `string` | `""` | no |
56141
| <a name="input_route_table_ids"></a> [route\_table\_ids](#input\_route\_table\_ids) | List of route table IDs to associate with the VPC interface endpoints | `list(string)` | `[]` | no |
57-
| <a name="input_security_group_ids"></a> [security\_group\_ids](#input\_security\_group\_ids) | List of security group IDs to associate with the VPC interface endpoints (Mode 2). Required if cluster\_name is not provided. | `list(string)` | `null` | no |
142+
| <a name="input_security_group_description"></a> [security\_group\_description](#input\_security\_group\_description) | Description for the security group | `string` | `"Security group for MPC VPC endpoint consumer"` | no |
143+
| <a name="input_security_group_ids"></a> [security\_group\_ids](#input\_security\_group\_ids) | List of security group IDs to associate with the VPC interface endpoints (Mode 2). Required if cluster\_name is not provided and create\_security\_group is false. | `list(string)` | `null` | no |
144+
| <a name="input_security_group_ingress_cidr_blocks"></a> [security\_group\_ingress\_cidr\_blocks](#input\_security\_group\_ingress\_cidr\_blocks) | CIDR blocks to allow ingress traffic from for MPC ports (when create\_security\_group is true) | `list(string)` | <pre>[<br/> "0.0.0.0/0"<br/>]</pre> | no |
145+
| <a name="input_security_group_ingress_source_sg_id"></a> [security\_group\_ingress\_source\_sg\_id](#input\_security\_group\_ingress\_source\_sg\_id) | Source security group ID to allow ingress traffic from for MPC ports (when create\_security\_group is true). If set, this takes precedence over cidr\_blocks. | `string` | `null` | no |
146+
| <a name="input_security_group_name"></a> [security\_group\_name](#input\_security\_group\_name) | Name of the security group to create (if create\_security\_group is true) | `string` | `"mpc-vpc-endpoint-consumer-sg"` | no |
58147
| <a name="input_subnet_ids"></a> [subnet\_ids](#input\_subnet\_ids) | List of subnet IDs where the VPC interface endpoints will be created (Mode 2). Required if cluster\_name is not provided. | `list(string)` | `null` | no |
59148
| <a name="input_sync_public_bucket"></a> [sync\_public\_bucket](#input\_sync\_public\_bucket) | Sync public bucket between partners | <pre>object({<br/> enabled = optional(bool, true)<br/> configmap_name = optional(string, "mpc-party")<br/> })</pre> | <pre>{<br/> "configmap_name": "mpc-party",<br/> "enabled": true<br/>}</pre> | no |
60149
| <a name="input_tags"></a> [tags](#input\_tags) | Tags to apply to VPC interface endpoint resources | `map(string)` | `{}` | no |
@@ -72,6 +161,9 @@ No modules.
72161
| <a name="output_namespace_name"></a> [namespace\_name](#output\_namespace\_name) | Name of the namespace where partner services are deployed |
73162
| <a name="output_partner_connection_endpoints"></a> [partner\_connection\_endpoints](#output\_partner\_connection\_endpoints) | Connection endpoints for applications to use when connecting to partner services |
74163
| <a name="output_partner_service_details"></a> [partner\_service\_details](#output\_partner\_service\_details) | Detailed information about the partner services and their connections |
164+
| <a name="output_security_group_arn"></a> [security\_group\_arn](#output\_security\_group\_arn) | ARN of the created security group (if create\_security\_group is true) |
165+
| <a name="output_security_group_id"></a> [security\_group\_id](#output\_security\_group\_id) | ID of the created security group (if create\_security\_group is true) |
166+
| <a name="output_security_group_name"></a> [security\_group\_name](#output\_security\_group\_name) | Name of the created security group (if create\_security\_group is true) |
75167
| <a name="output_vpc_interface_endpoint_dns_names"></a> [vpc\_interface\_endpoint\_dns\_names](#output\_vpc\_interface\_endpoint\_dns\_names) | DNS names of the created VPC interface endpoints |
76168
| <a name="output_vpc_interface_endpoint_hosted_zone_ids"></a> [vpc\_interface\_endpoint\_hosted\_zone\_ids](#output\_vpc\_interface\_endpoint\_hosted\_zone\_ids) | Hosted zone IDs of the created VPC interface endpoints |
77169
| <a name="output_vpc_interface_endpoint_ids"></a> [vpc\_interface\_endpoint\_ids](#output\_vpc\_interface\_endpoint\_ids) | IDs of the created VPC interface endpoints |

modules/vpc-endpoint-consumer/main.tf

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,66 @@ data "aws_subnet" "cluster_subnets" {
1616
data "aws_region" "current" {
1717
}
1818

19+
# ***************************************
20+
# Security Group
21+
# ***************************************
22+
resource "aws_security_group" "vpc_endpoint" {
23+
count = var.create_security_group ? 1 : 0
24+
25+
name = var.security_group_name
26+
description = var.security_group_description
27+
vpc_id = local.vpc_id
28+
29+
tags = merge(
30+
var.tags,
31+
{
32+
Name = var.security_group_name
33+
"mpc:component" = "vpc-endpoint-consumer"
34+
"mpc:cluster" = local.cluster_name_for_tags
35+
}
36+
)
37+
}
38+
39+
resource "aws_vpc_security_group_ingress_rule" "vpc_endpoint" {
40+
for_each = var.create_security_group ? local.security_group_ingress_rules : {}
41+
42+
security_group_id = aws_security_group.vpc_endpoint[0].id
43+
44+
description = each.value.description
45+
from_port = each.value.from_port
46+
to_port = each.value.to_port
47+
ip_protocol = each.value.protocol
48+
49+
# Use source security group if provided, otherwise use CIDR blocks
50+
cidr_ipv4 = var.security_group_ingress_source_sg_id == null ? var.security_group_ingress_cidr_blocks[0] : null
51+
referenced_security_group_id = var.security_group_ingress_source_sg_id
52+
53+
tags = merge(
54+
var.tags,
55+
{
56+
Name = "${var.security_group_name}-ingress-${each.key}"
57+
}
58+
)
59+
}
60+
61+
# Default egress rule to allow all outbound traffic
62+
resource "aws_vpc_security_group_egress_rule" "vpc_endpoint_default" {
63+
count = var.create_security_group ? 1 : 0
64+
65+
security_group_id = aws_security_group.vpc_endpoint[0].id
66+
67+
description = "Allow all outbound traffic"
68+
ip_protocol = "-1"
69+
cidr_ipv4 = "0.0.0.0/0"
70+
71+
tags = merge(
72+
var.tags,
73+
{
74+
Name = "${var.security_group_name}-egress-default"
75+
}
76+
)
77+
}
78+
1979
# ***************************************
2080
# Local variables
2181
# ***************************************
@@ -25,14 +85,23 @@ locals {
2585
for subnet_id, subnet in data.aws_subnet.cluster_subnets : subnet_id
2686
if subnet.map_public_ip_on_launch == false
2787
]
28-
security_group_ids = var.security_group_ids != null ? var.security_group_ids : [data.aws_eks_cluster.selected[0].vpc_config[0].cluster_security_group_id]
88+
89+
# Security group IDs logic:
90+
# 1. If create_security_group is true, use the created security group
91+
# 2. Otherwise, use provided security_group_ids if available
92+
# 3. Fall back to EKS cluster security group if cluster_name is provided
93+
security_group_ids = var.create_security_group ? [aws_security_group.vpc_endpoint[0].id] : (
94+
var.security_group_ids != null ? var.security_group_ids : [data.aws_eks_cluster.selected[0].vpc_config[0].cluster_security_group_id]
95+
)
2996

3097
# Cluster name for tagging (use provided cluster_name or default)
3198
cluster_name_for_tags = var.cluster_name != null ? var.cluster_name : "mpc-cluster"
3299

33100
# Convert party_services list to map for for_each usage
101+
# VPC endpoints needed for both consumer sync and Kubernetes services
34102
party_services_map = {
35103
for service in var.party_services : service.party_id => service
104+
if service.enable_consumer_sync == true
36105
}
37106

38107
party_services_map_with_s3_bucket = {
@@ -43,7 +112,27 @@ locals {
43112
# Create separate map for services that need Kubernetes services
44113
kube_services_map = {
45114
for service in var.party_services : service.party_id => service
46-
if service.create_kube_service
115+
if service.create_kube_service && service.enable_consumer_sync == true
116+
}
117+
118+
# Build ingress rules from default_mpc_ports
119+
mpc_ports_for_ingress = concat(
120+
var.enable_grpc_port ? [var.default_mpc_ports.grpc] : [],
121+
[
122+
var.default_mpc_ports.peer,
123+
var.default_mpc_ports.metrics
124+
]
125+
)
126+
127+
# Create ingress rules map for security group
128+
security_group_ingress_rules = {
129+
for port_config in local.mpc_ports_for_ingress :
130+
port_config.name => {
131+
description = "Allow ${port_config.name} traffic"
132+
from_port = port_config.port
133+
to_port = port_config.port
134+
protocol = lower(port_config.protocol)
135+
}
47136
}
48137

49138
}
@@ -166,7 +255,7 @@ locals {
166255
}
167256

168257
resource "null_resource" "sync_s3_bucket" {
169-
for_each = local.party_services_map_with_s3_bucket
258+
for_each = var.sync_public_bucket.enabled ? local.party_services_map_with_s3_bucket : {}
170259
provisioner "local-exec" {
171260
interpreter = ["bash", "-c"]
172261
command = <<-EOT

modules/vpc-endpoint-consumer/outputs.tf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
output "security_group_id" {
2+
description = "ID of the created security group (if create_security_group is true)"
3+
value = var.create_security_group ? aws_security_group.vpc_endpoint[0].id : null
4+
}
5+
6+
output "security_group_arn" {
7+
description = "ARN of the created security group (if create_security_group is true)"
8+
value = var.create_security_group ? aws_security_group.vpc_endpoint[0].arn : null
9+
}
10+
11+
output "security_group_name" {
12+
description = "Name of the created security group (if create_security_group is true)"
13+
value = var.create_security_group ? aws_security_group.vpc_endpoint[0].name : null
14+
}
15+
116
output "vpc_interface_endpoint_ids" {
217
description = "IDs of the created VPC interface endpoints"
318
value = [for endpoint in values(aws_vpc_endpoint.party_interface_endpoints) : endpoint.id]

0 commit comments

Comments
 (0)