Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🛡️ Avengers – BAM Challenge (T.I.Q.S. Edition) 🛡️

AWS Flask Nginx Gunicorn Python HTML5 CSS3 JavaScript AmazonLinux


📖 Project Summary

This project is part of the Be A Man (BAM) Challenge completed by T.I.Q.S..
It demonstrates how to serve a static Avengers-themed website from Amazon S3 that connects to a Flask API hosted on an EC2 instance via Gunicorn + Nginx.

The static website lets users:

  • Configure the API base URL (Captain America)
  • Check service health (Iron Man)
  • Send echo JSON payloads (Thor)
  • Run fun number stats (Hulk)

🏗️ Architecture

diagram

  • Frontend: Hosted on Amazon S3 static site
  • Backend: Flask app running under Gunicorn on EC2 (Amazon Linux 2023)
  • Reverse Proxy: NGINX routes traffic from port 80

📚 References


📜 Detailed Step-by-Step Challenge Guide

🪣 S3 Bucket Setup

  1. Create bucket

    • Name: bam-static-site-tiqs

    • Object ownership: leave as default create-bucket-pt1

    • Block Public Access: uncheck Block all public access (check the acknowledgment box)

    • Bucket Versioning: Enable create-bucket-pt2

    • Tags: namebam-static-site-tiqs

    • Default encryption: leave as default

    • Bucket Key: leave as default

    • Click Create Bucket create-bucket-pt3

  2. Enable Static Website Hosting

    • Open your bucket
    • Go to Properties tab bucket-properties
    • Scroll down to Static website hosting and click Edit edit-static-website-hosting-pt1
    • Static website hosting: Enable
    • Index document: index.html edit-static-website-hosting-pt2
    • Click Save changes edit-static-website-hosting-pt3

    Your S3 bucket website endpoint will be:

    http://bam-static-site-tiqs.s3-website-us-east-1.amazonaws.com
    

    edit-static-website-hosting-complete

  3. Bucket Policy (Public Read)

    • Go to Permissions tab

    • Scroll down to Bucket policy and click Edit bucket-policy-pt1

    • Paste:

      {
      "Version": "2012-10-17",
      "Statement": [
         {
            "Sid": "PublicReadForWebsite",
            "Effect": "Allow",
            "Principal": "*",
            "Action": ["s3:GetObject"],
            "Resource": ["arn:aws:s3:::bam-static-site-tiqs/*"]
         }
      ]
      }
    • Click Save changes bucket-policy-pt2

  4. CORS (Cross-origin resource sharing)

    • Scroll down to Cross-origin resource sharing (CORS) and click Edit cors-pt1

    • Paste:

      [
      {
         "AllowedHeaders": ["*"],
         "AllowedMethods": ["GET", "POST"],
         "AllowedOrigins": ["http://bam-static-site-tiqs.s3-website-us-east-1.amazonaws.com"],
         "ExposeHeaders": []
      }
      ]
    • Click Save changes cors-pt2 cors-complete

  5. Upload static files

    • Go to Objects tab and click Upload s3-objects-upload-pt1
    • Add the following static files:
      • index.html
      • captain-america.jpg
      • hulk.jpg
      • iron-man.jpg
      • thor.jpg
      • script.js
    • Verify all files are staged for upload.
    • Leave all other sections closed and as default.
    • Scroll down to the bottom of the page and click Upload. s3-objects-upload-pt2
    • Verify all six files are uploaded with the word Succeeded under status.
    • The green bar at the top say Upload succeeded. s3-objects-upload-complete

🔒 Security Group

  1. Go to Security Groups

    • Click the orange button *Create security group create-security-group-pt1
    • Security group name: bam-static-site-sg
    • Description: bam-static-site-sg
    • VPC: leave as default (NOTE: you can customize this if you want to use a custom VPC with defined subnets)
  2. Inbound rules

    Type Protocol Port range Source Description
    SSH TCP 22 Anywhere IPv4 SSH
    HTTP TCP 80 Anywhere IPv4 HTTP

    create-security-group-pt2

  3. SKIP OUTBOUND RULES!!

  4. Add tag: namebam-static-site-sg.

  5. Click Create security group. create-security-group-pt3 create-security-group-complete


🖥️ EC2 Instance

  1. Go to Instances

    • Click the orange button Launch instances instance-create

    • Name: bam-flask-api

    • AMI: Amazon Linux 2023 (default) instance-setup-pt1

    • Instance type: default (t3.micro is fine) instance-setup-pt2

  2. Key Pair (NOTE: You will need a key pair to SSH into your EC2 instance)

    • Click Create new key pair
    • Key pair name: bam-flask-api
    • Key pair type: RSA
    • Private Key File format: .pem
    • Click Create key pair create-key-pair
  3. Network settings

    • Firewall (security group):
    • Click Select existing security group
    • Choose bam-static-site-sg
  4. Configure storage: leave as default

  5. Click Launch instance. instance-setup-pt3 instance-complete


💻 Deploy Script in EC2 Terminal Window

  • Go to your EC2 Instance Summary
  • Click the Connect button on the top right side. connect-to-instance1
  • Leave everything as default on this page and click the Connect button on the bottom right of the page. connect-to-instance2

Copy and paste the whole thing into your EC2 Instance Connect terminal (and press ENTER):

# ========= Config =========
APP_USER="ec2-user"                        # OS user that runs the app
APP_DIR="/home/${APP_USER}/bam-flask"      # App directory
SERVICE_NAME="bam-flask"
GUNICORN_BIND="127.0.0.1:8000"             # Gunicorn bind address
ALLOWED_ORIGIN="*"                         # For production, set to your S3 Website URL
NGINX_SITE="/etc/nginx/conf.d/${SERVICE_NAME}.conf"
# ==========================

# System update + core packages
sudo dnf -y update
sudo dnf -y install nginx python3-pip python3-virtualenv

# Create/own app directory
sudo mkdir -p "${APP_DIR}"
sudo chown -R "${APP_USER}:${APP_USER}" "${APP_DIR}"

# -----------requirements.txt -----------------
cat > "${APP_DIR}/requirements.txt" <<'REQS'
flask==3.0.3
gunicorn==22.0.0
flask-cors==4.0.1
REQS
# ---------------------------------------------

# ------------------ app.py -------------------
cat > "${APP_DIR}/app.py" <<'PY'
from flask import Flask, jsonify, request
from flask_cors import CORS
import os

app = Flask(__name__)

allowed = os.environ.get("ALLOWED_ORIGIN", "*").strip()
if not allowed or allowed == "*":
    CORS(app, resources={r"/api/*": {"origins": "*"}})
else:
    CORS(app, resources={r"/api/*": {"origins": [o.strip() for o in allowed.split(",") if o.strip()]}})

@app.route("/api/health", methods=["GET"])
def health():
    return jsonify(status="ok", service="bam-flask-api")

@app.route("/api/echo", methods=["POST"])
def echo():
    data = request.get_json(silent=True) or {}
    return jsonify(ok=True, you_sent=data)

@app.route("/api/fun", methods=["GET"])
def fun():
    nums = request.args.get("nums", "2,4,6,9")
    arr = [float(x) for x in nums.split(",")] if nums else []
    return jsonify(count=len(arr), sum=sum(arr), avg=(sum(arr)/len(arr) if arr else None))
PY
# ---------------------------------------------

# ------------------ wsgi.py ------------------
cat > "${APP_DIR}/wsgi.py" <<'PY'
from app import app
if __name__ == "__main__":
    app.run()
PY
# ---------------------------------------------

# Python venv + dependents
sudo -u "${APP_USER}" bash -lc "python3 -m venv ${APP_DIR}/.venv"
sudo -u "${APP_USER}" bash -lc "${APP_DIR}/.venv/bin/pip install --upgrade pip"
sudo -u "${APP_USER}" bash -lc "${APP_DIR}/.venv/bin/pip install -r ${APP_DIR}/requirements.txt"

# systemd service for Gunicorn
sudo tee "/etc/systemd/system/${SERVICE_NAME}.service" > /dev/null <<EOF
[Unit]
Description=BAM Flask API (gunicorn)
Wants=network-online.target
After=network-online.target

[Service]
User=${APP_USER}
WorkingDirectory=${APP_DIR}
Environment=PATH=${APP_DIR}/.venv/bin
Environment=ALLOWED_ORIGIN=${ALLOWED_ORIGIN}
ExecStart=${APP_DIR}/.venv/bin/gunicorn --workers 2 --timeout 60 --bind ${GUNICORN_BIND} wsgi:app
Restart=always
RestartSec=2

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now "${SERVICE_NAME}"

# Nginx reverse proxy :80 -> Gunicorn :8000
if [ -f /etc/nginx/conf.d/default.conf ]; then sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak; fi
if [ -f /etc/nginx/conf.d/ssl.conf ]; then sudo mv /etc/nginx/conf.d/ssl.conf /etc/nginx/conf.d/ssl.conf.bak; fi

sudo tee "${NGINX_SITE}" > /dev/null <<EOF
server {
    listen 80 default_server;
    server_name _;

    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;

    location / {
        proxy_pass http://${GUNICORN_BIND}/;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_http_version 1.1;
    }
}
EOF

sudo nginx -t
sudo systemctl enable --now nginx
sudo systemctl reload nginx || true

# Helpful hint
# I'm not sure if this portion is actually needed but I left it in there just in case
EC2_DNS=$(curl -s http://169.254.169.254/latest/meta-data/public-hostname || true)
echo "All set."
[ -n "$EC2_DNS" ] && echo "Test from your laptop:  curl -i http://${EC2_DNS}/api/health"
echo "For production CORS, edit ALLOWED_ORIGIN in /etc/systemd/system/${SERVICE_NAME}.service then:"
echo "sudo systemctl daemon-reload && sudo systemctl restart ${SERVICE_NAME}"

ssh-to-ec2-instance-with-script1 ssh-to-ec2-instance-with-script2 ssh-to-ec2-instance-with-script3 ssh-to-ec2-instance-with-script4 ssh-to-ec2-instance-with-script5 ssh-to-ec2-instance-with-script6 ssh-to-ec2-instance-with-script7


✅ Verification of EC2 Instance

We will test the EC2 API Base URL to see if the Flask App backend components are working correctly before we present the S3 Static Website.

  1. Go to EC2 Instance Summary, copy the Public DNS.

  2. Test the EC2 Instance API endpoints: with health and fun?nums=1,2,3,4,5,6,8

    http://<EC2_PUBLIC_DNS>/api/health
    http://<EC2_PUBLIC_DNS>/api/fun?nums=1,2,3,4,5,6,8
    

    ec2-api-endpoints

  3. Open a new browser tab and go to Avengers themed custom S3 Static Bucket Endpoint Website to present your Flask API components:

    http://bam-static-site-tiqs.s3-website-us-east-1.amazonaws.com/
    
    • Wait for it to fully load. s3-static-website-pt1

    • In the Configure box next to Captain America, type your EC2 Base API URL:

      http://<EC2_PUBLIC_DNS>/
      
    • Click Save

      s3-static-website-pt2

    • Scroll Down and click GET API Health button next to Iron Man. It should read as:

      {
        "service": "bam-flask-api",
        "status": "ok"
      }

      s3-static-website-pt3

    • Scroll Down to Echo box and type class-7-rocks in the top box next to Thor.

    • Click the POST API Echo button. It should read as:

      {
        "ok": true,
        "you_sent": {
          "text": "class-7-rocks"
        }
      }

      s3-static-website-pt4

    • Scroll Down to the Fun Stats box and modify the numbers (i.e. 1,2,3,4,5,6,7,8) next to Hulk.

    • Click the GET Fun Stats button. It should read as:

      {
        "avg": 4.5,
        "count": 8,
        "sum": 36
      }

      s3-static-website-pt5


🔧 Teardown

  1. Terminate your EC2 instance. terminate-instance1 terminate-instance2 terminate-instance3

  2. Delete your Security Group. delete-security-group1 delete-security-group2 delete-security-group3

  3. Delete objects in your S3 bucket. delete-s3-objects1 delete-s3-objects2 delete-s3-objects3

  4. Empty Your S3 Bucket. delete-s3-trash1 delete-s3-trash2 delete-s3-trash3

  5. Delete your S3 bucket. delete-s3-bucket1 delete-s3-bucket2 delete-s3-bucket3


🔧 Troubleshooting

Issue Fix
Nginx shows default 404 page Remove or disable /etc/nginx/conf.d/default.conf and reload Nginx.
CORS errors in browser Ensure ALLOWED_ORIGIN in systemd service matches your S3 website endpoint【133†source】.
Gunicorn not starting Check logs with: journalctl -u bam-flask -e
API unreachable externally Verify EC2 Security Group allows inbound on port 80.
S3 site shows AccessDenied Ensure bucket policy allows s3:GetObject for *.

✍️ Authors & Acknowledgments

  • Author: T.I.Q.S.
  • Group Leader: John Sweeney

About

avengers-be-a-man-project

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages