Multi-Service Linux Server Build
Objective
Configure a single Ubuntu Server 22.04 LTS virtual machine to simultaneously host three critical network services: an OpenSSH server for secure remote administration, an Apache2 HTTP server for web content delivery, and a BIND9 DNS server for name resolution. The assignment required full deployment, firewall configuration with UFW, and end-to-end service verification from a separate client machine.
Tools & Technologies
Ubuntu Server 22.04 LTS— base operating systemOpenSSH Server— secure shell remote accessApache2— HTTP web serverBIND9— Berkeley Internet Name Domain DNS serverUFW— Uncomplicated Firewall for port managementsystemctl— service lifecycle managementdig / nslookup— DNS resolution verificationcurl / wget— HTTP connectivity testingVirtualBox— virtualization platform with host-only networking
Architecture Overview
Step-by-Step Process
Updated the system, installed OpenSSH, hardened the SSH daemon configuration, and set up key-based authentication for the admin user.
sudo apt update && sudo apt upgrade -y
sudo apt install -y openssh-server
# Harden SSH config
sudo nano /etc/ssh/sshd_config
# Key settings:
# PermitRootLogin no
# PasswordAuthentication no
# PubkeyAuthentication yes
# Port 22
# AllowUsers labadmin
# Generate key pair on client, copy public key to server
ssh-keygen -t ed25519 -C "lab-admin-key"
ssh-copy-id [email protected]
sudo systemctl restart ssh
sudo systemctl enable ssh
sudo systemctl status ssh
Installed Apache2, created a custom virtual host for the lab domain, deployed a test HTML page, and enabled necessary modules.
sudo apt install -y apache2
sudo systemctl enable apache2
# Create virtual host config
sudo nano /etc/apache2/sites-available/lab.local.conf
<VirtualHost *:80>
ServerName lab.local
ServerAlias www.lab.local
DocumentRoot /var/www/lab.local
ErrorLog ${APACHE_LOG_DIR}/lab-error.log
CustomLog ${APACHE_LOG_DIR}/lab-access.log combined
</VirtualHost>
sudo mkdir -p /var/www/lab.local
echo "<h1>Lab Server Online</h1>" | sudo tee /var/www/lab.local/index.html
sudo a2ensite lab.local.conf
sudo a2dissite 000-default.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
Installed BIND9, created a forward zone for lab.local, added A records for the server hostname and service aliases, and configured the server to listen on its static IP.
sudo apt install -y bind9 bind9utils bind9-doc
# Edit named.conf.local to add zone
sudo nano /etc/bind/named.conf.local
zone "lab.local" {
type master;
file "/etc/bind/zones/db.lab.local";
};
sudo mkdir -p /etc/bind/zones
sudo cp /etc/bind/db.local /etc/bind/zones/db.lab.local
sudo nano /etc/bind/zones/db.lab.local
; db.lab.local
$TTL 604800
@ IN SOA ns1.lab.local. admin.lab.local. (
2025010101 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative TTL
@ IN NS ns1.lab.local.
ns1 IN A 192.168.56.20
server IN A 192.168.56.20
www IN A 192.168.56.20
sudo named-checkconf
sudo named-checkzone lab.local /etc/bind/zones/db.lab.local
sudo systemctl enable bind9
sudo systemctl restart bind9
Configured UFW to allow only the required service ports and denied all other inbound traffic by default.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 53/tcp comment 'DNS TCP'
sudo ufw allow 53/udp comment 'DNS UDP'
sudo ufw enable
sudo ufw status verbose
Tested all three services from the client machine, confirming SSH login, HTTP response, and DNS resolution all functioned correctly.
# SSH test
ssh [email protected] "hostname && uptime"
# HTTP test (point DNS at server first)
curl -H "Host: lab.local" http://192.168.56.20/
# Expected: <h1>Lab Server Online</h1>
# DNS test
dig @192.168.56.20 www.lab.local A
nslookup server.lab.local 192.168.56.20
# Service status summary
ssh [email protected] "systemctl is-active ssh apache2 bind9"
Complete Workflow
Challenges & Solutions
- BIND9 refused to start — Zone file had a syntax error (missing trailing dot on SOA record). Fixed with
named-checkzonewhich pinpointed the exact line. - Apache virtual host not serving content — Default site was still enabled and overriding the lab.local vhost. Resolved with
a2dissite 000-default.conf. - DNS queries timing out through UFW — Initially only opened port 53/tcp. DNS primarily uses UDP; added
ufw allow 53/udpto fix. - SSH key authentication failing — Permissions on
~/.ssh/authorized_keyswere world-readable. Fixed withchmod 600 ~/.ssh/authorized_keys.
Key Takeaways
- Running multiple services on one server requires careful firewall management — open only required ports per protocol (TCP vs UDP matters for DNS).
- Always use
named-checkconfandnamed-checkzonebefore restarting BIND9 — cryptic startup errors are almost always zone file syntax issues. - Key-based SSH authentication with
PasswordAuthentication nois mandatory for any production or lab system exposed beyond localhost. - Apache virtual hosts must be explicitly enabled with
a2ensiteand the default site disabled to avoid routing conflicts. - Methodical service-by-service verification from a client machine is the only reliable way to confirm a multi-service deployment works end-to-end.