SSL/HTTPS Configuration

This guide explains how to enable HTTPS with Let’s Encrypt certificates for the University Inventory Management System using Nginx reverse proxy.

Overview

The HTTPS implementation provides:

  • SSL/TLS Termination: Nginx handles all certificate management

  • Automatic Certificate Renewal: Let’s Encrypt certificates auto-renew

  • Security Headers: Modern security practices with HSTS, CSP, etc.

  • Performance: HTTP/2, compression, and caching

  • Zero Downtime: No application code changes required

Architecture

Internet → Nginx (SSL Termination) → Express App (HTTP:5000)
             ↓
Let's Encrypt Certificates (Auto-renewal)

Components: - Nginx: Handles SSL certificates, HTTPS termination, security headers, rate limiting - Express App: Continues to run on HTTP internally (port 5000) - Let’s Encrypt: Provides free SSL certificates with automatic renewal - Certbot: Manages certificate lifecycle

Quick Start

The system supports three types of SSL certificates:

  • Let’s Encrypt: For public domains with automatic renewal

  • Self-signed: For development and internal testing

  • University-issued: For internal university domains requiring institutional certificates

Choose the appropriate method based on your deployment environment.

Local Development

For local development with self-signed certificates:

# Setup with self-signed certificates
make ssl-dev

# Or manually:
./scripts/setup-ssl.sh localhost.dev
docker-compose -f docker-compose.yml -f docker-compose.ssl.yml up -d

# Access at: https://localhost.dev

Production Deployment

For public domains with Let’s Encrypt:

# Set your domain and email
export DOMAIN="inventory.university.edu"
export EMAIL="admin@university.edu"

# Automated setup
make ssl-setup DOMAIN=$DOMAIN EMAIL=$EMAIL

# Start production services
make ssl-prod

# Or manually:
./scripts/setup-ssl.sh $DOMAIN $EMAIL
source .env.ssl
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

For internal university domains:

For domains like py-stores.lancaster.ac.uk, use university-issued certificates. See the University Certificate Authority section for the complete process.

# After obtaining university certificates
# Place certificates in ssl/university/ directory
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Staging Environment

For testing with Let’s Encrypt staging environment:

# Use staging certificates (higher rate limits)
export CERTBOT_STAGING=--staging
./scripts/setup-ssl.sh staging.inventory.university.edu admin@university.edu

# Start services
docker-compose -f docker-compose.yml -f docker-compose.ssl.yml up -d

Manual Setup

Prerequisites

  1. Domain Configuration: Point your domain’s DNS A record to your server’s IP

  2. Firewall: Ensure ports 80 and 443 are open

  3. Docker: Docker and docker-compose installed

  4. Server Access: Root or sudo access for certificate management

Step-by-Step Installation

  1. Initial HTTP Setup

    # Copy HTTP-only nginx config for initial setup
    cp nginx/nginx-http-only.conf nginx/nginx.conf
    
    # Start services without SSL first
    docker-compose up -d app db nginx
    
    # Verify app is accessible
    curl http://yourdomain.edu/health
    
  2. Create SSL Directories

    mkdir -p certbot/conf certbot/www logs/nginx logs/certbot
    
  3. Obtain SSL Certificates

    # Get Let's Encrypt certificates
    docker-compose run --rm certbot certonly \
      --webroot \
      --webroot-path=/var/www/certbot \
      --email admin@university.edu \
      --agree-tos \
      --no-eff-email \
      -d inventory.university.edu
    
  4. Configure HTTPS

    # Update nginx configuration with your domain
    sed "s/DOMAIN_PLACEHOLDER/inventory.university.edu/g" \
        nginx/nginx.conf > nginx/nginx.conf.tmp
    mv nginx/nginx.conf.tmp nginx/nginx.conf
    
    # Restart nginx with HTTPS configuration
    docker-compose restart nginx
    
  5. Enable Auto-Renewal

    # Start certbot service for automatic renewal
    docker-compose -f docker-compose.yml -f docker-compose.ssl.yml up -d
    

Configuration Files

Docker Compose Configuration

The SSL setup uses multiple Docker Compose files:

docker-compose.ssl.yml

SSL-enabled services for development/staging

docker-compose.prod.yml

Production configuration with SSL and monitoring

Services added:

nginx:
  image: nginx:alpine
  ports:
    - "80:80"
    - "443:443"
  volumes:
    - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
    - ./certbot/conf:/etc/letsencrypt:ro
    - ./certbot/www:/var/www/certbot:ro

certbot:
  image: certbot/certbot
  volumes:
    - ./certbot/conf:/etc/letsencrypt
    - ./certbot/www:/var/www/certbot
  entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"

Nginx Configuration

The Nginx configuration includes:

SSL/TLS Security: - TLS 1.2 and 1.3 protocols only - Strong cipher suites with Perfect Forward Secrecy - OCSP stapling for certificate validation - SSL session caching

Security Headers:

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Content-Security-Policy "default-src 'self'; ..." always;

Performance Features: - HTTP/2 support - Gzip compression (70% bandwidth reduction) - Static file caching (1-year expires) - Keep-alive connections

Rate Limiting: - API endpoints: 10 requests/second (20 burst) - Authentication: 5 requests/minute (5 burst)

Environment Variables

The setup creates environment variables:

# SSL Configuration
HTTPS=true                    # Enable HTTPS mode in Express
FORCE_HTTPS=true             # Force HTTPS redirects
DOMAIN=inventory.university.edu   # Your domain name
EMAIL=admin@university.edu   # Let's Encrypt contact email

Application Integration

The Express application automatically adapts to HTTPS with these settings:

// Session cookies become secure in production
cookie: {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production' &&
         (process.env.FORCE_HTTPS === 'true' || process.env.HTTPS === 'true'),
  sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax',
}

Management Commands

Makefile Commands

The included Makefile provides convenient SSL management:

# Setup SSL for specific domain
make ssl-setup DOMAIN=inventory.university.edu EMAIL=admin@university.edu

# Local development setup
make ssl-dev

# Start production with SSL
make ssl-prod

# Manually renew certificates
make ssl-renew

# Test SSL configuration
make ssl-test

# Clean SSL certificates
make ssl-clean

# View logs
make logs

Manual Certificate Management

Certificate Renewal:

# Manual renewal
docker-compose exec certbot certbot renew

# Test renewal (dry run)
docker-compose exec certbot certbot renew --dry-run

# Reload nginx after renewal
docker-compose reload nginx

Certificate Verification:

# Check certificate files
ls -la certbot/conf/live/inventory.university.edu/

# Verify certificate chain
openssl verify -CApath /etc/ssl/certs/ \
  certbot/conf/live/inventory.university.edu/fullchain.pem

# Test SSL connection
openssl s_client -connect inventory.university.edu:443 -servername inventory.university.edu

University Certificate Authority

For internal university domains (like py-stores.lancaster.ac.uk), using university-issued certificates provides better security and trust than self-signed certificates while avoiding Let’s Encrypt domain validation limitations.

When to Use University Certificates

Ideal for: - Internal university domains (*.university.edu) - VPN-only accessible systems - Campus network restricted services - Compliance requirements for institutional certificates

Benefits: - Trusted by university-managed devices - No browser security warnings - Better security than self-signed certificates - Institutional compliance and audit support

Certificate Request Process

Step 1: Contact IT Services

Contact your university’s IT services or certificate authority team:

Subject: SSL Certificate Request for [Your Domain]

Dear IT Services,

I am requesting an SSL certificate for our internal inventory management
system at [your-domain.university.edu].

Domain: your-domain.university.edu
Purpose: Internal inventory management system
Environment: Production
Access: VPN/Campus network only

Technical Contact: [Your Name] <[your-email]>
Department: [Your Department]

Please let me know what information you need to process this request.

Step 2: Provide Required Information

Typically required: - Domain name (e.g., py-stores.lancaster.ac.uk) - Server information (OS, web server software) - Certificate Signing Request (CSR) - Business justification - Technical contact information

Step 3: Generate Certificate Signing Request

# Create private key
openssl genrsa -out private.key 2048

# Generate CSR
openssl req -new -key private.key -out request.csr \
  -subj "/C=GB/ST=Lancashire/L=Lancaster/O=Lancaster University/OU=IT Services/CN=py-stores.lancaster.ac.uk"

# Verify CSR
openssl req -text -noout -verify -in request.csr

Step 4: Submit CSR to IT Services

Send the request.csr file to your IT services team along with any required forms.

Certificate Installation

Once you receive the certificate files from IT services:

Step 1: Organize Certificate Files

# Create certificate directory
mkdir -p ssl/university/

# Place received files
ssl/university/
├── certificate.crt       # Your domain certificate
├── intermediate.crt      # Intermediate CA certificate
├── ca-bundle.crt        # Root CA bundle
└── private.key          # Your private key (from CSR generation)

Step 2: Create Certificate Chain

# Combine certificates into full chain
cat ssl/university/certificate.crt \
    ssl/university/intermediate.crt \
    ssl/university/ca-bundle.crt > ssl/university/fullchain.pem

# Copy private key
cp ssl/university/private.key ssl/university/privkey.pem

Step 3: Update Docker Compose Configuration

# docker-compose.prod.yml
version: '3.8'
services:
  nginx:
    image: nginx:1.21-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl/university:/etc/ssl/certs:ro
      - ./logs/nginx:/var/log/nginx
    environment:
      - DOMAIN=py-stores.lancaster.ac.uk
      - SSL_CERT_PATH=/etc/ssl/certs/fullchain.pem
      - SSL_KEY_PATH=/etc/ssl/certs/privkey.pem

Step 4: Update Nginx Configuration

# nginx/nginx.conf
server {
    listen 443 ssl http2;
    server_name py-stores.lancaster.ac.uk;

    # University-issued certificates
    ssl_certificate /etc/ssl/certs/fullchain.pem;
    ssl_certificate_key /etc/ssl/certs/privkey.pem;

    # Enable OCSP stapling (university CAs usually support this)
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;

    # Modern SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Standard security headers
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;

    location / {
        proxy_pass http://app:5000;
        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;
    }
}

Certificate Validation

Verify Certificate Installation:

# Check certificate details
openssl x509 -in ssl/university/fullchain.pem -text -noout

# Verify certificate chain
openssl verify -CAfile ssl/university/ca-bundle.crt ssl/university/certificate.crt

# Test SSL connection
openssl s_client -connect py-stores.lancaster.ac.uk:443 -servername py-stores.lancaster.ac.uk

Expected Output: - Certificate should show your university as the issuer - Verification should return “OK” - SSL connection should establish without errors

Renewal Process

University certificates typically have different renewal procedures than Let’s Encrypt:

Renewal Timeline: - Most university certificates valid for 1-3 years - Renewal reminders usually sent 30-60 days before expiration - Plan renewal process 2-3 months in advance

Renewal Steps: 1. Generate new CSR (or reuse existing private key) 2. Submit renewal request to IT services 3. Install new certificate following same process 4. Test thoroughly before old certificate expires

Monitoring:

# Add to crontab for monthly certificate checks
0 0 1 * * /path/to/check-cert-expiry.sh py-stores.lancaster.ac.uk

Integration with Automatic Detection

The system’s nginx-startup.sh script automatically detects university certificates:

# Automatic detection logic
if [ -f "/etc/ssl/certs/fullchain.pem" ] && [ -f "/etc/ssl/certs/privkey.pem" ]; then
    echo "University certificates detected"
    # Configure nginx for university certificates with OCSP stapling
fi

This ensures seamless deployment regardless of certificate source.

Troubleshooting University Certificates

Common Issues:

  1. Certificate Chain Problems:

    # Verify complete chain
    openssl verify -CApath /etc/ssl/certs/ ssl/university/fullchain.pem
    
  2. OCSP Stapling Failures:

    # Test OCSP manually
    openssl x509 -in ssl/university/certificate.crt -noout -ocsp_uri
    
  3. Browser Trust Issues: - Ensure intermediate certificates are included - Check if university CA is trusted by browsers - May need to install university root CA on client machines

  4. Private Key Mismatch:

    # Verify certificate and key match
    openssl x509 -noout -modulus -in ssl/university/certificate.crt | openssl md5
    openssl rsa -noout -modulus -in ssl/university/private.key | openssl md5
    

Contact Information Template

For Lancaster University specifically:

IT Services Help Desk
Email: help@lancaster.ac.uk
Phone: +44 (0)1524 510000

SSL Certificate Request:
- Domain: py-stores.lancaster.ac.uk
- Service: Internal Inventory Management System
- Department: [Your Department]
- Justification: VPN-only internal system requiring trusted certificates

Monitoring and Maintenance

Health Checks

The system includes comprehensive health monitoring:

Nginx Health Check:

# Test nginx configuration
docker-compose exec nginx nginx -t

# Check nginx status
curl -I https://inventory.university.edu/health

SSL Certificate Monitoring:

# Check certificate expiration
echo | openssl s_client -servername inventory.university.edu \
  -connect inventory.university.edu:443 2>/dev/null | \
  openssl x509 -noout -dates

# SSL Labs test (external validation)
# Visit: https://www.ssllabs.com/ssltest/

Log Management

Logs are stored in organized directories:

logs/
├── nginx/
│   ├── access.log          # HTTP access logs
│   └── error.log           # Nginx error logs
├── certbot/                # Certificate management logs
└── app/                    # Application logs

Log Commands:

# View recent nginx logs
docker-compose logs nginx | tail -50

# Monitor access logs in real-time
docker-compose exec nginx tail -f /var/log/nginx/access.log

# Check for SSL errors
docker-compose exec nginx grep -i ssl /var/log/nginx/error.log

Performance Monitoring

Expected performance improvements with HTTPS:

  • SSL/TLS: Modern TLS 1.3 for faster handshakes

  • HTTP/2: Request multiplexing and server push

  • Compression: ~70% bandwidth reduction via Gzip

  • Caching: Static assets cached for 1 year

  • Keep-Alive: Persistent connections reduce overhead

Security Features

SSL/TLS Configuration

Protocol Security: - TLS 1.2 and 1.3 only (older versions disabled) - Perfect Forward Secrecy (PFS) cipher suites - Strong key exchange algorithms - No weak or legacy ciphers

Certificate Security: - OCSP stapling for real-time certificate validation - Certificate chain verification - Automatic renewal before expiration - Secure private key storage

HTTP Security Headers

Strict Transport Security (HSTS):

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

Forces HTTPS for 2 years, includes subdomains, eligible for browser preload lists.

Content Security Policy (CSP):

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; object-src 'none'; frame-ancestors 'none';" always;

Prevents XSS attacks by controlling resource loading.

Additional Headers: - X-Frame-Options: Prevents clickjacking - X-Content-Type-Options: Prevents MIME sniffing - X-XSS-Protection: Enables browser XSS filter - Referrer-Policy: Controls referrer information

Rate Limiting

Protection against abuse and DoS attacks:

# API endpoint protection
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;

# Authentication endpoint protection
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

Troubleshooting

Common Issues

1. Certificate Not Found

# Symptoms: Nginx fails to start, SSL errors
# Check certificate files exist
ls -la certbot/conf/live/yourdomain.edu/

# Verify nginx configuration
docker-compose exec nginx nginx -t

# Re-obtain certificates if missing
./scripts/setup-ssl.sh yourdomain.edu admin@yourdomain.edu

2. Domain Validation Failed

# Symptoms: Let's Encrypt cannot validate domain
# Check DNS resolution
nslookup inventory.university.edu

# Verify port 80 accessibility
curl http://inventory.university.edu/.well-known/acme-challenge/test

# Check firewall settings
sudo ufw status
sudo iptables -L

3. SSL Handshake Errors

# Test SSL configuration
openssl s_client -connect inventory.university.edu:443

# Check certificate chain
openssl verify -CApath /etc/ssl/certs/ \
  certbot/conf/live/inventory.university.edu/fullchain.pem

# Verify nginx SSL config
docker-compose exec nginx nginx -T | grep ssl

4. Mixed Content Warnings

# Symptoms: Browser shows "not secure" despite HTTPS
# Check for HTTP resources in HTTPS pages
# Update application to use HTTPS URLs
# Verify CSP headers allow HTTPS resources only

5. Certificate Renewal Failures

# Check certbot logs
docker-compose logs certbot

# Test renewal manually
docker-compose exec certbot certbot renew --dry-run

# Verify webroot permissions
ls -la certbot/www/

Recovery Procedures

Emergency HTTP Fallback:

# Switch to HTTP-only if SSL fails
cp nginx/nginx-http-only.conf nginx/nginx.conf
docker-compose restart nginx

# Re-setup SSL when ready
./scripts/setup-ssl.sh yourdomain.edu admin@yourdomain.edu

Certificate Reset:

# Complete SSL cleanup and restart
make ssl-clean
rm -rf certbot/conf/* certbot/www/*
./scripts/setup-ssl.sh yourdomain.edu admin@yourdomain.edu

Configuration Rollback:

# Restore from backup
git checkout nginx/nginx.conf
docker-compose restart nginx

Advanced Configuration

Load Balancer Integration

For deployment behind a load balancer (AWS ALB, Google Cloud Load Balancer):

  1. Configure SSL at the load balancer level

  2. Use HTTP-only nginx configuration

  3. Set environment variables:

    FORCE_HTTPS=false          # Avoid double redirects
    TRUST_PROXY=true          # Trust load balancer headers
    
  4. Update nginx configuration:

    # Remove SSL configuration, keep proxy settings
    real_ip_header X-Forwarded-For;
    set_real_ip_from 0.0.0.0/0;  # Trust load balancer
    

Multi-Domain Setup

For multiple domains or subdomains:

# Obtain certificates for multiple domains
docker-compose run --rm certbot certonly \
  --webroot \
  --webroot-path=/var/www/certbot \
  --email admin@university.edu \
  --agree-tos \
  -d inventory.university.edu \
  -d staging.inventory.university.edu \
  -d api.inventory.university.edu

Custom SSL Certificates

For custom or commercial SSL certificates:

  1. Place certificate files:

    mkdir -p ssl/custom/
    cp your-cert.pem ssl/custom/fullchain.pem
    cp your-private-key.pem ssl/custom/privkey.pem
    
  2. Update nginx configuration:

    ssl_certificate /etc/ssl/custom/fullchain.pem;
    ssl_certificate_key /etc/ssl/custom/privkey.pem;
    
  3. Mount in docker-compose:

    nginx:
      volumes:
        - ./ssl/custom:/etc/ssl/custom:ro
    

Production Checklist

Pre-Deployment

  • [ ] Domain DNS configured (A record points to server)

  • [ ] Firewall configured (ports 80, 443 open)

  • [ ] Server resources adequate (CPU, memory, disk)

  • [ ] Backup strategy in place

  • [ ] Monitoring systems configured

SSL Setup

  • [ ] SSL certificates obtained successfully

  • [ ] Nginx configuration tested (nginx -t)

  • [ ] HTTPS redirect working

  • [ ] Security headers present

  • [ ] SSL Labs test passes (A+ rating)

Post-Deployment

  • [ ] Application accessible via HTTPS

  • [ ] All features working correctly

  • [ ] Performance benchmarks met

  • [ ] Certificate auto-renewal configured

  • [ ] Monitoring alerts configured

  • [ ] Documentation updated

Security Validation

  • [ ] SSL/TLS configuration secure (no weak ciphers)

  • [ ] HTTP Strict Transport Security enabled

  • [ ] Content Security Policy configured

  • [ ] Rate limiting functional

  • [ ] No mixed content warnings

  • [ ] Penetration testing completed

Best Practices

Certificate Management

  • Regular Monitoring: Set up alerts for certificate expiration

  • Backup Certificates: Keep secure backups of private keys

  • Test Renewals: Regular dry-run testing of certificate renewal

  • Multiple Certificates: Use different certificates for different environments

Security Maintenance

  • Regular Updates: Keep Nginx, OpenSSL, and base images updated

  • Security Headers: Review and update security headers regularly

  • Cipher Suites: Update cipher suites as security standards evolve

  • Penetration Testing: Regular security assessments

Performance Optimization

  • HTTP/2: Ensure HTTP/2 is enabled and working

  • Compression: Monitor compression ratios and adjust

  • Caching: Review cache headers and performance

  • Connection Pooling: Optimize upstream connections

Conclusion

The SSL/HTTPS implementation provides enterprise-grade security for the University Inventory Management System with:

  • Zero-downtime deployment with no application code changes

  • Automatic certificate management via Let’s Encrypt

  • Industry-standard security with modern TLS and security headers

  • High performance with HTTP/2, compression, and caching

  • Comprehensive monitoring and alerting

The solution is designed for easy maintenance and scales from development through production environments.

For additional support, refer to: