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