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 <arron.wang@intel.com>
This commit is contained in:
Arron Wang 2022-03-08 16:16:16 +08:00
parent 6fdafd47ef
commit c7a7fc1267
3 changed files with 332 additions and 303 deletions

355
src/agent/Cargo.lock generated
View File

@ -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",
]

View File

@ -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"] }

View File

@ -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<OciDescriptor>,
}
pub struct ImageService {
sandbox: Arc<Mutex<Sandbox>>,
attestation_agent_started: AtomicBool,
image_client: Arc<Mutex<ImageClient>>,
}
impl ImageService {
pub fn new(sandbox: Arc<Mutex<Sandbox>>) -> 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<u8> = 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<String>,
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,25 +220,23 @@ impl ImageService {
&policy_path,
aa_kbc_params,
)?;
} 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();
// 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?;
}
Self::unpack_image(&cid)?;
} else {
let bundle_path = Path::new(CONTAINER_BASE).join(&cid);
fs::create_dir_all(&bundle_path)?;
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?;
}
let mut sandbox = self.sandbox.lock().await;
sandbox.images.insert(String::from(image), cid.to_string());