mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-29 10:12:38 +02:00
FIXUP: Use the digest when pulling the container
This commit is contained in:
parent
affb954103
commit
3b4f8f12be
5 changed files with 30 additions and 27 deletions
|
@ -228,13 +228,14 @@ def get_image_id_by_digest(digest: str) -> str:
|
||||||
return process.stdout.decode().strip().split("\n")[0]
|
return process.stdout.decode().strip().split("\n")[0]
|
||||||
|
|
||||||
|
|
||||||
def container_pull(image: str) -> bool:
|
def container_pull(image: str, manifest_digest: str):
|
||||||
"""Pull a container image from a registry."""
|
"""Pull a container image from a registry."""
|
||||||
runtime = Runtime()
|
runtime = Runtime()
|
||||||
cmd = [str(runtime.path), "pull", f"{image}"]
|
cmd = [str(runtime.path), "pull", f"{image}@sha256:{manifest_digest}"]
|
||||||
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
return process.returncode == 0
|
if process.returncode != 0:
|
||||||
|
raise errors.ContainerPullException(f"Could not pull the container image: {e}")
|
||||||
|
|
||||||
|
|
||||||
def get_local_image_digest(image: str) -> str:
|
def get_local_image_digest(image: str) -> str:
|
||||||
|
|
|
@ -122,25 +122,37 @@ def handle_document_errors(func: F) -> F:
|
||||||
#### Container-related errors
|
#### Container-related errors
|
||||||
|
|
||||||
|
|
||||||
class ImageNotPresentException(Exception):
|
class ContainerException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ImageInstallationException(Exception):
|
class ImageNotPresentException(ContainerException):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NoContainerTechException(Exception):
|
class MultipleImagesFoundException(ContainerException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ImageInstallationException(ContainerException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NoContainerTechException(ContainerException):
|
||||||
def __init__(self, container_tech: str) -> None:
|
def __init__(self, container_tech: str) -> None:
|
||||||
super().__init__(f"{container_tech} is not installed")
|
super().__init__(f"{container_tech} is not installed")
|
||||||
|
|
||||||
|
|
||||||
class NotAvailableContainerTechException(Exception):
|
class NotAvailableContainerTechException(ContainerException):
|
||||||
def __init__(self, container_tech: str, error: str) -> None:
|
def __init__(self, container_tech: str, error: str) -> None:
|
||||||
self.error = error
|
self.error = error
|
||||||
self.container_tech = container_tech
|
self.container_tech = container_tech
|
||||||
super().__init__(f"{container_tech} is not available")
|
super().__init__(f"{container_tech} is not available")
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedContainerRuntime(Exception):
|
class UnsupportedContainerRuntime(ContainerException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerPullException(ContainerException):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -38,6 +38,9 @@ def upgrade(image: str, pubkey: str) -> None:
|
||||||
except errors.ImageAlreadyUpToDate as e:
|
except errors.ImageAlreadyUpToDate as e:
|
||||||
click.echo(f"✅ {e}")
|
click.echo(f"✅ {e}")
|
||||||
raise click.Abort()
|
raise click.Abort()
|
||||||
|
except Exception as e:
|
||||||
|
click.echo(f"❌ {e}")
|
||||||
|
raise click.Abort()
|
||||||
|
|
||||||
|
|
||||||
@main.command()
|
@main.command()
|
||||||
|
|
|
@ -455,7 +455,7 @@ def prepare_airgapped_archive(image_name: str, destination: str) -> None:
|
||||||
archive.add(tmpdir, arcname=".")
|
archive.add(tmpdir, arcname=".")
|
||||||
|
|
||||||
|
|
||||||
def upgrade_container_image(image: str, manifest_digest: str, pubkey: str) -> bool:
|
def upgrade_container_image(image: str, manifest_digest: str, pubkey: str) -> str:
|
||||||
"""Verify and upgrade the image to the latest, if signed."""
|
"""Verify and upgrade the image to the latest, if signed."""
|
||||||
update_available, _ = is_update_available(image)
|
update_available, _ = is_update_available(image)
|
||||||
if not update_available:
|
if not update_available:
|
||||||
|
@ -464,20 +464,17 @@ def upgrade_container_image(image: str, manifest_digest: str, pubkey: str) -> bo
|
||||||
signatures = get_remote_signatures(image, manifest_digest)
|
signatures = get_remote_signatures(image, manifest_digest)
|
||||||
verify_signatures(signatures, manifest_digest, pubkey)
|
verify_signatures(signatures, manifest_digest, pubkey)
|
||||||
|
|
||||||
# Ensure that we only upgrade if the log index is higher than the last known one
|
# Only upgrade if the log index is higher than the last known one
|
||||||
incoming_log_index = get_log_index_from_signatures(signatures)
|
incoming_log_index = get_log_index_from_signatures(signatures)
|
||||||
last_log_index = get_last_log_index()
|
last_log_index = get_last_log_index()
|
||||||
|
|
||||||
if incoming_log_index < last_log_index:
|
if incoming_log_index < last_log_index:
|
||||||
raise errors.InvalidLogIndex(
|
raise errors.InvalidLogIndex(
|
||||||
"The log index is not higher than the last known one"
|
"Trying to upgrade to an image with a lower log index"
|
||||||
)
|
)
|
||||||
|
|
||||||
# let's upgrade the image
|
runtime.container_pull(image, manifest_digest)
|
||||||
# XXX Use the image digest here to avoid race conditions
|
|
||||||
upgraded = runtime.container_pull(image)
|
|
||||||
|
|
||||||
# At this point, the signatures are verified
|
# Store the signatures just now to avoid storing them unverified
|
||||||
# We store the signatures just now to avoid storing unverified signatures
|
|
||||||
store_signatures(signatures, manifest_digest, pubkey)
|
store_signatures(signatures, manifest_digest, pubkey)
|
||||||
return upgraded
|
return manifest_digest
|
||||||
|
|
|
@ -13,12 +13,6 @@ from dangerzone.gui import Application
|
||||||
sys.dangerzone_dev = True # type: ignore[attr-defined]
|
sys.dangerzone_dev = True # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
|
||||||
ASSETS_PATH = Path(__file__).parent / "assets"
|
|
||||||
TEST_PUBKEY_PATH = ASSETS_PATH / "test.pub.key"
|
|
||||||
INVALID_SIGNATURES_PATH = ASSETS_PATH / "signatures" / "invalid"
|
|
||||||
VALID_SIGNATURES_PATH = ASSETS_PATH / "signatures" / "valid"
|
|
||||||
TEMPERED_SIGNATURES_PATH = ASSETS_PATH / "signatures" / "tempered"
|
|
||||||
|
|
||||||
|
|
||||||
# Use this fixture to make `pytest-qt` invoke our custom QApplication.
|
# Use this fixture to make `pytest-qt` invoke our custom QApplication.
|
||||||
# See https://pytest-qt.readthedocs.io/en/latest/qapplication.html#testing-custom-qapplications
|
# See https://pytest-qt.readthedocs.io/en/latest/qapplication.html#testing-custom-qapplications
|
||||||
|
@ -140,10 +134,6 @@ for_each_doc = pytest.mark.parametrize(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def signature():
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
# External Docs - base64 docs encoded for externally sourced documents
|
# External Docs - base64 docs encoded for externally sourced documents
|
||||||
# XXX to reduce the chance of accidentally opening them
|
# XXX to reduce the chance of accidentally opening them
|
||||||
|
|
Loading…
Reference in a new issue