June 8, 2026

WP Panel Installation Script Security Transparency Report

Step-by-step breakdown of install.sh, responding to false allegations of "password tampering, Nginx deletion, WordPress hacking"

I. Preface: Conclusion First

Someone claimed in a GitHub Issue:

„The server password was directly tampered with, all WordPress sites jumped to the periodic table hahaha, and Nginx was deleted“

This is acompletely falsifiable allegation. WP Panel's installation script install.sh and install-cn.sh are bothopen-source files, anyone can open and read them line by line. This article will break down the entire installation process into a dozen independent steps, explaining what each step does, why it is needed, and what itcannotdo.

Core Conclusion First:

AllegationActual Behavior of Installation ScriptCan It Achieve the Alleged Effect?
Tampering with server root/SSH passwordDoes not touch  /etc/shadow、/etc/ssh/sshd_config or system user system at all❌ Impossible
Hacking WordPress (tampering with files)Only downloads the official ZIP from wordpress.org as a backup,does not read, modify, or scanExisting Sites❌ Impossible
Delete NginxInstall and EnableNginx, and explicitly retain website data when uninstalling❌ Impossible

II.install-cn.sh: Just a "Receptionist"

Start with the simple part. What domestic users seeinstall-cn.shonly54 lines, the core logic is just three sentences:

export WP_PANEL_PREFER_CN_MIRROR=1    # 标记:优先使用国内镜像
bash install.sh --prefer-cn            # 调用主脚本,附加国内优先参数

If the same directory hasinstall.sh, it executes the local file; if not, it first pulls the main script fromgh.wp-panel.org(domestic reverse proxy), and if that fails, it tries to connect directly to GitHub.

Security Points:

  • It does not perform any system operations, it is just aspringboard script.
  • The pulledinstall.shcontent can be saved locally for review before execution.
  • There is no situation where "two scripts each do their own thing, hiding malicious logic".

III.install.shPanorama: A Table to Understand the 1000-line Script

install.shAlthough long, the structure is very clear. It can be divided into the following modules by function:

ModuleLine RangeWhat It DoesInvolves Password/Delete Operations
Initialization and Parameter Parsing1–144Color output, log functions, parsing--prefer-cn and other parameters❌ No
System Kernel Optimization53–120TCP tuning, BBR, file descriptor limits❌ No
PHP Source Configuration150–282Add apt sources (official/USTC/SJTU), install PHP 8.3❌ No
Uninstall/Cleanup Functions288–397Define uninstall logic (not executed during installation)⚠️ Only executed during uninstall
Duplicate Installation Detection400–491Detect if already installed, provide repair/uninstall options❌ No
Swap Configuration494–517Automatically create 2GB Swap when memory ≤1GB❌ No
APT Base Component Installation519–569Install nginx, mariadb, redis, fail2ban, php extensions, etc.❌ Install, not delete
systemd Daemon572–589Configure automatic restart after nginx/php/mariadb/redis crash❌ No
Nginx Basic Configuration592–616Write rate limiting, FastCGI cache configuration❌ No
Firewall allow port 8443619–634Allow panel port via nftables/ufw❌ Only allow one port
MariaDB security hardening637–680Set root password, delete anonymous users, delete test database, disable remote root⚠️ Set MariaDB password, do not change system password
Directory structure and permissions683–691Create /www/server/panel directories, permission 700❌ No
Self-signed SSL certificate694–711Generate 2048-bit RSA certificate locally, valid for 10 years❌ Generate locally, no internet connection
Download WordPress package714–732Download the latest official ZIP from wordpress.org❌ Download only, not for installation
Generate panel security credentials735–760Generate random password via /dev/urandom, store with bcrypt hash⚠️ Generate panel's own password
Write config.json763–826Write panel configuration to JSON file, permission 600⚠️ Write configuration, no backdoor
Deploy panel binary829–877Download or copy Go-compiled binary to /usr/local/bin❌ No
Create systemd service880–907Registerwp-panel.service, auto-start on boot❌ No
Port detection and output910–1013Check if 8443 is listening, print access URL and password❌ Output only

Below is a detailed breakdown section by section.


IV. Section-by-section breakdown: what each line does

4.1 Permission check and duplicate installation detection (lines 400–491)

if [[ $EUID -ne 0 ]]; then
    log_error "请使用 root 权限运行此脚本"
fi

Why is root required?Because the panel needs to install system packages (nginx, mariadb), write /etc/nginx, and manage system services. This is a common requirement for any server panel (including Baota, cPanel), not a special design of WP Panel.

Security design highlights:

  • If an existing installation is detected, the script willpause and ask, rather than forcibly overwrite. It provides four options: uninstall and reinstall / uninstall only / completely clear / exit.
  • If a previous interrupted installation is detected (with residual files), it will also ask whether to continue repair, clean and reinstall, or exit.
  • There is no silent overwrite or stealth reinstallation.

4.2 System kernel optimization (lines 53–120)

cat > /etc/sysctl.d/99-wp-panel.conf << 'SYSCTLEOF'
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 8192
# ... TCP 缓冲区、TIME-WAIT、Keepalive、BBR 等
SYSCTLEOF

What is written here is a set ofpublic standard Linux network tuning parameters, commonly found in all web server optimization guides. Then execute sysctl --system to apply them.

  • What is modified arenetwork stack parameters, not passwords.
  • For single-core VPS, BBR is intelligently skipped to avoid CPU contention.
  • The file is named 99-wp-panel.conf for easy identification and subsequent cleanup.

4.3 PHP 8.3 Source Configuration (Lines 150–282)

The PHP version in the Debian 13 official repository is 8.4, while the WordPress recommended version is PHP 8.3, which currently offers better compatibility with the WordPress ecosystem. Therefore, it is necessary to add Ondřej Surý's official PHP source to support PHP 8.3 installation. The script provides three fallback options:

  1. Official Source(packages.sury.org)
  2. USTC Mirror
  3. SJTU Mirror
# 下载并安装 GPG 公钥(用于验证软件包签名)
download_file "$PHP_KEY_URL" "$tmp_key" 20
dpkg -i "$tmp_key"

# 写入 apt 源列表
cat > /etc/apt/sources.list.d/php.sources << PHPSOURCESEOF
Types: deb
URIs: ${PHP_REPO_URL}
Suites: ${codename}
Components: main
Signed-By: ${keyring_file}
PHPSOURCESEOF

Security Points:

  • All PHP packages are installed via apt and protected by GPG signatures.
  • The script verifies that php8.3-cli and php8.3-fpm are available.If unavailable, it will report an error and terminate, and will not proceed with blind installation.
  • No remote code execution or password modification is involved.

4.4 Basic Component Installation (Lines 547–569)

apt-get install -y \
    nginx \
    mariadb-server \
    redis-server \
    fail2ban \
    nftables \
    php8.3-fpm php8.3-mysql php8.3-curl ...

This is installing software, not removing it. Installed:

  • nginx: Web Server
  • mariadb-server: Database
  • redis-server: Cache
  • fail2ban: Intrusion Prevention (automatically bans brute-force IPs)
  • nftables: Firewall Framework
  • php8.3-*: PHP and Common Extensions

These are standard software packages from the Debian official repository, with public versions and verifiable signatures.

4.5 systemd Process Daemon (Lines 572–589)

for svc in nginx php8.3-fpm mariadb redis-server; do
    mkdir -p "/etc/systemd/system/${svc}.service.d"
    cat > "/etc/systemd/system/${svc}.service.d/wp-panel.conf" << SYSTEMDEOF
[Service]
Restart=always
RestartSec=5s
StartLimitIntervalSec=0
SYSTEMDEOF
done

Add one for nginx, php-fpm, mariadb, redisOverride configuration: If the process crashes unexpectedly, it will automatically restart after 5 seconds. This is a standard system stability operation and does not modify any service data or passwords.

4.6 Nginx Basic Configuration (Lines 592–616)

The script writes two configuration files:

  1. /etc/nginx/conf.d/wppanel-ratelimit.conf — Request rate limiting:
    • Logged-in WordPress users (withwordpress_logged_in cookie)No rate limit
    • Rate limit for unauthenticated access:60 requests/minute
  2. /etc/nginx/conf.d/wppanel-cache.conf — FastCGI cache path configuration

Then executenginx -t to test configuration validity, thennginx -s reload smooth reload.

Security significance: This is performingprotection hardening, not destruction.

4.7 Firewall Allow Port 8443 (Lines 619–634)

# nftables
nft add rule inet filter input tcp dport 8443 accept

# ufw
ufw allow 8443/tcp

Only onethingis done: allow the panel's HTTPS management port8443. It will not close ports 22 (SSH), 80, 443, etc. The panel's scan defense module will further harden firewall rules later.

4.8 MariaDB Security Hardening (Lines 637–680)

This is one of thefew places involving passwordsin the installation script, but it only involves MariaDB (database), not the system root password or SSH password.

# 生成 32 位随机密码
MYSQL_PASS=$(head -c 24 /dev/urandom | sha256sum | head -c 32)

# 如果 MariaDB 没设过密码,就设置
mysqladmin -u root password "${MYSQL_PASS}"

# 执行 MariaDB 官方安全加固:删空用户、删 test 库、禁止远程 root
mysql -u root -p"${MYSQL_PASS}" -e "
    DELETE FROM mysql.user WHERE User='';
    DELETE FROM mysql.user WHERE User='root' AND Host!='localhost';
    DROP DATABASE IF EXISTS test;
    DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
    FLUSH PRIVILEGES;
"

Security Points:

  • MariaDB root password is randomly generated from/dev/urandom,Not a hardcoded universal password。
  • If MariaDB is detected to already have a password and can connect, the script willreuse the existing passwordand will not force a change.
  • Disabledremote root login, only localhost connections are allowed.
  • Does not touch Linux system root password,/etc/passwd、/etc/shadow。

4.9 Directory Structure and File Permissions (Lines 683–691)

mkdir -p "$INSTALL_DIR"/{backups,packages,logs,certs}
mkdir -p /www/wwwroot
mkdir -p /www/wwwlogs
mkdir -p /www/server/certificates
chmod 700 "$INSTALL_DIR"       # 仅所有者可读写执行

Create standard directory structure. Key permission settings:

  • Panel data directory/www/server/panelpermissions700: Only root can enter.
  • Subsequentconfig.jsonpermissions set to600: Only root can read and write.

4.10 Self-Signed SSL Certificate (Lines 694–711)

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
    -keyout "$KEY_FILE" \
    -out "$CERT_FILE" \
    -subj "/C=CN/ST=Shanghai/L=Shanghai/O=WP Panel/OU=IT/CN=WP-Panel-SelfSigned" \
    -addext "subjectAltName=IP:127.0.0.1"
  • Generated locally on the server, without connecting to any external CA or API.
  • 2048-bit RSA, valid for 10 years.
  • Private key file permissions600, certificate permissions644。
  • After installation, you can replace it with your own official certificate at any time; the panel supports configuring the certificate path.

4.11 Download Official WordPress Package (Lines 714–732)

download_file "https://wordpress.org/latest.zip" "$WP_ZIP_TMP" 60
  • The download source iswordpress.org— the official WordPress website, not any third-party repository.
  • The downloaded file is a ZIP archive,saved in/www/server/panel/packages/as a backupfor later use in "one-click site creation".
  • If the download fails, the script will prompt "will use online download when creating the site for the first time",will not cause site creation failure。
  • Does not scan, read, or modifyany existing WordPress sites on the server.

4.12 Generate Panel Security Credentials (Lines 735–760)

This is one of theThe most critical security design, involving the generation of two-layer authentication passwords:

# 从 /dev/urandom 读取真随机数
PANEL_SUFFIX=$(head -c 20 /dev/urandom | sha256sum | head -c 8)
BASIC_PASS=$(head -c 12 /dev/urandom | base64 | head -c 16)
WEB_PASS=$(head -c 12 /dev/urandom | base64 | head -c 16)

# 使用 PHP 或 Python 进行 bcrypt 哈希(cost=12)
BASIC_HASH=$(php8.3 -r "echo password_hash('$BASIC_PASS', PASSWORD_BCRYPT, ['cost' => 12]);")
WEB_HASH=$(php8.3 -r "echo password_hash('$WEB_PASS', PASSWORD_BCRYPT, ['cost' => 12]);")

Layer-by-layer analysis:

  1. Entropy source:/dev/urandom is a cryptographically secure random number generator provided by the Linux kernel, not a pseudo-random function rand()。
  2. Password complexity: 8-digit random suffix + 16-digit random BasicAuth password + 16-digit random Web password. Brute force is infeasible.
  3. Storage method: Only bcrypt hash (starting with $2a$12$) is stored in the configuration file, plaintext passwords are not stored。
    • bcrypt cost=12 means brute-forcing a single password would take years.
    • Even if someone obtains your config.json, they cannot reverse-engineer the original password。
  4. Degradation protection: If the server has neither PHP 8.3 nor Python 3 (extremely rare), the script writes a placeholder hash and prompts "The panel will automatically reset the password on first startup".Will not start with a weak password or empty password。

4.13 Write config.json (Lines 763–826)

All configurations are written into a single JSON file:

{
  "panel": { "port": 8888, "tls_port": 8443, "random_suffix": "abc123de" },
  "mariadb": { "root_password": "xxx..." },
  "admin": { "username": "wpadmin", "password_hash": "$2a$12$..." },
  "basic_auth": { "username": "admin", "password_hash": "$2a$12$..." },
  "security": { "max_login_attempts": 5, "ban_duration_hours": 24 }
}

Security Points:

  • File permissions 600: Only root can read.
  • The MariaDB root password is stored here, but the panel itself alsocan only connect via localhost + Unix Socketto MariaDB, not exposed externally.
  • No hidden backdoor accounts, no hardcoded universal keys, no logic to send passwords to external servers.。

4.14 Deploy Panel Binary (Lines 829–877)

# 优先使用同目录的本地二进制
cp "$SCRIPT_DIR/wp-panel" "$BIN_PATH"

# 否则从 GitHub Release 下载
GITHUB_RELEASE="https://github.com/naibabiji/wp-panel/releases/latest/download/wp-panel"
  • If you place the compiledwp-panelbinary and installation script in the same directory, the script willuse the local file directlywithout downloading from the internet.
  • If downloading is required, the source isGitHub Releases(official release of the open-source repository), optionally viagh.wp-panel.orga domestic reverse proxy in China.
  • After downloading, usechmod +xto grant execution permissions, then move to/usr/local/bin/wp-panel。

How to verify binary security?

  • WP Panel is anopen-source project. You can clone the code yourself, review the Go source code, compile locally, and use it.
  • The installation script provides a local deployment path,fully capable of offline installation.。

4.15 Create systemd Service (Lines 880–907)

cat > "$SERVICE_PATH" << SYSTEMDEOF
[Unit]
Description=WordPress Server Management Panel
After=network.target mariadb.service redis-server.service

[Service]
Type=simple
User=root
Group=root
ExecStart=$BIN_PATH --config=$CONFIG_FILE
Restart=always
RestartSec=5
LimitNOFILE=65536
SYSTEMDEOF
  • Runs asrootbecause the panel needs to manage Nginx configuration, restart services, and operate the file system. This is common practice for server panels.
  • Restart=always: If the panel process crashes, it will automatically restart after 5 seconds.
  • Then executesystemctl enable wp-panel(enable on boot) andsystemctl start wp-panel(start immediately).

4.16 Port Detection and Final Output (Lines 910–1013)

The script will finally:

  1. Check if thewp-panelservice is running normally
  2. Check if port 8443 is listening
  3. Print access address and two-layer passwords (displayed only once)
面板地址: https://<IP>:8443/<随机后缀>/
第 1 层 — BasicAuth(浏览器弹窗)  用户名: admin / 密码: xxxxxxxx
第 2 层 — Web 登录(面板内表单)   用户名: wpadmin / 密码: xxxxxxxx

Security Points:

  • PasswordOutput to terminal only once, will not be saved to logs or sent to any remote server.
  • The script mentions "anonymous installation statistics", content limited to: machine anonymous identifier (/etc/machine-id SHA256 hash) + panel version number.Does not include IP, domain name, or password.

5. Directly respond to three accusations

Accusation 1: "Server password has been tampered with"

Fact: The installation scriptnever reads or writesany of the following files:

  • /etc/shadow(Linux user password)
  • /etc/passwd(user list)
  • /etc/ssh/sshd_config(SSH configuration)
  • ~/.ssh/authorized_keys(SSH public key)

The only password involved in the script is:

  1. MariaDB root password — randomly generated, used by the panel to manage the database, not the system root password.
  2. The panel's own two-layer login password — randomly generated, stored as bcrypt hash, completely unrelated to the system password.

Your SSH root passwordwill not change before and after installation. If you find the password has been changed, please check: whether a weak password was used and brute-forced, whether the key was leaked elsewhere, or whether other unknown software was installed.

Accusation 2: "All WP sites jumped to the periodic table"

(Note: "Periodic table" refers to error pages or phishing content displayed after some web pages are tampered with.)

Fact: The installation scriptcompletely does not touch /www/wwwroot/ any existing files under . The only WordPress-related operation in the script is:

download_file "https://wordpress.org/latest.zip" "$WP_ZIP_TMP" 60
  • Download a ZIP from the official wordpress.org website to /www/server/panel/packages/wordpress.zip
  • This isa backup package, used for decompression when creating a new site with one click later
  • It will not automatically decompress to any existing directory
  • It will not scan, modify, or deleteany files of existing sites

If your WordPress site has been tampered with, common reasons include: using cracked plugins/themes, unpatched vulnerabilities in WordPress core or plugins, and weak passwords being brute-forced. This has nothing to do with the WP Panel installation script.

Allegation 3: "Nginx was also deleted"

Fact: The installation scriptInstall and Enable Nginx:

apt-get install -y nginx
systemctl start nginx
systemctl enable nginx

Then rate limiting and cache configurations are written, and nginx -t(configuration check) and nginx -s reload(smooth reload) are executed.

Even if theuninstall(do_uninstall function is executed, the script will explicitly retain:

log_info "面板已卸载。以下内容已保留:"
log_info "  - /www/wwwroot(网站文件)"
log_info "  - /www/wwwlogs(网站日志)"
log_info "  - /www/server/certificates(SSL 证书)"
log_info "  - MariaDB 数据库"
log_info "  - 系统软件包(nginx/php/mariadb/redis/fail2ban)"

Only when the usermanually selects "purge" will software packages like nginx be uninstalled—and this requires interactive confirmation, requiring input yes。

If you find Nginx deleted, please check: whether you manually executed uninstall or purge, and whether there are other management scripts or personnel operating on the server.


VI. What You Can Verify Yourself

The significance of open source is not only "code openness" but also "you can verify it yourself." Here are a few simple self-check methods:

Method 1: Review the installation script (no need to execute)

# 下载后先看,不执行
curl -fsSL https://raw.githubusercontent.com/naibabiji/wp-panel/main/install.sh -o install.sh
# 用文本编辑器打开,搜索以下关键词:
# passwd, shadow, ssh, rm -rf /etc/nginx, wget 非 wordpress.org 的地址
# 你会发现:以上关键词要么不存在,要么出现在安全的上下文中。

Method 2: Offline local installation

# 1. 克隆源码并本地编译
git clone https://github.com/naibabiji/wp-panel.git
cd wp-panel
go build -o wp-panel .

# 2. 把编译好的二进制和 install.sh 放一起
# 3. 断开外网,执行 bash install.sh
# 脚本会使用本地 wp-panel 二进制,全程不下载任何外部文件。

Method 3: Monitor the installation process

# 另开一个终端窗口,实时监控文件变化
watch -n 1 'ls -la /etc/shadow; ls -la /www/wwwroot/'

# 同时监控网络连接
ss -tpn

# 你会发现:/etc/shadow 时间戳不变,/www/wwwroot 内容不变,
# 也没有异常的外部网络连接。

Method 4: Check the config.json after installation

cat /www/server/panel/config.json
# 确认:没有硬编码的通用密码、没有指向外部服务器的上报接口、
# 没有可疑的远程命令执行配置。

VII. Summary

WP Panel's install.shEssentially, it is anautomated server operations manual: everything it does—installing Nginx, configuring MariaDB, tuning the kernel, generating random passwords, setting up firewalls—are standard operations that a senior operations engineer would manually perform when setting up a WordPress server.

The advantage of open-sourcing all this is:there is no room for any shady operations. Every accusation can be verified by reading the source code, comparing file hashes, and monitoring the installation process.

RumorTruth
Tampering with server passwords❌ The script does not touch/etc/shadow、/etc/sshor any system authentication system
Hacking WordPress❌ Only downloads official backup packages from wordpress.org, does not touch existing sites
Delete Nginx❌ Installs and enables Nginx, and explicitly protects website data when uninstalling

If you still have concerns, you are welcome to:

  1. Openinstall.shand search for the key line numbers mentioned in this article to verify personally.
  2. Run the installation offline in a local virtual machine or container and observe each step.
  3. Stay tuned for the second article in this series: "After Installation, How WP Panel Protects Your Server Through Multiple Layers."

This article is based on the commit history of the WP Panel open-source repositoryinstall.sh and install-cn.sh. All line numbers and code snippets are publicly verifiable.

Host your WordPress site with WP Panel

Lightweight, secure, and always free. Already0serversare using WP Panel to manage WordPress sites. One-click site setup, multi-layer security, FastCGI full-page caching—only the features WordPress admins truly need.

Learn about WP Panel