Infrastructure as Code: Provisioning Secure PHP 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 the region where your resources will be deployed. It’s crucial to manage your API token securely, ideally using environment variables rather than hardcoding it directly into your Terraform configuration files.
Create a file named main.tf in your Terraform project directory and add the following provider configuration:
terraform {
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
# Alternatively, you can set the DIGITALOCEAN_TOKEN environment variable
# token = env("DIGITALOCEAN_TOKEN")
}
variable "do_token" {
description = "DigitalOcean API Token"
type = string
sensitive = true
}
variable "region" {
description = "The DigitalOcean region to deploy resources in."
type = string
default = "nyc3"
}
In this configuration:
- We declare the DigitalOcean provider and specify a version constraint.
- The
provider "digitalocean"block configures the connection. We’re using a variablevar.do_tokenfor the API token, which is marked assensitiveto prevent it from being displayed in Terraform output. - The
regionvariable defaults to “nyc3” but can be overridden.
To use this, you’ll need to set the DO_TOKEN environment variable or pass the token during terraform apply. For local development, you can create a terraform.tfvars file (ensure it’s in your .gitignore):
do_token = "YOUR_DIGITALOCEAN_API_TOKEN" region = "nyc3"
Provisioning a Secure PHP Web Server Droplet
Now, let’s define a DigitalOcean Droplet that will host our PHP application. For security, we’ll configure SSH access using a key pair and set up a basic firewall. We’ll use a common Ubuntu image and a moderately sized Droplet.
Add the following resource block to your main.tf file:
resource "digitalocean_droplet" "php_web_server" {
image = "ubuntu-22-04-x64"
name = "php-web-server-01"
region = var.region
size = "s-2vcpu-4gb" # Adjust size as needed
ssh_keys = [digitalocean_ssh_key.deployer.id]
monitoring = true
tags = ["php-app", "webserver", "production"]
connection {
type = "ssh"
user = "root" # Or your preferred user
private_key = file("~/.ssh/id_rsa") # Path to your private SSH key
timeout = "5m"
}
provisioner "remote-exec" {
inline = [
"apt-get update -y",
"apt-get upgrade -y",
"apt-get install -y nginx php-fpm php-mysql git curl", # Basic PHP stack
"ufw allow 'Nginx Full'",
"ufw allow OpenSSH",
"ufw --force enable",
"systemctl enable nginx",
"systemctl start nginx"
]
}
}
resource "digitalocean_ssh_key" "deployer" {
name = "deployer-key"
public_key = file("~/.ssh/id_rsa.pub") # Path to your public SSH key
}
output "php_web_server_ip" {
description = "The public IP address of the PHP web server."
value = digitalocean_droplet.php_web_server.ipv4_address
}
Explanation of the Droplet resource:
image: Specifies the operating system image.name: A unique name for the Droplet.region: Uses the variable defined earlier.size: The Droplet plan. Adjust based on your application’s needs.ssh_keys: Links the Droplet to an SSH key managed by DigitalOcean. We define this key in thedigitalocean_ssh_key.deployerresource.monitoring: Enables DigitalOcean’s infrastructure monitoring.tags: Useful for organizing and filtering resources.connectionblock: Configures how Terraform connects to the Droplet for remote execution. Ensure theprivate_keypath is correct.provisioner "remote-exec": Executes commands on the Droplet after it’s created. This installs Nginx, PHP-FPM, and essential PHP extensions, and configures the UFW firewall.digitalocean_ssh_keyresource: Uploads your public SSH key to DigitalOcean, allowing passwordless SSH access.output "php_web_server_ip": Exports the public IP address of the created Droplet, making it easy to access after provisioning.
Securing the Droplet with a Firewall and SSH Configuration
While the remote-exec provisioner installs and enables UFW (Uncomplicated Firewall), a more robust approach for production environments involves using a dedicated firewall resource or a more sophisticated configuration management tool. However, for a basic secure setup, the UFW commands are a good starting point. We’ve allowed Nginx Full (ports 80 and 443) and SSH (port 22).
For enhanced SSH security, consider the following:
- Disable root login: After initial setup, create a non-root user and disable direct root SSH login in
/etc/ssh/sshd_config. - Use key-based authentication only: Set
PasswordAuthentication noin/etc/ssh/sshd_config. - Change default SSH port: While not a foolproof security measure, changing the default port (22) can reduce automated scan attempts.
- Implement Fail2ban: Install and configure Fail2ban to protect against brute-force attacks.
These advanced SSH configurations are typically managed post-provisioning or through more advanced configuration management tools like Ansible, Chef, or Puppet, which can be integrated with Terraform.
Deploying a PHP Application with Terraform
To deploy your PHP application code, you can leverage Terraform’s file provisioner or a remote-exec provisioner to clone from a Git repository. For simplicity and demonstration, we’ll use remote-exec to clone a sample application.
First, ensure your PHP application is hosted in a Git repository (e.g., GitHub, GitLab). Let’s assume your repository is at [email protected]:your-username/your-php-app.git.
Modify the digitalocean_droplet resource to include a new provisioner:
resource "digitalocean_droplet" "php_web_server" {
# ... (previous configuration) ...
connection {
type = "ssh"
user = "root"
private_key = file("~/.ssh/id_rsa")
timeout = "5m"
}
provisioner "remote-exec" {
inline = [
"apt-get update -y",
"apt-get upgrade -y",
"apt-get install -y nginx php-fpm php-mysql git curl",
"ufw allow 'Nginx Full'",
"ufw allow OpenSSH",
"ufw --force enable",
"systemctl enable nginx",
"systemctl start nginx",
# Deploy PHP application
"rm -rf /var/www/html/*", # Clear default Nginx content
"git clone [email protected]:your-username/your-php-app.git /var/www/html", # Clone your app
"chown -R www-data:www-data /var/www/html", # Set correct ownership
"chmod -R 755 /var/www/html" # Set appropriate permissions
]
}
}
Important Considerations for Deployment:
- SSH Keys for Git: For private Git repositories, you’ll need to ensure the SSH key used by Terraform (
~/.ssh/id_rsa) has access to your Git provider. This might involve adding the public key to your Git hosting service or using a dedicated deployment key. - Database Configuration: This example assumes a simple deployment. For applications requiring a database, you would provision a DigitalOcean Managed Database or a separate database Droplet and configure your PHP application’s connection strings accordingly. This often involves passing database credentials as sensitive variables.
- Environment Variables: Sensitive configuration like database credentials or API keys should be managed via environment variables on the server, not hardcoded in the application code. You can set these using another
remote-execprovisioner or a configuration management tool. - Nginx Configuration: The default Nginx configuration might not be optimal for your PHP application. You’ll likely need to create a custom Nginx site configuration file (e.g.,
/etc/nginx/sites-available/your-app) and symlink it to/etc/nginx/sites-enabled/. This can also be done viaremote-execor by using Terraform’stemplate_filedata source to generate the Nginx config.
Running Terraform and Verifying the Deployment
Once your main.tf file is complete, you can provision your infrastructure:
- Initialize Terraform:
terraform init
This command downloads the DigitalOcean provider plugin.
- Review the Execution Plan:
terraform plan
This shows you what Terraform will create, modify, or destroy.
- Apply the Configuration:
terraform apply
Terraform will prompt you to confirm the changes. Type yes to proceed.
After the apply command completes, Terraform will output the public IP address of your web server:
Outputs: php_web_server_ip = "YOUR_DROPLET_PUBLIC_IP"
You can then access your PHP application by navigating to this IP address in your web browser. Verify that Nginx is serving your application and that the firewall rules are active by attempting to access disallowed ports.
Next Steps and Advanced Considerations
This setup provides a foundational secure PHP cluster. For production readiness, consider:
- Load Balancing: Deploy multiple web servers and use a DigitalOcean Load Balancer for high availability and scalability.
- Database Management: Utilize DigitalOcean Managed Databases for a robust and managed database solution.
- CI/CD Integration: Automate deployments using CI/CD pipelines (e.g., GitLab CI, GitHub Actions) that trigger Terraform and application deployments.
- Configuration Management: For complex setups, integrate tools like Ansible or Chef to manage server configurations more declaratively and idempotently.
- Monitoring and Alerting: Set up comprehensive monitoring for your Droplets and applications, integrating with services like Prometheus, Grafana, or Datadog.
- Secrets Management: Use a dedicated secrets management solution (e.g., HashiCorp Vault, AWS Secrets Manager) instead of relying solely on environment variables or Terraform variables for highly sensitive data.