Skip to content

Phase 0: Hands-On Job-Essential Lab Solutions

This module contains complete, step-by-step code solutions, terminal verification commands, and architectural explanations for the Phase 0 Job-Essential Exercises.


🛠️ Lab 1: The Automated Linux Web Server Bootstrap Script

Objective

Write an automated, idempotent Bash script (setup.sh) that provisions an Ubuntu VM, installs NGINX, configures a custom HTML homepage, enables firewall rules, and registers the service with systemd.

The Production Script (setup.sh)

#!/usr/bin/env bash
# ==============================================================================
# Script Name: setup.sh
# Description: Automated NGINX Web Server Provisioning
# ==============================================================================
set -euo pipefail

log() {
    echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] [INFO] $1"
}

# 1. Verify script is run as root
if [[ $EUID -ne 0 ]]; then
   echo "[ERROR] This script must be run as root (use sudo)." 
   exit 1
fi

log "Updating apt package index..."
apt-get update -y > /dev/null

log "Installing NGINX..."
apt-get install -y nginx > /dev/null

log "Deploying custom web application content..."
cat << 'EOF' > /var/www/html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Platform Architect Lab 1</title>
    <style>
        body { font-family: sans-serif; background: #0f172a; color: #38bdf8; text-align: center; padding-top: 10%; }
        h1 { font-size: 3rem; margin-bottom: 0.5rem; }
        p { font-size: 1.25rem; color: #94a3b8; }
    </style>
</head>
<body>
    <h1>🚀 Hello from NGINX!</h1>
    <p>Automated Linux Node Provisioning Verified.</p>
</body>
</html>
EOF

log "Adjusting file ownership and permissions..."
chown -R www-data:www-data /var/www/html
chmod 644 /var/www/html/index.html

log "Enabling and starting NGINX systemd service..."
systemctl enable nginx > /dev/null
systemctl restart nginx

log "Verifying web server response..."
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:80)

if [[ "${HTTP_STATUS}" -eq 200 ]]; then
    log "SUCCESS: NGINX is online and returning HTTP 200!"
else
    echo "[ERROR] Health check failed with HTTP status: ${HTTP_STATUS}"
    exit 1
fi

Verification Command

chmod +x setup.sh
sudo ./setup.sh
curl -I http://localhost

🧮 Lab 2: The Subnet Math Challenge

Objective

Given the enterprise VPC block 10.0.0.0/16 ($65,536$ total IP addresses), divide it into 4 equal subnets. Calculate the CIDR prefix, Network ID, Broadcast IP, total IPs, and usable AWS IP ranges for each subnet.

The Mathematical Breakdown

  • Original block: /16 (16 network bits, 16 host bits).
  • To create 4 subnets ($2^2 = 4$), we borrow 2 additional bits from the host portion.
  • New Prefix: $16 + 2 = \mathbf{/18}$.
  • Total IPs per subnet: $2^{(32 - 18)} = 2^{14} = \mathbf{16,384\text{ IPs}}$.
  • AWS Usable IPs: $16,384 - 5 = \mathbf{16,379\text{ IPs}}$.

The Subnet Allocation Table

Subnet # CIDR Block Network ID First Usable IP (AWS) Last Usable IP (AWS) Broadcast Address
Subnet 1 10.0.0.0/18 10.0.0.0 10.0.0.4 10.0.63.254 10.0.63.255
Subnet 2 10.0.64.0/18 10.0.64.0 10.0.64.4 10.0.127.254 10.0.127.255
Subnet 3 10.0.128.0/18 10.0.128.0 10.0.128.4 10.0.191.254 10.0.191.255
Subnet 4 10.0.192.0/18 10.0.192.0 10.0.192.4 10.0.255.254 10.0.255.255

(Note: In AWS VPCs, .1 is reserved for the VPC Router, .2 for the Amazon DNS Resolver, and .3 for future AWS expansion).


🐳 Lab 3: The Hardened Dockerized Application

Objective

Develop a minimal Python/Flask REST API, package it into a multi-stage Docker container running as an unprivileged user, run it with port mapping, and verify it using curl.

1. Application Code (app.py)

from flask import Flask, jsonify
import socket
import os

app = Flask(__name__)

@Flask.route("/healthz")
def health():
    return jsonify({
        "status": "healthy",
        "hostname": socket.gethostname(),
        "environment": os.getenv("APP_ENV", "development")
    }), 200

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

2. Multi-Stage Hardened Dockerfile (Dockerfile)

# Stage 1: Build & Dependencies
FROM python:3.11-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt

# Stage 2: Minimal Distroless / Non-Root Runtime
FROM python:3.11-slim
WORKDIR /app

# Security: Create non-root user with UID 10001
RUN useradd -u 10001 -m appuser

# Copy installed python dependencies from builder
COPY --from=builder /root/.local /home/appuser/.local
COPY app.py .

ENV PATH=/home/appuser/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1

# Switch to unprivileged user
USER 10001

EXPOSE 8080
CMD ["python", "app.py"]

3. Build & Execution Commands

# 1. Build image
docker build -t platform-api:v1.0.0 .

# 2. Run container mapping host port 8080 to container port 8080
docker run -d -p 8080:8080 -e APP_ENV=production --name test-api platform-api:v1.0.0

# 3. Verify reachability
curl -s http://localhost:8080/healthz | jq .

# 4. Cleanup
docker stop test-api && docker rm test-api