"""Schema reference for `integration.conf`.
Values are always returned as strings by `BaseConfig.get`. Type parsing
(int, int list, bool, rule-id map) is the caller's responsibility. The
format columns below are advisory conventions shared between producers
(wizard, installers, panel templates) and consumers, not runtime-enforced
schemas.
Validation today is narrow: `other/compatibility-check.sh` validates INI
syntax, `[paths] ui_path`, and the `panel_info` script at install time;
integration-script JSON outputs are validated at runtime via Cerberus
schemas under `panels/generic/users_script_schemas/`. All other keys are
read on demand and trusted.
Non-obvious `.get()` behavior:
- Missing file returns `None` (ConfigParser.read silently ignores missing
paths; use `BaseConfig.exists()` to distinguish).
- Malformed INI propagates `configparser.Error`; `.get()` only catches
`KeyError`.
- Section names are case-sensitive (ConfigParser default); option names
are case-insensitive. Match the casing documented below.
Sections
--------
`[PAM]`
- `SERVICE_NAME` (str): PAM service used for UI login
authentication.
`[panel]`
- `type` (str, `cpanel`|`plesk`|`directadmin`|`generic`):
master switch for panel class selection.
`[panel_ports]`
Comma-separated integer lists (e.g. `2082, 2095`). Empty or absent
means "no ports of this class".
- `http_ports`: ports the panel listens on for HTTP admin traffic.
- `https_ports`: HTTPS equivalents.
- `webshield_protected_ports`: subset of the above that WebShield
should protect.
`[panel_login]`
- `ossec_rules` (str, comma-separated `rule_id:bool` pairs, e.g.
`11006:false,11009:true`): OSSEC rule IDs to auto-whitelist for
panel-login events.
`[features]`
Boolean feature flags. Conventional values: `true` / `false`
(case-insensitive).
- `webshield_enabled` (bool)
- `cphulk_enabled` (bool)
`[smtp]`
- `allow_users` (str, comma-separated list of usernames): system
users allowed to send SMTP when the SMTP block feature is active.
- `conflict_config_file` (str, absolute path): panel config file
whose value toggles the SMTP-block conflict check.
- `conflict_config_key` (str): key inside `conflict_config_file`
that holds the conflicting setting.
`[web_server]`
- `server_type` (str, `apache`|`nginx`|...): web server in use.
- `modsec_audit_log` (str, absolute path): ModSecurity audit log
file.
- `modsec_audit_logdir` (str, absolute path): ModSecurity audit log
directory (concurrent writer layout).
- `graceful_restart_script` (str, command string): whitespace-split
command used to gracefully restart the web server
(e.g. `/usr/bin/systemctl restart apache2`).
- `config_test_script` (str, command string): whitespace-split
command used to validate the web server configuration before
reload (e.g. `/usr/sbin/apache2ctl -t`).
`[integration_scripts]`
Values are absolute paths to scripts executed by the agent as root.
Populate with trusted, integrator-controlled paths only; do not
interpolate user-controlled data.
- `users` (str path): emits JSON user list.
- `domains` (str path): emits JSON domain -> owner mapping.
- `admins` (str path): emits JSON admin list.
- `panel_info` (str path): emits JSON `{name, version, ...}`
describing the panel.
- `modsec_domain_config_script` (str path): emits per-domain
ModSecurity overrides.
`[paths]`
- `ui_path` (str, absolute path): document root for the standalone
UI.
- `ui_path_owner` (str, `user:group`): owner applied to UI files
during install.
`[malware]`
- `basedir` (str, **whitespace-separated** paths): base directories
scanned for malware. Note the separator differs from port lists
(whitespace here, comma for port lists).
`[metadata]`
- `schema_version` (int): schema version number.
- `created_by` (str, `wizard`|`agent`|`manual`): who wrote the file;
useful for support triage.
"""
import os
from typing import Optional
from defence360agent.application.determine_hosting_panel import GP_FILE
class BaseConfig:
@classmethod
def exists(cls):
return os.path.exists(cls._conf_path)
@classmethod
def to_dict(cls):
from configparser import ConfigParser
integration_conf = ConfigParser()
integration_conf.read(cls._conf_path)
return integration_conf
@classmethod
def get(cls, section: str, option: str) -> Optional[str]:
"""
Return *option* value in *section* in config if exist,
None otherwise.
"""
try:
return cls.to_dict()[section][option]
except KeyError:
return None
class IntegrationConfig(BaseConfig):
_conf_path = GP_FILE
class ClIntegrationConfig(BaseConfig):
_conf_path = "/opt/cpvendor/etc/integration.ini"