mirror of
https://github.com/bugfloyd/dnstt-deploy.git
synced 2026-06-02 06:13:40 +02:00
1179 lines
36 KiB
Bash
Executable File
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 "$@" |