mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2025-07-02 04:52:08 +00:00
Merge f7a3ebf2f3
into c8c0e77714
This commit is contained in:
commit
ac9ab6bcb2
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
|
set -euo pipefail
|
||||||
[ $XUI_ENABLE_FAIL2BAN == "true" ] && fail2ban-client -x start
|
|
||||||
|
|
||||||
# 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
|
#!/usr/bin/env bash
|
||||||
case $1 in
|
set -euo pipefail
|
||||||
amd64)
|
IFS=$'\n\t'
|
||||||
ARCH="64"
|
|
||||||
FNAME="amd64"
|
# ------------------------------------------------------------------------------
|
||||||
;;
|
# DockerInit.sh <20> download and prepare Xray binaries and geolocation databases
|
||||||
i386)
|
# ------------------------------------------------------------------------------
|
||||||
ARCH="32"
|
|
||||||
FNAME="i386"
|
# Xray version
|
||||||
;;
|
readonly XRAY_VERSION="v25.6.8"
|
||||||
armv8 | arm64 | aarch64)
|
|
||||||
ARCH="arm64-v8a"
|
# URL template for downloading Xray
|
||||||
FNAME="arm64"
|
readonly XRAY_URL_TEMPLATE="https://github.com/XTLS/Xray-core/releases/download/${XRAY_VERSION}/Xray-linux-%s.zip"
|
||||||
;;
|
|
||||||
armv7 | arm | arm32)
|
# Directories
|
||||||
ARCH="arm32-v7a"
|
readonly BUILD_DIR="build/bin"
|
||||||
FNAME="arm32"
|
readonly ROOT_DIR="$(pwd)"
|
||||||
;;
|
|
||||||
armv6)
|
# Check for required utilities
|
||||||
ARCH="arm32-v6"
|
check_dependencies() {
|
||||||
FNAME="armv6"
|
local deps=(wget unzip)
|
||||||
;;
|
for cmd in "${deps[@]}"; do
|
||||||
*)
|
if ! command -v "$cmd" &> /dev/null; then
|
||||||
ARCH="64"
|
echo "Error: Required utility '$cmd' is not installed. Please install it and try again." >&2
|
||||||
FNAME="amd64"
|
exit 1
|
||||||
;;
|
fi
|
||||||
esac
|
done
|
||||||
mkdir -p build/bin
|
}
|
||||||
cd build/bin
|
|
||||||
wget -q "https://github.com/XTLS/Xray-core/releases/download/v25.6.8/Xray-linux-${ARCH}.zip"
|
# Print usage help
|
||||||
unzip "Xray-linux-${ARCH}.zip"
|
usage() {
|
||||||
rm -f "Xray-linux-${ARCH}.zip" geoip.dat geosite.dat
|
cat <<EOF
|
||||||
mv xray "xray-linux-${FNAME}"
|
Usage: $0 <architecture>
|
||||||
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
|
Supported architectures:
|
||||||
wget -q -O geoip_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geoip.dat
|
amd64 ? 64-bit x86
|
||||||
wget -q -O geosite_IR.dat https://github.com/chocolate4u/Iran-v2ray-rules/releases/latest/download/geosite.dat
|
i386 ? 32-bit x86
|
||||||
wget -q -O geoip_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geoip.dat
|
armv8 ? ARM64-v8a (also accepts arm64, aarch64)
|
||||||
wget -q -O geosite_RU.dat https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/geosite.dat
|
armv7 ? ARM32-v7a (also accepts arm, arm32)
|
||||||
cd ../../
|
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 \
|
fail2ban \
|
||||||
bash
|
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/DockerEntrypoint.sh /app/
|
||||||
COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
|
COPY --from=builder /app/x-ui.sh /usr/bin/x-ui
|
||||||
|
|
||||||
|
|
||||||
# Configure fail2ban
|
# Configure fail2ban
|
||||||
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
|
RUN rm -f /etc/fail2ban/jail.d/alpine-ssh.conf \
|
||||||
&& cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local \
|
&& 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/^\[sshd\]$/&\nenabled = false/" /etc/fail2ban/jail.local \
|
||||||
&& sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf
|
&& sed -i "s/#allowipv6 = auto/allowipv6 = auto/g" /etc/fail2ban/fail2ban.conf
|
||||||
|
|
||||||
|
# Make scripts executable
|
||||||
RUN chmod +x \
|
RUN chmod +x \
|
||||||
/app/DockerEntrypoint.sh \
|
/app/DockerEntrypoint.sh \
|
||||||
/app/x-ui \
|
/app/x-ui \
|
||||||
/usr/bin/x-ui
|
/usr/bin/x-ui
|
||||||
|
|
||||||
|
# Enable fail2ban via environment variable (can be overridden)
|
||||||
ENV XUI_ENABLE_FAIL2BAN="true"
|
ENV XUI_ENABLE_FAIL2BAN="true"
|
||||||
|
|
||||||
|
# Define volume for persistent configuration
|
||||||
VOLUME [ "/etc/x-ui" ]
|
VOLUME [ "/etc/x-ui" ]
|
||||||
|
|
||||||
|
# Default command when container starts
|
||||||
CMD [ "./x-ui" ]
|
CMD [ "./x-ui" ]
|
||||||
|
|
||||||
|
# Custom entrypoint to initialize runtime environment
|
||||||
ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
|
ENTRYPOINT [ "/app/DockerEntrypoint.sh" ]
|
||||||
|
|
Loading…
Reference in a new issue