Commit Graph

192 Commits

Author SHA1 Message Date
MHSanaei cf5f37e409 fix(iplimit): ban UDP as well as TCP in fail2ban action (#5350)
The generated 3x-ipl fail2ban action only matched -p tcp, so UDP-based
inbounds (Hysteria2, TUIC, WireGuard) from a banned IP kept working,
bypassing IP-limit enforcement. Drop the protocol qualifier from the
chain jump and ban both tcp and udp, keeping the SSH/panel port exemption.
2026-06-15 17:34:23 +02:00
MHSanaei c200e248f7 fix(script): report per-file geo update status and skip restart when nothing changed
Updating geo files printed raw curl progress meters showing 0 bytes when
files were already current (curl -z conditional download), claimed success
unconditionally, and restarted xray even when nothing was downloaded —
confusing enough to be reported as a bug (#5230).

Now each file reports updated / already up to date / download failed,
failures no longer print a success message, and the restart (which drops
live connections) only happens when a file actually changed. Same for the
non-interactive 'x-ui update-all-geofiles' command.
2026-06-12 22:18:42 +02:00
MHSanaei dbee150b33 fix(script): SSL management fixes (#4994, #5010, #5070)
- Issue acme.sh HTTP-01 over IPv4 unless the host has no global IPv4
  address: the hardcoded --listen-v6 started a v6-only standalone
  listener, so validation of a domain whose A record points at this
  host always failed (#4994).
- Add a custom cert/key path option to the "Set Cert paths" menu so
  certificates living outside /root/cert (e.g. certbot under
  /etc/letsencrypt) can be wired to the panel from the CLI (#5010).
- Derive the displayed Access URL from the certificate's actual SAN
  list instead of the cert folder name, list the other covered names,
  and show the panel's custom-path certificate in "Show Existing
  Domains" (#5070). Also silence find when /root/cert doesn't exist.
2026-06-12 01:22:30 +02:00
MHSanaei 2db48174b0 fix: apply only the x-ui sysctl config when toggling BBR (#5160)
sysctl --system re-applies every sysctl file on the host, surfacing
unrelated "Invalid argument" errors from the distro's own defaults
(e.g. Ubuntu 22.04's 50-default.conf on kernels 5.14+). Apply only
/etc/sysctl.d/99-bbr-x-ui.conf on enable, and drop the redundant
re-apply on disable since sysctl -w already restores the live values.
2026-06-11 20:53:05 +02:00
Rouzbeh† 57e9661758 fix: properly configure fail2ban backend and dependencies on Ubuntu 22.04+ (#5159) (#5184)
Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com>
2026-06-11 01:27:39 +02:00
MHSanaei 6c1594693d feat(mtproto): add domain-fronting and essential mtg options
Expose mtg's [domain-fronting] section (ip/port/proxy-protocol) plus
proxy-protocol-listener, prefer-ip, and debug on MTProto inbounds. Each
key is written to the generated mtg-<id>.toml only when set, so mtg's own
defaults apply otherwise. The instance fingerprint now covers these
fields, so editing an option restarts the sidecar.

Since MTProto is mtg-served (not Xray), sniffing does not apply: hide the
Sniffing tab and the Advanced sniffing sub-editor, drop it from the
Advanced "All" JSON view, and emit empty sniffing in the wire payload,
all gated by a new canEnableSniffing predicate.
2026-06-09 12:44:04 +02:00
Sanaei f8e89cc848 fix(mtproto): reap orphaned mtg, fix SysLog viewer, mtg log visibility, export remark (#5105) (#5107)
* fix(logs): render journalctl output in the SysLog viewer

The log viewer's parseLogLine only understood the app-log format
(2006/01/02 15:04:05 LEVEL - body). With SysLog ticked the backend
returns journalctl lines (Mon DD HH:MM:SS host ident[pid]: LEVEL - body),
so the parser mistook the journal time for the level and dropped the
body, leaving only timestamps. Detect and strip the journald prefix,
keep the journal timestamp as the stamp, then parse the real level and
body from the remainder.

* feat(mtproto): surface mtg output and add status reporting

mtg's stdout/stderr was captured by a writer that kept only the last
line and showed it nowhere, so the reason a proxy could not reach
Telegram was invisible. Stream mtg output line-by-line into the x-ui
log, tagged per inbound, so it appears in the panel log viewer and
journald.

Also fix mangled log lines: logger.Info uses fmt.Sprint, which drops
the space between adjacent string operands, producing output like
'inbound3on0.0.0.0:8443'. Switch the affected mtproto calls to the
formatted (*f) variants.

Add show_mtproto_status to x-ui.sh so 'x-ui status' reports each
mtproto inbound's mtg process state and bind address.

* fix(logs): parse all journalctl message shapes in SysLog viewer

Real journalctl output mixes four message shapes after the
'Mon DD HH:MM:SS host ident[pid]:' prefix: go-logging 'LEVEL - msg'
(x-ui/xray), Go std-log with an embedded date (net/http, runtime),
telego's '[timestamp] LEVEL msg', and systemd lines. The viewer only
understood the first, so std-log and telego lines — which never contain
' - ' — collapsed to a bare timestamp (e.g. the 8s telego 409 spam).

Extract the parser into a pure, testable module and teach it the other
shapes: strip the redundant Go std-log date, lift the level out of
telego brackets, and always keep the message body. Add a unit test
covering each shape with real captured lines.

* fix(mtproto): reap orphaned mtg sidecars so a stale one can't break new clients

On Linux x-ui does not kill its mtg children when it dies (no kill-on-exit,
unlike the Windows job object). After a crash, OOM, kill -9, or update, a
stale mtg keeps holding the inbound port with an OLD secret, so new clients
fail the FakeTLS handshake and get silently domain-fronted to the fakeTLS
domain instead of proxied to Telegram (a few MB of traffic, never connects).

Sweep orphans at startup: on the first reconcile, before x-ui starts any of
its own mtg, scan /proc and SIGKILL any process whose executable is our
mtg-<goos>-<goarch> binary. x-ui is the sole owner of mtg, so anything alive
then is an orphan. Runs once per process (swept guard), survives the
binary-deleted-during-update case via /proc/<pid>/cmdline, and is a no-op on
Windows (job object) and other platforms.

Also clear stray mtg in update.sh/install.sh after stopping x-ui, anchored to
the 'mtg-linux-<arch> run ' invocation so the pattern can't match unrelated
command lines (e.g. x-ui.sh's own 'grep mtg-linux').

* fix(logs): drop dead body initializer flagged by eslint no-useless-assignment

* fix(mtproto): drop remark fragment from tg://proxy export link

The mtproto export link appended the inbound remark as a URL fragment
(tg://proxy?server=...&port=...&secret=...#remark). Telegram Desktop
rejects a proxy deep link with a trailing fragment as 'This proxy link
is invalid', breaking one-click import, and a remark is meaningless for
proxy links across clients. Stop adding it in both the panel link
(genMtprotoLink) and the subscription service. Fixes #5105.

* fix(x-ui.sh): remove unused check_mtproto_status helper

show_mtproto_status does its own process check, so check_mtproto_status
was dead code. Drop it (per Copilot review on #5107).
2026-06-09 04:01:33 +02:00
Sanaei 8ce61f3cb0 fix(script): revoke also removes cert files and acme.sh tracking (#5009)
The SSL Certificate Management "Revoke" option only called acme.sh --revoke,
leaving local files under /root/cert/<domain>, acme.sh renewal state under
~/.acme.sh/<domain>, and the panel cert paths in the DB. Renamed to
"Revoke & Remove": it now also drops acme.sh tracking, deletes the local cert
directory and acme.sh state dirs (RSA + ECC), resolves the real IP address(es)
for IP certs so their renewal state is torn down too, and clears the panel cert
paths (x-ui cert -reset) + restarts when the deleted domain was in use.
2026-06-08 22:53:56 +02:00
Sanaei 0706b0b3a8 feat(x-ui.sh): add migrateDB command for SQLite .db <-> .dump (#4910)
* feat(x-ui.sh): add migrateDB command and menu for SQLite .db <-> .dump

Adds an "x-ui migrateDB <file>" subcommand and a PostgreSQL-menu option (9)
that convert between a SQLite .db and a portable .dump file. Direction is
auto-detected from the extension and delegated to the bundled binary
(x-ui migrate-db --dump/--restore), so no external sqlite3 client is needed.

Depends on the matching binary support, so it is only usable from the next
panel release.

* fix(x-ui.sh): address review feedback on migrateDB

Per Copilot review on PR #4910:
- Probe the bundled binary for migrate-db --dump support and fail with a clear
  upgrade message instead of a raw "flag not defined" error on old builds.
- Prompt before overwriting an existing .dump in dump mode (parity with restore).
- Refuse to restore into the live database path while x-ui is running, to avoid
  corrupting the running panel.
- Fix the usage/synopsis strings to show input is optional ([file] not <file>).
2026-06-05 11:28:11 +02:00
MHSanaei 44291de989 fix(ssl): clean ECC state, guard cert reuse, register renew hook (#4875)
- Cleanup on issuance/install failure now also removes the acme.sh
  ${domain}_ecc (and ${ip}_ecc) directory, not just ${domain}, so a
  failed run no longer leaves partial state behind.
- The 'existing certificate' check only reuses a cert when its
  fullchain.cer and key files are actually present and non-empty;
  otherwise the broken state is removed and issuance proceeds. This
  fixes the 0-byte fullchain.pem produced by reusing failed state.
- Menu option 5 (set cert paths) now registers the acme.sh --installcert
  hook with --reloadcmd 'x-ui restart' when acme.sh knows the domain, so
  auto-renewal copies the renewed cert and reloads the panel.
2026-06-04 17:15:33 +02:00
MHSanaei b1d079fc24 fix(fail2ban): exempt SSH and panel ports from IP-limit ban (#4896)
The 3x-ipl action used iptables-allports, so a banned IP lost all TCP
access including SSH and the panel, locking admins out (especially with
dynamic-IP clients). The ban now blocks every TCP port except the SSH
and panel ports via a multiport negation, derived at jail-creation time
in both x-ui.sh and DockerEntrypoint.sh. This keeps IP-limit working for
all current and future inbounds without per-port config.
2026-06-04 17:05:27 +02:00
MHSanaei a07c7b7f4e feat(migrate-db): SQLite <-> .dump conversion and Download Migration in Overview
Binary: extend the migrate-db subcommand with --dump and --restore so a
SQLite database can be exported to a portable SQL text dump and rebuilt from
one, alongside the existing --dsn PostgreSQL copy. Implemented in Go via the
bundled sqlite driver (new database/dump_sqlite.go); no external sqlite3 client
is required. Add ExportPostgresToSQLite (reverse of MigrateData) to build a
SQLite .db from live PostgreSQL data, reusing the shared copyAllModels helper.

Overview: add a "Download Migration" item to Backup & Restore plus a
getMigration endpoint/service that returns a .dump on SQLite or a .db on
PostgreSQL, so the data can seed a panel on the other backend. Document the
endpoint in api-docs and translate the three new strings across all locales.

Tests: cover the destination-side copy (AutoMigrate + copyTable into SQLite)
and the dump/restore round-trip including quoted values. Ignore *.dump.

The x-ui.sh helper that drives this from the CLI is in PR #4910.
2026-06-04 15:32:22 +02:00
MHSanaei f901cd42a5 fix(docker): make x-ui CLI menu work inside containers
check_status() only recognized a systemd service or Alpine's
/etc/init.d/x-ui, neither of which exists in a container where the panel
runs as the foreground main process (PID 1 via "exec /app/x-ui"). Every
CLI command therefore failed with "Please install the panel first", and
restart/restart-xray relied on rc-service/systemctl that aren't present.

Detect the container (/.dockerenv or XUI_IN_DOCKER) and, when inside one:
- resolve the panel binary under /app instead of /usr/local/x-ui
- derive status from the running process instead of a service file
- restart via SIGHUP and restart-xray via SIGUSR1 to the panel process
- show Docker-appropriate guidance for start/stop/enable/disable

The Dockerfile sets XUI_IN_DOCKER/XUI_MAIN_FOLDER so detection is
explicit even though /.dockerenv alone suffices.

Closes #4817
2026-06-02 21:26:47 +02:00
MHSanaei cb17eb8c06 feat(x-ui.sh): support Cloudflare API Token for DNS SSL (menu 20) (#4595)
Menu 20 only exported CF_Key/CF_Email, so a restricted Cloudflare API Token was misread as a Global Key and acme.sh failed with 'invalid domain'. Add a token-or-global-key prompt (default token): an API Token exports CF_Token, the Global Key keeps the previous CF_Key + CF_Email behavior. Also stop echoing the key/token value to the debug log.
2026-06-02 00:22:12 +02:00
MHSanaei bcb982aeba fix(x-ui.sh): preserve 2FA on credential reset (#4758)
Go's flag package parses '-resetTwoFactor false' as '-resetTwoFactor=true' with a dangling positional 'false', so two-factor auth was always wiped on username/password reset regardless of the prompt answer. Omit the flag in the preserve branch (default is false) and use '-resetTwoFactor=true' in the disable branch.
2026-06-01 23:36:22 +02:00
MHSanaei 47d9b49666 feat(x-ui.sh): add PostgreSQL management menu
Add a self-contained 'PostgreSQL Management' submenu (main-menu option 27) so the panel can be set up and migrated without re-running the remote install script:

- Install PostgreSQL locally (server + client tools + dedicated xui user/db), ported from install.sh so x-ui.sh stays standalone

- Migrate SQLite to PostgreSQL via 'x-ui migrate-db', then write XUI_DB_TYPE/XUI_DB_DSN to the service env file and restart the panel; client tools are ensured first so in-panel backup/restore works for local and external databases

- Service control: status (clusters + port 5432), start, stop, restart, enable autostart, view log, with auto-detected cluster version
2026-06-01 23:00:35 +02:00
MHSanaei 90a64a1b22 fix(ssl): prompt before setting IP cert path for panel
The IP certificate flow auto-set the panel cert path silently, unlike the
domain and Cloudflare flows which ask first. Add the same
"Would you like to set this certificate for the panel? (y/n)" prompt so
the IP flow is consistent and only configures the panel on confirmation.
2026-05-29 02:52:57 +02:00
Sanaei b71ed1e3ee feat(bash): prompt for PostgreSQL (#4472)
* feat(install): prompt for SQLite vs PostgreSQL during install

* fix(install): write env file to per-distro path and handle pg-install failure

The env file was hardcoded to /etc/default/x-ui, but RHEL/Fedora units read
/etc/sysconfig/x-ui, Arch reads /etc/conf.d/x-ui, and Alpine OpenRC auto-
sources /etc/conf.d/x-ui. PostgreSQL selection was silently dropped on every
distro except Debian. Also initdb on openSUSE (service wouldn't start) and
prompt the operator on local-install failure instead of silently demoting
to SQLite.

* fix(scripts): make x-ui.sh and update.sh PostgreSQL-aware

update.sh ran setting -show and migrate without sourcing the env file, so
PostgreSQL users had migrations applied to the SQLite default and settings
introspection read the wrong DB. Sourcing the per-distro env file at the
start of update_x-ui exports XUI_DB_TYPE/XUI_DB_DSN to all binary calls.

x-ui.sh now shows the active backend in View Current Settings (password
masked) and removes the env file on uninstall so a later reinstall doesn't
inherit a stale DSN.
2026-05-23 19:52:37 +02:00
Aleksandr 5fb36d34c9 fix(fail2ban): escape percent signs in 3x-ipl datepattern (#4328)
* Update DockerEntrypoint.sh

fix(fail2ban): escape percent signs in Docker datepattern

* Update x-ui.sh

fix(fail2ban): escape percent signs in x-ui datepattern
2026-05-13 01:49:09 +02:00
MHSanaei 4c2915586c fix(alpine): restart_xray uses rc-service; OpenRC reload reads pidfile contents
`14. Restart Xray` failed on Alpine with `systemctl: command not found` —
restart_xray was the only service action missing an Alpine branch. While
fixing it, the OpenRC reload action was passing the pidfile path to `kill`
instead of the PID inside it, so `rc-service x-ui reload` would have
failed too.
2026-05-11 09:05:36 +02:00
MHSanaei 887fca86ec fix(fail2ban): escape % in 3x-ipl action date format (#4218)
Fail2ban parses % as variable interpolation in action.d configs, so the
unescaped %Y/%m/%d %H:%M:%S in the date command crashed fail2ban on
startup. Double the %s in the heredoc so the rendered action file
contains %% and fail2ban collapses it back to a literal % when invoking
the shell command.
2026-05-10 19:26:21 +02:00
MHSanaei 72d8ebd269 fix(x-ui.sh): pass silent flag to stop/start during IP SSL setup 2026-05-09 19:59:01 +02:00
MHSanaei 7f703f927e fix(scripts): harden server-IP detection with multi-provider + manual fallback
Try six IPv4 providers in turn, accept only HTTP 200 + IPv4-shaped body,
and prompt the user to enter their IP if every provider fails.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 00:51:28 +02:00
MHSanaei 3349dcbc13 fix(fail2ban): fix banning regression and Docker zero-jail issue
- DockerEntrypoint.sh: create jail.d/filter.d/action.d config files
  before starting fail2ban so Docker containers no longer start with
  0 active jails (fixes #4134)

- x-ui.sh create_iplimit_jails: lower maxretry from 2 to 1 so
  fail2ban bans on the first log entry; with maxretry=2 and the
  partitionLiveIps logic the second occurrence could arrive after the
  32 s findtime window, silently preventing any ban (fixes #4163)

- x-ui.sh: fix datepattern (%%Y -> %Y) so fail2ban parses the Go
  log timestamp correctly instead of looking for a literal %%Y string

- x-ui.sh / DockerEntrypoint.sh: fix date command in actionban /
  actionunban echo (%%Y -> %Y) so the ban log records actual dates

- check_client_ip_job.go: replace log.SetOutput / log.SetFlags on
  the global standard-library logger with a local log.New instance,
  eliminating the dangling closed-file-handle between calls and
  stopping unrelated stdlib log output from polluting 3xipl.log

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-07 13:53:34 +02:00
MHSanaei e19061d513 TLS: Remove ECH Force Query 2026-05-04 13:20:24 +02:00
pwnnex 71ac920436 x-ui.sh: install nftables alongside fail2ban in install_iplimit
On fresh Debian 12+, Ubuntu 24+ and recent RHEL-family minimal images
the fail2ban package ships with `banaction = nftables-multiport` as
the default in /etc/fail2ban/jail.conf but does not pull in the
`nftables` package as a dependency. The first SSH brute-force attempt
hits the default sshd jail and fail2ban logs

    stderr: /bin/sh: 1: nft: not found
    returned 127 -- HINT on 127: "Command not found"

repeatedly, which users mistake for a 3x-ui regression (see the
discussion on #4083). The 3x-ipl jail itself is unaffected — it uses
an iptables-based action configured in create_iplimit_jails — so this
is only stray noise, but noisy enough to look like a real failure on
first install.

Add `nftables` to the package list in every branch of install_iplimit
so new installs end up with a working default sshd jail out of the
box. Existing installs where `nftables` is already present are a
no-op.
2026-04-22 18:50:42 +03:00
Yunheng Liu e02f78ac68 Fix SSL domain setup on reinstall: reuse existing certs and avoid false success/failure logs (#4004)
* perf: replace /dev/urandom | tr with openssl rand to fix CPU spike

* fix: add cron to default package installation and improve SSL certificate handling

* Reworked `--installcert` success criteria, cleanup behavior adjusted.
2026-04-17 12:19:45 +02:00
Yunheng Liu 169b216d7e perf: replace /dev/urandom | tr with openssl rand to fix CPU spike (#3887) 2026-04-01 13:59:48 +02:00
kazan417 38d87230d3 Update x-ui.sh (#3947)
looks like now cert management is option 19
2026-03-18 19:45:45 +01:00
MHSanaei 258b08fff3 Update fail2ban filter regex in x-ui.sh 2026-03-08 11:53:34 +01:00
子寒 842fae18d7 Add 'default' runlevel to x-ui service in Alpine (#3854)
it should be 'default' runlevel when add x-ui service to openrc, default is 'sysinit' runlevel. 'sysinit' runlevel is unnecessary,maybe.
if not, there is an error when call to function 'check_enabled()' as command 'grep default -c' can`t print 'default' runlevel.

check_enabled() {
    if [[ $release == "alpine" ]]; then
        if [[ $(rc-update show | grep -F 'x-ui' | grep default -c) == 1 ]]; then
            return 0
        else
            return 1
        fi
2026-03-04 12:32:01 +01:00
Alireza Ahmadi 2b1d3e7347 [feat] restart xray-core from cli #3825 2026-02-20 00:03:16 +01:00
MHSanaei f4057989f5 Require HTTP 200 from curl before using IP
Replace simple curl+trim checks with a response+http_code parse to ensure the remote URL returns HTTP 200 and a non-empty body before assigning server_ip. Changes applied to install.sh, update.sh and x-ui.sh: use curl -w to append the status code, extract http_code and ip_result, and only set server_ip when http_code == 200 and ip_result is non-empty. This makes the IP discovery more robust against error pages or partial responses while keeping the existing timeout behavior.
2026-02-11 21:32:23 +01:00
Sam Mosleh d5ea8d0f38 Fix default CA by enforcing it everywhere (#3719) 2026-01-30 16:35:24 +01:00
Nebulosa 2f4018bbe5 feat: improve BBR management with sysctl.d and backup support (#3658) 2026-01-18 15:47:02 +01:00
Vorontsov Amadey f273708f6d Feature: Use of username and passwords consisting of several words (#3647) 2026-01-18 15:44:49 +01:00
VolgaIgor a691eaea8d Fixed incorrect filtering for IDN top-level domains (#3666) 2026-01-12 02:53:43 +01:00
MHSanaei f8c9aac97c Add port selection and checks for ACME HTTP-01 listener
Introduces user prompts to select the port for ACME HTTP-01 certificate validation (default 80), checks if the chosen port is available, and provides guidance for port forwarding. Adds is_port_in_use helper to all scripts and improves messaging for certificate issuance and error handling.
2026-01-11 15:28:43 +01:00
Nebulosa 427b7b67d8 Refactor ca-certificate dependency (#3655) 2026-01-09 17:05:55 +01:00
Nebulosa ccf08086ac refactor update geofiles fuctions (#3653) 2026-01-09 17:03:53 +01:00
Sanaei a9770e1da2 ip cert (#3631) 2026-01-05 05:47:15 +01:00
Nebulosa 719ae0e014 Remove wget dependency from everywhere (#3598)
* Remove wget dependency

* Merge branch 'curl_only' of https://github.com/nebulosa2007/3x-ui into nebulosa2007-curl_only

---------

Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-01-03 06:41:40 +01:00
Nebulosa 692a73788a Set variables for packaging purposes (#3600)
* Set Variables for settings
2026-01-03 03:57:19 +01:00
Nebulosa c061337ce7 Set log folder variable to /var/log/3x-ui (#3599)
* Set log folder variable to /var/log/3x-ui

* Set log folder as x-ui and create the log folder

* Create the log folder in install and update scripts
2026-01-02 16:11:32 +01:00
Wyatt 260eedf8c4 fix: add missing is_domain helper function to x-ui.sh (#3612)
The is_domain function was being called in ssl_cert_issue() but was never
defined in x-ui.sh, causing 'Invalid domain format' errors for valid domains.

Added is_ipv4, is_ipv6, is_ip, and is_domain helper functions to match
the definitions in install.sh and update.sh.

Co-authored-by: wyatt <wyatt@Wyatts-MacBook-Air.local>
2025-12-28 16:38:26 +01:00
Sanaei 69ccdba734 Self-signed SSL (#3611) 2025-12-28 00:03:33 +01:00
MHSanaei 0ea8b5352a fix 2025-12-04 00:09:13 +01:00
JieXu e8c509c720 Update for Red Hat base Linux (#3589)
* Update install.sh

* Update update.sh

* Update x-ui.sh

* Update install.sh

* Update update.sh

* Update x-ui.sh

* fix
2025-12-03 21:40:49 +01:00
Evgeny Popov ad659e48cf Update x-ui.sh (#3595)
Add curl & openssl pkgs for acme inside docker container
2025-12-03 14:42:10 +01:00
fgsfds cf38226b5d Add update-all-geofiles key to x-ui.sh (#3586)
* added update-all-geofiles key to x-ui.sh that updated all geofiles

* fix

* text fixes

* typo fix

* cleanup
2025-11-07 19:26:43 +01:00