Skip to main content

Lab 2 – Managing multiple websites with a reverse proxy in a Linux environment

Table of Contents​


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

Back to the table of contents


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.local must work via /etc/hosts
  • You must follow a professional approach, with structure, clarity, and rigor

Back to the table of contents


PART 1 – INSTALLATION AND CONFIGURATION OF WIKI.JS (40 POINTS)​

  1. Create three dedicated Linux users: wikijs_dev, wikijs_test, wikijs_prod

  2. Create three distinct databases under MariaDB:

    • wikidb_dev, wikidb_test, wikidb_prod
  3. Manually install Wiki.js in:

    • /var/www/dev.site1
    • /var/www/test.site1
    • /var/www/prod.site1
  4. Modify the config.yml file of each instance:

    • Different port (3000, 3001, 3002)
    • bindIP: 127.0.0.1
    • Correct database connection information
  5. Create and activate the systemd services:

    • wikijs-dev.service, wikijs-test.service, wikijs-prod.service

Back to the table of contents


PART 2 – CONFIGURATION OF THE NGINX REVERSE PROXY (30 POINTS)​

  1. Configure NGINX to serve the following subdomains:
HostnameInternal PortLocal Path
dev.site1.local3000/var/www/dev.site1
test.site1.local3001/var/www/test.site1
prod.site1.local3002/var/www/prod.site1
  1. Modify /etc/hosts to point these domains to 127.0.0.1

  2. Test site access with a browser or curl

Back to the table of contents


PART 3 – VALIDATION, TROUBLESHOOTING, AND ANALYSIS (30 POINTS)​

  1. Correct the following errors if they appear:

    • Bad YAML indentation
    • MariaDB connection issues
    • Port already in use
    • Insufficient file permissions
  2. 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?
  3. Propose a method to secure the installation with HTTPS:

    • Use of certbot
    • Configuration of bindIP
    • Role of public DNS

Back to the table of contents


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​

  1. 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)

Back to the table of contents


EVALUATION GRID​

CriteriaPoints
Wiki.js (x3) installed and functional15
Distinct databases10
Correct config.yml configuration10
Functional systemd services5
NGINX reverse proxy configured15
/etc/hosts well edited5
Error diagnosis and resolution10
Technical justification10
Argued HTTPS proposal10
Total90
Architectural analysisPoints
Presentation of differences2
Use cases for each approach2
Risk and compatibility analysis2
Clear and motivated recommendation4
Subtotal10

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 RangeAccount TypeExamples
0Superuserroot
1 – 999System / service accountsdaemon, www-data, sshd, systemd-*, apache
≥ 1000Normal (human) usersalice, bob, student
info

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/nologin or /bin/false (no interactive login)
  • No real user home directory (or a "technical" home)
  • Created with useradd -r (flag system account)

Common examples:

AccountServiceTypical UIDShell
rootSuperuser0/bin/bash
www-dataApache / Nginx (Debian/Ubuntu)33/usr/sbin/nologin
apacheApache (RHEL/CentOS)48/sbin/nologin
sshdSSH Server105/usr/sbin/nologin
mysqlMariaDB / MySQL27/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/bash or /bin/sh
  • Home directory in /home/<username>
  • Created with useradd (without the -r flag) or adduser

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

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
OptionRole
-rCreates a system account → UID assigned in the system range (< 1000)
-mCreates a home directory (useful for config files)
-s /bin/bashSets the shell (here bash to facilitate debugging in the lab)
tip

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.

Summary
  • 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/nologin for service accounts

Back to the table of contents


Lab created on: December 2024