Cấu hình firewall bảo vệ server Debian phong cách whitelist

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ặc all 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ặc dccp.

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.

Phản hồi về bài viết

Cùng thảo luận chút nhỉ!

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.