Files
2025-06-24 23:16:39 +02:00

1179 lines
36 KiB
Bash
Executable File

#!/bin/bash
# dnstt Server Setup Script
# Supports Fedora, Rocky, CentOS, Debian, Ubuntu
set -e
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "\033[0;31m[ERROR]\033[0m This script must be run as root"
exit 1
fi
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Global variables
DNSTT_BASE_URL="https://dnstt.network"
SCRIPT_URL="https://raw.githubusercontent.com/bugfloyd/dnstt-deploy/main/dnstt-deploy.sh"
INSTALL_DIR="/usr/local/bin"
CONFIG_DIR="/etc/dnstt"
SYSTEMD_DIR="/etc/systemd/system"
DNSTT_PORT="5300"
DNSTT_USER="dnstt"
CONFIG_FILE="${CONFIG_DIR}/dnstt-server.conf"
SCRIPT_INSTALL_PATH="/usr/local/bin/dnstt-deploy"
# Global variable to track if update is available
UPDATE_AVAILABLE=false
# Function to install/update the script itself
install_script() {
print_status "Installing/updating dnstt-deploy script..."
# Download the latest version
local temp_script="/tmp/dnstt-deploy-new.sh"
curl -Ls "$SCRIPT_URL" -o "$temp_script"
# Make it executable
chmod +x "$temp_script"
# Check if we're updating an existing installation
if [ -f "$SCRIPT_INSTALL_PATH" ]; then
# Compare checksums to see if update is needed
local current_checksum
local new_checksum
current_checksum=$(sha256sum "$SCRIPT_INSTALL_PATH" | cut -d' ' -f1)
new_checksum=$(sha256sum "$temp_script" | cut -d' ' -f1)
if [ "$current_checksum" = "$new_checksum" ]; then
print_status "Script is already up to date"
rm "$temp_script"
return 0
else
print_status "Updating existing script installation..."
fi
else
print_status "Installing script for the first time..."
fi
# Copy to installation directory
cp "$temp_script" "$SCRIPT_INSTALL_PATH"
rm "$temp_script"
print_status "Script installed to $SCRIPT_INSTALL_PATH"
print_status "You can now run 'dnstt-deploy' from anywhere"
}
# Function to handle manual update
update_script() {
print_status "Checking for script updates..."
local temp_script="/tmp/dnstt-deploy-latest.sh"
if ! curl -Ls "$SCRIPT_URL" -o "$temp_script"; then
print_error "Failed to download latest version"
return 1
fi
local current_checksum
local latest_checksum
current_checksum=$(sha256sum "$SCRIPT_INSTALL_PATH" | cut -d' ' -f1)
latest_checksum=$(sha256sum "$temp_script" | cut -d' ' -f1)
if [ "$current_checksum" = "$latest_checksum" ]; then
print_status "You are already running the latest version"
rm "$temp_script"
return 0
fi
print_status "New version available! Updating..."
chmod +x "$temp_script"
cp "$temp_script" "$SCRIPT_INSTALL_PATH"
rm "$temp_script"
print_status "Script updated successfully!"
print_status "Restarting with new version..."
# Restart the script with the new version immediately
exec "$SCRIPT_INSTALL_PATH"
}
# Function to show main menu
show_menu() {
echo ""
print_status "dnstt Server Management"
print_status "======================="
# Show update notification if available
if [ "$UPDATE_AVAILABLE" = true ]; then
echo -e "${YELLOW}[UPDATE AVAILABLE]${NC} A new version of this script is available!"
echo -e "${YELLOW} ${NC} Use option 2 to update to the latest version."
echo ""
fi
echo "1) Install/Reconfigure dnstt server"
echo "2) Update dnstt-deploy script"
echo "3) Check service status"
echo "4) View service logs"
echo "5) Show configuration info"
echo "0) Exit"
echo ""
print_question "Please select an option (0-5): "
}
# Function to handle menu selection
handle_menu() {
while true; do
show_menu
read -r choice
case $choice in
1)
print_status "Starting dnstt server installation/reconfiguration..."
return 0 # Continue with main installation
;;
2)
update_script
;;
3)
if systemctl is-active --quiet dnstt-server; then
print_status "dnstt-server service is running"
systemctl status dnstt-server --no-pager -l
else
print_warning "dnstt-server service is not running"
systemctl status dnstt-server --no-pager -l
fi
;;
4)
print_status "Showing dnstt-server logs (Press Ctrl+C to exit)..."
journalctl -u dnstt-server -f
;;
5)
show_configuration_info
;;
0)
print_status "Goodbye!"
exit 0
;;
*)
print_error "Invalid choice. Please enter 0-5."
;;
esac
if [ "$choice" != "4" ]; then
echo ""
print_question "Press Enter to continue..."
read -r
fi
done
}
# Function to show configuration information
show_configuration_info() {
print_status "Current Configuration Information"
print_status "================================"
# Check if configuration file exists
if [ ! -f "$CONFIG_FILE" ]; then
print_warning "No configuration found. Please install/configure dnstt server first."
return 1
fi
# Load existing configuration
if ! load_existing_config; then
print_error "Failed to load configuration from $CONFIG_FILE"
return 1
fi
# Check if service is running
local service_status
if systemctl is-active --quiet dnstt-server; then
service_status="${GREEN}Running${NC}"
else
service_status="${RED}Stopped${NC}"
fi
echo ""
echo -e "${BLUE}Configuration Details:${NC}"
echo -e " Nameserver subdomain: ${YELLOW}$NS_SUBDOMAIN${NC}"
echo -e " MTU: ${YELLOW}$MTU_VALUE${NC}"
echo -e " Tunnel mode: ${YELLOW}$TUNNEL_MODE${NC}"
echo -e " Service user: ${YELLOW}$DNSTT_USER${NC}"
echo -e " Listen port: ${YELLOW}$DNSTT_PORT${NC} (DNS traffic redirected from port 53)"
echo -e " Service status: $service_status"
echo ""
# Show public key if it exists
if [ -f "$PUBLIC_KEY_FILE" ]; then
echo -e "${BLUE}Public Key Content:${NC}"
echo -e "${YELLOW}$(cat "$PUBLIC_KEY_FILE")${NC}"
echo ""
else
print_warning "Public key file not found: $PUBLIC_KEY_FILE"
fi
echo -e "${BLUE}Management Commands:${NC}"
echo -e " Run menu: ${YELLOW}dnstt-deploy${NC}"
echo -e " Start service: ${YELLOW}systemctl start dnstt-server${NC}"
echo -e " Stop service: ${YELLOW}systemctl stop dnstt-server${NC}"
echo -e " Service status: ${YELLOW}systemctl status dnstt-server${NC}"
echo -e " View logs: ${YELLOW}journalctl -u dnstt-server -f${NC}"
# Show SOCKS info if applicable
if [ "$TUNNEL_MODE" = "socks" ]; then
echo ""
echo -e "${BLUE}SOCKS Proxy Information:${NC}"
echo -e "SOCKS proxy is running on ${YELLOW}127.0.0.1:1080${NC}"
echo -e "${BLUE}Dante service commands:${NC}"
echo -e " Status: ${YELLOW}systemctl status danted${NC}"
echo -e " Stop: ${YELLOW}systemctl stop danted${NC}"
echo -e " Start: ${YELLOW}systemctl start danted${NC}"
echo -e " Logs: ${YELLOW}journalctl -u danted -f${NC}"
fi
echo ""
}
check_for_updates() {
# Only check for updates if we're running from the installed location
if [ "$0" = "$SCRIPT_INSTALL_PATH" ]; then
print_status "Checking for script updates..."
local temp_script="/tmp/dnstt-deploy-latest.sh"
if curl -Ls "$SCRIPT_URL" -o "$temp_script" 2>/dev/null; then
local current_checksum
local latest_checksum
current_checksum=$(sha256sum "$SCRIPT_INSTALL_PATH" | cut -d' ' -f1)
latest_checksum=$(sha256sum "$temp_script" | cut -d' ' -f1)
if [ "$current_checksum" != "$latest_checksum" ]; then
UPDATE_AVAILABLE=true
print_warning "New version available! Use menu option 2 to update."
else
print_status "Script is up to date"
fi
rm "$temp_script"
else
print_warning "Could not check for updates (network issue)"
fi
fi
}
# Function to load existing configuration
load_existing_config() {
if [ -f "$CONFIG_FILE" ]; then
print_status "Loading existing configuration..."
# Source the config file to load variables
# shellcheck source=/dev/null
. "$CONFIG_FILE"
return 0
fi
return 1
}
# Function to save configuration
save_config() {
print_status "Saving configuration..."
cat > "$CONFIG_FILE" << EOF
# dnstt Server Configuration
# Generated on $(date)
NS_SUBDOMAIN="$NS_SUBDOMAIN"
MTU_VALUE="$MTU_VALUE"
TUNNEL_MODE="$TUNNEL_MODE"
PRIVATE_KEY_FILE="$PRIVATE_KEY_FILE"
PUBLIC_KEY_FILE="$PUBLIC_KEY_FILE"
EOF
chmod 640 "$CONFIG_FILE"
chown root:"$DNSTT_USER" "$CONFIG_FILE"
print_status "Configuration saved to $CONFIG_FILE"
}
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
print_question() {
echo -ne "${BLUE}[QUESTION]${NC} $1"
}
# Function to print success box without [INFO] prefix
print_success_box() {
local border_color='\033[1;32m' # Bright green
local text_color='\033[1;37m' # Bright white text
local key_color='\033[1;33m' # Yellow for key
local header_color='\033[1;36m' # Cyan for headers
local reset='\033[0m'
echo ""
# Top border
echo -e "${border_color}+================================================================================${reset}"
echo -e "${border_color}| SETUP COMPLETED SUCCESSFULLY! |${reset}"
echo -e "${border_color}+================================================================================${reset}"
echo ""
# Configuration Details
echo -e "${header_color}Configuration Details:${reset}"
echo -e " ${text_color}Nameserver subdomain: $NS_SUBDOMAIN${reset}"
echo -e " ${text_color}MTU: $MTU_VALUE${reset}"
echo -e " ${text_color}Tunnel mode: $TUNNEL_MODE${reset}"
echo -e " ${text_color}Service user: $DNSTT_USER${reset}"
echo -e " ${text_color}Listen port: $DNSTT_PORT (DNS traffic redirected from port 53)${reset}"
echo ""
# Public Key
echo -e "${header_color}Public Key Content:${reset}"
local pub_key_content
pub_key_content=$(cat "$PUBLIC_KEY_FILE")
echo -e "${key_color}$pub_key_content${reset}"
echo ""
# Script Location
echo -e "${text_color}Script installed at: $SCRIPT_INSTALL_PATH${reset}"
echo ""
# Management Commands
echo -e "${header_color}Management Commands:${reset}"
echo -e " ${text_color}Run menu: dnstt-deploy${reset}"
echo -e " ${text_color}Start service: systemctl start dnstt-server${reset}"
echo -e " ${text_color}Stop service: systemctl stop dnstt-server${reset}"
echo -e " ${text_color}Service status: systemctl status dnstt-server${reset}"
echo -e " ${text_color}View logs: journalctl -u dnstt-server -f${reset}"
# SOCKS info if applicable
if [ "$TUNNEL_MODE" = "socks" ]; then
echo ""
echo -e "${header_color}SOCKS Proxy Information:${reset}"
echo -e "${text_color}SOCKS proxy is running on 127.0.0.1:1080${reset}"
echo -e "${text_color}Dante service commands:${reset}"
echo -e " ${text_color}Status: systemctl status danted${reset}"
echo -e " ${text_color}Stop: systemctl stop danted${reset}"
echo -e " ${text_color}Start: systemctl start danted${reset}"
echo -e " ${text_color}Logs: journalctl -u danted -f${reset}"
fi
# Bottom border
echo ""
echo -e "${border_color}+================================================================================${reset}"
echo ""
}
# Function to print info lines without [INFO] prefix for final display
print_info_line() {
local text_color='\033[1;37m' # Bright white
local reset='\033[0m'
echo -e "${text_color}$1${reset}"
}
# Function to print section headers in final display
print_section_header() {
local header_color='\033[1;36m' # Bright cyan
local reset='\033[0m'
echo -e "${header_color}$1${reset}"
}
# Function to detect OS and package manager
detect_os() {
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$NAME
else
print_error "Cannot detect OS"
exit 1
fi
# Determine package manager
if command -v dnf &> /dev/null; then
PKG_MANAGER="dnf"
elif command -v yum &> /dev/null; then
PKG_MANAGER="yum"
elif command -v apt &> /dev/null; then
PKG_MANAGER="apt"
else
print_error "Unsupported package manager"
exit 1
fi
print_status "Detected OS: $OS"
print_status "Package manager: $PKG_MANAGER"
}
# Function to detect architecture
detect_arch() {
local arch
arch=$(uname -m)
case $arch in
x86_64)
ARCH="amd64"
;;
aarch64|arm64)
ARCH="arm64"
;;
armv7l|armv6l)
ARCH="arm"
;;
i386|i686)
ARCH="386"
;;
*)
print_error "Unsupported architecture: $arch"
exit 1
;;
esac
print_status "Detected architecture: $ARCH"
}
# Function to check and install required tools
check_required_tools() {
print_status "Checking required tools..."
local required_tools=("curl")
local missing_tools=()
# Check which tools are missing
for tool in "${required_tools[@]}"; do
if ! command -v "$tool" &> /dev/null; then
missing_tools+=("$tool")
fi
done
# Check for iptables separately since it might need special handling
if ! command -v "iptables" &> /dev/null; then
missing_tools+=("iptables")
fi
if [ ${#missing_tools[@]} -gt 0 ]; then
print_status "Installing missing tools: ${missing_tools[*]}"
install_dependencies "${missing_tools[@]}"
else
print_status "All required tools are available"
fi
# Verify iptables installation after potential installation
verify_iptables_installation
}
# Function to verify iptables installation and capabilities
verify_iptables_installation() {
print_status "Verifying iptables installation..."
if ! command -v iptables &> /dev/null; then
print_error "iptables is not available after installation attempt"
exit 1
fi
# Check if ip6tables is available (should be part of iptables package)
if command -v ip6tables &> /dev/null; then
print_status "Both iptables and ip6tables are available"
else
print_warning "ip6tables not found, IPv6 rules will be skipped"
fi
# Check if IPv6 is supported on the system
if [ -f /proc/net/if_inet6 ]; then
print_status "IPv6 support detected"
else
print_warning "IPv6 not supported on this system"
fi
}
# Function to install dependencies
install_dependencies() {
local tools=("$@")
print_status "Installing dependencies: ${tools[*]}"
# Safety check for PKG_MANAGER
if [[ -z "$PKG_MANAGER" ]]; then
print_error "Package manager not detected. Make sure detect_os() is called first."
exit 1
fi
case $PKG_MANAGER in
dnf|yum)
# For RHEL-based systems
local packages_to_install=()
for tool in "${tools[@]}"; do
case $tool in
"iptables")
packages_to_install+=("iptables" "iptables-services")
;;
*)
packages_to_install+=("$tool")
;;
esac
done
if ! $PKG_MANAGER install -y "${packages_to_install[@]}"; then
print_error "Failed to install packages: ${packages_to_install[*]}"
exit 1
fi
;;
apt)
# For Debian-based systems
if ! apt update; then
print_error "Failed to update package lists"
exit 1
fi
local packages_to_install=()
for tool in "${tools[@]}"; do
case $tool in
"iptables")
# iptables package includes both iptables and ip6tables
packages_to_install+=("iptables" "iptables-persistent")
;;
*)
packages_to_install+=("$tool")
;;
esac
done
if ! apt install -y "${packages_to_install[@]}"; then
print_error "Failed to install packages: ${packages_to_install[*]}"
exit 1
fi
;;
*)
print_error "Unsupported package manager: $PKG_MANAGER"
exit 1
;;
esac
print_status "Dependencies installed successfully"
}
# Function to get user input
get_user_input() {
# Load existing configuration if available
local existing_domain=""
local existing_mtu=""
local existing_mode=""
if load_existing_config; then
existing_domain="$NS_SUBDOMAIN"
existing_mtu="$MTU_VALUE"
existing_mode="$TUNNEL_MODE"
print_status "Found existing configuration for domain: $existing_domain"
fi
# Get nameserver subdomain
while true; do
if [[ -n "$existing_domain" ]]; then
print_question "Enter the nameserver subdomain (current: $existing_domain): "
else
print_question "Enter the nameserver subdomain (e.g., t.example.com): "
fi
read -r NS_SUBDOMAIN
# Use existing domain if user just presses enter
if [[ -z "$NS_SUBDOMAIN" && -n "$existing_domain" ]]; then
NS_SUBDOMAIN="$existing_domain"
fi
if [[ -n "$NS_SUBDOMAIN" ]]; then
break
else
print_error "Please enter a valid subdomain"
fi
done
# Get MTU value
if [[ -n "$existing_mtu" ]]; then
print_question "Enter MTU value (current: $existing_mtu): "
else
print_question "Enter MTU value (default: 1232): "
fi
read -r MTU_VALUE
# Use existing MTU if user just presses enter, otherwise use default
if [[ -z "$MTU_VALUE" ]]; then
if [[ -n "$existing_mtu" ]]; then
MTU_VALUE="$existing_mtu"
else
MTU_VALUE="1232"
fi
fi
# Get tunnel mode
while true; do
echo "Select tunnel mode:"
echo "1) SOCKS proxy"
echo "2) SSH mode"
if [[ -n "$existing_mode" ]]; then
local mode_number
if [[ "$existing_mode" == "socks" ]]; then
mode_number="1"
else
mode_number="2"
fi
print_question "Enter choice (current: $mode_number - $existing_mode): "
else
print_question "Enter choice (1 or 2): "
fi
read -r TUNNEL_MODE
# Use existing mode if user just presses enter
if [[ -z "$TUNNEL_MODE" && -n "$existing_mode" ]]; then
TUNNEL_MODE="$existing_mode"
break
fi
case $TUNNEL_MODE in
1)
TUNNEL_MODE="socks"
break
;;
2)
TUNNEL_MODE="ssh"
break
;;
*)
print_error "Invalid choice. Please enter 1 or 2"
;;
esac
done
print_status "Configuration:"
print_status " Nameserver subdomain: $NS_SUBDOMAIN"
print_status " MTU: $MTU_VALUE"
print_status " Tunnel mode: $TUNNEL_MODE"
}
# Function to download and verify dnstt-server
download_dnstt_server() {
local filename="dnstt-server-linux-${ARCH}"
local filepath="${INSTALL_DIR}/dnstt-server"
# Check if file already exists
if [ -f "$filepath" ]; then
print_status "dnstt-server already exists at $filepath"
return 0
fi
print_status "Downloading dnstt-server..."
# Download the binary
curl -L -o "/tmp/$filename" "${DNSTT_BASE_URL}/$filename"
# Download checksums
curl -L -o "/tmp/MD5SUMS" "${DNSTT_BASE_URL}/MD5SUMS"
curl -L -o "/tmp/SHA1SUMS" "${DNSTT_BASE_URL}/SHA1SUMS"
curl -L -o "/tmp/SHA256SUMS" "${DNSTT_BASE_URL}/SHA256SUMS"
# Verify checksums
print_status "Verifying file integrity..."
cd /tmp
# Verify MD5
if md5sum -c <(grep "$filename" MD5SUMS) 2>/dev/null; then
print_status "MD5 checksum verified"
else
print_error "MD5 checksum verification failed"
exit 1
fi
# Verify SHA1
if sha1sum -c <(grep "$filename" SHA1SUMS) 2>/dev/null; then
print_status "SHA1 checksum verified"
else
print_error "SHA1 checksum verification failed"
exit 1
fi
# Verify SHA256
if sha256sum -c <(grep "$filename" SHA256SUMS) 2>/dev/null; then
print_status "SHA256 checksum verified"
else
print_error "SHA256 checksum verification failed"
exit 1
fi
# Move to install directory and make executable
chmod +x "/tmp/$filename"
mv "/tmp/$filename" "$filepath"
print_status "dnstt-server installed successfully"
}
# Function to create dnstt user
create_dnstt_user() {
print_status "Creating dnstt user..."
if ! id "$DNSTT_USER" &>/dev/null; then
useradd -r -s /bin/false -d /nonexistent -c "dnstt service user" "$DNSTT_USER"
print_status "Created user: $DNSTT_USER"
else
print_status "User $DNSTT_USER already exists"
fi
# Create config directory first
mkdir -p "$CONFIG_DIR"
# Set ownership of config directory
chown -R "$DNSTT_USER":"$DNSTT_USER" "$CONFIG_DIR"
chmod 750 "$CONFIG_DIR"
}
# Function to generate keys
generate_keys() {
# Generate key file names based on subdomain
local key_prefix
# shellcheck disable=SC2001
key_prefix=$(echo "$NS_SUBDOMAIN" | sed 's/\./_/g')
PRIVATE_KEY_FILE="${CONFIG_DIR}/${key_prefix}_server.key"
PUBLIC_KEY_FILE="${CONFIG_DIR}/${key_prefix}_server.pub"
# Check if keys already exist for this domain
if [[ -f "$PRIVATE_KEY_FILE" && -f "$PUBLIC_KEY_FILE" ]]; then
print_status "Found existing keys for domain: $NS_SUBDOMAIN"
print_status " Private key: $PRIVATE_KEY_FILE"
print_status " Public key: $PUBLIC_KEY_FILE"
# Verify key ownership and permissions
chown "$DNSTT_USER":"$DNSTT_USER" "$PRIVATE_KEY_FILE" "$PUBLIC_KEY_FILE"
chmod 600 "$PRIVATE_KEY_FILE"
chmod 644 "$PUBLIC_KEY_FILE"
print_status "Using existing keys (verified ownership and permissions)"
else
print_status "Generating new keys for domain: $NS_SUBDOMAIN"
# Generate keys (run as root, then change ownership)
dnstt-server -gen-key -privkey-file "$PRIVATE_KEY_FILE" -pubkey-file "$PUBLIC_KEY_FILE"
# Set proper ownership and permissions
chown "$DNSTT_USER":"$DNSTT_USER" "$PRIVATE_KEY_FILE" "$PUBLIC_KEY_FILE"
chmod 600 "$PRIVATE_KEY_FILE"
chmod 644 "$PUBLIC_KEY_FILE"
print_status "New keys generated:"
print_status " Private key: $PRIVATE_KEY_FILE"
print_status " Public key: $PUBLIC_KEY_FILE"
fi
# Always display public key content
print_status "Public key content:"
cat "$PUBLIC_KEY_FILE"
}
# Function to configure iptables rules
configure_iptables() {
print_status "Configuring iptables rules for DNS redirection..."
# Verify iptables is available
if ! command -v iptables &> /dev/null; then
print_error "iptables command not found. Cannot configure firewall rules."
exit 1
fi
# Get the primary network interface
local interface
interface=$(ip route | grep default | awk '{print $5}' | head -1)
if [[ -z "$interface" ]]; then
# Try alternative method to get interface
interface=$(ip link show | grep -E "^[0-9]+: (eth|ens|enp)" | head -1 | cut -d':' -f2 | awk '{print $1}')
if [[ -z "$interface" ]]; then
interface="eth0" # fallback
print_warning "Could not detect network interface, using eth0 as fallback"
else
print_status "Detected network interface: $interface"
fi
else
print_status "Using network interface: $interface"
fi
# IPv4 rules
print_status "Setting up IPv4 iptables rules..."
if ! iptables -I INPUT -p udp --dport "$DNSTT_PORT" -j ACCEPT; then
print_error "Failed to add IPv4 INPUT rule"
exit 1
fi
if ! iptables -t nat -I PREROUTING -i "$interface" -p udp --dport 53 -j REDIRECT --to-ports "$DNSTT_PORT"; then
print_error "Failed to add IPv4 NAT rule"
exit 1
fi
print_status "IPv4 iptables rules configured successfully"
# IPv6 rules (if IPv6 and ip6tables are available)
if command -v ip6tables &> /dev/null && [ -f /proc/net/if_inet6 ]; then
print_status "Setting up IPv6 iptables rules..."
if ip6tables -I INPUT -p udp --dport "$DNSTT_PORT" -j ACCEPT 2>/dev/null; then
print_status "IPv6 INPUT rule added successfully"
else
print_warning "Failed to add IPv6 INPUT rule (IPv6 might not be fully configured)"
fi
if ip6tables -t nat -I PREROUTING -i "$interface" -p udp --dport 53 -j REDIRECT --to-ports "$DNSTT_PORT" 2>/dev/null; then
print_status "IPv6 NAT rule added successfully"
else
print_warning "Failed to add IPv6 NAT rule (IPv6 NAT might not be supported)"
fi
else
if ! command -v ip6tables &> /dev/null; then
print_warning "ip6tables not available, skipping IPv6 rules"
elif [ ! -f /proc/net/if_inet6 ]; then
print_warning "IPv6 not enabled on system, skipping IPv6 rules"
fi
fi
# Save iptables rules based on distribution
save_iptables_rules
}
# Function to save iptables rules with better error handling
save_iptables_rules() {
print_status "Saving iptables rules..."
case $PKG_MANAGER in
dnf|yum)
# For RHEL-based systems
if command -v iptables-save &> /dev/null; then
# Create directory if it doesn't exist
mkdir -p /etc/sysconfig
if iptables-save > /etc/sysconfig/iptables; then
print_status "IPv4 iptables rules saved to /etc/sysconfig/iptables"
else
print_warning "Failed to save IPv4 iptables rules"
fi
if command -v ip6tables-save &> /dev/null && [ -f /proc/net/if_inet6 ]; then
if ip6tables-save > /etc/sysconfig/ip6tables; then
print_status "IPv6 iptables rules saved to /etc/sysconfig/ip6tables"
else
print_warning "Failed to save IPv6 iptables rules"
fi
fi
# Enable and start iptables service if available
if systemctl list-unit-files | grep -q iptables.service; then
systemctl enable iptables 2>/dev/null || print_warning "Could not enable iptables service"
if command -v ip6tables &> /dev/null && [ -f /proc/net/if_inet6 ]; then
systemctl enable ip6tables 2>/dev/null || print_warning "Could not enable ip6tables service"
fi
fi
else
print_warning "iptables-save not available, rules will not persist after reboot"
fi
;;
apt)
# For Debian-based systems
if command -v iptables-save &> /dev/null; then
# Create directory if it doesn't exist
mkdir -p /etc/iptables
if iptables-save > /etc/iptables/rules.v4; then
print_status "IPv4 iptables rules saved to /etc/iptables/rules.v4"
else
print_warning "Failed to save IPv4 iptables rules"
fi
if command -v ip6tables-save &> /dev/null && [ -f /proc/net/if_inet6 ]; then
if ip6tables-save > /etc/iptables/rules.v6; then
print_status "IPv6 iptables rules saved to /etc/iptables/rules.v6"
else
print_warning "Failed to save IPv6 iptables rules"
fi
fi
# Try to enable netfilter-persistent if available
if systemctl list-unit-files | grep -q netfilter-persistent.service; then
systemctl enable netfilter-persistent 2>/dev/null || print_warning "Could not enable netfilter-persistent service"
fi
else
print_warning "iptables-save not available, rules will not persist after reboot"
fi
;;
esac
}
# Function to configure firewall
configure_firewall() {
print_status "Configuring firewall..."
# Check if firewalld is available and active
if command -v firewall-cmd &> /dev/null && systemctl is-active --quiet firewalld; then
print_status "Configuring active firewalld..."
firewall-cmd --permanent --add-port="$DNSTT_PORT"/udp
firewall-cmd --permanent --add-port=53/udp
firewall-cmd --reload
print_status "Firewalld configured successfully"
# Check if ufw is available and active
elif command -v ufw &> /dev/null && ufw status | grep -q "Status: active"; then
print_status "Configuring active ufw..."
ufw allow "$DNSTT_PORT"/udp
ufw allow 53/udp
print_status "UFW configured successfully"
else
print_status "No active firewall service detected"
print_status "Available firewall tools:"
# List available but inactive firewall tools
if command -v firewall-cmd &> /dev/null; then
print_status " - firewalld (inactive)"
fi
if command -v ufw &> /dev/null; then
print_status " - ufw (inactive)"
fi
print_status "Relying on iptables rules only"
print_status "If you have a firewall active, manually allow ports $DNSTT_PORT/udp and 53/udp"
fi
# Configure iptables rules regardless of firewall service
configure_iptables
}
# Function to detect SSH port
detect_ssh_port() {
local ssh_port
ssh_port=$(ss -tlnp | grep sshd | awk '{print $4}' | cut -d':' -f2 | head -1)
if [[ -z "$ssh_port" ]]; then
# Fallback to default SSH port
ssh_port="22"
fi
echo "$ssh_port"
}
# Function to install and configure Dante SOCKS proxy
setup_dante() {
print_status "Setting up Dante SOCKS proxy..."
# Install Dante
case $PKG_MANAGER in
dnf|yum)
$PKG_MANAGER install -y dante-server
;;
apt)
apt install -y dante-server
;;
esac
# Get the primary network interface for external interface
local external_interface
external_interface=$(ip route | grep default | awk '{print $5}' | head -1)
if [[ -z "$external_interface" ]]; then
external_interface="eth0" # fallback
fi
# Configure Dante
cat > /etc/danted.conf << EOF
# Dante SOCKS server configuration
logoutput: syslog
user.privileged: root
user.unprivileged: nobody
# Internal interface (where clients connect)
internal: 127.0.0.1 port = 1080
# External interface (where connections go out)
external: $external_interface
# Authentication method
socksmethod: none
# Compatibility settings
compatibility: sameport
extension: bind
# Client rules - allow connections from localhost
client pass {
from: 127.0.0.0/8 to: 0.0.0.0/0
log: error
}
# SOCKS rules - allow SOCKS requests to anywhere
socks pass {
from: 127.0.0.0/8 to: 0.0.0.0/0
command: bind connect udpassociate
log: error
}
# Block IPv6 if not properly configured
socks block {
from: 0.0.0.0/0 to: ::/0
log: error
}
client block {
from: 0.0.0.0/0 to: ::/0
log: error
}
EOF
# Enable and start Dante service
systemctl enable danted
systemctl restart danted
print_status "Dante SOCKS proxy configured and started on port 1080"
print_status "External interface: $external_interface"
}
# Function to create systemd service
create_systemd_service() {
print_status "Creating systemd service..."
local service_name="dnstt-server"
local service_file="${SYSTEMD_DIR}/${service_name}.service"
local target_port
if [ "$TUNNEL_MODE" = "ssh" ]; then
target_port=$(detect_ssh_port)
print_status "Detected SSH port: $target_port"
else
target_port="1080" # Dante SOCKS port
fi
# Stop service if it's running to allow reconfiguration
if systemctl is-active --quiet "$service_name"; then
print_status "Stopping existing dnstt-server service for reconfiguration..."
systemctl stop "$service_name"
fi
# Create systemd service file
cat > "$service_file" << EOF
[Unit]
Description=dnstt DNS Tunnel Server
After=network.target
Wants=network.target
[Service]
Type=simple
User=$DNSTT_USER
Group=$DNSTT_USER
ExecStart=${INSTALL_DIR}/dnstt-server -udp :${DNSTT_PORT} -privkey-file ${PRIVATE_KEY_FILE} -mtu ${MTU_VALUE} ${NS_SUBDOMAIN} 127.0.0.1:${target_port}
Restart=always
RestartSec=5
KillMode=mixed
TimeoutStopSec=5
# Security settings
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadOnlyPaths=/
ReadWritePaths=${CONFIG_DIR}
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
[Install]
WantedBy=multi-user.target
EOF
# Reload systemd and enable service
systemctl daemon-reload
systemctl enable "$service_name"
print_status "Systemd service created: $service_name"
print_status "Service will run as user: $DNSTT_USER"
print_status "Service will listen on port: $DNSTT_PORT (redirected from port 53)"
print_status "Service will tunnel to 127.0.0.1:$target_port"
print_status "Mode: $TUNNEL_MODE"
}
# Function to start services
start_services() {
print_status "Starting services..."
# Start dnstt-server service
systemctl start dnstt-server
print_status "dnstt-server service started"
# Show service status
systemctl status dnstt-server --no-pager -l
}
# Function to display final information
display_final_info() {
print_success_box
}
# Main function
main() {
# If not running from installed location (curl/GitHub), install the script first
if [ "$0" != "$SCRIPT_INSTALL_PATH" ]; then
print_status "Installing dnstt-deploy script..."
install_script
print_status "Starting dnstt server setup..."
else
# Running from installed location - check for updates and show menu
check_for_updates
handle_menu
# If we reach here, user chose option 1 (Install/Reconfigure), so continue
print_status "Starting dnstt server installation/reconfiguration..."
fi
# Detect OS and architecture
detect_os
detect_arch
# Check and install required tools
check_required_tools
# Get user input
get_user_input
# Download and verify dnstt-server
download_dnstt_server
# Create dnstt user
create_dnstt_user
# Generate keys
generate_keys
# Save configuration after keys are generated
save_config
# Configure firewall and iptables
configure_firewall
# Setup tunnel mode specific configurations
if [ "$TUNNEL_MODE" = "socks" ]; then
setup_dante
else
# If switching from SOCKS to SSH, stop and disable Dante
if systemctl is-active --quiet danted; then
print_status "Switching from SOCKS to SSH mode - stopping Dante service..."
systemctl stop danted
systemctl disable danted
fi
fi
# Create systemd service
create_systemd_service
# Start services
start_services
# Display final information
display_final_info
}
# Run main function
main "$@"