#!/bin/bash

function cleanNetworkConfig()
{
    ####delete network config
    # Define the file to edit
    CONFIG_FILE="/etc/network/interfaces.d/50-cloud-init"
    BACKUP_FILE="/etc/network/interfaces.d/50-cloud-init.bak"

    # Check if the configuration file exists
    if [ ! -f "$CONFIG_FILE" ]; then
        echo "Configuration file $CONFIG_FILE does not exist."
        exit 1
    fi

    # Create a backup of the configuration file
    echo "Creating a backup of $CONFIG_FILE at $BACKUP_FILE..."
    cp "$CONFIG_FILE" "$BACKUP_FILE"

    # Remove all configurations related to eth2
    echo "Removing eth2 configurations from $CONFIG_FILE..."
    sed -i '/^\s*auto eth2/,/^$/d' "$CONFIG_FILE"
    sed -i '/^\s*iface eth2/,/^$/d' "$CONFIG_FILE"


    ip addr flush dev eth2
    ip -6 addr flush dev eth2
    ip link set eth2 down

    echo "Configurations for eth2 have been removed and changes saved."
}
############################
# clean public route
############################
function cleanOtherRoute(){

    # The default gateway to keep
    TARGET_GATEWAY="169.254.168.249"

    # Get all default routes except the target gateway
    DEFAULT_ROUTES=$(ip route | grep '^default' | grep -v "via $TARGET_GATEWAY")

    # Check if there are routes to delete
    if [ -z "$DEFAULT_ROUTES" ]; then
        echo "No default routes to delete, only $TARGET_GATEWAY exists."
        return 0
    fi

    # Loop through and delete each default route except the target gateway
    echo "Deleting default routes except $TARGET_GATEWAY..."
    while read -r ROUTE; do
        # Extract the interface and gateway from the route
        GATEWAY=$(echo "$ROUTE" | awk '{print $3}')
        INTERFACE=$(echo "$ROUTE" | awk '{print $5}')

        # Delete the default route
        echo "Deleting default route via $GATEWAY on $INTERFACE..."
        ip route del default via "$GATEWAY" dev "$INTERFACE"
    done <<< "$DEFAULT_ROUTES"

    echo "Default route cleanup completed. Only $TARGET_GATEWAY remains."
}
############################
# config firewall role
# Allow Form: 0.0.0.0/0 169.254.168.254 53(TCP and UDP)
# Allow Form: 0.0.0.0/0 dst:169.254.168.254 ICMP
# Allow Form: 169.254.168.248/29 
# Allow Form: 10.0.0.0/24 
############################
function configFirewall(){

    echo "Configuring Firewall..."
    # Flush existing rules
    echo "Flushing existing rules..."
    nft flush ruleset

    # Create the 'firewall' table
    echo "Creating nftables table and chains..."
    nft add table inet firewall

    # Add input chain with default policy 'drop'
    nft add chain inet firewall input { type filter hook input priority 0 \; policy drop \; }

    # Allow loopback traffic
    nft add rule inet firewall input iif "lo" accept

    # Allow established and related connections
    nft add rule inet firewall input ct state established,related accept

    # Allow DNS traffic (TCP and UDP) to 169.254.168.254:53
    nft add rule inet firewall input ip daddr 169.254.168.254 tcp dport 53 accept
    nft add rule inet firewall input ip daddr 169.254.168.254 udp dport 53 accept

    # Allow ICMP traffic (ping) to 169.254.168.254
    nft add rule inet firewall input ip daddr 169.254.168.254 icmp type echo-request accept
    nft add rule inet firewall input ip daddr 169.254.168.254 icmp type echo-reply accept

    # Allow all traffic from 169.254.168.248/29
    nft add rule inet firewall input ip saddr 169.254.168.248/29 accept

    # Allow all traffic from 10.0.0.0/24
    nft add rule inet firewall input ip saddr 10.0.0.0/24 accept

    # Drop all other traffic
    nft add rule inet firewall input drop

    # Add forward chain with default policy 'drop'
    nft add chain inet firewall forward { type filter hook forward priority 0 \; policy drop \; }

    # Add output chain with default policy 'accept'
    nft add chain inet firewall output { type filter hook output priority 0 \; policy accept \; }

    # Save rules to configuration file
    echo "Saving rules to /etc/nftables.conf..."
    nft list ruleset > /etc/nftables.conf

    # Enable and restart nftables service
    echo "Ensuring nftables service is enabled..."
    systemctl enable nftables
    systemctl restart nftables

    echo "nftables configuration completed successfully."
}
############################
# Configure Dnsdist
############################
function configDnsdist(){
    echo "Configuring Dnsdist..."
    cat > /etc/dnsdist/dnsdist.conf <<EOF
setLocal('$VIP:53')
setACL("0.0.0.0/0")
setServerPolicy(leastOutstanding)
addAction(AllRule(), LogAction())

EOF

    # Add DNS servers dynamically with UDP health checks
    for dns_server in "${DNS_SERVERS[@]}"; do
        echo "newServer({address=\"${dns_server}:53\"})" >> /etc/dnsdist/dnsdist.conf
    done

    # Enable and restart Dnsdist
    echo "Starting and enabling Dnsdist..."
    systemctl enable --now dnsdist
    systemctl restart dnsdist

}

###########################
# Install package
###########################
function installPackages(){
    # dnsdist apt source
    echo "deb [signed-by=/etc/apt/keyrings/dnsdist-19-pub.asc] http://repo.powerdns.com/debian bookworm-dnsdist-19 main" > /etc/apt/sources.list.d/pdns.list
    sudo install -d /etc/apt/keyrings; 
    curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo tee /etc/apt/keyrings/dnsdist-19-pub.asc 
    # Update system and install required packages
    echo "Updating system and installing Keepalived dnsdist nftables..."
    apt update
    apt install -y keepalived dnsdist nftables

}

#########################
function configKernelParameters(){

    cat > /etc/sysctl.conf <<EOF
# 增加可用文件描述符限制
fs.file-max = 2097152

# TCP/IP 优化
net.ipv4.tcp_tw_reuse = 1            # 允许重用 TIME-WAIT 状态的套接字
net.ipv4.tcp_syncookies = 1          # 启用 SYN Cookies，防止 SYN Flood 攻击
net.ipv4.tcp_max_syn_backlog = 4096  # 增加 SYN 队列长度
net.ipv4.tcp_fin_timeout = 15        # 缩短 TIME-WAIT 超时时间
net.core.somaxconn = 4096            # 增加队列中最大挂起连接数
net.core.netdev_max_backlog = 8192   # 增加网络设备接收队列大小

# IP 路由转发（用于 keepalived）
net.ipv4.ip_forward = 1              # 启用路由转发
net.ipv4.conf.all.send_redirects = 0 # 禁用 ICMP 重定向
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0 # 禁用接收 ICMP 重定向
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.rp_filter = 0      # 禁用反向路径过滤（避免 keepalived 的 VRRP 问题）

# 增加 TCP 缓存大小
net.ipv4.tcp_rmem = 4096 87380 6291456 # TCP 接收缓存大小
net.ipv4.tcp_wmem = 4096 65536 6291456 # TCP 发送缓存大小
net.core.rmem_max = 16777216          # 增大接收缓存上限
net.core.wmem_max = 16777216          # 增大发送缓存上限

# 增大 UDP 缓存（DNS 用于高并发查询）
net.core.rmem_default = 262144
net.core.wmem_default = 262144

# 禁用内存过度交换
vm.swappiness = 10                    # 降低交换分区使用的优先级
vm.dirty_ratio = 15                   # 限制脏页占比
vm.dirty_background_ratio = 5         # 限制后台刷新的脏页占比

# 防止 ARP 攻击
net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.default.arp_announce = 2

# 禁止 IPv4 ICMP 广播回显
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1

# 限制伪造数据包
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1

EOF
    sudo sysctl -p /etc/sysctl.conf
}

function configKeepalived(){
    if [[ $HOSTNAME =~ proxy01-dns ]]; then
        PRIORITY=$PRIORITY_MASTER
    elif [[ $HOSTNAME =~ proxy02-dns ]]; then
        PRIORITY=$PRIORITY_BACKUP
    else
        echo "Error: Unable to determine the role for this machine. Ensure hostname follows naming convention 'proxyXX-dns'."
        exit 1
    fi

    echo "Configuring this machine with role $ROLE and priority $PRIORITY."


    # Configure Keepalived
    echo "Configuring Keepalived..."
    cat > /etc/keepalived/keepalived.conf <<EOF
! Configuration File for keepalived

vrrp_instance VI_1 {
    state BACKUP                     # Always BACKUP to avoid preemption
    interface $INTERFACE             # Monitoring network interface
    virtual_router_id $VRRP_ID       # VRRP ID
    priority $PRIORITY               # Priority based on role
    advert_int 1                     # Advertisement interval in seconds
    nopreempt                        # Prevent preemption when higher-priority node recovers

    authentication {
        auth_type PASS
        auth_pass $AUTH_PASS
    }

    virtual_ipaddress {
        $VIP
    }
}
EOF

    # Enable and restart Keepalived
    echo "Starting and enabling Keepalived..."
    systemctl enable --now keepalived

}
# Ensure the script is run as root
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root" 
   exit 1
fi

# Variables
VIP="169.254.168.254"
INTERFACE="eth0"
VRRP_ID="53"
AUTH_PASS="Ows@.DNS"
PRIORITY_MASTER=150
PRIORITY_BACKUP=100
DNS_SERVERS=("10.0.0.1" "10.0.0.2")  # Backend DNS servers

# Determine role based on hostname
ROLE="BACKUP"
PRIORITY=""

HOSTNAME=$(hostname)


#1. install packages
installPackages

#2. config kernel
configKernelParameters

#3. config keepalived services
configKeepalived

#4. config Dnsdist
configDnsdist

#5. clean eth2
cleanNetworkConfig

#6. clean route
cleanOtherRoute
#7. conig firewall
configFirewall






