mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-06-22 09:56:30 +00:00

* Improve gdbus error handling * Fix logging type * Detect no dbus * Fix issue with complex * Update hassio/dbus/__init__.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Update hassio/dbus/hostname.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Update hassio/dbus/rauc.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Update hassio/dbus/systemd.py Co-Authored-By: Franck Nijhof <frenck@frenck.nl> * Fix black
121 lines
4.1 KiB
Python
121 lines
4.1 KiB
Python
"""AppArmor control for host."""
|
|
import logging
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from ..coresys import CoreSysAttributes
|
|
from ..exceptions import DBusError, HostAppArmorError
|
|
from ..utils.apparmor import validate_profile
|
|
|
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
|
|
|
SYSTEMD_SERVICES = {"hassos-apparmor.service", "hassio-apparmor.service"}
|
|
|
|
|
|
class AppArmorControl(CoreSysAttributes):
|
|
"""Handle host AppArmor controls."""
|
|
|
|
def __init__(self, coresys):
|
|
"""Initialize host power handling."""
|
|
self.coresys = coresys
|
|
self._profiles = set()
|
|
self._service = None
|
|
|
|
@property
|
|
def available(self):
|
|
"""Return True if AppArmor is available on host."""
|
|
return self._service is not None
|
|
|
|
def exists(self, profile):
|
|
"""Return True if a profile exists."""
|
|
return profile in self._profiles
|
|
|
|
async def _reload_service(self):
|
|
"""Reload internal service."""
|
|
try:
|
|
await self.sys_host.services.reload(self._service)
|
|
except DBusError as err:
|
|
_LOGGER.error("Can't reload %s: %s", self._service, err)
|
|
|
|
def _get_profile(self, profile_name):
|
|
"""Get a profile from AppArmor store."""
|
|
if profile_name not in self._profiles:
|
|
_LOGGER.error("Can't find %s for removing", profile_name)
|
|
raise HostAppArmorError()
|
|
return Path(self.sys_config.path_apparmor, profile_name)
|
|
|
|
async def load(self):
|
|
"""Load available profiles."""
|
|
for content in self.sys_config.path_apparmor.iterdir():
|
|
if not content.is_file():
|
|
continue
|
|
self._profiles.add(content.name)
|
|
|
|
# Is connected with systemd?
|
|
_LOGGER.info("Load AppArmor Profiles: %s", self._profiles)
|
|
for service in SYSTEMD_SERVICES:
|
|
if not self.sys_host.services.exists(service):
|
|
continue
|
|
self._service = service
|
|
|
|
# Load profiles
|
|
if self.available:
|
|
await self._reload_service()
|
|
else:
|
|
_LOGGER.info("AppArmor is not enabled on host")
|
|
|
|
async def load_profile(self, profile_name, profile_file):
|
|
"""Load/Update a new/exists profile into AppArmor."""
|
|
if not validate_profile(profile_name, profile_file):
|
|
_LOGGER.error("Profile is not valid with name %s", profile_name)
|
|
raise HostAppArmorError()
|
|
|
|
# Copy to AppArmor folder
|
|
dest_profile = Path(self.sys_config.path_apparmor, profile_name)
|
|
try:
|
|
shutil.copy(profile_file, dest_profile)
|
|
except OSError as err:
|
|
_LOGGER.error("Can't copy %s: %s", profile_file, err)
|
|
raise HostAppArmorError() from None
|
|
|
|
# Load profiles
|
|
_LOGGER.info("Add or Update AppArmor profile: %s", profile_name)
|
|
self._profiles.add(profile_name)
|
|
if self.available:
|
|
await self._reload_service()
|
|
|
|
async def remove_profile(self, profile_name):
|
|
"""Remove a AppArmor profile."""
|
|
profile_file = self._get_profile(profile_name)
|
|
|
|
# Only remove file
|
|
if not self.available:
|
|
try:
|
|
profile_file.unlink()
|
|
except OSError as err:
|
|
_LOGGER.error("Can't remove profile: %s", err)
|
|
raise HostAppArmorError()
|
|
return
|
|
|
|
# Marks als remove and start host process
|
|
remove_profile = Path(self.sys_config.path_apparmor, "remove", profile_name)
|
|
try:
|
|
profile_file.rename(remove_profile)
|
|
except OSError as err:
|
|
_LOGGER.error("Can't mark profile as remove: %s", err)
|
|
raise HostAppArmorError()
|
|
|
|
_LOGGER.info("Remove AppArmor profile: %s", profile_name)
|
|
self._profiles.remove(profile_name)
|
|
await self._reload_service()
|
|
|
|
def backup_profile(self, profile_name, backup_file):
|
|
"""Backup A profile into a new file."""
|
|
profile_file = self._get_profile(profile_name)
|
|
|
|
try:
|
|
shutil.copy(profile_file, backup_file)
|
|
except OSError as err:
|
|
_LOGGER.error("Can't backup profile %s: %s", profile_name, err)
|
|
raise HostAppArmorError()
|