Infrastructure as Code: Provisioning Secure C Clusters on DigitalOcean Using Terraform
Terraform Provider Configuration for DigitalOcean
To begin provisioning infrastructure on DigitalOcean using Terraform, we first need to configure the DigitalOcean provider. This involves specifying your API token and optionally a region. It’s crucial to manage your API token securely, ideally using environment variables or a secrets management system rather than hardcoding it directly into your Terraform configuration.
Create a file named main.tf in your Terraform project directory and add the following provider configuration:
# main.tf
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
# region = "nyc3" # Uncomment and set your preferred region if not using the default
}
variable "do_token" {
description = "DigitalOcean API Token"
type = string
sensitive = true
}
To set the do_token variable, you can either create a terraform.tfvars file (ensure this file is NOT committed to version control) or set it as an environment variable:
- Environment Variable:
export TF_VAR_do_token="your_digitalocean_api_token" - terraform.tfvars:
do_token = "your_digitalocean_api_token"
Defining the C Cluster Resources
A C cluster typically consists of a control plane and one or more worker nodes. For simplicity and cost-effectiveness, we’ll start with a single control plane node and a couple of worker nodes. We’ll use DigitalOcean’s managed Kubernetes service (DOKS) for this. Each node will be a Droplet with specific configurations.
In your main.tf file, add the following resource definitions:
# main.tf (continued)
resource "digitalocean_kubernetes_cluster" "c_cluster" {
name = "my-secure-c-cluster"
region = "nyc3" # Ensure this matches your provider configuration or is set via variable
version = "1.28.2-do.0" # Specify your desired Kubernetes version
node_pool {
name = "control-plane"
size = "s-2vcpu-4gb" # Control plane node size
node_count = 1
auto_scale = false
tags = ["control-plane", "c-cluster"]
}
node_pool {
name = "worker-pool-1"
size = "s-4vcpu-8gb" # Worker node size
node_count = 2
auto_scale = true
min_nodes = 1
max_nodes = 5
tags = ["worker", "c-cluster"]
}
tags = ["c-cluster", "production"]
}
# Output the cluster endpoint and kubeconfig
output "kube_endpoint" {
description = "The API server endpoint for the Kubernetes cluster."
value = digitalocean_kubernetes_cluster.c_cluster.endpoint
sensitive = false # Endpoint is generally safe to expose, but be mindful of access controls
}
output "kubeconfig" {
description = "The kubeconfig file for the Kubernetes cluster."
value = digitalocean_kubernetes_cluster.c_cluster.kube_config
sensitive = true # Kubeconfig contains sensitive credentials
}
Explanation:
digitalocean_kubernetes_cluster: This resource defines the managed Kubernetes cluster itself.name: A unique name for your cluster.region: The DigitalOcean region where the cluster will be provisioned.version: The specific Kubernetes version to deploy. It’s recommended to pin this to a stable version.node_pool: Defines a group of nodes. We have one for the control plane and one for workers.size: The Droplet slug for the nodes in the pool (e.g.,s-2vcpu-4gb).node_count: The initial number of nodes in the pool.auto_scale: Enables or disables auto-scaling for the node pool.min_nodes/max_nodes: Define the scaling range ifauto_scaleis true.tags: Assigns tags to the underlying Droplets for organization and potential firewall rules.output "kube_endpoint": Exposes the Kubernetes API server endpoint.output "kubeconfig": Exposes the full kubeconfig file, which is essential for interacting with the cluster usingkubectl. This is marked assensitiveto prevent accidental exposure in logs.
Securing the C Cluster
Security is paramount. For DOKS, DigitalOcean handles much of the control plane security. However, we need to consider network security and access control for our nodes and applications.
1. Network Security Groups (NSGs): While DOKS creates default NSGs, you can define custom ones for more granular control. For this example, we’ll rely on DOKS’s default security, which is generally well-configured for basic access. For production, you would typically define explicit firewall rules using DigitalOcean’s firewall resources or integrate with Kubernetes Network Policies.
2. IAM and RBAC: Access to the cluster is managed via Kubernetes RBAC. The kubeconfig output provides administrative access. For application-level access or user roles, you’ll need to configure Kubernetes Roles and RoleBindings within your cluster.
3. Private Networking: For enhanced security, consider enabling private networking for your node pools. This ensures that nodes communicate over a private IP address, reducing exposure to the public internet. This is configured within the node_pool block:
# main.tf (within digitalocean_kubernetes_cluster resource)
node_pool {
# ... other configurations ...
private_networking = true # Enable private networking for this node pool
}
4. Secrets Management: Avoid storing sensitive application secrets directly in your Terraform code or Kubernetes manifests. Use Kubernetes Secrets and integrate with external secrets management solutions like HashiCorp Vault, AWS Secrets Manager, or DigitalOcean’s own secrets management capabilities if available.
Provisioning the Cluster
Once your Terraform configuration is in place and your DigitalOcean API token is set, you can provision the cluster. Navigate to your Terraform project directory in your terminal.
1. Initialize Terraform: This downloads the necessary provider plugins.
terraform init
2. Review the Execution Plan: This shows you what Terraform will create, modify, or destroy.
terraform plan
3. Apply the Configuration: This will create the DigitalOcean Kubernetes cluster.
terraform apply
Terraform will prompt you to confirm the action. Type yes to proceed. This process can take several minutes as DigitalOcean provisions the Kubernetes control plane and worker nodes.
Accessing and Managing the Cluster
After terraform apply completes successfully, you will have the kubeconfig output. You can retrieve it using:
terraform output -raw kubeconfig > ~/.kube/config_c_cluster
This command saves the kubeconfig to a file named config_c_cluster in your ~/.kube/ directory. You can then merge this with your existing kubeconfig or set it as your current context:
To set it as the current context (assuming you have kubectl installed and configured):
export KUBECONFIG=~/.kube/config_c_cluster kubectl config use-context default # Or the context name provided in the kubeconfig kubectl get nodes
You should see your control plane and worker nodes listed. To destroy the cluster and all associated resources when no longer needed, run:
terraform destroy
This ensures you don’t incur unnecessary costs and maintains a clean infrastructure state.