We have been using CSF on a virtualized (OpenVZ) environment successfully for a while combining venet and bridged interfaces so we can use public IPs + local addressed virtual systems.
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.0.0.100 0.0.0.0 255.255.255.255 UH 0 0 0 venet0
xxx.xxx.xxx.24 0.0.0.0 255.255.255.255 UH 0 0 0 venet0
xxx.xxx.xxx.0 0.0.0.0 255.255.255.0 U 0 0 0 vmbr0
10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 vmbr10
0.0.0.0 xxx.xxx.xxx.254 0.0.0.0 UG 0 0 0 vmbr0
We route traffic of "local" ips via an iptables rule:
iptables -t nat -A POSTROUTING -s "10.0.0.0/24" -o vmbr0 -j MASQUERADE
/proc/sys/net/ipv4/ip_forward value is obvioyly set to 1
The host and venet networked machines run their own CSF installation that creates all the firewall rules for this setup which includes the possibility to create "redirects" to internal IPs.
With this we can open specific ports on the host IP that are served by the local internal virtual machines on local based network. Among the long list of firewall rules that CSF creates are:
Chain PREROUTING (policy ACCEPT 53M packets, 15G bytes)
num pkts bytes target prot opt in out source destination
1 0 0 DNAT tcp -- !lo * 0.0.0.0/0 XXX.XXX.XXX.184 tcp dpt:5100 to:10.0.0.100:5000
Chain POSTROUTING (policy ACCEPT 9700K packets, 615M bytes)
num pkts bytes target prot opt in out source destination
3 0 0 SNAT tcp -- * !lo 0.0.0.0/0 10.0.0.100 to:XXX.XXX.XXX.184
8 10 600 MASQUERADE all -- * vmbr0 10.0.0.0/24 0.0.0.0/0
The redirect feature on CSF firewall works but the IP reported is the host one, not the originating IP so we can not limit it with another CSF instance (or simple firewall rule) on the destination virtual system. We understand that being on PREROUTING they can not be limited on on the host CSF either.
The CSF readme actually states "All redirections to another IP address will always appear on the destination server with the source of this server, not the originating IP address." so this is a standard feature.
CSF supports a postrules.sh file where we could include the NAT rules manually but we are unsure if with IPTABLES SNAT and/or DNAT it is possible to pass the "real" source IP to the destination of the NAT IP.