Skip to main content

2 posts tagged with "WHM"

View all tags

AlmaLinux 10 + cPanel: New-Server Pitfalls from TFA to DNS

Β· 9 min read

cPanel 138 on AlmaLinux 10 installs smoothly and the panel opens β€” the real traps cluster in the close-out phase: security hardening, panel asset repairs, and domain mounting, each with counter-intuitive behavior waiting.

Encountered this while hardening a production cPanel server for a client β€” all three failure classes showed up during the close-out of the same AlmaLinux 10 machine. This post breaks them down by scenario.

TL;DR​

Three scenarios, three "looks right, does nothing" traps: TFA configured but login never asks for a code β€” the policy-wide switch is off, so per-user configuration is inert; frontend assets that look "missing" must be verified against the official manifest first β€” some never existed on that cpanelsync tree, and some are skipped because a polluted digest cache lets upcp fake success; Park domains rejected by the NS ownership check β€” when DNS lives on a cloud provider, the userdata include injecting a ServerAlias is the supported path.

Scenario one: "missing" WHM frontend libraries β€” verify before fixing​

While triaging panel oddities, /usr/local/cpanel/base/libraries turns out not to exist β€” do not rush to repair a defect. Upstream cPanel 138 simply has no such path. The real homes of the shared frontend libraries:

  • base/frontend/jupiter/libraries/ β€” a symlink farm pointing at ../../../../3rdparty/share/<lib>, distributed by jupiter's own cpanelsync tree (sortablejs, ui-fonts, fontawesome, cldr)
  • base/unprotected/libraries/ β€” same mechanism, hosting legacy libraries

To decide whether a file is genuinely missing, pull the official manifest instead of trusting one path:

curl -sO http://httpupdate.cpanel.net/cpanelsync/138/<tree>/.cpanelsync.bz2
bzcat .cpanelsync.bz2 | grep <target-path>
# Entry format: d===./path===755 (directory)
# l===./link-name===777===target (symlink)

Second rule: base/ is not entirely cpanelsync-distributed. The v138 cpanel-* RPMs (bootstrap5, ace-editor, sortablejs and friends) install libraries directly into /usr/local/cpanel/3rdparty/share/<lib>/<version>, and cpanelsync only lays the symlinks into the theme trees. Verify the RPM side with rpm -V <package> and the cpanelsync side with the manifest β€” both.

If files are confirmed missing but upcp --sync reports success without restoring them, suspect the digest caches: /usr/local/cpanel/.cpanelsync.digest and the per-theme-tree digests. When an interrupted update pollutes them, --sync skips the missing files and still exits 0. Delete the affected digest and run upcp --force for a full reconciliation.

A successful --sync is not proof of complete files β€” judge by actual page loading: drive a headless Chromium to collect console errors and requests at 4xx or above; that is more honest than any exit code.

Two more operational traps on hardened machines:

  • For root access, prefer the cloud assistant (aliyun ecs RunCommand) β€” out-of-band, SSH-free, audited by default. Pass the instance via --InstanceId.1 and feed CommandContent the raw script, not base64. Any temporary sudoers grant needs a self-cleaning /etc/cron.d entry; after cleanup, sudo -n whoami must answer a password is required to confirm the revoke took.
  • Bulk-probing cpsrvd triggers rate limiting: a shell loop of individual curl calls degrades to all-000 responses after a few dozen requests, which reads like a mass 404. A single curl process fetching multiple URLs over keepalive behaves normally. And pkill -f matches your own bash -c command line β€” use the [] character-class trick or a plain PID.

A 200 from a WHM page does not mean an authenticated session β€” the login page returns 200 too. Assert on the <title> or a body fingerprint (the Two-Factor Authentication page's title, for instance).

Scenario two: the hardening chain of traps​

The goal: no root over SSH, TFA on the panel, a minimal sudo whitelist. Every step hides a precondition.

The TFA policy switch is a precondition. After twofactorauth_set_tfa_config writes a user's secret, the login form may never show the code step β€” twofactorauth_policy_status must report is_enabled = 1 (enabled via twofactorauth_enable_policy), otherwise WHM validates password only. Verified in a browser: before the policy, the password logs straight in; after, an "Enter the security code" page appears.

Two CLI details for TFA: the token parameter of twofactorauth_set_tfa_config is tfa_token, not code (passing code silently fails with "security code is invalid"); and the TOTP must be computed against the server clock β€” a 23-second skew crosses the window and the locally computed code is always rejected. The secret lands in /var/cpanel/authn/twofactor_auth/tfa_userdata.json.

The supported API path without root SSH is session plus cpsess prefix. After create_user_session and a curl cookie-jar login, API calls must carry the cpsess path: https://host:2087/cpsessNNN/json-api/<function>; hitting /json-api/ directly answers "Token denied". The service parameter of create_user_session is whostmgrd (with the d) β€” whostmgr and cpanel are rejected; only cpaneld, webmaild, whostmgrd are valid.

sudoers matches the entire command sequence exactly. Even the position of --output=json and the argument order are locked; any edit to the caller's command silently degrades to password authentication β€” which, with the opsuser password locked, is an outright refusal. Changing the command means changing the matching sudoers file.

Three whmapi1 details: the real path is /usr/local/cpanel/bin/whmapi1 (prefer it over the symlink); the default output is YAML, so --output=json before jq; and sethostname takes hostname, not domain β€” domain= silently passes an empty value and runs to no effect, and cPanel refuses whm./cpanel./webmail.-prefixed hostnames. On machines with the DNS role disabled, the trailing dnsadmin socket "Connection refused" is expected and harmless; after a rename, create_user_session URLs follow the new hostname automatically, and AutoSSL reissues the cpsrvd certificate within about a minute.

sshd drop-ins: first value wins. The Include sshd_config.d/*.conf in AlmaLinux's main sshd_config sits at the top, so drop-ins parse before the main body and sshd keeps the first occurrence β€” that is how a drop-in overrides the main file; among drop-ins, filename sort order decides (000- sorts before 00-). After any change: sshd -t, then sshd -T | grep -E 'permitrootlogin|passwordauthentication|allowusers' to confirm the effective values before reloading.

Host Access Control does nothing here. cPanel 138 + AlmaLinux 10 ships a cpsrvd that does not link libwrap (tcp_wrappers is gone from RHEL-line distributions); rules written to /etc/hosts.allow and a cpsrvd restart changed nothing in testing. Layer-3/4 allowlists belong in firewalld rich rules; leaving hosts.allow in place is harmless β€” it activates automatically if libwrap ever returns.

Three AlmaLinux 10 verification blind spots: last is always empty β€” systemd 256 dropped wtmp, login records live only in the journal (journalctl -u sshd as root); opsuser cannot execute /usr/bin/su (denied at the exec layer), so the root password can only be verified through a WHM form or the console; and /etc/ssh/sshd_config.d/, /etc/cron.d/* (mode 600), /var/cpanel/authn/ are unreadable to opsuser β€” hardening audits must run in WHM Terminal or VNC.

Scenario three: domain NS lives elsewhere, alias mounting rejected​

uapi Park park domain=test.xxx is refused: the domain's nameservers (hosted on a cloud DNS) are "not associated with this server" β€” cPanel validates that the domain's authoritative NS points at the machine, and in common China-hosting setups DNS lives on the provider, so this check can never pass.

The supported path, without touching the domain's NS, is a userdata include injecting a ServerAlias:

# one for http, one for https
/etc/apache2/conf.d/userdata/std/2_4/<user>/<domain>/alias.conf
/etc/apache2/conf.d/userdata/sssl/2_4/<user>/<domain>/alias.conf

(The ssl-side directory name varies by version β€” sssl/2_4 here, sometimes written ssl/2_4; trust the uncommented include line in httpd.conf.) Contents, one line:

ServerAlias test.xxx

Then /scripts/rebuildhttpdconf. A wrong directory name leaves the include line commented out β€” silently inert. Verify by inspecting whether the Include "...userdata..." lines in httpd.conf carry a comment prefix.

Confirm routing with httpd -S and by watching which vhost's domlog receives the requests.

One linkage note: domains attached via ServerAlias are not managed by AutoSSL, so no certificate is issued for them automatically β€” see cPanel AutoSSL Not Issuing? The Exclusion List and vhost Paths.

Warnings

  • Hardening steps have order dependencies: enable the TFA policy switch before configuring user secrets; confirm the sudoers whitelist command works before disabling root SSH β€” inverted order locks you out.
  • Never declare a file missing without diffing the official manifest and checking the RPM side β€” two distribution channels, both must be cleared.
  • Configuration file content is not runtime behavior: after drop-in edits, read the effective values from sshd -T.

Frequently Asked Questions​

Why is ssh PermitRootLogin no not working?​

Because sshd takes the first value it parses. On AlmaLinux the Include directive sits at the top of the main sshd_config, so drop-in files under sshd_config.d/ are parsed before the main body β€” a drop-in overrides the main file, and among drop-ins the lexicographically first filename wins. Always verify with sshd -t followed by sshd -T | grep permitrootlogin to see the effective value before reloading.

How does WHM two-factor authentication work?​

Two layers must both be active: the server-wide policy switch (twofactorauth_enable_policy) and the per-user TOTP secret (twofactorauth_set_tfa_config). With the policy off, WHM keeps validating password only even when a secret exists. When configuring via CLI, the token parameter is tfa_token β€” not code β€” and the TOTP must be computed against the server clock; a skew of just 20-odd seconds crosses the window and gets rejected.

Why is my Apache ServerAlias not working on cPanel?​

The userdata include is most likely inert: a wrong directory name leaves the Include line commented out in httpd.conf, and the alias never loads. ServerAlias files belong in /etc/apache2/conf.d/userdata/std/2_4/<user>/<domain>/ and the ssl counterpart, followed by /scripts/rebuildhttpdconf. Verify by checking whether the Include userdata lines in httpd.conf carry a comment prefix, then confirm with httpd -S and the vhost domlogs.

CCLEE

Independent developer, 24 years in e-commerce, focused on grounding AI in real business scenarios.

Work with me

cPanel Install Says Complete but MariaDB Is Missing

Β· 7 min read

You provision a fresh server with cPanel, the installer reports "complete" β€” and then account creation, database setup, or the sites themselves fail one after another. These failures share a trait: the place that errors is not the place that broke.

Encountered this while building a compliance hosting environment on Aliyun China for a client β€” all three failure classes showed up on the same machine during provisioning, and each had to be cleared before the site migration could start.

TL;DR​

Fresh cPanel installs fail in three recurring ways, all wearing a mask of success: the installer reports complete while MariaDB never installed (a failed stage does not roll back); account creation is blocked by a chain of missing keys when /etc/wwwacct.conf is empty; and from China, httpupdate.cpanel.net crawls at roughly 50 KB/s β€” slow enough to cause the first failure in the first place. Accept a provisioning only after three checks: MariaDB RPMs present, mysql service active, client connects.

Scenario one: install reports complete, MariaDB is missing​

WordPress throws a Database Error, the mysql binary does not exist on the server, and systemctl is-active mysql returns inactive β€” while the cPanel installer reported success and the panel opens fine.

The install log's tail hides the real event:

(FATAL): The background process "SQL Databases and dependent apps" failed ... error number 127

The MariaDB RPM transaction in the SQL stage failed on download, but every later stage ran and finished anyway, and the final screen still said complete. Installation "success" does not mean the components are present β€” the installer neither rolls back nor blocks on a stage failure.

Fix order:

  1. Confirm the gap: rpm -q MariaDB-server β€” most likely not installed
  2. Install the full RPM set: MariaDB-server, MariaDB-client, MariaDB-devel, MariaDB-shared, MariaDB-common β€” every one of them
  3. RPMs alone are not enough β€” /usr/local/cpanel/scripts/securemysql does not make restore tools connect; restorepkg fails with Missing: admin_mysql_password. Create /root/.my.cnf with a client-section password and run SET PASSWORD for root@localhost

Never trust the installer's exit status for acceptance. Run three checks:

rpm -qa | grep -i maria
systemctl is-active mysql
mysql -N -e "select version()"

All three green, the SQL stage is genuinely done.

Scenario two: first account creation hits a chain of missing keys​

restorepkg or manual account creation gets blocked once per run, in this order: Please setup a nameserver β†’ Missing HOMEDIR β†’ Missing DEFMOD β†’ Missing LOGSTYLE β†’ Missing SCRIPTALIAS.

The cause is direct: on a fresh WHM that never ran the Basic Setup wizard, /etc/wwwacct.conf is an empty file, and account creation validates it hard. The trap is the error mechanism β€” each run reports exactly one missing key, so patching one at a time costs five or more rounds.

Write the full standard key set in one shot:

cat > /etc/wwwacct.conf <<'EOF'
ADDR 172.28.100.10
CLUSTERED_DNS disabled
DEFMOD default
ETHDEV eth0
FTPHOMEDIR 0
HOMEDIR /home
HOMEMATCH home
LANG english
LOGSTYLE semicolon
MINUID 500
NS ns1.example-ns.com
NS2 ns2.example-ns.com
SCRIPT x3
SCRIPT x3parked
SCRIPT x3addon
SCRIPTALIAS y
EOF

Three details:

  • ADDR takes the private IP, not the public one β€” on NAT architectures the public IP never lands on the NIC, and the consequences of binding it are covered in cPanel Sites Hit the Default Page? The Aliyun NAT vhost Trap
  • whmapi1 set_nameserver takes the singular parameter nameserver (values: bind/powerdns/disabled), unlike the plural fields from get_nameserver_config; and the NS validation reads NS/NS2 from wwwacct.conf, not ns1/ns2 from cpanel.config
  • When real DNS lives on cloud DNS, the NS values are nominal placeholders β€” pair with CLUSTERED_DNS disabled

For failed transfers, the details live in the JSON of /var/cpanel/transfer_sessions/<session>/master.log (search failure); note that view_transfer itself tails and blocks β€” do not get stuck in it during triage.

Scenario three: cpanel.net downloads at 50 KB/s from China​

Scenario one's RPM download failure usually traces back here: from an Aliyun Shanghai ECS, every mirror IP of httpupdate.cpanel.net measured about 50 KB/s (the international-site route in the same region was just as slow, ruling out any proxy transit benefit); the same source over a residential connection measured 1.8-6 MB/s.

The acceleration pattern lets the server borrow a faster line: a reverse dynamic SOCKS tunnel from a local machine, with proxychains-ng wrapping the installer on the server:

# Local machine: open a remote dynamic SOCKS port
ssh -N -R 1080 root@<server-ip>

# Server: with proxychains-ng installed, run the installer through the tunnel
proxychains4 -q sh latest

Measured lift: from 50 KB/s to 708 KB/s, about 14x. Three traps to avoid:

  • The proxychains config must exempt localnet ranges (10/8, 172.16/12, 100.64/10, etc.) and drop proxy_dns β€” otherwise Aliyun internal mirror domains (mirrors.cloud.aliyuncs.com) get pushed into the tunnel and fail outright
  • tinyproxy is incompatible with httpupdate.cpanel.net β€” it returns 404 reliably; do not use it as the tunnel exit
  • Never clean up the installer with pkill -f "sh latest" β€” the pattern matches your own ssh session's command line and kills your connection (the source of exit code 255); kill by PID instead

Tear it down when done: the tunnel lives exactly as long as the local ssh process, and the server keeps no proxy configuration β€” uninstall proxychains-ng and delete its config after the install. If an interrupted install already left RPMs missing, cPanel's self-repair is /usr/local/cpanel/scripts/sysup β€” in our case a missing splitlogs binary had left httpd unable to start, fixed by sysup plus a manual RPM install.

Warnings

  • The three failure classes chain together: slow downloads break RPM transactions, the installer skips rollback and reports success, and the missing components explode later at account creation or site setup. Debug from the network layer up β€” do not stop at the layer that surfaced the error.
  • wwwacct.conf reports one missing key per run; writing half the file and retesting only burns rounds. Write it complete.
  • Tunnel acceleration is a temporary tool β€” no resident proxy configuration stays on the server; after the RPMs land, run sysup once for a full reconciliation.

Frequently Asked Questions​

How do I install MariaDB on a cPanel server?​

Install the full RPM set β€” MariaDB-server, MariaDB-client, MariaDB-devel, MariaDB-shared and MariaDB-common; a partial set passes rpm checks but breaks later steps. Then create /root/.my.cnf with a client-section password and run SET PASSWORD for root@localhost, otherwise restore tools fail with Missing: admin_mysql_password. Verify with rpm -qa | grep -i maria, systemctl is-active mysql, and mysql -N -e "select version()".

Why does a cPanel install or update fail without an error?​

Because the installer does not roll back. When one background stage fails (SQL Databases and dependent apps, error number 127 in our case), the remaining stages still run and the installer still reports success β€” the only trace is a (FATAL) line at the tail of the install log. Always grep the log tail for FATAL and run the three-component acceptance checks before trusting the completion message.

How can I speed up cPanel downloads in China?​

Direct throughput from a China cloud server to httpupdate.cpanel.net measures around 50 KB/s across all mirror IPs, while the same source reaches 1.8-6 MB/s over a residential line elsewhere. The workable pattern is a reverse dynamic SOCKS tunnel from a faster network plus proxychains on the installer β€” measured 14x faster at about 708 KB/s. Treat it as temporary: uninstall the proxy tooling when the install finishes.

CCLEE

Independent developer, 24 years in e-commerce, focused on grounding AI in real business scenarios.

Work with me