Disclosure: ispmanager provided a free test licence for this review; I was not paid. In our correspondence they also mentioned a free month of use and a "longer-term partnership" that could include free ongoing use of the panel; I deferred all of that until after testing and accepted none of it. The vendor has no control over the test scope, the findings, the severity ratings or this text. The text was sent to them for a technical accuracy check only: on 18 September, and in its post-second-round form on 21 September. The ispmanager links in this article exist so readers can reach the documentation; nothing was received in return for them.
On 10 September an email arrived. Aleksandra from ispmanager had been reading the blog; they wanted to grow in the Turkish market and were looking for an honest review from the perspective of someone who normally wouldn't need a panel. My relationship with control panels has never been good. On my own servers I prefer the terminal, because when something breaks I know exactly whom to ask: myself.
That is also what made the offer interesting. The question was never "is the UI easy?" It was what the panel takes on, what it keeps out of sight, and whether it tells the operator the truth when something breaks. In my first reply I set a single condition: independence. I would test in my own environment and reach my own conclusions, and the final word on both positive and negative findings would be mine. They agreed.
Three weeks later I have 23 findings, three rounds of testing and an outcome I did not expect. The vendor first rejected the most serious finding, accepted it after the evidence from my second round, and closed it in version 6.153; on 1 October I verified that fix in my own lab. I, in turn, retracted the substance of six of my own findings, because when I measured again, I turned out to be wrong. This article covers both. In my view, the value of a review is not in its number of findings but in how those findings change when new evidence arrives.
Three rounds, one environment
The test environment was not a production server, and I should say so up front: Docker Desktop on a Mac, running a privileged AlmaLinux 9.8 container booted with systemd. Because ispmanager's AlmaLinux repository ships only x86_64 packages, it ran under x86_64 emulation on Apple Silicon.
| Round | Date | Version | Focus |
|---|---|---|---|
| 1 | 14 September | Lite 6.150.4 (core 5.445.2) | 13 areas, 23 findings, 15-page report |
| 2 | 21 September | Lite 6.150.4, clean install | Measuring the vendor's objections |
| 3 | 1 October | Lite 6.153.0 (core 5.448.0), clean install; stable 6.152.2 for comparison | The SEC-02 fix + re-measuring the findings |
The first round used the test licence the vendor sent. In the second and third rounds that licence's activation key was no longer accepted, so I used the trial licence the installer issues automatically to this IP. Round two installed the same packages as round one; round three installed 6.153.0 with the current installer script from the vendor's server.
The method was the same every time. I exercised each feature through the panel's API first, then verified it on the operating system: the filesystem, the process table, iptables, systemd, the generated configuration files. On top of that I added hostile scenarios: symlink escapes, cross-tenant read attempts, killed services, a broken nginx configuration, changes made behind the panel's back.
Running in a container produced three side effects, which I separated out from the start: IP management expects an ifcfg-eth0 file, panel login depends on the sshd PAM profile, and MariaDB fails on first start. This round I finally found the cause of the last one. I'll come back to it in a minute, because it is small but instructive.
What the panel takes ownership of
This was the most important question for me, and the answer was more mature than I expected. The installed stack is a classic split: nginx terminates TLS and serves static content, and PHP runs either under MPM-ITK, one of Apache's third-party MPM modules, with mod_php under a separate UID per site, or in per-site PHP-FPM pools over a Unix socket. The panel itself lives in a separate HTTP server, ihttpd, on port 1500.
Configuration is generated from templates, and the distribution's own nginx.conf only gets include lines added; the file is not overwritten. Every site and user has vhosts-resources/<site>/ and users-resources/<user>/ directories; that is the way to extend things without breaking the panel. In the first round I added a line by hand to a vhost file the panel had generated, and after the panel rewrote the file the line was still there. A good sign for anyone who lives in fear of "don't touch it, the panel will overwrite it".
What held up
This list is long, and all of it was measured. nginx returned 403 for a web-reachable symlink pointing at /etc; the panel writes disable_symlinks if_not_owner into the vhosts. Apache MPM-ITK really does run each site under its owner's UID; the test script under mod_php ran as uid=1010 (usera). A database user created through the panel could create and write tables in its own database and got ERROR 1044 on its neighbour's; I repeated that on 6.153 on 1 October as well.
An archive taken with backup2 contained files with their ownership preserved, a valid mysqldump and metadata. I deleted the site's files and restored them from the archive, and the site returned 200 again. Self-signed certificate generation, assignment to a site and the HTTP-to-HTTPS 301 redirect all worked. I injected a broken nginx configuration: the reload was refused and the running workers were left alone. The history_users table records who did what, from which IP, and which fields changed, with a timestamp. Firewall rules added through the panel show up as commented, persistent iptables entries and are removed cleanly. During testing my own IP got caught by the brute-force lockout, so that works too.
Round three added two items to this list, and both came from places where I had been wrong: the panel's own metadata does go into the full backup, and backups can be managed through the API on Lite. I'll come back to both below.
SEC-02: three rounds of one password
This turned out to be the longest-lived finding of the review, and in my view the most instructive.
Round one. You register the database server in the panel with the root user and a password. Then you call the func=db.server list: the response carries the password you saved, in plain text, in the password= field. I rated it High in the report and wrote "including reseller accounts" under impact.
The vendor's objection (18 September, in writing). The engineering team assessed it as "by design". The reasoning: db.server.edit is available only to the superadmin, the superadmin is equivalent to root on the server, so seeing the password gives them no new capability. The last sentence of their reply left the door open: if I could show a scenario in which someone other than the superadmin obtains the credential, they would reassess.
That reply changed my initial assessment. I withdrew the reseller claim, because I had not measured it. But nobody had measured the "superadmin only" claim either.
Round two (21 September). I created an ordinary administrator with admin.edit, level=29. The superadmin is level=30. This account stayed within the limits the panel documents: the file manager, the shell, creating administrators and db.server.edit were all denied. But it could call the db.server list, and the list carried the password:
authinfo=opadmin:...&func=db.server
id=1 name=MySQL type=mysql host=127.0.0.1 username=root password=MgrRoot#2026 savedver=10.5.29
authinfo=opadmin:...&func=file
ERROR access(function): You have insufficient permissions to execute the function 'file'
(The password is a lab-only test value.) The table in the vendor's account levels documentation shows "DBMS management", "File manager", "Command execution" and "Shell client" as unavailable to the administrator. Everything that was denied matched the documentation exactly; the password in the db.server list, however, leaked out beneath the documented boundary. And it was not a placeholder value: what the panel had stored was the real password of the MariaDB root account, in other words the key to every database on the server. I sent this to the vendor on 21 September, together with the reproduction steps.
The whole weight of the threat-model debate rested on a single detail. The vendor had looked at the db.server.edit form; the password was being returned by the db.server list. Four letters separate the two function names, and one role separates their privilege levels.
The vendor's reply (23 September). It was clear and direct: "You are correct: our previous response referred to the wrong API call." They reproduced the scenario in their own environment. They didn't hide their own perspective either: in their model the administrator is a trusted, high-privilege role, and as a rule they don't hide from an administrator what an administrator can see. But they agreed that a stored root password surfacing for a role that is denied the file manager, the shell and administrator management sits below that line, and wrote that they would remove the password from the db.server response, targeting version 6.153, due on 29 September.
I don't see this often. The vendor put forward a claim in their first reply, I measured it, the measurement refuted it, and the vendor revised its initial assessment in writing. To me that is more valuable than an "I found a vulnerability" story: a disagreement was settled by measurement.
Round three (1 October). According to ispmanager's changelog, 6.153.0 shipped a day late, on 30 September, in the beta channel; the stable channel moved to 6.152.2 the same day and is still there as of 2 October. I passed --release 6.153 to the installer, installed a clean 6.153.0 and repeated the second-round scenario exactly. Same administrator role, same call, three output formats:
# opadmin (level 29), 6.153.0
out=text id=1 name=MySQL type=mysql remote_access=off host=localhost hostandport=127.0.0.1:3306 savedver=10.5.29
out=JSONdata [{"id":"1","name":"MySQL","type":"mysql","remote_access":"off","host":"localhost","hostandport":"127.0.0.1:3306","savedver":"10.5.29"}]
out=xml <elem><id>1</id><name>MySQL</name>...<savedver>10.5.29</savedver></elem>
# root (level 30), 6.153.0
out=text id=1 name=MySQL ... username=root password=MgrRoot#2026 savedver=10.5.29
The administrator's response no longer contains a password field, nor a username field, in any of the three formats: text, XML and JSONdata. The superadmin's list still carries the password. That is consistent with the threat model the vendor defended from the start: the superadmin is root anyway. If it were up to me, I would mask the password on read paths there too, but that is a defence-in-depth preference, not a boundary violation.
I also checked that the fix did not break anything else. The administrator is still denied db.server.edit, the file manager, the shell, creating administrators and the scheduler. A user-level account cannot call db.server at all. The administrator can create a database on behalf of a user, and the list keeps working without exposing the stored credentials. The test password does not appear anywhere in the panel's log files under /usr/local/mgr5/var. One detail remains: although the documentation table shows "DBMS management" as unavailable to the administrator, the administrator can still call the db.server list, now without the password. Documentation and behaviour still don't fully match, but no secret leaks any more.
The detail that matters most for readers: for now the fix exists only in the beta channel. I ran the same scenario in a second container on 6.152.2, which is what the installer fetches with --release stable. The administrator account still receives the password in plain text:
# opadmin (level 29), 6.152.2 (stable)
out=text id=1 name=MySQL ... username=root password=MgrRoot#2026 savedver=10.5.29
The vendor says it updates the beta channel every two weeks and the stable channel once a month; it hasn't yet said when 6.153 will reach stable. Until then, anyone on stable has two options: move to 6.153 in the beta channel, or create no administrator accounts other than the superadmin. If level-29 administrator accounts could reach the database server list before 6.153, the password may have been exposed to them; change the MariaDB root password after upgrading as well. The upgrade itself doesn't require this; the reason is that exposure window. How to do it is in the day-one list below, because the wrong method breaks the panel.
One more note. The 6.153.0 changelog entry has three "fixed a security issue" lines; two are rated low and one medium, and none of them is named. I don't know which one is SEC-02, and I won't guess. What I do know is that the behaviour I measured has changed.
SEC-01: open_basedir still does not carry over to FPM
When you create a site with the default settings (mod_php), open_basedir is set correctly: limited to /var/www/<user>/data:., and a test script cannot read /etc/passwd. Switch the same site to the PHP-FPM mode the panel supports, call the same script, and the restriction is gone. The panel keeps showing basedir=on; the generated pool file has no open_basedir, the site-level include file is zero bytes, and the user-level one only carries upload_tmp_dir and session.save_path.
The vendor reproduced this in their first reply, agreed that PHP modes should behave consistently, and put a fix for a future release on their backlog. But they disputed the severity: in the multi-tenant model the real boundary is at the operating-system level, each pool runs under its own system user, so one tenant's script cannot read another tenant's files even without open_basedir.
That objection was measurable, and I had not measured it in the first report. /etc/passwd is world-readable anyway. In round two I tried a direct cross-tenant read, and on 1 October I repeated it on 6.153. User A's FPM site tries to read a file on user B's site:
# 6.153.0, a.local (PHP-FPM, isp-php84)
SAPI=fpm-fcgi user=usera uid=1010 open_basedir=[]
/etc/passwd => OK 1796 bytes
/var/www/userb/data/www/b.local/secret.txt => FAIL (Permission denied)
/usr/local/mgr5/etc/ispmgr.conf => FAIL (Permission denied)
/etc/shadow => FAIL (Permission denied)
# 6.153.0, a2.local (mod_php), same user
SAPI=apache2handler user=usera uid=1010 open_basedir=[/var/www/usera/data:.]
/etc/passwd => FAIL (Operation not permitted)
The result goes the vendor's way. The mechanism is worth learning, too. B's data directory is drwx---r-x userb:mgrsecure: "others" can read and traverse it, which is what nginx and Apache need to serve static files. But the mgrsecure group has no rights at all, and the panel adds every tenant to that group. On Linux, permission checking picks the first matching class out of owner, group and others and applies only that class's bits; once the group matches, "others" is never consulted. Tenants cannot enter each other's directories; service users can. A clever layout; I had missed it in the first report.
That is why I lowered SEC-01 from High to Medium in round two, and that is where it stays. A defence layer that the panel shows as enabled silently disappearing on a supported mode switch is a real defect; world-readable system files are exposed. But the tenant boundary held in every measurement. The 6.153 changelog has no line about this, and the behaviour has not changed.
Why I retracted six of my findings
In round three I also re-measured the findings other than SEC-02, or opened and read their documentation. I did not re-measure LIF-01 (uninstall) or LIF-02 (resources outside the panel); for API-02 and PLT-02 I only checked the documentation.
I also need to say one thing plainly. On 23 September the vendor wrote that they had sent feedback on the whole file, and according to my working notes that feedback included design explanations for ROB-04, FUN-04, FUN-05 and API-02. I verified everything below by measuring it myself or opening the documentation, not by trusting those explanations. But for some of these retractions, it was the vendor who pointed me in the right direction.
ROB-03 (monitoring). In the first report I wrote "the panel monitors services but does not bring a failed one back". In round two I stopped nginx and waited: the panel's srvmon monitoring job brought it back with systemctl restart nginx.service after 9 minutes 47 seconds. I had waited only 20 seconds before passing judgement. On 1 October I repeated it on 6.153: 11 minutes 58 seconds. In 6.153's crontab the srvmon job runs every 15 minutes (*/15), and in both measurements the restart landed on the hour. So on this install a failed service can stay down for close to a quarter of an hour. In round two the panel's service list showed nginx as started throughout; on 6.153 it correctly showed stopped from start to finish, which I note as an improvement. One more confession: in this round's first measurement nginx came back after one minute. The cause was not monitoring; another test I was running at the same time had made the panel rewrite a site configuration. I threw that measurement away and repeated the test without touching the machine.
ROB-04 (panel metadata not in the backup). Wrong. On 1 October I ran backup2 -m full and opened the root account's archive. It contains /usr/local/mgr5/etc/ispmgr.db, core_core.db, their -wal and -shm files and ispmgr.conf; 2,203 files in total. In the first round I had looked at the user archives and never opened root's.
FUN-02 (backups cannot be managed via the API). Wrong, but in an interesting way. In the first round I called the backup.plan and backup.storages functions, got module is missing, and wrote "on Lite, backup management is effectively CLI-only". In round three a banner inside one of the responses pointed at a backup2.schedule function. I tried it: backup2.settings, backup2.schedule and backup2.list work. The API reference lists both families; the backup.plan family is absent on Lite, the backup2 family is present. What remains is a documentation clarity note: both families are documented side by side, and the docs don't say which works in which edition.
FUN-03 (no WAF on Lite). Wrong. The WAF is an optional component installed from the Software configuration screen; on 6.153 the form shows a package_nginx_modsecurity=off option. I had seen that it was not installed and concluded that it did not exist.
FUN-04 (no supervision for Node.js apps). Wrong. I had looked for a systemd unit, didn't find one, and wrote "no mechanism that restarts the app after a crash was visible". The panel manages Node.js and Python applications with PM2; /usr/lib/ispnodejs/bin/pm2 is installed, the documentation gives that same path for diagnostics, and the 6.152 changelog contains a fix related to the PM2 interpreter. I had been looking for the wrong tool. I did not separately measure a restart after a crash; this retraction rests on the installed tool and the documentation.
FUN-05 (the system user cannot log in over FTP). Wrong. The FTP server documentation describes virtual FTP users as the design itself: FTP access without creating real system accounts. I had also noted "the docs say password, the API wants passwd"; the API reference says passwd too, so that note was wrong as well.
In three places I corrected a finding without retracting it. In PLT-01 (no ARM packages) the behaviour is unchanged; the aarch64 path in the 6.153 repository also returns 404. But the system requirements page says "use the server version of an operating system with the x64 architecture". I had written up a documented limitation as a Medium defect; I downgraded it to an informational note. In FUN-01 (Python does not work) I had given "Phusion Passenger is not installed" as the cause; in ispmanager 6, Python runs under PM2, and the Python component is disabled in a default install. The remaining defect lies elsewhere, below. In SEC-03 I had written that AlmaLinux 9's default password scheme is "yescrypt"; the container's /etc/login.defs says ENCRYPT_METHOD SHA512. The finding stands; the comparison was wrong.
There is no point hiding any of this. If I value the vendor revising its initial assessment of SEC-02, I have to apply the same standard to myself. The common denominator is clear, too: everywhere I was wrong, I had either not waited long enough or drawn a conclusion without opening the documentation. In the first report my numbers were confident; my evidence was not as strong.
What is still open on 6.153
I reproduced every remaining medium-severity finding on 6.153.0 on 1 October. None of them has changed.
SEC-03. Users created through the panel are written to /etc/shadow with the $1$ prefix, that is, md5crypt. The libxcrypt documentation is explicit about this scheme: MD5 is so cheap on modern hardware that it should not be used for new hashes, and its processing cost is not adjustable. On the same machine login.defs says SHA512; the panel bypasses the system default.
SEC-04. MariaDB listens on *:3306, and the single multiport rule in the default firewall opens 20:22,25,80,443,110,143,465,587,993,995,53,3306,5432,1500 to every address. The two anonymous accounts the install leaves behind (''@localhost and ''@<hostname>) are still in the table. Since every named account is @localhost, remote login should not work at the moment. I did not try it. But the day the first @% account is created, nothing is left at the door.
SEC-05. The panel's root login calls PAM with the sshd service name. On 1 October I temporarily removed /etc/pam.d/sshd, and an API call with the correct password returned ERROR auth(badpassword); once the file was back, the same call worked. Every hardening change to sshd's PAM stack, an MFA module or a deny rule for instance, also affects whether the panel can be reached.
SEC-06. This round I measured it with a control group. You send secure=on for a site owned by a user whose SSL limit is off, the API returns OK, and when you read the site back it says secure=off and nginx has no 443 line. Turn the limit on for the same user and send the request: because I passed the certificate name in the wrong format, I first got an explicit validation error (ERROR value(ssl_cert)), so with the limit on, the panel does tell you what's wrong. With the correct name: secure=on, port 443 is listening, and HTTPS returns 200. I turned the limit off and sent the same correct parameters again: OK again, secure=off again. The operator believes HTTPS is on.
What is left of FUN-01 belongs to the same family. With the Python component not installed, sending python=on for a site still returns OK, and the site stays without Python. I lowered it to Low, because this time it is not a security setting; but the pattern is the same.
ROB-01. The panel connects to MariaDB through root@localhost for its own database operations. After a routine hardening step, ALTER USER 'root'@'localhost' IDENTIFIED BY '...', I tried to create a database: ERROR db(connect): ... Access denied for user 'root'@'localhost'. There is no health indicator and no startup check. Reverting fixes it. There is nothing wrong with unix_socket authentication itself; the problem is that the panel never tells the operator about this dependency.
ROB-02. Here is the small story I promised. In all three rounds MariaDB failed during installation. A .cache subdirectory appears in /var/lib/mysql, and mariadb-prepare-db-dir refuses to initialise a non-empty directory. This round I looked inside: .cache/rosetta. The mysql user's home directory is /var/lib/mysql. Under emulation, running any process as a user creates .cache/rosetta in that user's home directory; I reproduced this with a separate test user by running runuser -u rtest -- /bin/true. So the trigger is my environment; you will not see this on a real x86 server.
The product finding is not the trigger but what happens next. The 6.153 install log has Error in POSTIN scriptlet in rpm package coremanager-pkg-mysql, three Job for mariadb.service failed lines, and then seven The server wasn't added. Retrying... lines, backing off from 1 to 64 seconds. The installation still ends with EXIT=0 and the message "Your newly installed ispmanager panel is available". What's left is a panel with no database server registered, and the operator finds out when they try to create their first database and get notconfigured(nodbserver). The retry loop in the coremanager-pkg-mysql package's post-install script and a "pending registration" marker file show that the vendor has been looking at this class of problem; the 6.152.0 changelog also has a fix for the default database server not appearing in the list after installation. The same thing happened in the stable 6.152.2 container. Failure is still reported as success.
The full table
| ID | Initial rating | As of 1 October | Note |
|---|---|---|---|
| SEC-01 | High | Medium, open | No open_basedir under FPM; tenant boundary holds |
| SEC-02 | High | Fixed (6.153.0) | No password in the admin response; independently verified |
| SEC-03 | Medium | Medium, open | md5crypt ($1$) |
| SEC-04 | Medium | Medium, open |
*:3306, world-open rule, anonymous accounts |
| SEC-05 | Medium | Medium, open | Panel login tied to the sshd PAM profile |
| SEC-06 | Medium | Medium, open | SSL request swallowed with OK
|
| ROB-01 | Medium | Medium, open | Panel silently breaks when the root credential changes |
| ROB-02 | Medium | Medium, open | Install "succeeds" without a working DB |
| FUN-01 | Medium | Low |
python=on → OK without the Python component |
| FUN-02 | Medium | Info |
backup2 API exists; docs mix two families |
| PLT-01 | Medium | Info | x64 is a documented requirement |
| ROB-03 | Low | Info | Recovery exists: srvmon, */15 on 6.153 (measured 11 min 58 s) |
| ROB-04 | Low | Retracted | The root backup includes the panel databases |
| ROB-05 | Low | Low, open |
restore2 date and file options |
| FUN-03 | Low | Retracted | The WAF is an optional component |
| FUN-04 | Low | Retracted | PM2 supervision |
| FUN-05 | Low | Retracted | Virtual FTP users are by design |
| API-01 | Low | Info | Docs say elid; name= is ignored without an error |
| LIF-01 | Low | Low (not re-measured) | Leftovers after uninstall |
| API-02 | Info | Info | Password in the URL; a session-key alternative is documented |
| LIF-02 | Info | Info (not re-measured) | Resources created outside the panel don't appear in its lists |
| PLT-02 | Info | Info | The installer requires SELinux to be disabled (documented) |
| OBS-01 | Info | Info | 13 MB ispmgr.log in ~16 min, including install and two test runs |
In numbers: as of 1 October, 6.153.0 has 7 Medium, 3 Low and 8 informational items open. One finding has been fixed and four have been fully retracted. When I say I retracted the substance of six findings, I mean those four plus ROB-03 and FUN-02, whose claims collapsed and which were reduced to informational notes. PLT-01 also became an informational note, but I didn't retract it: the behaviour I measured was real, it was just documented. On 6.153 there is no open High-severity finding left; on stable 6.152.2, SEC-02 is still open.
Two rows need more explanation. For API-01 I sent name=nginx to services.restart; the panel returned no error and did not touch nginx. It worked with elid=nginx. The API reference already says elid, so this is less a defect than a note about an API that silently swallows parameters it doesn't recognise. For API-02, carrying credentials in the URL via authinfo= is protected in transit by TLS. The panel masks the value as authinfo=* in its own access log, but the same is not guaranteed for the log of a proxy in between; however, the API guide also describes a session ID valid for one hour, one-time key login and an IP whitelist for authinfo. The choice is the operator's.
Two themes
Put the remaining findings side by side and most of them cluster in two places.
The first is silent failure semantics. A limit rejection is swallowed (SEC-06, FUN-01). The installer "succeeds" with a broken database (ROB-02). The panel silently breaks when the root credential changes (ROB-01). A configured defence layer disappears on a supported mode switch while the UI still shows it as enabled (SEC-01). The product tells the truth, but not loudly enough. On a security-related setting, "OK, but not applied" is the worst answer there is.
The second is a tight coupling to the host's identity infrastructure: the sshd profile in PAM, the root credential in the database, and SELinux, which the installer requires to be disabled. The panel does not carry its own identity; it leans on the host's. Neither requires an architectural rewrite; both call for a clearer definition of the ownership boundary and of the failure contract.
If you are installing ispmanager: a day-one list
I am writing these based on the behaviour I measured on 6.153.0. They are worth re-checking as versions change.
- Version. If you are going to give administrators panel accounts, install 6.153.0 or later, or hold off on administrator accounts until you can. As of 2 October, 6.153 is in the beta channel.
-
Database port. Check the listening address with
ss -ltnp | grep 3306. If you don't need remote access, remove 3306 and 5432 from the firewall's multiport rule or narrow the source, and pull the daemon back to localhost with bind-address. -
Anonymous accounts. Delete the rows with an empty user name from
SELECT user,host FROM mysql.user. -
FPM sites. On every site you switch to PHP-FPM, check
ini_get('open_basedir')with a small script. If it is empty, there is no restriction, whatever the panel shows. -
Whether the install really finished. After the installer finishes, verify that
mgrctl -m ispmgr db.serveris not empty. The "success" message does not guarantee it. - Credential changes. Before you change the MariaDB root credential or touch sshd's PAM stack, write down in your runbook that the panel depends on them, and try a panel operation after the change.
- Panel port. The same multiport rule also opens 1500, the panel itself, to every address. If the panel should only be reachable from a management network, narrow the source.
-
The password after moving to 6.153. If level-29 administrator accounts could reach the database server list before 6.153, they may have read the stored root password; change the MariaDB root password. The upgrade itself doesn't require this. Keep
unix_socketwhile doing it (ALTER USER 'root'@'localhost' IDENTIFIED VIA unix_socket OR mysql_native_password USING PASSWORD('...')), then save the new password in the panel withdb.server.edit. I tried this sequence on 6.153 and the panel kept working; a plainIDENTIFIED BYtriggers ROB-01. -
Backups. Open the
rootarchive of a full backup yourself once and see that it contains the panel databases. I wrote it up wrong without looking.
What the vendor did
I sent the report on the night of 14 September. On 15 September the reply came: "we've shared it with the team, we'd love a full article". On 18 September the engineering team's written assessment arrived: they reproduced SEC-01 and put it on the backlog, and objected to SEC-02 on threat-model grounds. For the remaining findings the summary was: some make good sense and will be acted on, in a smaller number the intended design wasn't fully accounted for in the report, nothing is urgent, everything goes into the backlog. On 23 September they revised their assessment of SEC-02 and gave a fix date. On 1 October Aleksandra asked whether I had run the test, when the article would be out, and, with a smiley, whether there would be "at least one link".
In round three I saw how right that "intended design wasn't fully accounted for" sentence was: FTP, the WAF, PM2 and the backup API fell squarely into that category. It made me think again that a good vendor response is not saying "you're right" to every finding. A good response reproduces, concedes where it agrees, and gives a testable justification where it doesn't. ispmanager did that. When I tested the testable justification, it turned out to be wrong in one place, and they accepted that. The answer to the link question is in this article too: I linked their documentation generously, because readers should be able to check my claims on the vendor's own pages.
Who it is for, and what I would do
ispmanager's natural territory is web hosting, reseller setups, and servers that are run by several operators or handed over to customers. The panel makes sense wherever someone who doesn't know every corner of Linux has to do routine work from a safe surface. Tenant isolation, template discipline and backup integrity are solid enough for that. For anyone on ARM infrastructure the door is closed, and documented as such; for those who manage configuration heavily outside the panel, LIF-02 is a nuisance. For anyone thinking of building their own PaaS layer instead of a classic hosting panel, what I wrote about Coolify describes a different trade-off.
My default is the terminal, and that hasn't changed. But this test changed my view of where a panel creates value. In a setup where I am not the only operator, and want to hand part of the work to someone who doesn't go deep into the system, I would take ispmanager seriously. That is not the answer I would have given before I started. My conditions are clear too: 6.153 or later, the day-one list above, and checking open_basedir on FPM sites myself until SEC-01 is closed.
Closing
I tested ispmanager Lite at its edges, not on the happy path. Template discipline, OS-level isolation, backup integrity, the TLS lifecycle, the audit trail and atomic reloads genuinely held up. Of the two High findings, one was lowered to Medium by measurement and the other was closed in 6.153, which I verified independently; the fix has not reached the stable channel yet. Seven medium-severity findings remain open on 6.153, and most of them are symptoms of the same habit: failing silently.
Both sides changed their minds during this process. The vendor revised its initial threat-model assessment of SEC-02 after reproducing the privilege-boundary case, and decided to fix the behaviour. I lowered SEC-01's severity, deleted ROB-03's "no recovery" sentence and retracted the substance of six of my findings. Two days ago I wrote about how the patch that closed twenty CodeQL findings turned out to be the source of the twenty-first. This week I learned the same lesson from a different direction. A control panel should make your work easier on a good day and tell you plainly what happened on a bad one. The same standard applies to a review.
Environment: ispmanager Lite 6.150.4 (core 5.445.2) and 6.153.0 (core 5.448.0), AlmaLinux 9.8, a --privileged systemd container on Docker Desktop, x86_64 emulation on Apple Silicon. Round 1: 14 September, vendor test licence. Round 2 (21 September) and Round 3 (1 October): IP-bound trial licence, clean install; in Round 3, 6.152.2 (core 5.447.2) in a separate container for the stable-channel comparison. The three environment-induced conditions (ifcfg, the sshd PAM profile, MariaDB .cache) were not counted in the severity totals. Not tested: Let's Encrypt end to end (there was no real domain), disk quotas (container filesystem), and behaviour on a real virtual machine or physical server. The raw command logs are kept.
Official Sources
- nginx — disable_symlinks
- Apache HTTP Server — Multi-Processing Modules
- PHP documentation — open_basedir (ini.core)
- PHP documentation — PHP-FPM configuration, php_admin_value
- Linux-PAM — pam_start(3) and the service name
- libxcrypt — crypt(5), md5crypt and yescrypt
- Linux man-pages — path_resolution(7), permission checks
- MariaDB — unix_socket authentication plugin (source)
Top comments (0)