Skip to content

Commit d3e7df3

Browse files
committed
Remove CodeNotary specific integrity checking
The current code is specific to how CodeNotary was doing integrity checking. A future integrity checking mechanism likely will work differently (e.g. through EROFS based containers). Remove the current code to make way for a future implementation.
1 parent 4b24255 commit d3e7df3

File tree

15 files changed

+14
-633
lines changed

15 files changed

+14
-633
lines changed

supervisor/addons/addon.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,13 +1510,6 @@ def _restore_data():
15101510
_LOGGER.info("Finished restore for add-on %s", self.slug)
15111511
return wait_for_start
15121512

1513-
def check_trust(self) -> Awaitable[None]:
1514-
"""Calculate Addon docker content trust.
1515-
1516-
Return Coroutine.
1517-
"""
1518-
return self.instance.check_trust()
1519-
15201513
@Job(
15211514
name="addon_restart_after_problem",
15221515
throttle_period=WATCHDOG_THROTTLE_PERIOD,

supervisor/api/security.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,14 @@
11
"""Init file for Supervisor Security RESTful API."""
22

3-
import asyncio
4-
import logging
53
from typing import Any
64

75
from aiohttp import web
8-
import attr
96
import voluptuous as vol
107

118
from ..const import ATTR_CONTENT_TRUST, ATTR_FORCE_SECURITY, ATTR_PWNED
129
from ..coresys import CoreSysAttributes
1310
from .utils import api_process, api_validate
1411

15-
_LOGGER: logging.Logger = logging.getLogger(__name__)
16-
1712
# pylint: disable=no-value-for-parameter
1813
SCHEMA_OPTIONS = vol.Schema(
1914
{
@@ -54,6 +49,9 @@ async def options(self, request: web.Request) -> None:
5449

5550
@api_process
5651
async def integrity_check(self, request: web.Request) -> dict[str, Any]:
57-
"""Run backend integrity check."""
58-
result = await asyncio.shield(self.sys_security.integrity_check())
59-
return attr.asdict(result)
52+
"""Run backend integrity check.
53+
54+
CodeNotary integrity checking has been removed. This endpoint now returns
55+
an error indicating the feature is currently non-functional.
56+
"""
57+
return {"error": "No integrity checking available"}

supervisor/docker/addon.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -835,16 +835,6 @@ async def stop(self, remove_container: bool = True) -> None:
835835
):
836836
self.sys_resolution.dismiss_issue(self.addon.device_access_missing_issue)
837837

838-
async def _validate_trust(self, image_id: str) -> None:
839-
"""Validate trust of content."""
840-
if not self.addon.signed:
841-
return
842-
843-
checksum = image_id.partition(":")[2]
844-
return await self.sys_security.verify_content(
845-
cast(str, self.addon.codenotary), checksum
846-
)
847-
848838
@Job(
849839
name="docker_addon_hardware_events",
850840
conditions=[JobCondition.OS_AGENT],

supervisor/docker/homeassistant.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
import re
77

8-
from awesomeversion import AwesomeVersion, AwesomeVersionCompareException
8+
from awesomeversion import AwesomeVersion
99
from docker.types import Mount
1010

1111
from ..const import LABEL_MACHINE
@@ -244,13 +244,3 @@ def is_initialize(self) -> Awaitable[bool]:
244244
self.image,
245245
self.sys_homeassistant.version,
246246
)
247-
248-
async def _validate_trust(self, image_id: str) -> None:
249-
"""Validate trust of content."""
250-
try:
251-
if self.version in {None, LANDINGPAGE} or self.version < _VERIFY_TRUST:
252-
return
253-
except AwesomeVersionCompareException:
254-
return
255-
256-
await super()._validate_trust(image_id)

supervisor/docker/interface.py

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -374,17 +374,7 @@ async def process_pull_image_log(reference: PullLogEntry) -> None:
374374
platform=MAP_ARCH[image_arch],
375375
)
376376

377-
# Validate content
378-
try:
379-
await self._validate_trust(cast(str, docker_image.id))
380-
except CodeNotaryError:
381-
with suppress(docker.errors.DockerException):
382-
await self.sys_run_in_executor(
383-
self.sys_docker.images.remove,
384-
image=f"{image}:{version!s}",
385-
force=True,
386-
)
387-
raise
377+
# CodeNotary content trust validation has been removed
388378

389379
# Tag latest
390380
if latest:
@@ -755,24 +745,3 @@ def run_inside(self, command: str) -> Awaitable[CommandReturn]:
755745
return self.sys_run_in_executor(
756746
self.sys_docker.container_run_inside, self.name, command
757747
)
758-
759-
async def _validate_trust(self, image_id: str) -> None:
760-
"""Validate trust of content."""
761-
checksum = image_id.partition(":")[2]
762-
return await self.sys_security.verify_own_content(checksum)
763-
764-
@Job(
765-
name="docker_interface_check_trust",
766-
on_condition=DockerJobError,
767-
concurrency=JobConcurrency.GROUP_REJECT,
768-
)
769-
async def check_trust(self) -> None:
770-
"""Check trust of exists Docker image."""
771-
try:
772-
image = await self.sys_run_in_executor(
773-
self.sys_docker.images.get, f"{self.image}:{self.version!s}"
774-
)
775-
except (docker.errors.DockerException, requests.RequestException):
776-
return
777-
778-
await self._validate_trust(cast(str, image.id))

supervisor/homeassistant/core.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -420,13 +420,6 @@ def logs(self) -> Awaitable[bytes]:
420420
"""
421421
return self.instance.logs()
422422

423-
def check_trust(self) -> Awaitable[None]:
424-
"""Calculate HomeAssistant docker content trust.
425-
426-
Return Coroutine.
427-
"""
428-
return self.instance.check_trust()
429-
430423
async def stats(self) -> DockerStats:
431424
"""Return stats of Home Assistant."""
432425
try:

supervisor/plugins/base.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,6 @@ def in_progress(self) -> bool:
7676
"""Return True if a task is in progress."""
7777
return self.instance.in_progress
7878

79-
def check_trust(self) -> Awaitable[None]:
80-
"""Calculate plugin docker content trust.
81-
82-
Return Coroutine.
83-
"""
84-
return self.instance.check_trust()
85-
8679
def logs(self) -> Awaitable[bytes]:
8780
"""Get docker plugin logs.
8881
Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
"""Evaluation class for Content Trust."""
22

3-
import errno
43
import logging
54
from pathlib import Path
65

76
from ...const import CoreState
87
from ...coresys import CoreSys
9-
from ...exceptions import CodeNotaryError, CodeNotaryUntrusted
10-
from ...utils.codenotary import calc_checksum_path_sourcecode
11-
from ..const import ContextType, IssueType, UnhealthyReason, UnsupportedReason
8+
from ..const import UnsupportedReason
129
from .base import EvaluateBase
1310

1411
_SUPERVISOR_SOURCE = Path("/usr/src/supervisor/supervisor")
@@ -44,29 +41,4 @@ async def evaluate(self) -> bool:
4441
_LOGGER.warning("Disabled content-trust, skipping evaluation")
4542
return False
4643

47-
# Calculate sume of the sourcecode
48-
try:
49-
checksum = await self.sys_run_in_executor(
50-
calc_checksum_path_sourcecode, _SUPERVISOR_SOURCE
51-
)
52-
except OSError as err:
53-
if err.errno == errno.EBADMSG:
54-
self.sys_resolution.add_unhealthy_reason(
55-
UnhealthyReason.OSERROR_BAD_MESSAGE
56-
)
57-
58-
self.sys_resolution.create_issue(
59-
IssueType.CORRUPT_FILESYSTEM, ContextType.SYSTEM
60-
)
61-
_LOGGER.error("Can't calculate checksum of source code: %s", err)
62-
return False
63-
64-
# Validate checksum
65-
try:
66-
await self.sys_security.verify_own_content(checksum)
67-
except CodeNotaryUntrusted:
68-
return True
69-
except CodeNotaryError:
70-
pass
71-
7244
return False

supervisor/security/module.py

Lines changed: 1 addition & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,10 @@
1111
FILE_HASSIO_SECURITY,
1212
)
1313
from ..coresys import CoreSys, CoreSysAttributes
14-
from ..exceptions import (
15-
CodeNotaryError,
16-
CodeNotaryUntrusted,
17-
PwnedError,
18-
SecurityJobError,
19-
)
20-
from ..jobs.const import JobConcurrency
21-
from ..jobs.decorator import Job, JobCondition
22-
from ..resolution.const import ContextType, IssueType, SuggestionType
23-
from ..utils.codenotary import cas_validate
14+
from ..exceptions import PwnedError
2415
from ..utils.common import FileConfiguration
2516
from ..utils.pwned import check_pwned_password
2617
from ..validate import SCHEMA_SECURITY_CONFIG
27-
from .const import ContentTrustResult, IntegrityResult
2818

2919
_LOGGER: logging.Logger = logging.getLogger(__name__)
3020

@@ -67,30 +57,6 @@ def pwned(self, value: bool) -> None:
6757
"""Set pwned is enabled/disabled."""
6858
self._data[ATTR_PWNED] = value
6959

70-
async def verify_content(self, signer: str, checksum: str) -> None:
71-
"""Verify content on CAS."""
72-
if not self.content_trust:
73-
_LOGGER.warning("Disabled content-trust, skip validation")
74-
return
75-
76-
try:
77-
await cas_validate(signer, checksum)
78-
except CodeNotaryUntrusted:
79-
raise
80-
except CodeNotaryError:
81-
if self.force:
82-
raise
83-
self.sys_resolution.create_issue(
84-
IssueType.TRUST,
85-
ContextType.SYSTEM,
86-
suggestions=[SuggestionType.EXECUTE_INTEGRITY],
87-
)
88-
return
89-
90-
async def verify_own_content(self, checksum: str) -> None:
91-
"""Verify content from HA org."""
92-
return await self.verify_content("[email protected]", checksum)
93-
9460
async def verify_secret(self, pwned_hash: str) -> None:
9561
"""Verify pwned state of a secret."""
9662
if not self.pwned:
@@ -103,73 +69,3 @@ async def verify_secret(self, pwned_hash: str) -> None:
10369
if self.force:
10470
raise
10571
return
106-
107-
@Job(
108-
name="security_manager_integrity_check",
109-
conditions=[JobCondition.INTERNET_SYSTEM],
110-
on_condition=SecurityJobError,
111-
concurrency=JobConcurrency.REJECT,
112-
)
113-
async def integrity_check(self) -> IntegrityResult:
114-
"""Run a full system integrity check of the platform.
115-
116-
We only allow to install trusted content.
117-
This is a out of the band manual check.
118-
"""
119-
result: IntegrityResult = IntegrityResult()
120-
if not self.content_trust:
121-
_LOGGER.warning(
122-
"Skipping integrity check, content_trust is globally disabled"
123-
)
124-
return result
125-
126-
# Supervisor
127-
try:
128-
await self.sys_supervisor.check_trust()
129-
result.supervisor = ContentTrustResult.PASS
130-
except CodeNotaryUntrusted:
131-
result.supervisor = ContentTrustResult.ERROR
132-
self.sys_resolution.create_issue(IssueType.TRUST, ContextType.SUPERVISOR)
133-
except CodeNotaryError:
134-
result.supervisor = ContentTrustResult.FAILED
135-
136-
# Core
137-
try:
138-
await self.sys_homeassistant.core.check_trust()
139-
result.core = ContentTrustResult.PASS
140-
except CodeNotaryUntrusted:
141-
result.core = ContentTrustResult.ERROR
142-
self.sys_resolution.create_issue(IssueType.TRUST, ContextType.CORE)
143-
except CodeNotaryError:
144-
result.core = ContentTrustResult.FAILED
145-
146-
# Plugins
147-
for plugin in self.sys_plugins.all_plugins:
148-
try:
149-
await plugin.check_trust()
150-
result.plugins[plugin.slug] = ContentTrustResult.PASS
151-
except CodeNotaryUntrusted:
152-
result.plugins[plugin.slug] = ContentTrustResult.ERROR
153-
self.sys_resolution.create_issue(
154-
IssueType.TRUST, ContextType.PLUGIN, reference=plugin.slug
155-
)
156-
except CodeNotaryError:
157-
result.plugins[plugin.slug] = ContentTrustResult.FAILED
158-
159-
# Add-ons
160-
for addon in self.sys_addons.installed:
161-
if not addon.signed:
162-
result.addons[addon.slug] = ContentTrustResult.UNTESTED
163-
continue
164-
try:
165-
await addon.check_trust()
166-
result.addons[addon.slug] = ContentTrustResult.PASS
167-
except CodeNotaryUntrusted:
168-
result.addons[addon.slug] = ContentTrustResult.ERROR
169-
self.sys_resolution.create_issue(
170-
IssueType.TRUST, ContextType.ADDON, reference=addon.slug
171-
)
172-
except CodeNotaryError:
173-
result.addons[addon.slug] = ContentTrustResult.FAILED
174-
175-
return result

supervisor/supervisor.py

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
from .docker.stats import DockerStats
2424
from .docker.supervisor import DockerSupervisor
2525
from .exceptions import (
26-
CodeNotaryError,
27-
CodeNotaryUntrusted,
2826
DockerError,
2927
HostAppArmorError,
3028
SupervisorAppArmorError,
@@ -35,7 +33,6 @@
3533
from .jobs.const import JobCondition, JobThrottle
3634
from .jobs.decorator import Job
3735
from .resolution.const import ContextType, IssueType, UnhealthyReason
38-
from .utils.codenotary import calc_checksum
3936
from .utils.sentry import async_capture_exception
4037

4138
_LOGGER: logging.Logger = logging.getLogger(__name__)
@@ -148,20 +145,6 @@ async def update_apparmor(self) -> None:
148145
_LOGGER.error,
149146
) from err
150147

151-
# Validate
152-
try:
153-
await self.sys_security.verify_own_content(calc_checksum(data))
154-
except CodeNotaryUntrusted as err:
155-
raise SupervisorAppArmorError(
156-
"Content-Trust is broken for the AppArmor profile fetch!",
157-
_LOGGER.critical,
158-
) from err
159-
except CodeNotaryError as err:
160-
raise SupervisorAppArmorError(
161-
f"CodeNotary error while processing AppArmor fetch: {err!s}",
162-
_LOGGER.error,
163-
) from err
164-
165148
# Load
166149
temp_dir: TemporaryDirectory | None = None
167150

@@ -261,13 +244,6 @@ def logs(self) -> Awaitable[bytes]:
261244
"""
262245
return self.instance.logs()
263246

264-
def check_trust(self) -> Awaitable[None]:
265-
"""Calculate Supervisor docker content trust.
266-
267-
Return Coroutine.
268-
"""
269-
return self.instance.check_trust()
270-
271247
async def stats(self) -> DockerStats:
272248
"""Return stats of Supervisor."""
273249
try:

0 commit comments

Comments
 (0)