diff --git a/dangerzone/container_utils.py b/dangerzone/container_utils.py index 9cb79e0..8882ac5 100644 --- a/dangerzone/container_utils.py +++ b/dangerzone/container_utils.py @@ -11,7 +11,6 @@ from .settings import Settings from .util import get_resource_path, get_subprocess_startupinfo OLD_CONTAINER_NAME = "dangerzone.rocks/dangerzone" -CONTAINER_NAME = "ghcr.io/freedomofpress/dangerzone/dangerzone" log = logging.getLogger(__name__) @@ -110,6 +109,7 @@ def list_image_tags() -> List[str]: and which image ID does the "latest" tag point to. """ runtime = Runtime() + container_name = expected_image_name() return ( subprocess.check_output( [ @@ -118,7 +118,7 @@ def list_image_tags() -> List[str]: "list", "--format", "{{ .Tag }}", - CONTAINER_NAME, + container_name, ], text=True, startupinfo=get_subprocess_startupinfo(), @@ -154,10 +154,11 @@ def delete_image_tag(tag: str) -> None: ) -def load_image_tarball() -> None: +def load_image_tarball(tarball_path: Optional[Path] = None) -> None: runtime = Runtime() log.info("Installing Dangerzone container image...") - tarball_path = get_resource_path("container.tar") + if not tarball_path: + tarball_path = get_resource_path("container.tar") try: res = subprocess.run( [str(runtime.path), "load", "-i", str(tarball_path)], @@ -185,17 +186,18 @@ def load_image_tarball() -> None: # `share/image-id.txt` and delete the incorrect tag. # # [1] https://github.com/containers/podman/issues/16490 - if runtime.name == "podman" and get_runtime_version(runtime) == (3, 4): - expected_tag = get_expected_tag() - bad_tag = f"localhost/{expected_tag}:latest" - good_tag = f"{CONTAINER_NAME}:{expected_tag}" + # if runtime.name == "podman" and get_runtime_version(runtime) == (3, 4): + # FIXME image-id.txt has been removed, this needs to be adapted. + # expected_tag = get_expected_tag() + # bad_tag = f"localhost/{expected_tag}:latest" + # good_tag = f"{CONTAINER_NAME}:{expected_tag}" - log.debug( - f"Dangerzone images loaded in Podman v3.4 usually have an invalid tag." - " Fixing it..." - ) - add_image_tag(bad_tag, good_tag) - delete_image_tag(bad_tag) + # log.debug( + # f"Dangerzone images loaded in Podman v3.4 usually have an invalid tag." + # " Fixing it..." + # ) + # add_image_tag(bad_tag, good_tag) + # delete_image_tag(bad_tag) def tag_image_by_digest(digest: str, tag: str) -> None: @@ -228,6 +230,11 @@ def get_image_id_by_digest(digest: str) -> str: return process.stdout.decode().strip().split("\n")[0] +def expected_image_name(): + image_name_path = get_resource_path("image-name.txt") + return image_name_path.read_text().strip("\n") + + def container_pull( image: str, manifest_digest: str, callback: Optional[Callable] = None ): @@ -253,10 +260,11 @@ def container_pull( ) -def get_local_image_digest(image: str) -> str: +def get_local_image_digest(image: Optional[str] = None) -> str: """ Returns a image hash from a local image name """ + image = image or expected_image_name() # Get the image hash from the "podman images" command. # It's not possible to use "podman inspect" here as it # returns the digest of the architecture-bound image diff --git a/dangerzone/isolation_provider/container.py b/dangerzone/isolation_provider/container.py index 317315b..a1a3eb6 100644 --- a/dangerzone/isolation_provider/container.py +++ b/dangerzone/isolation_provider/container.py @@ -7,7 +7,7 @@ import sys from typing import Callable, List, Optional, Tuple from .. import container_utils, errors -from ..container_utils import CONTAINER_NAME, Runtime +from ..container_utils import Runtime from ..document import Document from ..updater import ( DEFAULT_PUBKEY_LOCATION, @@ -127,24 +127,20 @@ class Container(IsolationProvider): if not installed_tags: install_local_container_tar() else: + container_name = container_utils.expected_image_name() update_available, image_digest = is_update_available( - CONTAINER_NAME, + container_name, DEFAULT_PUBKEY_LOCATION, ) if update_available and image_digest: log.debug("Upgrading container image to %s", image_digest) - upgrade_container_image( - CONTAINER_NAME, - image_digest, - DEFAULT_PUBKEY_LOCATION, - callback=callback, - ) + upgrade_container_image(image_digest, callback=callback) else: log.debug("No update available for the container.") if not installed_tags: install_local_container_tar() try: - verify_local_image(CONTAINER_NAME) + verify_local_image() except UpdaterError: # delete_image() if last_try: @@ -236,9 +232,11 @@ class Container(IsolationProvider): name: str, ) -> subprocess.Popen: runtime = Runtime() + container_name = container_utils.expected_image_name() - image_digest = container_utils.get_local_image_digest(CONTAINER_NAME) - verify_local_image(CONTAINER_NAME) + # FIXME: Image digest is also computed inside the verify_local_image + image_digest = container_utils.get_local_image_digest() + verify_local_image() security_args = self.get_runtime_security_args() debug_args = [] if self.debug: @@ -247,7 +245,7 @@ class Container(IsolationProvider): enable_stdin = ["-i"] set_name = ["--name", name] prevent_leakage_args = ["--rm"] - image_name = [CONTAINER_NAME + "@sha256:" + image_digest] + image_name = [container_name + "@sha256:" + image_digest] args = ( ["run"] + security_args diff --git a/dangerzone/updater/signatures.py b/dangerzone/updater/signatures.py index e8a1a24..ab0d900 100644 --- a/dangerzone/updater/signatures.py +++ b/dangerzone/updater/signatures.py @@ -287,7 +287,7 @@ def upgrade_container_image_airgapped( archive.add(Path(tmpdir) / "oci-layout", arcname="oci-layout") archive.add(Path(tmpdir) / "blobs", arcname="blobs") - runtime.load_image_tarball_from_tar(temporary_tar.name) + runtime.load_image_tarball(temporary_tar.name) runtime.tag_image_by_digest(image_digest, image_name) store_signatures(signatures, image_digest, pubkey) @@ -433,10 +433,13 @@ def store_signatures( write_log_index(get_log_index_from_signatures(signatures)) -def verify_local_image(image: str, pubkey: str = DEFAULT_PUBKEY_LOCATION) -> bool: +def verify_local_image( + image: Optional[str] = None, pubkey: str = DEFAULT_PUBKEY_LOCATION +) -> bool: """ Verifies that a local image has a valid signature """ + image = image or runtime.expected_image_name() log.info(f"Verifying local image {image} against pubkey {pubkey}") try: image_digest = runtime.get_local_image_digest(image) @@ -495,9 +498,13 @@ def prepare_airgapped_archive(image_name: str, destination: str) -> None: def upgrade_container_image( - image: str, manifest_digest: str, pubkey: str, callback: Optional[Callable] = None + manifest_digest: str, + image: Optional[str] = None, + pubkey: Optional[str] = DEFAULT_PUBKEY_LOCATION, + callback: Optional[Callable] = None, ) -> str: """Verify and upgrade the image to the latest, if signed.""" + image = image or runtime.expected_image_name() update_available, remote_digest = registry.is_new_remote_image_available(image) if not update_available: raise errors.ImageAlreadyUpToDate("The image is already up to date") diff --git a/tests/isolation_provider/test_container.py b/tests/isolation_provider/test_container.py index 81f147d..0adf979 100644 --- a/tests/isolation_provider/test_container.py +++ b/tests/isolation_provider/test_container.py @@ -6,7 +6,7 @@ from pytest_mock import MockerFixture from pytest_subprocess import FakeProcess from dangerzone import errors -from dangerzone.container_utils import CONTAINER_NAME, Runtime +from dangerzone.container_utils import Runtime, expected_image_name from dangerzone.isolation_provider.container import Container from dangerzone.isolation_provider.qubes import is_qubes_native_conversion from dangerzone.updater import SignatureError, UpdaterError @@ -80,7 +80,7 @@ class TestContainer(IsolationProviderTest): "list", "--format", "{{ .Tag }}", - CONTAINER_NAME, + expected_image_name(), ], occurrences=2, )