Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 

Repository files navigation

πŸš€ HNG DevOps Internship β€” Stage 0

Stage Track Internship Server Nginx SSL OS

A production-ready Linux server setup with Nginx, SSL, and hardened security β€” built from scratch on AWS EC2.

Live Site Β· API Endpoint Β· HNG Internship


πŸ“‹ Table of Contents


🌍 Overview

This project fulfills the HNG Internship 14 β€” DevOps Track Stage 0 requirements. It involves provisioning a bare Linux server on AWS EC2, configuring Nginx to serve a static homepage and a JSON API endpoint, securing everything with a valid Let's Encrypt SSL certificate, and hardening the server with UFW firewall rules and SSH security best practices.

No Docker. No Compose. No automation tools β€” just a bare Linux server and raw configuration.


πŸ“Œ Requirements

Requirement Implementation
Linux Server AWS EC2 β€” Ubuntu 22.04 LTS (t2.micro)
Non-root sudo user hngdevops with sudo privileges
Passwordless sudo (restricted) /usr/sbin/sshd and /usr/sbin/ufw only
Disable root SSH login PermitRootLogin no in sshd_config
Key-based SSH only PasswordAuthentication no in sshd_config
Firewall UFW β€” ports 22, 80, 443 only
Web Server Nginx
Static Homepage (GET /) Serves HTML with HNG username visible
JSON API (GET /api) Returns { message, track, username }
SSL Certificate Let's Encrypt via Certbot
HTTP β†’ HTTPS Redirect 301 Permanent Redirect

πŸ—οΈ Architecture

Internet
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        AWS EC2 Instance      β”‚
β”‚       Ubuntu 22.04 LTS       β”‚
β”‚                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚        UFW            β”‚  β”‚
β”‚  β”‚  22 βœ… 80 βœ… 443 βœ…   β”‚  β”‚
β”‚  β”‚  All others ❌        β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚             β”‚               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚        Nginx           β”‚  β”‚
β”‚  β”‚                       β”‚  β”‚
β”‚  β”‚  :80  β†’ 301 β†’ :443   β”‚  β”‚
β”‚  β”‚  :443 /     β†’ HTML   β”‚  β”‚
β”‚  β”‚  :443 /api  β†’ JSON   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚             β”‚               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚   Let's Encrypt SSL   β”‚  β”‚
β”‚  β”‚   (Auto-renewing)     β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
  DuckDNS Domain
  emmanuel-kabari.duckdns.org

πŸ› οΈ Step-by-Step Setup

Phase 1: AWS EC2 Provisioning

  1. Log in to AWS Console
  2. Navigate to EC2 β†’ Instances β†’ Launch Instances
  3. Configure the instance:
Name:          hng-devops-stage0
AMI:           Ubuntu Server 22.04 LTS (Free Tier)
Instance Type: t2.micro
Key Pair:      Create new β†’ hng-devops-key (RSA, .pem)
  1. Configure Security Group inbound rules:
Type Protocol Port Source
SSH TCP 22 Anywhere (0.0.0.0/0)
HTTP TCP 80 Anywhere (0.0.0.0/0)
HTTPS TCP 443 Anywhere (0.0.0.0/0)
  1. Click Launch Instance

  2. Allocate an Elastic IP for a permanent public IP:

    • EC2 β†’ Network & Security β†’ Elastic IPs β†’ Allocate
    • Actions β†’ Associate β†’ select your instance
    • Click Associate

Phase 2: Domain Setup with DuckDNS

  1. Go to duckdns.org and log in
  2. Create a subdomain (e.g. hng-yourname)
  3. Enter your AWS Elastic IP in the IP field
  4. Click Update IP

Verify DNS propagation:

nslookup hng-yourname.duckdns.org
# Should return your Elastic IP

Phase 3: Connect to Server

# Fix key permissions
chmod 400 ~/Downloads/hng-devops-key.pem

# SSH into server
ssh -i ~/Downloads/hng-devops-key.pem ubuntu@YOUR_ELASTIC_IP

Phase 4: Create hngdevops User

# Create user
sudo adduser hngdevops

# Add to sudo group
sudo usermod -aG sudo hngdevops

# Configure restricted passwordless sudo
sudo visudo -f /etc/sudoers.d/hngdevops

Add exactly this line:

hngdevops ALL=(root) NOPASSWD:/usr/sbin/sshd,/usr/sbin/ufw

Verify syntax:

sudo visudo -c -f /etc/sudoers.d/hngdevops
# Expected: parsed ok

Phase 5: SSH Key Setup

On your local machine:

# Generate SSH key pair
ssh-keygen -t ed25519 -C "hngdevops" -f ~/.ssh/hngdevops_key
# Press Enter twice (no passphrase)

# View public key (copy this)
cat ~/.ssh/hngdevops_key.pub

On the server:

# Set up SSH directory for hngdevops
sudo mkdir -p /home/hngdevops/.ssh
sudo chmod 700 /home/hngdevops/.ssh
sudo touch /home/hngdevops/.ssh/authorized_keys
sudo chmod 600 /home/hngdevops/.ssh/authorized_keys
sudo chown -R hngdevops:hngdevops /home/hngdevops/.ssh

# Add your public key
sudo nano /home/hngdevops/.ssh/authorized_keys
# Paste your public key, save with Ctrl+X, Y, Enter

Test the new SSH login:

ssh -i ~/.ssh/hngdevops_key hngdevops@YOUR_ELASTIC_IP
# Expected: hngdevops@ip-xxx-xx-xx-xx:~$

Phase 6: Harden SSH

sudo nano /etc/ssh/sshd_config

Set the following values:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM no

Apply changes:

sudo systemctl restart sshd

⚠️ Always test SSH login in a new terminal before closing your current session.


Phase 7: Configure UFW Firewall

# Reset UFW
sudo ufw --force reset

# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow required ports only
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw --force enable

# Verify
sudo ufw status verbose

Expected output:

Status: active
22/tcp   ALLOW IN  Anywhere
80/tcp   ALLOW IN  Anywhere
443/tcp  ALLOW IN  Anywhere

Phase 8: Install Nginx

sudo apt update && sudo apt upgrade -y
sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx

Phase 9: Configure Nginx

Create the static HTML page:

sudo mkdir -p /var/www/hng
sudo nano /var/www/hng/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>HNG DevOps Stage 0</title>
</head>
<body>
    <h1>Emmanuel Kabari</h1>
    <p>HNG Internship 14 - DevOps Track</p>
    <p>Stage 0 Submission</p>
</body>
</html>

Create the Nginx server block:

sudo nano /etc/nginx/sites-available/hng
server {
    listen 80;
    listen [::]:80;
    server_name your-domain.duckdns.org;

    root /var/www/hng;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }

    location /api {
        default_type application/json;
        return 200 '{"message":"HNGI14 Stage 0","track":"DevOps","username":"your-hng-username"}';
    }
}

Enable and test:

sudo ln -s /etc/nginx/sites-available/hng /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Phase 10: SSL with Certbot

# Install Certbot
sudo apt install certbot python3-certbot-nginx -y

# Obtain SSL certificate
sudo certbot --nginx -d your-domain.duckdns.org

# Test auto-renewal
sudo certbot renew --dry-run

Certbot automatically:

  • Installs the SSL certificate
  • Updates Nginx config with SSL settings
  • Sets up a 301 redirect from HTTP to HTTPS
  • Schedules auto-renewal

πŸ”— Endpoints

GET / β€” Static Homepage

https://emmanuel-kabari.duckdns.org/

Returns an HTML page with the HNG username visibly displayed.


GET /api β€” JSON API

https://emmanuel-kabari.duckdns.org/api

Response:

{
  "message": "HNGI14 Stage 0",
  "track": "DevOps",
  "username": "Emmanuel Kabari"
}

Headers:

Content-Type: application/json
HTTP Status: 200 OK

πŸ”’ Security Checklist

Check Status
Root SSH login disabled βœ… PermitRootLogin no
Password SSH auth disabled βœ… PasswordAuthentication no
Key-based SSH only βœ… PubkeyAuthentication yes
UFW active βœ… Enabled on startup
Only ports 22, 80, 443 open βœ… All others denied
HTTP β†’ HTTPS 301 redirect βœ… Configured by Certbot
Valid SSL certificate βœ… Let's Encrypt
Auto SSL renewal βœ… Certbot cron job
hngdevops passwordless sudo (restricted) βœ… sshd and ufw only
Non-root deployment user βœ… hngdevops

βœ… Verification

Run these commands to verify the full setup:

# 1. HTTP redirects to HTTPS with 301
curl -I http://your-domain.duckdns.org

# 2. HTTPS homepage returns 200
curl -I https://your-domain.duckdns.org

# 3. API returns correct JSON
curl https://your-domain.duckdns.org/api

# 4. API Content-Type is application/json
curl -I https://your-domain.duckdns.org/api

# 5. UFW is active
sudo ufw status verbose

# 6. Nginx is running
sudo systemctl status nginx

# 7. Passwordless sudo works
sudo -u hngdevops sudo /usr/sbin/sshd -T | head -5
sudo -u hngdevops sudo /usr/sbin/ufw status

πŸ“ Project Structure

/
β”œβ”€β”€ /etc/nginx/
β”‚   └── sites-available/
β”‚       └── hng                  # Nginx server block config
β”œβ”€β”€ /var/www/hng/
β”‚   └── index.html               # Static homepage
β”œβ”€β”€ /etc/letsencrypt/
β”‚   └── live/your-domain/
β”‚       β”œβ”€β”€ fullchain.pem        # SSL certificate
β”‚       └── privkey.pem          # SSL private key
β”œβ”€β”€ /etc/ssh/
β”‚   └── sshd_config              # Hardened SSH config
β”œβ”€β”€ /etc/sudoers.d/
β”‚   └── hngdevops                # Restricted passwordless sudo
└── /home/hngdevops/
    └── .ssh/
        └── authorized_keys      # SSH public keys

πŸ‘€ Author

Emmanuel Kabari HNG Internship 14 β€” DevOps Track

HNG Domain


Built with πŸ’» on AWS EC2 Β· Secured with πŸ”’ Let's Encrypt Β· Served by ⚑ Nginx

About

HNG Internship 14 - DevOps Track Stage 0: Linux server setup, Nginx configuration, SSL and firewall hardening on AWS EC2

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors