From c7a7fc1267a7d43e5075a8d025212bd68e02ae06 Mon Sep 17 00:00:00 2001 From: Arron Wang Date: Tue, 8 Mar 2022 16:16:16 +0800 Subject: [PATCH] image: Enable image-rs crate to pull image inside guest Image-rs crate image pull/decrypt/decompression/unpack/mount features are ready now. With image-rs pull_image API, the downloaded container image layers will store at IMAGE_RS_WORK_DIR, and generated bundle dir with rootfs and config.json will be saved under CONTAINER_BASE/cid directory. Fixes: #3860 Signed-off-by: Arron Wang --- src/agent/Cargo.lock | 355 ++++++++++++++++++++++++++++++++----- src/agent/Cargo.toml | 4 +- src/agent/src/image_rpc.rs | 276 +++------------------------- 3 files changed, 332 insertions(+), 303 deletions(-) diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 916ec61172..5c28cb1d29 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -24,12 +24,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ "cfg-if 1.0.0", - "cipher", + "cipher 0.3.0", "cpufeatures", - "ctr", "opaque-debug", ] +[[package]] +name = "aes" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba" +dependencies = [ + "cfg-if 1.0.0", + "cipher 0.4.3", + "cpufeatures", +] + [[package]] name = "aes-gcm" version = "0.9.4" @@ -37,9 +47,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" dependencies = [ "aead", - "aes", - "cipher", - "ctr", + "aes 0.7.5", + "cipher 0.3.0", + "ctr 0.8.0", "ghash", "subtle", ] @@ -246,6 +256,9 @@ name = "cc" version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +dependencies = [ + "jobserver", +] [[package]] name = "cfg-if" @@ -293,6 +306,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "3.0.1" @@ -403,11 +426,22 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" dependencies = [ "generic-array", + "typenum", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", ] [[package]] @@ -416,7 +450,51 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" dependencies = [ - "cipher", + "cipher 0.3.0", +] + +[[package]] +name = "ctr" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d14f329cfbaf5d0e06b5e87fff7e265d2673c5ea7d2c27691a2c107db1442a0" +dependencies = [ + "cipher 0.4.3", +] + +[[package]] +name = "darling" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" +dependencies = [ + "darling_core", + "quote", + "syn", ] [[package]] @@ -430,6 +508,37 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_builder" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73" +dependencies = [ + "derive_builder_core", + "syn", +] + [[package]] name = "digest" version = "0.9.0" @@ -487,6 +596,18 @@ dependencies = [ "libc", ] +[[package]] +name = "filetime" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi", +] + [[package]] name = "fixedbitset" version = "0.2.0" @@ -657,6 +778,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ghash" version = "0.4.4" @@ -678,7 +811,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.6", + "http", "indexmap", "slab", "tokio", @@ -722,6 +855,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.12.0" @@ -731,17 +874,6 @@ dependencies = [ "digest 0.10.1", ] -[[package]] -name = "http" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" -dependencies = [ - "bytes 0.4.12", - "fnv", - "itoa 0.4.8", -] - [[package]] name = "http" version = "0.2.6" @@ -760,7 +892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes 1.1.0", - "http 0.2.6", + "http", "pin-project-lite", ] @@ -787,7 +919,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.6", + "http", "http-body", "httparse", "httpdate", @@ -827,22 +959,26 @@ dependencies = [ [[package]] name = "hyperx" -version = "0.13.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a94cbc2c6f63028e5736ca4e811ae36d3990059c384cbe68298c66728a9776" +checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" dependencies = [ "base64 0.10.1", - "bytes 0.4.12", - "http 0.1.21", - "httparse", + "bytes 1.1.0", + "http", + "httpdate", "language-tags", - "log", "mime", - "percent-encoding 1.0.1", - "time 0.1.43", + "percent-encoding 2.1.0", "unicase 2.6.0", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.1.5" @@ -865,6 +1001,27 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image-rs" +version = "0.1.0" +source = "git+https://github.com/confidential-containers/image-rs#8dc4d9d1c0df76e8bd021d3ee43c98ffc1ca6b70" +dependencies = [ + "anyhow", + "flate2", + "futures-util", + "libc", + "nix 0.23.1", + "oci-distribution", + "oci-spec", + "ocicrypt-rs", + "serde", + "serde_json", + "sha2 0.10.0", + "tar", + "tokio", + "zstd", +] + [[package]] name = "indexmap" version = "1.7.0" @@ -897,6 +1054,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1f03d4ab4d5dc9ec2d219f86c15d2a15fc08239d1cd3b2d6a19717c0a2f443" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -951,6 +1117,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +[[package]] +name = "jobserver" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +dependencies = [ + "libc", +] + [[package]] name = "josekit" version = "0.7.4" @@ -977,6 +1152,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jwt" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98328bb4f360e6b2ceb1f95645602c7014000ef0c3809963df8ad3a3a09f8d99" +dependencies = [ + "base64 0.13.0", + "crypto-mac", + "digest 0.9.0", + "hmac 0.11.0", + "serde", + "serde_json", + "sha2 0.9.8", +] + [[package]] name = "kata-agent" version = "0.1.0" @@ -988,6 +1178,7 @@ dependencies = [ "cgroups-rs", "clap", "futures", + "image-rs", "ipnetwork", "lazy_static", "libc", @@ -997,8 +1188,6 @@ dependencies = [ "netlink-sys", "nix 0.23.1", "oci", - "oci-distribution", - "ocicrypt-rs", "openssl", "opentelemetry", "procfs 0.12.0", @@ -1013,7 +1202,6 @@ dependencies = [ "serde", "serde_json", "serial_test", - "sha2 0.9.8", "slog", "slog-scope", "slog-stdlog", @@ -1032,9 +1220,9 @@ dependencies = [ [[package]] name = "language-tags" -version = "0.2.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" [[package]] name = "lazy_static" @@ -1354,14 +1542,15 @@ dependencies = [ [[package]] name = "oci-distribution" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c9f50cb73141efc685844c84c98c361429c3a7db550d8d9888af037d93358a7" +version = "0.8.1" +source = "git+https://github.com/arronwy/oci-distribution?branch=export_pull_layer#3dfe4be9b0f8a04ec2bf4bea0374c0d76e9428a0" dependencies = [ "anyhow", "futures-util", "hyperx", + "jwt", "lazy_static", + "olpc-cjson", "regex", "reqwest", "serde", @@ -1369,21 +1558,35 @@ dependencies = [ "sha2 0.9.8", "tokio", "tracing", + "unicase 1.4.2", + "url 1.7.2", "www-authenticate", ] +[[package]] +name = "oci-spec" +version = "0.5.5" +source = "git+https://github.com/containers/oci-spec-rs#6ce2be8e775d7469e0f28dcc089d7e72308c6744" +dependencies = [ + "derive_builder", + "getset", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "ocicrypt-rs" version = "0.1.0" -source = "git+https://github.com/containers/ocicrypt-rs#c4e1505a7bb2f1f556b653b180fef972fa12ae79" +source = "git+https://github.com/arronwy/ocicrypt-rs?branch=oci_distribution#e9b8111a1973d70b99bec05a96dfbb0f46c4e467" dependencies = [ - "aes", + "aes 0.8.1", "aes-gcm", "anyhow", "base64 0.13.0", "base64-serde", - "ctr", - "hmac", + "ctr 0.9.1", + "hmac 0.12.0", "josekit", "lazy_static", "oci-distribution", @@ -1399,6 +1602,17 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "olpc-cjson" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ca49fe685014bbf124ee547da94ed7bb65a6eb9dc9c4711773c081af96a39c" +dependencies = [ + "serde", + "serde_json", + "unicode-normalization", +] + [[package]] name = "once_cell" version = "1.9.0" @@ -1984,7 +2198,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http 0.2.6", + "http", "http-body", "hyper", "hyper-tls", @@ -2352,6 +2566,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + [[package]] name = "tempfile" version = "3.2.0" @@ -2554,7 +2779,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 0.2.6", + "http", "http-body", "hyper", "hyper-timeout", @@ -3048,11 +3273,49 @@ dependencies = [ [[package]] name = "www-authenticate" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c62efb8259cda4e4c732287397701237b78daa4c43edcf3e613c8503a6c07dd" +checksum = "02fd1970505d8d9842104b229ba0c6b6331c0897677d0fc0517ea657e77428d0" dependencies = [ "hyperx", "unicase 1.4.2", "url 1.7.2", ] + +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +dependencies = [ + "libc", +] + +[[package]] +name = "zstd" +version = "0.9.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "4.1.3+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "1.6.2+zstd.1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" +dependencies = [ + "cc", + "libc", +] diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 33cad67144..082fc91437 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -64,9 +64,7 @@ toml = "0.5.8" clap = { version = "3.0.1", features = ["derive"] } # Image pull/decrypt -oci-distribution = "0.7.0" -ocicrypt-rs = { git = "https://github.com/containers/ocicrypt-rs" } -sha2 = "0.9.8" +image-rs = { git = "https://github.com/confidential-containers/image-rs" } # "vendored" feature for openssl is required by musl build openssl = { version = "0.10.38", features = ["vendored"] } diff --git a/src/agent/src/image_rpc.rs b/src/agent/src/image_rpc.rs index e1e68e3a6d..79edc483bc 100644 --- a/src/agent/src/image_rpc.rs +++ b/src/agent/src/image_rpc.rs @@ -5,7 +5,7 @@ use std::env; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::process::{Command, ExitStatus}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -13,8 +13,6 @@ use std::sync::Arc; use anyhow::{anyhow, ensure, Result}; use async_trait::async_trait; use protocols::image; -use std::convert::TryFrom; -use std::fs::File; use tokio::sync::Mutex; use ttrpc::{self, error::get_rpc_status as ttrpc_error}; @@ -22,20 +20,8 @@ use crate::rpc::{verify_cid, CONTAINER_BASE}; use crate::sandbox::Sandbox; use crate::AGENT_CONFIG; -use oci_distribution::client::{ImageData, ImageLayer}; -use oci_distribution::manifest::{OciDescriptor, OciManifest}; -use oci_distribution::{manifest, secrets::RegistryAuth, Client, Reference}; -use ocicrypt_rs::config::CryptoConfig; -use ocicrypt_rs::encryption::decrypt_layer; -use ocicrypt_rs::helpers::create_decrypt_config; -use ocicrypt_rs::spec::{ - MEDIA_TYPE_LAYER_ENC, MEDIA_TYPE_LAYER_GZIP_ENC, MEDIA_TYPE_LAYER_NON_DISTRIBUTABLE_ENC, - MEDIA_TYPE_LAYER_NON_DISTRIBUTABLE_GZIP_ENC, -}; -use serde::Serialize; -use sha2::{Digest, Sha256}; -use std::collections::HashMap; -use std::io::{Read, Write}; +use image_rs::image::ImageClient; +use std::io::Write; const SKOPEO_PATH: &str = "/usr/bin/skopeo"; const UMOCI_PATH: &str = "/usr/local/bin/umoci"; @@ -44,12 +30,8 @@ const AA_PATH: &str = "/usr/local/bin/attestation-agent"; const AA_KEYPROVIDER_PORT: &str = "127.0.0.1:50000"; const AA_GETRESOURCE_PORT: &str = "127.0.0.1:50001"; const OCICRYPT_CONFIG_PATH: &str = "/tmp/ocicrypt_config.json"; -const OCI_ANNOTATION_REF_NAME: &str = "org.opencontainers.image.ref.name"; -const OCI_IMAGE_MANIFEST_NAME: &str = "application/vnd.oci.image.manifest.v1+json"; -const OCI_LAYOUT: &str = r#"{"imageLayoutVersion": "1.0.0"}"#; -const IMAGE_DOCKER_LAYER_FOREIGN_GZIP_MEDIA_TYPE: &str = - "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"; -const DIGEST_SHA256: &str = "sha256"; +// kata rootfs is readonly, use tmpfs before CC storage is implemented. +const KATA_CC_IMAGE_WORK_DIR: &str = "/run/image/"; // Convenience macro to obtain the scope logger macro_rules! sl { @@ -58,234 +40,22 @@ macro_rules! sl { }; } -#[derive(Serialize, Debug, Default, Clone)] -#[serde(rename_all = "camelCase")] -pub struct IndexDescriptor { - pub schema_version: u8, - pub manifests: Vec, -} - pub struct ImageService { sandbox: Arc>, attestation_agent_started: AtomicBool, + image_client: Arc>, } impl ImageService { pub fn new(sandbox: Arc>) -> Self { + env::set_var("CC_IMAGE_WORK_DIR", KATA_CC_IMAGE_WORK_DIR); Self { sandbox, attestation_agent_started: AtomicBool::new(false), + image_client: Arc::new(Mutex::new(ImageClient::default())), } } - fn build_oci_path(cid: &str) -> PathBuf { - let mut oci_path = PathBuf::from("/tmp"); - oci_path.push(cid); - oci_path.push(IMAGE_OCI); - oci_path - } - - fn decrypt_layer_data( - layer: &ImageLayer, - layer_digest: &str, - image_manifest: &mut OciManifest, - crypto_config: &CryptoConfig, - oci_blob_path: &Path, - ) -> Result<()> { - if let Some(decrypt_config) = &crypto_config.decrypt_config { - for layer_desc in image_manifest.layers.iter_mut() { - if layer_desc.digest.as_str() == layer_digest { - let (layer_decryptor, _dec_digest) = - decrypt_layer(decrypt_config, layer.data.as_slice(), layer_desc, false)?; - let mut plaintxt_data: Vec = Vec::new(); - let mut decryptor = - layer_decryptor.ok_or_else(|| anyhow!("Missing layer decryptor"))?; - - decryptor.read_to_end(&mut plaintxt_data)?; - let layer_name = format!("{:x}", Sha256::digest(&plaintxt_data)); - let mut out_file = File::create(oci_blob_path.join(&layer_name))?; - out_file.write_all(&plaintxt_data)?; - layer_desc.media_type = manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE.to_string(); - - layer_desc.digest = format!("{}:{}", DIGEST_SHA256, layer_name); - } - } - } else { - return Err(anyhow!("No decrypt config available")); - } - - Ok(()) - } - - fn handle_layer_data( - image_data: &ImageData, - image_manifest: &mut OciManifest, - crypto_config: &CryptoConfig, - oci_blob_path: &Path, - ) -> Result<()> { - for layer in image_data.layers.iter() { - let layer_digest = layer.clone().sha256_digest(); - - if layer.media_type == MEDIA_TYPE_LAYER_GZIP_ENC - || layer.media_type == MEDIA_TYPE_LAYER_ENC - { - Self::decrypt_layer_data( - layer, - &layer_digest, - image_manifest, - crypto_config, - oci_blob_path, - )?; - } else if let Some(layer_name) = - layer_digest.strip_prefix(format!("{}:", DIGEST_SHA256).as_str()) - { - let mut out_file = File::create(oci_blob_path.join(&layer_name))?; - out_file.write_all(&layer.data)?; - } else { - error!( - sl!(), - "layer digest algo not supported:: {:?}", layer_digest - ); - } - } - - Ok(()) - } - - #[tokio::main] - async fn download_image( - image: &str, - auth: &RegistryAuth, - ) -> anyhow::Result<(OciManifest, String, ImageData)> { - let reference = Reference::try_from(image)?; - let mut client = Client::default(); - let (image_manifest, _image_digest, image_config) = - client.pull_manifest_and_config(&reference, auth).await?; - - // TODO: Get the value from config - let max_attempt = 2; - let attempt_interval = 1; - for i in 1..max_attempt { - match client - .pull( - &reference, - auth, - vec![ - manifest::IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE, - manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE, - MEDIA_TYPE_LAYER_GZIP_ENC, - MEDIA_TYPE_LAYER_ENC, - MEDIA_TYPE_LAYER_NON_DISTRIBUTABLE_ENC, - MEDIA_TYPE_LAYER_NON_DISTRIBUTABLE_GZIP_ENC, - ], - ) - .await - { - Ok(data) => return Ok((image_manifest, image_config, data)), - Err(e) => { - info!( - sl!(), - "Got error on pull call attempt #{}. Will retry in {}s: {:?}", - attempt_interval, - i, - e - ); - tokio::time::sleep(tokio::time::Duration::from_secs(attempt_interval)).await; - } - } - } - - Err(anyhow!("Failed to download image data")) - } - - fn pull_image_with_oci_distribution( - image: &str, - cid: &str, - source_creds: &Option, - aa_kbc_params: &str, - ) -> Result<()> { - let oci_path = Self::build_oci_path(cid); - fs::create_dir_all(&oci_path)?; - - let mut auth = RegistryAuth::Anonymous; - if let Some(source_creds) = source_creds { - if let Some((username, password)) = source_creds.split_once(':') { - auth = RegistryAuth::Basic(username.to_string(), password.to_string()); - } else { - return Err(anyhow!("Invalid authentication info ({:?})", source_creds)); - } - } - - let (mut image_manifest, image_config, image_data) = Self::download_image(image, &auth)?; - - // Prepare OCI layout storage for umoci - image_manifest.config.media_type = manifest::IMAGE_CONFIG_MEDIA_TYPE.to_string(); - // TODO: support other digest algo like sha512 - let oci_blob_path = oci_path.join(format!("blobs/{}", DIGEST_SHA256)); - fs::create_dir_all(&oci_blob_path)?; - - if let Some(config_name) = &image_manifest - .config - .digest - .strip_prefix(format!("{}:", DIGEST_SHA256).as_str()) - { - let mut out_file = File::create(oci_blob_path.join(config_name))?; - out_file.write_all(image_config.as_bytes())?; - } - - let mut cc = CryptoConfig::default(); - - if !aa_kbc_params.is_empty() { - let decrypt_config = format!("provider:attestation-agent:{}", aa_kbc_params); - cc = create_decrypt_config(vec![decrypt_config], vec![])?; - } - - // Covert docker layer media type to OCI type - for layer_desc in image_manifest.layers.iter_mut() { - if layer_desc.media_type == manifest::IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE - || layer_desc.media_type == IMAGE_DOCKER_LAYER_FOREIGN_GZIP_MEDIA_TYPE - { - layer_desc.media_type = manifest::IMAGE_LAYER_GZIP_MEDIA_TYPE.to_string(); - } - } - - Self::handle_layer_data(&image_data, &mut image_manifest, &cc, &oci_blob_path)?; - - let manifest_json = serde_json::to_string(&image_manifest)?; - let manifest_digest = format!("{:x}", Sha256::digest(manifest_json.as_bytes())); - - let mut out_file = File::create(oci_blob_path.join(manifest_digest))?; - out_file.write_all(manifest_json.as_bytes())?; - - let mut annotations = HashMap::new(); - annotations.insert(OCI_ANNOTATION_REF_NAME.to_string(), "latest".to_string()); - - let manifest_descriptor = OciDescriptor { - media_type: OCI_IMAGE_MANIFEST_NAME.to_string(), - digest: format!( - "{}:{:x}", - DIGEST_SHA256, - Sha256::digest(manifest_json.as_bytes()) - ), - size: manifest_json.len() as i64, - annotations: Some(annotations), - ..Default::default() - }; - - let index_descriptor = IndexDescriptor { - schema_version: image_manifest.schema_version, - manifests: vec![manifest_descriptor], - }; - - let mut out_file = File::create(format!("{}/index.json", oci_path.to_string_lossy()))?; - out_file.write_all(serde_json::to_string(&index_descriptor)?.as_bytes())?; - - let mut out_file = File::create(format!("{}/oci-layout", oci_path.to_string_lossy()))?; - out_file.write_all(OCI_LAYOUT.as_bytes())?; - - Ok(()) - } - fn pull_image_from_registry( image: &str, cid: &str, @@ -450,26 +220,24 @@ impl ImageService { &policy_path, aa_kbc_params, )?; + + Self::unpack_image(&cid)?; } else { - let image = image.to_string(); - let cid = cid.to_string(); - let source_creds = - (!req.get_source_creds().is_empty()).then(|| req.get_source_creds().to_string()); - let aa_kbc_params = aa_kbc_params.to_string(); + let bundle_path = Path::new(CONTAINER_BASE).join(&cid); + fs::create_dir_all(&bundle_path)?; - // ocicrypt-rs keyprovider module will create a new runtime to talk with - // attestation agent, to avoid startup a runtime within a runtime, we - // spawn a new thread here. - tokio::task::spawn_blocking(move || { - Self::pull_image_with_oci_distribution(&image, &cid, &source_creds, &aa_kbc_params) - .map_err(|err| warn!(sl!(), "pull image failed: {:?}", err)) - .ok(); - }) - .await?; + let decrypt_config = format!("provider:attestation-agent:{}", aa_kbc_params); + + info!(sl!(), "pull image {:?}, bundle path {:?}", cid, bundle_path); + // Image layers will store at KATA_CC_IMAGE_WORK_DIR, generated bundles + // with rootfs and config.json will store under CONTAINER_BASE/cid. + self.image_client + .lock() + .await + .pull_image(image, &bundle_path, &source_creds, &Some(&decrypt_config)) + .await?; } - Self::unpack_image(&cid)?; - let mut sandbox = self.sandbox.lock().await; sandbox.images.insert(String::from(image), cid.to_string()); Ok(image.to_owned())