Categories
Docker Linux Technology

Setup Nginx reverse proxy + Docker compose + IPv6 nat

Setting up an Nginx reverse proxy with Docker Compose and IPv6 NAT might sound complex, but it’s a powerful combination for managing and securing web traffic.

In this guide, we’ll walk you through the steps to configure Nginx as a reverse proxy, use Docker Compose to manage containers, and set up IPv6 NAT (Network Address Translation) to ensure smooth and efficient communication across your network.

Whether you’re new to these tools or looking to improve your setup, this guide will help you get everything running smoothly.

This setup was tested using a RPM package-based distro, so the tool of choice is Dnf, but it could be Yum as well.

1 – Installing Docker

Preparing the repo and installing Docker:

# dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
# dnf install docker-ce docker-ce-cli containerd.io
# systemctl enable docker
# systemctl start docker

Install docker-compose:

# wget https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m) -O /usr/local/bin/docker-compose

# chmod +x /usr/local/bin/docker-compose

2 – Adding IPv6 support to Linux and Docker

Prepare a Linux host and Docker to support IPv6 connections:

Make sure your Linux server has IPV6 enabled in your network card.

Enable IPv6 for all network interfaces.

# sysctl -w net.ipv6.conf.all.disable_ipv6=0
# sysctl -w net.ipv6.conf.default.disable_ipv6=0

Enable IPv6 for a specific network interface. I use the eth0 network interface in this example.

# sysctl -w net.ipv6.conf.eth0.disable_ipv6=0

It is also important to enable forward capabilities for IPv6 connections.

# sysctl -w net.ipv6.conf.all.forwarding=1
# sysctl -w net.ipv6.conf.eth0.proxy_ndp=1
# sysctl -w net.ipv6.conf.eth0.accept_ra=2
# sysctl -w net.ipv6.conf.default.forwarding=1

Apply the changes:

# sysctl -p

Restart your network service and check if your network card has an IPv6 assigned.

# ip -6 address show dev eth0

eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1460 qdisc fq_codel state UP group default qlen 1000
inet6 af30:a1cd:b319:c0f3:c5f8:cd0d:7700:493f/128 scope global dynamic valid_lft 2512sec preferred_lft 2452sec

# ping6 google.com
PING google.com(in-in-f139.1e100.net (2607:f8b0:4001:c01::8b)) 56 data bytes
64 bytes from in-in-f139.1e100.net (2607:f8b0:4001:c01::8b): icmp_seq=1 ttl=115 time=0.658 ms
64 bytes from in-in-f139.1e100.net (2607:f8b0:4001:c01::8b): icmp_seq=2 ttl=115 time=0.699 ms
64 bytes from in-in-f139.1e100.net (2607:f8b0:4001:c01::8b): icmp_seq=3 ttl=115 time=0.712 ms

Create the file /etc/docker/daemon.json

Note that according to the IPv6 Nat project page (https://github.com/robbertkl/docker-ipv6nat), the network block must be in the range fc00::/7.

{
"ipv6": true,
"fixed-cidr-v6": "fd00:dead:beef::/64"
}

And restart the Docker service:

# systemctl restart docker

3 – Creating Docker Compose files

Now, we need to create the directory structure to launch the containers:

# mkdir -p /data/docker/nginx_config

# mkdir /data/docker/nginx_data

And create a docker-compose file:

# cd /data/docker

# vi docker-compose.yaml

docker-compose.yaml

services:
redirect:
container_name: redirect
image: 'nginx:latest'
depends_on:
- ipv6nat
environment:
ENABLE_IPV6: true
restart: unless-stopped
ports:
- target: 80
published: 80
protocol: tcp
mode: host
- target: 443
published: 443
protocol: tcp
mode: host
networks:
private:
ipv4_address: 172.19.0.2
ipv6_address: fd00:dead:beef:abcd::2
volumes:
- ./nginx_config/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx_config/conf.d:/etc/nginx/conf.d/
- ./nginx_config/ssl/fullchain.pem:/root/fullchain.pem:ro
- ./nginx_config/ssl/privkey.pem:/root/privkey.pem:ro
- ./nginx_data/:/var/log/nginx/
- /etc/localtime:/etc/localtime:ro

 ipv6nat:
   container_name: ipv6nat
   image: robbertkl/ipv6nat
   restart: unless-stopped
   network_mode: "host"
   privileged: true
   volumes:
     - /var/run/docker.sock:/var/run/docker.sock:ro
     - /lib/modules:/lib/modules:ro

networks:
 private:
   name: private
   driver: bridge
   enable_ipv6: true
   driver_opts:
     com.docker.network.enable_ipv6: "true"
     com.docker.network.bridge.gateway_mode_ipv6: "nat"
     com.docker.network.bridge.name: "private"
     com.docker.network.bridge.enable_icc: "true"
     com.docker.network.bridge.enable_ip_forwarding: "true"
     com.docker.network.bridge.enable_ip_masquerade: "true"
   ipam:
     driver: default
     config:
       - subnet: "172.19.0.0/16"
         gateway: "172.19.0.1"
       - subnet: "fd00:dead:beef:abcd::/64"

4 – Creating Nginx config files and directories

Nginx main config file.

/data/docker/nginx_config/nginx.conf

# nginx
user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Logging NAT ip
set_real_ip_from 0.0.0.0/0;
set_real_ip_from ::/0;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';

access_log /var/log/nginx/access.log main;

# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/conf.d/*.conf;
}

Create extra directories:

# mkdir /data/docker/nginx_config/conf.d
# mkdir /data/docker/nginx_config/ssl

Extended Nginx configuration files.

/data/docker/nginx_config/conf.d/proxy.conf

# Nginx proxy
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_intercept_errors on;

proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
send_timeout 60s;

/data/docker/nginx_config/conf.d/ssl.conf

ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS";

Website to be accessed:

/data/docker/nginx_config/conf.d/00-redir-wordpress.conf

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

server_name example.com www.example.com;

ssl_certificate /root/fullchain.pem;
ssl_certificate_key /root/privkey.pem;

include /etc/nginx/conf.d/ssl.conf;

if ($ssl_protocol = "") {
rewrite ^ https://$server_name$request_uri? permanent;
}

location / {
include /etc/nginx/conf.d/proxy.conf;
proxy_pass http://172.19.0.3;
}
}

Copy certificates to the designated directory:

# cp /etc/letsencrypt/live/example.com/fullchain.pem /data/docker/nginx_config/ssl/
# cp /etc/letsencrypt/live/example.com/privkey.pem /data/docker/nginx_config/ssl/

5 – Launching the containers and testing the setup

# cd /data/docker
# docker-compose up -d

Check if they were built and run:

# docker ps 
CONTAINER ID   IMAGE                                  COMMAND                  CREATED        STATUS       PORTS                                                                      NAMES
3d2899dfcc8e   nginx:latest                           "/docker-entrypoint.…"   25 hours ago   Up 6 hours   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp   redirect
f5f5f61ebb7e   robbertkl/ipv6nat                      "/docker-ipv6nat-com…"   41 hours ago   Up 6 hours                                                                              ipv6nat

Check if the network called “private” was created and has IPv6 assigned to it:

# docker inspect network private

[
{
"Name": "private",
"Id": "ec3013e265ef97be8081df6aa1a161762e1692899b21f5007eb95eb1be9b2347",
"Created": "2024-08-24T13:31:46.161380218-03:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": true,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
},
{
"Subnet": "fd00:dead:beef:abcd::/64",
"Gateway": "fd00:dead:beef:abcd::1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"3d2899dfcc8ea068739c4a7f4748c723831862dbfe7dcd33ab3afacb8c56435f": {
"Name": "redirect",
"EndpointID": "66ab9ee74aabbec9f97319ef11d9516d5d9c0dfde9fe5268d6e11ee261f6b6ab",
"MacAddress": "02:42:ac:13:00:02",
"IPv4Address": "172.19.0.2/16",
"IPv6Address": "fd00:dead:beef:abcd::2/64"
},
"Options": {
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_forwarding": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.gateway_mode_ipv6": "nat",
"com.docker.network.bridge.name": "private"
},
"Labels": {
"com.docker.compose.network": "private",
"com.docker.compose.project": "docker",
"com.docker.compose.version": "2.29.1"
}
}
]

Remember that is important to allow ports 80 and 443 TCP in your firewall.

Iptables rules (IPv6 + Ipv4):

# ip6tables -A INPUT -m state --state NEW -p tcp -m multiport --dports 80,443 -j ACCEPT
# iptables -A INPUT -m state --state NEW -p tcp -m multiport --dports 80,443 -j ACCEPT

If everything is running well, you might be able to see the access log as it follows:

# tail -f /data/docker/nginx_data/access.log

2001:818:e264:7200:e62:dffe:9007:731a - - [27/Aug/2024:09:04:46 -0300]"GET / HTTP/1.0" 200 39550 "https://example.com/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"

In conclusion, setting up an Nginx reverse proxy with Docker Compose and IPv6 NAT enhances your server’s network capabilities and optimizes traffic management. By following the detailed steps, including configuring Docker for IPv6, enabling IPv6 NAT, and customizing Nginx, you can ensure efficient handling of both IPv4 and IPv6 traffic. This guide simplifies the process, making it easier to implement a modern, flexible network infrastructure, suitable for diverse applications, while maintaining strong security and performance standards.