backend/e2e/bin/forward-ports.sh (view raw)
1#!/bin/sh
2
3#
4# Adds TCP/UDP port forwarding rules to the pf firewall (MacOS/BSD).
5#
6# Adds rules for both TCP and UDP in addition to those from /etc/pf.conf.
7# Requires an existing rdr-anchor entry in /etc/pf.conf.
8# Only adds rules temporarily, without changing any files.
9#
10# Usage: ./forward-ports.sh [[nic:]port=[ip:]port [...]]
11#
12# If no network interface is given, forwards from all interfaces.
13# If no IP is given, forwards to 127.0.0.1.
14# If no port forwarding rule is given, resets to the rules from /etc/pf.conf.
15#
16# e.g. forwarding ports 80 and 443 on network interface en0 to ports 8080 and
17# 8443 on localhost respectively:
18# ./forward-ports.sh en0:80=8080 en0:443=8443
19#
20# Copyright 2019, Sebastian Tschan
21# https://blueimp.net
22#
23# Licensed under the MIT license:
24# https://opensource.org/licenses/MIT
25#
26
27set -e
28
29RULES=
30NEWLINE='
31'
32
33print_usage_exit() {
34 if [ -n "$RULES" ]; then
35 printf '\nError in custom rules:\n%s\n' "$RULES" >&2
36 fi
37 echo "Usage: $0 [[nic:]port=[ip:]port [...]]" >&2
38 exit 1
39}
40
41print_nat_rules() {
42 echo
43 echo 'Loaded NAT rules:'
44 sudo pfctl -s nat 2>/dev/null
45 echo
46}
47
48# Print usage and exit if option arguments like "-h" are used:
49if [ "${1#-}" != "$1" ]; then print_usage_exit; fi
50
51while test $# -gt 0; do
52 # Separate the from=to parts:
53 from=${1%=*}
54 to=${1#*=}
55 # If from part has a nic defined, extract it, else forward from all:
56 case "$from" in
57 *:*) nic="on ${from%:*}";;
58 *) nic=;;
59 esac
60 # Extract the port to forward from:
61 from_port=${from##*:}
62 # If to part has an IP defined, extract it, else forward to 127.0.0.1:
63 case "$to" in
64 *:*) to_ip=${to%:*};;
65 *) to_ip=127.0.0.1;;
66 esac
67 # Extract the port to forward to:
68 to_port=${to##*:}
69 # Create the packet filter (pf) forwarding rule for both TCP and UDP:
70 rule=$(
71 printf \
72 'rdr pass %s inet proto %s from any to any port %s -> %s port %s' \
73 "$nic" '{tcp udp}' "$from_port" "$to_ip" "$to_port"
74 )
75 # Add it to the list of rules:
76 RULES="$RULES$rule$NEWLINE"
77 shift
78done
79
80# Add the rules after the line matching "rdr-anchor" in /etc/pf.conf, print the
81# combined rules to STDOUT and load the rules into pf from STDIN.
82# Finally, display the loaded NAT rules or print the script usage on failure:
83# shellcheck disable=SC2015
84printf %s "$RULES" | sed -e '/rdr-anchor/r /dev/stdin' /etc/pf.conf |
85sudo pfctl -Ef - 2>/dev/null && print_nat_rules || print_usage_exit