Publishing Web Services over IPv6 (Apache & Nginx on Ubuntu) 
--------------
Prerequisites
--------------
1. Ubuntu 22.04+ server with both IPv4 and IPv6 connectivity. 
2. DNS domain with A (IPv4) and AAAA (IPv6) records configured. 
3. A non-root user with sudo privileges. 

-----------------
Lab Objectives: 
-----------------
By the end of this lab, you will be able to: 

1. Verify IPv6 readiness on an Ubuntu server, ensuring proper addressing and connectivity. 

2. Install and configure a web server (Apache or Nginx) to support dual-stack (IPv4 + IPv6) operations. 

3. Set up dedicated web roots for ERP, LMS, and Portal applications. 

4. Configure Virtual Hosts / Server Blocks with proper IPv6 bindings for each application domain. 

5. Secure applications with firewall rules, allowing only required services over IPv4 and IPv6. 

6. Enable HTTPS encryption using Let’s Encrypt (Certbot) for ERP, LMS, and Portal domains. 

7. Validate IPv6 routing and application accessibility from an IPv6-enabled client. 

 

============================
 Step 1. Verify IPv6 Address 
==============================
Check if your system already has an IPv6 address: 

ip -6 addr show 
or 
ip addr show | grep inet6 
 
===============================
Step 2. Install & Verify Apache 
===============================
Check if Apache is installed: 

apache2 -v 
 
If not installed: 

sudo apt update 
sudo apt install apache2 -y 
 

Verify Apache service status: 

sudo systemctl status apache2 
 
Expected output: active (running). 

 ======================================
Step 3. Configure Firewall for Apache 
======================================
Allow HTTP & HTTPS traffic: 

sudo ufw allow OpenSSH 
sudo ufw allow in "Apache Full" 
sudo ufw enable 
sudo ufw status 
  
=================================================
Step 4. Configure Apache to Listen on IPv4 & IPv6 
=================================================
Edit the ports.conf: 

sudo nano /etc/apache2/ports.conf 
 
Add IPv4 and IPv6 listeners: 

Listen your-IPv4-address:80 
Listen [your-IPv6-address]:80 
 
<IfModule ssl_module> 
    Listen your-IPv4-address:443 
    Listen [your-IPv6-address]:443 
</IfModule> 
 
<IfModule mod_gnutls.c> 
    Listen your-IPv4-address:443 
    Listen [your-IPv6-address]:443 
</IfModule> 
 

Validate configuration: 

sudo apache2ctl configtest 
# Output: Syntax OK 
 

Restart Apache: 

sudo systemctl restart apache2 
 
Confirm status: 

sudo systemctl status apache2 
 
=====================================
Step 5. Verify Apache IPv6 Bindings 
=====================================
Check sockets: 
sudo ss -ltpn 
 

Show only IPv6 sockets: 
sudo ss -6ltpn 
 

Alternatively, with netstat: 
netstat -anlp | grep 80 
 

Final test: enter IPv6 in browser: 

http://[your-IPv6-address]/ 
  
==================================
Step 6. Install & Configure Nginx  
==================================
Check installation: 
nginx -v 
 
If missing: 
sudo apt install nginx -y 
 
Verify service: 
sudo systemctl status nginx 
 
Firewall for Nginx 

sudo ufw allow in "Nginx Full" 
sudo ufw enable 
sudo ufw status 
 
Stop Apache if running (to free port 80): 
sudo systemctl stop apache2 
 
Edit Nginx config: 

sudo nano /etc/nginx/nginx.conf 
 
Minimal dual-stack listener: 

server { 
    listen 80 default_server; 
    listen [your-IPv6-address]:80 default_server; 
    root /var/www/html; 
    index index.html; 
} 
 
Validate and restart: 

sudo nginx -t 
sudo systemctl restart nginx 
 
Verify sockets: 
sudo ss -ltpn 
 
Browser test: 
http://[your-IPv6-address]/ 
 
=============================================================== 
Step 7. Configure Virtual Hosts (Apache) / Server Blocks (Nginx) 
================================================================
Apache VirtualHost (dual-stack): 

<VirtualHost *:80 [::]:80> 
    ServerAdmin webmaster@localhost 
    ServerName example.com 
    ServerAlias www.example.com 
    DocumentRoot /var/www/example.com 
    ErrorLog ${APACHE_LOG_DIR}/error.log 
    CustomLog ${APACHE_LOG_DIR}/access.log combined 
</VirtualHost> 
 
<VirtualHost *:443 [::]:443> 
    ServerName example.com 
    DocumentRoot /var/www/example.com 
    SSLEngine on 
    SSLCertificateFile /etc/ssl/certs/example.crt 
    SSLCertificateKeyFile /etc/ssl/private/example.key 
</VirtualHost> 
 
Enable & restart: 

sudo a2ensite example.com.conf 
sudo a2dissite 000-default.conf 
sudo apache2ctl configtest 
sudo systemctl reload apache2 
 
-----------------------------------
Nginx Server Block (dual-stack): 
-------------------------------
server { 
    listen 80; 
    listen [::]:80; 
    server_name example.com www.example.com; 
    root /var/www/example; 
    index index.html index.htm; 
 
    access_log /var/log/nginx/example-access.log; 
    error_log  /var/log/nginx/example-error.log; 
} 
 
server { 
    listen 443 ssl; 
    listen [::]:443 ssl; 
    server_name example.com www.example.com; 
    root /var/www/example; 
 
    ssl_certificate     /etc/ssl/certs/example.crt; 
    ssl_certificate_key /etc/ssl/private/example.key; 
 
    access_log /var/log/nginx/example-ssl-access.log; 
    error_log  /var/log/nginx/example-ssl-error.log; 
} 
 

Validate & reload: 

sudo nginx -t 
sudo systemctl reload nginx 
 
=========================================
 Step 8. Install Certbot (Let’s Encrypt) 
=========================================
sudo apt update 
sudo apt install certbot python3-certbot-nginx python3-certbot-apache -y 
 
Verify IPv6 connectivity: 

ping6 example.com 
curl -6 https://example.com 
 

Obtain certificate (Apache): 
sudo certbot --apache -d example.com -d www.example.com 
 
Obtain certificate (Nginx): 
sudo certbot --nginx -d example.com -d www.example.com 
 
Enable auto-renew: 

sudo systemctl enable certbot.timer 
sudo systemctl start certbot.timer 
sudo certbot renew --dry-run 
 
====================================
Step 9. Final IPv6 SSL Verification 
====================================
curl -6I https://example.com 
 
================================
 Checklist for Dual-Stack Setup 
================================
IPv4 + IPv6 addresses configured 

Firewall open for ports 80/443 (both stacks) 

Apache/Nginx configs tested (apache2ctl configtest / nginx -t) 

Certbot SSL certificates applied and auto-renew enabled 

External tests: 

curl -4 http://example.com   # IPv4 
curl -6 http://example.com   # IPv6 

==================================================
Step 10. Create Web Root for ERP, LMS, and Portal 
==================================================
Create directories: 

sudo mkdir -p /var/www/erp.example.com 
sudo mkdir -p /var/www/lms.example.com 
sudo mkdir -p /var/www/portal.example.com 
 

Assign permissions: 

sudo chown -R $USER:$USER /var/www/erp.example.com 
sudo chown -R $USER:$USER /var/www/lms.example.com 
sudo chown -R $USER:$USER /var/www/portal.example.com 
 

Add test index files (example ERP page): 

echo "<h1>ERP IPv6 Lab Page</h1>" | sudo tee /var/www/erp.example.com/index.html 
echo "<h1>LMS IPv6 Lab Page</h1>" | sudo tee /var/www/lms.example.com/index.html 
echo "<h1>Portal IPv6 Lab Page</h1>" | sudo tee /var/www/portal.example.com/index.html 
 
================================================
Step 11: Configure Virtual Hosts / Server Blocks 
================================================
Apache (dual-stack VirtualHosts) 
------------------------------------
Create config files: 

sudo nano /etc/apache2/sites-available/erp.example.com.conf 
 
Example ERP config: 

<VirtualHost *:80 [::]:80> 
    ServerName erp.example.com 
    DocumentRoot /var/www/erp.example.com 
    ErrorLog ${APACHE_LOG_DIR}/erp-error.log 
    CustomLog ${APACHE_LOG_DIR}/erp-access.log combined 
</VirtualHost> 
 
Repeat for LMS and Portal. 

Enable configs: 

sudo a2ensite erp.example.com.conf 
sudo a2ensite lms.example.com.conf 
sudo a2ensite portal.example.com.conf 
sudo systemctl reload apache2 
 
Nginx (dual-stack Server Blocks) 
--------------------------------
Create config files: 
sudo nano /etc/nginx/sites-available/erp.example.com 
 

Example ERP block: 

server { 
    listen 80; 
    listen [::]:80; 
    server_name erp.example.com; 
 
    root /var/www/erp.example.com; 
    index index.html; 
    access_log /var/log/nginx/erp-access.log; 
    error_log  /var/log/nginx/erp-error.log; 
} 
 

Repeat for LMS and Portal. 

Enable and reload: 

sudo ln -s /etc/nginx/sites-available/erp.example.com /etc/nginx/sites-enabled/ 
sudo ln -s /etc/nginx/sites-available/lms.example.com /etc/nginx/sites-enabled/ 
sudo ln -s /etc/nginx/sites-available/portal.example.com /etc/nginx/sites-enabled/ 
sudo nginx -t 
sudo systemctl reload nginx 
 
====================================================
Step 12. Enable HTTPS with Let’s Encrypt (Certbot) 
====================================================
Obtain certificates (example for ERP): 

sudo certbot --apache -d erp.example.com 
# or 
sudo certbot --nginx -d erp.example.com 
 

Repeat for LMS and Portal. 

Verify auto-renewal: 

sudo certbot renew --dry-run 
=========================================
Step 12: Verify IPv6 Routing and Firewall 
=========================================
Confirm services are listening on IPv6: 

sudo ss -6ltpn | grep 80 
sudo ss -6ltpn | grep 443 
 

Confirm DNS AAAA records: 

dig AAAA erp.example.com 
 

Browser test (from an IPv6-enabled client): 

http://your-IPv6-address/ 
https://erp.example.com 
 
========================================
 • DNS: BIND configuration to serve over IPv6 and add AAAA records
=======================================================================
====== how to configure BIND (named) to: 

Serve DNS queries over IPv6 

Add AAAA records for your hostnames 
====================================
1. Install BIND9 (if not installed) 
====================================

sudo apt update 
sudo apt install bind9 bind9utils bind9-doc -y 

======================= 
2. Enable IPv6 in BIND 
=======================
Edit /etc/bind/named.conf.options 

conf
options { 
    directory "/var/cache/bind"; 

     # Listen on all IPv4 and IPv6 addresses 

    listen-on { any; }; 

    listen-on-v6 { any; }; 

    // Alternatively, specify specific IPv6 addresses: 

    // listen-on-v6 { 2001:db8::1; 2001:db8::2; }; 

     # Allow queries from anywhere (or restrict to your network) 
     allow-query { any; }; 

     recursion yes; 

    dnssec-validation auto; 

}; 

==============================================
3. Create/Update Zone File with AAAA Records 
==============================================
Edit your zone file, e.g., /etc/bind/db.example.com 
 
dns 
$TTL 86400 
@   IN  SOA ns1.example.com. admin.example.com. ( 

        2025081701 ; Serial 
        3600       ; Refresh 
        1800       ; Retry 
        1209600    ; Expire 
        86400 )    ; Minimum TTL 

 
; Nameservers 

    IN  NS   ns1.example.com. 
    IN  NS   ns2.example.com. 

 

; A Records (IPv4) 

ns1 IN  A    192.0.2.1 
ns2 IN  A    192.0.2.2 
www IN  A    192.0.2.100 

 
; AAAA Records (IPv6) 
ns1 IN  AAAA 2001:db8::1 
ns2 IN  AAAA 2001:db8::2 
www IN  AAAA 2001:db8::100 

 ==============================
 4. Declare the Zone in BIND 
================================
In /etc/bind/named.conf.local: 

conf 
zone "example.com" { 
    type master; 
    file "/etc/bind/db.example.com"; 
}; 
==========================
5. Check Config & Restart 
=========================
Check for syntax errors: 

sudo named-checkconf 
sudo named-checkzone example.com /etc/bind/db.example.com 

Restart BIND9: 

sudo systemctl restart bind9 
sudo systemctl enable bind9 

 =========================
6. Verify IPv6 & AAAA 
=========================
Check if BIND is listening on IPv6: 
sudo ss -tulpn | grep named 

Test AAAA records resolution: 

dig AAAA www.example.com @::1 
dig AAAA www.example.com @2001:db8::1 

At this point: 

 Your BIND server is answering queries over IPv6 
 Clients can resolve AAAA records (IPv6 addresses) 

 ======================================================= 
===================================================== 

• Mail: Postfix o with IPv6 listeners 

===================================== 

 Let’s configure Postfix to listen and send mail over IPv6 (dual-stack: IPv4 + IPv6). 

 =================================
Step 1: Enable IPv6 in Postfix 
==================================
Open the main configuration file: 

sudo nano /etc/postfix/main.cf 

Add (or edit) these parameters: 

 # Enable both IPv4 and IPv6 
inet_protocols = all 

 
# Restrict Postfix to listen on specific interfaces if needed 
# (default = all interfaces, both IPv4 & IPv6) 
inet_interfaces = all 


 # Explicitly listen on IPv6 if you only want v6 
# inet_protocols = ipv6 

# Optional: limit to certain addresses 
# Example: listen only on localhost and specific IPv6 

# inet_interfaces = 127.0.0.1, ::1, [2001:db8::1] 

=====================================================
Step 2: Configure master.cf for Dual-Stack Listeners 
=====================================================

sudo nano /etc/postfix/master.cf 

By default, Postfix listens on all protocols when inet_protocols = all is set. 

But you can explicitly add IPv6 listeners if you want separate control. 

Example (standard SMTP over both IPv4 + IPv6): 

smtp      inet  n       -       y       -       -       smtpd 
[::]:25   inet  n       -       y       -       -       smtpd 

For submission ports (587 + 465 with TLS): 

submission inet n       -       y       -       -       smtpd 
[::]:587   inet n       -       y       -       -       smtpd 

 smtps      inet n       -       y       -       -       smtpd 
[::]:465   inet n       -       y       -       -       smtpd 

=======================================
Step 3: Ensure System Supports IPv6 
=======================================
Make sure IPv6 is enabled: 
sudo sysctl net.ipv6.conf.all.disable_ipv6 

If it returns 0 → IPv6 is enabled. 

To check listening sockets: 
sudo ss -ltnp | grep master 

You should see 0.0.0.0:25 and [::]:25. 

========================================== 
Step 4: Configure DNS (AAAA + MX Records) 
==========================================
Add an AAAA record for your mail server hostname: 
mail.example.com.  IN  AAAA  2001:db8::1234 

Add/update MX record to point to that hostname. 
example.com.  IN  MX  10 mail.example.com. 

========================
Step 5: Reload Postfix 
========================
sudo systemctl restart postfix 
sudo postfix check 

========================================
Step 6: Verify IPv6 Sending & Receiving 
========================================
Test incoming IPv6 listener: 
telnet -6 mail.example.com 25 

Force sending mail over IPv6: 
sendmail -Am -f you@example.com recipient@remote.com 

Check logs: 
sudo tail -f /var/log/mail.log 

With this config, Postfix will: 
1. Accept SMTP connections on IPv4 + IPv6 
2. Listen on submission ports (if enabled) for mail clients 
3. Use IPv6 when remote MX has an AAAA record 
======================================================== 

 • ERP, LMS, Portals: Web apps with proper IPv6 routing and firewall config  

========================================================== 

🔹 2. DNS Records for Web Apps 

For each service (ERP, LMS, Portal), create A and AAAA records: 

 

Example (ERP, LMS, Portal on same server): 
erp.example.com.    IN A    203.0.113.10 
erp.example.com.    IN AAAA 2001:db8::10 

lms.example.com.    IN A    203.0.113.10 
lms.example.com.    IN AAAA 2001:db8::10 

portal.example.com. IN A    203.0.113.10 
portal.example.com. IN AAAA 2001:db8::10 

🔹 3. Web Server Configuration 

You can use Apache or Nginx (both support dual-stack). 

Example: Nginx Dual-Stack VirtualHosts 

nginx: 

server { 

    listen 80; 
    listen [::]:80; 

 

    server_name erp.example.com; 

 

    root /var/www/erp; 

    index index.php index.html; 

 

    location / { 

        try_files $uri $uri/ =404; 

    } 

} 

 

server { 
    listen 80; 
    listen [::]:80; 

 

    server_name lms.example.com; 

 

    root /var/www/lms; 

    index index.php index.html; 

 

    location / { 
        try_files $uri $uri/ =404; 
    } 
} 

 

server { 
    listen 80; 
    listen [::]:80; 

 

    server_name portal.example.com; 

 

    root /var/www/portal; 

    index index.php index.html; 

 

    location / { 
        try_files $uri $uri/ =404; 
    } 
} 

Enable HTTPS with Let’s Encrypt later. 

 

4. SSL (Let’s Encrypt with IPv6) 

Install certbot: 

sudo apt install certbot python3-certbot-nginx -y 

Obtain certs: 

sudo certbot --nginx -d erp.example.com -d lms.example.com -d portal.example.com 

Certbot + Nginx will auto-detect dual-stack listeners (80 + [::]:80). 

 

Test renewal: 
sudo certbot renew --dry-run 

🔹 5. Firewall Configuration (UFW for IPv4 + IPv6) 

Edit UFW to ensure it handles IPv6: 

sudo nano /etc/default/ufw 

Set: 

IPV6=yes 

Reload: 
sudo ufw reload 

Allow services (applies to IPv4 + IPv6): 
sudo ufw allow ssh 
sudo ufw allow http 
sudo ufw allow https 

Default deny other traffic: 

sudo ufw default deny incoming 
sudo ufw default allow outgoing 

Check: 
sudo ufw status verbose 

🔹 6. Verification 

Check IPv6 web reachability: 
curl -6 -I http://erp.example.com 
curl -6 -I https://lms.example.com 

Check firewall rules: 
sudo ss -ltnp | grep nginx 

You should see 0.0.0.0:80, 0.0.0.0:443, [::]:80, [::]:443. 

 External test: 

Use https://tools.keycdn.com/ipv6-ping or ping6 erp.example.com. 

 Final Setup Outcome 

1. ERP, LMS, and Portals available via IPv4 & IPv6. 
2. Proper DNS with A + AAAA records. 
3.Secured with Let’s Encrypt SSL (auto-renew). 

Firewall locked down to allow only SSH, HTTP, HTTPS over IPv4 + IPv6. 