Skip to content

Commit 8294e50

Browse files
committed
In beta testing
1 parent 1e7b879 commit 8294e50

1 file changed

Lines changed: 211 additions & 0 deletions

File tree

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
#!/bin/bash
2+
#
3+
#
4+
#
5+
########################################################################################
6+
####[ Global Variables ]################################################################
7+
8+
9+
## URL for retrieving the current Cloudflare IP ranges.
10+
readonly C_CLOUDFLARE_IPV4_RANGES_URL="https://www.cloudflare.com/ips-v4/"
11+
readonly C_CLOUDFLARE_IPV6_RANGES_URL="https://www.cloudflare.com/ips-v6/"
12+
13+
current_cloudflare_rule_numbers=()
14+
current_cloudflare_ip_ranges=()
15+
new_cloudflare_ip_ranges=()
16+
stage=0
17+
18+
19+
####[ Function ]########################################################################
20+
21+
22+
####
23+
# Description:
24+
# Check if a UFW rule exists for a specific IP address and port.
25+
#
26+
# Arguments:
27+
# - $1: ip (Required)
28+
# - The IP address to check.
29+
# - $2: port (Required)
30+
# - The port to check.
31+
#
32+
# Return:
33+
# - 0: The rule exists.
34+
# - ?: The rule does not exist.
35+
ufw_rule_exists() {
36+
local ip="$1"
37+
local port="$2"
38+
39+
ufw status | grep -qE "^${port}.*ALLOW.*${ip}.*$"
40+
}
41+
42+
####
43+
# Description:
44+
# Retrieves the rule number of all Cloudflare IP rules currently set in UFW, then
45+
# stores them in an array.
46+
#
47+
# Arguments:
48+
# - $1: string_to_grep (Required)
49+
# - The string to grep for in the UFW status output.
50+
# - Acceptable values:
51+
# - 0: "Cloudflare IP"
52+
# - 1: "Temporary rule"
53+
get_set_cloudflare_rule_numbers() {
54+
if (( $1 == 0 )); then
55+
local string_to_grep="Cloudflare IP"
56+
elif (( $1 == 1 )); then
57+
local string_to_grep="Temporary rule"
58+
else
59+
echo "Invalid argument: $1"
60+
exit 1
61+
fi
62+
63+
mapfile -t current_cloudflare_rule_numbers < <(
64+
ufw status numbered \
65+
| grep "$string_to_grep" \
66+
| awk -F'[][]' '{print $2}' \
67+
| sort -rn
68+
)
69+
}
70+
71+
####
72+
# Description:
73+
# Retrieves the IP addresses of all Cloudflare IP rules currently set in UFW, then
74+
# stores them in an array.
75+
get_set_cloudflare_ip_ranges() {
76+
while IFS= read -r line; do
77+
ip=$(echo "$line" | awk '{print $3}') # Extract the IP address.
78+
current_cloudflare_ip_ranges+=("$ip")
79+
done < <(sudo ufw status | grep "Cloudflare IP")
80+
}
81+
82+
####
83+
# Description:
84+
# Set the new Cloudflare IP ranges in UFW, retrieved from the Cloudflare website.
85+
set_new_cloudflare_ip_ranges() {
86+
for ip in "${new_cloudflare_ip_ranges[@]}"; do
87+
ufw_rule_exists "$ip" "80,443" \
88+
|| ufw allow from "$ip" to any port 80,443 proto tcp comment "Cloudflare IP"
89+
done
90+
}
91+
92+
####
93+
# Description:
94+
# Restores the previous (non-new) Cloudflare IP ranges in UFW.
95+
restore_current_cloudflare_ip_ranges() {
96+
for ip in "${current_cloudflare_ip_ranges[@]}"; do
97+
ufw_rule_exists "$ip" "80,443" \
98+
|| ufw allow from "$ip" to any port 80,443 proto tcp comment "Cloudflare IP"
99+
done
100+
}
101+
102+
####
103+
# Description:
104+
# Deletes all Cloudflare IP rules currently set in UFW.
105+
delete_set_cloudflare_rules() {
106+
get_set_cloudflare_rule_numbers "0"
107+
108+
for rule_num in "${current_cloudflare_rule_numbers[@]}"; do
109+
# TODO: Add configuration option to confirm deletion.
110+
yes | ufw delete "$rule_num"
111+
done
112+
}
113+
114+
####
115+
# Description:
116+
# Cleanup function to close ports 80 and 443 from any IP address.
117+
cleanup() {
118+
case $stage in
119+
2)
120+
delete_set_cloudflare_rules "1"
121+
;;
122+
3)
123+
echo "Potential error or interruption detected."
124+
echo "Restoring the previous Cloudflare IP ranges..."
125+
restore_current_cloudflare_ip_ranges
126+
delete_set_cloudflare_rules "1"
127+
;;
128+
4)
129+
echo "Potential error or interruption detected."
130+
echo "Restoring the previous Cloudflare IP ranges..."
131+
delete_new_cloudflare_ip_ranges
132+
restore_current_cloudflare_ip_ranges
133+
delete_set_cloudflare_rules "1"
134+
;;
135+
5)
136+
# Continue, as we are too far along to realistically undo anything
137+
;;
138+
*)
139+
echo "Invalid stage: $stage"
140+
;;
141+
esac
142+
}
143+
144+
145+
####[ Trap Logic ]######################################################################
146+
147+
148+
trap cleanup EXIT
149+
150+
151+
####[ Main ]############################################################################
152+
153+
154+
###
155+
### [ Initial Setup ]
156+
###
157+
158+
stage=1
159+
160+
get_set_cloudflare_ip_ranges
161+
mapfile -t new_cloudflare_ip_ranges < <(curl -s "$C_CLOUDFLARE_IPV4_RANGES_URL")
162+
mapfile -t new_cloudflare_ipv6_ranges < <(curl -s "$C_CLOUDFLARE_IPV6_RANGES_URL")
163+
164+
new_cloudflare_ip_ranges+=("${new_cloudflare_ipv6_ranges[@]}")
165+
unset new_cloudflare_ipv6_ranges
166+
167+
###
168+
### [ Opening ports 80 and 443 from any IP address ]
169+
###
170+
171+
stage=2
172+
173+
echo "Temporarily opening ports 80 and 443 from any IP address..."
174+
ufw allow from any to any port 80,443 proto tcp comment "Temporary rule"
175+
sleep 1 # Wait for the rule to take effect.
176+
177+
###
178+
### [ Removing the existing Cloudflare IP ranges ]
179+
###
180+
181+
stage=3
182+
183+
if (( ${#current_cloudflare_ip_ranges[@]} != 0 )); then
184+
echo "Removing the existing Cloudflare IP ranges..."
185+
delete_set_cloudflare_rules
186+
fi
187+
188+
sleep 1 # Wait for the rule to take effect.
189+
190+
###
191+
### [ Adding the new Cloudflare IP ranges ]
192+
###
193+
194+
stage=4
195+
196+
echo "Adding the new Cloudflare IPv4 and IPv6 ranges..."
197+
set_new_cloudflare_ip_ranges
198+
sleep 1 # Wait for the rule to take effect.
199+
200+
###
201+
### [ Finalizing ]
202+
###
203+
204+
stage=5
205+
206+
echo "Removing temporary rules..."
207+
ufw delete allow from any to any port 80,443 proto tcp
208+
sleep 1 # Wait for the rule to take effect.
209+
210+
echo "Done."
211+

0 commit comments

Comments
 (0)