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
,icmp
hoặcall
hoặ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
,sctp
hoặ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.