Cloud-1
42 Abu Dhabi

Cloud-1

Automated Cloud VM & Infrastructure Deployment

3 weeks
Individual Project
AnsibleDocker ComposeNGINXMariaDBUFW & IptablesCertbot

Cloud-1 elevates containerized architecture into fully automated cloud operations. Starting with a fresh Ubuntu cloud VM, an Ansible automation pipeline handles the entire host lifecycle: provisioning system dependencies, configuring strict UFW firewall policies with DOCKER-USER iptables chain mitigations, installing Docker CE, managing Let's Encrypt TLS certificates, and orchestrating a secure multi-service stack with persistent storage and encrypted Ansible Vault secrets.

Key Features

100% Idempotent Automation

Modular Ansible roles ensure running site.yml repeatedly results in 0 changed tasks on already-configured targets.

DOCKER-USER Chain Hardening

Mitigated Docker's automatic iptables manipulation bypass by deploying custom systemd services enforcing strict external port isolation.

Multi-Container Architecture

Dedicated microservice containers for NGINX TLS termination, WordPress PHP-FPM, phpMyAdmin, and internal-only MariaDB database.

Ansible Vault Security

Encrypted credential management securing database passwords, admin tokens, and private host variables in version control.

Development Journey

Phase 1

Host Bootstrapping & Roles Architecture

Structured modular Ansible roles (common, firewall, docker, tls, app), configured cloud-init hooks, and tuned system resources including swap and unattended security upgrades.

Phase 2

Firewall Hardening & Network Security

Configured UFW default-deny incoming policies and developed custom iptables rules injected into the DOCKER-USER chain to ensure only ports 22, 80, and 443 are reachable externally.

Phase 3

Containerized Stack & TLS Lifecycle

Crafted Jinja2 templated Docker Compose configurations with automated Certbot Let's Encrypt issuance, certificate renewal crons, and self-signed fallbacks.

Phase 4

Ansible Vault & Multi-Host Scaling

Encrypted all sensitive environment secrets with Ansible Vault, verified multi-host deployment with parallel forks (-f 5), and confirmed full reboot resilience.

Challenges & Solutions

Docker Bypassing UFW Rules

Problem:

By default, Docker directly injects PREROUTING iptables rules that route external traffic to published container ports, completely bypassing standard UFW allow/deny filters.

Solution:

Engineered a persistent DOCKER-USER chain rule applied via a systemd unit triggered after docker.service, dropping external traffic to unexposed services before Docker evaluates its rules.

Automated TLS vs Development Fallback

Problem:

Let's Encrypt requires a publicly accessible domain and HTTP-01 challenge completion, which fails when evaluating against raw IP addresses in staging.

Solution:

Implemented dynamic Ansible Jinja2 logic that automatically generates a trusted self-signed certificate fallback when no domain is provided, switching to Certbot when a valid FQDN is detected.

Playbook Idempotency Guarantee

Problem:

Ensuring tasks execute without altering system state or restarting healthy services if the desired target state is already met.

Solution:

Used fine-grained file stat checks, state-aware handlers with notifications, and template checksum diffing to guarantee subsequent runs report 0 changed tasks.

roles/firewall/tasks/main.yml
yaml
---
- name: Set ufw default deny incoming
  community.general.ufw:
    direction: incoming
    policy: deny

- name: Allow SSH (22/tcp), HTTP (80/tcp), HTTPS (443/tcp)
  community.general.ufw:
    rule: allow
    port: "{{ item }}"
    proto: tcp
  loop: ["22", "80", "443"]

- name: Deploy DOCKER-USER chain hardening script
  ansible.builtin.template:
    src: docker-user-hardening.sh.j2
    dest: /usr/local/sbin/docker-user-hardening.sh
    owner: root
    group: root
    mode: "0755"
  notify: Apply DOCKER-USER rules

- name: Deploy systemd unit to apply rules after Docker starts
  ansible.builtin.template:
    src: docker-user-hardening.service.j2
    dest: /etc/systemd/system/docker-user-hardening.service
    owner: root
    group: root
    mode: "0644"