code-server/terraform/modules/vpn/main.tf
Claude 369f459203
Add AWS Client VPN support for secure private access to code-server
This commit adds comprehensive VPN infrastructure to enable secure,
certificate-based access to code-server deployments. VPN provides an
additional security layer by requiring network-level authentication
before accessing internal resources.

Features:
- AWS Client VPN endpoint with certificate-based authentication
- Split tunnel support (route only VPC traffic through VPN)
- CloudWatch logging for all VPN connections
- Multi-platform client support (Windows, macOS, Linux, iOS, Android)
- Automatic certificate generation and ACM upload
- Client configuration export scripts
- Integration with both EC2 and EKS deployments

New Terraform Module:
- modules/vpn: Complete AWS Client VPN infrastructure
  - VPN endpoint with configurable authentication
  - Network associations for HA across multiple AZs
  - Authorization rules for VPC access
  - Security groups for VPN traffic
  - CloudWatch log groups and streams
  - Support for SAML/federated authentication

Scripts:
- scripts/generate-vpn-certificates.sh: Generate and upload VPN certificates
  - Creates CA, server, and client certificates
  - Automatically uploads to AWS Certificate Manager
  - Outputs certificate ARNs for Terraform configuration
- scripts/export-vpn-config.sh: Export client VPN configuration
  - Downloads VPN config from AWS
  - Embeds client certificates
  - Creates platform-ready .ovpn files

Deployment Updates:
- EC2 and EKS deployments now support optional VPN
- New variables for VPN configuration
- Updated outputs to include VPN endpoint information
- Example configurations with VPN settings

Documentation:
- VPN-SETUP-GUIDE.md: Comprehensive VPN setup guide
  - Certificate generation process
  - Terraform configuration
  - Client setup for all major platforms
  - Testing and troubleshooting
  - Advanced configuration options
  - Cost considerations and optimization

Configuration Options:
- Certificate-based or SAML/SSO authentication
- Split tunnel (recommended) or full tunnel
- UDP (faster) or TCP (more reliable) transport
- Configurable session timeout (8-24 hours)
- Custom DNS servers
- Client login banner
- Multiple authorization rules

Security Features:
- X.509 certificate authentication
- Private subnet associations
- Network-level access control
- Session logging and audit trail
- Support for multi-factor (VPN cert + OAuth2/SAML)

Cost: ~$216/month base + ~$0.40/user/day for active connections
2025-11-15 17:40:23 +00:00

182 lines
5.9 KiB
HCL

# VPN Module for Code-Server
# Creates AWS Client VPN endpoint for secure access to private resources
# CloudWatch Log Group for VPN connection logs
resource "aws_cloudwatch_log_group" "vpn" {
name = "/aws/vpn/${var.name_prefix}"
retention_in_days = var.log_retention_days
tags = var.tags
}
resource "aws_cloudwatch_log_stream" "vpn" {
name = "vpn-connection-logs"
log_group_name = aws_cloudwatch_log_group.vpn.name
}
# Client VPN Endpoint
resource "aws_ec2_client_vpn_endpoint" "main" {
description = "Client VPN endpoint for ${var.name_prefix}"
server_certificate_arn = var.server_certificate_arn
client_cidr_block = var.client_cidr_block
split_tunnel = var.split_tunnel
vpc_id = var.vpc_id
# Authentication using certificate-based or Active Directory
authentication_options {
type = var.authentication_type
root_certificate_chain_arn = var.authentication_type == "certificate-authentication" ? var.client_certificate_arn : null
# For Active Directory authentication
active_directory_id = var.authentication_type == "directory-service-authentication" ? var.active_directory_id : null
}
# Additional authentication option for MFA (optional)
dynamic "authentication_options" {
for_each = var.enable_saml_authentication ? [1] : []
content {
type = "federated-authentication"
saml_provider_arn = var.saml_provider_arn
self_service_saml_provider_arn = var.self_service_saml_provider_arn
}
}
# Connection logging
connection_log_options {
enabled = true
cloudwatch_log_group = aws_cloudwatch_log_group.vpn.name
cloudwatch_log_stream = aws_cloudwatch_log_stream.vpn.name
}
# DNS servers to use
dns_servers = var.dns_servers
# Transport protocol
transport_protocol = var.transport_protocol
# VPN port
vpn_port = var.vpn_port
# Session timeout
session_timeout_hours = var.session_timeout_hours
# Client connect options (for custom authorization)
dynamic "client_connect_options" {
for_each = var.enable_client_connect_handler ? [1] : []
content {
enabled = true
lambda_function_arn = var.client_connect_lambda_arn
}
}
# Client login banner
dynamic "client_login_banner_options" {
for_each = var.client_login_banner != "" ? [1] : []
content {
enabled = true
banner_text = var.client_login_banner
}
}
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-vpn-endpoint"
}
)
}
# Associate VPN endpoint with subnets
resource "aws_ec2_client_vpn_network_association" "main" {
count = length(var.subnet_ids)
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
subnet_id = var.subnet_ids[count.index]
lifecycle {
# The issue why we are ignoring changes is that on the first resource creation, its terraform-aws-client-vpn-endpoint
# This will change on AWS's next apply to the instance ID.
ignore_changes = [subnet_id]
}
}
# Authorization rule to allow access to VPC CIDR
resource "aws_ec2_client_vpn_authorization_rule" "vpc_access" {
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
target_network_cidr = var.vpc_cidr
authorize_all_groups = var.authorize_all_groups
access_group_id = var.authorize_all_groups ? null : var.access_group_id
description = "Allow access to VPC"
}
# Additional authorization rules for specific CIDRs
resource "aws_ec2_client_vpn_authorization_rule" "additional" {
count = length(var.additional_authorization_rules)
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
target_network_cidr = var.additional_authorization_rules[count.index].cidr
authorize_all_groups = var.additional_authorization_rules[count.index].authorize_all_groups
access_group_id = var.additional_authorization_rules[count.index].access_group_id
description = var.additional_authorization_rules[count.index].description
}
# Route to direct traffic to the VPC
resource "aws_ec2_client_vpn_route" "vpc_route" {
count = var.split_tunnel ? 1 : 0
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
destination_cidr_block = var.vpc_cidr
target_vpc_subnet_id = aws_ec2_client_vpn_network_association.main[0].subnet_id
description = "Route to VPC"
depends_on = [aws_ec2_client_vpn_network_association.main]
}
# Additional routes for specific networks
resource "aws_ec2_client_vpn_route" "additional" {
count = length(var.additional_routes)
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
destination_cidr_block = var.additional_routes[count.index].cidr
target_vpc_subnet_id = aws_ec2_client_vpn_network_association.main[0].subnet_id
description = var.additional_routes[count.index].description
depends_on = [aws_ec2_client_vpn_network_association.main]
}
# Security group for VPN endpoint
resource "aws_security_group" "vpn" {
name_prefix = "${var.name_prefix}-vpn-"
description = "Security group for Client VPN endpoint"
vpc_id = var.vpc_id
ingress {
description = "VPN traffic"
from_port = var.vpn_port
to_port = var.vpn_port
protocol = var.transport_protocol == "tcp" ? "tcp" : "udp"
cidr_blocks = var.vpn_ingress_cidr_blocks
}
egress {
description = "Allow all outbound"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-vpn-sg"
}
)
lifecycle {
create_before_destroy = true
}
}
# Apply security group to VPN endpoint
resource "aws_ec2_client_vpn_endpoint_security_group_association" "main" {
count = var.apply_security_group ? 1 : 0
client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
security_group_id = aws_security_group.vpn.id
}