HeNjV:
d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlZddlm Z ddl
mZmZm
Z
mZ ddlmZmZ dd lmZ dd
l
mZ ddlmZ dZd
ZdZdZ eddh ZdZd Zd Zd Z ddZ!d Z"d Z#d Z$d Z%d Z&d Z'dS )u
clsudoers unix group management for lvemanager.
Operator upgrade note (CLOS-4576, F-37): ``add_unix_user_to_sudoers()`` now
fails closed when the ``clsudoers`` unix group has pre-existing members
outside the legitimate set (``admins()`` + the user being added +
package-owned members ``root`` / ``lvemanager``). Previously,
``groupadd -f`` was silently idempotent and any stowaway primary-GID
owners or ``gr_mem`` entries (left by manual admin steps or another
RPM's post-install) were quietly elevated to passwordless sudo for
selectorctl / cloudlinux-selector.
The allowlist explicitly includes ``root`` (implicit group membership in
many tools) and ``lvemanager`` (the package's runtime user, placed in
``clsudoers`` by the lvemanager RPM's ``%post`` for SPA's clsudo flow on
DirectAdmin/Plesk) — so the gate fires only on the F-37 attack signature
(a stowaway secondary member or a primary-GID-reuse owner), not on
legitimate package-installed state.
After upgrading to a build that ships this change, the first admin-add
operation on an AFFECTED host (one with non-package stowaways in the
group) raises an exception from ``_assert_sudoers_group_clean()``. To
recover, operators must either:
* remove the stowaway entries from the ``clsudoers`` group (audit with
``getent group clsudoers`` and ``awk -F: '$4== {print $1}' /etc/passwd``
for primary-GID owners), OR
* add those users via the panel's ``admins()`` interface (panel admin user
list) so they are recognised as legitimate members before retrying.
This is intentional — silent elevation of unaudited accounts is the
vulnerability being closed; reverting to ``groupadd -f`` idempotence is not
an acceptable workaround.
)print_function)division)absolute_importN)remount_proc)adminscpusers getCPNameis_reseller)
SysCtlConfSYSCTL_CL_CONF_FILE)Feature)is_panel_feature_supported)Clsudoadmin
clsupergid clsudoersi root
lvemanager)lvemanager_c dk rdS t v pt fdt D }|sdS t j j t k S # t $ r Y dS w xY w)u Return True if `name` is a known package-owned `clsudoers` member
(always-allowed name OR an lvemanager-package-family name) AND lives
in the system-UID range. Both conditions are required — a regular
user named `lvemanager_evil` (UID 5000) is NOT package-owned; an
unrelated system account named `daemon` (UID 2) is NOT package-owned
either. The gate exists to fire on the F-37 priv_group_reuse
signature; we only want to exempt the RPM's own post-install state.
r Tc 3 B K | ]} | V d S N)
startswith).0pnames I/opt/cloudlinux/venv/lib64/python3.11/site-packages/lvemanager/sudoers.py z(_is_system_owned_user..a s/ OO1DOOAOOOOOO F) _PACKAGE_OWNED_CLSUDOERS_MEMBERSany&_PACKAGE_OWNED_CLSUDOERS_NAME_PREFIXESpwdgetpwnampw_uid_SYSTEM_UID_MAXKeyError)r
is_known_names ` r _is_system_owned_userr) S s v~~t00 POOOO(NOOOOO u|D!!(O;; uus !A
A('A(c F t j dd| |g }|dk rdS dS )zAdd user to given unix group/usr/bin/gpasswdz-ar FT
subprocesscall user_name
group_nameretcodes r _add_user_to_groupr3 m s. o14JOPPG!||u4r c F t j dd| |g }|dk rdS dS )Nr+ z-dr FTr, r/ s r _remove_user_from_groupr5 v s. o14JOPPG!||u4r c , t j | j }n# t $ r Y dS w xY w|sdS t t }t t
}n-# t $ r }t d| d| Y d}~dS d}~ww xY w|st d| z dS t | |z }|D ]S}||v s||vr t | rn# t $ r Y )w xY wt d|d| d t || TdS )a6
Remove panel end-users (tenants) from a privileged group.
CLOS-4594 (F-54/F-56): membership of the fs.proc_super_gid group (clsupergid) is
privileged and must be limited to legitimate elevated actors. lve-utils
(clsupergid_process / clsetuplib) may reuse a pre-existing group of that name, so we
reconcile its membership here. lve-utils cannot tell a legitimate panel admin
(a regular uid >= UID_MIN login) from a tenant; lvemanager can, because it knows the
authoritative cpapi sets. Here we remove only CONFIRMED panel end-users
(cpapi.cpusers() that are neither admins nor resellers) -- a tenant must never be a
member. This deliberately does NOT use a uid threshold (which is what made the
earlier in-tool attempt false-abort on real cPanel/DA/Plesk hosts by flagging legitimate
panel admins as untrusted).
Fail-safe rules (must never strip a legitimate elevated actor):
* If cpapi enumeration raises, OR admins() returns an EMPTY set, do nothing and return.
An empty admin set cannot be distinguished from a failed-open read (e.g. DirectAdmin
admins() returns set() without raising when admin.list is empty/truncated mid-write,
while DA admins are simultaneously listed by cpusers()); reconciling against it would
strip every real admin from the group -- the very regression this fix exists to avoid.
* Admins (admins()), the just-added admin (keep), and resellers (is_reseller()) are
elevated actors and are never removed. Service accounts (nagios/mailman/polkitd, the
lvemanager system user, ...) are not panel users, so they are never candidates.
:param group_name: the group to reconcile (the configured proc-super group)
:param keep: extra usernames to preserve regardless (e.g. the admin being added right now)
NzCWARNING: could not enumerate panel admins/users to validate group 'z': zSWARNING: admins() returned no admins; skipping tenant eviction for '%s' (fail-safe)zWARNING: removing panel user 'z' from privileged group 'z
' (CLOS-4594))grpgetgrnamgr_memr' setr r Exceptionprintr
r5 )r1 keepmembers admin_set
tenant_setekeep_setmembers r _remove_tenants_from_grouprE } s 8,z**1 MM ^^
QQ ! ! ! c 4yy9$H
4
4Xz!9!9 6""
H
% &