Перевод
* Полный обход исходников скриптом, повторяющим логику подстановки: было
видно 681 строку, которая осталась бы русской. Переведены все — словарь
вырос с 640 до 1304 записей.
* Покрыты разделы, которых раньше не было в проверке: консоль сервера с
описанием всех RCON-команд, планировщик задач, файрвол, базы MySQL, FTP,
автоустановка модов, совладельцы, статистика сервера, страница аккаунта,
сообщения планировщика и все письма.
* Убран ключ-предлог 'в' => 'at': он подменялся ВНУТРИ ещё не переведённых
предложений и давал «at поле имя пользователя». Служебные слова переводятся
только вместе с фразой.
* Формат даты 'd.m.Y в H:i' содержал русское «в» между датой и временем —
теперь проходит через t() (31 вызов date() в 20 файлах), в английском
режиме получается 30.07.2026 14:22.
* Абзацы FAQ разбиты по строкам вёрстки, атрибуты placeholder и tooltip
добавлены отдельными ключами — раньше они не попадали в разбор.
* Живой обход 45 страниц в английском режиме: русскими остались 4 места, и
все намеренно — подпись «Русский» у переключателя, коды языков игрового
сервера в FAQ и регулярное выражение проверки имени.
Скриншоты
* docs/screenshots/{ru,en} — по 38 кадров: вход, регистрация, восстановление
пароля, кабинет, заказ сервера, новости, статус, тикеты, FAQ, веб-хостинг и
вся админка вместе со скрытыми вкладками настроек и переключателем капчи.
* docs/screenshots/README.md и README.ru.md — галереи, где каждый раздел
показан на двух языках рядом.
Документация
* README.md теперь английский (главное описание), русский переехал в
README.ru.md; в начале каждого — кнопка на другой язык.
* Добавлен раздел о доступных играх: 11 игр каталога с кодами,
query-протоколами, диапазонами слотов и портов, плюс список ядер, которые
клиент переключает в один клик.
* Добавлен раздел планов: больше игр по просьбам людей и современные
платёжные системы — PayPal, Stripe, Lava.
Поддерживается и развивается с помощью REDL.IO.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
249 lines
13 KiB
Markdown
249 lines
13 KiB
Markdown
[Русский](ИЗМЕНЕНИЯ.md) · **English**
|
||
|
||
# What changed compared to the original
|
||
|
||
Original: **HostinPL 5.6** as a "nulled" build (the `Xopowblu-4EJlOBEK/HostinPL-5.6` fork),
|
||
written for Debian 9 and PHP 7.0.
|
||
|
||
Below is everything we changed, with file names and line numbers. Panel code changes live in `panel/`.
|
||
|
||
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
|
||
|
||
---
|
||
|
||
## 1. The panel now runs on modern PHP
|
||
|
||
The original targets PHP 7.0. On PHP 8 parts of it died outright. It now runs on **PHP 8.4**:
|
||
walking all 21 sections (home, servers, news, status, tickets, web hosting and the whole admin area)
|
||
returns **200 on every one and zero errors in the log**.
|
||
|
||
| File | Before | After |
|
||
|------|--------|-------|
|
||
| `engine/engine_ftp/elFinder.class.php:4497` | `utf8_encode()` — removed in PHP 8.2, the file manager died with a fatal error | Wrapped in `function_exists()`, falls back to `mb_convert_encoding($str,'UTF-8','ISO-8859-1')` |
|
||
| `application/views/admin/checksys/index.php:22` | `apache_get_modules()` — only exists under Apache with mod_php, so the "System check" page returned 500 under nginx | Wrapped in `function_exists()`, otherwise checked via `REQUEST_URI` |
|
||
| `application/views/admin/index.php:215` | `$item['invoice_ammount']` used after the `foreach`, producing an Undefined variable when there were no invoices | `isset()` check |
|
||
| `application/models/users.php:199-206` | `$city[1]`, `$country[1]`, `$countryCode[1]` used without checking that the regex matched | `isset()` checks |
|
||
| `application/controllers/common/loginheader.php:35` | `$_GET['ref']` without `isset` | `isset()` plus a cast to `(int)` |
|
||
|
||
### What turned out to be a false alarm
|
||
|
||
The bundled **phpseclib 1.x** uses `create_function()`, which PHP 8 removed. It looked like a blocker,
|
||
but checking showed that **phpseclib is not included anywhere in the panel** — it is dead code.
|
||
Communication with game nodes goes through the native `php-ssh2` extension
|
||
(`engine/libs/ssh2.php` → `ssh2_exec`). Nothing needed fixing.
|
||
|
||
`get_magic_quotes_gpc()` in `elFinderConnector.class.php:320` is equally harmless: it sits behind
|
||
`version_compare(PHP_VERSION,'5.4','<') && ...`, so on PHP 8 it is never reached.
|
||
|
||
---
|
||
|
||
## 2. The captcha is off and managed from the admin area
|
||
|
||
The captcha used to be hard-wired: without valid Google keys **it was impossible to log in**,
|
||
and the form displayed "ERROR for site owner: Invalid site key". There is now a `captcha_enable`
|
||
flag in `application/config.php`, defaulting to `0` (off).
|
||
|
||
**Backend.** All four validators received the line
|
||
`if($this->config->captcha_enable != '1') return $result;` before the captcha check:
|
||
|
||
* `application/controllers/account/login.php` — login
|
||
* `application/controllers/common/loginheader.php` — registration and the contact form (2 places)
|
||
* `application/controllers/tickets/create.php` — ticket creation
|
||
|
||
**Markup.** The five captcha widgets (4 in `views/common/loginheader.php`, 1 in
|
||
`views/tickets/create.php`) are wrapped in `<?php if(@$captcha_enable == '1'): ?>`. Google's `api.js`
|
||
is only loaded when the captcha is on. Every `grecaptcha.reset(...)` call became
|
||
`window.grecaptcha && grecaptcha.reset(...)` — otherwise, with the captcha off, JavaScript threw
|
||
inside the error handlers and the forms stopped responding.
|
||
|
||
**Admin area.** `views/admin/settings.php`, the "Other settings" tab, gained a
|
||
"Bot protection (reCAPTCHA v2)" block: an Off/On selector plus Site key and Secret key fields.
|
||
|
||
Verified both ways: with the captcha off, login and registration succeed and the user is really
|
||
created in the database; with it on, a fresh session gets "Confirm that you are not a robot!"
|
||
and the widget returns to the page.
|
||
|
||
> **Careful when adding your own settings.** The settings writer matches configuration lines
|
||
> **by substring** (`strpos`). That is why the flag is called `captcha_enable` and not `captcha`:
|
||
> the string `captcha` occurs inside `recaptcha` and `secret_recaptcha`, and saving would have
|
||
> overwritten the wrong parameter. New keys must not be substrings of existing ones.
|
||
|
||
---
|
||
|
||
## 3. Vulnerabilities fixed
|
||
|
||
In brief (full detail with code in [SECURITY.en.md](SECURITY.en.md)):
|
||
|
||
* **Two pre-authentication SQL injections** in `application/models/users.php` (`createAuthLog()`):
|
||
the login journal received the password from the form and the IP from the `CF-Connecting-IP`
|
||
header — which was not validated at all — without escaping. Every value now goes through
|
||
`$this->db->escape()` or a cast to `(int)`, and the header is validated with
|
||
`filter_var(..., FILTER_VALIDATE_IP)`.
|
||
* **Plaintext passwords**: `login.php` wrote the real password into the `authlog` table on every
|
||
login attempt, including failed ones. Removed.
|
||
* **XSS** in the hidden `ref` field of the registration form — `htmlspecialchars()` added.
|
||
* The request to the `ip-api.com` geolocation service now only runs for a valid IP and uses `urlencode()`.
|
||
|
||
---
|
||
|
||
## 4. The installer was rewritten from scratch
|
||
|
||
The original `install` was dangerous on any current system:
|
||
|
||
* `echo "deb ... stretch main" > /etc/apt/sources.list` — **wiped the repository list** and replaced
|
||
it with Debian 9, after which apt was broken
|
||
* installed `php7.0` and hard-coded edits to `/etc/php/7.0/apache2/php.ini`
|
||
* switched MariaDB to `bind-address = 0.0.0.0` without a word of warning
|
||
* only ran when `/etc/issue.net` said `Debian9`, and otherwise refused to start
|
||
* generated passwords and tokens but saved them nowhere — you could only copy them off the screen
|
||
* carried on after any failure: every command ended in `> /dev/null 2>&1`
|
||
|
||
The new scripts:
|
||
|
||
**`install-panel.sh`** — nginx, PHP 8.x (version detected automatically), MariaDB, a database with a
|
||
random password, configuration, scheduler, autostart watchdog, administrator creation, and a final
|
||
check that the login page really is served with its form. Idempotent: running it again does not wipe
|
||
a database that is already loaded. Credentials are written to `/root/.redl-panel-credentials`
|
||
(chmod 600). With `set -euo pipefail` the script stops on error instead of pretending all is well.
|
||
|
||
**`install-node.sh`** — Docker from the official repository, image build, the
|
||
`/home/cp/gameservers/files` layout, the `gameservers` group, MariaDB for game server databases,
|
||
SteamCMD, ProFTPD, and sshd configuration that rolls back if `sshd -t` fails. **Before doing anything
|
||
it checks whether Docker can work on this machine at all** (`unshare -Ur`) and says so plainly if it
|
||
cannot. At the end it prints ready-to-use location details and firewall commands.
|
||
|
||
Neither script touches `/etc/apt/sources.list`.
|
||
|
||
---
|
||
|
||
## 5. The game server image was rebuilt
|
||
|
||
The original `docker/Dockerfile.original-stretch` is based on `debian:stretch`, and the Debian 9
|
||
repositories were shut down in 2023 — **that image no longer builds**, `apt-get update` inside it fails.
|
||
|
||
The new `docker/Dockerfile` is based on Debian 12 (bookworm) but **must still be tagged
|
||
`debian:stretch`**: that name is hard-coded in the panel
|
||
(`application/models/servers.php:642`, `docker create ... debian:stretch`) and cannot be changed
|
||
without editing the panel.
|
||
|
||
What is inside: 32-bit libraries (SA-MP, CRMP, MTA and older CS builds are i386), `screen` for
|
||
consoles, Java for Minecraft, Node.js 20 for RAGE:MP, and `gdb` for crash analysis.
|
||
Node.js comes from NodeSource **over HTTPS with repository key verification**, unlike before.
|
||
|
||
---
|
||
|
||
## 6. Working without systemd
|
||
|
||
The panel assumes systemd is present. Container VPSes do not have it, so watchdogs were added on
|
||
cron: `/usr/local/bin/hostinpl-guard` (panel) and `/usr/local/bin/gamenode-guard` (node).
|
||
Once a minute (every 2 minutes on a node) they check MariaDB, PHP-FPM, nginx, Docker and cron and
|
||
restart whatever died, plus an `@reboot` job to bring everything up after a restart.
|
||
|
||
Panel liveness is determined by an **HTTP request** to the login page rather than by searching for a
|
||
process name — a pattern search would have matched the watchdog's own command line.
|
||
|
||
---
|
||
|
||
## 7. Scheduler
|
||
|
||
The original pointed its 9 jobs at the panel's public domain. They now go to `127.0.0.1`
|
||
(independent of DNS and external reachability) and carry `-m` timeouts so that a hung request does
|
||
not pile up processes.
|
||
|
||
> **Token pitfall.** You cannot extract the scheduler token from the config with a plain
|
||
> `grep "'token'"` — the line `'yk_password1' => 'token'` matches the same pattern, two values end
|
||
> up in the URL separated by a newline, and the jobs then fail silently. The installer writes the
|
||
> token directly when it generates the configuration.
|
||
|
||
---
|
||
|
||
## 8. Interface languages: Russian and English
|
||
|
||
The original was Russian-only, with every string hard-coded in the templates. The build now has a
|
||
translation layer:
|
||
|
||
* `engine/main/lang.php` — the `Lang` class and the global `t()` helper
|
||
* `application/lang/ru.php`, `application/lang/en.php` — dictionaries
|
||
* Language selection order: `?lang=` parameter → `lang` cookie → **the browser's `Accept-Language`
|
||
header** → panel default
|
||
* A switcher (RU / EN) in the header of the client area and on the login page
|
||
* Anything not yet translated falls back to the original Russian text, so nothing can disappear
|
||
from the interface
|
||
|
||
Translation is applied to the finished response via `ob_start()`, so none of the 200 template files
|
||
had to be rewritten and the coverage extends to the admin area and the e-mails at once. Replacement
|
||
only happens on word boundaries — without that a short key corrupts longer words ("Мод" turned
|
||
"Модуль" into "Modуль").
|
||
|
||
### Translation coverage
|
||
|
||
The dictionary holds **1304 phrases** — everything the panel shows a human: the client area,
|
||
ordering a server, server management (the console with every RCON command described, FTP, MySQL
|
||
databases, the firewall, the task scheduler, mod auto-install), tickets, the whole FAQ, the entire
|
||
admin area, the e-mails and the scheduler's status messages.
|
||
|
||
Coverage is not eyeballed: a script replays the replacement logic against the sources and lists
|
||
what would stay Russian. By that measure **four** items are uncovered, and all four should be:
|
||
|
||
* "Русский" — the label of the language switcher itself;
|
||
* `rus => Pyccĸий` and `ukr => Українська` in the FAQ — these are game server language codes, not UI;
|
||
* the name validation regular expression `/^([А-ЯЁ])([а-яё]{1,15})$/u` — code, not text.
|
||
|
||
Two traps that produced mixed-language output were fixed separately:
|
||
|
||
* the preposition "в" was its own dictionary entry and got substituted inside sentences that were not
|
||
translated yet, producing "at поле имя пользователя". Function words like that are only translated
|
||
together with the phrase they belong to;
|
||
* the date format `d.m.Y в H:i` contains the Russian "в" between the date and the time. It now goes
|
||
through `t()` (31 `date()` calls across 20 files), so English mode renders `30.07.2026 14:22`.
|
||
|
||
Verified by a live crawl of 45 pages in English mode, including the creation forms and the hidden
|
||
settings tabs. Details: see [GUIDE.en.md](GUIDE.en.md), the "Interface language" section.
|
||
|
||
---
|
||
|
||
## 9. Branding and dates
|
||
|
||
* The year in footers: `2020©` → `2026©` (`views/common/footer.php`, `views/common/loginheader.php`)
|
||
* Name and description are REDL.IO: `application/config.php` (`description`, `keywords`,
|
||
`mail_sender`, `mail_from`), footers, the "System check" page
|
||
* Links to the previous build owner's sites (`hostinpl.ru`, `osmp.ga`) replaced with `redl.io`
|
||
* "VK community" links pointing at somebody else's community replaced with `redl.io`
|
||
(`footer.php`, `views/main/index.php`, `views/offline/index.php`)
|
||
* All 15 e-mail templates: a "REDL.IO / AI-powered hosting" header and the signature
|
||
"Sincerely, the REDL.IO team"
|
||
|
||
**The authors' copyright headers were not removed** — they remain in every file that had them.
|
||
|
||
---
|
||
|
||
## 10. phpMyAdmin
|
||
|
||
The panel links to `/phpmyadmin` from the admin area. The original installer set phpMyAdmin up
|
||
through Apache, which did not work under nginx. It is now installed from the distribution repository
|
||
and served by nginx itself.
|
||
|
||
> **Reverse proxy pitfall.** For the address `/phpmyadmin` (without a trailing slash) nginx replies
|
||
> with a redirect and by default puts **its own port** into it, for example
|
||
> `http://panel.example.com:8095/phpmyadmin/`. If the panel sits behind a reverse proxy that address
|
||
> is unreachable from outside and the admin link does not open. The cure is
|
||
> `absolute_redirect off; port_in_redirect off;`, already present in the configuration the installer
|
||
> generates.
|
||
|
||
---
|
||
|
||
## What we did NOT do
|
||
|
||
* We did not rewrite password hashing. The panel stores passwords as **unsalted MD5**
|
||
(`md5($password)`, a `varchar(32)` column). Moving to `password_hash()` affects login,
|
||
registration, recovery and password changes, and requires migrating existing users.
|
||
* We did not move game server creation away from Docker.
|
||
* We did not delete `panel/application/public/js/proxy/proxy.php` — the file is defanged but kept as
|
||
evidence (see [SECURITY.en.md](SECURITY.en.md)).
|
||
* We did not test the payment gateways with real payments, nor VK login.
|
||
* We did not verify the game servers themselves: that needs a node with Docker.
|
||
|
||
---
|
||
|
||
**Maintained and developed with [REDL.IO](https://redl.io) — AI-powered hosting.**
|