containerz: add signed bundle transfer to Deploy RPC - #351
Conversation
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
Extend DeployRequest to stream Sigstore cosign bundles alongside container images for offline signature verification during image onboarding. - Add cosign_bundle_size to ImageTransfer - Add cosign_bundle_content and CosignBundleTransferEnd to DeployRequest - Add CosignBundleTransferReady/Progress to DeployResponse - Bump gnoi_version to 0.3.0
Address review feedback: keep the signing mechanism opaque in the Deploy RPC by using signed_bundle_* field and message names instead of cosign-specific naming.
Run regenerate-files.sh (bazel 8.4.2) to update containerz.pb.go and containerz_grpc.pb.go after signed_bundle_* proto rename.
45b2757 to
472a38f
Compare
Match the protoc version stamp on upstream main for containerz stubs only, without bumping the repo-wide protobuf dependency.
nburnwalcisco
left a comment
There was a problem hiding this comment.
Testing if have permission to approve.
| // SignedBundleTransferReady indicates to the client that the target is ready | ||
| // to receive signed bundle content after the image transfer completes. | ||
| message SignedBundleTransferReady { | ||
| // Indicates the size of the chunks that the client should break the bundle | ||
| // into as it is transferred. | ||
| int32 chunk_size = 1; | ||
| } | ||
|
|
||
| // SignedBundleTransferProgress is sent periodically to the client during the | ||
| // signed bundle transfer. | ||
| message SignedBundleTransferProgress { | ||
| // The number of signed bundle bytes transferred so far. | ||
| uint64 bytes_received = 1; | ||
| } |
There was a problem hiding this comment.
Do these need to be a separate message? Could we integrate this in InageTransferReady/Progess?
There was a problem hiding this comment.
We added dedicated bundle Ready/Progress types mainly for clarity at the protocol boundary:
Different phases — ImageTransferReady is sent before image content; bundle transfer only starts after ImageTransferEnd. A second ready is a phase transition, not a repeat of the first handshake.
Clear progress semantics — ImageTransferProgress.bytes_received today means image bytes. During bundle upload it would mean bundle bytes. Separate types make that explicit in generated code, logs, and client state machines.
Consistent pattern — Image transfer uses Ready → Progress → End; we mirrored that for the bundle after ImageTransferEnd.
DeployResponse oneof — Distinct response variants let clients branch on message type without inferring phase from stream position alone.
There was a problem hiding this comment.
Given the size of a signature bundle is typically ~10KB, is it worth adding this as a separately streamed and chunked message?
Could it instead be simply included in the ImageTransfer message?
Document that targets must return image_transfer_error with FAILED_PRECONDITION in response to ImageTransfer when signed_bundle_size is non-zero but signed bundle transfer is not supported, before ImageTransferReady.
|
High level question: could we get the same guarantee of integrity just by updating the Like: message ImageTransferEnd {
// optional hash of the image tar. If present, the server should
// verify that the hash matches the hash of the content transferred.
gnoi.types.HashType hash = 1;
}similar to how the File/Put RPC works: Line 95 in b56005d That way, we can be sure that the gNOI client and server both have the same tar file present, and then the verification on that tar file can be performed from the client's end prior to transfer. |
Thanks for the suggestion — the File/Put hash pattern is a good reference for transfer integrity. We can consider that, but a hash on
If verification is done only on the client before transfer, a compromised or misconfigured client could still push an unsigned image and a device that only checks a hash would accept it. The signed-bundle flow lets the target enforce supply-chain policy without relying on the client to have verified correctly beforehand. That said, adding an optional hash on |
I think it would be good to formalize this in some way so that we dont have clients sending blobs where the blob verification is completely opaque and out-of-scope. Doing the verification like this on the server-side like this feels like its going to cause issues for a network operator which uses multiple vendors. |
Thanks — agreed we should avoid leaving verification fully opaque for operators. The proto stays tool-agnostic on the wire, but in practice Cosign/Sigstore is the industry standard for container signing; Notary is deprecated and not a good baseline for a multi-vendor profile. If openconfig standardizes on a Sigstore bundle as the recommended signed_bundle_content format, operators send one blob type to all compliant targets; per-vendor differences stay in trust/policy config, not bundle encoding. We can document that profile alongside this PR and optionally add a format hint on ImageTransfer in a follow-up. Hash on ImageTransferEnd remains complementary integrity, not a substitute for on-box signature policy. |
Summary
Extend the Containerz Deploy RPC to transfer an opaque signed bundle alongside the container image tarball. This enables offline/air-gapped image onboarding where the target verifies a signature before loading the image.
The proto intentionally does not name a specific signing tool (cosign, notary, etc.). It defines only what is transferred; how the target verifies the bundle remains implementation-specific.
Changes
signed_bundle_sizeonImageTransfersigned_bundle_contentandSignedBundleTransferEndonDeployRequestSignedBundleTransferReady/SignedBundleTransferProgressonDeployResponsegnoi_versionbumped to0.3.0Server behavior when signing is not supported
When
signed_bundle_size = 0, behavior is unchanged — backward compatible with targets that do not implement signed bundle transfer.When
signed_bundle_size > 0and the target does not support signed bundle transfer or verification, the target must:ImageTransferwithimage_transfer_error(google.rpc.Statuscode FAILED_PRECONDITION)ImageTransferReadyClients can detect lack of support without uploading the image tarball.
Deploy flow (when
signed_bundle_size > 0and target supports signing)There is one terminal success for the entire Deploy operation:
ImageTransferSuccess. The signed bundle phase uses Ready / Progress / End messages only (no separate bundle success message). Failures at any stage are reported viaimage_transfer_error.Phases
ImageTransfer(signed_bundle_size > 0)image_transfer_errorif unsupported, or continueImageTransferImageTransferReadycontent(chunks)ImageTransferProgressImageTransferEndSignedBundleTransferReadysigned_bundle_content(chunks)SignedBundleTransferProgressSignedBundleTransferEndImageTransferSuccessorimage_transfer_errorWhen
signed_bundle_size = 0, phase 0 does not apply and behavior matches the pre-0.3.0 flow.Call flow diagram
sequenceDiagram participant Client participant Server Client->>Server: ImageTransfer (image_size, signed_bundle_size) alt Target does not support signed bundle transfer Server->>Client: image_transfer_error (FAILED_PRECONDITION) else Target supports signed bundle transfer Server->>Client: ImageTransferReady (chunk_size) loop Image chunks Client->>Server: content Server->>Client: ImageTransferProgress (bytes_received) end Client->>Server: ImageTransferEnd Server->>Client: SignedBundleTransferReady (chunk_size) loop Signed bundle chunks Client->>Server: signed_bundle_content Server->>Client: SignedBundleTransferProgress (bytes_received) end Client->>Server: SignedBundleTransferEnd Server->>Server: Verify signed bundle against image Server->>Server: Load image into registry / plugin store alt Success Server->>Client: ImageTransferSuccess (name, tag, image_size) else Failure (verify or load) Server->>Client: image_transfer_error end endMotivation
signed_bundle_*naming keeps the RPC opaque to the verifier implementationimage_transfer_erroravoids uploading a large image to an unsupported targetImplementation notes
containerz(containerz.pb.go,containerz_grpc.pb.go)signed_bundle_size > 0is not supportedTest plan
signed_bundle_size = 0)regenerate-files.sh