Linux Web Server Setup

196 views
techtech.linux

Debian Web Server Setup Guide

A comprehensive guide for setting up and hardening a new Debian/Ubuntu web server.

Table of Contents


Prerequisites

  • Fresh Debian or Ubuntu server installation
  • Root access or sudo privileges
  • SSH access to the server
  • Basic familiarity with Linux command line

Initial Setup

System Updates

Always update the system before installing new packages:

sudo apt update sudo apt upgrade -y sudo apt autoremove -y

Timezone Configuration

Set your server's timezone:

sudo timedatectl set-timezone America/New_York

List available timezones:

timedatectl list-timezones

User Management

Add new user:

sudo useradd -m -s /bin/bash USERNAME sudo passwd USERNAME

Add user to sudo group:

sudo usermod -aG sudo USERNAME

Copy SSH keys to new user:

Before disabling root login, ensure your new user can SSH in:

sudo mkdir -p /home/USERNAME/.ssh sudo cp ~/.ssh/authorized_keys /home/USERNAME/.ssh/ sudo chown -R USERNAME:USERNAME /home/USERNAME/.ssh sudo chmod 700 /home/USERNAME/.ssh sudo chmod 600 /home/USERNAME/.ssh/authorized_keys

SSH Hardening

Change SSH port and disable root login:

sudo nano /etc/ssh/sshd_config

Update the following settings:

Port 2222 PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes

Important: Make sure you've set up SSH keys and tested login with your new user before applying these changes.

Restart SSH service:

sudo systemctl restart sshd

Hostname Configuration

Change hostname:

sudo nano /etc/hostname sudo nano /etc/hosts

Update /etc/hosts to include your new hostname:

127.0.0.1       localhost
127.0.1.1       your-hostname

# IPv6 entries
::1             localhost ip6-localhost ip6-loopback

Apply changes:

sudo hostnamectl set-hostname your-hostname

Security

Uncomplicated Firewall

Install UFW:

sudo apt install ufw

Setup defaults:

sudo ufw default deny incoming sudo ufw default allow outgoing

Allow SSH (use your custom port if changed):

sudo ufw allow 2222/tcp sudo ufw status verbose

Important: If you changed your SSH port, make sure to allow that port instead of 22.

Allow web traffic:

sudo ufw allow 'Nginx Full'

Or manually:

sudo ufw allow 80/tcp sudo ufw allow 443/tcp

Enable UFW:

sudo ufw enable

Important: Make sure you've allowed your SSH port before enabling UFW to avoid being locked out.

Other useful UFW commands:

View rules with numbers:

sudo ufw status numbered

Delete a rule:

sudo ufw delete NUMBER

Allow port ranges:

sudo ufw allow 1111:1115/tcp sudo ufw allow 1116:1119/udp

Disable UFW:

sudo ufw disable

Fail2ban

Fail2ban protects against brute-force attacks by banning IPs that show malicious signs.

Install fail2ban:

sudo apt install fail2ban

Review default configuration:

sudo nano /etc/fail2ban/jail.conf

Don't edit this file directly. Instead, create a local override:

Create local configuration:

sudo nano /etc/fail2ban/jail.local

Add the following configuration:

[DEFAULT] bantime = 3600 findtime = 600 maxretry = 4 destemail = your-email@example.com sendername = Fail2Ban action = %(action_mw)s [sshd] enabled = true port = 2222 logpath = %(sshd_log)s backend = %(sshd_backend)s [nginx-http-auth] enabled = true filter = nginx-http-auth port = http,https logpath = /var/log/nginx/error.log [nginx-noscript] enabled = true port = http,https filter = nginx-noscript logpath = /var/log/nginx/access.log [nginx-badbots] enabled = true port = http,https filter = nginx-badbots logpath = /var/log/nginx/access.log

Start and enable fail2ban:

sudo systemctl start fail2ban sudo systemctl enable fail2ban

Reload fail2ban after config changes:

sudo fail2ban-client reload

Check fail2ban status:

sudo fail2ban-client status sudo fail2ban-client status sshd

Unban an IP address:

sudo fail2ban-client set sshd unbanip IP_ADDRESS

Automatic Security Updates

Configure unattended upgrades for security patches:

sudo apt install unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades

Edit the configuration:

sudo nano /etc/apt/apt.conf.d/50unattended-upgrades

Enable automatic updates:

sudo systemctl enable unattended-upgrades sudo systemctl start unattended-upgrades

Web Server

Nginx

Install Nginx:

sudo apt install nginx

Start and enable Nginx:

sudo systemctl start nginx sudo systemctl enable nginx

Create a site configuration:

sudo nano /etc/nginx/sites-available/example.com.conf

Basic configuration example:

server { listen 80; listen [::]:80; server_name example.com www.example.com; root /var/www/example.com; index index.html index.htm; location / { try_files $uri $uri/ =404; } }

Enable a site configuration:

sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/

Disable the default site:

sudo rm /etc/nginx/sites-enabled/default

Test Nginx configuration:

sudo nginx -t

Reload Nginx:

sudo systemctl reload nginx

Restart Nginx:

sudo systemctl restart nginx

Check Nginx status:

sudo systemctl status nginx

SSL Certificates

Official documentation: Certbot Instructions

Obtain SSL certificate:

For automatic Nginx configuration:

sudo certbot --nginx -d example.com -d www.example.com

Or use certonly mode (manual configuration):

sudo certbot certonly --nginx -d example.com -d www.example.com

Test auto-renewal:

Certbot installed via snap automatically sets up renewal. Test it:

sudo certbot renew --dry-run

Verify auto-renewal timer:

Check that the snap timer is active:

sudo snap list certbot sudo systemctl list-timers | grep certbot

The snap installation automatically handles renewal twice daily.

Check certificate expiration:

sudo certbot certificates

Renew certificates manually (if needed):

sudo certbot renew

Development Tools

Git

Install Git:

sudo apt install git

Configure Git:

git config --global user.name "Your Name" git config --global user.email "your.email@example.com"

Setup GitHub SSH:

Generate SSH key:

ssh-keygen -t ed25519 -C "your_email@example.com"

Start SSH agent:

eval "$(ssh-agent -s)"

Create or edit SSH config:

nano ~/.ssh/config

Add:

Host github.com User git Hostname github.com PreferredAuthentications publickey IdentityFile ~/.ssh/id_ed25519

Add SSH key to agent:

ssh-add ~/.ssh/id_ed25519

Display public key (add this to GitHub):

cat ~/.ssh/id_ed25519.pub

Test connection:

ssh -T git@github.com

Node Version Manager

Official repository: nvm-sh/nvm

Install build dependencies:

sudo apt update sudo apt install build-essential libssl-dev

Install NVM:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

Or using wget:

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash

Load NVM:

Close and reopen your terminal, or run:

export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"

List available Node versions:

nvm ls-remote

Install latest LTS version:

nvm install --lts nvm use --lts

Install specific version:

nvm install 20.10.0 nvm use 20.10.0

Set default Node version:

nvm alias default 20.10.0

List installed versions:

nvm ls

Alternative: Consider fnm for a faster, Rust-based Node version manager.

Docker

Official documentation: Docker Engine Install

Install Docker:

sudo apt update sudo apt install ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc

Add Docker repository:

echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker packages:

sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Add user to docker group:

This allows running Docker commands without sudo, which is necessary for cron jobs:

sudo usermod -aG docker $USER

Important: Log out and log back in for the group changes to take effect, or run:

newgrp docker

Verify Docker installation:

docker run hello-world

Enable Docker to start on boot:

sudo systemctl enable docker sudo systemctl enable containerd

Basic Docker commands:

List running containers:

docker ps

List all containers:

docker ps -a

View Docker images:

docker images

Stop a container:

docker stop CONTAINER_ID

Remove a container:

docker rm CONTAINER_ID

Docker Compose:

Docker Compose is included with the installation above. Verify:

docker compose version

Start services defined in docker-compose.yml:

docker compose up -d

Stop services:

docker compose down

Using Docker in cron jobs:

Since your user is in the docker group, you can run Docker commands in cron jobs:

crontab -e

Example cron job:

0 2 * * * /usr/bin/docker exec my-container /path/to/backup-script.sh

Note: Use full paths in cron jobs (/usr/bin/docker instead of just docker).


Process Management

PM2

PM2 is a production process manager for Node.js applications.

Install PM2 globally:

npm install -g pm2

Start an application:

pm2 start app.js --name myapp

List applications:

pm2 list

Monitor applications:

pm2 monit

View logs:

pm2 logs pm2 logs myapp

Restart application:

pm2 restart myapp

Stop application:

pm2 stop myapp

Setup PM2 startup script:

pm2 startup systemd

Run the command that PM2 outputs, then:

pm2 save

Configuration file example:

Create ecosystem.config.js:

module.exports = { apps: [ { name: 'myapp', script: './app.js', instances: 'max', exec_mode: 'cluster', env: { NODE_ENV: 'production', PORT: 3000, }, }, ], };

Start with config:

pm2 start ecosystem.config.js

Monitoring & Utilities

Essential tools:

sudo apt install htop ncdu net-tools curl wget
  • htop: Interactive process viewer
  • ncdu: Disk usage analyzer
  • net-tools: Network tools (netstat, ifconfig)

Check disk usage:

df -h ncdu /

Check memory usage:

free -h

Check running processes:

htop

View system logs:

sudo journalctl -xe sudo journalctl -u nginx

Check open ports:

sudo netstat -tulpn sudo ss -tulpn