A comprehensive infrastructure-as-code project that provisions and configures secure, production-ready cloud servers on Hetzner Cloud with automated deployment, security hardening, and service orchestration.
This project implements a modern DevOps workflow combining Terraform for infrastructure provisioning and Ansible for configuration management, creating a robust foundation for cloud-native applications.
- Infrastructure Provisioning: Terraform configuration for Hetzner Cloud servers
- Configuration Management: Ansible playbooks for server hardening and service deployment
- Container Orchestration: Docker with Traefik reverse proxy
- Secrets Management: HashiCorp Vault integration
- DNS Management: Cloudflare DNS automation
- Security: Multi-layered security with UFW, Fail2Ban, and SSH hardening
blithe-3/
βββ infra-terraform/ # Terraform infrastructure code
β βββ main.tf # Main infrastructure resources
β βββ variables.tf # Input variables
β βββ outputs.tf # Output values
β βββ providers.tf # Cloud provider configurations
β βββ versions.tf # Terraform version and provider constraints
β βββ inventory.tf # Dynamic Ansible inventory generation
β βββ inventory.tftpl # Template for Ansible inventory
β βββ secrets/ # Sensitive configuration (gitignored)
βββ config-ansible/ # Ansible configuration management
β βββ configure-server.yml # Main playbook
β βββ inventory.yml # Generated inventory file
β βββ group_vars/ # Global variables
β βββ roles/ # Reusable configuration roles
β βββ security/ # System hardening
β βββ docker/ # Docker installation
β βββ traefik/ # Reverse proxy setup
β βββ vault/ # Secrets management
β βββ traefik-net/ # Docker networking
βββ docker/ # Docker configuration
β βββ Dockerfile # Ansible execution environment
β βββ docker-compose.yml # Containerized Ansible runner
βββ .secrets # Local secrets storage (gitignored)
Terraform for Provisioning
- Why Terraform: Declarative syntax, state management, and multi-cloud support
- Provider Choice: Hetzner Cloud for cost-effective European hosting with excellent performance
- State Management: Cloudflare R2 backend for secure, remote state storage with S3-compatible API
Ansible for Configuration
- Why Ansible: Agentless, idempotent, and excellent for complex orchestration
- Role-Based Design: Modular, reusable configuration components
- Containerized Execution: Consistent environment with Docker-based Ansible runner
Multi-Layer Security Approach:
- Network Level: UFW firewall with deny-by-default policy
- Application Level: Fail2Ban for intrusion prevention
- Access Level: SSH hardening with key-based authentication
- Secrets Level: HashiCorp Vault for centralized secrets management
- Container Level: Docker networks and Traefik security headers
Security Hardening Features:
- Automated security updates via unattended-upgrades
- SSH configuration hardening (disable root login, key-based auth only)
- AppArmor for mandatory access control
- Custom firewall rules for specific services (WireGuard, Git SSH, etc.)
Traefik as Edge Router:
- Why Traefik: Native Docker integration, automatic service discovery, and built-in Let's Encrypt support
- Certificate Management: Cloudflare DNS challenge for wildcard SSL certificates
- Load Balancing: Automatic load balancing for containerized services
- Monitoring: Prometheus metrics and structured logging
Vault for Secrets Management:
- Why Vault: Centralized secrets management with audit logging
- Initialization: Automated initialization with secure key generation
- Storage: File-based backend for simplicity and reliability
- Access Control: Token-based authentication with fine-grained policies
Cloudflare Integration:
- Why Cloudflare: Global CDN, DDoS protection, and excellent DNS management
- Automation: Terraform-managed DNS records pointing to provisioned servers
- SSL/TLS: End-to-end encryption with Cloudflare-issued certificates
- Performance: DNS optimization and caching
- Terraform >= 1.0
- Docker and Docker Compose
- Hetzner Cloud account and API token
- Cloudflare account with API token
- SSH key pair for server access
-
Clone the repository:
git clone <repository-url> cd blithe-3
-
Configure environment variables:
export HCLOUD_TOKEN="your-hetzner-token" export CLOUDFLARE_API_TOKEN="your-cloudflare-token" export CLOUDFLARE_ACCOUNT_ID="your-account-id" export CLOUDFLARE_ZONE_ID="your-zone-id" export CLOUDFLARE_RECORD_NAME="your-domain"
-
Initialize Terraform:
cd infra-terraform terraform init
-
Provision Infrastructure:
terraform apply -var="hcloud_token=$HCLOUD_TOKEN" \ -var="cloudflare_api_token=$CLOUDFLARE_API_TOKEN" \ -var="cloudflare_account_id=$CLOUDFLARE_ACCOUNT_ID" \ -var="cloudflare_zone_id=$CLOUDFLARE_ZONE_ID" \ -var="cloudflare_record_name=$CLOUDFLARE_RECORD_NAME"
-
Configure Servers:
cd ../docker docker-compose up --build
Key configurable parameters in infra-terraform/variables.tf:
node_count: Number of servers to provision (default: 1)server_type: Hetzner Cloud server type (default: cax11)image: Operating system image (default: debian-12)admin_username: Admin user for servers (default: adminuser)node_prefix: Naming prefix for servers (default: blithe)
-
Create Ansible Role:
mkdir -p config-ansible/roles/new-service/{tasks,templates,defaults,handlers} -
Add Role to Playbook:
# config-ansible/configure-server.yml - role: new-service tags: ["new-service"]
-
Configure Traefik Router: Add service configuration to
config-ansible/roles/traefik/templates/dynamic_conf.yml.j2
Update firewall rules in config-ansible/roles/security/tasks/main.yml:
- name: Allow custom service
ufw:
rule: allow
port: "8080"
proto: tcpModify node_count variable and reapply Terraform:
terraform apply -var="node_count=3"- Vault Status: Check via
docker exec vault vault status - Traefik Dashboard: Available at
https://your-domain/dashboard/ - Service Logs: Monitor via
docker-compose logs -f
- Terraform State: Automatically backed up to Cloudflare R2
- Vault Data: File-based storage in
/vault/file - Configuration: Git-tracked Ansible playbooks and Terraform code
- System Updates: Automated via unattended-upgrades
- Docker Images: Update via
docker-compose pull - Terraform Providers: Update via
terraform init -upgrade
- Vault initialization keys are stored locally in
.secretsfile - Terraform state contains sensitive data and is secured in remote backend
- SSH keys are managed separately and not stored in repository
- Default deny firewall policy with explicit allow rules
- SSH access restricted to key-based authentication
- Services exposed only through Traefik reverse proxy
- Internal Docker networks for service isolation
- Admin user with sudo privileges created during provisioning
- Vault token-based authentication for secrets access
- Role-based access control for different service components
- Terraform State Lock: If state is locked, check for concurrent runs
- Ansible Connection: Verify SSH keys and firewall rules
- Vault Initialization: Ensure Vault container is running before initialization
- Traefik Certificates: Check Cloudflare API credentials and DNS propagation
# Check Terraform state
terraform show
# Test Ansible connectivity
ansible hcloud_nodes -i config-ansible/inventory.yml -m ping
# Check Docker services
docker-compose ps
# View Vault status
docker exec vault vault status- Terraform Documentation
- Ansible Documentation
- Traefik Documentation
- Vault Documentation
- Hetzner Cloud Documentation
- Cloudflare API Documentation
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
Note: This infrastructure is designed for production use but should be thoroughly tested in a staging environment before deployment to critical systems.