diff --git a/README.md b/README.md index 2698c8e..6d621e5 100644 --- a/README.md +++ b/README.md @@ -64,3 +64,7 @@ The primary objective of maintaining a managed, public-facing deployment is to c Based on your technology experience and preferences, you can choose from the following deployment guides: - [[Medium] Install Kerberos.io on Kubernetes (AWS, GCP, Azure, etc.)](/README.k8s-managed.md) + +Or provision the infrastructure yourself with infrastructure as code: + +- [[AWS] Terraform: EKS cluster + Amazon DocumentDB](./modules/amazon-eks-documentdb/README.md) diff --git a/modules/amazon-documentdb/README.md b/modules/amazon-documentdb/README.md index 43a630e..e360e4b 100644 --- a/modules/amazon-documentdb/README.md +++ b/modules/amazon-documentdb/README.md @@ -1 +1,57 @@ # Amazon DocumentDB + +[Amazon DocumentDB](https://aws.amazon.com/documentdb/) is a managed, MongoDB +compatible database. It can be used as the metadata store for Kerberos Hub +instead of a self-hosted MongoDB. + +## Things to know + +- **Not reachable from outside its VPC.** DocumentDB has no public endpoint, so + the Kerberos Hub services must run inside (or be peered with) the same VPC. +- **TLS is enabled by default.** Clients must trust the Amazon RDS certificate + authority bundle: + `https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem`. +- **Not every MongoDB feature is available.** Retryable writes, the MongoDB + Stable API, geospatial queries/indexes and complex `$lookup` pipelines are + unsupported. Set `mongodb.flavor: "documentdb"` and + `mongodb.retryWrites: "false"` in the hub chart so those code paths are + disabled. + +## Provisioning + +The [`amazon-eks-documentdb`](../amazon-eks-documentdb/README.md) Terraform +stack creates a VPC, an EKS cluster and a DocumentDB cluster with TLS enforced, +and outputs a ready to paste `mongodb` values block for the hub helm chart. + +## Connecting Kerberos Hub + +Configure the database through `mongodb.uri` (not `mongodb.host`) and point the +chart at the CA bundle: + +```bash +curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem +kubectl create secret generic mongodb-ca --from-file=global-bundle.pem -n kerberos-hub +``` + +```yaml +mongodb: + flavor: "documentdb" + retryWrites: "false" + uri: "mongodb://:@.docdb.amazonaws.com:27017/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false" + adminDatabase: "admin" + authenticationMechanism: "SCRAM-SHA-1" + tls: + enabled: true + existingSecret: "mongodb-ca" + caFileName: "global-bundle.pem" + mountPath: "/certs" +``` + +The chart mounts the bundle read-only into every workload that talks to +MongoDB and appends `tls=true&tlsCAFile=/certs/global-bundle.pem` to the +connection string. + +## Related + +- [`../amazon-eks-documentdb`](../amazon-eks-documentdb/README.md) — Terraform for EKS + DocumentDB +- [`../../overlays/documentdb`](../../overlays/documentdb) — Kustomize overlay using DocumentDB diff --git a/modules/amazon-eks-documentdb/.gitignore b/modules/amazon-eks-documentdb/.gitignore new file mode 100644 index 0000000..0059a7a --- /dev/null +++ b/modules/amazon-eks-documentdb/.gitignore @@ -0,0 +1,12 @@ +.terraform/ +.terraform.lock.hcl +*.tfstate +*.tfstate.* +*.tfplan +crash.log +override.tf +override.tf.json +*_override.tf +*_override.tf.json +terraform.tfvars +*.auto.tfvars diff --git a/modules/amazon-eks-documentdb/README.md b/modules/amazon-eks-documentdb/README.md new file mode 100644 index 0000000..c40fb3f --- /dev/null +++ b/modules/amazon-eks-documentdb/README.md @@ -0,0 +1,236 @@ +# Amazon EKS + DocumentDB (Terraform) + +Terraform stack that creates a **basic Kubernetes cluster (EKS) and a managed +MongoDB-compatible database (Amazon DocumentDB) on AWS**, wired together so +Kerberos Hub can be installed on it straight away. + +It is primarily meant as a **reproducible test environment** for the DocumentDB +support in the [`hub` helm chart](https://github.com/kerberos-io/helm-charts), +in particular the `mongodb.tls.*` values that mount the Amazon RDS certificate +authority bundle. It is deliberately small and cheap, not a hardened production +landing zone. + +## What it creates + +```mermaid +flowchart LR + subgraph VPC["VPC (10.20.0.0/16)"] + subgraph Public["Public subnets"] + NAT[NAT gateway] + LB[Load balancers] + end + subgraph Private["Private subnets"] + NODES[EKS managed node group] + DOCDB[(DocumentDB cluster
TLS enforced)] + end + end + EKSCP[EKS control plane] --- NODES + NODES -- "27017 / TLS" --> DOCDB + NODES --> NAT +``` + +| Component | Details | +| --------- | ------- | +| VPC | Public + private subnets across 3 availability zones, internet gateway, NAT gateway | +| EKS | Managed control plane, one managed node group, `coredns`, `kube-proxy`, `vpc-cni`, `eks-pod-identity-agent` and `aws-ebs-csi-driver` add-ons (IRSA role included) | +| DocumentDB | Cluster + instances in the private subnets, encryption **at rest** (KMS) and **in transit** (`tls=enabled`), subnet group, cluster parameter group | +| Security | A dedicated security group that only allows port `27017` from the EKS worker node security group (plus any extra CIDRs you pass in) | + +> [!IMPORTANT] +> DocumentDB has **no public endpoint**. It can only be reached from inside the +> VPC, which is why the workloads that talk to it must run on this cluster (or +> you must tunnel through a bastion host / VPN). + +> [!WARNING] +> This stack costs money while it exists (EKS control plane, NAT gateway, EC2 +> nodes, DocumentDB instances and storage). Run `terraform destroy` when you are +> done. + +## Prerequisites + +- [Terraform](https://developer.hashicorp.com/terraform/downloads) >= 1.5 +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) v2, authenticated with permissions to create VPC, EKS, IAM and DocumentDB resources +- `kubectl` and `helm` + +## Usage + +```bash +cd deployment/modules/amazon-eks-documentdb + +cp terraform.tfvars.example terraform.tfvars +$EDITOR terraform.tfvars + +terraform init +terraform plan +terraform apply +``` + +Creating the cluster and the database takes a while (EKS and DocumentDB are +both slow to provision). + +State is kept locally by default. For anything shared, add a backend, for +example: + +```hcl +terraform { + backend "s3" { + bucket = "my-terraform-state" + key = "kerberos-hub/eks-documentdb.tfstate" + region = "eu-west-1" + } +} +``` + +### Connect kubectl + +```bash +$(terraform output -raw update_kubeconfig_command) +kubectl get nodes +``` + +## Installing Kerberos Hub against DocumentDB + +### 1. Create the certificate authority secret + +DocumentDB presents a certificate signed by the Amazon RDS certificate +authority, so every client needs the bundle: + +```bash +kubectl create namespace kerberos-hub + +curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem +kubectl create secret generic mongodb-ca \ + --from-file=global-bundle.pem \ + -n kerberos-hub +``` + +### 2. Generate the values + +```bash +terraform output -raw hub_values_snippet > hub-documentdb-values.yaml +``` + +Which produces something like: + +```yaml +mongodb: + flavor: "documentdb" + retryWrites: "false" + uri: "mongodb://kerberos:...@kerberos-hub-docdb.cluster-xxxx.eu-west-1.docdb.amazonaws.com:27017/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false" + adminDatabase: "admin" + authenticationMechanism: "SCRAM-SHA-1" + tls: + enabled: true + existingSecret: "mongodb-ca" + caFileName: "global-bundle.pem" + mountPath: "/certs" +``` + +The chart mounts the bundle into every workload that talks to MongoDB, appends +`tls=true&tlsCAFile=/certs/global-bundle.pem` to the URI, and exposes +`MONGODB_TLS`, `MONGODB_TLS_CA_FILE` and `MONGODB_TLS_INSECURE_SKIP_VERIFY` +through the `mongodb-config` ConfigMap. + +> [!NOTE] +> With DocumentDB you must configure the database through `mongodb.uri`, not +> through `mongodb.host` / `mongodb.username` / `mongodb.password`, so that the +> TLS parameters end up in the connection string that every service uses. + +### 3. Install the chart + +```bash +helm repo add kerberos https://charts.kerberos.io +helm install hub kerberos/hub \ + -n kerberos-hub \ + -f your-hub-values.yaml \ + -f hub-documentdb-values.yaml +``` + +The `hub_values_snippet` output contains credentials, so treat the generated +file as a secret and do not commit it. + +### 4. Verify + +```bash +kubectl logs -n kerberos-hub deploy/hub-api | head -50 +kubectl exec -n kerberos-hub deploy/hub-api -- ls -l /certs +``` + +A one-off connectivity check from inside the cluster: + +```bash +kubectl run mongosh --rm -it --restart=Never -n kerberos-hub \ + --image=mongodb/mongodb-community-server:7.0-ubi8 \ + --overrides='{"spec":{"volumes":[{"name":"ca","secret":{"secretName":"mongodb-ca"}}],"containers":[{"name":"mongosh","image":"mongodb/mongodb-community-server:7.0-ubi8","stdin":true,"tty":true,"command":["mongosh"],"args":["'"$(terraform output -raw mongodb_uri)"'&tls=true&tlsCAFile=/certs/global-bundle.pem"],"volumeMounts":[{"name":"ca","mountPath":"/certs"}]}]}}' +``` + +## Persistent volumes + +The EBS CSI driver is installed, but EKS ships `gp2` as the default storage +class. To use `gp3` instead: + +```bash +kubectl patch storageclass gp2 -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' +kubectl apply -f - <<'EOF' +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: gp3 + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: ebs.csi.aws.com +volumeBindingMode: WaitForFirstConsumer +allowVolumeExpansion: true +parameters: + type: gp3 +EOF +``` + +## Tear down + +```bash +# Remove the release first so its load balancers and volumes are cleaned up. +helm uninstall hub -n kerberos-hub + +terraform destroy +``` + +## Inputs + +The defaults are tuned for a small test stack. See [variables.tf](variables.tf) +for the full list; the ones you are most likely to change: + +| Variable | Default | Description | +| -------- | ------- | ----------- | +| `name` | `kerberos-hub` | Name prefix for every resource | +| `region` | `eu-west-1` | AWS region | +| `vpc_cidr` | `10.20.0.0/16` | VPC CIDR block | +| `single_nat_gateway` | `true` | One shared NAT gateway (cheaper, not highly available) | +| `kubernetes_version` | `1.31` | EKS control plane version | +| `cluster_endpoint_public_access_cidrs` | `["0.0.0.0/0"]` | Who may reach the Kubernetes API, **narrow this down** | +| `node_instance_types` | `["t3.large"]` | Worker node instance types | +| `node_desired_size` | `2` | Number of worker nodes | +| `docdb_instance_class` | `db.t3.medium` | DocumentDB instance class | +| `docdb_instance_count` | `1` | Number of DocumentDB instances | +| `docdb_username` | `kerberos` | Master username | +| `docdb_password` | generated | Master password, generated when unset | +| `docdb_tls` | `true` | Enforce TLS on the cluster | +| `docdb_allowed_cidrs` | `[]` | Extra CIDRs allowed on port 27017 | + +## Outputs + +| Output | Description | +| ------ | ----------- | +| `cluster_name`, `cluster_endpoint` | EKS cluster identity | +| `update_kubeconfig_command` | Ready to run `aws eks update-kubeconfig ...` | +| `vpc_id`, `private_subnet_ids` | Networking identifiers | +| `docdb_endpoint`, `docdb_reader_endpoint`, `docdb_port` | DocumentDB connection details | +| `docdb_username`, `docdb_password` | Master credentials (password is sensitive) | +| `mongodb_uri` | Connection string for `mongodb.uri` (sensitive) | +| `hub_values_snippet` | Ready to paste helm values including the TLS block (sensitive) | + +## Related + +- [`../amazon-documentdb`](../amazon-documentdb/README.md) — using DocumentDB as the Kerberos Hub metadata store +- [`../../overlays/documentdb`](../../overlays/documentdb) — Kustomize overlay that deploys Kerberos Hub against DocumentDB +- [`../../README.k8s-managed.md`](../../README.k8s-managed.md) — installing on managed Kubernetes diff --git a/modules/amazon-eks-documentdb/documentdb.tf b/modules/amazon-eks-documentdb/documentdb.tf new file mode 100644 index 0000000..53559b8 --- /dev/null +++ b/modules/amazon-eks-documentdb/documentdb.tf @@ -0,0 +1,122 @@ +############################################################################### +# DocumentDB +# +# The cluster is created with TLS (encryption in transit) and encryption at +# rest enabled. Clients must trust the Amazon RDS certificate authority bundle: +# +# curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem +# +# For Kerberos Hub that bundle is mounted through the helm chart's +# `mongodb.tls` values, see the README next to this file. +############################################################################### + +resource "random_password" "docdb" { + count = var.docdb_password == null ? 1 : 0 + + length = 32 + special = true + + # DocumentDB rejects '/', '"' and '@' in the master password. '@' and '/' + # would also break the MongoDB connection string. + override_special = "!#$%&*()-_=+[]{}<>:?" +} + +locals { + docdb_password = var.docdb_password != null ? var.docdb_password : random_password.docdb[0].result +} + +resource "aws_security_group" "docdb" { + name = "${local.name}-docdb" + description = "MongoDB wire protocol access to the Kerberos Hub DocumentDB cluster" + vpc_id = module.vpc.vpc_id + + tags = merge(local.tags, { Name = "${local.name}-docdb" }) +} + +resource "aws_vpc_security_group_ingress_rule" "docdb_from_eks_nodes" { + security_group_id = aws_security_group.docdb.id + description = "DocumentDB from the EKS worker nodes" + + referenced_security_group_id = module.eks.node_security_group_id + ip_protocol = "tcp" + from_port = 27017 + to_port = 27017 +} + +resource "aws_vpc_security_group_ingress_rule" "docdb_from_cidrs" { + for_each = toset(var.docdb_allowed_cidrs) + + security_group_id = aws_security_group.docdb.id + description = "DocumentDB from ${each.value}" + + cidr_ipv4 = each.value + ip_protocol = "tcp" + from_port = 27017 + to_port = 27017 +} + +resource "aws_docdb_subnet_group" "this" { + name = "${local.name}-docdb" + description = "Private subnets of the Kerberos Hub VPC" + subnet_ids = module.vpc.private_subnets + + tags = local.tags +} + +resource "aws_docdb_cluster_parameter_group" "this" { + name = "${local.name}-docdb" + family = var.docdb_parameter_group_family + description = "Kerberos Hub DocumentDB parameters" + + parameter { + name = "tls" + value = var.docdb_tls ? "enabled" : "disabled" + } + + tags = local.tags + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_docdb_cluster" "this" { + cluster_identifier = "${local.name}-docdb" + engine = "docdb" + engine_version = var.docdb_engine_version + port = 27017 + + master_username = var.docdb_username + master_password = local.docdb_password + + db_subnet_group_name = aws_docdb_subnet_group.this.name + db_cluster_parameter_group_name = aws_docdb_cluster_parameter_group.this.name + vpc_security_group_ids = [aws_security_group.docdb.id] + + storage_encrypted = true + kms_key_id = var.docdb_kms_key_id + + backup_retention_period = var.docdb_backup_retention_period + preferred_backup_window = "02:00-04:00" + preferred_maintenance_window = "sun:04:30-sun:05:30" + + enabled_cloudwatch_logs_exports = var.docdb_enabled_cloudwatch_logs_exports + + deletion_protection = var.docdb_deletion_protection + skip_final_snapshot = var.docdb_skip_final_snapshot + final_snapshot_identifier = var.docdb_skip_final_snapshot ? null : "${local.name}-docdb-final" + + tags = local.tags +} + +resource "aws_docdb_cluster_instance" "this" { + count = var.docdb_instance_count + + identifier = "${local.name}-docdb-${count.index}" + cluster_identifier = aws_docdb_cluster.this.id + instance_class = var.docdb_instance_class + + auto_minor_version_upgrade = true + + tags = local.tags +} diff --git a/modules/amazon-eks-documentdb/eks.tf b/modules/amazon-eks-documentdb/eks.tf new file mode 100644 index 0000000..2e9b937 --- /dev/null +++ b/modules/amazon-eks-documentdb/eks.tf @@ -0,0 +1,70 @@ +############################################################################### +# EKS +############################################################################### + +module "eks" { + source = "terraform-aws-modules/eks/aws" + version = "~> 20.31" + + cluster_name = local.name + cluster_version = var.kubernetes_version + + cluster_endpoint_public_access = var.cluster_endpoint_public_access + cluster_endpoint_public_access_cidrs = var.cluster_endpoint_public_access_cidrs + + # Give the identity running `terraform apply` cluster-admin, so that + # `aws eks update-kubeconfig` immediately works. + enable_cluster_creator_admin_permissions = true + + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + + cluster_addons = { + coredns = {} + kube-proxy = {} + vpc-cni = {} + eks-pod-identity-agent = {} + aws-ebs-csi-driver = { + service_account_role_arn = module.ebs_csi_irsa.iam_role_arn + } + } + + eks_managed_node_groups = { + default = { + instance_types = var.node_instance_types + capacity_type = "ON_DEMAND" + + min_size = var.node_min_size + max_size = var.node_max_size + desired_size = var.node_desired_size + + disk_size = var.node_disk_size + } + } + + tags = local.tags +} + +############################################################################### +# EBS CSI driver +# +# Kerberos Hub's supporting components (RabbitMQ, VerneMQ, MinIO, ...) claim +# persistent volumes, so the cluster needs a working CSI driver. +############################################################################### + +module "ebs_csi_irsa" { + source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + version = "~> 5.44" + + role_name = "${local.name}-ebs-csi" + attach_ebs_csi_policy = true + + oidc_providers = { + main = { + provider_arn = module.eks.oidc_provider_arn + namespace_service_accounts = ["kube-system:ebs-csi-controller-sa"] + } + } + + tags = local.tags +} diff --git a/modules/amazon-eks-documentdb/outputs.tf b/modules/amazon-eks-documentdb/outputs.tf new file mode 100644 index 0000000..b3c52e5 --- /dev/null +++ b/modules/amazon-eks-documentdb/outputs.tf @@ -0,0 +1,124 @@ +############################################################################### +# Cluster +############################################################################### + +output "region" { + description = "AWS region the stack is deployed in." + value = var.region +} + +output "cluster_name" { + description = "Name of the EKS cluster." + value = module.eks.cluster_name +} + +output "cluster_endpoint" { + description = "Endpoint of the Kubernetes API server." + value = module.eks.cluster_endpoint +} + +output "update_kubeconfig_command" { + description = "Command to point kubectl at the new cluster." + value = "aws eks update-kubeconfig --region ${var.region} --name ${module.eks.cluster_name}" +} + +output "vpc_id" { + description = "ID of the VPC. DocumentDB is only reachable from inside this VPC." + value = module.vpc.vpc_id +} + +output "private_subnet_ids" { + description = "IDs of the private subnets hosting the worker nodes and DocumentDB." + value = module.vpc.private_subnets +} + +############################################################################### +# DocumentDB +############################################################################### + +output "docdb_endpoint" { + description = "Cluster (writer) endpoint of the DocumentDB cluster." + value = aws_docdb_cluster.this.endpoint +} + +output "docdb_reader_endpoint" { + description = "Reader endpoint of the DocumentDB cluster." + value = aws_docdb_cluster.this.reader_endpoint +} + +output "docdb_port" { + description = "Port the DocumentDB cluster listens on." + value = aws_docdb_cluster.this.port +} + +output "docdb_username" { + description = "DocumentDB master username." + value = aws_docdb_cluster.this.master_username +} + +output "docdb_password" { + description = "DocumentDB master password. Read it with: terraform output -raw docdb_password" + value = local.docdb_password + sensitive = true +} + +output "docdb_security_group_id" { + description = "Security group guarding the DocumentDB cluster." + value = aws_security_group.docdb.id +} + +output "docdb_tls_enabled" { + description = "Whether TLS is enforced on the DocumentDB cluster." + value = var.docdb_tls +} + +############################################################################### +# Kerberos Hub wiring +############################################################################### + +output "mongodb_uri" { + description = <<-EOT + Connection string for the Kerberos Hub helm chart (`mongodb.uri`). + The chart appends `tls=true` and `tlsCAFile=...` itself when + `mongodb.tls.enabled=true`, so no TLS parameters are included here. + Read it with: terraform output -raw mongodb_uri + EOT + + value = format( + "mongodb://%s:%s@%s:%d/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false", + var.docdb_username, + urlencode(local.docdb_password), + aws_docdb_cluster.this.endpoint, + aws_docdb_cluster.this.port, + ) + + sensitive = true +} + +output "hub_values_snippet" { + description = <<-EOT + Ready to paste values for the Kerberos Hub helm chart. Write it to a file with: + terraform output -raw hub_values_snippet > hub-documentdb-values.yaml + It expects the Amazon RDS CA bundle to be available as the `mongodb-ca` secret: + curl -O https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem + kubectl create secret generic mongodb-ca --from-file=global-bundle.pem -n kerberos-hub + EOT + + value = <<-EOT + mongodb: + # DocumentDB does not support geospatial queries, complex $lookup + # pipelines or retryable writes, hence the flavor and retryWrites below. + flavor: "documentdb" + retryWrites: "false" + uri: "mongodb://${var.docdb_username}:${urlencode(local.docdb_password)}@${aws_docdb_cluster.this.endpoint}:${aws_docdb_cluster.this.port}/?replicaSet=rs0&readPreference=secondaryPreferred&retryWrites=false" + adminDatabase: "admin" + authenticationMechanism: "SCRAM-SHA-1" + tls: + enabled: ${var.docdb_tls} + existingSecret: "mongodb-ca" + caFileName: "global-bundle.pem" + mountPath: "/certs" + EOT + + sensitive = true +} diff --git a/modules/amazon-eks-documentdb/terraform.tfvars.example b/modules/amazon-eks-documentdb/terraform.tfvars.example new file mode 100644 index 0000000..0625a92 --- /dev/null +++ b/modules/amazon-eks-documentdb/terraform.tfvars.example @@ -0,0 +1,33 @@ +# Copy to terraform.tfvars and adjust. + +name = "kerberos-hub" +region = "eu-west-1" +environment = "test" + +# Networking +vpc_cidr = "10.20.0.0/16" +availability_zone_count = 3 +single_nat_gateway = true + +# EKS +kubernetes_version = "1.31" +# Restrict this to your office or VPN range. +cluster_endpoint_public_access_cidrs = ["0.0.0.0/0"] +node_instance_types = ["t3.large"] +node_desired_size = 2 +node_min_size = 2 +node_max_size = 4 + +# DocumentDB +docdb_engine_version = "5.0.0" +docdb_parameter_group_family = "docdb5.0" +docdb_instance_class = "db.t3.medium" +docdb_instance_count = 1 +docdb_username = "kerberos" +# Leave docdb_password unset to have one generated: +# terraform output -raw docdb_password +docdb_tls = true + +# Throwaway test stack settings, flip these for anything long lived. +docdb_deletion_protection = false +docdb_skip_final_snapshot = true diff --git a/modules/amazon-eks-documentdb/variables.tf b/modules/amazon-eks-documentdb/variables.tf new file mode 100644 index 0000000..5e6ad1a --- /dev/null +++ b/modules/amazon-eks-documentdb/variables.tf @@ -0,0 +1,194 @@ +############################################################################### +# General +############################################################################### + +variable "name" { + description = "Name prefix used for every resource created by this stack." + type = string + default = "kerberos-hub" + + validation { + condition = can(regex("^[a-z][a-z0-9-]{2,30}$", var.name)) + error_message = "The name must be lowercase, start with a letter and contain only letters, digits and dashes (3-31 characters)." + } +} + +variable "region" { + description = "AWS region to deploy into." + type = string + default = "eu-west-1" +} + +variable "environment" { + description = "Environment label applied as a tag (for example test, staging, production)." + type = string + default = "test" +} + +variable "tags" { + description = "Extra tags merged into every resource." + type = map(string) + default = {} +} + +############################################################################### +# Networking +############################################################################### + +variable "vpc_cidr" { + description = "CIDR block of the VPC. DocumentDB is only reachable from within this VPC." + type = string + default = "10.20.0.0/16" +} + +variable "availability_zone_count" { + description = "Number of availability zones to spread the subnets over. DocumentDB requires at least two." + type = number + default = 3 + + validation { + condition = var.availability_zone_count >= 2 && var.availability_zone_count <= 4 + error_message = "availability_zone_count must be between 2 and 4." + } +} + +variable "single_nat_gateway" { + description = "Use one shared NAT gateway instead of one per availability zone. Cheaper, but not highly available." + type = bool + default = true +} + +############################################################################### +# EKS +############################################################################### + +variable "kubernetes_version" { + description = "Kubernetes version of the EKS control plane." + type = string + default = "1.31" +} + +variable "cluster_endpoint_public_access" { + description = "Expose the Kubernetes API server publicly. Keep it on for a test cluster, restrict it with cluster_endpoint_public_access_cidrs." + type = bool + default = true +} + +variable "cluster_endpoint_public_access_cidrs" { + description = "CIDR blocks allowed to reach the public Kubernetes API endpoint. Narrow this to your office/VPN range." + type = list(string) + default = ["0.0.0.0/0"] +} + +variable "node_instance_types" { + description = "Instance types of the managed node group." + type = list(string) + default = ["t3.large"] +} + +variable "node_desired_size" { + description = "Desired number of worker nodes." + type = number + default = 2 +} + +variable "node_min_size" { + description = "Minimum number of worker nodes." + type = number + default = 2 +} + +variable "node_max_size" { + description = "Maximum number of worker nodes." + type = number + default = 4 +} + +variable "node_disk_size" { + description = "EBS volume size (GiB) of each worker node." + type = number + default = 50 +} + +############################################################################### +# DocumentDB +############################################################################### + +variable "docdb_engine_version" { + description = "DocumentDB engine version." + type = string + default = "5.0.0" +} + +variable "docdb_parameter_group_family" { + description = "Parameter group family matching the engine version (docdb5.0, docdb4.0, ...)." + type = string + default = "docdb5.0" +} + +variable "docdb_instance_class" { + description = "Instance class of the DocumentDB instances." + type = string + default = "db.t3.medium" +} + +variable "docdb_instance_count" { + description = "Number of DocumentDB instances. One is enough for a test stack, use two or more for failover." + type = number + default = 1 +} + +variable "docdb_username" { + description = "DocumentDB master username. 'admin' and other reserved words are rejected by AWS." + type = string + default = "kerberos" +} + +variable "docdb_password" { + description = "DocumentDB master password. Leave null to generate one; read it afterwards with 'terraform output -raw docdb_password'." + type = string + default = null + sensitive = true +} + +variable "docdb_tls" { + description = "Enforce TLS (encryption in transit) on the cluster. Keep this enabled; it is the configuration the hub chart's mongodb.tls values are meant for." + type = bool + default = true +} + +variable "docdb_kms_key_id" { + description = "KMS key ARN for encryption at rest. Leave null to use the AWS managed key." + type = string + default = null +} + +variable "docdb_backup_retention_period" { + description = "Number of days automated backups are retained." + type = number + default = 1 +} + +variable "docdb_deletion_protection" { + description = "Prevent the cluster from being deleted. Keep false for a throwaway test stack." + type = bool + default = false +} + +variable "docdb_skip_final_snapshot" { + description = "Skip the final snapshot on destroy. Keep true for a throwaway test stack." + type = bool + default = true +} + +variable "docdb_enabled_cloudwatch_logs_exports" { + description = "Log types exported to CloudWatch (audit, profiler)." + type = list(string) + default = [] +} + +variable "docdb_allowed_cidrs" { + description = "Extra CIDR blocks allowed to reach DocumentDB on port 27017, on top of the EKS worker nodes (for example a bastion subnet)." + type = list(string) + default = [] +} diff --git a/modules/amazon-eks-documentdb/versions.tf b/modules/amazon-eks-documentdb/versions.tf new file mode 100644 index 0000000..1d59ad0 --- /dev/null +++ b/modules/amazon-eks-documentdb/versions.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.5.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.60" + } + random = { + source = "hashicorp/random" + version = ">= 3.6" + } + } +} + +provider "aws" { + region = var.region +} diff --git a/modules/amazon-eks-documentdb/vpc.tf b/modules/amazon-eks-documentdb/vpc.tf new file mode 100644 index 0000000..49fdb73 --- /dev/null +++ b/modules/amazon-eks-documentdb/vpc.tf @@ -0,0 +1,59 @@ +locals { + name = var.name + + tags = merge( + { + Project = "kerberos-hub" + Environment = var.environment + ManagedBy = "terraform" + Module = "deployment/modules/amazon-eks-documentdb" + }, + var.tags, + ) + + azs = slice(data.aws_availability_zones.available.names, 0, var.availability_zone_count) +} + +data "aws_availability_zones" "available" { + state = "available" + + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] + } +} + +############################################################################### +# VPC +# +# DocumentDB has no public endpoint: it only listens inside the VPC. Both the +# EKS worker nodes and the DocumentDB instances therefore live in the private +# subnets, and the workers reach the internet (image pulls) through NAT. +############################################################################### + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 5.13" + + name = "${local.name}-vpc" + cidr = var.vpc_cidr + + azs = local.azs + private_subnets = [for index in range(var.availability_zone_count) : cidrsubnet(var.vpc_cidr, 4, index)] + public_subnets = [for index in range(var.availability_zone_count) : cidrsubnet(var.vpc_cidr, 4, index + 8)] + + enable_nat_gateway = true + single_nat_gateway = var.single_nat_gateway + enable_dns_hostnames = true + enable_dns_support = true + + public_subnet_tags = { + "kubernetes.io/role/elb" = "1" + } + + private_subnet_tags = { + "kubernetes.io/role/internal-elb" = "1" + } + + tags = local.tags +}