mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-05-01 19:22:23 +02:00
Use a share/image-name.txt
file which defines what image name we should expect
This is especially useful when running the tests against some non-production version of the image. Note that the signature for this image will need to match the key at `share/freedomofpress-dangerzone-pub.key`
This commit is contained in:
parent
d678568ace
commit
e9b399baf5
4 changed files with 45 additions and 32 deletions
|
@ -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,9 +154,10 @@ 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...")
|
||||
if not tarball_path:
|
||||
tarball_path = get_resource_path("container.tar")
|
||||
try:
|
||||
res = subprocess.run(
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue