Skip to content

peterle95/Inception

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Inception

This project has been created as part of the 42 curriculum by pmolzer.

Description

Inception is a system administration and Docker orchestration project that demonstrates the deployment of a complete web infrastructure using Docker containers. The goal is to set up a small infrastructure composed of different services following specific rules, using Docker Compose to orchestrate multiple containers.

The project implements a WordPress website with:

  • NGINX web server with TLSv1.3 only
  • WordPress with PHP-FPM
  • MariaDB database
  • All services running in separate Docker containers
  • Data persistence through Docker volumes
  • Secure credential management using Docker secrets
  • Custom Docker network for inter-container communication

This infrastructure runs entirely in Docker containers, built from either Debian Bookworm or Alpine Linux (penultimate stable version), with no pre-built images from DockerHub (except for the base OS).

Instructions

Prerequisites

  • Docker Engine (version 20.10 or higher)
  • Docker Compose (version 2.0 or higher)
  • Make
  • Sufficient disk space (~2GB)
  • Linux environment (tested on Debian/Ubuntu)

Configuration

  1. Set up your domain name:
    • Edit /etc/hosts to add your domain:
     sudo echo "127.0.0.1 pmolzer.42.fr" >> /etc/hosts
  • Replace pmolzer with your 42 login in all configuration files
  1. Create secrets directory structure:
   mkdir -p secrets
  1. Configure credentials in the secrets/ directory:

    • db_root_password.txt - MariaDB root password
    • db_password.txt - WordPress database user password
    • credentials.txt - WordPress admin credentials (format: username=admin\npassword=pass)
  2. Update environment variables in srcs/.env:

    • DOMAIN_NAME - Your domain (e.g., pmolzer.42.fr)
    • Database and WordPress configuration

Installation & Execution

# Build and start all services
make

# Or step by step:
make build    # Build Docker images
make up       # Start containers

# Stop services
make down

# Clean Docker system
make clean

# Full cleanup (removes volumes and data)
make fclean

# Rebuild from scratch
make re

Accessing the Services

Verifying Installation

# Check all containers are running
docker ps

# Check logs
docker logs nginx
docker logs wordpress
docker logs mariadb

# Test website
curl -k https://pmolzer.42.fr

Resources

Official Documentation

Tutorials & Articles

AI Usage

AI (Claude) was used in this project for:

Understanding & Research:

  • Clarifying Docker networking concepts (bridge networks, DNS resolution)
  • Understanding Docker Compose service orchestration
  • Learning Docker secrets vs environment variables best practices
  • Researching TLS 1.3 configuration in NGINX

Debugging & Troubleshooting:

  • Resolving MariaDB container restart loops after system reboot
  • Fixing WordPress-MariaDB connection issues (authentication failures)
  • Debugging permission issues on mounted volumes
  • Troubleshooting NGINX 403 Forbidden errors

Code Review & Optimization:

  • Reviewing entrypoint scripts for idempotency
  • Optimizing Dockerfile layer caching
  • Improving Makefile commands
  • Validating security practices (secrets management, TLS configuration)

Documentation:

  • Structuring technical documentation
  • Explaining complex concepts (VMs vs Docker, networking, volumes)
  • Creating user and developer guides

NOT generated by AI:

  • Core Dockerfiles (written manually, reviewed by AI)
  • Docker Compose orchestration logic (manual design)
  • All passwords and secrets (manually created)

Project Description

Docker Architecture

This project uses Docker containerization to isolate services and ensure reproducibility. Each service (NGINX, WordPress, MariaDB) runs in its own container with:

  • Isolated filesystem: Each container has its own file system based on its image
  • Process isolation: Services run independently without interfering with each other
  • Resource limits: Containers can be constrained in CPU and memory usage
  • Portability: The entire stack can run on any system with Docker installed

Source Structure

inception/
├── Makefile                          # Build and orchestration commands
├── secrets/                          # Docker secrets (not in Git)
│   ├── db_root_password.txt
│   ├── db_password.txt
│   └── credentials.txt
└── srcs/
    ├── .env                         # Environment variables
    ├── docker-compose.yml           # Service orchestration
    └── requirements/
        ├── mariadb/
        │   ├── Dockerfile           # MariaDB image definition
        │   ├── conf/
        │   │   └── 50-server.cnf    # MariaDB configuration
        │   └── tools/
        │       └── entrypoint.sh    # Initialization script
        ├── nginx/
        │   ├── Dockerfile           # NGINX image definition
        │   ├── conf/
        │   │   └── nginx.conf       # NGINX site configuration
        │   └── tools/
        │       └── entrypoint.sh    # SSL generation script
        └── wordpress/
            ├── Dockerfile           # WordPress image definition
            ├── conf/
            │   ├── www.conf         # PHP-FPM pool configuration
            │   └── custom.ini       # PHP settings
            └── tools/
                └── entrypoint.sh    # WordPress installation script

Design Choices

1. Debian Bookworm as Base Image

Choice: Use debian:bookworm for all services

Rationale:

  • Stable and well-tested distribution
  • Rich package ecosystem (easier to install MariaDB, NGINX, PHP)
  • Consistent base across all containers
  • Better compatibility than Alpine for some packages

Alternative considered: Alpine Linux (smaller, but occasionally has compatibility issues)

2. Custom Entrypoint Scripts

Choice: Use bash scripts for runtime initialization

Rationale:

  • Secrets only available at runtime (can't access during build)
  • Need to check if database is initialized (idempotency)
  • Must wait for service dependencies (WordPress waits for MariaDB)
  • Dynamic configuration based on environment variables

Why not in Dockerfile: Build time vs runtime separation - Dockerfiles define "what it is," entrypoints define "how it behaves"

3. WP-CLI for WordPress Installation

Choice: Use WP-CLI instead of web-based installation

Rationale:

  • Fully automated installation (no manual clicks)
  • Scriptable and reproducible
  • Can create users programmatically
  • Industry standard for WordPress automation

4. TLS 1.3 Only

Choice: Restrict NGINX to TLS 1.3 only

Rationale:

  • Maximum security (project requirement)
  • Removes legacy protocol vulnerabilities
  • Modern browsers all support TLS 1.3
  • Demonstrates security best practices

5. Bridge Network

Choice: Use Docker bridge network for inter-container communication

Rationale:

  • Automatic DNS resolution (services find each other by name)
  • Network isolation (containers can't access host directly)
  • Standard pattern for multi-container applications
  • Better security than host network

Virtual Machines vs Docker

Aspect Virtual Machines Docker Containers
Isolation Level Full OS isolation with hypervisor Process-level isolation with shared kernel
Boot Time Minutes (full OS boot) Seconds (process start)
Size Gigabytes (includes full OS) Megabytes (only app + dependencies)
Resource Usage High (each VM has dedicated RAM/CPU) Low (shares host resources efficiently)
Portability Limited (VM images are large) High (images are small, standardized)
Use Case Run different OS, strong isolation Microservices, development, CI/CD
Performance Slower (virtualization overhead) Near-native (minimal overhead)

Why Docker for this project:

  • ✅ Lightweight (can run 3 services on modest hardware)
  • ✅ Fast iteration (rebuild images in seconds)
  • ✅ Portable (runs identically on any Docker host)
  • ✅ Efficient (shares host kernel, minimal overhead)
  • ✅ Modern standard for web application deployment

When to use VMs instead:

  • Need to run different operating systems (Windows + Linux)
  • Require complete isolation (security-critical applications)
  • Legacy applications that can't be containerized

Secrets vs Environment Variables

Aspect Docker Secrets Environment Variables
Security Encrypted at rest, in memory only Visible in docker inspect, logs
Storage Mounted as files in /run/secrets/ Set as env vars in container
Scope Swarm and Compose Any Docker environment
Visibility Not visible in docker inspect Visible in docker inspect
Best For Passwords, API keys, certificates Configuration, non-sensitive data
Rotation Easy (update secret, restart service) Requires rebuild or restart

Our implementation:

secrets:
  db_password:
    file: ../secrets/db_password.txt  # Stored securely

environment:
  MYSQL_DATABASE: wordpress           # Non-sensitive config

Why this split:

  • 🔒 Secrets: Passwords (db_password, db_root_password, credentials) - never exposed
  • 🔓 Env vars: Database name, domain, email - not sensitive, easier to change

Example attack scenario:

  • ❌ Env var: Attacker runs docker inspect wordpress → sees password
  • ✅ Secret: Attacker runs docker inspect wordpress → password not visible

Docker Network vs Host Network

Aspect Docker Network (Bridge) Host Network
Isolation Containers isolated from host Container uses host network directly
DNS Resolution Automatic (service names work) No DNS (must use localhost)
Port Conflicts No conflicts (internal ports) Ports conflict with host services
Security Better (network isolation) Weaker (direct host access)
Performance Slight overhead (NAT) Native (no NAT)
Use Case Multi-container apps Network monitoring, high performance

Our choice: Bridge network

networks:
  inception:
    driver: bridge

Why bridge network:

  • ✅ WordPress connects to MariaDB using mariadb:3306 (DNS works)
  • ✅ NGINX connects to WordPress using wordpress:9000 (DNS works)
  • ✅ MariaDB port 3306 not exposed to host (security)
  • ✅ Only NGINX port 443 exposed (minimal attack surface)

With host network (what we avoided):

  • ❌ No automatic DNS (must use localhost or IP addresses)
  • ❌ Port conflicts (if host has MariaDB, conflict on 3306)
  • ❌ Less secure (containers can access all host network)

Network flow in our project:

Internet → Host:443 → NGINX:443 → WordPress:9000 → MariaDB:3306
         (exposed)  (bridge net)   (bridge net)    (isolated)

Docker Volumes vs Bind Mounts

Aspect Docker Volumes Bind Mounts
Management Docker managed User managed
Location Docker area (/var/lib/docker/volumes/) Any host path
Portability Portable (Docker manages path) Not portable (hardcoded paths)
Permissions Docker handles permissions Manual permission management
Backup Use docker volume commands Standard file backup
Performance Optimized by Docker Native filesystem performance
Use Case Database data, production Development, config files

Our implementation: Bind mounts with volume abstraction

volumes:
  mariadb:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /home/pmolzer/data/mariadb  # Bind mount

  wordpress:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /home/pmolzer/data/wordpress  # Bind mount

Why this hybrid approach:

  • Explicit control: We know exactly where data is stored
  • Easy backup: Can directly backup /home/pmolzer/data/
  • Debugging: Can inspect files directly on host
  • Project requirement: Must use bind mounts to specific paths
  • Persistence: Data survives container deletion

Comparison for our data:

If we used pure Docker volumes:

volumes:
  mariadb:  # Docker creates volume automatically
  wordpress:
  • ✅ Portable
  • ✅ Better permissions handling
  • ❌ Data location unclear (/var/lib/docker/volumes/srcs_mariadb/_data)
  • ❌ Harder to backup
  • ❌ Doesn't meet project requirements

Our bind mount approach:

volumes:
  mariadb:
    driver_opts:
      device: /home/pmolzer/data/mariadb
  • ✅ Clear data location
  • ✅ Easy to backup (cp -r /home/pmolzer/data/ backup/)
  • ✅ Meets project requirements
  • ⚠️ Must manually fix permissions after reboot
  • ⚠️ Path hardcoded (less portable)

Data persistence verification:

# Create post in WordPress
docker exec -it wordpress wp post create --post_title="Test" --allow-root

# Restart entire system
make down
make up

# Post still exists (data persisted in /home/pmolzer/data/wordpress)
docker exec -it wordpress wp post list --allow-root

Summary of Technical Choices

Decision Choice Rationale
Containerization Docker Lightweight, portable, modern standard
Base Image Debian Bookworm Stable, compatible, rich packages
Secrets Docker Secrets Secure password management
Network Bridge Network Isolation + DNS resolution
Volumes Bind Mounts Explicit control, easy backup
TLS TLS 1.3 only Maximum security
Orchestration Docker Compose Declarative, reproducible

This architecture balances security (secrets, TLS 1.3, network isolation), maintainability (clear structure, automation), and performance (efficient containerization, static file serving).

License

This is an educational project created for the 42 school curriculum.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors