This example for two destinations (could be more)
Install socat
apt install socat
Add iptables rules to firewalld (works only with IP – replace hostname with associated IP)
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -p tcp -d redcap.vumc.org --dport 443 -j REDIRECT --to-ports 12345
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -p tcp -d api.twilio.com --dport 443 -j REDIRECT --to-ports 12346
firewall-cmd --reload
check results
firewall-cmd --direct --get-all-rules
create systemd template
touch /etc/systemd/system/socat@.service
content of template
[Unit]
Description=Socat tunnel for port %i
After=network.target
[Service]
ExecStart=
ExecStart=/usr/bin/socat TCP4-LISTEN=%i,fork,reuseaddr PROXY:your_proxy.ca:example.com:443,proxyport=3128
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
We’ll override example.com per instance using systemctl edit.
systemctl edit socat@12345
content of socat@12345
[Service]
ExecStart=
ExecStart=/usr/bin/socat TCP4-LISTEN:12345,fork,reuseaddr PROXY:your_proxy.ca:redcap.vumc.org:443,proxyport=3128
another
systemctl edit socat@12346
content of socat@12346
[Service]
ExecStart=
ExecStart=/usr/bin/socat TCP4-LISTEN:12346,fork,reuseaddr PROXY:your_proxy.ca:api.twilio.com:443,proxyport=3128
start and enable both services
systemctl daemon-reload
systemctl enable --now socat@12345
systemctl enable --now socat@12346
check service status
systemctl status socat@12345
systemctl status socat@12346
done.
Little extra: Script example to update dynamically changed IP for hostnames
#!/bin/bash
LOGFILE="/var/log/redcap_firewall_update.log"
MIN_PORT=12345
MAX_PORT=12351
DPORT=443 # destination port to match on (e.g., HTTPS)
# List of "hostname redirect_port"
HOSTS_AND_PORTS=(
"redcap.vumc.org 12345"
"api.twilio.com 12346"
"api.mosio.com 12347"
"api.sendgrid.com 12348"
"www.redcap-cats.org 12349"
"redcap.link 12350"
"cde.nlm.nih.gov 12351"
)
# Ensure the log file exists and is writable
touch "$LOGFILE" || { echo "Error: Cannot write to $LOGFILE"; exit 1; }
{
echo "[$(date)] --- Starting firewall rule cleanup and insertion ---"
echo ""
# Get all permanent direct rules
rules=$(firewall-cmd --permanent --direct --get-all-rules)
# Loop through each rule and remove those within the port range
while IFS= read -r rule; do
to_port=$(echo "$rule" | grep -oP -- '--to-ports\s+\K[0-9]+')
if [[ -n "$to_port" && "$to_port" -ge "$MIN_PORT" && "$to_port" -le "$MAX_PORT" ]]; then
echo "Removing rule: $rule"
firewall-cmd --permanent --direct --remove-rule $rule
fi
done <<< "$rules"
echo "Reloading firewall after cleanup..."
firewall-cmd --reload
echo ""
echo "Inserting updated rules..."
# Add new rules
for entry in "${HOSTS_AND_PORTS[@]}"; do
HOSTNAME=$(echo "$entry" | awk '{print $1}')
REDIRECT_PORT=$(echo "$entry" | awk '{print $2}')
echo ""
echo "Resolving $HOSTNAME..."
echo ""
IPV4S=$(dig +short "$HOSTNAME" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}')
for IP in $IPV4S; do
echo "Adding rule: $IP -> $REDIRECT_PORT"
firewall-cmd --permanent --direct --add-rule ipv4 nat OUTPUT 0 -p tcp -d "$IP" --dport $DPORT -j REDIRECT --to-ports $REDIRECT_PORT
done
done
echo "Reloading firewall after insertion..."
firewall-cmd --reload
echo ""
echo "[$(date)] --- Firewall update complete ---"
echo ""
} >> "$LOGFILE" 2>&1
Schedule it in crontab
0 */4 * * * /bin/bash /opt/update_redcap_firewall.sh
be happy!