Shell script giúp đơn giản hóa tác vụ dựng tường lửa bằng iptables theo phong cách whitelist.
Tắt IPv6
Mặc dù IPv6 là tương lai của Internet nhưng đến thời điểm hiện tại thì thực sự là chưa phổ biến. Vì vậy đây có thể là lỗ hổng cho những hacker khai thác do quản trị thường “bỏ quên” bảo mật giao thức này.
Tắt IPv6 bằng cách thêm 3 dòng sau vào cuối file /etc/sysctl.conf.
net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1
Reload lại sysctl để áp dụng cấu hình trên.
$ sudo sysctl -p
Kiểm tra lại xem IPv6 đã được tắt chưa bằng cách xem giá trị trong /proc/sys/net/ipv6/conf/all/disable_ipv6 có bằng 1 hay không.
$ cat /proc/sys/net/ipv6/conf/all/disable_ipv6 1
Cài đặt iptables-persistent
Cài đặt iptables-persistent để các rule của iptables được thiết lập lại mỗi khi hệ thống khởi động.
$ sudo apt-get install -y iptables-persistent
Các firewall rule của iptables-persistent được đặt tại /etc/iptables/rules.v4 cho IPv4, và /etc/iptables/rules.v6 cho IPv6. Tuy nhiên vì chúng ta đã tắt IPv6 nên sẽ bỏ qua file rules.v6.
Nội dung file theo cấu trúc của iptables-save nên ta sẽ dùng lệnh này để lưu rule sau khi thực hiện thành công.
Shell script tường lửa (firewall) kiểu whitelist
Script sau có thứ tự duyệt rule như sau:
- Cho phép các gói tin qua lại trong localhost.
- Cho phép các gói tin outgoing từ server này (coi như không bị dính backdoor).
- Cho phép các gói tin trong kết nối TCP đã được thiết lập (ESTABLISHED) từ client đến server.
- Cho phép gói tin tạo kết nối mới (NEW) từ client thỏa mãn cấu hình của WHITELIST tới Server.
- Chặn toàn bộ gói tin không thỏa mãn những điều kiện trên.
Cấu hình whitelist được đặt tại /etc/fwl.conf là một danh sách các rule, mỗi rule một dòng. Cú pháp mỗi dòng như sau:
[interface] [addresses] [ports] [protocol]
Trong đó:
- interface – Là tên của network interface mà server hỗ trợ. Giá trị
*nếu cho phép truy cập đến tất cả interface. - addresses – Một địa chỉ IP, một subnet, một danh sách IP/subnet (ngăn cách bằng dấu phẩy), một dải IP (ngăn cách bằng dấu gạch ngang) hoặc địa chỉ MAC của client nếu có. Giá trị
*nếu cho phép tất cả client. - ports – một cổng, một danh sách cổng (ngăn cách bằng dấu phẩy) hoặc dải cổng (ngăn cách bằng dấu hai chấm) dịch vụ trên Server cho phép client truy cập. Giá trị
*nếu cho phép tất cả các port. - protocol – Giao thức có thể là
udp,tcp,icmphoặcallhoặc*nếu cho phép tất cả các cổng. Ngoài ra giá trị này còn có thể là protocol được định nghĩa trong/etc/protocols. Lưu ý nếu port là một danh sách hoặc một dải thì protocol phải là một trong các giá trịtcp,udp,udplite,sctphoặcdccp.
Ví dụ nội dung của file cấu hình whitelist như sau:
/etc/fwl.sh
* 192.168.1.2 * * * 192.168.1.0/24,123.100.222.111 5600,10000:20000 udp eth1 * 80,443 * tcp
/usr/local/bin/fwl.sh
#!/bin/bash
WHITELIST_RULES="/etc/fwl.conf"
PERSISTENT_RULES="/etc/iptables/rules.v4"
IPT="/sbin/iptables"
# Validation helpers
is_ip() { [[ "$1" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(\/[0-9]{1,2})?(,[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(\/[0-9]{1,2})?)*$ ]]; }
is_iprange() { [[ "$1" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}-([0-9]{1,3})(\.[0-9]{1,3}){0,3}$ ]]; }
is_mac() { [[ "$1" =~ ^[0-9A-Za-z]{2}:[0-9A-Za-z]{2}:[0-9A-Za-z]{2}:[0-9A-Za-z]{2}:[0-9A-Za-z]{2}:[0-9A-Za-z]{2}$ ]]; }
is_port() { [[ "$1" =~ ^[0-9]{1,5}$ ]]; }
is_multiport() { [[ "$1" =~ ^([0-9]{1,5}:[0-9]{1,5}|[0-9]{1,5}(:[0-9]{1,5})?(,[0-9]{1,5}(:[0-9]{1,5})?)+)$ ]]; }
is_prot() { rm_cmnts /etc/protocols | awk '{print $1}' | grep -wq ${1}; }
is_if() { ip link show | grep -E '^[0-9]+: ' | awk -F': ' '{print $2}' | grep -wq ${1}; }
# Remove all comments, blank lines and redundant spaces in a given file
rm_cmnts()
{
sed -r -e 's/#.*//g' -e 's/\s+/ /g' -e '/^\s*$/d' "${1}"
}
# Save all current iptables rules to a file
ipt_save()
{
${IPT}-save > "${1}"
# OPTIONAL: Reset the accounting counters, remove comments and blank lines
sed -i -r -e 's/\[[0-9]+:[0-9]+\]/\[0:0\]/g' -e 's/#.*//g' -e '/^$/d' "${1}"
}
# Restore the iptables rules from a file
ipt_restore()
{
${IPT}-restore < "${1}"
}
ipt_status()
{
${IPT} -L -v -n --line-numbers | more
}
# Clear all existing iptables rules
ipt_reset()
{
# Flush all rules
${IPT} -F
${IPT} -X
${IPT} -t nat -F
${IPT} -t nat -X
${IPT} -t mangle -F
${IPT} -t mangle -X
${IPT} -P INPUT ACCEPT
${IPT} -P FORWARD ACCEPT
${IPT} -P OUTPUT ACCEPT
${IPT} -Z
}
ipt_apply()
{
if [ -f "${1}" ]; then
# Clear all existing rules
ipt_reset
# Create a custom chain and forward packets from INPUT and FORWARD chain to it
${IPT} -N Firewall-INPUT
${IPT} -A INPUT -j Firewall-INPUT
${IPT} -A FORWARD -j Firewall-INPUT
# Allow connection established from this host
${IPT} -A Firewall-INPUT -i lo -j ACCEPT
${IPT} -A Firewall-INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# Parse the configuration file, create and apply the WHITELISTed firewall rules
rm_cmnts ${1} | while read if addr port prot; do
local params=()
# Network interface name
if is_if ${if}; then
params+=( -i ${if} )
elif [ "${if}" != "*" ]; then
echo "Error: Invalid interface '${if}'" >&2
return 1
fi
# Protocol
if is_prot ${prot} || [ "${prot}" = "all" ]; then
params+=( -p ${prot} )
elif [ "${prot}" != "*" ]; then
echo "Error: Invalid protocol '${prot}'" >&2
return 1
fi
# IP range
if is_iprange ${addr}; then
params+=( -m iprange --src-range ${addr} )
# Single IP address
elif is_ip ${addr}; then
params+=( -s ${addr} )
# MAC address
elif is_mac ${addr}; then
params+=( -m mac --mac-source ${addr} )
# Invalid address
elif [ "${addr}" != "*" ]; then
echo "Error: Invalid address '${addr}'" >&2
return 1
fi
# Single port
if is_port ${port}; then
params+=( --dport ${port} )
# Port list and/or range
elif is_multiport ${port}; then
params+=( -m multiport --dports ${port} )
# Invalid port format
elif [ "${port}" != "*" ]; then
echo "Error: Invalid port '${port}'" >&2
return 1
fi
# Apply the rule
${IPT} -A Firewall-INPUT "${params[@]}" -m state --state NEW -j ACCEPT || return $?
done
# Drop all other packets
${IPT} -A Firewall-INPUT -j REJECT --reject-with icmp-host-prohibited
fi
}
if [ "$EUID" -ne 0 ]; then
echo "You must be root to use this script!" >&2
exit 1
fi
if [ $# -eq 0 ]; then
# Backup the old rules to restore in case of failure
OLD_RULES=$(mktemp)
ipt_save ${OLD_RULES}
# Read the configuration file and apply the new rules
# Save the new rules if success, otherwise restore the previous rules
ipt_apply ${WHITELIST_RULES} && \
ipt_save ${PERSISTENT_RULES} || \
ipt_restore ${OLD_RULES}
rm -f ${OLD_RULES}
else
case "$1" in
reset)
ipt_reset
ipt_save ${PERSISTENT_RULES}
;;
status)
ipt_status
;;
*)
echo "Usage: $(basename ${0}) [reset|status]" >&2
exit 3
;;
esac
fi
Phân quyền thực thi cho script này
chmod +x /usr/local/bin/fwl.sh
Để sử dụng script, ta cần quyền sudo. Ví dụ để áp dụng rule mới mỗi khi chỉnh sửa file /etc/fwl.conf ta sử dụng như sau:
sudo fwl.sh
Để xóa hết rule, đặt tường lửa về trạng thái mặc định:
sudo fwl.sh reset
Mỗi khi script được thực hiện thành công (không có lỗi gì trong quá trình set rule) thì hiện trạng của iptables sẽ được lưu vào /etc/iptables/rules.v4 để được tái lập trong lần khởi động tiếp theo mà không cần chạy lại script này.
Đây là những cấu hình thuộc dạng đơn giản nhất của iptables đã được hệ thống hóa trong một file shell script. Ngoài ra, ta có thể bổ sung các rule cho iptables vào script, ví dụ như khả năng ứng phó với tấn công DDoS chẳng hạn.








