Proxmox VE Network Configuration: NAT Shared IP Port Mapping with iptables and Brook

,

Configure a bridged network card so that Proxmox VE (PVE) can create both NAT virtual machines and independent IP virtual machines, while also using scripts to map the NAT virtual machine’s ports to the external network.

1. Enable IPv4 and IPv6 Forwarding

Edit `/etc/sysctl.conf`:

vim /etc/sysctl.conf

Add the following configuration at the end of the file:

net.ipv4.ip_forward=1

net.ipv4.conf.all.rp_filter=1

net.ipv4.icmp_echo_ignore_broadcasts=1

net.ipv4.conf.default.forwarding=1

net.ipv4.conf.default.proxy_arp=0

net.ipv4.conf.default.send_redirects=1

net.ipv4.conf.all.send_redirects=0

net.ipv6.conf.eno1.autoconf=0

net.ipv6.conf.eno1.accept_ra=2

net.ipv6.conf.default.forwarding=1

net.ipv6.conf.all.forwarding=1

net.ipv6.conf.default.proxy_ndp=1

net.ipv6.conf.all.proxy_ndp=1

2. Configure Proxmox VE Network Card Information

Edit `/etc/network/interfaces`:

vim /etc/network/interfaces

Add a new `vmbr172` bridge and set up traffic forwarding:

auto lo

iface lo inet loopback

iface enp40 inet manual

auto vmbr0

iface vmbr0 inet static

        address 192.168.0.103/24

        gateway 192.168.0.1

        bridge-ports enp40

        bridge-stp off

        bridge-fd 0

# Physical network card configuration usually does not need changes, system templates are pre-configured.

# Create a new virtual bridge for virtual machines.

# Internal network address and virtual machine gateway.

auto vmbr172

iface vmbr172 inet static

        address  172.16.1.1

        netmask  255.255.255.0

        bridge-ports none

        bridge-stp off

        bridge-fd 0

        post-up echo 1 > /proc/sys/net/ipv4/ip_forward

        post-up echo 1 > /proc/sys/net/ipv4/conf/eno1/proxy_arp

        # Forward IPv4 traffic to the virtual machine to allow connectivity to the external network.

        post-up iptables -t nat -A POSTROUTING -s ‘172.16.1.0/16’ -o vmbr0 -j MASQUERADE

        post-down iptables -t nat -D POSTROUTING -s ‘172.16.1.0/16’ -o vmbr0 -j MASQUERADE

Restart networking:

sudo service networking restart

3. Set Up a DHCP Server

4. Internal and External Network Port Mapping Forwarding

Add a new port forwarding rule:

# Add

iptables -t nat -A PREROUTING -p tcp -m tcp –dport 10022 -j DNAT –to-destination 10.10.10.100:22

To delete (change `-A` to `-D`):

# Delete

iptables -t nat -D PREROUTING -p tcp -m tcp –dport 10022 -j DNAT –to-destination 10.10.10.100:22

View NAT rules with line numbers:

iptables -t nat –list –line-number

Delete a specific iptables rule by line number:

iptables -t nat -D POSTROUTING 10

Since IPv4 is routed through the internal network, external access to the virtual machine is currently blocked. We need to configure port forwarding to direct external traffic to the virtual machine. Typically, this is done using `iptables`.

4.1 Using UFW

The default firewall tool in Linux, `iptables`, can be quite complex. Therefore, Ubuntu provides `ufw` (Uncomplicated Firewall), a simpler front-end for `iptables`, which also supports graphical interface operations. To use `ufw`, simply run the `ufw` command in the terminal to see a range of operations.

UFW (Uncomplicated Firewall) is the default firewall component on Ubuntu, developed to simplify the configuration of `iptables`. It provides a very user-friendly interface for creating firewall rules based on IPv4 and IPv6.

Starting from Linux kernel version 2.4, a powerful firewall tool named `netfilter/iptables` became available. It’s free and powerful, allowing for fine-grained control of incoming and outgoing traffic. It supports functionalities such as firewalling, NAT (Network Address Translation), and packet segmentation. While `netfilter` works at the kernel level, `iptables` allows users to define rule sets for filtering.

However, `iptables` rules can be somewhat complex, so Ubuntu provides `ufw` to simplify certain configurations, even though it still relies on `iptables` in the background. UFW stands for Uncomplicated Firewall.

4.2 Using iptables

Port mapping can be fully configured using iptables, and there are convenient scripts written by others for this purpose. Below is the script named `iptables.sh`.

Script: `iptables.sh`

#! /bin/

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH

# Config file

iptablesconf=’/root/iptables.config.sh’

function rootness(){

    if [[ $EUID -ne 0 ]]; then

       echo “This script requires ROOT privileges!”

       exit 1

    fi

}

function conf_list(){

    cat $iptablesconf

}

function conf_add(){

    if [ ! -f $iptablesconf ]; then

        echo “Configuration file not found!”

        exit 1

    fi

    echo “Please enter the internal IP of the virtual machine:”

    read -p “(Default: Exit):” confvmip

    [ -z “$confvmip” ] && exit 1

    echo

    echo “Virtual Machine Internal IP = $confvmip”

    echo

    while true

    do

        echo “Please enter the virtual machine port:”

        read -p “(Default port: 22):” confvmport

        [ -z “$confvmport” ] && confvmport=”22″

        expr $confvmport + 0 &>/dev/null

        if [ $? -eq 0 ]; then

            if [ $confvmport -ge 1 ] && [ $confvmport -le 65535 ]; then

                echo

                echo “Virtual Machine Port = $confvmport”

                echo

                break

            else

                echo “Invalid input, the port range should be 1-65535!”

            fi

        else

            echo “Invalid input, the port range should be 1-65535!”

        fi

    done

    echo

    while true

    do

        echo “Please enter the host machine port:”

        read -p “(Default port: 8899):” natconfport

        [ -z “$natconfport” ] && natconfport=”8899″

        expr $natconfport + 0 &>/dev/null

        if [ $? -eq 0 ]; then

            if [ $natconfport -ge 1 ] && [ $natconfport -le 65535 ]; then

                echo

                echo “Host Machine Port = $natconfport”

                echo

                break

            else

                echo “Invalid input, the port range should be 1-65535!”

            fi

        else

            echo “Invalid input, the port range should be 1-65535!”

        fi

    done

    echo “Please enter the forwarding protocol:”

    read -p “(tcp or udp, press enter to exit):” conftype

    [ -z “$conftype” ] && exit 1

    echo

    echo “Protocol Type = $conftype”

    echo

    iptablesshell=”iptables -t nat -A PREROUTING -i vmbr0 -p $conftype –dport $natconfport -j DNAT –to-destination $confvmip:$confvmport”

    if [ `grep -c “$iptablesshell” $iptablesconf` != ‘0’ ]; then

        echo “Configuration already exists”

        exit 1

    fi

    get_char(){

        SAVEDSTTY=`stty -g`

        stty -echo

        stty cbreak

        dd if=/dev/tty bs=1 count=1 2> /dev/null

        stty -raw

        stty echo

        stty $SAVEDSTTY

    }

    echo

    echo “Press Enter to continue, Ctrl+C to exit the script”

    char=`get_char`

    echo $iptablesshell >> $iptablesconf

    runreturn=`$iptablesshell`

    echo $runreturn

    echo ‘Configuration added successfully’

}

function add_confs(){

    rootness

    conf_add

}

function del_conf(){

    echo

    while true

    do

        echo “Please enter the host machine port:”

        read -p “(Default action: Exit):” confserverport

        [ -z “$confserverport” ] && exit 1

        expr $confserverport + 0 &>/dev/null

        if [ $? -eq 0 ]; then

            if [ $confserverport -ge 1 ] && [ $confserverport -le 65535 ]; then

                echo

                echo “Host Machine Port = $confserverport”

                echo

                break

            else

                echo “Invalid input, the port range should be 1-65535!”

            fi

        else

            echo “Invalid input, the port range should be 1-65535!”

        fi

    done

    echo

    iptablesshelldel=`cat $iptablesconf | grep “dport $confserverport”`

    if [ ! -n “$iptablesshelldel” ]; then

         echo “No such host machine port in the configuration file”

         exit 1

    fi

    iptablesshelldelshell=`echo ${iptablesshelldel//-A/-D}`

    runreturn=`$iptablesshelldelshell`

    echo $runreturn

    sed -i “/$iptablesshelldel/d” $iptablesconf

    echo ‘Configuration deleted successfully’

}

function del_confs(){

    printf “Are you sure you want to delete the configuration? This operation is irreversible (y/n) “

    printf “\n”

    read -p “(Default: n):” answer

    if [ -z $answer ]; then

        answer=”n”

    fi

    if [ “$answer” = “y” ]; then

        rootness

        del_conf

    else

        echo “Configuration deletion operation canceled”

    fi

}

action=$1

case “$action” in

add)

    add_confs

    ;;

list)

    conf_list

    ;;

del)

    del_confs

    ;;

*)

    echo “Invalid argument! [${action} ]”

    echo “Usage: `basename $0` {add|list|del}”

    ;;

esac

Script Configuration File: `iptables.config.sh`

#!/usr/bin/env

How to Use:

1. Upload the `iptables.sh` and `iptables.config.sh` scripts to the root directory of the host machine, or create them directly in the root directory.

2. Grant executable permissions to `iptables.config.sh`:

   chmod +x /xxx/iptables.config.sh

   *(Replace `/xxx/iptables.config.sh` with the correct path to the file.)*

3. Execute commands to add or delete port mappings:

To add a port mapping:

      iptables.sh add

To delete a port mapping:

      iptables.sh del

4.3 Using Brook for Unified Control

With this setup, accessing port 10022 on the server will automatically forward the traffic to port 22 on the virtual machine with IP 10.10.10.100, allowing external access to the virtual machine via SSH. However, there is a problem: Proxmox VE comes with its own firewall, and opening port 22 using `iptables` is equivalent to creating an open hole in the firewall, which is not controlled by the firewall itself. If security protection is needed, you would have to configure `iptables` separately, which is not very convenient. So, a compromise solution is to use Brook for port forwarding while leveraging Proxmox VE’s firewall for unified control.

Getting Brook

# Choose the version you need

wget -O /root/brook https://github.com/txthinking/brook/releases/download/{version}/brook/brook_linux_xxx

# Or you can download a specific version like this:

wget -O /root/brook https://github.com/txthinking/brook/releases/download/v20221212/brook_linux_amd64

Move the Brook binary to the desired location and give it execution permissions. Replace `brook_path` with the actual path you prefer.

chmod +x /brook_path/brook

To start port forwarding from the host machine’s port to the virtual machine’s IP and port:

# Run in the foreground, exit terminal to stop forwarding

nohup /root/brook relay –from :10022 –to 10.10.10.100:22

# Or run in the background:

nohup /root/brook relay –from :10022 –to 10.10.10.100:22 > output 2>&1 &

You can check if the forwarding is working by running:

ps -ef | grep brook

You can also control external access to the virtual machine by opening or closing port 10022 in Proxmox VE’s firewall.

The configuration for KVM virtual machines is similar: when creating the VM, select bridge `vmbr0`. Whether installing a Linux or Windows system, you will need to manually input the IPv4 and IPv6 address, as well as add port forwarding on the host.

Automatic Port Mapping on Boot

To automatically start port forwarding at boot, you can write the above command into a shell script and add it to the system’s service manager.

Create a script for port forwarding, called `brookforward.sh`:

vim /root/brookforward.sh

Add the following commands to the script:

#! /bin/sh

nohup /root/brook relay –from :10022 –to 10.10.10.100:22 > output 2>&1

Give the script execute permissions:

chmod +x brookforward.sh

Setting Brook for Automatic Startup with systemd

To configure Brook for automatic startup using systemd, write the following service configuration.

Create the `brookforward.service` file:

vim /etc/systemd/system/brookforward.service

Add the following content:

# /etc/systemd/system/brookforward.service

[Unit]

Description=brookforward

After=syslog.target

After=network.target

[Service]

Type=simple

ExecStart=/root/brookforward.sh

Restart=always

RestartSec=5

[Install]

WantedBy=multi-user.target

Reload systemd to apply the new configuration:

systemctl daemon-reload

Start the Brookforward service and enable it to start at boot:

systemctl enable –now brookforward

To check the status of the service:

systemctl status brookforward

Alternatively, you can add the commands to `/etc/rc.local` to execute them on startup, but this method doesn’t provide automatic restart of the service.

Using Supervisor for Process Management

If you want both startup on boot and automatic service restart, you can use Supervisor. Supervisor is a process management program written in Python. It allows you to turn normal command-line processes into background daemons and monitor their status. If the process crashes, Supervisor will automatically restart it. You can configure it by adding the executable paths of the processes you want to manage.

If you are not using IPv6, this section can be skipped. If needed, you can explore more on Supervisor for advanced management of processes.


Leave a Reply

Your email address will not be published. Required fields are marked *