-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathudm-firewall.sh
executable file
·286 lines (237 loc) · 12.4 KB
/
udm-firewall.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
#!/bin/bash
######################################################################################
#
# Description:
# ------------
# If different corporate and guest networks are defined Unifi Network, these
# networks ar not separated in default configuaration, as a default allow
# principle is implemented on UDM Pro. Thus, coporate LAN to corporate LAN
# and guest LAN to guest LAN traffic is allowed per default. In addition it is
# not possible to restrict inter corporate and guest IPv6 traffic via GUI.
# This script adds some rules to sepearte all defined LAN and Guest networks
# (see https://nerdig.es/udm-pro-netzwerktrennung-1/).
# As firewall rules may be reseted whenever ruleset is changed in GUI, this
# file should be executed regularly (e.g. via systemd timer or cron) to ensure
# that firewall is permanently activated.
#
######################################################################################
######################################################################################
#
# Configuration
#
# Add rules to separate LAN interfaces
separate_lan=true
# Add rules to separate Guest interfaces
separate_guest=true
# interfaces listed in exclude will not be separted and can still access
# the other VLANs. Multiple interfaces are to be separated by spaces.
exclude="br20"
# Add rule to allow established and related network traffic coming in to LAN interface
allow_related_lan=true
# Add rule to allow established and related network traffic coming in to guest interface
allow_related_guest=true
# OBSOLETE: Remove predefined NAT rules
# Starting with UnifiOS Version 4.x NAT can be disabled via GUI)
disable_nat=false
# List of commands that should be executed before firewall rules are adopted (e.g. setup
# wireguard interfaces, before adopting ruleset to ensure wireguard interfaces are
# considerd when separating VLANs).
# It is recommended to use absolute paths for the commands.
commands_before=(
"[ -x /data/custom/wireguard/udm-wireguard.sh ] && /data/custom/wireguard/udm-wireguard.sh"
""
)
# List of commands that should be executed after firewall rules are adopted.
# It is recommended to use absolute paths for the commands.
commands_after=(
"[ -x /data/custom/ipv6/udm-ipv6.sh ] && /data/custom/ipv6/udm-ipv6.sh"
""
)
#
# No further changes should be necessary beyond this line.
#
######################################################################################
# set scriptname
me=$(basename $0)
# include local configuration if available
[ -e "$(dirname $0)/${me%.*}.conf" ] && source "$(dirname $0)/${me%.*}.conf"
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Exectue Scripts defined in $commands_before
#
for cmd in "${commands_before[@]}"; do
eval "$cmd"
done
# Buffer IPv4 ruleset
ipv4rules=$(/usr/sbin/iptables --list-rules)
function in_ip4rules () { [[ $ipv4rules =~ ${1// /\\ } ]] || return 1; }
# Buffer IPv6 ruleset
ipv6rules=$(/usr/sbin/ip6tables --list-rules)
function in_ip6rules () { [[ $ipv6rules =~ ${1// /\\ } ]] || return 1; }
# Get list of relevant LAN interfaces and total number of interfaces
lan_if=$(echo -e "$ipv4rules" | /usr/bin/awk '/^-A UBIOS_FORWARD_IN_USER.*-j UBIOS_LAN_IN_USER/ { print $4 }')
lan_if_count=$(echo $lan_if | /usr/bin/wc -w)
# Get list of relevant guest interfaces and total number of interfaces
guest_if=$(echo -e "$ipv4rules" | /usr/bin/awk '/^-A UBIOS_FORWARD_IN_USER.*-j UBIOS_GUEST_IN_USER/ { print $4 }')
guest_if_count=$(echo $guest_if | /usr/bin/wc -w)
# Get list of WAN interfacess
wan_if=$(echo -e "$ipv4rules" | /usr/bin/awk '/^-A UBIOS_FORWARD_IN_USER.*-j UBIOS_WAN_IN_USER/ { print $4 }')
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# add allow related/established to UBIOS_LAN_IN_USER if requested
#
if [ $allow_related_lan == "true" ]; then
rule="-A UBIOS_LAN_IN_USER -m conntrack --ctstate RELATED,ESTABLISHED.*-j RETURN"
in_ip4rules "$rule" || /usr/sbin/iptables -I UBIOS_LAN_IN_USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
in_ip6rules "$rule" || /usr/sbin/ip6tables -I UBIOS_LAN_IN_USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
fi
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# LAN separation
#
if [ $separate_lan == "true" ]; then
# prepare ip(6)tables chains lan_separation
in_ip4rules "-N lan_separation" || (/usr/sbin/iptables -N lan_separation &> /dev/null && /usr/bin/logger "$me: IPv4 chain created (lan_separation)")
in_ip6rules "-N lan_separation" || (/usr/sbin/ip6tables -N lan_separation &> /dev/null && /usr/bin/logger "$me: IPv6 chain created (lan_separation)")
# allow Outbound internet traffic to WAN
for o in $wan_if; do
# Reject Outbound RFC1918 to deny DMZ access
rule="-A lan_separation -d 192.168.0.0/16 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
rule="-A lan_separation -d 172.16.0.0/12 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
rule="-A lan_separation -d 10.0.0.0/8 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
rule="-A lan_separation -d 169.254.0.0/16 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
# Reject Outbound ULA to deny DMZ access
rule="-A lan_separation -d fc00::/7 -o $o -j REJECT"
in_ip6rules "$rule" || /usr/sbin/ip6tables $rule
# Allow all other traffic
rule="-A lan_separation -o $o -j RETURN"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
in_ip6rules "$rule" || /usr/sbin/ip6tables $rule
done
# Add rules to separate LAN-VLANs to chain lan_separation
for i in $lan_if; do
case "$exclude " in
*"$i "*)
/usr/bin/logger "$me: Excluding $i from LAN separation as requested in config."
;;
*)
rule="-A lan_separation -i $i -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
in_ip6rules "$rule" || /usr/sbin/ip6tables $rule
;;
esac
done
# add IPv4 rule to include rules in chain lan_separation
if ! in_ip4rules "-A UBIOS_LAN_IN_USER -j lan_separation" ; then
rules=$(/usr/sbin/iptables -L UBIOS_LAN_IN_USER --line-numbers | /usr/bin/awk 'END { print $1 }')
v4_idx=$(/usr/bin/expr $rules - $lan_if_count)
/usr/sbin/iptables -I UBIOS_LAN_IN_USER $v4_idx -j lan_separation
fi
# add IPv6 rule to include rules in chain lan_separation
if ! in_ip6rules "-A UBIOS_LAN_IN_USER -j lan_separation" ; then
v6_idx=$(/usr/sbin/ip6tables -L UBIOS_LAN_IN_USER --line-numbers | /usr/bin/awk '/match-set UBIOS_ALL_NETv6_br[0-9]+ src \/\*/{ print $1; exit}')
/usr/sbin/ip6tables -I UBIOS_LAN_IN_USER $v6_idx -j lan_separation
fi
else
/usr/bin/logger "$me: Separation for guest VLANs deactivated. Starting clean up..."
if in_ip4rules "-N lan_separation"; then
/usr/sbin/iptables -F lan_separation && /usr/bin/logger "$me: Existing IPv4 chain lan_separation flushed."
/usr/sbin/iptables -X lan_separation && /usr/bin/logger "$me: Existing IPv4 chain lan_separation deleted."
fi
if in_ip6rules "-N lan_separation"; then
/usr/sbin/ip6tables -F lan_separation && /usr/bin/logger "$me: Existing IPv6 chain lan_separation flushed."
/usr/sbin/ip6tables -X lan_separation && /usr/bin/logger "$me: Existing IPv6 chain lan_separation deleted."
fi
fi
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# add allow related/established to UBIOS_GUEST_IN_USER if requested
#
if [ $allow_related_guest == "true" ]; then
rule="-A UBIOS_GUEST_IN_USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN"
in_ip4rules "$rule" || /usr/sbin/iptables -I UBIOS_GUEST_IN_USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
in_ip6rules "$rule" || /usr/sbin/ip6tables -I UBIOS_GUEST_IN_USER 1 -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
fi
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Guest separation
#
if [ $separate_guest == "true" ]; then
# prepare ip(6)tables chains guest_separation
in_ip4rules "-N guest_separation" || (/usr/sbin/iptables -N guest_separation &> /dev/null && /usr/bin/logger "$me: IPv4 chain created (guest_separation)")
in_ip6rules "-N guest_separation" || (/usr/sbin/ip6tables -N guest_separation &> /dev/null && /usr/bin/logger "$me: IPv6 chain created (guest_separation)")
# allow Outbound internet traffic to WAN
for o in $wan_if; do
# Reject Outbound RFC1918 to deny DMZ access
rule="-A guest_separation -d 192.168.0.0/16 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
rule="-A guest_separation -d 172.16.0.0/12 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
rule="-A guest_separation -d 10.0.0.0/8 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
rule="-A guest_separation -d 169.254.0.0/16 -o $o -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
# Reject Outbound ULA to deny DMZ access
rule="-A guest_separation -d fc00::/7 -o $o -j REJECT"
in_ip6rules "$rule" || /usr/sbin/ip6tables $rule
# Allow all other traffic
rule="-A guest_separation -o $o -j RETURN"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
in_ip6rules "$rule" || /usr/sbin/ip6tables $rule
done
# Add rules to chain guest_separation
for i in $guest_if; do
case "$exclude " in
*"$i "*)
/usr/bin/logger "$me: Excluding $i from guest VLAN separation as requested in config."
;;
*)
rule="-A guest_separation -i $i -j REJECT"
in_ip4rules "$rule" || /usr/sbin/iptables $rule
in_ip6rules "$rule" || /usr/sbin/ip6tables $rule
;;
esac
done
if ! in_ip6rules "-A UBIOS_GUEST_IN_USER -j guest_separation" ; then
# add IPv4 rule to include rules in chain guest_separation
rules=$(/usr/sbin/iptables -L UBIOS_GUEST_IN_USER --line-numbers | /usr/bin/awk 'END { print $1 }')
v4_idx=$(expr $rules - $guest_if_count)
/usr/sbin/iptables -I UBIOS_GUEST_IN_USER $v4_idx -j guest_separation
fi
# add IPv6 rule to include rules in chain guest_separation
if ! in_ip6rules "-A UBIOS_GUEST_IN_USER -j guest_separation" ; then
v6_idx=$(/usr/sbin/ip6tables -L UBIOS_GUEST_IN_USER --line-numbers | /usr/bin/awk '/RETURN.*match-set UBIOS_ALL_NETv6_br[0-9]+ src \/\*/ { print $1; exit }')
/usr/sbin/ip6tables -I UBIOS_GUEST_IN_USER $v6_idx -j guest_separation
fi
else
/usr/bin/logger "$me: Separation for guest VLANs deactivated. Starting clean up..."
if in_ip4rules "-N guest_separation"; then
/usr/sbin/iptables -F guest_separation && /usr/bin/logger "$me: Existing IPv4 chain guest_separation flushed."
/usr/sbin/iptables -X guest_separation && /usr/bin/logger "$me: Existing IPv4 chain guest_separation deleted."
fi
if in_ip6rules "-N guest_separation"; then
/usr/sbin/ip6tables -F guest_separation && /usr/bin/logger "$me: Existing IPv6 chain guest_separation flushed."
/usr/sbin/ip6tables -X guest_separation && /usr/bin/logger "$me: Existing IPv6 chain guest_separation deleted."
fi
fi
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# disable NAT if requested
#
if [ $disable_nat == "true" ]; then
# identify MASQUERADE jump target in UBIOS_POSTROUTING_USER_HOOK chain
# which will be added per default for UBIOS_ADDRv4_ethX (eth8/eth9) to
# manage NAT throught WAN
rules=$(/usr/sbin/iptables -t nat -L UBIOS_POSTROUTING_USER_HOOK --line-numbers | \
/usr/bin/awk '/MASQUERADE .* UBIOS_.*ADDRv4_eth. src/ { print $1 }')
# for each rule identified we issue a delete operation in reverse
# order so that UBIOS_POSTROUTINE_USER_HOOK will really only contain
# NAT rules a user manually defined in the Network UI.
for rulenum in $(echo -e "${rules}" | /usr/bin/sort -r); do
/usr/sbin/iptables -t nat -D UBIOS_POSTROUTING_USER_HOOK ${rulenum}
done
fi
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# Exectue Scripts defined in $commands_after
#
for cmd in "${commands_after[@]}"; do
eval "$cmd"
done