diff --git a/cbscore/src/cbscore/builder/builder.py b/cbscore/src/cbscore/builder/builder.py index 5a838c25..984f2b57 100644 --- a/cbscore/src/cbscore/builder/builder.py +++ b/cbscore/src/cbscore/builder/builder.py @@ -64,6 +64,7 @@ class Builder: ccache_path: Path | None skip_build: bool force: bool + tls_verify: bool def __init__( self, @@ -72,6 +73,7 @@ def __init__( *, skip_build: bool = False, force: bool = False, + tls_verify: bool = True ) -> None: self.desc = desc self.config = config @@ -82,6 +84,7 @@ def __init__( self.ccache_path = config.paths.ccache self.skip_build = skip_build self.force = force + self.tls_verify = tls_verify try: vault_config = self.config.get_vault_config() @@ -109,7 +112,7 @@ async def run(self) -> None: raise BuilderError(msg=msg) from e container_img_uri = get_container_canonical_uri(self.desc) - if skopeo_image_exists(container_img_uri, self.secrets): + if skopeo_image_exists(container_img_uri, self.secrets, tls_verify=self.tls_verify): logger.info(f"image '{container_img_uri}' already exists -- do not build!") return else: diff --git a/cbscore/src/cbscore/builder/prepare.py b/cbscore/src/cbscore/builder/prepare.py index 7db78250..7c6c1119 100644 --- a/cbscore/src/cbscore/builder/prepare.py +++ b/cbscore/src/cbscore/builder/prepare.py @@ -105,16 +105,19 @@ async def _cb(s: str) -> None: raise BuilderError(msg="unable to install dependencies") # install cosign rpm - rc, _, stderr = await async_run_cmd( + rc, stdout, stderr = await async_run_cmd( [ "rpm", "-Uvh", "https://github.com/sigstore/cosign/releases/download/v2.4.3/" + "cosign-2.4.3-1.x86_64.rpm", ], - outcb=_cb, ) - if rc != 0: + logger.debug(stdout) + if rc == 2 and re.match(".*already installed.*", stderr): + msg = f'skip install cosign. allready installed' + logger.debug(msg) + elif rc != 0: msg = f"error installing cosign package: {stderr}" logger.error(msg) raise BuilderError(msg) diff --git a/cbscore/src/cbscore/cmds/builds.py b/cbscore/src/cbscore/cmds/builds.py index 84a56a4f..44ae6fb7 100644 --- a/cbscore/src/cbscore/cmds/builds.py +++ b/cbscore/src/cbscore/cmds/builds.py @@ -124,6 +124,11 @@ is_flag=True, default=False, ) +@click.option( + "--tls-verify", + help="Require HTTPS and verify certificates when talking to the container registry or daemon.", + default=True, +) @pass_ctx def cmd_build( ctx: Ctx, @@ -136,6 +141,7 @@ def cmd_build( log_file_path: Path | None, skip_build: bool, force: bool, + tls_verify: bool, ) -> None: assert ctx.config_path @@ -192,6 +198,7 @@ def cmd_build( log_file_path=log_file_path, skip_build=skip_build, force=force, + tls_verify=tls_verify, ) ) @@ -245,12 +252,18 @@ def cmd_runner_grp() -> None: is_flag=True, default=False, ) +@click.option( + "--tls-verify", + help="Require HTTPS and verify certificates when talking to the container registry or daemon.", + default=True, +) @with_config def cmd_runner_build( config: Config, desc_path: Path, skip_build: bool, force: bool, + tls_verify: bool, ) -> None: upload_to_str = ( config.storage.s3.url @@ -285,6 +298,7 @@ def cmd_runner_build( registry: {registry_str} skip build: {skip_build} force: {force} + tls-verify: {tls_verify} """) if not desc_path.exists(): @@ -303,6 +317,7 @@ def cmd_runner_build( config, skip_build=skip_build, force=force, + tls_verify=tls_verify, ) except BuilderError as e: logger.error(f"unable to initialize builder: {e}") diff --git a/cbscore/src/cbscore/images/signing.py b/cbscore/src/cbscore/images/signing.py index 8e017675..a2373246 100644 --- a/cbscore/src/cbscore/images/signing.py +++ b/cbscore/src/cbscore/images/signing.py @@ -36,34 +36,53 @@ def __str__(self) -> str: return f"Signing Error: {self.msg}" -def sign( - registry: str, img: str, secrets: SecretsMgr, transit: str -) -> tuple[int, str, str]: +def _get_signing_params( + registry: str, secrets: SecretsMgr, transit: str +) -> tuple[str, str, str, str]: + """ + Check preconditions and return (username, password, transit_mount, transit_key). + + Raises SigningError if any prerequisite is missing. + """ if not secrets.has_vault(): - msg = "no vault configured, can't sign image" - logger.error(msg) - raise SigningError(msg) + raise SigningError("no vault configured, can't sign image") assert secrets.vault is not None if not secrets.has_transit_key(transit): - msg = f"vault transit key '{transit}' not found, can't sign image" - logger.error(msg) - raise SigningError(msg) + raise SigningError(f"vault transit key '{transit}' not found, can't sign image") try: _, username, password = secrets.registry_creds(registry) except SecretsMgrError as e: - msg = f"unable to obtain registry credentials for '{registry}': {e}" - logger.error(msg) - raise SigningError(msg) from e + raise SigningError( + f"unable to obtain registry credentials for '{registry}': {e}" + ) from e try: transit_mount, transit_key = secrets.transit(transit) except SecretsMgrError as e: - msg = f"unable to obtain transit key '{transit}': {e}" - logger.error(msg) - raise SigningError(msg) from e + raise SigningError(f"unable to obtain transit key '{transit}': {e}") from e + + return username, password, transit_mount, transit_key + + +def can_sign(registry: str, secrets: SecretsMgr, transit: str) -> bool: + try: + _get_signing_params(registry, secrets, transit) + except SigningError as e: + logger.debug(e.msg) + return False + else: + return True + + +def sign( + registry: str, img: str, secrets: SecretsMgr, transit: str +) -> tuple[int, str, str]: + username, password, transit_mount, transit_key = _get_signing_params( + registry, secrets, transit + ) cmd: CmdArgs = [ "cosign", diff --git a/cbscore/src/cbscore/images/skopeo.py b/cbscore/src/cbscore/images/skopeo.py index 511152df..a00064cd 100644 --- a/cbscore/src/cbscore/images/skopeo.py +++ b/cbscore/src/cbscore/images/skopeo.py @@ -24,7 +24,7 @@ from cbscore.images import get_image_name from cbscore.images import logger as parent_logger from cbscore.images.errors import ImageNotFoundError, SkopeoError -from cbscore.images.signing import sign +from cbscore.images.signing import can_sign, sign from cbscore.utils import CmdArgs, Password, run_cmd from cbscore.utils.containers import get_container_image_base_uri from cbscore.utils.secrets import SecretsMgrError @@ -99,20 +99,23 @@ def skopeo_copy( logger.info(f"copied '{src}' to '{dst}'") - try: - retcode, out, err = sign(dst_registry, dst, secrets, transit) - except SkopeoError as e: - logger.exception(f"error signing image '{dst}'") - raise e # noqa: TRY201 + if can_sign(dst_registry, secrets, transit): + try: + retcode, out, err = sign(dst_registry, dst, secrets, transit) + except SkopeoError as e: + logger.exception(f"error signing image '{dst}'") + raise e # noqa: TRY201 - if retcode != 0: - logger.error(f"error signing image '{dst}': {err}") - raise SkopeoError() + if retcode != 0: + logger.error(f"error signing image '{dst}': {err}") + raise SkopeoError() - logger.info(f"signed image '{dst}': {out}") + logger.info(f"signed image '{dst}': {out}") + else: + logger.warning(f"signing skipped for image '{dst}'") -def skopeo_inspect(img: str, secrets: SecretsMgr) -> str: +def skopeo_inspect(img: str, secrets: SecretsMgr, *, tls_verify: bool = True) -> str: logger.debug(f"inspect image '{img}'") try: @@ -132,6 +135,7 @@ def skopeo_inspect(img: str, secrets: SecretsMgr) -> str: retcode, raw_out, err = skopeo( [ "inspect", + f"--tls-verify={tls_verify}", "--creds", Password(f"{user}:{passwd}"), f"docker://{img}", @@ -143,19 +147,22 @@ def skopeo_inspect(img: str, secrets: SecretsMgr) -> str: if retcode != 0: msg = f"error inspecting image '{img}': {err}" - logger.error(msg) - if re.match(r".*not\s+found.*", err): + if retcode == 2 or re.match(r".*not\s+found.*", err): + logger.debug(msg) raise ImageNotFoundError(img) from None + logger.error(msg) raise SkopeoError(msg) from None return raw_out -def skopeo_image_exists(img: str, secrets: SecretsMgr) -> bool: +def skopeo_image_exists( + img: str, secrets: SecretsMgr, *, tls_verify: bool = True +) -> bool: logger.debug(f"check if image '{img}' exists") try: - _ = skopeo_inspect(img, secrets) + _ = skopeo_inspect(img, secrets, tls_verify=tls_verify) except ImageNotFoundError: logger.debug(f"image '{img}' does not exist") return False diff --git a/cbscore/src/cbscore/runner.py b/cbscore/src/cbscore/runner.py index 7e3318a1..c3029f85 100644 --- a/cbscore/src/cbscore/runner.py +++ b/cbscore/src/cbscore/runner.py @@ -118,6 +118,7 @@ async def runner( log_out_cb: AsyncRunCmdOutCallback | None = None, skip_build: bool = False, force: bool = False, + tls_verify: bool = True, ) -> None: our_actual_loc = Path(__file__).parent @@ -174,6 +175,7 @@ async def runner( log to file: {log_file_path if log_file_path else "not logging to file"} skip build: {skip_build} force: {force} + tls-verify: {tls_verify} """) if not entrypoint_path.exists() or not entrypoint_path.is_file(): @@ -250,7 +252,7 @@ async def runner( logger.error(msg) raise RunnerError(msg) from e - podman_args = ["--desc", desc_mount_loc] + podman_args = ["--desc", desc_mount_loc, f"--tls-verify={tls_verify}"] podman_volumes = { desc_file_path.resolve().as_posix(): desc_mount_loc, cbscore_path.resolve().as_posix(): "/runner/cbscore",