Files
id_configurator/id_tools/publication.py
2026-09-04 21:58:28 +02:00

130 lines
4.3 KiB
Python

import json
import logging
import os
import subprocess
import tempfile
import typer
from id_tools.config import SourceConfig
app = typer.Typer(
help="Publish the documentation and release announcements to external destinations."
)
logger = logging.getLogger(__name__)
@app.command(name="sign-docker", help="Signe l'image Docker sur le registre")
def sign_docker_image(
registry: str = typer.Option(
"interdiode", help="Registre cible pour l'image Docker"
),
docker_login: str = typer.Option(
"", envvar="DOCKER_HUB_LOGIN", help="Nom d'utilisateur Docker"
),
docker_token: str = typer.Option(
"", envvar="DOCKER_HUB_TOKEN", help="Jeton d'accès Docker"
),
cosign_password: str = typer.Option(
"", envvar="COSIGN_PASSWORD", help="Mot de passe de la clé privée Cosign"
),
cosign_private_key: str = typer.Option(
"", envvar="COSIGN_PRIVATE_KEY", help="Contenu de la clé privée Cosign"
),
):
config = SourceConfig()
version = config.get_project_version()
image = config.get_project_name()
v_tag = f"{image}:{version}"
l_tag = f"{image}:latest"
destinations = [f"{registry}/{v_tag}", f"{registry}/{l_tag}"]
if docker_login and docker_token:
cmd = ["docker", "login", "-u", docker_login, "--password-stdin"]
subprocess.run(
cmd, input=docker_token, text=True, check=True, cwd=config.source_dir
)
digest_references = {
get_reference_from_tag(destination) for destination in destinations
}
if cosign_password and cosign_private_key:
with (
tempfile.NamedTemporaryFile(mode="w", suffix=".key", delete=True) as key_fd,
tempfile.TemporaryDirectory() as tmp_dir,
):
key_fd.write(cosign_private_key)
key_fd.flush()
cosign_env = {**os.environ, "COSIGN_PASSWORD": cosign_password}
trivy_report = os.path.join(tmp_dir, "trivy_report.json")
for digest_reference in digest_references:
# noinspection SpellCheckingInspection
cmd = [
"trivy",
"image",
"--format",
"json",
"--output",
trivy_report,
digest_reference,
]
subprocess.run(cmd, text=True, check=True, cwd=config.source_dir)
cmd = [
"cosign",
"attest",
"--key",
key_fd.name,
"--type",
"vuln",
"--predicate",
trivy_report,
digest_reference,
]
subprocess.run(cmd, text=True, check=True, cwd=config.source_dir)
cmd = [
"cosign",
"sign",
"--yes",
"--recursive",
"--key",
key_fd.name,
digest_reference,
]
subprocess.check_call(cmd, env=cosign_env)
cmd = [
"cosign",
"verify-attestation",
"--key",
config.cosign_key_url,
"--type",
"vuln",
digest_reference,
]
subprocess.check_call(cmd, env=cosign_env)
cmd = [
"cosign",
"verify",
"--key",
config.cosign_key_url,
digest_reference,
]
subprocess.check_call(cmd, env=cosign_env)
typer.echo(f"✓ Image signée : {registry}/{v_tag} et {registry}/{l_tag}")
def get_reference_from_tag(destination: str) -> str:
# noinspection SpellCheckingInspection
cmd = [
"docker",
"buildx",
"imagetools",
"inspect",
destination,
"--format",
"{{json .Manifest}}",
]
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
manifest = json.loads(result.stdout)
image_name = destination.rsplit(":", 1)[0]
reference = f"{image_name}@{manifest['digest']}"
return reference