-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathip-to-ipset-script.sh
More file actions
171 lines (159 loc) · 8.42 KB
/
Copy pathip-to-ipset-script.sh
File metadata and controls
171 lines (159 loc) · 8.42 KB
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
#!/bin/bash
# ip-to-ipset-script.sh
# Convert list of IPs to script to block all through ipset and iptables.
# Version 20260705
#
# Copyright (C) 2025-2026 Michael McMahon
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# This script depends on these projects:
# ipset, iptables, bash, sed, echo, pwd, cd, mktemp, grep, sleep, date
# How do I use this script?
# 1. Place a list of IP addresses into the `ip-to-ipset-script.txt` file in
# the same directory as this script with one IP address on each line.
# 2. Run this command to run this script from the command line of a system that
# meets the dependencies.
# bash ip-to-ipset-script.sh
# 3. If successful, you will have a new file. Copy the file to the server that
# you want to block those addresses on. Replace
# `root@production.server:/root/ipset/` with the username, address, and
# directory that you want to place the files in.
# scp *-$(date +%Y%m%d).sh root@production.server:/root/ipset/
# 4. Login to the server.
# ssh root@production.server
# 5. Change to the directory where you store the files.
# cd ipset
# 6. Run the individual scripts like so.
# bash ddos-ipset-20260201.sh
# If you are applying several from today, run all of them with this BASH
# loop command:
# for i in $(ls *$(date +%Y%m%d).sh); do echo $i; bash $i; done
# If you are applying all of the scripts from a directory, run all of them
# with this BASH loop command:
# for i in $(ls *.sh); do echo $i; bash $i; done
set -euo pipefail
#set -euxo pipefail # DEBUG
# Where is the file with the IP list?
iplistfile="ip-to-ipset-script.txt"
# What is today?
today=$(date +%Y%m%d)
# What should the names of the ipsets start with?
name="ddos"
if [ ! -f "$iplistfile" ]; then
echo "ERROR: $iplistfile not found! Create it with one IP per line." >&2
exit 1
fi
# Keep only valid IP/CIDR lines so malformed input cannot become an ipset
# entry in the generated root script.
sane="$(grep -E '^[0-9A-Fa-f:.]+(/[0-9]{1,3})?$' "$iplistfile" || true)"
if [ -z "$sane" ]; then
echo "ERROR: no valid IP/CIDR lines in $iplistfile." >&2
exit 1
fi
echo -e "Building ipset script...\n"
echo "Building $name ipset script in $(pwd)/$name-ipset-$today.sh file..."
addressSet4="$name-4"
addressSet6="$name-6"
# Keep only valid IP/CIDR lines so a stray HTML error page cannot become an
# ipset entry in the generated root script.
sane="$(grep -E '^[0-9A-Fa-f:.]+(/[0-9]{1,3})?$' "$iplistfile" || true)"
if [ -z "$sane" ]; then
echo "WARNING: no valid IP/CIDR lines for AS$ASN; skipping." >&2
continue
fi
# Count how large the ipset needs to be. Do not waste extra resources.
ipv4max="$(printf '%s\n' "$sane" | grep -c -v ":" || true)"
ipv6max="$(printf '%s\n' "$sane" | grep -c ":" || true)"
{
# Build ipset script.
# Add header.
printf '#!/bin/bash\n\n# %s-ipset-%s.sh\n' "$name" "$today"
echo "# ipset script to handle $name generated by ip-to-ipset-script.sh"
echo "# from the https://github.com/TechnologyClassroom/firewallblockgen/"
echo -e "# repository.\n\n# How do I use this script?"
echo "# 1. Place this script on a server that you want to make a firewall"
echo "# adjustment to."
echo "# ssh root@production.server 'mkdir -p /root/ipset/'"
echo "# scp $name-ipset-$today.sh root@production.server:/root/ipset/"
echo "# 2. Login to the server."
echo "# ssh root@production.server"
echo "# 3. Change to the directory where you store the files."
echo "# cd ipset"
echo "# 4. Run the individual scripts like so."
echo "# bash $name-ipset-$today.sh"
echo -e "\nset -euo pipefail\n#set -euxo pipefail # DEBUG\n"
# IPv4
if [ "$ipv4max" -gt 0 ]; then
# Create two ipsets with the final name and a temporary name. ipset -N is
# an abbreviated way to write ipset create.
echo "ipset -exist -N $addressSet4 hash:net family inet maxelem $ipv4max"
echo "ipset -exist -N tmp$addressSet4 hash:net family inet maxelem $ipv4max"
# Send the data straight to `ipset restore` without an intermediary file
# or running individual `ipset add` commands. This uses heredoc format.
echo "cat << 'EOF' | ipset restore"
# Add CIDR/IP entries.
printf '%s\n' "$sane" \
| grep -v ":" \
| sed "s|^| add tmp$addressSet4 |" \
|| true
echo "EOF"
# Swap the temporary ipset over the live ipset. This way the the script
# can be run even if a previous version was applied to the system. ipset
# -W is an abbreviated way to write ipset swap.
echo "ipset -W $addressSet4 tmp$addressSet4"
# Destroy the extra temporary ipset. ipset -X is an abbreviated way to
# write ipset destroy.
echo "ipset -X tmp$addressSet4"
# Insert iptables rules only if they are not already present.
echo "iptables -C INPUT -m set --match-set $addressSet4 src -j DROP 2>/dev/null || iptables -I INPUT 1 -m set --match-set $addressSet4 src -j DROP"
echo "iptables -C FORWARD -m set --match-set $addressSet4 src -j DROP 2>/dev/null || iptables -I FORWARD 1 -m set --match-set $addressSet4 src -j DROP"
# Adds the ipset to iptables for only ports 80 and 443 for tcp connections.
# echo "iptables -C INPUT -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet4 src -j DROP 2>/dev/null || iptables -I INPUT 1 -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet4 src -j DROP"
# echo "iptables -C FORWARD -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet4 src -j DROP 2>/dev/null || iptables -I FORWARD 1 -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet4 src -j DROP"
fi
# IPv6
if [ "$ipv6max" -gt 0 ]; then
# Create two ipsets with the final name and a temporary name. ipset -N is
# an abbreviated way to write ipset create.
echo "ipset -exist -N $addressSet6 hash:net family inet6 maxelem $ipv6max"
echo "ipset -exist -N tmp$addressSet6 hash:net family inet6 maxelem $ipv6max"
# Send the data straight to `ipset restore` without an intermediary file
# or running individual `ipset add` commands. This uses heredoc format.
echo "cat << 'EOF' | ipset restore"
# Add CIDR/IP entries.
printf '%s\n' "$sane" \
| grep ":" \
| sed "s|^| add tmp$addressSet6 |" \
|| true
echo "EOF"
# Swap the temporary ipset over the live ipset. This way the the script
# can be run even if a previous version was applied to the system. ipset
# -W is an abbreviated way to write ipset swap.
echo "ipset -W $addressSet6 tmp$addressSet6"
# Destroy the extra temporary ipset. ipset -X is an abbreviated way to
# write ipset destroy.
echo "ipset -X tmp$addressSet6"
# Insert iptables rules only if they are not already present.
echo "ip6tables -C INPUT -m set --match-set $addressSet6 src -j DROP 2>/dev/null || ip6tables -I INPUT 1 -m set --match-set $addressSet6 src -j DROP"
echo "ip6tables -C FORWARD -m set --match-set $addressSet6 src -j DROP 2>/dev/null || ip6tables -I FORWARD 1 -m set --match-set $addressSet6 src -j DROP"
# Adds the ipset to iptables for only ports 80 and 443 for tcp connections.
# echo "ip6tables -C INPUT -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet6 src -j DROP 2>/dev/null || ip6tables -I INPUT 1 -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet6 src -j DROP"
# echo "ip6tables -C FORWARD -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet6 src -j DROP 2>/dev/null || ip6tables -I FORWARD 1 -p tcp -m multiport --dports 80,443 -m set --match-set $addressSet6 src -j DROP"
fi
echo -e "\nexit 0"
} > "$name-ipset-$today.sh"
echo -e "\nIf build was successful, ipset script can be found in the"
echo -e "$name-ipset-$today.sh directory.\n"
echo "Copy the scripts to the servers that need the block and run them."
exit 0