mirror of
https://gitverse.ru/kpa39l/xmpp-server-prosody.git
synced 2026-09-29 09:45:02 +00:00
155 lines
7.5 KiB
Markdown
155 lines
7.5 KiB
Markdown
# Prosody 13.0 parallel test stand (icq-prosody-test) — recipe + state
|
|
|
|
Goal: validate Prosody 13.0 (custom image `gitea.nixg.ru/hermes/icq-prosody:13.0`) in
|
|
parallel with the production 0.11.9 container, WITHOUT touching prod. Checked:
|
|
authorization (works), mod_privilege (XEP-0356) functional test — **PASSED 2026-08-30**:
|
|
external component got privileges and a privileged roster IQ got `type=result`.
|
|
|
|
## Layout (on bigbox, /opt/icq/test/prosody)
|
|
|
|
```
|
|
/opt/icq/test/prosody/
|
|
config/prosody.cfg.lua # test.nixg.ru, ports 15222/15269/15280, component secret
|
|
config/certs/ # self-signed test.nixg.ru.{crt,key}
|
|
data/ # prosody data (URL-encoded: data/test%2enixg%2eru/accounts/)
|
|
logs/prosody.log|err
|
|
auth_test.py # slixmpp auth tests (passes)
|
|
privilege_test.py # external-component mod_privilege probe (PASSES)
|
|
```
|
|
|
|
## Container
|
|
|
|
```bash
|
|
docker run -d --name icq-prosody-test --restart unless-stopped -h test.nixg.ru \
|
|
-v /opt/icq/test/prosody/config:/etc/prosody \
|
|
-v /opt/icq/test/prosody/data:/var/lib/prosody \
|
|
-v /opt/icq/test/prosody/logs:/var/log/prosody \
|
|
-p 15222:15222 -p 15269:15269 -p 15280:15280 \
|
|
-p 15347:5347 \
|
|
gitea.nixg.ru/hermes/icq-prosody:13.0
|
|
```
|
|
|
|
NOTE: on 13.0 the component listener binds 5347 inside the container (component_ports
|
|
removed — see SKILL.md). Publish as 15347:5347.
|
|
|
|
## Key config: external component (module-less Component) + secrets
|
|
|
|
```lua
|
|
component_secrets = {
|
|
["telegram.test.nixg.ru"] = "test-secret-telegram-123",
|
|
}
|
|
-- 13.0 requires the explicit external-component block to raise the 5347 listener:
|
|
-- Component "telegram.test.nixg.ru" -- NO module name => external XEP-0114
|
|
-- component_secret = "test-secret-telegram-123";
|
|
```
|
|
|
|
pubsub (13.0): `Component "pubsub.test.nixg.ru" "pubsub"` — NOT a VirtualHost module.
|
|
|
|
## Auth test (slixmpp — verified WORKING on 13.0)
|
|
|
|
- slixmpp must skip cert verification on the self-signed stand:
|
|
```python
|
|
self.ssl_context = ssl.create_default_context()
|
|
self.ssl_context.check_hostname = False
|
|
self.ssl_context.verify_mode = ssl.CERT_NONE
|
|
```
|
|
- Force non-standard port the RELIABLE way: `await x.connect('127.0.0.1', 15222)`.
|
|
`x.address = ...` is IGNORED (DNS lookup of the JID domain wins; it dialed the
|
|
public IP :5222 → connect errors). `custom_address` attribute also did not work in
|
|
slixmpp 1.17.0 — the positional args to connect() are the only thing that worked.
|
|
- Results: testuser1/testuser2/admin AUTH OK; wrong password → `failed_auth` → "AUTH FAILED".
|
|
- Handler registration differs: ComponentXMPP has NO `add_handler`/`make_iq_get(queryns=...)`.
|
|
Use `iq = comp.Iq('get', id)` + `iq.append(ET.Element('{jabber:iq:roster}query'))`.
|
|
NOTE: `comp.add_event_handler('iq', ...)` does NOT fire for incoming IQ on ComponentXMPP —
|
|
register a raw Callback on `{jabber:component:accept}iq` instead (see mod_privilege section below).
|
|
|
|
## mod_privilege (XEP-0356) status — PASSED
|
|
|
|
- NOT in Prosody core in 13.0 (stock image has zero privilege files). Community module.
|
|
- 13.0 needs the prosody-modules TIP version (promise API) — the old 0.11.9 callback
|
|
stub is incompatible (`:next` vs callback). hg.prosody.im/prosody-modules/raw-file/tip/mod_privilege/mod_privilege.lua (~685 lines).
|
|
- The custom image embeds it (prosody-docker/modules/mod_privilege.lua in the hermes/icq repo; workflow builds via Gitea Actions).
|
|
|
|
### Working config (exact, verified)
|
|
|
|
```lua
|
|
-- GLOBAL: component_interfaces MUST be global (above VirtualHost/Component), else listener stays 127.0.0.1
|
|
component_interfaces = { "0.0.0.0" }
|
|
|
|
-- EXTERNAL COMPONENT block — module-less => XEP-0114; raises the 5347 listener
|
|
Component "telegram.test.nixg.ru"
|
|
component_secret = "test-secret-telegram-123"
|
|
modules_enabled = { "privilege" } -- so mod_privilege sees component-authenticated
|
|
|
|
-- VirtualHost: mod_privilege MUST also be in the host's modules_enabled,
|
|
-- and privileged_entities lives HERE (host scope, NOT component scope)
|
|
VirtualHost "test.nixg.ru"
|
|
modules_enabled = { "privilege", ... }
|
|
privileged_entities = {
|
|
["telegram.test.nixg.ru"] = {
|
|
roster = "both", -- perm access=roster type=both
|
|
message = "outgoing",
|
|
iq = { { namespace = "http://jabber.org/protocol/pubsub", type = "both" }, ... }
|
|
}
|
|
}
|
|
```
|
|
|
|
Trigger: `log = { debug = "/var/log/prosody/prosody.log", error = "/var/log/prosody/prosody.err" }`
|
|
(or `info = ...`) — then the debug lines prove it end-to-end:
|
|
|
|
```
|
|
test.nixg.ru:privilege debug Entity is privileged
|
|
test.nixg.ru:privilege debug Roster get from allowed privileged entity received
|
|
```
|
|
|
|
### Gotchas found on 13.0 (IMPORTANT)
|
|
|
|
1. **`component_ports` removed entirely** in 13.0 (not a single grep hit in /usr/lib/prosody).
|
|
Component listener = hardcoded 5347 (`default_port = 5347` in mod_component.lua). Publish as e.g. 15347:5347.
|
|
2. **`component_secrets` alone does NOT activate the listener.** You MUST have the module-less
|
|
`Component "jid"` block (mod_component loads only for an actual external-component host).
|
|
3. **`component_interfaces` is global-scope only.** Put it at the top of the config, NOT inside
|
|
a VirtualHost/Component block, or it is silently ignored (check config complains "Problems found").
|
|
4. **`log` block: only ONE `log = {...}` allowed.** A second log= later in the file OVERWRITES the
|
|
first (Lua). If first had `info = file` and second `debug = console`, info-file logging silently dies.
|
|
Keep one log block in the global section, `info = file, error = file`.
|
|
5. **Component session has NO `full_jid`** (mod_component sets only `session.host`). The tip
|
|
mod_privilege logs `Entity nil try to get roster without permission` when privileges are missing —
|
|
but with the config above privileges ARE granted; "Entity is privileged" debug appears. The
|
|
"Entity nil" line is a log cosmetic, not a privileges failure.
|
|
6. **mod_privilege must be in modules_enabled BOTH on the Component block AND on the VirtualHost.**
|
|
Loaded only globally (outside any host) it won't resolve `privileged_entities` from the VirtualHost.
|
|
|
|
### slixmpp 1.17 ComponentXMPP — in-band IQ gotcha
|
|
|
|
ComponentXMPP registers ONLY handshake + presence_probe handlers — **incoming IQ stanzas are NOT
|
|
processed** (`add_event_handler('iq', ...)` never fires). The XML arrives fine (visible in
|
|
xmlstream DEBUG), but no callback runs. Fix: register your own Callback on the raw IQ path:
|
|
|
|
```python
|
|
from slixmpp.xmlstream.handler import Callback
|
|
from slixmpp.xmlstream.matcher import MatchXPath
|
|
comp.register_handler(Callback('IQPriv', MatchXPath('{jabber:component:accept}iq'), on_iq))
|
|
```
|
|
|
|
...and note the callback receives the IQ as a RAW XML STRING, not a stanza object — parse with
|
|
`ET.fromstring(...)` then `elem.get('type')` / `elem.findall('{jabber:iq:roster}item')`.
|
|
|
|
### Verified test output (2026-08-30)
|
|
|
|
```
|
|
>>> component connected as telegram.test.nixg.ru
|
|
>>> on_iq fired: type=result, id=priv-roster-1
|
|
>>> PRIVILEGE OK: mod_privilege ответил result (roster testuser1)
|
|
```
|
|
|
|
(The trailing "TIMEOUT" in the probe is cosmetic — disconnect() races wait_until('disconnected');
|
|
the result was already received.)
|
|
|
|
## Version sanity checks (recap)
|
|
|
|
- prod = prosody/prosody:latest = 0.11.9 (the buggy mod_privilege era)
|
|
- image = hermes/icq-prosody:13.0 (Debian trixie-slim base, apt prosody 13.0, 5 custom modules)
|
|
- runner = gitea/runner:3.3.1; job image docker27-bash (docker:27 + bash) — runner shells
|
|
run steps via bash; Alpine docker:27 has no bash → exit 127. Use GITHUB_TOKEN for clone,
|
|
PKG_TOKEN (write:package) only for registry login. |