mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-07-01 20:42:07 +00:00
Merge bacbcc61a4
into cad07be847
This commit is contained in:
commit
3599c06902
3 changed files with 296 additions and 47 deletions
|
@ -1,7 +1,133 @@
|
|||
#!/bin/sh
|
||||
#!/usr/bin/env bash
|
||||
# Description:
|
||||
# 1) Handle SIGTERM for graceful shutdown
|
||||
# 2) Run all scripts from /mnt/sh/beforestart before starting the main application
|
||||
# 3) Copy binaries from /mnt/bin to /app/bin
|
||||
# 4) Launch x-ui in the background and capture its PID
|
||||
# 5) After x-ui has started, run scripts from /mnt/sh/afterstart
|
||||
# 6) Start fail2ban if enabled
|
||||
# 7) Wait for x-ui to exit or receive a signal
|
||||
|
||||
# Start fail2ban
|
||||
[ $XUI_ENABLE_FAIL2BAN == "true" ] && fail2ban-client -x start
|
||||
set -euo pipefail
|
||||
|
||||
# Run x-ui
|
||||
exec /app/x-ui
|
||||
###############################################################################
|
||||
# Logging function
|
||||
###############################################################################
|
||||
log() {
|
||||
local level=$1
|
||||
shift
|
||||
echo "$(date '+%Y-%m-%d %H:%M:%S') $level $*"
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Function: start_fail2ban_if_enabled
|
||||
###############################################################################
|
||||
start_fail2ban_if_enabled() {
|
||||
if [ "${XUI_ENABLE_FAIL2BAN:-false}" != "true" ]; then
|
||||
log INFO "Fail2Ban is disabled; skipping start."
|
||||
return
|
||||
fi
|
||||
|
||||
log INFO "Attempting to start Fail2Ban..."
|
||||
if fail2ban-client -x start; then
|
||||
log INFO "Fail2Ban started successfully."
|
||||
else
|
||||
log ERROR "Fail2Ban failed to start."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# 1) Handle SIGTERM for graceful shutdown
|
||||
###############################################################################
|
||||
# When a SIGTERM is received, properly stop the child process (x-ui)
|
||||
term_handler() {
|
||||
log INFO "Caught SIGTERM. Stopping x-ui..."
|
||||
if [[ -n "${XUI_PID:-}" ]]; then
|
||||
kill -TERM "$XUI_PID" 2>/dev/null || true
|
||||
wait "$XUI_PID"
|
||||
fi
|
||||
log INFO "x-ui stopped."
|
||||
exit 0
|
||||
}
|
||||
trap 'term_handler' SIGTERM
|
||||
|
||||
###############################################################################
|
||||
# 2) Execute scripts from /mnt/sh/beforestart
|
||||
###############################################################################
|
||||
if [ -d "/mnt/sh/beforestart" ]; then
|
||||
log INFO "Detected /mnt/sh/beforestart directory. Setting permissions..."
|
||||
chmod -R 777 /mnt/sh/beforestart
|
||||
|
||||
log INFO "Searching for scripts in /mnt/sh/beforestart..."
|
||||
find /mnt/sh/beforestart -type f -name "*.sh" -print0 | sort -z | while IFS= read -r -d '' script; do
|
||||
log INFO "Executing script: $script"
|
||||
sh "$script"
|
||||
if [ $? -ne 0 ]; then
|
||||
log ERROR "Script failed: $script. Aborting startup."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log INFO "All scripts in /mnt/sh/beforestart executed successfully."
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# 3) Copy binaries from /mnt/bin to /app/bin
|
||||
###############################################################################
|
||||
if [ -d "/mnt/bin" ]; then
|
||||
log INFO "Detected /mnt/bin directory. Copying contents to /app/bin..."
|
||||
if ! cp -r /mnt/bin/* /app/bin/; then
|
||||
log ERROR "Failed to copy from /mnt/bin to /app/bin. Aborting startup."
|
||||
exit 1
|
||||
fi
|
||||
log INFO "Binaries copied to /app/bin successfully."
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# 4) Launch the main application x-ui in the background
|
||||
###############################################################################
|
||||
log INFO "Launching x-ui in background..."
|
||||
/app/x-ui &
|
||||
XUI_PID=$!
|
||||
log INFO "x-ui PID is $XUI_PID."
|
||||
|
||||
# Sleep briefly to ensure the process has started before running afterstart scripts
|
||||
sleep 10
|
||||
|
||||
###############################################################################
|
||||
# 5) Execute scripts from /mnt/sh/afterstart after x-ui has started
|
||||
###############################################################################
|
||||
if [ -d "/mnt/sh/afterstart" ]; then
|
||||
log INFO "Detected /mnt/sh/afterstart directory. Setting permissions..."
|
||||
chmod -R 777 /mnt/sh/afterstart
|
||||
|
||||
log INFO "Searching for scripts in /mnt/sh/afterstart..."
|
||||
find /mnt/sh/afterstart -type f -name "*.sh" -print0 | sort -z | while IFS= read -r -d '' script; do
|
||||
log INFO "Executing script: $script"
|
||||
sh "$script"
|
||||
if [ $? -ne 0 ]; then
|
||||
log ERROR "Script failed: $script. Aborting startup."
|
||||
# Stop x-ui if an afterstart script fails
|
||||
kill -TERM "$XUI_PID" 2>/dev/null || true
|
||||
wait "$XUI_PID"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
log INFO "All scripts in /mnt/sh/afterstart executed successfully."
|
||||
fi
|
||||
|
||||
###############################################################################
|
||||
# 6) Start Fail2Ban if enabled
|
||||
###############################################################################
|
||||
start_fail2ban_if_enabled
|
||||
|
||||
###############################################################################
|
||||
# 7) Wait for x-ui to exit (or receive a signal)
|
||||
###############################################################################
|
||||
log INFO "Waiting for x-ui (PID $XUI_PID) to exit..."
|
||||
wait "$XUI_PID"
|
||||
EXIT_CODE=$?
|
||||
log INFO "x-ui exited with code $EXIT_CODE."
|
||||
exit "$EXIT_CODE"
|
||||
|
|
195
DockerInit.sh
195
DockerInit.sh
|
@ -1,40 +1,155 @@
|
|||
#!/bin/sh
|
||||
case $1 in
|
||||
amd64)
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
i386)
|
||||
ARCH="32"
|
||||
FNAME="i386"
|
||||
;;
|
||||
armv8 | arm64 | aarch64)
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64"
|
||||
;;
|
||||
armv7 | arm | arm32)
|
||||
ARCH="arm32-v7a"
|
||||
FNAME="arm32"
|
||||
;;
|
||||
armv6)
|
||||
ARCH="arm32-v6"
|
||||
FNAME="armv6"
|
||||
;;
|
||||
*)
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
esac
|
||||
mkdir -p build/bin
|
||||
cd build/bin
|
||||
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.6.8/Xray-linux-${ARCH}.zip"
|
||||
unzip "Xray-linux-${ARCH}.zip"
|
||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
||||
mv xray "xray-linux-${FNAME}"
|
||||
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -q https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
wget -q -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
||||
wget -q -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
||||
wget -q -O geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
|
||||
wget -q -O geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
|
||||
cd ../../
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# DockerInit.sh <20> download and prepare Xray binaries and geolocation databases
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Xray version
|
||||
readonly XRAY_VERSION="v25.6.8"
|
||||
|
||||
# URL template for downloading Xray
|
||||
readonly XRAY_URL_TEMPLATE="https://github.com/XTLS/Xray-core/releases/download/${XRAY_VERSION}/Xray-linux-%s.zip"
|
||||
|
||||
# Directories
|
||||
readonly BUILD_DIR="build/bin"
|
||||
readonly ROOT_DIR="$(pwd)"
|
||||
|
||||
# Check for required utilities
|
||||
check_dependencies() {
|
||||
local deps=(wget unzip)
|
||||
for cmd in "${deps[@]}"; do
|
||||
if ! command -v "$cmd" &> /dev/null; then
|
||||
echo "Error: Required utility '$cmd' is not installed. Please install it and try again." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Print usage help
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 <architecture>
|
||||
|
||||
Supported architectures:
|
||||
amd64 ? 64-bit x86
|
||||
i386 ? 32-bit x86
|
||||
armv8 ? ARM64-v8a (also accepts arm64, aarch64)
|
||||
armv7 ? ARM32-v7a (also accepts arm, arm32)
|
||||
armv6 ? ARM32-v6
|
||||
|
||||
If no argument is provided or the argument is not recognized, 'amd64' will be used by default.
|
||||
|
||||
Example:
|
||||
$0 armv7
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Determine ARCH and FNAME based on input argument
|
||||
detect_arch() {
|
||||
local input="$1"
|
||||
|
||||
case "$input" in
|
||||
amd64)
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
i386)
|
||||
ARCH="32"
|
||||
FNAME="i386"
|
||||
;;
|
||||
armv8 | arm64 | aarch64)
|
||||
ARCH="arm64-v8a"
|
||||
FNAME="arm64"
|
||||
;;
|
||||
armv7 | arm | arm32)
|
||||
ARCH="arm32-v7a"
|
||||
FNAME="arm32"
|
||||
;;
|
||||
armv6)
|
||||
ARCH="arm32-v6"
|
||||
FNAME="armv6"
|
||||
;;
|
||||
"")
|
||||
# If argument is empty, default to amd64
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
*)
|
||||
echo "Warning: Architecture '$input' not recognized. Defaulting to 'amd64'." >&2
|
||||
ARCH="64"
|
||||
FNAME="amd64"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Generic function to download a file by URL (with error handling)
|
||||
download_file() {
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
|
||||
echo "Downloading: $url"
|
||||
if ! wget -q -O "$output" "$url"; then
|
||||
echo "Error: Failed to download '$url'" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main function: create directory, download and unpack Xray, then geolocation databases
|
||||
main() {
|
||||
# Check dependencies
|
||||
check_dependencies
|
||||
|
||||
# Get architecture from argument
|
||||
local ARCH_ARG="${1-}"
|
||||
detect_arch "$ARCH_ARG"
|
||||
|
||||
# Construct URL for Xray download
|
||||
local xray_url
|
||||
printf -v xray_url "$XRAY_URL_TEMPLATE" "$ARCH"
|
||||
|
||||
# Create build directory
|
||||
echo "Creating directory: $BUILD_DIR"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
cd "$BUILD_DIR" || exit 1
|
||||
|
||||
# Download and unpack Xray
|
||||
local xray_zip="Xray-linux-${ARCH}.zip"
|
||||
download_file "$xray_url" "$xray_zip"
|
||||
echo "Unpacking $xray_zip"
|
||||
unzip -q "$xray_zip"
|
||||
rm -f "$xray_zip" geoip.dat geosite.dat
|
||||
|
||||
# Rename binary according to target architecture
|
||||
mv xray "xray-linux-${FNAME}"
|
||||
chmod +x "xray-linux-${FNAME}"
|
||||
|
||||
# Return to project root
|
||||
cd "$ROOT_DIR" || exit 1
|
||||
|
||||
# Download standard GeoIP and GeoSite databases
|
||||
echo "Downloading default GeoIP and GeoSite databases"
|
||||
download_file "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geoip.dat" "geoip.dat"
|
||||
download_file "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat" "geosite.dat"
|
||||
|
||||
# Download alternative GeoIP/GeoSite for Iran (IR) and Russia (RU)
|
||||
echo "Downloading alternative GeoIP/GeoSite for Iran"
|
||||
download_file "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat" "geoip_IR.dat"
|
||||
download_file "https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat" "geosite_IR.dat"
|
||||
|
||||
echo "Downloading alternative GeoIP/GeoSite for Russia"
|
||||
download_file "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat" "geoip_RU.dat"
|
||||
download_file "https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat" "geosite_RU.dat"
|
||||
|
||||
echo "Done."
|
||||
}
|
||||
|
||||
# If -h or --help is passed, show usage
|
||||
if [[ "${1-}" == "-h" || "${1-}" == "--help" ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
# Run main with the provided argument (if any)
|
||||
main "${1-}"
|
12
Dockerfile
12
Dockerfile
|
@ -31,11 +31,11 @@ RUN apk add --no-cache --update \
|
|||
fail2ban \
|
||||
bash
|
||||
|
||||
COPY --from=builder /app/build/ /app/
|
||||
# Copy only required build artifacts from builder stage
|
||||
COPY --from=builder /app/build /app/
|
||||
COPY --from=builder /app/DockerEntrypoint.sh /app/
|
||||
COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
|
||||
|
||||
|
||||
# Configure fail2ban
|
||||
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
|
||||
&& cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
|
||||
|
@ -43,12 +43,20 @@ RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
|
|||
&& sed -i "s/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local \
|
||||
&& sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf
|
||||
|
||||
# Make scripts executable
|
||||
RUN chmod +x \
|
||||
/app/DockerEntrypoint.sh \
|
||||
/app/x-ui \
|
||||
/usr/bin/x-ui
|
||||
|
||||
# Enable fail2ban via environment variable (can be overridden)
|
||||
ENV XUI_ENABLE_FAIL2BAN="true"
|
||||
|
||||
# Define volume for persistent configuration
|
||||
VOLUME [ "/etc/x-ui" ]
|
||||
|
||||
# Default command when container starts
|
||||
CMD [ "./x-ui" ]
|
||||
|
||||
# Custom entrypoint to initialize runtime environment
|
||||
ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
|
||||
|
|
Loading…
Reference in a new issue