From f41fe5845577439c4ea4cffc6614e0b9158d02e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 01:02:12 +0000 Subject: [PATCH] feat: verify cosign image signatures before pulling ghcr.io/nextcloud-releases images Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/fd95dc7d-c221-4c10-9e14-e6f2253a7a22 Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com> --- Containers/mastercontainer/Dockerfile | 3 +- php/src/Docker/DockerActionManager.php | 41 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Containers/mastercontainer/Dockerfile b/Containers/mastercontainer/Dockerfile index 61cfbbcf..bba0765f 100644 --- a/Containers/mastercontainer/Dockerfile +++ b/Containers/mastercontainer/Dockerfile @@ -46,7 +46,8 @@ RUN set -ex; \ sudo \ netcat-openbsd \ curl \ - grep; \ + grep \ + cosign; \ \ apk add --no-cache --virtual .build-deps \ autoconf \ diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php index 940814fe..18af9d33 100644 --- a/php/src/Docker/DockerActionManager.php +++ b/php/src/Docker/DockerActionManager.php @@ -523,6 +523,7 @@ readonly class DockerActionManager { } $imageName = $this->BuildImageName($container); + $this->verifyImageSignature($imageName); $encodedImageName = urlencode($imageName); $url = $this->BuildApiUrl(sprintf('images/create?fromImage=%s', $encodedImageName)); $imageIsThere = true; @@ -557,6 +558,46 @@ readonly class DockerActionManager { } } + private function verifyImageSignature(string $imageName): void { + // Only verify images from the nextcloud-releases ghcr.io registry, + // as those are the images signed via the CI workflows in PR #97. + if (!str_starts_with($imageName, 'ghcr.io/nextcloud-releases/')) { + return; + } + + $command = [ + 'cosign', + 'verify', + '--certificate-identity-regexp', + '^https://github\\.com/nextcloud-releases/all-in-one/\\.github/workflows/', + '--certificate-oidc-issuer', + 'https://token.actions.githubusercontent.com', + $imageName, + ]; + + $process = proc_open( + $command, + [ + 0 => ['file', '/dev/null', 'r'], + 1 => ['file', '/dev/null', 'w'], + 2 => ['pipe', 'w'], + ], + $pipes + ); + + if (!is_resource($process)) { + throw new \Exception('Could not run cosign to verify image ' . $imageName); + } + + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[2]); + $exitCode = proc_close($process); + + if ($exitCode !== 0) { + throw new \Exception('Image signature verification failed for ' . $imageName . ': ' . ($stderr !== false ? $stderr : '')); + } + } + private function isContainerUpdateAvailable(string $id): string { $container = $this->containerDefinitionFetcher->GetContainerById($id);