diff --git a/Cargo.toml b/Cargo.toml index 2c8a8fa4..7cd147fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ dns-lookup = "1.0.8" json-patch = "0.2.6" kube = { version = "0.73.1", default-features = false, features = ["client", "rustls-tls"] } k8s-openapi = { version = "0.15.0", default-features = false } -kubewarden-policy-sdk = "0.6.2" +kubewarden-policy-sdk = "0.6.3" lazy_static = "1.4.0" policy-fetcher = { git = "https://github.com/kubewarden/policy-fetcher", tag = "v0.7.8" } serde_json = "1.0" diff --git a/src/callback_handler/mod.rs b/src/callback_handler/mod.rs index df995385..20d419b2 100644 --- a/src/callback_handler/mod.rs +++ b/src/callback_handler/mod.rs @@ -5,13 +5,11 @@ use std::collections::HashMap; use tokio::sync::{mpsc, oneshot}; use tracing::{debug, warn}; -use crate::callback_requests::{CallbackRequest, CallbackResponse}; +use crate::callback_requests::{CallbackRequest, CallbackRequestType, CallbackResponse}; +use kubewarden_policy_sdk::host_capabilities::verification::{KeylessInfo, KeylessPrefixInfo}; use kubewarden_policy_sdk::host_capabilities::{ - net::LookupResponse, - oci::ManifestDigestResponse, - verification::{KeylessInfo, KeylessPrefixInfo, VerificationResponse}, - CallbackRequestType, + net::LookupResponse, oci::ManifestDigestResponse, verification::VerificationResponse, }; use policy_fetcher::verify::FulcioAndRekorData; diff --git a/src/callback_requests.rs b/src/callback_requests.rs index 2838798d..f4480c23 100644 --- a/src/callback_requests.rs +++ b/src/callback_requests.rs @@ -1,5 +1,10 @@ use anyhow::Result; -use kubewarden_policy_sdk::host_capabilities::CallbackRequestType; +use kubewarden_policy_sdk::host_capabilities::verification::{KeylessInfo, KeylessPrefixInfo}; +use kubewarden_policy_sdk::host_capabilities::{ + SigstoreVerificationInputV1, SigstoreVerificationInputV2, +}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use tokio::sync::oneshot; /// Holds the response to a waPC evaluation request @@ -18,3 +23,136 @@ pub struct CallbackRequest { /// A tokio oneshot channel over which the evaluation response has to be sent pub response_channel: oneshot::Sender>, } + +/// Describes the different kinds of request a waPC guest can make to +/// our host. +#[derive(Serialize, Deserialize, Debug)] +pub enum CallbackRequestType { + /// Require the computation of the manifest digest of an OCI object (be + /// it an image or anything else that can be stored into an OCI registry) + OciManifestDigest { + /// String pointing to the object (e.g.: `registry.testing.lan/busybox:1.0.0`) + image: String, + }, + + /// Require the verification of the manifest digest of an OCI object (be + /// it an image or anything else that can be stored into an OCI registry) + /// to be signed by Sigstore, using public keys mode + SigstorePubKeyVerify { + /// String pointing to the object (e.g.: `registry.testing.lan/busybox:1.0.0`) + image: String, + /// List of PEM encoded keys that must have been used to sign the OCI object + pub_keys: Vec, + /// Optional - Annotations that must have been provided by all signers when they signed the OCI artifact + annotations: Option>, + }, + + // Require the verification of the manifest digest of an OCI object to be + // signed by Sigstore, using keyless mode + SigstoreKeylessVerify { + /// String pointing to the object (e.g.: `registry.testing.lan/busybox:1.0.0`) + image: String, + /// List of keyless signatures that must be found + keyless: Vec, + /// Optional - Annotations that must have been provided by all signers when they signed the OCI artifact + annotations: Option>, + }, + + // Require the verification of the manifest digest of an OCI object to be + // signed by Sigstore using keyless mode, where the passed subject is a URL + // prefix of the subject to match + SigstoreKeylessPrefixVerify { + /// String pointing to the object (e.g.: `registry.testing.lan/busybox:1.0.0`) + image: String, + /// List of keyless signatures that must be found + keyless_prefix: Vec, + /// Optional - Annotations that must have been provided by all signers when they signed the OCI artifact + annotations: Option>, + }, + + // Require the verification of the manifest digest of an OCI object to be + // signed by Sigstore using keyless mode and performed in GitHub Actions + SigstoreGithubActionsVerify { + /// String pointing to the object (e.g.: `registry.testing.lan/busybox:1.0.0`) + image: String, + /// owner of the repository. E.g: octocat + owner: String, + /// Optional - Repo of the GH Action workflow that signed the artifact. E.g: example-repo + repo: Option, + /// Optional - Annotations that must have been provided by all signers when they signed the OCI artifact + annotations: Option>, + }, + + /// Lookup the addresses for a given hostname via DNS + DNSLookupHost { host: String }, +} + +impl From for CallbackRequestType { + fn from(val: SigstoreVerificationInputV2) -> Self { + match val { + SigstoreVerificationInputV2::SigstorePubKeyVerify { + image, + pub_keys, + annotations, + } => CallbackRequestType::SigstorePubKeyVerify { + image, + pub_keys, + annotations, + }, + SigstoreVerificationInputV2::SigstoreKeylessVerify { + image, + keyless, + annotations, + } => CallbackRequestType::SigstoreKeylessVerify { + image, + keyless, + annotations, + }, + SigstoreVerificationInputV2::SigstoreKeylessPrefixVerify { + image, + keyless_prefix, + annotations, + } => CallbackRequestType::SigstoreKeylessPrefixVerify { + image, + keyless_prefix, + annotations, + }, + SigstoreVerificationInputV2::SigstoreGithubActionsVerify { + image, + owner, + repo, + annotations, + } => CallbackRequestType::SigstoreGithubActionsVerify { + image, + owner, + repo, + annotations, + }, + } + } +} + +impl From for CallbackRequestType { + fn from(val: SigstoreVerificationInputV1) -> Self { + match val { + SigstoreVerificationInputV1::SigstorePubKeyVerify { + image, + pub_keys, + annotations, + } => CallbackRequestType::SigstorePubKeyVerify { + image, + pub_keys, + annotations, + }, + SigstoreVerificationInputV1::SigstoreKeylessVerify { + image, + keyless, + annotations, + } => CallbackRequestType::SigstoreKeylessVerify { + image, + keyless, + annotations, + }, + } + } +} diff --git a/src/runtimes/wapc.rs b/src/runtimes/wapc.rs index 373ed9c4..6a18fe47 100644 --- a/src/runtimes/wapc.rs +++ b/src/runtimes/wapc.rs @@ -8,12 +8,14 @@ use tracing::{debug, error}; pub(crate) struct Runtime<'a>(pub(crate) &'a mut wapc::WapcHost); use crate::admission_response::AdmissionResponse; -use crate::callback_requests::{CallbackRequest, CallbackResponse}; +use crate::callback_requests::{CallbackRequest, CallbackRequestType, CallbackResponse}; use crate::cluster_context::ClusterContext; use crate::policy::Policy; use crate::policy_evaluator::{PolicySettings, ValidateRequest}; -use kubewarden_policy_sdk::host_capabilities::CallbackRequestType; +use kubewarden_policy_sdk::host_capabilities::{ + SigstoreVerificationInputV1, SigstoreVerificationInputV2, +}; use kubewarden_policy_sdk::metadata::ProtocolVersion; use kubewarden_policy_sdk::response::ValidationResponse as PolicyValidationResponse; use kubewarden_policy_sdk::settings::SettingsValidationResponse; @@ -54,9 +56,22 @@ pub(crate) fn host_callback( } }, "oci" => match operation { - "v1/verify" | "v2/verify" => { - let req_type: CallbackRequestType = + "v1/verify" => { + let req: SigstoreVerificationInputV1 = serde_json::from_slice(payload.to_vec().as_ref())?; + let req_type: CallbackRequestType = req.into(); + let (tx, rx) = oneshot::channel::>(); + let req = CallbackRequest { + request: req_type, + response_channel: tx, + }; + + send_request_and_wait_for_response(policy_id, binding, operation, req, rx) + } + "v2/verify" => { + let req: SigstoreVerificationInputV2 = + serde_json::from_slice(payload.to_vec().as_ref())?; + let req_type: CallbackRequestType = req.into(); let (tx, rx) = oneshot::channel::>(); let req = CallbackRequest { request: req_type,