The image shipped busybox crond but the entrypoint never started it, and the acme.sh crontab entry vanished on every container recreation, so certificates issued via the panel's SSL menu silently expired after 90 days. The entrypoint now re-registers the acme.sh cron job and starts crond when acme.sh is installed, and docker-compose gains an acme volume so renewal state survives recreation.
Closes#5116
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.
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.
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.
- 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>