[Unit] Description=Imunify360 agent Before=cagefs.service After=imunify360.service network.target iptables.service firewalld.service systemd-modules-load.service Wants=imunify360.service imunify360-agent.socket imunify360-agent-user.socket # Service will NOT start if this file exists ConditionPathExists=!/var/lib/rpm-state/imunify360-transaction-in-progress [Service] CPUAccounting=true MemoryAccounting=true BlockIOAccounting=true Slice=Imunify-agent_non_resident.slice Environment=PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=upb Environment=LANG=en_US.UTF-8 Environment=LC_ALL=en_US.UTF-8 Environment=PYTHONNOUSERSITE=1 Environment=SQLITE_TMPDIR=/var/imunify360/tmp Environment=I360_SOCKET_ACTIVATION=1 Type=simple ExecStartPre=/usr/share/imunify360/scripts/set-service-resources.sh imunify360-agent.service 50 50 ExecStart=/opt/imunify360/venv/bin/python3 -m im360.run ExecStartPost=/bin/bash -c "echo $MAINPID > /var/run/imunify360-agent.pid" ExecStartPost=/bin/systemctl restart imunify360-resource-unlock@imunify360-agent.timer PIDFile=/var/run/imunify360-agent.pid #TODO: must be not less than defence360agent/cli/server.py:stop(seconds=8) TimeoutStartSec=900 TimeoutStopSec=60 RestartSec=5 StartLimitInterval=600s StartLimitBurst=15 # Orphans child processes instead of killing them when the main process is shut down. KillMode=process NoNewPrivileges=true CapabilityBoundingSet=CAP_BPF CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_PERFMON CAP_SETGID CAP_SETUID CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYS_RESOURCE AmbientCapabilities=CAP_BPF CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_KILL CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_PERFMON CAP_SETGID CAP_SETUID CAP_SYS_ADMIN CAP_SYS_PTRACE CAP_SYS_RESOURCE ProtectSystem=true # This unit ships the same Python package as imunify360.service; mirror # its ReadWritePaths so the agent's /etc/ writers (config, integration, # whitelist/blacklist, modsec, cron.d, repo files) keep working. ReadWritePaths=/etc/sysconfig/imunify360 ReadWritePaths=/etc/imunify360 ReadWritePaths=/etc/imunify-agent-proxy ReadWritePaths=/etc/cron.d ReadWritePaths=-/etc/imunify360-wafd ReadWritePaths=-/etc/imunify360-webshield ReadWritePaths=-/etc/csf ReadWritePaths=-/etc/httpd/conf.d ReadWritePaths=-/etc/httpd/conf/plesk.conf.d ReadWritePaths=-/etc/httpd/conf/extra ReadWritePaths=-/etc/apache2/conf.d ReadWritePaths=-/etc/apache2/conf-enabled ReadWritePaths=-/etc/apache2/plesk.conf.d ReadWritePaths=-/etc/modsecurity.d ReadWritePaths=-/etc/yum.repos.d ReadWritePaths=-/etc/apt/sources.list.d ReadWritePaths=-/usr/share/i360-php-opts ReadWritePaths=-/etc/httpd/conf/modsecurity.d ReadWritePaths=-/usr/local/directadmin ReadWritePaths=-/usr/local/cpanel ReadWritePaths=-/etc/cagefs ReadWritePaths=-/var/cagefs ReadWritePaths=-/usr/share/cagefs # Separate top-level dir (not under /usr/share/cagefs); cagefsctl's # check_skeleton() chmods it and hits EROFS under ProtectSystem= otherwise (DEF-47738). ReadWritePaths=-/usr/share/cagefs-skeleton ReadWritePaths=-/usr/local/psa/admin/plib/modules/imunify360 # Plesk runtime state — notification log written by send-notifications.php # (/usr/local/psa/var/modules/imunify360/imunify360-local.log) and the # plesk-sendmail spool/tempfile dir. ProtectSystem=true bind-mounts /usr # read-only and CAP_DAC_OVERRIDE cannot bypass a mount-layer RO, so the # Plesk notification hook fails with EACCES without this entry. ReadWritePaths=-/usr/local/psa/var # Webuzo keeps its Apache/modsec tree under /usr/local/apps; mirror the # carve-out (see imunify360.service) so webuzo writers are not blocked by # read-only /usr. ReadWritePaths=-/usr/local/apps # LiteSpeed keeps its config tree under /usr/local/lsws, including the # per-domain .d/modsec.conf files the agent rewrites via # integration.sh rewrite-domain-configs. ProtectSystem=true makes /usr # read-only, so without this carve-out the per-domain modsec rewrite fails # with EROFS. ReadWritePaths=-/usr/local/lsws # PrivateTmp= deliberately not set — see imunify360.service for the # rationale (shared /tmp is required for the Sample backup backend # fixture and for inotify-watching user-writable /tmp on hosts). [Install] WantedBy=multi-user.target