Lab 2 – Managing multiple websites with a reverse proxy in a Linux environment
Table of Contents
- Context
- Objectives of the test
- General instructions
- Part 1 – Installation and configuration of Wiki.js
- Part 2 – Configuration of the NGINX reverse proxy
- Part 3 – Validation, troubleshooting, and analysis
- Part 4 – Architectural analysis question
- Evaluation grid
- Bonus Module 1 – System users vs normal users
CONTEXT
You are a system administrator in a digital services company. Your task is to configure a local environment hosting three independent instances of Wiki.js, each representing a classic software development environment:
- Development environment (
dev.site1.local) - Testing environment (
test.site1.local) - Production environment (
prod.site1.local)
Each instance of Wiki.js must be installed manually on a single Ubuntu Server 24.04 LTS machine, connected to its own MariaDB database and exposed via a NGINX reverse proxy.
OBJECTIVES OF THE TEST
- Master the manual installation of a Node.js-based wiki engine
- Properly manage distinct SQL databases
- Automate service startup via
systemd - Configure an HTTP reverse proxy with internal subdomains
- Diagnose common deployment errors
- Apply best practices in service isolation
GENERAL INSTRUCTIONS
- You are working on a single Ubuntu 24.04 virtual machine
- All instances must be independent, with no port conflicts
- No server should be exposed directly: only the NGINX proxy listens on port 80
- The addresses
*.site1.localmust work via/etc/hosts - You must follow a professional approach, with structure, clarity, and rigor
PART 1 – INSTALLATION AND CONFIGURATION OF WIKI.JS (40 POINTS)
-
Create three dedicated Linux users:
wikijs_dev,wikijs_test,wikijs_prod -
Create three distinct databases under MariaDB:
wikidb_dev,wikidb_test,wikidb_prod
-
Manually install Wiki.js in:
/var/www/dev.site1/var/www/test.site1/var/www/prod.site1
-
Modify the
config.ymlfile of each instance:- Different port (
3000,3001,3002) bindIP: 127.0.0.1- Correct database connection information
- Different port (
-
Create and activate the systemd services:
wikijs-dev.service,wikijs-test.service,wikijs-prod.service
PART 2 – CONFIGURATION OF THE NGINX REVERSE PROXY (30 POINTS)
- Configure NGINX to serve the following subdomains:
| Hostname | Internal Port | Local Path |
|---|---|---|
| dev.site1.local | 3000 | /var/www/dev.site1 |
| test.site1.local | 3001 | /var/www/test.site1 |
| prod.site1.local | 3002 | /var/www/prod.site1 |
-
Modify
/etc/hoststo point these domains to127.0.0.1 -
Test site access with a browser or
curl
PART 3 – VALIDATION, TROUBLESHOOTING, AND ANALYSIS (30 POINTS)
-
Correct the following errors if they appear:
- Bad YAML indentation
- MariaDB connection issues
- Port already in use
- Insufficient file permissions
-
Justify the following decisions:
- Why use different users for each Wiki.js?
- Why prefer a reverse proxy over direct exposure?
- What are the security risks if ports are exposed without NGINX?
-
Propose a method to secure the installation with HTTPS:
- Use of
certbot - Configuration of
bindIP - Role of public DNS
- Use of
PART 4 – ARCHITECTURAL ANALYSIS QUESTION (10 POINTS)
Statement
Two approaches are possible to expose multiple environments:
Approach A:
subdomains
→ http://dev.site1.local, http://test.site1.local, http://prod.site1.local
Approach B:
internal paths
→ http://site1.local/dev, http://site1.local/test, http://site1.local/prod
Question
-
Compare the two approaches:
a) Main technical implications (4 points) b) Use cases where approach B is tolerable, and when it is discouraged (3 points) c) Which approach do you recommend and why? (3 points)
EVALUATION GRID
| Criteria | Points |
|---|---|
| Wiki.js (x3) installed and functional | 15 |
| Distinct databases | 10 |
Correct config.yml configuration | 10 |
Functional systemd services | 5 |
| NGINX reverse proxy configured | 15 |
/etc/hosts well edited | 5 |
| Error diagnosis and resolution | 10 |
| Technical justification | 10 |
| Argued HTTPS proposal | 10 |
| Total | 90 |
| Architectural analysis | Points |
|---|---|
| Presentation of differences | 2 |
| Use cases for each approach | 2 |
| Risk and compatibility analysis | 2 |
| Clear and motivated recommendation | 4 |
| Subtotal | 10 |
BONUS MODULE 1 – System users vs normal users on Linux
UID Convention on Linux
On most modern Linux distributions, users are classified according to their UID (User ID):
| UID Range | Account Type | Examples |
|---|---|---|
| 0 | Superuser | root |
| 1 – 999 | System / service accounts | daemon, www-data, sshd, systemd-*, apache |
| ≥ 1000 | Normal (human) users | alice, bob, student |
This range is defined in the /etc/login.defs file of your distribution. It is not a universal rule, but a widely adopted convention (Debian, Ubuntu, RHEL, Fedora, etc.).
How to check the ranges on your machine
# See the UID ranges defined on your distro
grep -E '^(UID_MIN|UID_MAX|SYS_UID_MIN|SYS_UID_MAX)' /etc/login.defs
Typical result on Ubuntu:
SYS_UID_MIN 100
SYS_UID_MAX 999
UID_MIN 1000
UID_MAX 60000
System accounts (UID < 1000) – Characteristics
System accounts are created to run services/daemons, not for human logins.
Typical indicators of a system account:
- Low UID (< 1000)
- Shell =
/usr/sbin/nologinor/bin/false(no interactive login) - No real user home directory (or a "technical" home)
- Created with
useradd -r(flag system account)
Common examples:
| Account | Service | Typical UID | Shell |
|---|---|---|---|
root | Superuser | 0 | /bin/bash |
www-data | Apache / Nginx (Debian/Ubuntu) | 33 | /usr/sbin/nologin |
apache | Apache (RHEL/CentOS) | 48 | /sbin/nologin |
sshd | SSH Server | 105 | /usr/sbin/nologin |
mysql | MariaDB / MySQL | 27 | /bin/false |
Quick verification:
# See the UID and shell of www-data
id www-data
getent passwd www-data
# Result: uid=33(www-data) gid=33(www-data) groups=33(www-data)
Normal / human accounts (UID ≥ 1000) – Characteristics
Human accounts are created for people who log into the system.
Typical indicators of a human account:
- UID ≥ 1000
- Shell =
/bin/bashor/bin/sh - Home directory in
/home/<username> - Created with
useradd(without the-rflag) oradduser
Useful commands to list and distinguish user types
List human users (UID ≥ 1000):
UID_MIN=$(awk '/^UID_MIN/{print $2}' /etc/login.defs)
awk -F: -v m="$UID_MIN" '$3>=m {print $1" (uid="$3")"}' /etc/passwd | sort -t'=' -k2 -n
List system users (UID < 1000):
UID_MIN=$(awk '/^UID_MIN/{print $2}' /etc/login.defs)
awk -F: -v m="$UID_MIN" '$3<m {print $1" (uid="$3")"}' /etc/passwd | sort -t'=' -k2 -n
List accounts without login (typically services):
awk -F: '($7 ~ /(nologin|false)$/) {print $1" -> "$7}' /etc/passwd
List "probable human" accounts (home in /home + bash/sh shell):
awk -F: '($6 ~ /^\/home\//) && ($7 ~ /(bash|sh)$/) {print $1" home="$6" shell="$7}' /etc/passwd
Link with the lab: Why are wikijs_dev, wikijs_test, wikijs_prod system accounts?
In this lab, the Wiki.js users were created with the command:
sudo useradd -r -m -s /bin/bash wikijs_dev
sudo useradd -r -m -s /bin/bash wikijs_test
sudo useradd -r -m -s /bin/bash wikijs_prod
| Option | Role |
|---|---|
-r | Creates a system account → UID assigned in the system range (< 1000) |
-m | Creates a home directory (useful for config files) |
-s /bin/bash | Sets the shell (here bash to facilitate debugging in the lab) |
It's the -r flag that makes the difference! Without -r, the account would be a normal user (UID ≥ 1000). The -s flag only sets the shell, it does not make the account "system".
These accounts are "service accounts": they are responsible for running the Wiki.js services, as defined in the systemd files:
[Service]
User=wikijs_dev # ← the service runs under this system account
Verification:
# See the assigned UID (should be < 1000)
id wikijs_dev
id wikijs_test
id wikijs_prod
# See which user runs each Wiki.js process
ps -eo user,pid,cmd | grep '[n]ode'
# Check the service status
systemctl status wikijs-dev
Recommendation in production
In production, it is preferable not to give an interactive shell to service accounts:
# Best practice (nologin shell)
sudo useradd -r -m -s /usr/sbin/nologin wikijs_dev
sudo useradd -r -m -s /usr/sbin/nologin wikijs_test
sudo useradd -r -m -s /usr/sbin/nologin wikijs_prod
With /usr/sbin/nologin, no one can open an interactive session with this account, but systemd and sudo -u wikijs_dev <command> still work normally. This is the principle of least privilege.
useradd -r→ system account (UID < 1000, intended for a service)useradd(without-r) → human account (UID ≥ 1000, intended for a person)-s→ sets the shell, does not change the account type- The
wikijs_*accounts in this lab are system accounts responsible for running the Wiki.js service - In production: always use
-s /usr/sbin/nologinfor service accounts
Lab created on: December 2024