Problem
There are good reasons that you might want a specific service to listen to a port number that is different from the “standard” port. By standard I mean like port 22 for SSH or 80 for HTTP. Like when you have multiple webserverers running on the same host that cannot share port 80 with each other. Or if you want to “hide” your service from all the automatic script attacks that focus on standard ports. The latter is the motivation for the following scenario:
I have a Proxmox host. That host should not be accessable via SSH on the standard port 22 (TCP). Instead it should be port 10022. Because of the way Proxmox works you cannot just change the listening port of the SSH deamon from 22 to 10022. Also because there is no firewall/router in front of that host that could take over the port mapping (10022 -> Router -> 22 -> Proxmox) it has to be accomplished by the Proxmox host itself.
Solution
My way to achieve the goal is to configure netfilter with the help of itables. I will show you two different flavours in terms of network interfaces that are used to fulfill our needs. But before we start the following preconditions must be met:
The cluster firewall
- allows exceptions for port 10022 and as long as this walkthrough lasts two exceptions for the ports 22/8006 to avoid locking ourselves out of the system unexpectedly
- drops incoming traffic by default
- is turned OFF until we finish the walkthrough
Flavour 1: Only use the build in Linux bridge vmbr0
WARNING: From a security standpoint this is not the best solution unless you really know what you’re doing. Read on to get to know why.
On your Proxmox host open the the interfaces configuration file:
$ sudo nano /etc/network/interfaces
Alter the vmbr0 part as follows:
auto vmbr0 iface vmbr0 inet static address 192.168.2.190/24 gateway 192.168.2.1 bridge-ports enp0s3 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/all/route_localnet post-up iptables -I INPUT -i vmbr0 -p tcp -d 192.168.2.190 --dport 22 -j DROP post-down iptables -D INPUT -i vmbr0 -p tcp -d 192.168.2.190 --dport 22 -j DROP post-up iptables -t nat -A PREROUTING -p tcp --dport 10022 -j DNAT --to-destination 127.0.0.1:22 post-down iptables -t nat -D PREROUTING -p tcp --dport 10022 -j DNAT --to-destination 127.0.0.1:22 post-up iptables -I FORWARD -p tcp -d 127.0.0.1 --dport 22 -j ACCEPT post-down iptables -D FORWARD -p tcp -d 127.0.0.1 --dport 22 -j ACCEPT post-up iptables -I INPUT -i lo -p tcp --dport 22 -j ACCEPT post-down iptables -D INPUT -i lo -p tcp --dport 22 -j ACCEPT
So what do we have here… let’s see.
The first part is nothing special, just adapt:
address 192.168.2.190/24 gateway 192.168.2.1 bridge-ports enp0s3 bridge-stp off bridge-fd 0
Then make sure, whenever this interface comes up (post-up directive), that the flag to enable forwarding of IP packets is set:
post-up echo 1 > /proc/sys/net/ipv4/ip_forward
Also make sure that local routing is enabled:
post-up echo 1 > /proc/sys/net/ipv4/conf/all/route_localnet
Since local routing is enabled we can use the localhost to be the receiver of the packets that came in on the public IP address on port 10022. We change them to be delvered to localhost on port 22 (the SSH deamon on Proxmox is by default listening not only on the public but also on the local address):
# Enable destination NATting from incoming addresses on port 10022 to localhost on port 22 post-up iptables -t nat -A PREROUTING -p tcp --dport 10022 -j DNAT --to-destination 127.0.0.1:22 post-down iptables -t nat -D PREROUTING -p tcp --dport 10022 -j DNAT --to-destination 127.0.0.1:22 # Allow forwarding of traffic from the public address to the local one post-up iptables -I FORWARD -p tcp -d 127.0.0.1 --dport 22 -j ACCEPT post-down iptables -D FORWARD -p tcp -d 127.0.0.1 --dport 22 -j ACCEPT # Allow incoming traffic on the localhost interface for the port 22 post-up iptables -I INPUT -i lo -p tcp --dport 22 -j ACCEPT post-down iptables -D INPUT -i lo -p tcp --dport 22 -j ACCEPT
As you might recognized I left out a rule from above. This one is optional and will force blocking traffic on port 22 to the Proxmox host:
post-up iptables -I INPUT -i vmbr0 -p tcp -d 192.168.2.190 --dport 22 -j DROP post-down iptables -D INPUT -i vmbr0 -p tcp -d 192.168.2.190 --dport 22 -j DROP
By default certain network traffic that originates from the same “management” subnet that the Proxmox host is a part of (in my case 192.168.2.0/24) will be allowed to pass firewall rules. It will be blocked if it comes from the outside world but not from within the management subnet. If you’re fine with that skip the rule.
Now apply the network changes (see this post for a little helper script):
$ sudo cp /etc/network/interfaces /etc/network/interfaces.new $ sudo systemctl restart networking.service
Flavour 2: Use a dedicated Linux bridge
Now instead of reusing the vmbr0 bridge, let’s create a new one (vmbr0 with IPv4/CIDR 10.0.99.1/30):
Hint: I am using that bridge only for the purpose of securing my SSH port. No VM or LXC container will ever be attached to it.
Then edit the network configuration /etc/network/interfaces, add a iptables rule:
auto vmbr99 iface vmbr99 inet static address 10.0.99.1/30 bridge-ports none bridge-stp off bridge-fd 0 # General IP forwarding echo 1 > /proc/sys/net/ipv4/ip_forward # Enable destination NATting from incoming addresses on port 10022 to localhost on port 22 post-up iptables -t nat -A PREROUTING -i vmbr0 -p tcp --dport 10022 -j DNAT --to-destination 10.0.99.1:22 post-down iptables -t nat -D PREROUTING -i vmbr0 -p tcp --dport 10022 -j DNAT --to-destination 10.0.99.1:22
Apply the network changes:
$ sudo cp /etc/network/interfaces /etc/network/interfaces.new $ sudo systemctl restart networking.service
Thats it. Much more pretty than the first flavour, isn’t it? The rule is nearly the same as above so I think there is no need for further explaination.
Final steps
Check if you are able to connect to the Proxmox host via SSH on port 10022. If that works go on and turn on the cluster firewall of Proxmox:
Software Versions used
- Proxmox: 7.1