mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-18 00:57:55 +00:00
* fix(deb): keep plugin_packages in-tree to fix admin-installed plugins
The .deb postinstall symlinked /opt/etherpad/src/plugin_packages to
/var/lib/etherpad/plugin_packages so the etherpad user could install
plugins under ProtectSystem=strict. Node.js resolves symlinks to their
realpath before walking node_modules, so a plugin installed via the
admin UI lived under /var/lib/etherpad/... and could no longer reach
the bundled ep_etherpad-lite in /opt/etherpad/node_modules. Every
require('ep_etherpad-lite/...') in installed plugins (and the matching
esbuild client-bundle build) failed with MODULE_NOT_FOUND, etherpad
exited, and the systemd unit restart-looped (ether/ep_comments_page#416).
Keep plugin_packages as a real in-tree directory, make it (and its
.versions/ subdir) group-writable by etherpad like node_modules already
is, and add it to ReadWritePaths= in the unit. Migrate the contents of
any pre-existing /var/lib/etherpad/plugin_packages symlink target back
in-tree on upgrade.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(deb): update assertions for in-tree plugin_packages + cover migration
The previous assertions in packaging/test-local.sh and the deb-package
workflow checked that /opt/etherpad/src/plugin_packages was a symlink to
/var/lib/etherpad/plugin_packages -- the layout this PR is removing.
They would have failed under the new postinst.
- Assert plugin_packages is a real directory (not a symlink), owned by
group etherpad with mode 2775, matching node_modules.
- After the happy-path /health check, simulate a pre-fix install by
recreating the symlink with a marker plugin, re-run the postinst via
dpkg-reconfigure, and assert the marker payload was migrated back
in-tree and the symlink is gone. Locks in regression coverage for the
upgrade path (ether/ep_comments_page#416).
* ci(deb): add ep_layout_trip_wire fixture to gate plugin_packages layout
Layout assertions alone don't actually exercise the failure mode from
ether/ep_comments_page#416: they confirm /opt/etherpad/src/plugin_packages
is a real directory but never load a plugin whose index.js does
require('ep_etherpad-lite/...') from the on-disk realpath.
Ship a tiny test plugin under packaging/test-fixtures/ep_layout_trip_wire
that exercises the four require() patterns from the bug report (eejs,
Settings, log4js, pad_utils) and emits a marker line from
expressCreateServer. Both packaging/test-local.sh and the deb-package
workflow now stage it into plugin_packages/.versions/, wire up the
toplevel + node_modules symlinks live-plugin-manager would create,
list it in installed_plugins.json, restart etherpad, and assert:
* marker line appears in the journal (every require resolved), and
* no "Cannot find module 'ep_etherpad-lite" appears anywhere.
Verified locally that the fixture loads under the in-tree layout
(marker present) and fails under a symlinked-out-of-tree layout
(marker absent, MODULE_NOT_FOUND in the log) -- so the gate catches
the regression in both directions.
* fix(deb): clean up runtime plugin artifacts on purge
With plugin_packages now living under /opt/etherpad/src/plugin_packages,
admin-installed plugins land in dpkg-unmanaged paths (the .versions/
stage that live-plugin-manager populates plus matching ep_* symlinks
under src/node_modules/). dpkg --purge would leave them behind because
the manifest never recorded them.
In postremove's purge branch: explicitly rm -rf plugin_packages and any
runtime ep_* symlinks in node_modules, then rm -rf the whole APP_DIR
as belt-and-braces against other runtime drift. Verified in a sandbox
that a staged ep_runtime@1.0.0 + node_modules/ep_runtime symlink are
both gone after purge runs.
Add post-purge assertions to both packaging/test-local.sh and the
deb-package workflow: /opt/etherpad/src/plugin_packages and
/var/lib/etherpad must not exist after dpkg --purge.
Addresses Qodo PR #7750 review item 3 (\"Purge cleanup misses
plugins\", Reliability).
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
1.7 KiB
Desktop File
56 lines
1.7 KiB
Desktop File
[Unit]
|
|
Description=Etherpad - real-time collaborative editor
|
|
Documentation=https://etherpad.org https://github.com/ether/etherpad
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=etherpad
|
|
Group=etherpad
|
|
WorkingDirectory=/opt/etherpad
|
|
EnvironmentFile=-/etc/default/etherpad
|
|
ExecStart=/usr/bin/etherpad
|
|
Restart=on-failure
|
|
RestartSec=5s
|
|
TimeoutStopSec=20s
|
|
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
SyslogIdentifier=etherpad
|
|
|
|
# --- Sandboxing ---------------------------------------------------------
|
|
NoNewPrivileges=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
PrivateTmp=true
|
|
PrivateDevices=true
|
|
ProtectKernelTunables=true
|
|
ProtectKernelModules=true
|
|
ProtectKernelLogs=true
|
|
ProtectControlGroups=true
|
|
ProtectHostname=true
|
|
ProtectClock=true
|
|
RestrictRealtime=true
|
|
RestrictSUIDSGID=true
|
|
RestrictNamespaces=true
|
|
LockPersonality=true
|
|
MemoryDenyWriteExecute=false # Node's JIT needs W+X mappings
|
|
SystemCallArchitectures=native
|
|
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
|
UMask=0027
|
|
|
|
# /opt/etherpad/src/{node_modules,plugin_packages} must be writable so the
|
|
# admin UI can install new plugins alongside the bundled deps. We expose
|
|
# both as writable paths under ProtectSystem=strict rather than symlinking
|
|
# plugin_packages outside the tree -- Node.js resolves symlinks to their
|
|
# realpath before walking node_modules, so a plugin installed under
|
|
# /var/lib/etherpad cannot reach the bundled ep_etherpad-lite in
|
|
# /opt/etherpad/node_modules and every require('ep_etherpad-lite/...')
|
|
# fails with MODULE_NOT_FOUND (see ether/ep_comments_page#416).
|
|
ReadWritePaths=/var/lib/etherpad /var/log/etherpad /etc/etherpad /opt/etherpad/src/node_modules /opt/etherpad/src/plugin_packages
|
|
|
|
LimitNOFILE=65536
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|