mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
fix(icu): update documentation and fixes
This commit is contained in:
parent
3b858dac27
commit
858d31458b
5 changed files with 65 additions and 25 deletions
|
@ -3,7 +3,7 @@ import logging
|
|||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
from typing import List, Tuple
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from . import errors
|
||||
from .util import get_resource_path, get_subprocess_startupinfo
|
||||
|
@ -192,10 +192,14 @@ def container_pull(image: str) -> bool:
|
|||
return process.returncode == 0
|
||||
|
||||
|
||||
def get_local_image_hash(image: str) -> str:
|
||||
def get_local_image_hash(image: str) -> Optional[str]:
|
||||
"""
|
||||
Returns a image hash from a local image name
|
||||
"""
|
||||
cmd = [get_runtime_name(), "image", "inspect", image, "-f", "{{.Digest}}"]
|
||||
result = subprocess.run(cmd, capture_output=True, check=True)
|
||||
return result.stdout.strip().decode().strip("sha256:")
|
||||
try:
|
||||
result = subprocess.run(cmd, capture_output=True, check=True)
|
||||
except subprocess.CalledProcessError as e:
|
||||
return None
|
||||
else:
|
||||
return result.stdout.strip().decode().strip("sha256:")
|
||||
|
|
|
@ -8,7 +8,7 @@ from ..util import get_resource_path
|
|||
from . import attestations, errors, log, registry, signatures
|
||||
|
||||
DEFAULT_REPOSITORY = "freedomofpress/dangerzone"
|
||||
DEFAULT_IMAGE_NAME = "ghcr.io/freedomofpress/dangerzone"
|
||||
DEFAULT_IMAGE_NAME = "ghcr.io/freedomofpress/dangerzone/dangerzone"
|
||||
PUBKEY_DEFAULT_LOCATION = get_resource_path("freedomofpress-dangerzone-pub.key")
|
||||
|
||||
|
||||
|
@ -24,14 +24,18 @@ def main(debug: bool) -> None:
|
|||
|
||||
|
||||
@main.command()
|
||||
@click.argument("image")
|
||||
@click.argument("image", default=DEFAULT_IMAGE_NAME)
|
||||
@click.option("--pubkey", default=PUBKEY_DEFAULT_LOCATION)
|
||||
def upgrade(image: str, pubkey: str) -> None:
|
||||
"""Upgrade the image to the latest signed version."""
|
||||
manifest_hash = registry.get_manifest_hash(image)
|
||||
try:
|
||||
is_upgraded = signatures.upgrade_container_image(image, manifest_hash, pubkey)
|
||||
click.echo(f"✅ The local image {image} has been upgraded")
|
||||
if is_upgraded:
|
||||
click.echo(f"✅ The local image {image} has been upgraded")
|
||||
click.echo(f"✅ The image has been signed with {pubkey}")
|
||||
click.echo(f"✅ Signatures has been verified and stored locally")
|
||||
|
||||
except errors.ImageAlreadyUpToDate as e:
|
||||
click.echo(f"✅ {e}")
|
||||
raise click.Abort()
|
||||
|
@ -56,15 +60,15 @@ def load_archive(image_filename: str, pubkey: str) -> None:
|
|||
|
||||
@main.command()
|
||||
@click.argument("image")
|
||||
@click.option("--destination", default="dangerzone-airgapped.tar")
|
||||
def prepare_archive(image: str, destination: str) -> None:
|
||||
@click.option("--output", default="dangerzone-airgapped.tar")
|
||||
def prepare_archive(image: str, output: str) -> None:
|
||||
"""Prepare an archive to upgrade the dangerzone image on an airgapped environment."""
|
||||
signatures.prepare_airgapped_archive(image, destination)
|
||||
click.echo(f"✅ Archive {destination} created")
|
||||
signatures.prepare_airgapped_archive(image, output)
|
||||
click.echo(f"✅ Archive {output} created")
|
||||
|
||||
|
||||
@main.command()
|
||||
@click.argument("image")
|
||||
@click.argument("image", default=DEFAULT_IMAGE_NAME)
|
||||
@click.option("--pubkey", default=PUBKEY_DEFAULT_LOCATION)
|
||||
def verify_local(image: str, pubkey: str) -> None:
|
||||
"""
|
||||
|
@ -85,6 +89,7 @@ def verify_local(image: str, pubkey: str) -> None:
|
|||
@main.command()
|
||||
@click.argument("image")
|
||||
def list_remote_tags(image: str) -> None:
|
||||
"""List the tags available for a given image."""
|
||||
click.echo(f"Existing tags for {image}")
|
||||
for tag in registry.list_tags(image):
|
||||
click.echo(tag)
|
||||
|
@ -93,6 +98,7 @@ def list_remote_tags(image: str) -> None:
|
|||
@main.command()
|
||||
@click.argument("image")
|
||||
def get_manifest(image: str) -> None:
|
||||
"""Retrieves a remove manifest for a given image and displays it."""
|
||||
click.echo(registry.get_manifest(image))
|
||||
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@ class ImageAlreadyUpToDate(UpdaterError):
|
|||
pass
|
||||
|
||||
|
||||
class ImageNotFound(UpdaterError):
|
||||
pass
|
||||
|
||||
|
||||
class SignatureError(UpdaterError):
|
||||
pass
|
||||
|
||||
|
|
|
@ -64,8 +64,13 @@ def verify_signature(signature: dict, image_hash: str, pubkey: str) -> bool:
|
|||
signature_bundle = signature_to_bundle(signature)
|
||||
|
||||
payload_bytes = b64decode(signature_bundle["Payload"])
|
||||
if json.loads(payload_bytes)["critical"]["type"] != f"sha256:{image_hash}":
|
||||
raise errors.SignatureMismatch("The signature does not match the image hash")
|
||||
payload_hash = json.loads(payload_bytes)["critical"]["image"][
|
||||
"docker-manifest-digest"
|
||||
]
|
||||
if payload_hash != f"sha256:{image_hash}":
|
||||
raise errors.SignatureMismatch(
|
||||
f"The signature does not match the image hash ({payload_hash}, {image_hash})"
|
||||
)
|
||||
|
||||
with (
|
||||
NamedTemporaryFile(mode="w") as signature_file,
|
||||
|
@ -220,7 +225,7 @@ def convert_oci_images_signatures(
|
|||
"Payload": payload_b64,
|
||||
"Cert": None,
|
||||
"Chain": None,
|
||||
"rekorBundle": bundle,
|
||||
"Bundle": bundle,
|
||||
"RFC3161Timestamp": None,
|
||||
}
|
||||
|
||||
|
@ -311,7 +316,11 @@ def verify_local_image(image: str, pubkey: str) -> bool:
|
|||
Verifies that a local image has a valid signature
|
||||
"""
|
||||
log.info(f"Verifying local image {image} against pubkey {pubkey}")
|
||||
image_hash = runtime.get_local_image_hash(image)
|
||||
try:
|
||||
image_hash = runtime.get_local_image_hash(image)
|
||||
except subprocess.CalledProcessError:
|
||||
raise errors.ImageNotFound(f"The image {image} does not exist locally")
|
||||
|
||||
log.debug(f"Image hash: {image_hash}")
|
||||
signatures = load_signatures(image_hash, pubkey)
|
||||
if len(signatures) < 1:
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
# Independent Container Updates
|
||||
|
||||
Since version 0.9.0, Dangerzone is able to ship container images independently
|
||||
from issuing a new release of the software.
|
||||
from releases.
|
||||
|
||||
This is useful as images need to be kept updated with the latest security fixes.
|
||||
One of the main benefits of doing so is to lower the time needed to patch security issues inside the containers.
|
||||
|
||||
## Nightly images and attestations
|
||||
## Checking attestations
|
||||
|
||||
Each night, new images are built and pushed to our container registry, alongside
|
||||
Each night, new images are built and pushed to the container registry, alongside
|
||||
with a provenance attestation, enabling anybody to ensure that the image has
|
||||
been originally built by Github CI runners, from a defined source repository (in our case `freedomofpress/dangerzone`).
|
||||
|
||||
To verify the attestations against our expectations, use the following command:
|
||||
```bash
|
||||
poetry run ./dev_scripts/registry.py attest ghcr.io/freedomofpress/dangerzone/dangerzone:latest --repo freedomofpress/dangerzone
|
||||
dangerzone-image attest-provenance ghcr.io/freedomofpress/dangerzone/dangerzone --repository freedomofpress/dangerzone
|
||||
```
|
||||
|
||||
In case of sucess, it will report back:
|
||||
|
@ -22,18 +22,35 @@ In case of sucess, it will report back:
|
|||
🎉 The image available at `ghcr.io/freedomofpress/dangerzone/dangerzone:latest` has been built by Github runners from the `freedomofpress/dangerzone` repository.
|
||||
```
|
||||
|
||||
## Container updates on air-gapped environments
|
||||
## Install updates
|
||||
|
||||
To check if a new container image has been released, and update your local installation with it, you can use the following commands:
|
||||
|
||||
```bash
|
||||
./dev_scripts/dangerzone-image --debug upgrade ghcr.io/almet/dangerzone/dangerzone
|
||||
```
|
||||
|
||||
## Verify local
|
||||
|
||||
You can verify that the image you have locally matches the stored signatures, and that these have been signed with a trusted public key:
|
||||
|
||||
```bash
|
||||
dangerzone-image verify-local ghcr.io/almet/dangerzone/dangerzone
|
||||
```
|
||||
|
||||
## Air-gapped environments
|
||||
|
||||
In order to make updates on an air-gapped environment, you will need to prepare an archive for the air-gapped environment. This archive will contain all the needed material to validate that the new container image has been signed and is valid.
|
||||
|
||||
On the machine on which you prepare the packages:
|
||||
|
||||
```bash
|
||||
dangerzone-image prepare-archive ghcr.io/almet/dangerzone/dangerzone@sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7
|
||||
dangerzone-image prepare-archive --output dz-fa94872.tar ghcr.io/almet/dangerzone/dangerzone@sha256:fa948726aac29a6ac49f01ec8fbbac18522b35b2491fdf716236a0b3502a2ca7
|
||||
```
|
||||
|
||||
On the airgapped machine, copy the file and run the following command:
|
||||
|
||||
```bash
|
||||
dangerzone-image load-archive
|
||||
```
|
||||
dangerzone-image load-archive dz-fa94872.tar
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue