Compare commits
5
Commits
9c91e9e266
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0476a7e9c | ||
|
|
b0a0aa9e3c | ||
|
|
f516d848c1 | ||
|
|
653d965320 | ||
|
|
496aced0d0 |
@@ -1,21 +1,25 @@
|
||||
# SSH-Manager (TNS243-GLOBAL Manager — self-contained bundle)
|
||||
|
||||
`TNS243-GLOBAL/ManagerPanel`'in remote-bağımsız (offline) dağıtımı.
|
||||
Kurulum sırasında GitHub'a bağlanmaz; tüm binary'ler paketin içindedir.
|
||||
Kurulum sırasında GitHub'a bağlanmaz; tüm birinci-parti dosyalar paketin içindedir.
|
||||
`udp-custom` binary'lerinin UPX katmanı açılmış (unpacked) haldedir.
|
||||
|
||||
Upstream (`ManagerPanel`) bağımlılığı tamamen kaldırılmıştır: panel, udp-custom,
|
||||
udpgw ve falconproxy artık **yalnızca** yerel bundle'dan kurulur. Dosya eksikse
|
||||
menü uzaktan indirmeye düşmez, hata verip durur.
|
||||
|
||||
## VPS kurulumu
|
||||
|
||||
### Yöntem 1 — Tek satır (önerilen)
|
||||
|
||||
```bash
|
||||
bash <(curl -sL http://146.19.208.111:3001/yasin/SSH-Manager/raw/branch/main/install.sh)
|
||||
bash <(curl -sL https://git.yasindemir.link/yasin/SSH-Manager/raw/branch/main/install.sh)
|
||||
```
|
||||
|
||||
### Yöntem 2 — Manuel klon
|
||||
|
||||
```bash
|
||||
git clone http://146.19.208.111:3001/yasin/SSH-Manager.git
|
||||
git clone https://git.yasindemir.link/yasin/SSH-Manager.git
|
||||
cd SSH-Manager
|
||||
sudo bash install.sh
|
||||
```
|
||||
@@ -32,3 +36,26 @@ sudo bash install.sh
|
||||
- `udp/udp-custom-linux-amd64` / `udp-custom-linux-arm`: UPX ile açılmış build'ler.
|
||||
Orijinal paketli haller dışarıda (`*.upxbackup`) tutulur ve bundle'a girmez.
|
||||
- Web panel: `menu` içinden kurulur, JSON tabanlı oturum yönetimi `/etc/firewallfalcon` altındadır.
|
||||
- `udp/udpgw` bir x86-64 build'idir. ARM64 sunucuda udpgw kaynaktan derlenir
|
||||
(`ambrop72/badvpn`, internet gerekir). Tamamen offline ARM kurulumu için
|
||||
arm64 derlenmiş bir udpgw'yi `udp/udpgw-linux-arm64` adıyla repoya koymak
|
||||
yeterlidir; menü varsa otomatik onu kullanır.
|
||||
|
||||
## Kalan internet bağımlılıkları (üçüncü taraf / opsiyonel)
|
||||
|
||||
Aşağıdakiler `ManagerPanel` ile ilgili değildir; ilgili menü öğesi seçilmedikçe
|
||||
çalışmazlar:
|
||||
|
||||
| Modül | Kaynak |
|
||||
|---|---|
|
||||
| badvpn (ve ARM'de udpgw derlemesi) | `github.com/ambrop72/badvpn` |
|
||||
| ZiVPN | `github.com/zahidbd2/udp-zivpn` releases |
|
||||
| DNSTT | `dnstt.network` |
|
||||
| 3x-ui / x-ui | `raw.githubusercontent.com/mhsanaei`, `alireza0` install script'leri |
|
||||
| DDNS | `desec.io` API |
|
||||
| Public IP tespiti | `icanhazip.com` |
|
||||
| SSL sertifikası | certbot / Let's Encrypt |
|
||||
| Paket kurulumu | dağıtımın apt/dnf depoları |
|
||||
|
||||
Ayrıca web panel arayüzü `fonts.googleapis.com` üzerinden font çeker; internet
|
||||
yoksa yalnızca yazı tipi fallback'e düşer, işlevsellik etkilenmez.
|
||||
+42
-2
@@ -10,12 +10,12 @@ fi
|
||||
echo "Installing TNS243-GLOBAL Manager (localized bundle)..."
|
||||
|
||||
# Repo URI used when this script is executed piped (curl | bash) instead of from a local clone
|
||||
REPO_URL="http://146.19.208.111:3001/yasin/SSH-Manager.git"
|
||||
REPO_URL="https://git.yasindemir.link/yasin/SSH-Manager.git"
|
||||
|
||||
# Resolve the directory this script lives in (the cloned repo root).
|
||||
# Supports both:
|
||||
# git clone $REPO_URL && cd SSH-Manager && bash install.sh
|
||||
# bash <(curl -sL http://146.19.208.111:3001/yasin/SSH-Manager/raw/branch/main/install.sh)
|
||||
# bash <(curl -sL https://git.yasindemir.link/yasin/SSH-Manager/raw/branch/main/install.sh)
|
||||
SCRIPT_DIR=""
|
||||
PIPED_CLONE_DIR=""
|
||||
if [[ -n "${BASH_SOURCE[0]}" && -f "${BASH_SOURCE[0]}" ]]; then
|
||||
@@ -62,6 +62,25 @@ cp -r "$SCRIPT_DIR/release" "$BUNDLE_INSTALL_DIR/release"
|
||||
# Drop UPX packed originals from the bundle (unpacked binaries are used)
|
||||
rm -f "$BUNDLE_INSTALL_DIR"/udp/*.upxbackup
|
||||
|
||||
# Verify the staged bundle: the menu has no remote fallback, so a missing file
|
||||
# here would surface later as a failed module install.
|
||||
for staged in \
|
||||
"$BUNDLE_INSTALL_DIR/panel/panel.py" \
|
||||
"$BUNDLE_INSTALL_DIR/panel/index.html" \
|
||||
"$BUNDLE_INSTALL_DIR/udp/udp-custom-linux-amd64" \
|
||||
"$BUNDLE_INSTALL_DIR/udp/udp-custom-linux-arm" \
|
||||
"$BUNDLE_INSTALL_DIR/udp/udpgw" \
|
||||
"$BUNDLE_INSTALL_DIR/release/falconproxy" \
|
||||
"$BUNDLE_INSTALL_DIR/release/falconproxyarm"
|
||||
do
|
||||
if [[ ! -s "$staged" ]]; then
|
||||
echo "Error: Bundle staging failed, missing or empty: $staged"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
chmod +x "$BUNDLE_INSTALL_DIR"/udp/udp-custom-linux-* "$BUNDLE_INSTALL_DIR"/udp/udpgw* \
|
||||
"$BUNDLE_INSTALL_DIR"/release/falconproxy* 2>/dev/null || true
|
||||
|
||||
# Install menu from the local copy
|
||||
cp "$SCRIPT_DIR/menu.sh" /usr/local/bin/menu
|
||||
chmod +x /usr/local/bin/menu
|
||||
@@ -80,6 +99,27 @@ cp "$SSHD_CONFIG" "$BACKUP"
|
||||
cp "$SCRIPT_DIR/ssh" "$SSHD_CONFIG"
|
||||
chmod 600 "$SSHD_CONFIG"
|
||||
|
||||
# The template carries the Debian path for sftp-server. sshd -t does not check
|
||||
# that the binary exists, so on other distributions SFTP would silently break.
|
||||
SFTP_SERVER=""
|
||||
for candidate in \
|
||||
/usr/lib/openssh/sftp-server \
|
||||
/usr/libexec/openssh/sftp-server \
|
||||
/usr/lib/ssh/sftp-server \
|
||||
/usr/libexec/sftp-server
|
||||
do
|
||||
if [[ -x "$candidate" ]]; then
|
||||
SFTP_SERVER="$candidate"
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -n "$SFTP_SERVER" ]]; then
|
||||
sed -i "s|^Subsystem sftp .*|Subsystem sftp $SFTP_SERVER|" "$SSHD_CONFIG"
|
||||
echo "Using sftp-server: $SFTP_SERVER"
|
||||
else
|
||||
echo "WARNING: no sftp-server binary found; leaving the Subsystem line unchanged."
|
||||
fi
|
||||
|
||||
# Validate SSH config (silent)
|
||||
if ! sshd -t 2>/dev/null; then
|
||||
echo "ERROR: SSH configuration is invalid!"
|
||||
|
||||
@@ -75,10 +75,22 @@ PANEL_HTML_FILE="$DB_DIR/panel/index.html"
|
||||
PANEL_CONF="$DB_DIR/panel.conf"
|
||||
PANEL_SERVICE_FILE="/etc/systemd/system/firewallfalcon-panel.service"
|
||||
PANEL_PORT=44380
|
||||
PANEL_REPO_BASE="https://raw.githubusercontent.com/TNS243-GLOBAL/ManagerPanel/main/panel"
|
||||
# Localized offline bundle (remote-independent install). Overridden by install.sh.
|
||||
# Every first-party asset (panel, udp-custom, udpgw, falconproxy) is read from here;
|
||||
# there is no remote fallback.
|
||||
FF_BUNDLE_DIR="${FF_BUNDLE_DIR:-/opt/firewallfalcon-bundle}"
|
||||
|
||||
# Abort the caller when a required bundle asset is missing.
|
||||
ff_require_bundle_file() {
|
||||
local path="$1"
|
||||
if [[ ! -f "$path" ]]; then
|
||||
echo -e "\n${C_RED}❌ Missing bundle file: $path${C_RESET}"
|
||||
echo -e "${C_YELLOW} The local bundle is incomplete. Re-run install.sh to restore it.${C_RESET}"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- ZiVPN Variables ---
|
||||
ZIVPN_DIR="/etc/zivpn"
|
||||
ZIVPN_BIN="/usr/local/bin/zivpn"
|
||||
@@ -391,6 +403,9 @@ check_environment() {
|
||||
ensure_firewallfalcon_dirs() {
|
||||
mkdir -p "$DB_DIR" "$SSL_CERT_DIR" "$BANDWIDTH_DIR" /etc/ssh/sshd_config.d
|
||||
touch "$DB_FILE"
|
||||
# users.db holds cleartext credentials and lives on a box where the managed
|
||||
# users have accounts; keep it readable by root only.
|
||||
chmod 600 "$DB_FILE" 2>/dev/null
|
||||
}
|
||||
|
||||
ensure_firewallfalcon_system_group() {
|
||||
@@ -402,6 +417,49 @@ db_has_user() {
|
||||
awk -F: -v target="$1" '$1 == target { found=1; exit } END { exit(found ? 0 : 1) }' "$DB_FILE"
|
||||
}
|
||||
|
||||
# users.db fields: 1=user 2=pass 3=expiry 4=conn_limit 5=bandwidth 6=daily_bandwidth 7=marker
|
||||
DB_FIELD_PASS=2
|
||||
DB_FIELD_EXPIRY=3
|
||||
DB_FIELD_LIMIT=4
|
||||
DB_FIELD_BW=5
|
||||
DB_FIELD_DAILY_BW=6
|
||||
|
||||
# Rewrite a single field of a user's record. Uses awk instead of `sed s/^user:.*/.../`
|
||||
# so that values containing / & \ are stored literally, and so the trailing marker
|
||||
# field is preserved instead of being dropped.
|
||||
db_set_user_field() {
|
||||
local username="$1" field="$2" value="$3" tmp
|
||||
[[ -f "$DB_FILE" ]] || return 1
|
||||
tmp=$(mktemp) || return 1
|
||||
if ! awk -F: -v OFS=: -v u="$username" -v f="$field" -v v="$value" \
|
||||
'$1 == u { $f = v } { print }' "$DB_FILE" > "$tmp"; then
|
||||
rm -f "$tmp"
|
||||
return 1
|
||||
fi
|
||||
# Copy contents rather than mv so the 0600 mode of users.db survives.
|
||||
cat "$tmp" > "$DB_FILE"
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
# Reject passwords that would corrupt the ':'-delimited record or the shell
|
||||
# pipelines that consume it.
|
||||
ff_is_valid_password() {
|
||||
local pw="$1"
|
||||
if [[ -z "$pw" ]]; then
|
||||
echo -e "\n${C_RED}❌ Password cannot be empty.${C_RESET}"
|
||||
return 1
|
||||
fi
|
||||
# Quoted literals rather than backslash-escaped case patterns: the latter are
|
||||
# easy to get subtly wrong. ':' breaks the record layout, a backslash is eaten
|
||||
# by awk -v, and quotes/whitespace break the shell pipelines that consume it.
|
||||
if [[ "$pw" == *:* || "$pw" == *'\'* || "$pw" == *"'"* || "$pw" == *'"'* ]] \
|
||||
|| [[ "$pw" =~ [[:space:]] ]]; then
|
||||
echo -e "\n${C_RED}❌ Password cannot contain ':', backslash, quotes or whitespace.${C_RESET}"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
is_firewallfalcon_orphan_user() {
|
||||
local username="$1"
|
||||
local passwd_line system_user _ uid _ home shell
|
||||
@@ -485,6 +543,7 @@ delete_firewallfalcon_user_accounts() {
|
||||
rm -f "$BANDWIDTH_DIR/${username}.daily_usage"
|
||||
rm -f "$BANDWIDTH_DIR/${username}.conn_locked"
|
||||
rm -f "$BANDWIDTH_DIR/${username}.daily_locked"
|
||||
rm -f "$BANDWIDTH_DIR/${username}.trial_expiry"
|
||||
rm -rf "$BANDWIDTH_DIR/pidtrack/${username}"
|
||||
done
|
||||
|
||||
@@ -1210,7 +1269,8 @@ update_ssh_banners_config() {
|
||||
fi
|
||||
|
||||
ensure_firewallfalcon_dirs
|
||||
tmp_conf="/tmp/ff_banners_new.conf"
|
||||
# mktemp, not a fixed /tmp name: root writes this file and /tmp is world-writable.
|
||||
tmp_conf=$(mktemp) || return
|
||||
echo "# FirewallFalcon - Dynamic per-user SSH banners" > "$tmp_conf"
|
||||
|
||||
if [[ -f "$DB_FILE" ]]; then
|
||||
@@ -1223,6 +1283,7 @@ update_ssh_banners_config() {
|
||||
|
||||
if ! cmp -s "$tmp_conf" "$SSHD_FF_CONFIG" 2>/dev/null; then
|
||||
mv "$tmp_conf" "$SSHD_FF_CONFIG"
|
||||
chmod 644 "$SSHD_FF_CONFIG"
|
||||
if ! grep -q "^Include /etc/ssh/sshd_config.d/" /etc/ssh/sshd_config 2>/dev/null; then
|
||||
echo "Include /etc/ssh/sshd_config.d/*.conf" >> /etc/ssh/sshd_config
|
||||
fi
|
||||
@@ -1616,7 +1677,7 @@ create_user() {
|
||||
password=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 8)
|
||||
echo -e "${C_GREEN}🔑 Auto-generated password: ${C_YELLOW}$password${C_RESET}"
|
||||
break
|
||||
else
|
||||
elif ff_is_valid_password "$password"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
@@ -1642,7 +1703,7 @@ create_user() {
|
||||
fi
|
||||
usermod -aG "$FF_USERS_GROUP" "$username" 2>/dev/null
|
||||
echo "$username:$password" | chpasswd; chage -E "$expire_date" "$username"
|
||||
echo "$username:$password:$expire_date:$limit:$bandwidth_gb:$daily_bandwidth_gb:trial" >> "$DB_FILE"
|
||||
echo "$username:$password:$expire_date:$limit:$bandwidth_gb:$daily_bandwidth_gb:normal" >> "$DB_FILE"
|
||||
|
||||
local bw_display="Unlimited"
|
||||
if [[ "$bandwidth_gb" != "0" ]]; then bw_display="${bandwidth_gb} GB"; fi
|
||||
@@ -1745,25 +1806,27 @@ edit_user() {
|
||||
if [[ -z "$new_pass" ]]; then
|
||||
new_pass=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 8)
|
||||
echo -e "${C_GREEN}🔑 Auto-generated: ${C_YELLOW}$new_pass${C_RESET}"
|
||||
elif ! ff_is_valid_password "$new_pass"; then
|
||||
continue
|
||||
fi
|
||||
echo "$username:$new_pass" | chpasswd
|
||||
sed -i "s/^$username:.*/$username:$new_pass:$cur_expiry:$cur_limit:$cur_bw:$cur_daily_bw/" "$DB_FILE"
|
||||
db_set_user_field "$username" "$DB_FIELD_PASS" "$new_pass"
|
||||
echo -e "\n${C_GREEN}✅ Password for '$username' changed to: ${C_YELLOW}$new_pass${C_RESET}"
|
||||
;;
|
||||
2) read -p "Enter new duration (in days from today): " days
|
||||
if [[ "$days" =~ ^[0-9]+$ ]]; then
|
||||
local new_expire_date; new_expire_date=$(date -d "+$days days" +%Y-%m-%d); chage -E "$new_expire_date" "$username"
|
||||
sed -i "s/^$username:.*/$username:$cur_pass:$new_expire_date:$cur_limit:$cur_bw:$cur_daily_bw/" "$DB_FILE"
|
||||
db_set_user_field "$username" "$DB_FIELD_EXPIRY" "$new_expire_date"
|
||||
echo -e "\n${C_GREEN}✅ Expiration for '$username' set to ${C_YELLOW}$new_expire_date${C_RESET}."
|
||||
else echo -e "\n${C_RED}❌ Invalid number of days.${C_RESET}"; fi ;;
|
||||
3) read -p "Enter new simultaneous connection limit: " new_limit
|
||||
if [[ "$new_limit" =~ ^[0-9]+$ ]]; then
|
||||
sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$new_limit:$cur_bw:$cur_daily_bw/" "$DB_FILE"
|
||||
db_set_user_field "$username" "$DB_FIELD_LIMIT" "$new_limit"
|
||||
echo -e "\n${C_GREEN}✅ Connection limit for '$username' set to ${C_YELLOW}$new_limit${C_RESET}."
|
||||
else echo -e "\n${C_RED}❌ Invalid limit.${C_RESET}"; fi ;;
|
||||
4) read -p "Enter new TOTAL bandwidth limit in GB (0 = unlimited): " new_bw
|
||||
if [[ "$new_bw" =~ ^[0-9]+\.?[0-9]*$ ]]; then
|
||||
sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$cur_limit:$new_bw:$cur_daily_bw/" "$DB_FILE"
|
||||
db_set_user_field "$username" "$DB_FIELD_BW" "$new_bw"
|
||||
local bw_msg="Unlimited"; [[ "$new_bw" != "0" ]] && bw_msg="${new_bw} GB"
|
||||
echo -e "\n${C_GREEN}✅ Total bandwidth limit for '$username' set to ${C_YELLOW}$bw_msg${C_RESET}."
|
||||
# Unlock user if they were locked due to bandwidth
|
||||
@@ -1777,7 +1840,7 @@ edit_user() {
|
||||
else echo -e "\n${C_RED}❌ Invalid bandwidth value.${C_RESET}"; fi ;;
|
||||
5) read -p "Enter new DAILY bandwidth limit in GB (0 = unlimited): " new_daily_bw
|
||||
if [[ "$new_daily_bw" =~ ^[0-9]+\.?[0-9]*$ ]]; then
|
||||
sed -i "s/^$username:.*/$username:$cur_pass:$cur_expiry:$cur_limit:$cur_bw:$new_daily_bw/" "$DB_FILE"
|
||||
db_set_user_field "$username" "$DB_FIELD_DAILY_BW" "$new_daily_bw"
|
||||
local daily_bw_msg="Unlimited"; [[ "$new_daily_bw" != "0" ]] && daily_bw_msg="${new_daily_bw} GB/day"
|
||||
echo -e "\n${C_GREEN}✅ Daily bandwidth limit for '$username' set to ${C_YELLOW}$daily_bw_msg${C_RESET}."
|
||||
# Unlock user if they were locked due to daily bandwidth
|
||||
@@ -1981,11 +2044,7 @@ renew_user() {
|
||||
echo -e "\n${C_BLUE}🔄 Renewing selected users for $days days...${C_RESET}"
|
||||
for u in "${SELECTED_USERS[@]}"; do
|
||||
chage -E "$new_expire_date" "$u"
|
||||
local line pass _expiry limit bw
|
||||
line=$(grep "^$u:" "$DB_FILE")
|
||||
IFS=: read -r _ pass _expiry limit bw _ <<< "$line"
|
||||
[[ -z "$bw" ]] && bw="0"
|
||||
sed -i "s/^$u:.*/$u:$pass:$new_expire_date:$limit:$bw/" "$DB_FILE"
|
||||
db_set_user_field "$u" "$DB_FIELD_EXPIRY" "$new_expire_date"
|
||||
echo -e " ✅ ${C_YELLOW}$u${C_RESET} renewed until ${C_GREEN}${new_expire_date}${C_RESET}."
|
||||
done
|
||||
}
|
||||
@@ -2251,14 +2310,11 @@ install_udp_custom() {
|
||||
echo -e "\n${C_GREEN}⚙️ Detecting system architecture...${C_RESET}"
|
||||
local arch
|
||||
arch=$(uname -m)
|
||||
local binary_url=""
|
||||
local binary_source=""
|
||||
if [[ "$arch" == "x86_64" ]]; then
|
||||
binary_url="https://raw.githubusercontent.com/TNS243-GLOBAL/ManagerPanel/main/udp/udp-custom-linux-amd64"
|
||||
binary_source="udp-custom-linux-amd64"
|
||||
echo -e "${C_BLUE}ℹ️ Detected x86_64 (amd64) architecture.${C_RESET}"
|
||||
elif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then
|
||||
binary_url="https://raw.githubusercontent.com/TNS243-GLOBAL/ManagerPanel/main/udp/udp-custom-linux-arm"
|
||||
binary_source="udp-custom-linux-arm"
|
||||
echo -e "${C_BLUE}ℹ️ Detected ARM64 architecture.${C_RESET}"
|
||||
else
|
||||
@@ -2267,28 +2323,32 @@ install_udp_custom() {
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "\n${C_GREEN}📥 Installing udp-custom binary...${C_RESET}"
|
||||
if [[ -f "$FF_BUNDLE_DIR/udp/$binary_source" ]]; then
|
||||
echo -e "${C_BLUE}ℹ️ Using local bundle copy: $binary_source${C_RESET}"
|
||||
cp "$FF_BUNDLE_DIR/udp/$binary_source" "$UDP_CUSTOM_DIR/udp-custom"
|
||||
else
|
||||
wget -q --show-progress -O "$UDP_CUSTOM_DIR/udp-custom" "$binary_url"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "\n${C_RED}❌ Failed to download the udp-custom binary.${C_RESET}"
|
||||
echo -e "\n${C_GREEN}📥 Installing udp-custom binary from local bundle...${C_RESET}"
|
||||
if ! ff_require_bundle_file "$FF_BUNDLE_DIR/udp/$binary_source"; then
|
||||
rm -rf "$UDP_CUSTOM_DIR"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
echo -e "${C_BLUE}ℹ️ Using local bundle copy: $binary_source${C_RESET}"
|
||||
cp "$FF_BUNDLE_DIR/udp/$binary_source" "$UDP_CUSTOM_DIR/udp-custom"
|
||||
chmod +x "$UDP_CUSTOM_DIR/udp-custom"
|
||||
|
||||
echo -e "\n${C_GREEN}📦 Setting up udpgw helper...${C_RESET}"
|
||||
# The bundled udpgw is an x86-64 build; ARM uses an optional arm64 bundle copy,
|
||||
# otherwise it is compiled from source.
|
||||
local udpgw_source=""
|
||||
if [[ "$arch" == "x86_64" ]]; then
|
||||
if [[ -f "$FF_BUNDLE_DIR/udp/udpgw" ]]; then
|
||||
echo -e "${C_BLUE}ℹ️ Using local bundle copy: udpgw${C_RESET}"
|
||||
cp "$FF_BUNDLE_DIR/udp/udpgw" "$UDPGW_BINARY"
|
||||
else
|
||||
wget -q --show-progress -O "$UDPGW_BINARY" "https://raw.githubusercontent.com/http-custom/udp-custom/main/module/udpgw"
|
||||
udpgw_source="$FF_BUNDLE_DIR/udp/udpgw"
|
||||
if ! ff_require_bundle_file "$udpgw_source"; then
|
||||
rm -rf "$UDP_CUSTOM_DIR"
|
||||
return
|
||||
fi
|
||||
elif [[ -f "$FF_BUNDLE_DIR/udp/udpgw-linux-arm64" ]]; then
|
||||
udpgw_source="$FF_BUNDLE_DIR/udp/udpgw-linux-arm64"
|
||||
fi
|
||||
|
||||
if [[ -n "$udpgw_source" ]]; then
|
||||
echo -e "${C_BLUE}ℹ️ Using local bundle copy: $(basename "$udpgw_source")${C_RESET}"
|
||||
cp "$udpgw_source" "$UDPGW_BINARY"
|
||||
if [ ! -s "$UDPGW_BINARY" ]; then
|
||||
echo -e "\n${C_RED}❌ Failed to obtain the udpgw helper binary.${C_RESET}"
|
||||
rm -rf "$UDP_CUSTOM_DIR"
|
||||
@@ -2296,10 +2356,11 @@ install_udp_custom() {
|
||||
fi
|
||||
chmod +x "$UDPGW_BINARY"
|
||||
else
|
||||
echo -e "${C_YELLOW}ℹ️ Architecture is $arch. Compiling udpgw from source (this may take a minute)...${C_RESET}"
|
||||
echo -e "${C_YELLOW}ℹ️ Architecture is $arch and no bundled arm64 udpgw was found. Compiling udpgw from source (needs internet, this may take a minute)...${C_RESET}"
|
||||
ff_pkg_install cmake g++ make git >/dev/null 2>&1
|
||||
local temp_build="/tmp/badvpn_build"
|
||||
rm -rf "$temp_build"
|
||||
# mktemp -d, not a fixed /tmp path: this is built and copied from as root.
|
||||
local temp_build
|
||||
temp_build=$(mktemp -d) || return
|
||||
git clone -q https://github.com/ambrop72/badvpn.git "$temp_build"
|
||||
(cd "$temp_build" && cmake . >/dev/null 2>&1 && make >/dev/null 2>&1)
|
||||
local compiled_bin=$(find "$temp_build" -name "badvpn-udpgw" -type f | head -n 1)
|
||||
@@ -3437,53 +3498,13 @@ install_falcon_proxy() {
|
||||
if [[ "$confirm_reinstall" != "y" ]]; then return; fi
|
||||
fi
|
||||
|
||||
local use_local_bundle=false
|
||||
if [[ -f "$FF_BUNDLE_DIR/release/falconproxy" || -f "$FF_BUNDLE_DIR/release/falconproxyarm" ]]; then
|
||||
use_local_bundle=true
|
||||
fi
|
||||
|
||||
local SELECTED_VERSION=""
|
||||
if $use_local_bundle; then
|
||||
SELECTED_VERSION="v0-websockets"
|
||||
echo -e "${C_BLUE}ℹ️ Local bundle detected: using bundled release v0-websockets.${C_RESET}"
|
||||
else
|
||||
echo -e "\n${C_BLUE}🌐 Fetching available versions from GitHub...${C_RESET}"
|
||||
local releases_json=$(curl -s "https://api.github.com/repos/TNS243-GLOBAL/ManagerPanel/releases")
|
||||
if [[ -z "$releases_json" || "$releases_json" == "[]" ]]; then
|
||||
echo -e "${C_RED}❌ Error: Could not fetch releases. Check internet or API limits.${C_RESET}"
|
||||
# Falcon Proxy ships inside the local bundle; there is no remote release lookup.
|
||||
local SELECTED_VERSION="v0-websockets"
|
||||
if [[ ! -f "$FF_BUNDLE_DIR/release/falconproxy" && ! -f "$FF_BUNDLE_DIR/release/falconproxyarm" ]]; then
|
||||
ff_require_bundle_file "$FF_BUNDLE_DIR/release/falconproxy"
|
||||
return
|
||||
fi
|
||||
|
||||
# Extract tag names
|
||||
mapfile -t versions < <(echo "$releases_json" | jq -r '.[].tag_name')
|
||||
|
||||
if [ ${#versions[@]} -eq 0 ]; then
|
||||
echo -e "${C_RED}❌ No releases found in the repository.${C_RESET}"
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "\n${C_CYAN}Select a version to install:${C_RESET}"
|
||||
for i in "${!versions[@]}"; do
|
||||
printf " ${C_GREEN}[%2d]${C_RESET} %s\n" "$((i+1))" "${versions[$i]}"
|
||||
done
|
||||
echo -e " ${C_RED} [ 0]${C_RESET} ↩️ Cancel"
|
||||
|
||||
local choice
|
||||
while true; do
|
||||
if ! read -r -p "👉 Enter version number [1]: " choice; then
|
||||
echo
|
||||
return
|
||||
fi
|
||||
choice=${choice:-1}
|
||||
if [[ "$choice" == "0" ]]; then return; fi
|
||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -le "${#versions[@]}" ]; then
|
||||
SELECTED_VERSION="${versions[$((choice-1))]}"
|
||||
break
|
||||
else
|
||||
echo -e "${C_RED}❌ Invalid selection.${C_RESET}"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
echo -e "${C_BLUE}ℹ️ Using bundled release $SELECTED_VERSION.${C_RESET}"
|
||||
|
||||
local ports
|
||||
read -p "👉 Enter port(s) for Falcon Proxy (e.g., 8080 or 8080 8888) [8080]: " ports
|
||||
@@ -3513,16 +3534,11 @@ install_falcon_proxy() {
|
||||
return
|
||||
fi
|
||||
|
||||
echo -e "\n${C_GREEN}📥 Installing Falcon Proxy $SELECTED_VERSION ($binary_name)...${C_RESET}"
|
||||
if $use_local_bundle; then
|
||||
echo -e "\n${C_GREEN}📥 Installing Falcon Proxy $SELECTED_VERSION ($binary_name) from local bundle...${C_RESET}"
|
||||
ff_require_bundle_file "$FF_BUNDLE_DIR/release/$binary_name" || return
|
||||
cp "$FF_BUNDLE_DIR/release/$binary_name" "$FALCONPROXY_BINARY"
|
||||
else
|
||||
# Construct download URL based on selected version
|
||||
local download_url="https://github.com/TNS243-GLOBAL/ManagerPanel/releases/download/$SELECTED_VERSION/$binary_name"
|
||||
wget -q --show-progress -O "$FALCONPROXY_BINARY" "$download_url"
|
||||
fi
|
||||
if [ ! -s "$FALCONPROXY_BINARY" ]; then
|
||||
echo -e "\n${C_RED}❌ Failed to obtain the binary. Please ensure version $SELECTED_VERSION has asset '$binary_name'.${C_RESET}"
|
||||
echo -e "\n${C_RED}❌ Failed to install the bundled binary '$binary_name'.${C_RESET}"
|
||||
return
|
||||
fi
|
||||
chmod +x "$FALCONPROXY_BINARY"
|
||||
@@ -4363,32 +4379,26 @@ install_web_panel() {
|
||||
local panel_secret
|
||||
panel_secret="panel_$(tr -dc 'a-z0-9' < /dev/urandom | head -c 8)"
|
||||
|
||||
echo -e "${C_BLUE}📥 Downloading panel files...${C_RESET}"
|
||||
echo -e "${C_BLUE}📥 Installing panel files from local bundle...${C_RESET}"
|
||||
mkdir -p "$PANEL_HTML_DIR"
|
||||
|
||||
# Install backend
|
||||
if [[ -f "$FF_BUNDLE_DIR/panel/panel.py" ]]; then
|
||||
ff_require_bundle_file "$FF_BUNDLE_DIR/panel/panel.py" || return
|
||||
echo -e "${C_BLUE}ℹ️ Using local bundle copy: panel.py${C_RESET}"
|
||||
cp "$FF_BUNDLE_DIR/panel/panel.py" "$PANEL_SCRIPT"
|
||||
else
|
||||
curl -sL "$PANEL_REPO_BASE/panel.py" -o "$PANEL_SCRIPT"
|
||||
fi
|
||||
if [ $? -ne 0 ] || [ ! -s "$PANEL_SCRIPT" ]; then
|
||||
echo -e "${C_RED}❌ Failed to obtain panel backend.${C_RESET}"
|
||||
if [ ! -s "$PANEL_SCRIPT" ]; then
|
||||
echo -e "${C_RED}❌ Failed to install panel backend.${C_RESET}"
|
||||
return
|
||||
fi
|
||||
chmod +x "$PANEL_SCRIPT"
|
||||
sed -i 's/\r$//' "$PANEL_SCRIPT" 2>/dev/null
|
||||
|
||||
# Install frontend
|
||||
if [[ -f "$FF_BUNDLE_DIR/panel/index.html" ]]; then
|
||||
ff_require_bundle_file "$FF_BUNDLE_DIR/panel/index.html" || return
|
||||
echo -e "${C_BLUE}ℹ️ Using local bundle copy: index.html${C_RESET}"
|
||||
cp "$FF_BUNDLE_DIR/panel/index.html" "$PANEL_HTML_FILE"
|
||||
else
|
||||
curl -sL "$PANEL_REPO_BASE/index.html" -o "$PANEL_HTML_FILE"
|
||||
fi
|
||||
if [ $? -ne 0 ] || [ ! -s "$PANEL_HTML_FILE" ]; then
|
||||
echo -e "${C_RED}❌ Failed to obtain panel frontend.${C_RESET}"
|
||||
if [ ! -s "$PANEL_HTML_FILE" ]; then
|
||||
echo -e "${C_RED}❌ Failed to install panel frontend.${C_RESET}"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -4757,7 +4767,10 @@ create_trial_account() {
|
||||
# Password
|
||||
local password=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 8)
|
||||
read -p "🔑 Password [${password}]: " custom_pass
|
||||
password=${custom_pass:-$password}
|
||||
if [[ -n "$custom_pass" ]]; then
|
||||
ff_is_valid_password "$custom_pass" || return
|
||||
password="$custom_pass"
|
||||
fi
|
||||
|
||||
# Connection limit
|
||||
read -p "📶 Connection limit [1]: " limit
|
||||
@@ -4787,10 +4800,15 @@ create_trial_account() {
|
||||
usermod -aG "$FF_USERS_GROUP" "$username" 2>/dev/null
|
||||
echo "$username:$password" | chpasswd
|
||||
chage -E "$expire_date" "$username"
|
||||
echo "$username:$password:$expire_date:$limit:$bandwidth_gb:trial" >> "$DB_FILE"
|
||||
# Keep the 7-field layout: the cleanup script reads the marker from field 7,
|
||||
# so the daily-bandwidth field must be present even though trials do not set one.
|
||||
echo "$username:$password:$expire_date:$limit:$bandwidth_gb:0:trial" >> "$DB_FILE"
|
||||
|
||||
# Schedule auto-cleanup via 'at'
|
||||
echo "$TRIAL_CLEANUP_SCRIPT $username" | at now + ${duration_hours} hours 2>/dev/null
|
||||
# Fallback for the limiter's trial sweep, in case atd is stopped or the job is lost.
|
||||
mkdir -p "$BANDWIDTH_DIR"
|
||||
date -d "+${duration_hours} hours" +%s > "$BANDWIDTH_DIR/${username}.trial_expiry"
|
||||
|
||||
local bw_display="Unlimited"
|
||||
if [[ "$bandwidth_gb" != "0" ]]; then bw_display="${bandwidth_gb} GB"; fi
|
||||
|
||||
@@ -28,3 +28,8 @@ AcceptEnv LANG LC_*
|
||||
Subsystem sftp /usr/lib/openssh/sftp-server
|
||||
UsePAM yes
|
||||
Banner /etc/bannerssh
|
||||
|
||||
# Kept last on purpose. OpenSSH uses the first value it obtains for a keyword,
|
||||
# so everything above still wins over any drop-in; this only lets the Match
|
||||
# blocks that menu.sh writes to sshd_config.d (per-user banners) take effect.
|
||||
Include /etc/ssh/sshd_config.d/*.conf
|
||||
|
||||
Reference in New Issue
Block a user