AhmedZedan/ssl-demo

★ 0Forks 0ShellGitHub ↗Compare

README

SSL Demo — Hands-On Project

A complete local HTTPS setup from scratch. You will generate your own CA, sign a certificate, and serve a secure app over Docker.

Browser → https://localhost:443 → Nginx (SSL) → http://app:3000 → Node.js

Project Structure

ssl-demo/
├── app/
│   ├── index.js          ← Simple Node.js web app (plain HTTP, port 3000)
│   └── Dockerfile
├── nginx/
│   └── nginx.conf        ← SSL termination + proxy to app
├── certs/                ← Generated by generate-certs.sh (do not commit)
├── generate-certs.sh     ← Creates local CA + server certificate
├── docker-compose.yml    ← Wires everything together
└── README.md

How SSL Works Here

[Your local Root CA]
    ca.key  → used to sign
    ca.crt  → you trust this in your browser

[Server certificate]
    server.key  → private key (nginx holds this)
    server.crt  → signed by ca.crt (nginx sends this to browser)

The browser trusts ca.crt → verifies server.crt → padlock appears. This is exactly how DigiCert/Let's Encrypt works, just with your own CA.


Step 1 — Generate Certificates

chmod +x generate-certs.sh
./generate-certs.sh

This creates 6 files in ./certs/:

File What it is
ca.key Root CA private key — keep secret
ca.crt Root CA certificate — import this into your browser
server.key Server private key — used by nginx
server.csr Certificate signing request
server.crt Server certificate — signed by your CA
server.ext SAN extensions (required by modern browsers)

Step 2 — Trust Your Local CA

You need to tell your browser to trust ca.crt. This replaces what normally happens when DigiCert gets added to Chrome's trust store.

Chrome / Chromium

  1. Open chrome://settings/certificates
  2. Go to the Authorities tab → click Import
  3. Select certs/ca.crt
  4. Check "Trust this certificate for identifying websites"
  5. Click OK

Firefox

  1. Open about:preferences#privacy
  2. Scroll to Certificates → click View Certificates
  3. Go to the Authorities tab → click Import
  4. Select certs/ca.crt
  5. Check "Trust this CA to identify websites"

Ubuntu system-wide (also affects curl, wget)

sudo cp certs/ca.crt /usr/local/share/ca-certificates/localdev-ca.crt
sudo update-ca-certificates

Step 3 — Start the App

docker compose up --build

You will see:

ssl-demo-app    | App running on port 3000 (internal)
ssl-demo-nginx  | ... nginx started

Step 4 — Open in Browser

https://localhost

You should see the padlock icon and the SSL Demo App page.

If you see a warning instead: your CA was not imported correctly. Repeat Step 2.


Step 5 — Inspect the Certificate

In Chrome, click the padlock → Connection is secure → Certificate is valid

You will see:

  • Issued to: localhost
  • Issued by: LocalDev Root CA
  • Valid from / to: 1 year

This is your certificate — the one generate-certs.sh created.


What Each File Does at Runtime

Browser connects to port 443
         │
         ▼
    nginx receives request
    nginx reads  server.crt  (sends to browser as proof of identity)
    nginx reads  server.key  (uses to complete TLS handshake)
         │
    Browser checks server.crt:
    - Domain = localhost? ✓
    - Not expired? ✓
    - Signed by a trusted CA? ✓  (because you imported ca.crt)
         │
    TLS handshake complete — session key established
         │
    nginx decrypts request → proxies to http://app:3000
         │
    Node.js handles plain HTTP and returns the HTML page
         │
    nginx encrypts response → sends to browser
         │
    🔒 Padlock appears

Verify Certificate from Terminal

# Check the certificate details
openssl x509 -in certs/server.crt -text -noout

# Verify the chain: server.crt was signed by ca.crt
openssl verify -CAfile certs/ca.crt certs/server.crt

# Test the live HTTPS connection
curl -v --cacert certs/ca.crt https://localhost

# See the full TLS handshake details
openssl s_client -connect localhost:443 -CAfile certs/ca.crt

Stop the App

docker compose down

Cleaning Up

# Remove containers and network
docker compose down

# Remove generated certificates (to start fresh)
rm -rf certs/

Contributors

cs-ai-ahmed-zedan

Issues