diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index a69195f92..fd0799f36 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -191,9 +191,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" +checksum = "d7ebdfa2ebdab6b1760375fa7d6f382b9f486eac35fc994625a00e89280bdbb7" dependencies = [ "async-task", "concurrent-queue", @@ -324,7 +324,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -386,7 +386,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -560,7 +560,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afb15541e888071f64592c0b4364fdff21b7cb0a247f984296699351963a8721" dependencies = [ "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -656,7 +656,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", "syn_derive", ] @@ -729,9 +729,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "bzip2" @@ -805,13 +805,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.106" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066fce287b1d4eafef758e89e09d724a24808a9196fe9756b8ca90e86d0719a2" +checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -979,7 +978,7 @@ version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "memchr", ] @@ -1250,7 +1249,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1275,12 +1274,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.9", - "darling_macro 0.20.9", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -1312,16 +1311,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1348,13 +1347,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.9", + "darling_core 0.20.10", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1387,13 +1386,13 @@ dependencies = [ [[package]] name = "der_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1442,10 +1441,10 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d48cda787f839151732d396ac69e3473923d54312c070ee21e9effcaa8ca0b1d" dependencies = [ - "darling 0.20.9", + "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1455,7 +1454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1508,7 +1507,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1658,7 +1657,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -1804,9 +1803,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flagset" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" +checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" [[package]] name = "flate2" @@ -1923,7 +1922,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -2172,7 +2171,7 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "fnv", "itoa", ] @@ -2188,11 +2187,11 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "http", ] @@ -2202,7 +2201,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures-util", "http", "http-body", @@ -2217,11 +2216,11 @@ checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "hyper" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4fe55fb7a772d59a5ff1dfbff4fe0258d19b89fec4b233e75d35d5d2316badc" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures-channel", "futures-util", "http", @@ -2258,7 +2257,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures-channel", "futures-util", "http", @@ -2410,7 +2409,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -2795,7 +2794,7 @@ dependencies = [ "netlink-packet-utils", "netlink-sys", "nix 0.24.3", - "oci", + "oci-spec", "opentelemetry", "procfs 0.12.0", "prometheus", @@ -2805,6 +2804,7 @@ dependencies = [ "regorus", "rstest", "rtnetlink", + "runtime-spec", "rustjail", "safe-path", "scan_fmt", @@ -2848,9 +2848,10 @@ dependencies = [ "lazy_static", "libc", "nix 0.24.3", - "oci", + "oci-spec", "once_cell", "rand", + "runtime-spec", "safe-path", "serde", "serde_json", @@ -2871,7 +2872,7 @@ dependencies = [ "glob", "lazy_static", "num_cpus", - "oci", + "oci-spec", "regex", "safe-path", "serde", @@ -3354,7 +3355,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd06e90449ae973fe3888c1ff85949604ef5189b4ac9a2ae39518da1e00762d" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures", "log", "netlink-packet-core", @@ -3625,23 +3626,13 @@ dependencies = [ "memchr", ] -[[package]] -name = "oci" -version = "0.1.0" -dependencies = [ - "libc", - "serde", - "serde_derive", - "serde_json", -] - [[package]] name = "oci-distribution" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b95a2c51531af0cb93761f66094044ca6ea879320bccd35ab747ff3fcab3f422" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "chrono", "futures-util", "http", @@ -3662,12 +3653,14 @@ dependencies = [ [[package]] name = "oci-spec" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf88ddc01cc6bccbe1044adb6a29057333f523deadcb4953c011a73158cfa5e" +checksum = "3f5a3fe998d50101ae009351fec56d88a69f4ed182e11000e711068c2f5abf72" dependencies = [ "derive_builder", "getset", + "once_cell", + "regex", "serde", "serde_json", "strum 0.26.3", @@ -3846,7 +3839,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.5.2", + "redox_syscall 0.5.3", "smallvec", "windows-targets 0.52.6", ] @@ -3974,7 +3967,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -4238,7 +4231,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "prost-derive", ] @@ -4248,7 +4241,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "heck 0.3.3", "itertools 0.10.5", "log", @@ -4279,7 +4272,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "prost", ] @@ -4354,7 +4347,8 @@ name = "protocols" version = "0.1.0" dependencies = [ "async-trait", - "oci", + "kata-sys-util", + "oci-spec", "protobuf 3.5.0", "serde", "serde_json", @@ -4404,7 +4398,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "pin-project-lite", "quinn-proto", "quinn-udp", @@ -4421,7 +4415,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "rand", "ring", "rustc-hash", @@ -4539,9 +4533,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ "bitflags 2.6.0", ] @@ -4640,7 +4634,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ "base64 0.22.1", - "bytes 1.6.0", + "bytes 1.6.1", "cookie", "cookie_store", "futures-core", @@ -4732,7 +4726,7 @@ checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" dependencies = [ "bitvec", "bytecheck", - "bytes 1.6.0", + "bytes 1.6.1", "hashbrown 0.12.3", "ptr_meta", "rend", @@ -4808,7 +4802,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.70", + "syn 2.0.71", "unicode-ident", ] @@ -4827,6 +4821,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "runtime-spec" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "rust_decimal" version = "1.35.0" @@ -4835,7 +4839,7 @@ checksum = "1790d1c4c0ca81211399e0e0af16333276f375209e71a37b67698a373db5b47a" dependencies = [ "arrayvec", "borsh", - "bytes 1.6.0", + "bytes 1.6.1", "num-traits", "rand", "rkyv", @@ -4910,12 +4914,13 @@ dependencies = [ "libc", "libseccomp", "nix 0.24.3", - "oci", + "oci-spec", "path-absolutize", "protobuf 3.5.0", "protocols", "regex", "rlimit", + "runtime-spec", "scan_fmt", "scopeguard", "serde", @@ -5042,7 +5047,7 @@ checksum = "d2ee4885492bb655bfa05d039cd9163eb8fe9f79ddebf00ca23a1637510c2fd2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5091,9 +5096,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "sequoia-openpgp" -version = "1.21.1" +version = "1.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b870b0275eeae174058fcf0ce5affccaaafeb7eceeabce8d6c7f51fbe6a41e2a" +checksum = "13261ee216b44d932ef93b2d4a75d45199bef77864bcc5b77ecfc7bc0ecb02d6" dependencies = [ "aes", "aes-gcm", @@ -5194,7 +5199,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5216,7 +5221,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5611,7 +5616,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5624,7 +5629,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5669,9 +5674,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.70" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -5687,7 +5692,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5704,7 +5709,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5781,22 +5786,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5892,7 +5897,7 @@ checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5902,7 +5907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", - "bytes 1.6.0", + "bytes 1.6.1", "libc", "mio", "num_cpus", @@ -5922,7 +5927,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -5953,7 +5958,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures-core", "futures-sink", "log", @@ -5967,7 +5972,7 @@ version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures-core", "futures-sink", "pin-project-lite", @@ -5993,7 +5998,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52a15c15b1bc91f90902347eff163b5b682643aff0c8e972912cca79bd9208dd" dependencies = [ - "bytes 1.6.0", + "bytes 1.6.1", "futures", "libc", "tokio", @@ -6084,7 +6089,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -6454,7 +6459,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -6488,7 +6493,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6937,7 +6942,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", "synstructure", ] @@ -7024,7 +7029,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", "synstructure", ] @@ -7045,7 +7050,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] @@ -7067,7 +7072,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.70", + "syn 2.0.71", ] [[package]] diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 2df8ce15f..efdf652cd 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -6,7 +6,8 @@ edition = "2018" license = "Apache-2.0" [dependencies] -oci = { path = "../libs/oci" } +runtime-spec = { path = "../libs/runtime-spec" } +oci-spec = { version = "0.6.8", features = ["runtime"] } rustjail = { path = "rustjail" } protocols = { path = "../libs/protocols", features = ["async", "with-serde"] } lazy_static = "1.3.0" @@ -19,7 +20,7 @@ serde_json = "1.0.39" scan_fmt = "0.2.3" scopeguard = "1.0.0" thiserror = "1.0.26" -regex = "1.10.4" +regex = "1.10.5" serial_test = "0.5.1" url = "2.5.0" derivative = "2.2.0" diff --git a/src/agent/rustjail/Cargo.toml b/src/agent/rustjail/Cargo.toml index 4c9f1c10b..9278c9e66 100644 --- a/src/agent/rustjail/Cargo.toml +++ b/src/agent/rustjail/Cargo.toml @@ -10,7 +10,8 @@ awaitgroup = "0.6.0" serde = "1.0.91" serde_json = "1.0.39" serde_derive = "1.0.91" -oci = { path = "../../libs/oci" } +runtime-spec = { path = "../../libs/runtime-spec" } +oci-spec = { version = "0.6.8", features = ["runtime"] } protocols = { path ="../../libs/protocols" } kata-sys-util = { path = "../../libs/kata-sys-util" } caps = "0.5.0" @@ -44,6 +45,7 @@ xattr = "0.2.3" serial_test = "0.5.0" tempfile = "3.1.0" test-utils = { path = "../../libs/test-utils" } +protocols = { path ="../../libs/protocols" } [features] seccomp = ["libseccomp"] diff --git a/src/agent/rustjail/src/capabilities.rs b/src/agent/rustjail/src/capabilities.rs index 0abc6f643..4dbf9089f 100644 --- a/src/agent/rustjail/src/capabilities.rs +++ b/src/agent/rustjail/src/capabilities.rs @@ -10,17 +10,20 @@ use crate::log_child; use crate::sync::write_count; use anyhow::{anyhow, Result}; use caps::{self, runtime, CapSet, Capability, CapsHashSet}; -use oci::LinuxCapabilities; +use oci::{Capability as LinuxCapability, LinuxCapabilities}; +use oci_spec::runtime as oci; +use std::collections::HashSet; use std::os::unix::io::RawFd; use std::str::FromStr; -fn to_capshashset(cfd_log: RawFd, caps: &[String]) -> CapsHashSet { +fn to_capshashset(cfd_log: RawFd, capabilities: &Option>) -> CapsHashSet { let mut r = CapsHashSet::new(); - + let binding: HashSet = HashSet::new(); + let caps = capabilities.as_ref().unwrap_or(&binding); for cap in caps.iter() { - match Capability::from_str(cap) { + match Capability::from_str(&format!("CAP_{}", cap)) { Err(_) => { - log_child!(cfd_log, "{} is not a cap", cap); + log_child!(cfd_log, "{} is not a cap", &cap.to_string()); continue; } Ok(c) => r.insert(c), @@ -48,33 +51,33 @@ pub fn reset_effective() -> Result<()> { pub fn drop_privileges(cfd_log: RawFd, caps: &LinuxCapabilities) -> Result<()> { let all = get_all_caps(); - for c in all.difference(&to_capshashset(cfd_log, caps.bounding.as_ref())) { + for c in all.difference(&to_capshashset(cfd_log, caps.bounding())) { caps::drop(None, CapSet::Bounding, *c).map_err(|e| anyhow!(e.to_string()))?; } caps::set( None, CapSet::Effective, - &to_capshashset(cfd_log, caps.effective.as_ref()), + &to_capshashset(cfd_log, caps.effective()), ) .map_err(|e| anyhow!(e.to_string()))?; caps::set( None, CapSet::Permitted, - &to_capshashset(cfd_log, caps.permitted.as_ref()), + &to_capshashset(cfd_log, caps.permitted()), ) .map_err(|e| anyhow!(e.to_string()))?; caps::set( None, CapSet::Inheritable, - &to_capshashset(cfd_log, caps.inheritable.as_ref()), + &to_capshashset(cfd_log, caps.inheritable()), ) .map_err(|e| anyhow!(e.to_string()))?; let _ = caps::set( None, CapSet::Ambient, - &to_capshashset(cfd_log, caps.ambient.as_ref()), + &to_capshashset(cfd_log, caps.ambient()), ) .map_err(|_| log_child!(cfd_log, "failed to set ambient capability")); diff --git a/src/agent/rustjail/src/cgroups/fs/mod.rs b/src/agent/rustjail/src/cgroups/fs/mod.rs index ce1ae12b1..c2e0138b3 100644 --- a/src/agent/rustjail/src/cgroups/fs/mod.rs +++ b/src/agent/rustjail/src/cgroups/fs/mod.rs @@ -23,9 +23,10 @@ use crate::container::DEFAULT_DEVICES; use anyhow::{anyhow, Context, Result}; use libc::{self, pid_t}; use oci::{ - LinuxBlockIo, LinuxCpu, LinuxDevice, LinuxDeviceCgroup, LinuxHugepageLimit, LinuxMemory, - LinuxNetwork, LinuxPids, LinuxResources, Spec, + LinuxBlockIo, LinuxCpu, LinuxDevice, LinuxDeviceCgroup, LinuxDeviceCgroupBuilder, + LinuxHugepageLimit, LinuxMemory, LinuxNetwork, LinuxPids, LinuxResources, Spec, }; +use oci_spec::runtime as oci; use protobuf::MessageField; use protocols::agent::{ @@ -72,7 +73,7 @@ pub struct Manager { // set_resource is used to set reources by cgroup controller. macro_rules! set_resource { ($cont:ident, $func:ident, $res:ident, $field:ident) => { - let resource_value = $res.$field.unwrap_or(0); + let resource_value = $res.$field().unwrap_or(0); if resource_value != 0 { $cont.$func(resource_value)?; } @@ -95,38 +96,40 @@ impl CgroupManager for Manager { let pod_res = &mut cgroups::Resources::default(); // set cpuset and cpu reources - if let Some(cpu) = &r.cpu { + if let Some(cpu) = &r.cpu() { set_cpu_resources(&self.cgroup, cpu)?; } // set memory resources - if let Some(memory) = &r.memory { + if let Some(memory) = &r.memory() { set_memory_resources(&self.cgroup, memory, update)?; } // set pids resources - if let Some(pids_resources) = &r.pids { + if let Some(pids_resources) = &r.pids() { set_pids_resources(&self.cgroup, pids_resources)?; } // set block_io resources - if let Some(blkio) = &r.block_io { + if let Some(blkio) = &r.block_io() { set_block_io_resources(&self.cgroup, blkio, res); } // set hugepages resources - if !r.hugepage_limits.is_empty() { - set_hugepages_resources(&self.cgroup, &r.hugepage_limits, res); + if let Some(hugepage_limits) = r.hugepage_limits() { + set_hugepages_resources(&self.cgroup, hugepage_limits, res); } // set network resources - if let Some(network) = &r.network { + if let Some(network) = &r.network() { set_network_resources(&self.cgroup, network, res); } // set devices resources if !self.devcg_allowed_all { - set_devices_resources(&self.cgroup, &r.devices, res, pod_res); + if let Some(devices) = r.devices() { + set_devices_resources(&self.cgroup, devices, res, pod_res); + } } debug!( sl(), @@ -301,7 +304,7 @@ fn set_network_resources( // set classid // description can be found at https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_cls.html - let class_id = network.class_id.unwrap_or(0) as u64; + let class_id = network.class_id().unwrap_or(0) as u64; if class_id != 0 { res.network.class_id = Some(class_id); } @@ -309,10 +312,11 @@ fn set_network_resources( // set network priorities // description can be found at https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v1/net_prio.html let mut priorities = vec![]; - for p in network.priorities.iter() { + let interface_priority = network.priorities().clone().unwrap_or_default(); + for p in interface_priority.iter() { priorities.push(NetworkPriority { - name: p.name.clone(), - priority: p.priority as u64, + name: p.name().clone(), + priority: p.priority() as u64, }); } @@ -351,17 +355,18 @@ fn set_hugepages_resources( let hugetlb_controller = cg.controller_of::(); for l in hugepage_limits.iter() { - if hugetlb_controller.is_some() && hugetlb_controller.unwrap().size_supported(&l.page_size) + if hugetlb_controller.is_some() && hugetlb_controller.unwrap().size_supported(l.page_size()) { let hr = HugePageResource { - size: l.page_size.clone(), - limit: l.limit, + size: l.page_size().clone(), + limit: l.limit() as u64, }; limits.push(hr); } else { warn!( sl(), - "{} page size support cannot be verified, dropping requested limit", l.page_size + "{} page size support cannot be verified, dropping requested limit", + l.page_size() ); } } @@ -375,29 +380,47 @@ fn set_block_io_resources( ) { info!(sl(), "cgroup manager set block io"); - res.blkio.weight = blkio.weight; - res.blkio.leaf_weight = blkio.leaf_weight; + res.blkio.weight = blkio.weight(); + res.blkio.leaf_weight = blkio.leaf_weight(); let mut blk_device_resources = vec![]; - for d in blkio.weight_device.iter() { + let default_weight_device = vec![]; + let weight_device = blkio + .weight_device() + .as_ref() + .unwrap_or(&default_weight_device); + for d in weight_device.iter() { let dr = BlkIoDeviceResource { - major: d.blk.major as u64, - minor: d.blk.minor as u64, - weight: blkio.weight, - leaf_weight: blkio.leaf_weight, + major: d.major() as u64, + minor: d.minor() as u64, + weight: blkio.weight(), + leaf_weight: blkio.leaf_weight(), }; blk_device_resources.push(dr); } res.blkio.weight_device = blk_device_resources; - res.blkio.throttle_read_bps_device = - build_blk_io_device_throttle_resource(&blkio.throttle_read_bps_device); - res.blkio.throttle_write_bps_device = - build_blk_io_device_throttle_resource(&blkio.throttle_write_bps_device); - res.blkio.throttle_read_iops_device = - build_blk_io_device_throttle_resource(&blkio.throttle_read_iops_device); - res.blkio.throttle_write_iops_device = - build_blk_io_device_throttle_resource(&blkio.throttle_write_iops_device); + res.blkio.throttle_read_bps_device = build_blk_io_device_throttle_resource( + blkio.throttle_read_bps_device().as_ref().unwrap_or(&vec![]), + ); + res.blkio.throttle_write_bps_device = build_blk_io_device_throttle_resource( + blkio + .throttle_write_bps_device() + .as_ref() + .unwrap_or(&vec![]), + ); + res.blkio.throttle_read_iops_device = build_blk_io_device_throttle_resource( + blkio + .throttle_read_iops_device() + .as_ref() + .unwrap_or(&vec![]), + ); + res.blkio.throttle_write_iops_device = build_blk_io_device_throttle_resource( + blkio + .throttle_write_iops_device() + .as_ref() + .unwrap_or(&vec![]), + ); } fn set_cpu_resources(cg: &cgroups::Cgroup, cpu: &LinuxCpu) -> Result<()> { @@ -405,19 +428,19 @@ fn set_cpu_resources(cg: &cgroups::Cgroup, cpu: &LinuxCpu) -> Result<()> { let cpuset_controller: &CpuSetController = cg.controller_of().unwrap(); - if !cpu.cpus.is_empty() { - if let Err(e) = cpuset_controller.set_cpus(&cpu.cpus) { + if let Some(cpus) = cpu.cpus() { + if let Err(e) = cpuset_controller.set_cpus(cpus) { warn!(sl(), "write cpuset failed: {:?}", e); } } - if !cpu.mems.is_empty() { - cpuset_controller.set_mems(&cpu.mems)?; + if let Some(mems) = cpu.mems() { + cpuset_controller.set_mems(mems)?; } let cpu_controller: &CpuController = cg.controller_of().unwrap(); - if let Some(shares) = cpu.shares { + if let Some(shares) = cpu.shares() { let shares = if cg.v2() { convert_shares_to_v2_value(shares) } else { @@ -449,12 +472,12 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool // If the memory update is set to -1 we should also // set swap to -1, it means unlimited memory. - let mut swap = memory.swap.unwrap_or(0); - if memory.limit == Some(-1) { + let mut swap = memory.swap().unwrap_or(0); + if memory.limit() == Some(-1) { swap = -1; } - if memory.limit.is_some() && swap != 0 { + if memory.limit().is_some() && swap != 0 { let memstat = get_memory_stats(cg) .into_option() .ok_or_else(|| anyhow!("failed to get the cgroup memory stats"))?; @@ -475,7 +498,7 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool } else { set_resource!(mem_controller, set_limit, memory, limit); swap = if cg.v2() { - convert_memory_swap_to_v2_value(swap, memory.limit.unwrap_or(0))? + convert_memory_swap_to_v2_value(swap, memory.limit().unwrap_or(0))? } else { swap }; @@ -488,7 +511,7 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool set_resource!(mem_controller, set_kmem_limit, memory, kernel); set_resource!(mem_controller, set_tcp_limit, memory, kernel_tcp); - if let Some(swappiness) = memory.swappiness { + if let Some(swappiness) = memory.swappiness() { if (0..=100).contains(&swappiness) { mem_controller.set_swappiness(swappiness)?; } else { @@ -499,7 +522,7 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool } } - if memory.disable_oom_killer.unwrap_or(false) { + if memory.disable_oom_killer().unwrap_or(false) { mem_controller.disable_oom_killer()?; } @@ -509,8 +532,8 @@ fn set_memory_resources(cg: &cgroups::Cgroup, memory: &LinuxMemory, update: bool fn set_pids_resources(cg: &cgroups::Cgroup, pids: &LinuxPids) -> Result<()> { info!(sl(), "cgroup manager set pids"); let pid_controller: &PidController = cg.controller_of().unwrap(); - let v = if pids.limit > 0 { - MaxValue::Value(pids.limit) + let v = if pids.limit() > 0 { + MaxValue::Value(pids.limit()) } else { MaxValue::Max }; @@ -525,9 +548,9 @@ fn build_blk_io_device_throttle_resource( let mut blk_io_device_throttle_resources = vec![]; for d in input.iter() { let tr = BlkIoDeviceThrottleResource { - major: d.blk.major as u64, - minor: d.blk.minor as u64, - rate: d.rate, + major: d.major() as u64, + minor: d.minor() as u64, + rate: d.rate(), }; blk_io_device_throttle_resources.push(tr); } @@ -536,13 +559,20 @@ fn build_blk_io_device_throttle_resource( } fn linux_device_cgroup_to_device_resource(d: &LinuxDeviceCgroup) -> Option { - let dev_type = match DeviceType::from_char(d.r#type.chars().next()) { + let dev_type = match DeviceType::from_char(d.typ().unwrap_or_default().as_str().chars().next()) + { Some(t) => t, None => return None, }; let mut permissions: Vec = vec![]; - for p in d.access.chars().collect::>() { + for p in d + .access() + .as_ref() + .unwrap_or(&"".to_owned()) + .chars() + .collect::>() + { match p { 'r' => permissions.push(DevicePermissions::Read), 'w' => permissions.push(DevicePermissions::Write), @@ -552,10 +582,10 @@ fn linux_device_cgroup_to_device_resource(d: &LinuxDeviceCgroup) -> Option = { vec![ // all mknod to all char devices - LinuxDeviceCgroup { - allow: true, - r#type: "c".to_string(), - major: Some(WILDCARD), - minor: Some(WILDCARD), - access: "m".to_string(), - }, + LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::C) + .major(WILDCARD) + .minor(WILDCARD) + .access("m") + .build() + .unwrap(), // all mknod to all block devices - LinuxDeviceCgroup { - allow: true, - r#type: "b".to_string(), - major: Some(WILDCARD), - minor: Some(WILDCARD), - access: "m".to_string(), - }, + LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::B) + .major(WILDCARD) + .minor(WILDCARD) + .access("m") + .build() + .unwrap(), // all read/write/mknod to char device /dev/console - LinuxDeviceCgroup { - allow: true, - r#type: "c".to_string(), - major: Some(5), - minor: Some(1), - access: "rwm".to_string(), - }, + LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::C) + .major(5) + .minor(1) + .access("rwm") + .build() + .unwrap(), // all read/write/mknod to char device /dev/pts/ - LinuxDeviceCgroup { - allow: true, - r#type: "c".to_string(), - major: Some(136), - minor: Some(WILDCARD), - access: "rwm".to_string(), - }, + LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::C) + .major(136) + .minor(WILDCARD) + .access("rwm") + .build() + .unwrap(), // all read/write/mknod to char device /dev/ptmx - LinuxDeviceCgroup { - allow: true, - r#type: "c".to_string(), - major: Some(5), - minor: Some(2), - access: "rwm".to_string(), - }, + LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::C) + .major(5) + .minor(2) + .access("rwm") + .build() + .unwrap(), // all read/write/mknod to char device /dev/net/tun - LinuxDeviceCgroup { - allow: true, - r#type: "c".to_string(), - major: Some(10), - minor: Some(200), - access: "rwm".to_string(), - }, + LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::C) + .major(10) + .minor(200) + .access("rwm") + .build() + .unwrap(), ] }; } @@ -1218,19 +1254,24 @@ impl Manager { /// Check if OCI spec contains a rule of allowed all devices. fn has_allowed_all_devices_rule(spec: &Spec) -> bool { - let linux = match spec.linux.as_ref() { + let linux = match spec.linux().as_ref() { Some(linux) => linux, None => return false, }; - let resources = match linux.resources.as_ref() { + let resources = match linux.resources().as_ref() { Some(resource) => resource, None => return false, }; + resources - .devices - .iter() - .find(|dev| rule_for_all_devices(dev)) - .map(|dev| dev.allow) + .devices() + .as_ref() + .and_then(|devices| { + devices + .iter() + .find(|dev| rule_for_all_devices(dev)) + .map(|dev| dev.allow()) + }) .unwrap_or_default() } } @@ -1254,7 +1295,7 @@ fn default_allowed_devices() -> Vec { /// Convert LinuxDevice to DeviceResource. fn linux_device_to_device_resource(d: &LinuxDevice) -> Option { - let dev_type = match DeviceType::from_char(d.r#type.chars().next()) { + let dev_type = match DeviceType::from_char(d.typ().as_str().chars().next()) { Some(t) => t, None => return None, }; @@ -1268,8 +1309,8 @@ fn linux_device_to_device_resource(d: &LinuxDevice) -> Option { Some(DeviceResource { allow: true, devtype: dev_type, - major: d.major, - minor: d.minor, + major: d.major(), + minor: d.minor(), access: permissions, }) } @@ -1328,7 +1369,11 @@ mod tests { use std::time::{SystemTime, UNIX_EPOCH}; use cgroups::devices::{DevicePermissions, DeviceType}; - use oci::{Linux, LinuxDeviceCgroup, LinuxResources, Spec}; + use oci::{ + LinuxBuilder, LinuxDeviceCgroup, LinuxDeviceCgroupBuilder, LinuxDeviceType, + LinuxResourcesBuilder, SpecBuilder, + }; + use oci_spec::runtime as oci; use test_utils::skip_if_not_root; use super::default_allowed_devices; @@ -1423,21 +1468,22 @@ mod tests { container_devices_list: Vec, } - let allow_all = LinuxDeviceCgroup { - allow: true, - r#type: String::new(), - major: Some(0), - minor: Some(0), - access: String::from("rwm"), - }; - - let deny_all = LinuxDeviceCgroup { - allow: false, - r#type: String::new(), - major: Some(0), - minor: Some(0), - access: String::from("rwm"), - }; + let allow_all = LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(LinuxDeviceType::A) + .major(0) + .minor(0) + .access("rwm") + .build() + .unwrap(); + let deny_all = LinuxDeviceCgroupBuilder::default() + .allow(false) + .typ(LinuxDeviceType::A) + .major(0) + .minor(0) + .access("rwm") + .build() + .unwrap(); let now = SystemTime::now() .duration_since(UNIX_EPOCH) @@ -1490,16 +1536,20 @@ mod tests { let mut managers = Vec::with_capacity(tc.devices.len()); for cid in 0..tc.devices.len() { - let spec = Spec { - linux: Some(Linux { - resources: Some(LinuxResources { - devices: tc.devices[cid].clone(), - ..Default::default() - }), - ..Default::default() - }), - ..Default::default() - }; + let spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .resources( + LinuxResourcesBuilder::default() + .devices(tc.devices[cid].clone()) + .build() + .unwrap(), + ) + .build() + .unwrap(), + ) + .build() + .unwrap(); managers.push( Manager::new(&tc.cpath[cid], &spec, Some(sandbox.devcg_info.clone())).unwrap(), ); diff --git a/src/agent/rustjail/src/cgroups/mock.rs b/src/agent/rustjail/src/cgroups/mock.rs index ce1cfb2bc..f7185403d 100644 --- a/src/agent/rustjail/src/cgroups/mock.rs +++ b/src/agent/rustjail/src/cgroups/mock.rs @@ -11,6 +11,7 @@ use anyhow::Result; use cgroups::freezer::FreezerState; use libc::{self, pid_t}; use oci::{LinuxResources, Spec}; +use oci_spec::runtime as oci; use std::any::Any; use std::collections::HashMap; use std::string::String; diff --git a/src/agent/rustjail/src/cgroups/mod.rs b/src/agent/rustjail/src/cgroups/mod.rs index 7eb8a7376..18b64966f 100644 --- a/src/agent/rustjail/src/cgroups/mod.rs +++ b/src/agent/rustjail/src/cgroups/mod.rs @@ -5,7 +5,7 @@ use anyhow::{anyhow, Result}; use core::fmt::Debug; -use oci::{LinuxDeviceCgroup, LinuxResources}; +use oci_spec::runtime::{LinuxDeviceCgroup, LinuxDeviceType, LinuxResources}; use protocols::agent::CgroupStats; use std::any::Any; @@ -75,15 +75,20 @@ impl Debug for dyn Manager + Send + Sync { /// /// The formats representing all devices between OCI spec and cgroups-rs /// are different. -/// - OCI spec: major: 0, minor: 0, type: "", access: "rwm"; +/// - OCI spec: major: Some(0), minor: Some(0), type: Some(A), access: Some("rwm"); /// - Cgroups-rs: major: -1, minor: -1, type: "a", access: "rwm"; /// - Linux: a *:* rwm #[inline] fn rule_for_all_devices(dev_cgroup: &LinuxDeviceCgroup) -> bool { - dev_cgroup.major.unwrap_or(0) == 0 - && dev_cgroup.minor.unwrap_or(0) == 0 - && (dev_cgroup.r#type.as_str() == "" || dev_cgroup.r#type.as_str() == "a") - && dev_cgroup.access.contains('r') - && dev_cgroup.access.contains('w') - && dev_cgroup.access.contains('m') + let cgrp_access = dev_cgroup.access().clone().unwrap_or_default(); + let dev_type = dev_cgroup + .typ() + .as_ref() + .map_or(LinuxDeviceType::default(), |x| *x); + dev_cgroup.major().unwrap_or(0) == 0 + && dev_cgroup.minor().unwrap_or(0) == 0 + && dev_type == LinuxDeviceType::A + && cgrp_access.contains('r') + && cgrp_access.contains('w') + && cgrp_access.contains('m') } diff --git a/src/agent/rustjail/src/cgroups/systemd/manager.rs b/src/agent/rustjail/src/cgroups/systemd/manager.rs index b4974d2bb..c3158bdf6 100644 --- a/src/agent/rustjail/src/cgroups/systemd/manager.rs +++ b/src/agent/rustjail/src/cgroups/systemd/manager.rs @@ -9,6 +9,7 @@ use anyhow::{anyhow, Result}; use cgroups::freezer::FreezerState; use libc::{self, pid_t}; use oci::LinuxResources; +use oci_spec::runtime as oci; use std::any::Any; use std::collections::HashMap; use std::convert::TryInto; diff --git a/src/agent/rustjail/src/cgroups/systemd/subsystem/cpu.rs b/src/agent/rustjail/src/cgroups/systemd/subsystem/cpu.rs index 4f7ad1f8c..ce356bb93 100644 --- a/src/agent/rustjail/src/cgroups/systemd/subsystem/cpu.rs +++ b/src/agent/rustjail/src/cgroups/systemd/subsystem/cpu.rs @@ -8,6 +8,7 @@ use super::transformer::Transformer; use anyhow::{bail, Result}; use oci::{LinuxCpu, LinuxResources}; +use oci_spec::runtime as oci; use zbus::zvariant::Value; const BASIC_SYSTEMD_VERSION: &str = "242"; @@ -25,7 +26,7 @@ impl Transformer for Cpu { cgroup_hierarchy: &CgroupHierarchy, systemd_version: &str, ) -> Result<()> { - if let Some(cpu_resources) = &r.cpu { + if let Some(cpu_resources) = &r.cpu() { match cgroup_hierarchy { CgroupHierarchy::Legacy => { Self::legacy_apply(cpu_resources, properties, systemd_version)? @@ -50,7 +51,7 @@ impl Cpu { properties: &mut Properties, systemd_version: &str, ) -> Result<()> { - if let Some(shares) = cpu_resources.shares { + if let Some(shares) = cpu_resources.shares() { // Minimum value of CPUShares should be 2, see https://github.com/systemd/systemd/blob/d19434fbf81db04d03c8cffa87821f754a86635b/src/basic/cgroup-util.h#L122 let shares = match shares { 0 => 1024, @@ -60,14 +61,14 @@ impl Cpu { properties.push(("CPUShares", Value::U64(shares))); } - if let Some(period) = cpu_resources.period { + if let Some(period) = cpu_resources.period() { if period != 0 && systemd_version >= BASIC_SYSTEMD_VERSION { properties.push(("CPUQuotaPeriodUSec", Value::U64(period))); } } - if let Some(quota) = cpu_resources.quota { - let period = cpu_resources.period.unwrap_or(DEFAULT_CPUQUOTAPERIOD); + if let Some(quota) = cpu_resources.quota() { + let period = cpu_resources.period().unwrap_or(DEFAULT_CPUQUOTAPERIOD); if period != 0 { let cpu_quota_per_sec_usec = resolve_cpuquota(quota, period); properties.push(("CPUQuotaPerSecUSec", Value::U64(cpu_quota_per_sec_usec))); @@ -86,19 +87,19 @@ impl Cpu { properties: &mut Properties, systemd_version: &str, ) -> Result<()> { - if let Some(shares) = cpu_resources.shares { + if let Some(shares) = cpu_resources.shares() { let weight = shares_to_weight(shares).unwrap(); properties.push(("CPUWeight", Value::U64(weight))); } - if let Some(period) = cpu_resources.period { + if let Some(period) = cpu_resources.period() { if period != 0 && systemd_version >= BASIC_SYSTEMD_VERSION { properties.push(("CPUQuotaPeriodUSec", Value::U64(period))); } } - if let Some(quota) = cpu_resources.quota { - let period = cpu_resources.period.unwrap_or(DEFAULT_CPUQUOTAPERIOD); + if let Some(quota) = cpu_resources.quota() { + let period = cpu_resources.period().unwrap_or(DEFAULT_CPUQUOTAPERIOD); if period != 0 { let cpu_quota_per_sec_usec = resolve_cpuquota(quota, period); properties.push(("CPUQuotaPerSecUSec", Value::U64(cpu_quota_per_sec_usec))); diff --git a/src/agent/rustjail/src/cgroups/systemd/subsystem/cpuset.rs b/src/agent/rustjail/src/cgroups/systemd/subsystem/cpuset.rs index 3f05cdc7c..89b465295 100644 --- a/src/agent/rustjail/src/cgroups/systemd/subsystem/cpuset.rs +++ b/src/agent/rustjail/src/cgroups/systemd/subsystem/cpuset.rs @@ -10,6 +10,7 @@ use super::transformer::Transformer; use anyhow::{bail, Result}; use bit_vec::BitVec; use oci::{LinuxCpu, LinuxResources}; +use oci_spec::runtime as oci; use std::convert::{TryFrom, TryInto}; use zbus::zvariant::Value; @@ -24,7 +25,7 @@ impl Transformer for CpuSet { _: &CgroupHierarchy, systemd_version: &str, ) -> Result<()> { - if let Some(cpuset_resources) = &r.cpu { + if let Some(cpuset_resources) = &r.cpu() { Self::apply(cpuset_resources, properties, systemd_version)?; } @@ -45,15 +46,13 @@ impl CpuSet { return Ok(()); } - let cpus = cpuset_resources.cpus.as_str(); - if !cpus.is_empty() { - let cpus_vec: BitMask = cpus.try_into()?; + if let Some(cpus) = cpuset_resources.cpus().as_ref() { + let cpus_vec: BitMask = cpus.as_str().try_into()?; properties.push(("AllowedCPUs", Value::Array(cpus_vec.0.into()))); } - let mems = cpuset_resources.mems.as_str(); - if !mems.is_empty() { - let mems_vec: BitMask = mems.try_into()?; + if let Some(mems) = cpuset_resources.mems().as_ref() { + let mems_vec: BitMask = mems.as_str().try_into()?; properties.push(("AllowedMemoryNodes", Value::Array(mems_vec.0.into()))); } diff --git a/src/agent/rustjail/src/cgroups/systemd/subsystem/memory.rs b/src/agent/rustjail/src/cgroups/systemd/subsystem/memory.rs index e2ec5343c..1df4267a3 100644 --- a/src/agent/rustjail/src/cgroups/systemd/subsystem/memory.rs +++ b/src/agent/rustjail/src/cgroups/systemd/subsystem/memory.rs @@ -9,6 +9,7 @@ use super::transformer::Transformer; use anyhow::{bail, Result}; use oci::{LinuxMemory, LinuxResources}; +use oci_spec::runtime as oci; use zbus::zvariant::Value; pub struct Memory {} @@ -20,7 +21,7 @@ impl Transformer for Memory { cgroup_hierarchy: &CgroupHierarchy, _: &str, ) -> Result<()> { - if let Some(memory_resources) = &r.memory { + if let Some(memory_resources) = &r.memory() { match cgroup_hierarchy { CgroupHierarchy::Legacy => Self::legacy_apply(memory_resources, properties)?, CgroupHierarchy::Unified => Self::unified_apply(memory_resources, properties)?, @@ -35,7 +36,7 @@ impl Memory { // v1: // memory.limit <-> MemoryLimit fn legacy_apply(memory_resources: &LinuxMemory, properties: &mut Properties) -> Result<()> { - if let Some(limit) = memory_resources.limit { + if let Some(limit) = memory_resources.limit() { let limit = match limit { 1..=i64::MAX => limit as u64, 0 => u64::MAX, @@ -52,7 +53,7 @@ impl Memory { // memory.max <-> MemoryMax // memory.swap & memory.limit <-> MemorySwapMax fn unified_apply(memory_resources: &LinuxMemory, properties: &mut Properties) -> Result<()> { - if let Some(limit) = memory_resources.limit { + if let Some(limit) = memory_resources.limit() { let limit = match limit { 1..=i64::MAX => limit as u64, 0 => u64::MAX, @@ -61,7 +62,7 @@ impl Memory { properties.push(("MemoryMax", Value::U64(limit))); } - if let Some(reservation) = memory_resources.reservation { + if let Some(reservation) = memory_resources.reservation() { let reservation = match reservation { 1..=i64::MAX => reservation as u64, 0 => u64::MAX, @@ -70,11 +71,11 @@ impl Memory { properties.push(("MemoryLow", Value::U64(reservation))); } - let swap = match memory_resources.swap { + let swap = match memory_resources.swap() { Some(0) => u64::MAX, - Some(1..=i64::MAX) => match memory_resources.limit { + Some(1..=i64::MAX) => match memory_resources.limit() { Some(1..=i64::MAX) => { - (memory_resources.limit.unwrap() - memory_resources.swap.unwrap()) as u64 + (memory_resources.limit().unwrap() - memory_resources.swap().unwrap()) as u64 } _ => bail!("invalid memory.limit when memory.swap specified"), }, @@ -93,18 +94,21 @@ mod tests { use super::Memory; use super::Properties; use super::Value; + use oci_spec::runtime as oci; #[test] fn test_unified_memory() { - let memory_resources = oci::LinuxMemory { - limit: Some(736870912), - reservation: Some(536870912), - swap: Some(536870912), - kernel: Some(0), - kernel_tcp: Some(0), - swappiness: Some(0), - disable_oom_killer: Some(false), - }; + let memory_resources = oci::LinuxMemoryBuilder::default() + .limit(736870912) + .reservation(536870912) + .swap(536870912) + .kernel(0) + .kernel_tcp(0) + .swappiness(0u64) + .disable_oom_killer(false) + .build() + .unwrap(); + let mut properties: Properties = vec![]; assert_eq!( diff --git a/src/agent/rustjail/src/cgroups/systemd/subsystem/pids.rs b/src/agent/rustjail/src/cgroups/systemd/subsystem/pids.rs index 7ff1ee7c6..3025863ad 100644 --- a/src/agent/rustjail/src/cgroups/systemd/subsystem/pids.rs +++ b/src/agent/rustjail/src/cgroups/systemd/subsystem/pids.rs @@ -9,6 +9,7 @@ use super::transformer::Transformer; use anyhow::Result; use oci::{LinuxPids, LinuxResources}; +use oci_spec::runtime as oci; use zbus::zvariant::Value; pub struct Pids {} @@ -20,7 +21,7 @@ impl Transformer for Pids { _: &CgroupHierarchy, _: &str, ) -> Result<()> { - if let Some(pids_resources) = &r.pids { + if let Some(pids_resources) = &r.pids() { Self::apply(pids_resources, properties)?; } @@ -31,8 +32,8 @@ impl Transformer for Pids { // pids.limit <-> TasksMax impl Pids { fn apply(pids_resources: &LinuxPids, properties: &mut Properties) -> Result<()> { - let limit = if pids_resources.limit > 0 { - pids_resources.limit as u64 + let limit = if pids_resources.limit() > 0 { + pids_resources.limit() as u64 } else { u64::MAX }; @@ -47,10 +48,13 @@ mod tests { use super::Pids; use super::Properties; use super::Value; + use oci_spec::runtime as oci; #[test] fn test_subsystem_workflow() { - let pids_resources = oci::LinuxPids { limit: 0 }; + let mut pids_resources = oci::LinuxPids::default(); + pids_resources.set_limit(0 as i64); + let mut properties: Properties = vec![]; assert_eq!(true, Pids::apply(&pids_resources, &mut properties).is_ok()); diff --git a/src/agent/rustjail/src/cgroups/systemd/subsystem/transformer.rs b/src/agent/rustjail/src/cgroups/systemd/subsystem/transformer.rs index 952ed4dd2..7f0bcd874 100644 --- a/src/agent/rustjail/src/cgroups/systemd/subsystem/transformer.rs +++ b/src/agent/rustjail/src/cgroups/systemd/subsystem/transformer.rs @@ -6,6 +6,7 @@ use super::super::common::{CgroupHierarchy, Properties}; use anyhow::Result; use oci::LinuxResources; +use oci_spec::runtime as oci; pub trait Transformer { fn apply( diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index b5311f46a..d211381b3 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -5,8 +5,10 @@ use anyhow::{anyhow, Context, Result}; use libc::pid_t; -use oci::{ContainerState, LinuxDevice, LinuxIdMapping}; -use oci::{Linux, LinuxNamespace, LinuxResources, Spec}; +use oci::{Linux, LinuxDevice, LinuxIdMapping, LinuxNamespace, LinuxResources, Spec}; +use oci_spec::runtime as oci; +use runtime_spec as spec; +use spec::{ContainerState, State as OCIState}; use std::clone::Clone; use std::ffi::CString; use std::fmt::Display; @@ -51,7 +53,6 @@ use std::os::unix::io::AsRawFd; use protobuf::MessageField; -use oci::State as OCIState; use regex::Regex; use std::collections::HashMap; use std::os::unix::io::FromRawFd; @@ -130,82 +131,88 @@ lazy_static! { m.insert("user", CloneFlags::CLONE_NEWUSER); m.insert("ipc", CloneFlags::CLONE_NEWIPC); m.insert("pid", CloneFlags::CLONE_NEWPID); - m.insert("network", CloneFlags::CLONE_NEWNET); - m.insert("mount", CloneFlags::CLONE_NEWNS); + m.insert("net", CloneFlags::CLONE_NEWNET); + m.insert("mnt", CloneFlags::CLONE_NEWNS); m.insert("uts", CloneFlags::CLONE_NEWUTS); m.insert("cgroup", CloneFlags::CLONE_NEWCGROUP); m }; // type to name hashmap, better to be in NAMESPACES - pub static ref TYPETONAME: HashMap<&'static str, &'static str> = { + pub static ref TYPETONAME: HashMap = { let mut m = HashMap::new(); - m.insert("ipc", "ipc"); - m.insert("user", "user"); - m.insert("pid", "pid"); - m.insert("network", "net"); - m.insert("mount", "mnt"); - m.insert("cgroup", "cgroup"); - m.insert("uts", "uts"); + m.insert(oci::LinuxNamespaceType::Ipc, "ipc"); + m.insert(oci::LinuxNamespaceType::User, "user"); + m.insert(oci::LinuxNamespaceType::Pid, "pid"); + m.insert(oci::LinuxNamespaceType::Network, "net"); + m.insert(oci::LinuxNamespaceType::Mount, "mnt"); + m.insert(oci::LinuxNamespaceType::Cgroup, "cgroup"); + m.insert(oci::LinuxNamespaceType::Uts, "uts"); m }; pub static ref DEFAULT_DEVICES: Vec = { vec![ - LinuxDevice { - path: "/dev/null".to_string(), - r#type: "c".to_string(), - major: 1, - minor: 3, - file_mode: Some(0o666), - uid: Some(0xffffffff), - gid: Some(0xffffffff), - }, - LinuxDevice { - path: "/dev/zero".to_string(), - r#type: "c".to_string(), - major: 1, - minor: 5, - file_mode: Some(0o666), - uid: Some(0xffffffff), - gid: Some(0xffffffff), - }, - LinuxDevice { - path: "/dev/full".to_string(), - r#type: "c".to_string(), - major: 1, - minor: 7, - file_mode: Some(0o666), - uid: Some(0xffffffff), - gid: Some(0xffffffff), - }, - LinuxDevice { - path: "/dev/tty".to_string(), - r#type: "c".to_string(), - major: 5, - minor: 0, - file_mode: Some(0o666), - uid: Some(0xffffffff), - gid: Some(0xffffffff), - }, - LinuxDevice { - path: "/dev/urandom".to_string(), - r#type: "c".to_string(), - major: 1, - minor: 9, - file_mode: Some(0o666), - uid: Some(0xffffffff), - gid: Some(0xffffffff), - }, - LinuxDevice { - path: "/dev/random".to_string(), - r#type: "c".to_string(), - major: 1, - minor: 8, - file_mode: Some(0o666), - uid: Some(0xffffffff), - gid: Some(0xffffffff), - }, + oci::LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/null")) + .typ(oci::LinuxDeviceType::C) + .major(1) + .minor(3) + .file_mode(0o066_u32) + .uid(0xffffffff_u32) + .gid(0xffffffff_u32) + .build() + .unwrap(), + oci::LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/zero")) + .typ(oci::LinuxDeviceType::C) + .major(1) + .minor(5) + .file_mode(0o066_u32) + .uid(0xffffffff_u32) + .gid(0xffffffff_u32) + .build() + .unwrap(), + oci::LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/full")) + .typ(oci::LinuxDeviceType::C) + .major(1) + .minor(7) + .file_mode(0o066_u32) + .uid(0xffffffff_u32) + .gid(0xffffffff_u32) + .build() + .unwrap(), + oci::LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/tty")) + .typ(oci::LinuxDeviceType::C) + .major(5) + .minor(0) + .file_mode(0o066_u32) + .uid(0xffffffff_u32) + .gid(0xffffffff_u32) + .build() + .unwrap(), + oci::LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/urandom")) + .typ(oci::LinuxDeviceType::C) + .major(1) + .minor(9) + .file_mode(0o066_u32) + .uid(0xffffffff_u32) + .gid(0xffffffff_u32) + .build() + .unwrap(), + oci::LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/random")) + .typ(oci::LinuxDeviceType::C) + .major(1) + .minor(8) + .file_mode(0o066_u32) + .uid(0xffffffff_u32) + .gid(0xffffffff_u32) + .build() + .unwrap(), ] }; @@ -402,7 +409,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { let buf = read_sync(crfd)?; let state_str = std::str::from_utf8(&buf)?; - let mut state: oci::State = serde_json::from_str(state_str)?; + let mut state: OCIState = serde_json::from_str(state_str)?; log_child!(cfd_log, "notify parent to send cgroup manager"); write_sync(cwfd, SYNC_SUCCESS, "")?; @@ -416,16 +423,16 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { #[cfg(feature = "standard-oci-runtime")] let csocket_fd = console::setup_console_socket(&std::env::var(CONSOLE_SOCKET_FD)?)?; - let p = if spec.process.is_some() { - spec.process.as_ref().unwrap() + let p = if spec.process().is_some() { + spec.process().as_ref().unwrap() } else { return Err(anyhow!("didn't find process in Spec")); }; - if spec.linux.is_none() { + if spec.linux().is_none() { return Err(anyhow!(MissingLinux)); } - let linux = spec.linux.as_ref().unwrap(); + let linux = spec.linux().as_ref().unwrap(); // get namespace vector to join/new let nses = get_namespaces(linux); @@ -435,25 +442,30 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { let mut to_join = Vec::new(); for ns in &nses { - let s = NAMESPACES.get(&ns.r#type.as_str()); + let ns_type = ns.typ().to_string(); + let s = NAMESPACES.get(&ns_type.as_str()); if s.is_none() { return Err(anyhow!(InvalidNamespace)); } let s = s.unwrap(); - if ns.path.is_empty() { + if ns + .path() + .as_ref() + .map_or(true, |p| p.as_os_str().is_empty()) + { // skip the pidns since it has been done in parent process. if *s != CloneFlags::CLONE_NEWPID { to_new.set(*s, true); } } else { - let fd = - fcntl::open(ns.path.as_str(), OFlag::O_CLOEXEC, Mode::empty()).map_err(|e| { + let fd = fcntl::open(ns.path().as_ref().unwrap(), OFlag::O_CLOEXEC, Mode::empty()) + .map_err(|e| { log_child!( cfd_log, "cannot open type: {} path: {}", - ns.r#type.clone(), - ns.path.clone() + &ns.typ().to_string(), + ns.path().as_ref().unwrap().display() ); log_child!(cfd_log, "error is : {:?}", e); e @@ -469,21 +481,23 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { userns = true; } - if p.oom_score_adj.is_some() { - log_child!(cfd_log, "write oom score {}", p.oom_score_adj.unwrap()); + if p.oom_score_adj().is_some() { + log_child!(cfd_log, "write oom score {}", p.oom_score_adj().unwrap()); fs::write( "/proc/self/oom_score_adj", - p.oom_score_adj.unwrap().to_string().as_bytes(), + p.oom_score_adj().unwrap().to_string().as_bytes(), )?; } // set rlimit - for rl in p.rlimits.iter() { + let default_rlimits = Vec::new(); + let process_rlimits = p.rlimits().as_ref().unwrap_or(&default_rlimits); + for rl in process_rlimits.iter() { log_child!(cfd_log, "set resource limit: {:?}", rl); setrlimit( - Resource::from_str(&rl.r#type)?, - Rlim::from_raw(rl.soft), - Rlim::from_raw(rl.hard), + Resource::from_str(&rl.typ().to_string())?, + Rlim::from_raw(rl.soft()), + Rlim::from_raw(rl.hard()), )?; } @@ -565,12 +579,17 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { } if to_new.contains(CloneFlags::CLONE_NEWUTS) { - unistd::sethostname(&spec.hostname)?; + unistd::sethostname( + spec.hostname() + .as_ref() + .map_or("".to_string(), |x| x.clone()), + )?; } - let rootfs = spec.root.as_ref().unwrap().path.as_str(); - log_child!(cfd_log, "setup rootfs {}", rootfs); - let root = fs::canonicalize(rootfs)?; + let rootfs = spec.root().as_ref().unwrap().path().display().to_string(); + + log_child!(cfd_log, "setup rootfs {}", &rootfs); + let root = fs::canonicalize(&rootfs)?; let rootfs = root.to_str().unwrap(); if to_new.contains(CloneFlags::CLONE_NEWNS) { @@ -605,15 +624,22 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { // CreateContainer Hooks: // before pivot_root after prestart, createruntime state.pid = std::process::id() as i32; - state.status = oci::ContainerState::Created; - if let Some(hooks) = spec.hooks.as_ref() { + state.status = spec::ContainerState::Created; + if let Some(hooks) = spec.hooks().as_ref() { log_child!( cfd_log, "create_container hooks {:?}", - hooks.create_container + hooks.create_container() ); let mut create_container_states = HookStates::new(); - create_container_states.execute_hooks(&hooks.create_container, Some(state.clone()))?; + create_container_states.execute_hooks( + hooks + .create_container() + .clone() + .unwrap_or_default() + .as_slice(), + Some(state.clone()), + )?; } } @@ -627,7 +653,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { } // setup sysctl - set_sysctls(&linux.sysctl)?; + set_sysctls(&linux.sysctl().clone().unwrap_or_default())?; unistd::chdir("/")?; } @@ -635,14 +661,14 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { mount::finish_rootfs(cfd_log, &spec, &oci_process)?; } - if !oci_process.cwd.is_empty() { - unistd::chdir(oci_process.cwd.as_str())?; + if !oci_process.cwd().as_os_str().is_empty() { + unistd::chdir(oci_process.cwd().display().to_string().as_str())?; } - let guser = &oci_process.user; + let guser = &oci_process.user(); - let uid = Uid::from_raw(guser.uid); - let gid = Gid::from_raw(guser.gid); + let uid = Uid::from_raw(guser.uid()); + let gid = Gid::from_raw(guser.gid()); // only change stdio devices owner when user // isn't root. @@ -652,9 +678,8 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { setid(uid, gid)?; - if !guser.additional_gids.is_empty() { - let gids: Vec = guser - .additional_gids + if let Some(additional_gids) = guser.additional_gids() { + let gids: Vec = additional_gids .iter() .map(|gid| Gid::from_raw(*gid)) .collect(); @@ -671,12 +696,17 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { } // NoNewPrivileges - if oci_process.no_new_privileges { + if oci_process.no_new_privileges().unwrap_or_default() { capctl::prctl::set_no_new_privs().map_err(|_| anyhow!("cannot set no new privileges"))?; } // Set SELinux label - if !oci_process.selinux_label.is_empty() { + if !oci_process + .selinux_label() + .clone() + .unwrap_or_default() + .is_empty() + { if !selinux_enabled { return Err(anyhow!( "SELinux label for the process is provided but SELinux is not enabled on the running kernel" @@ -684,12 +714,18 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { } log_child!(cfd_log, "Set SELinux label to the container process"); - selinux::set_exec_label(&oci_process.selinux_label)?; + let default_label = String::new(); + selinux::set_exec_label( + oci_process + .selinux_label() + .as_ref() + .unwrap_or(&default_label), + )?; } // Log unknown seccomp system calls in advance before the log file descriptor closes. #[cfg(feature = "seccomp")] - if let Some(ref scmp) = linux.seccomp { + if let Some(ref scmp) = linux.seccomp() { if let Some(syscalls) = seccomp::get_unknown_syscalls(scmp) { log_child!(cfd_log, "unknown seccomp system calls: {:?}", syscalls); } @@ -699,20 +735,21 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { // before dropping capabilities because the calling thread // must have the CAP_SYS_ADMIN. #[cfg(feature = "seccomp")] - if !oci_process.no_new_privileges { - if let Some(ref scmp) = linux.seccomp { + if !oci_process.no_new_privileges().unwrap_or_default() { + if let Some(ref scmp) = linux.seccomp() { seccomp::init_seccomp(scmp)?; } } // Drop capabilities - if oci_process.capabilities.is_some() { - let c = oci_process.capabilities.as_ref().unwrap(); + if oci_process.capabilities().is_some() { + let c = oci_process.capabilities().as_ref().unwrap(); capabilities::drop_privileges(cfd_log, c)?; } - let args = oci_process.args.to_vec(); - let env = oci_process.env.to_vec(); + let default_vec = Vec::new(); + let args = oci_process.args().as_ref().unwrap_or(&default_vec).to_vec(); + let env = oci_process.env().as_ref().unwrap_or(&default_vec).to_vec(); let mut fifofd = -1; if init { @@ -734,7 +771,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { if env::var_os(HOME_ENV_KEY).is_none() { // try to set "HOME" env by uid - if let Ok(Some(user)) = User::from_uid(Uid::from_raw(guser.uid)) { + if let Ok(Some(user)) = User::from_uid(Uid::from_raw(guser.uid())) { if let Ok(user_home_dir) = user.dir.into_os_string().into_string() { env::set_var(HOME_ENV_KEY, user_home_dir); } @@ -758,7 +795,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { let _ = unistd::close(crfd); let _ = unistd::close(cwfd); - if oci_process.terminal { + if oci_process.terminal().unwrap_or_default() { cfg_if::cfg_if! { if #[cfg(feature = "standard-oci-runtime")] { if let Some(csocket_fd) = csocket_fd { @@ -791,10 +828,17 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { // * should be run after container is created and before container is started (before user-specific command is executed) // * spec details: https://github.com/opencontainers/runtime-spec/blob/c1662686cff159595277b79322d0272f5182941b/config.md#startcontainer-hooks state.pid = std::process::id() as i32; - state.status = oci::ContainerState::Created; - if let Some(hooks) = spec.hooks.as_ref() { + state.status = spec::ContainerState::Created; + if let Some(hooks) = spec.hooks().as_ref() { let mut start_container_states = HookStates::new(); - start_container_states.execute_hooks(&hooks.start_container, Some(state))?; + start_container_states.execute_hooks( + hooks + .start_container() + .clone() + .unwrap_or_default() + .as_slice(), + Some(state), + )?; } } @@ -802,8 +846,8 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { // do_exec as possible in order to reduce the amount of // system calls in the seccomp profiles. #[cfg(feature = "seccomp")] - if oci_process.no_new_privileges { - if let Some(ref scmp) = linux.seccomp { + if oci_process.no_new_privileges().unwrap_or_default() { + if let Some(ref scmp) = linux.seccomp() { seccomp::init_seccomp(scmp)?; } } @@ -869,8 +913,8 @@ impl BaseContainer for LinuxContainer { 0 }; - let root = match oci.root.as_ref() { - Some(s) => s.path.as_str(), + let root = match oci.root().as_ref() { + Some(s) => s.path().display().to_string(), None => return Err(anyhow!("Unable to get root path: oci.root is none")), }; @@ -881,12 +925,12 @@ impl BaseContainer for LinuxContainer { }; Ok(OCIState { - version: oci.version.clone(), + version: oci.version().clone(), id: self.id(), status, pid, bundle, - annotations: oci.annotations.clone(), + annotations: oci.annotations().clone().unwrap_or_default(), }) } @@ -920,14 +964,10 @@ impl BaseContainer for LinuxContainer { fn set(&mut self, r: LinuxResources) -> Result<()> { self.cgroup_manager.as_ref().set(&r, true)?; - self.config - .spec - .as_mut() - .unwrap() - .linux - .as_mut() - .unwrap() - .resources = Some(r); + if let Some(linux) = self.config.spec.as_mut().unwrap().linux_mut() { + linux.set_resources(Some(r)); + } + Ok(()) } @@ -956,23 +996,23 @@ impl BaseContainer for LinuxContainer { } let spec = self.config.spec.as_ref().unwrap(); - if spec.linux.is_none() { + if spec.linux().is_none() { return Err(anyhow!("no linux config")); } - let linux = spec.linux.as_ref().unwrap(); + let linux = spec.linux().as_ref().unwrap(); - if p.oci.capabilities.is_none() { + if p.oci.capabilities().is_none() { // No capabilities, inherit from container process let process = spec - .process + .process() .as_ref() .ok_or_else(|| anyhow!("no process config"))?; - p.oci.capabilities = Some( + p.oci.set_capabilities(Some( process - .capabilities + .capabilities() .clone() .ok_or_else(|| anyhow!("missing process capabilities"))?, - ); + )); } let (pfd_log, cfd_log) = unistd::pipe().context("failed to create pipe")?; @@ -1247,15 +1287,24 @@ impl BaseContainer for LinuxContainer { // * should be executed after the container is deleted but before the delete operation returns // * the executable file is in agent namespace // * should also be executed in agent namespace. - if let Some(hooks) = spec.hooks.as_ref() { + if let Some(hooks) = spec.hooks().as_ref() { info!(self.logger, "guest Poststop hook"); let mut hook_states = HookStates::new(); - hook_states.execute_hooks(&hooks.poststop, Some(st))?; + hook_states.execute_hooks( + hooks.poststop().clone().unwrap_or_default().as_slice(), + Some(st), + )?; } self.status.transition(ContainerState::Stopped); mount::umount2( - spec.root.as_ref().unwrap().path.as_str(), + spec.root() + .as_ref() + .unwrap() + .path() + .display() + .to_string() + .as_str(), MntFlags::MNT_DETACH, )?; fs::remove_dir_all(&self.root)?; @@ -1300,10 +1349,13 @@ impl BaseContainer for LinuxContainer { // * should be executed after the container is started but before the delete operation returns // * the executable file is in agent namespace // * should also be executed in agent namespace. - if let Some(hooks) = spec.hooks.as_ref() { + if let Some(hooks) = spec.hooks().as_ref() { info!(self.logger, "guest Poststart hook"); let mut hook_states = HookStates::new(); - hook_states.execute_hooks(&hooks.poststart, Some(st))?; + hook_states.execute_hooks( + hooks.poststart().clone().unwrap_or_default().as_slice(), + Some(st), + )?; } unistd::close(fd)?; @@ -1351,21 +1403,26 @@ fn do_exec(args: &[String]) -> ! { pub fn update_namespaces(logger: &Logger, spec: &mut Spec, init_pid: RawFd) -> Result<()> { info!(logger, "updating namespaces"); let linux = spec - .linux + .linux_mut() .as_mut() .ok_or_else(|| anyhow!("Spec didn't contain linux field"))?; - let namespaces = linux.namespaces.as_mut_slice(); - for namespace in namespaces.iter_mut() { - if TYPETONAME.contains_key(namespace.r#type.as_str()) { - let ns_path = format!( - "/proc/{}/ns/{}", - init_pid, - TYPETONAME.get(namespace.r#type.as_str()).unwrap() - ); + if let Some(namespaces) = linux.namespaces_mut().as_mut() { + for namespace in namespaces.iter_mut() { + if TYPETONAME.contains_key(&namespace.typ()) { + let ns_path = format!( + "/proc/{}/ns/{}", + init_pid, + TYPETONAME.get(&namespace.typ()).unwrap() + ); - if namespace.path.is_empty() { - namespace.path = ns_path; + if namespace + .path() + .as_ref() + .map_or(true, |p| p.as_os_str().is_empty()) + { + namespace.set_path(Some(PathBuf::from(&ns_path))); + } } } } @@ -1374,24 +1431,28 @@ pub fn update_namespaces(logger: &Logger, spec: &mut Spec, init_pid: RawFd) -> R } fn get_pid_namespace(logger: &Logger, linux: &Linux) -> Result { - for ns in &linux.namespaces { - if ns.r#type == "pid" { - if ns.path.is_empty() { - return Ok(PidNs::new(true, None)); - } - - let fd = - fcntl::open(ns.path.as_str(), OFlag::O_RDONLY, Mode::empty()).map_err(|e| { + let linux_namespaces = linux.namespaces().clone().unwrap_or_default(); + for ns in &linux_namespaces { + if &ns.typ().to_string() == "pid" { + let fd = match ns.path() { + None => return Ok(PidNs::new(true, None)), + Some(ns_path) => fcntl::open( + ns_path.display().to_string().as_str(), + OFlag::O_RDONLY, + Mode::empty(), + ) + .map_err(|e| { error!( logger, "cannot open type: {} path: {}", - ns.r#type.clone(), - ns.path.clone() + &ns.typ().to_string(), + ns_path.display() ); error!(logger, "error is : {:?}", e); e - })?; + })?, + }; return Ok(PidNs::new(true, Some(fd))); } @@ -1402,18 +1463,25 @@ fn get_pid_namespace(logger: &Logger, linux: &Linux) -> Result { fn is_userns_enabled(linux: &Linux) -> bool { linux - .namespaces + .namespaces() + .clone() + .unwrap_or_default() .iter() - .any(|ns| ns.r#type == "user" && ns.path.is_empty()) + .any(|ns| &ns.typ().to_string() == "user" && ns.path().is_none()) } fn get_namespaces(linux: &Linux) -> Vec { linux - .namespaces + .namespaces() + .clone() + .unwrap_or_default() .iter() - .map(|ns| LinuxNamespace { - r#type: ns.r#type.clone(), - path: ns.path.clone(), + .map(|ns| { + let mut namespace = LinuxNamespace::default(); + namespace.set_typ(ns.typ()); + namespace.set_path(ns.path().clone()); + + namespace }) .collect() } @@ -1455,8 +1523,11 @@ async fn join_namespaces( ) -> Result<()> { let logger = logger.new(o!("action" => "join-namespaces")); - let linux = spec.linux.as_ref().unwrap(); - let res = linux.resources.as_ref(); + let linux = spec + .linux() + .as_ref() + .ok_or_else(|| anyhow!("Spec didn't contain linux field"))?; + let res = linux.resources().as_ref(); let userns = is_userns_enabled(linux); @@ -1494,17 +1565,11 @@ async fn join_namespaces( if userns { info!(logger, "setup uid/gid mappings"); + let uid_mappings = linux.uid_mappings().clone().unwrap_or_default(); + let gid_mappings = linux.gid_mappings().clone().unwrap_or_default(); // setup uid/gid mappings - write_mappings( - &logger, - &format!("/proc/{}/uid_map", p.pid), - &linux.uid_mappings, - )?; - write_mappings( - &logger, - &format!("/proc/{}/gid_map", p.pid), - &linux.gid_mappings, - )?; + write_mappings(&logger, &format!("/proc/{}/uid_map", p.pid), &uid_mappings)?; + write_mappings(&logger, &format!("/proc/{}/gid_map", p.pid), &gid_mappings)?; } // apply cgroups @@ -1534,10 +1599,13 @@ async fn join_namespaces( // * should be executed during the start operation, and before the container command is executed // * the executable file is in agent namespace // * should also be executed in agent namespace. - if let Some(hooks) = spec.hooks.as_ref() { + if let Some(hooks) = spec.hooks().as_ref() { info!(logger, "guest Prestart hook"); let mut hook_states = HookStates::new(); - hook_states.execute_hooks(&hooks.prestart, Some(st.clone()))?; + hook_states.execute_hooks( + hooks.prestart().clone().unwrap_or_default().as_slice(), + Some(st.clone()), + )?; } // notify child run prestart hooks completed @@ -1554,8 +1622,8 @@ async fn join_namespaces( fn write_mappings(logger: &Logger, path: &str, maps: &[LinuxIdMapping]) -> Result<()> { let data = maps .iter() - .filter(|m| m.size != 0) - .map(|m| format!("{} {} {}\n", m.container_id, m.host_id, m.size)) + .filter(|m| m.size() != 0) + .map(|m| format!("{} {} {}\n", m.container_id(), m.host_id(), m.size())) .collect::>() .join(""); @@ -1624,18 +1692,24 @@ impl LinuxContainer { .context(format!("Cannot change owner of container {} root", id))?; let spec = config.spec.as_ref().unwrap(); - let linux = spec.linux.as_ref().unwrap(); + let linux_cgroups_path = spec + .linux() + .as_ref() + .unwrap() + .cgroups_path() + .as_ref() + .map_or(String::new(), |cgrp| cgrp.display().to_string()); let cpath = if config.use_systemd_cgroup { - if linux.cgroups_path.len() == 2 { + if linux_cgroups_path.len() == 2 { format!("system.slice:kata_agent:{}", id.as_str()) } else { - linux.cgroups_path.clone() + linux_cgroups_path.clone() } - } else if linux.cgroups_path.is_empty() { + } else if linux_cgroups_path.is_empty() { format!("/{}", id.as_str()) } else { // if we have a systemd cgroup path we need to convert it to a fs cgroup path - linux.cgroups_path.replace(':', "/") + linux_cgroups_path.replace(':', "/") }; let cgroup_manager: Box = if config.use_systemd_cgroup { @@ -1708,7 +1782,8 @@ mod tests { use super::*; use crate::process::Process; use nix::unistd::Uid; - use oci::{LinuxDeviceCgroup, Root}; + use oci::{LinuxBuilder, LinuxDeviceCgroupBuilder, LinuxResourcesBuilder, Root, SpecBuilder}; + use oci_spec::runtime as oci; use std::fs; use std::os::unix::fs::MetadataExt; use std::os::unix::io::AsRawFd; @@ -1777,10 +1852,10 @@ mod tests { let ns = NAMESPACES.get("pid"); assert!(ns.is_some()); - let ns = NAMESPACES.get("network"); + let ns = NAMESPACES.get("net"); assert!(ns.is_some()); - let ns = NAMESPACES.get("mount"); + let ns = NAMESPACES.get("mnt"); assert!(ns.is_some()); let ns = NAMESPACES.get("uts"); @@ -1795,25 +1870,25 @@ mod tests { lazy_static::initialize(&TYPETONAME); assert_eq!(TYPETONAME.len(), 7); - let ns = TYPETONAME.get("user"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::User); assert!(ns.is_some()); - let ns = TYPETONAME.get("ipc"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Ipc); assert!(ns.is_some()); - let ns = TYPETONAME.get("pid"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Pid); assert!(ns.is_some()); - let ns = TYPETONAME.get("network"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Network); assert!(ns.is_some()); - let ns = TYPETONAME.get("mount"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Mount); assert!(ns.is_some()); - let ns = TYPETONAME.get("uts"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Uts); assert!(ns.is_some()); - let ns = TYPETONAME.get("cgroup"); + let ns = TYPETONAME.get(&oci::LinuxNamespaceType::Cgroup); assert!(ns.is_some()); } @@ -1823,21 +1898,18 @@ mod tests { .duration_since(UNIX_EPOCH) .expect("Time went backwards"); - let root = Root { - path: String::from("/tmp"), - ..Default::default() - }; + let mut root = Root::default(); + root.set_path(String::from("/tmp").into()); - let linux_resources = LinuxResources { - devices: vec![LinuxDeviceCgroup { - allow: true, - r#type: String::new(), - major: None, - minor: None, - access: String::from("rwm"), - }], - ..Default::default() - }; + let linux_resources = LinuxResourcesBuilder::default() + .devices(vec![LinuxDeviceCgroupBuilder::default() + .allow(true) + .typ(oci::LinuxDeviceType::C) + .access("rwm") + .build() + .unwrap()]) + .build() + .unwrap(); let cgroups_path = format!( "/{}/dummycontainer{}", @@ -1845,15 +1917,18 @@ mod tests { since_the_epoch.as_millis() ); - let spec = Spec { - linux: Some(Linux { - cgroups_path, - resources: Some(linux_resources), - ..Default::default() - }), - root: Some(root), - ..Default::default() - }; + let mut spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .cgroups_path(cgroups_path) + .resources(linux_resources) + .build() + .unwrap(), + ) + .root(root) + .build() + .unwrap(); + spec.set_process(None); CreateOpts { cgroup_name: "".to_string(), @@ -1959,7 +2034,14 @@ mod tests { #[test] fn test_linuxcontainer_oci_state_no_root_parent() { let ret = new_linux_container_and_then(|mut c: LinuxContainer| { - c.config.spec.as_mut().unwrap().root.as_mut().unwrap().path = "/".to_string(); + c.config + .spec + .as_mut() + .unwrap() + .root_mut() + .as_mut() + .unwrap() + .set_path("/".to_string().into()); c.oci_state() }); assert!(ret.is_err(), "Expecting Err, Got {:?}", ret); @@ -2032,21 +2114,25 @@ mod tests { #[tokio::test] async fn test_linuxcontainer_start() { let (c, _dir) = new_linux_container(); + let mut oci_process = oci::Process::default(); + oci_process.set_capabilities(None); let ret = c .unwrap() - .start(Process::new(&sl(), &oci::Process::default(), "123", true, 1, None).unwrap()) + .start(Process::new(&sl(), &oci_process, "123", true, 1, None).unwrap()) .await; - assert!(ret.is_err(), "Expecting Err, Got {:?}", ret); + assert!(format!("{:?}", ret).contains("no process config")); } #[tokio::test] async fn test_linuxcontainer_run() { let (c, _dir) = new_linux_container(); + let mut oci_process = oci::Process::default(); + oci_process.set_capabilities(None); let ret = c .unwrap() - .run(Process::new(&sl(), &oci::Process::default(), "123", true, 1, None).unwrap()) + .run(Process::new(&sl(), &oci_process, "123", true, 1, None).unwrap()) .await; - assert!(ret.is_err(), "Expecting Err, Got {:?}", ret); + assert!(format!("{:?}", ret).contains("no process config")); } #[tokio::test] diff --git a/src/agent/rustjail/src/lib.rs b/src/agent/rustjail/src/lib.rs index d3647f42e..3acf9c28d 100644 --- a/src/agent/rustjail/src/lib.rs +++ b/src/agent/rustjail/src/lib.rs @@ -24,7 +24,6 @@ extern crate protobuf; extern crate slog; #[macro_use] extern crate scan_fmt; -extern crate oci; extern crate path_absolutize; extern crate regex; @@ -43,1099 +42,3 @@ pub mod specconv; pub mod sync; pub mod sync_with_async; pub mod validator; - -use std::collections::HashMap; - -use protocols::oci as grpc; - -// construct ociSpec from grpc::Spec, which is needed for hook -// execution. since hooks read config.json -pub fn process_grpc_to_oci(p: &grpc::Process) -> oci::Process { - let console_size = if p.ConsoleSize.is_some() { - let c = p.ConsoleSize.as_ref().unwrap(); - Some(oci::Box { - height: c.Height, - width: c.Width, - }) - } else { - None - }; - - let user = if p.User.is_some() { - let u = p.User.as_ref().unwrap(); - oci::User { - uid: u.UID, - gid: u.GID, - additional_gids: u.AdditionalGids.clone(), - username: u.Username.clone(), - } - } else { - oci::User { - uid: 0, - gid: 0, - additional_gids: vec![], - username: String::from(""), - } - }; - - let capabilities = if p.Capabilities.is_some() { - let cap = p.Capabilities.as_ref().unwrap(); - - Some(oci::LinuxCapabilities { - bounding: cap.Bounding.clone(), - effective: cap.Effective.clone(), - inheritable: cap.Inheritable.clone(), - permitted: cap.Permitted.clone(), - ambient: cap.Ambient.clone(), - }) - } else { - None - }; - - let rlimits = { - let mut r = Vec::new(); - for lm in p.Rlimits.iter() { - r.push(oci::PosixRlimit { - r#type: lm.Type.clone(), - hard: lm.Hard, - soft: lm.Soft, - }); - } - r - }; - - oci::Process { - terminal: p.Terminal, - console_size, - user, - args: p.Args.clone(), - env: p.Env.clone(), - cwd: p.Cwd.clone(), - capabilities, - rlimits, - no_new_privileges: p.NoNewPrivileges, - apparmor_profile: p.ApparmorProfile.clone(), - oom_score_adj: Some(p.OOMScoreAdj as i32), - selinux_label: p.SelinuxLabel.clone(), - } -} - -fn root_grpc_to_oci(root: &grpc::Root) -> oci::Root { - oci::Root { - path: root.Path.clone(), - readonly: root.Readonly, - } -} - -fn mount_grpc_to_oci(m: &grpc::Mount) -> oci::Mount { - oci::Mount { - destination: m.destination.clone(), - r#type: m.type_.clone(), - source: m.source.clone(), - options: m.options.clone(), - } -} - -use protocols::oci::Hook as grpcHook; - -fn hook_grpc_to_oci(h: &[grpcHook]) -> Vec { - let mut r = Vec::new(); - for e in h.iter() { - r.push(oci::Hook { - path: e.Path.clone(), - args: e.Args.clone(), - env: e.Env.clone(), - timeout: Some(e.Timeout as i32), - }); - } - r -} - -fn hooks_grpc_to_oci(h: &grpc::Hooks) -> oci::Hooks { - let prestart = hook_grpc_to_oci(h.Prestart.as_ref()); - let create_runtime = hook_grpc_to_oci(h.CreateRuntime.as_ref()); - let create_container = hook_grpc_to_oci(h.CreateContainer.as_ref()); - let start_container = hook_grpc_to_oci(h.StartContainer.as_ref()); - let poststart = hook_grpc_to_oci(h.Poststart.as_ref()); - let poststop = hook_grpc_to_oci(h.Poststop.as_ref()); - - oci::Hooks { - prestart, - create_runtime, - create_container, - start_container, - poststart, - poststop, - } -} - -fn idmap_grpc_to_oci(im: &grpc::LinuxIDMapping) -> oci::LinuxIdMapping { - oci::LinuxIdMapping { - container_id: im.ContainerID, - host_id: im.HostID, - size: im.Size, - } -} - -fn idmaps_grpc_to_oci(ims: &[grpc::LinuxIDMapping]) -> Vec { - let mut r = Vec::new(); - for im in ims.iter() { - r.push(idmap_grpc_to_oci(im)); - } - r -} - -fn throttle_devices_grpc_to_oci( - tds: &[grpc::LinuxThrottleDevice], -) -> Vec { - let mut r = Vec::new(); - for td in tds.iter() { - r.push(oci::LinuxThrottleDevice { - blk: oci::LinuxBlockIoDevice { - major: td.Major, - minor: td.Minor, - }, - rate: td.Rate, - }); - } - r -} - -fn weight_devices_grpc_to_oci(wds: &[grpc::LinuxWeightDevice]) -> Vec { - let mut r = Vec::new(); - for wd in wds.iter() { - r.push(oci::LinuxWeightDevice { - blk: oci::LinuxBlockIoDevice { - major: wd.Major, - minor: wd.Minor, - }, - weight: Some(wd.Weight as u16), - leaf_weight: Some(wd.LeafWeight as u16), - }); - } - r -} - -fn blockio_grpc_to_oci(blk: &grpc::LinuxBlockIO) -> oci::LinuxBlockIo { - let weight_device = weight_devices_grpc_to_oci(blk.WeightDevice.as_ref()); - let throttle_read_bps_device = throttle_devices_grpc_to_oci(blk.ThrottleReadBpsDevice.as_ref()); - let throttle_write_bps_device = - throttle_devices_grpc_to_oci(blk.ThrottleWriteBpsDevice.as_ref()); - let throttle_read_iops_device = - throttle_devices_grpc_to_oci(blk.ThrottleReadIOPSDevice.as_ref()); - let throttle_write_iops_device = - throttle_devices_grpc_to_oci(blk.ThrottleWriteIOPSDevice.as_ref()); - - oci::LinuxBlockIo { - weight: Some(blk.Weight as u16), - leaf_weight: Some(blk.LeafWeight as u16), - weight_device, - throttle_read_bps_device, - throttle_write_bps_device, - throttle_read_iops_device, - throttle_write_iops_device, - } -} - -pub fn resources_grpc_to_oci(res: &grpc::LinuxResources) -> oci::LinuxResources { - let devices = { - let mut d = Vec::new(); - for dev in res.Devices.iter() { - let major = if dev.Major == -1 { - None - } else { - Some(dev.Major) - }; - - let minor = if dev.Minor == -1 { - None - } else { - Some(dev.Minor) - }; - d.push(oci::LinuxDeviceCgroup { - allow: dev.Allow, - r#type: dev.Type.clone(), - major, - minor, - access: dev.Access.clone(), - }); - } - d - }; - - let memory = if res.Memory.is_some() { - let mem = res.Memory.as_ref().unwrap(); - Some(oci::LinuxMemory { - limit: Some(mem.Limit), - reservation: Some(mem.Reservation), - swap: Some(mem.Swap), - kernel: Some(mem.Kernel), - kernel_tcp: Some(mem.KernelTCP), - swappiness: Some(mem.Swappiness), - disable_oom_killer: Some(mem.DisableOOMKiller), - }) - } else { - None - }; - - let cpu = if res.CPU.is_some() { - let c = res.CPU.as_ref().unwrap(); - Some(oci::LinuxCpu { - shares: Some(c.Shares), - quota: Some(c.Quota), - period: Some(c.Period), - realtime_runtime: Some(c.RealtimeRuntime), - realtime_period: Some(c.RealtimePeriod), - cpus: c.Cpus.clone(), - mems: c.Mems.clone(), - }) - } else { - None - }; - - let pids = if res.Pids.is_some() { - let p = res.Pids.as_ref().unwrap(); - Some(oci::LinuxPids { limit: p.Limit }) - } else { - None - }; - - let block_io = if res.BlockIO.is_some() { - let blk = res.BlockIO.as_ref().unwrap(); - // copy LinuxBlockIO - Some(blockio_grpc_to_oci(blk)) - } else { - None - }; - - let hugepage_limits = { - let mut r = Vec::new(); - for hl in res.HugepageLimits.iter() { - r.push(oci::LinuxHugepageLimit { - page_size: hl.Pagesize.clone(), - limit: hl.Limit, - }); - } - r - }; - - let network = if res.Network.is_some() { - let net = res.Network.as_ref().unwrap(); - let priorities = { - let mut r = Vec::new(); - for pr in net.Priorities.iter() { - r.push(oci::LinuxInterfacePriority { - name: pr.Name.clone(), - priority: pr.Priority, - }); - } - r - }; - Some(oci::LinuxNetwork { - class_id: Some(net.ClassID), - priorities, - }) - } else { - None - }; - - oci::LinuxResources { - devices, - memory, - cpu, - pids, - block_io, - hugepage_limits, - network, - rdma: HashMap::new(), - } -} - -fn seccomp_grpc_to_oci(sec: &grpc::LinuxSeccomp) -> oci::LinuxSeccomp { - let syscalls = { - let mut r = Vec::new(); - - for sys in sec.Syscalls.iter() { - let mut args = Vec::new(); - - let errno_ret: u32 = if sys.has_errnoret() { - sys.errnoret() - } else { - libc::EPERM as u32 - }; - - for arg in sys.Args.iter() { - args.push(oci::LinuxSeccompArg { - index: arg.Index as u32, - value: arg.Value, - value_two: arg.ValueTwo, - op: arg.Op.clone(), - }); - } - - r.push(oci::LinuxSyscall { - names: sys.Names.clone(), - action: sys.Action.clone(), - errno_ret, - args, - }); - } - r - }; - - oci::LinuxSeccomp { - default_action: sec.DefaultAction.clone(), - architectures: sec.Architectures.clone(), - flags: sec.Flags.clone(), - syscalls, - } -} - -fn linux_grpc_to_oci(l: &grpc::Linux) -> oci::Linux { - let uid_mappings = idmaps_grpc_to_oci(l.UIDMappings.as_ref()); - let gid_mappings = idmaps_grpc_to_oci(l.GIDMappings.as_ref()); - - let resources = if l.Resources.is_some() { - Some(resources_grpc_to_oci(l.Resources.as_ref().unwrap())) - } else { - None - }; - - let seccomp = if l.Seccomp.is_some() { - Some(seccomp_grpc_to_oci(l.Seccomp.as_ref().unwrap())) - } else { - None - }; - - let namespaces = { - let mut r = Vec::new(); - - for ns in l.Namespaces.iter() { - r.push(oci::LinuxNamespace { - r#type: ns.Type.clone(), - path: ns.Path.clone(), - }); - } - r - }; - - let devices = { - let mut r = Vec::new(); - - for d in l.Devices.iter() { - // if the filemode for the device is 0 (unset), use a default value as runc does - let filemode = if d.FileMode != 0 { - Some(d.FileMode) - } else { - Some(0o666) - }; - r.push(oci::LinuxDevice { - path: d.Path.clone(), - r#type: d.Type.clone(), - major: d.Major, - minor: d.Minor, - file_mode: filemode, - uid: Some(d.UID), - gid: Some(d.GID), - }); - } - r - }; - - let intel_rdt = if l.IntelRdt.is_some() { - let rdt = l.IntelRdt.as_ref().unwrap(); - - Some(oci::LinuxIntelRdt { - l3_cache_schema: rdt.L3CacheSchema.clone(), - }) - } else { - None - }; - - oci::Linux { - uid_mappings, - gid_mappings, - sysctl: l.Sysctl.clone(), - resources, - cgroups_path: l.CgroupsPath.clone(), - namespaces, - devices, - seccomp, - rootfs_propagation: l.RootfsPropagation.clone(), - masked_paths: l.MaskedPaths.clone(), - readonly_paths: l.ReadonlyPaths.clone(), - mount_label: l.MountLabel.clone(), - intel_rdt, - } -} - -pub fn grpc_to_oci(grpc: &grpc::Spec) -> oci::Spec { - // process - let process = if grpc.Process.is_some() { - Some(process_grpc_to_oci(grpc.Process.as_ref().unwrap())) - } else { - None - }; - - // root - let root = if grpc.Root.is_some() { - Some(root_grpc_to_oci(grpc.Root.as_ref().unwrap())) - } else { - None - }; - - // mounts - let mounts = { - let mut r = Vec::new(); - for m in grpc.Mounts.iter() { - r.push(mount_grpc_to_oci(m)); - } - r - }; - - // hooks - let hooks = if grpc.Hooks.is_some() { - Some(hooks_grpc_to_oci(grpc.Hooks.as_ref().unwrap())) - } else { - None - }; - - // Linux - let linux = if grpc.Linux.is_some() { - Some(linux_grpc_to_oci(grpc.Linux.as_ref().unwrap())) - } else { - None - }; - - oci::Spec { - version: grpc.Version.clone(), - process, - root, - hostname: grpc.Hostname.clone(), - mounts, - hooks, - annotations: grpc.Annotations.clone(), - linux, - solaris: None, - windows: None, - vm: None, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // Parameters: - // - // 1: expected Result - // 2: actual Result - // 3: string used to identify the test on error - #[macro_export] - macro_rules! assert_result { - ($expected_result:expr, $actual_result:expr, $msg:expr) => { - if $expected_result.is_ok() { - let expected_value = $expected_result.as_ref().unwrap(); - let actual_value = $actual_result.unwrap(); - assert!(*expected_value == actual_value, "{}", $msg); - } else { - assert!($actual_result.is_err(), "{}", $msg); - - let expected_error = $expected_result.as_ref().unwrap_err(); - let expected_error_msg = format!("{:?}", expected_error); - - let actual_error_msg = format!("{:?}", $actual_result.unwrap_err()); - - assert!(expected_error_msg == actual_error_msg, "{}", $msg); - } - }; - } - - #[test] - fn test_process_grpc_to_oci() { - #[derive(Debug)] - struct TestData { - grpcproc: grpc::Process, - result: oci::Process, - } - - let tests = &[ - TestData { - // All fields specified - grpcproc: grpc::Process { - Terminal: true, - ConsoleSize: protobuf::MessageField::::some(grpc::Box { - Height: 123, - Width: 456, - ..Default::default() - }), - User: protobuf::MessageField::::some(grpc::User { - UID: 1234, - GID: 5678, - AdditionalGids: Vec::from([910, 1112]), - Username: String::from("username"), - ..Default::default() - }), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env")]), - Cwd: String::from("cwd"), - Capabilities: protobuf::MessageField::some(grpc::LinuxCapabilities { - Bounding: Vec::from([String::from("bnd")]), - Effective: Vec::from([String::from("eff")]), - Inheritable: Vec::from([String::from("inher")]), - Permitted: Vec::from([String::from("perm")]), - Ambient: Vec::from([String::from("amb")]), - ..Default::default() - }), - Rlimits: Vec::from([ - grpc::POSIXRlimit { - Type: String::from("r#type"), - Hard: 123, - Soft: 456, - ..Default::default() - }, - grpc::POSIXRlimit { - Type: String::from("r#type2"), - Hard: 789, - Soft: 1011, - ..Default::default() - }, - ]), - NoNewPrivileges: true, - ApparmorProfile: String::from("apparmor profile"), - OOMScoreAdj: 123456, - SelinuxLabel: String::from("Selinux Label"), - ..Default::default() - }, - result: oci::Process { - terminal: true, - console_size: Some(oci::Box { - height: 123, - width: 456, - }), - user: oci::User { - uid: 1234, - gid: 5678, - additional_gids: Vec::from([910, 1112]), - username: String::from("username"), - }, - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env")]), - cwd: String::from("cwd"), - capabilities: Some(oci::LinuxCapabilities { - bounding: Vec::from([String::from("bnd")]), - effective: Vec::from([String::from("eff")]), - inheritable: Vec::from([String::from("inher")]), - permitted: Vec::from([String::from("perm")]), - ambient: Vec::from([String::from("amb")]), - }), - rlimits: Vec::from([ - oci::PosixRlimit { - r#type: String::from("r#type"), - hard: 123, - soft: 456, - }, - oci::PosixRlimit { - r#type: String::from("r#type2"), - hard: 789, - soft: 1011, - }, - ]), - no_new_privileges: true, - apparmor_profile: String::from("apparmor profile"), - oom_score_adj: Some(123456), - selinux_label: String::from("Selinux Label"), - }, - }, - TestData { - // None ConsoleSize - grpcproc: grpc::Process { - ConsoleSize: protobuf::MessageField::::none(), - OOMScoreAdj: 0, - ..Default::default() - }, - result: oci::Process { - console_size: None, - oom_score_adj: Some(0), - ..Default::default() - }, - }, - TestData { - // None User - grpcproc: grpc::Process { - User: protobuf::MessageField::::none(), - OOMScoreAdj: 0, - ..Default::default() - }, - result: oci::Process { - user: oci::User { - uid: 0, - gid: 0, - additional_gids: vec![], - username: String::from(""), - }, - oom_score_adj: Some(0), - ..Default::default() - }, - }, - TestData { - // None Capabilities - grpcproc: grpc::Process { - Capabilities: protobuf::MessageField::none(), - OOMScoreAdj: 0, - ..Default::default() - }, - result: oci::Process { - capabilities: None, - oom_score_adj: Some(0), - ..Default::default() - }, - }, - ]; - - for (i, d) in tests.iter().enumerate() { - let msg = format!("test[{}]: {:?}", i, d); - - let result = process_grpc_to_oci(&d.grpcproc); - - let msg = format!("{}, result: {:?}", msg, result); - - assert_eq!(d.result, result, "{}", msg); - } - } - - #[test] - fn test_root_grpc_to_oci() { - #[derive(Debug)] - struct TestData { - grpcroot: grpc::Root, - result: oci::Root, - } - - let tests = &[ - TestData { - // Default fields - grpcroot: grpc::Root { - ..Default::default() - }, - result: oci::Root { - ..Default::default() - }, - }, - TestData { - // Specified fields, readonly false - grpcroot: grpc::Root { - Path: String::from("path"), - Readonly: false, - ..Default::default() - }, - result: oci::Root { - path: String::from("path"), - readonly: false, - ..Default::default() - }, - }, - TestData { - // Specified fields, readonly true - grpcroot: grpc::Root { - Path: String::from("path"), - Readonly: true, - ..Default::default() - }, - result: oci::Root { - path: String::from("path"), - readonly: true, - ..Default::default() - }, - }, - ]; - - for (i, d) in tests.iter().enumerate() { - let msg = format!("test[{}]: {:?}", i, d); - - let result = root_grpc_to_oci(&d.grpcroot); - - let msg = format!("{}, result: {:?}", msg, result); - - assert_eq!(d.result, result, "{}", msg); - } - } - - #[test] - fn test_hooks_grpc_to_oci() { - #[derive(Debug)] - struct TestData { - grpchooks: grpc::Hooks, - result: oci::Hooks, - } - - let tests = &[ - TestData { - // Default fields - grpchooks: grpc::Hooks { - ..Default::default() - }, - result: oci::Hooks { - ..Default::default() - }, - }, - TestData { - // All specified - grpchooks: grpc::Hooks { - Prestart: Vec::from([ - grpc::Hook { - Path: String::from("prestartpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }, - grpc::Hook { - Path: String::from("prestartpath2"), - Args: Vec::from([String::from("arg3"), String::from("arg4")]), - Env: Vec::from([String::from("env3"), String::from("env4")]), - Timeout: 25, - ..Default::default() - }, - ]), - Poststart: Vec::from([grpc::Hook { - Path: String::from("poststartpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - Poststop: Vec::from([grpc::Hook { - Path: String::from("poststoppath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - CreateRuntime: Vec::from([grpc::Hook { - Path: String::from("createruntimepath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - CreateContainer: Vec::from([grpc::Hook { - Path: String::from("createcontainerpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - StartContainer: Vec::from([grpc::Hook { - Path: String::from("startcontainerpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - ..Default::default() - }, - result: oci::Hooks { - prestart: Vec::from([ - oci::Hook { - path: String::from("prestartpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }, - oci::Hook { - path: String::from("prestartpath2"), - args: Vec::from([String::from("arg3"), String::from("arg4")]), - env: Vec::from([String::from("env3"), String::from("env4")]), - timeout: Some(25), - }, - ]), - poststart: Vec::from([oci::Hook { - path: String::from("poststartpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - poststop: Vec::from([oci::Hook { - path: String::from("poststoppath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - create_runtime: Vec::from([oci::Hook { - path: String::from("createruntimepath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - create_container: Vec::from([oci::Hook { - path: String::from("createcontainerpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - start_container: Vec::from([oci::Hook { - path: String::from("startcontainerpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - }, - }, - TestData { - // Prestart empty - grpchooks: grpc::Hooks { - Prestart: Vec::from([]), - Poststart: Vec::from([grpc::Hook { - Path: String::from("poststartpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - Poststop: Vec::from([grpc::Hook { - Path: String::from("poststoppath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - CreateRuntime: Vec::from([grpc::Hook { - Path: String::from("createruntimepath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - CreateContainer: Vec::from([grpc::Hook { - Path: String::from("createcontainerpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - StartContainer: Vec::from([grpc::Hook { - Path: String::from("startcontainerpath"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }]), - ..Default::default() - }, - result: oci::Hooks { - prestart: Vec::from([]), - poststart: Vec::from([oci::Hook { - path: String::from("poststartpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - poststop: Vec::from([oci::Hook { - path: String::from("poststoppath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - create_runtime: Vec::from([oci::Hook { - path: String::from("createruntimepath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - create_container: Vec::from([oci::Hook { - path: String::from("createcontainerpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - start_container: Vec::from([oci::Hook { - path: String::from("startcontainerpath"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }]), - }, - }, - ]; - - for (i, d) in tests.iter().enumerate() { - let msg = format!("test[{}]: {:?}", i, d); - - let result = hooks_grpc_to_oci(&d.grpchooks); - - let msg = format!("{}, result: {:?}", msg, result); - - assert_eq!(d.result, result, "{}", msg); - } - } - - #[test] - fn test_mount_grpc_to_oci() { - #[derive(Debug)] - struct TestData { - grpcmount: grpc::Mount, - result: oci::Mount, - } - - let tests = &[ - TestData { - // Default fields - grpcmount: grpc::Mount { - ..Default::default() - }, - result: oci::Mount { - ..Default::default() - }, - }, - TestData { - grpcmount: grpc::Mount { - destination: String::from("destination"), - source: String::from("source"), - type_: String::from("fieldtype"), - options: Vec::from([String::from("option1"), String::from("option2")]), - ..Default::default() - }, - result: oci::Mount { - destination: String::from("destination"), - source: String::from("source"), - r#type: String::from("fieldtype"), - options: Vec::from([String::from("option1"), String::from("option2")]), - }, - }, - TestData { - grpcmount: grpc::Mount { - destination: String::from("destination"), - source: String::from("source"), - type_: String::from("fieldtype"), - options: Vec::new(), - ..Default::default() - }, - result: oci::Mount { - destination: String::from("destination"), - source: String::from("source"), - r#type: String::from("fieldtype"), - options: Vec::new(), - }, - }, - TestData { - grpcmount: grpc::Mount { - destination: String::new(), - source: String::from("source"), - type_: String::from("fieldtype"), - options: Vec::from([String::from("option1")]), - ..Default::default() - }, - result: oci::Mount { - destination: String::new(), - source: String::from("source"), - r#type: String::from("fieldtype"), - options: Vec::from([String::from("option1")]), - }, - }, - TestData { - grpcmount: grpc::Mount { - destination: String::from("destination"), - source: String::from("source"), - type_: String::new(), - options: Vec::from([String::from("option1")]), - ..Default::default() - }, - result: oci::Mount { - destination: String::from("destination"), - source: String::from("source"), - r#type: String::new(), - options: Vec::from([String::from("option1")]), - }, - }, - ]; - - for (i, d) in tests.iter().enumerate() { - let msg = format!("test[{}]: {:?}", i, d); - - let result = mount_grpc_to_oci(&d.grpcmount); - - let msg = format!("{}, result: {:?}", msg, result); - - assert_eq!(d.result, result, "{}", msg); - } - } - - #[test] - fn test_hook_grpc_to_oci<'a>() { - #[derive(Debug)] - struct TestData<'a> { - grpchook: &'a [grpc::Hook], - result: Vec, - } - - let tests = &[ - TestData { - // Default fields - grpchook: &[ - grpc::Hook { - Timeout: 0, - ..Default::default() - }, - grpc::Hook { - Timeout: 0, - ..Default::default() - }, - ], - result: vec![ - oci::Hook { - timeout: Some(0), - ..Default::default() - }, - oci::Hook { - timeout: Some(0), - ..Default::default() - }, - ], - }, - TestData { - // Specified fields - grpchook: &[ - grpc::Hook { - Path: String::from("path"), - Args: Vec::from([String::from("arg1"), String::from("arg2")]), - Env: Vec::from([String::from("env1"), String::from("env2")]), - Timeout: 10, - ..Default::default() - }, - grpc::Hook { - Path: String::from("path2"), - Args: Vec::from([String::from("arg3"), String::from("arg4")]), - Env: Vec::from([String::from("env3"), String::from("env4")]), - Timeout: 20, - ..Default::default() - }, - ], - result: vec![ - oci::Hook { - path: String::from("path"), - args: Vec::from([String::from("arg1"), String::from("arg2")]), - env: Vec::from([String::from("env1"), String::from("env2")]), - timeout: Some(10), - }, - oci::Hook { - path: String::from("path2"), - args: Vec::from([String::from("arg3"), String::from("arg4")]), - env: Vec::from([String::from("env3"), String::from("env4")]), - timeout: Some(20), - }, - ], - }, - ]; - - for (i, d) in tests.iter().enumerate() { - let msg = format!("test[{}]: {:?}", i, d); - - let result = hook_grpc_to_oci(d.grpchook); - - let msg = format!("{}, result: {:?}", msg, result); - - assert_eq!(d.result, result, "{}", msg); - } - } -} diff --git a/src/agent/rustjail/src/mount.rs b/src/agent/rustjail/src/mount.rs index 92a8d0dad..14e3d9560 100644 --- a/src/agent/rustjail/src/mount.rs +++ b/src/agent/rustjail/src/mount.rs @@ -13,6 +13,7 @@ use nix::sys::stat::{self, Mode, SFlag}; use nix::unistd::{self, Gid, Uid}; use nix::NixPath; use oci::{LinuxDevice, Mount, Process, Spec}; +use oci_spec::runtime as oci; use std::collections::{HashMap, HashSet}; use std::fs::{self, OpenOptions}; use std::mem::MaybeUninit; @@ -172,24 +173,33 @@ pub fn init_rootfs( lazy_static::initialize(&LINUXDEVICETYPE); let linux = &spec - .linux + .linux() .as_ref() .ok_or_else(|| anyhow!("Could not get linux configuration from spec"))?; let mut flags = MsFlags::MS_REC; - match PROPAGATION.get(&linux.rootfs_propagation.as_str()) { + let default_propagation = String::new(); + match PROPAGATION.get( + &linux + .rootfs_propagation() + .as_ref() + .unwrap_or(&default_propagation) + .as_str(), + ) { Some(fl) => flags |= *fl, None => flags |= MsFlags::MS_SLAVE, } - let label = &linux.mount_label; + let default_mntlabel = String::new(); + let label = linux.mount_label().as_ref().unwrap_or(&default_mntlabel); let root = spec - .root + .root() .as_ref() .ok_or_else(|| anyhow!("Could not get rootfs path from spec")) .and_then(|r| { - fs::canonicalize(r.path.as_str()).context("Could not canonicalize rootfs path") + fs::canonicalize(r.path().display().to_string().as_str()) + .context("Could not canonicalize rootfs path") })?; let rootfs = (*root) @@ -209,13 +219,13 @@ pub fn init_rootfs( )?; let mut bind_mount_dev = false; - for m in &spec.mounts { + let default_mnts = vec![]; + for m in spec.mounts().as_ref().unwrap_or(&default_mnts) { let (mut flags, pgflags, data) = parse_mount(m); - if !m.destination.starts_with('/') || m.destination.contains("..") { - return Err(anyhow!( - "the mount destination {} is invalid", - m.destination - )); + + let mount_dest = &m.destination().display().to_string(); + if !mount_dest.starts_with('/') || mount_dest.contains("..") { + return Err(anyhow!("the mount destination {} is invalid", mount_dest)); } // From https://github.com/opencontainers/runtime-spec/blob/main/config.md#mounts @@ -223,35 +233,37 @@ pub fn init_rootfs( // bind may be only specified in the oci spec options -> flags update r#type let m = &{ let mut mbind = m.clone(); - if mbind.r#type.is_empty() && flags & MsFlags::MS_BIND == MsFlags::MS_BIND { - mbind.r#type = "bind".to_string(); + if mbind.typ().is_none() && flags & MsFlags::MS_BIND == MsFlags::MS_BIND { + mbind.set_typ(Some("bind".to_string())); } mbind }; - if m.r#type == "cgroup" { + let default_typ = String::new(); + let mount_typ = m.typ().as_ref().unwrap_or(&default_typ); + if mount_typ == "cgroup" { mount_cgroups(cfd_log, m, rootfs, flags, &data, cpath, mounts)?; } else { - if m.destination == "/dev" { - if m.r#type == "bind" { + if mount_dest.clone().as_str() == "/dev" { + if mount_typ == "bind" { bind_mount_dev = true; } flags &= !MsFlags::MS_RDONLY; } - if m.r#type == "bind" { + if mount_typ == "bind" { check_proc_mount(m)?; } // If the destination already exists and is not a directory, we bail // out This is to avoid mounting through a symlink or similar -- which // has been a "fun" attack scenario in the past. - if m.r#type == "proc" || m.r#type == "sysfs" { - if let Ok(meta) = fs::symlink_metadata(&m.destination) { + if mount_typ == "proc" || mount_typ == "sysfs" { + if let Ok(meta) = fs::symlink_metadata(mount_dest) { if !meta.is_dir() { return Err(anyhow!( "Mount point {} must be ordinary directory: got {:?}", - m.destination, + &mount_dest, meta.file_type() )); } @@ -263,8 +275,8 @@ pub fn init_rootfs( // effective. // first check that we have non-default options required before attempting a // remount - if m.r#type == "bind" && !pgflags.is_empty() { - let dest = secure_join(rootfs, &m.destination); + if mount_typ == "bind" && !pgflags.is_empty() { + let dest = secure_join(rootfs, mount_dest); mount( None::<&str>, dest.as_str(), @@ -282,9 +294,11 @@ pub fn init_rootfs( // in case the /dev directory was binded mount from guest, // then there's no need to create devices nodes and symlinks // in /dev. + let default_devs = Vec::new(); + let linux_devices = linux.devices().as_ref().unwrap_or(&default_devs); if !bind_mount_dev { default_symlinks()?; - create_devices(&linux.devices, bind_device)?; + create_devices(linux_devices, bind_device)?; ensure_ptmx()?; } @@ -308,17 +322,19 @@ fn check_proc_mount(m: &Mount) -> Result<()> { "/proc/net/dev", ]; + let mount_dest = m.destination().display().to_string(); for i in valid_destinations.iter() { - if m.destination.as_str() == *i { + if mount_dest == *i { return Ok(()); } } - if m.destination == PROC_PATH { + if mount_dest == PROC_PATH { // only allow a mount on-top of proc if it's source is "proc" unsafe { let mut stats = MaybeUninit::::uninit(); - if m.source + let mount_source = m.source().as_ref().unwrap().display().to_string(); + if mount_source .with_nix_path(|path| libc::statfs(path.as_ptr(), stats.as_mut_ptr())) .is_ok() { @@ -331,15 +347,15 @@ fn check_proc_mount(m: &Mount) -> Result<()> { return Err(anyhow!(format!( "{} cannot be mounted to {} because it is not of type proc", - m.source, m.destination + &mount_source, &mount_dest ))); } } - if m.destination.starts_with(PROC_PATH) { + if mount_dest.starts_with(PROC_PATH) { return Err(anyhow!(format!( "{} cannot be mounted because it is inside /proc", - m.destination + &mount_dest ))); } @@ -351,12 +367,11 @@ fn mount_cgroups_v2(cfd_log: RawFd, m: &Mount, rootfs: &str, flags: MsFlags) -> unistd::chdir(rootfs)?; // https://github.com/opencontainers/runc/blob/09ddc63afdde16d5fb859a1d3ab010bd45f08497/libcontainer/rootfs_linux.go#L287 - let bm = Mount { - source: "cgroup".to_string(), - r#type: "cgroup2".to_string(), - destination: m.destination.clone(), - options: Vec::new(), - }; + + let mut bm = oci::Mount::default(); + bm.set_source(Some(PathBuf::from("cgroup"))); + bm.set_typ(Some("cgroup2".to_string())); + bm.set_destination(m.destination().clone()); let mount_flags: MsFlags = flags; @@ -365,7 +380,11 @@ fn mount_cgroups_v2(cfd_log: RawFd, m: &Mount, rootfs: &str, flags: MsFlags) -> unistd::chdir(&olddir)?; if flags.contains(MsFlags::MS_RDONLY) { - let dest = format!("{}{}", rootfs, m.destination.as_str()); + let dest = format!( + "{}{}", + rootfs, + m.destination().display().to_string().as_str() + ); mount( Some(dest.as_str()), dest.as_str(), @@ -390,13 +409,13 @@ fn mount_cgroups( if cgroups::hierarchies::is_cgroup2_unified_mode() { return mount_cgroups_v2(cfd_log, m, rootfs, flags); } + + let mount_dest = m.destination().display().to_string(); // mount tmpfs - let ctm = Mount { - source: "tmpfs".to_string(), - r#type: "tmpfs".to_string(), - destination: m.destination.clone(), - options: Vec::new(), - }; + let mut ctm = oci::Mount::default(); + ctm.set_source(Some(PathBuf::from("tmpfs"))); + ctm.set_typ(Some("tmpfs".to_string())); + ctm.set_destination(m.destination().clone()); let cflags = MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NODEV; mount_from(cfd_log, &ctm, rootfs, cflags, "", "")?; @@ -421,12 +440,12 @@ fn mount_cgroups( &mount[..] }; - let destination = format!("{}/{}", m.destination.as_str(), base); + let destination = format!("{}/{}", &mount_dest, base); if srcs.contains(source) { // already mounted, xxx,yyy style cgroup if key != base { - let src = format!("{}/{}", m.destination.as_str(), key); + let src = format!("{}/{}", &mount_dest, key); unix::fs::symlink(destination.as_str(), &src[1..])?; } @@ -437,12 +456,10 @@ fn mount_cgroups( log_child!(cfd_log, "mount destination: {}", destination.as_str()); - let bm = Mount { - source: source.to_string(), - r#type: "bind".to_string(), - destination: destination.clone(), - options: Vec::new(), - }; + let mut bm = oci::Mount::default(); + bm.set_source(Some(PathBuf::from(source))); + bm.set_typ(Some("bind".to_string())); + bm.set_destination(PathBuf::from(destination.clone())); let mut mount_flags: MsFlags = flags | MsFlags::MS_REC | MsFlags::MS_BIND; if key.contains("systemd") { @@ -451,7 +468,7 @@ fn mount_cgroups( mount_from(cfd_log, &bm, rootfs, mount_flags, "", "")?; if key != base { - let src = format!("{}/{}", m.destination.as_str(), key); + let src = format!("{}/{}", &mount_dest, key); unix::fs::symlink(destination.as_str(), &src[1..]).map_err(|e| { log_child!( cfd_log, @@ -469,7 +486,7 @@ fn mount_cgroups( unistd::chdir(&olddir)?; if flags.contains(MsFlags::MS_RDONLY) { - let dest = format!("{}{}", rootfs, m.destination.as_str()); + let dest = format!("{}{}", rootfs, &mount_dest); mount( Some(dest.as_str()), dest.as_str(), @@ -710,7 +727,9 @@ fn parse_mount(m: &Mount) -> (MsFlags, MsFlags, String) { let mut pgflags = MsFlags::empty(); let mut data = Vec::new(); - for o in &m.options { + let default_options = Vec::new(); + let mount_options = m.options().as_ref().unwrap_or(&default_options); + for o in mount_options { if let Some(v) = OPTIONS.get(o.as_str()) { let (clear, fl) = *v; if clear { @@ -783,10 +802,13 @@ fn mount_from( label: &str, ) -> Result<()> { let mut d = String::from(data); - let dest = secure_join(rootfs, &m.destination); + let mount_dest = m.destination().display().to_string(); + let mount_typ = m.typ().as_ref().unwrap(); + let dest = secure_join(rootfs, &mount_dest); - let src = if m.r#type.as_str() == "bind" { - let src = fs::canonicalize(m.source.as_str())?; + let mount_source = m.source().as_ref().unwrap().display().to_string(); + let src = if mount_typ == "bind" { + let src = fs::canonicalize(&mount_source)?; let dir = if src.is_dir() { Path::new(&dest) } else { @@ -822,11 +844,10 @@ fn mount_from( src.to_str().unwrap().to_string() } else { let _ = fs::create_dir_all(&dest); - if m.r#type.as_str() == "cgroup2" { + if mount_typ == "cgroup2" { "cgroup2".to_string() } else { - let tmp = PathBuf::from(&m.source); - tmp.to_str().unwrap().to_string() + mount_source.to_string() } }; @@ -839,11 +860,16 @@ fn mount_from( let mut use_xattr = false; if !label.is_empty() { if selinux::is_enabled()? { - let device = Path::new(&m.source) + let device = m + .source() + .as_ref() + .unwrap() .file_name() - .ok_or_else(|| anyhow!("invalid device source path: {}", &m.source))? + .ok_or_else(|| anyhow!("invalid device source path: {}", &mount_source))? .to_str() - .ok_or_else(|| anyhow!("failed to convert device source path: {}", &m.source))?; + .ok_or_else(|| { + anyhow!("failed to convert device source path: {}", &mount_source) + })?; match device { // SELinux does not support labeling of /proc or /sys @@ -869,7 +895,7 @@ fn mount_from( mount( Some(src.as_str()), dest.as_str(), - Some(m.r#type.as_str()), + Some(mount_typ.as_str()), flags, Some(d.as_str()), ) @@ -924,9 +950,7 @@ fn default_symlinks() -> Result<()> { Ok(()) } -fn dev_rel_path(path: &str) -> Option<&Path> { - let path = Path::new(path); - +fn dev_rel_path(path: &PathBuf) -> Option<&Path> { if !path.starts_with("/dev") || path == Path::new("/dev") || path.components().any(|c| c == Component::ParentDir) @@ -940,12 +964,17 @@ fn create_devices(devices: &[LinuxDevice], bind: bool) -> Result<()> { let op: fn(&LinuxDevice, &Path) -> Result<()> = if bind { bind_dev } else { mknod_dev }; let old = stat::umask(Mode::from_bits_truncate(0o000)); for dev in DEFAULT_DEVICES.iter() { - let path = Path::new(&dev.path[1..]); + let dev_path = dev.path().display().to_string(); + let path = Path::new(&dev_path[1..]); op(dev, path).context(format!("Creating container device {:?}", dev))?; } for dev in devices { - let path = dev_rel_path(&dev.path).ok_or_else(|| { - let msg = format!("{} is not a valid device path", dev.path); + let dev_path = &dev.path(); + let path = dev_rel_path(dev_path).ok_or_else(|| { + let msg = format!( + "{} is not a valid device path", + &dev.path().display().to_string().as_str() + ); anyhow!(msg) })?; if let Some(dir) = path.parent() { @@ -974,7 +1003,7 @@ lazy_static! { } fn mknod_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> { - let f = match LINUXDEVICETYPE.get(dev.r#type.as_str()) { + let f = match LINUXDEVICETYPE.get(dev.typ().as_str()) { Some(v) => v, None => return Err(anyhow!("invalid spec".to_string())), }; @@ -982,14 +1011,14 @@ fn mknod_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> { stat::mknod( relpath, *f, - Mode::from_bits_truncate(dev.file_mode.unwrap_or(0)), - nix::sys::stat::makedev(dev.major as u64, dev.minor as u64), + Mode::from_bits_truncate(dev.file_mode().unwrap_or(0)), + nix::sys::stat::makedev(dev.major() as u64, dev.minor() as u64), )?; unistd::chown( relpath, - Some(Uid::from_raw(dev.uid.unwrap_or(0) as uid_t)), - Some(Gid::from_raw(dev.gid.unwrap_or(0) as uid_t)), + Some(Uid::from_raw(dev.uid().unwrap_or(0) as uid_t)), + Some(Gid::from_raw(dev.gid().unwrap_or(0) as uid_t)), )?; Ok(()) @@ -1005,7 +1034,7 @@ fn bind_dev(dev: &LinuxDevice, relpath: &Path) -> Result<()> { unistd::close(fd)?; mount( - Some(&*dev.path), + Some(dev.path()), relpath, None::<&str>, MsFlags::MS_BIND, @@ -1019,30 +1048,34 @@ pub fn finish_rootfs(cfd_log: RawFd, spec: &Spec, process: &Process) -> Result<( log_child!(cfd_log, "old cwd: {}", olddir.to_str().unwrap()); unistd::chdir("/")?; - if !process.cwd.is_empty() { + let process_cwd = process.cwd().display().to_string(); + if process_cwd.is_empty() { // Although the process.cwd string can be unclean/malicious (../../dev, etc), // we are running on our own mount namespace and we just chrooted into the // container's root. It's safe to create CWD from there. - log_child!(cfd_log, "Creating CWD {}", process.cwd.as_str()); + log_child!(cfd_log, "Creating CWD {}", process_cwd.as_str()); // Unconditionally try to create CWD, create_dir_all will not fail if // it already exists. - fs::create_dir_all(process.cwd.as_str())?; + fs::create_dir_all(process_cwd.as_str())?; } - if spec.linux.is_some() { - let linux = spec.linux.as_ref().unwrap(); - - for path in linux.masked_paths.iter() { + if spec.linux().is_some() { + let linux = spec.linux().as_ref().unwrap(); + let linux_masked_paths = linux.masked_paths().clone().unwrap_or_default(); + for path in linux_masked_paths.iter() { mask_path(path)?; } - - for path in linux.readonly_paths.iter() { + let ro_paths = vec![]; + let linux_readonly_paths = linux.readonly_paths().as_ref().unwrap_or(&ro_paths); + for path in linux_readonly_paths.iter() { readonly_path(path)?; } } - - for m in spec.mounts.iter() { - if m.destination == "/dev" { + let default_mnts = vec![]; + let spec_mounts = spec.mounts().as_ref().unwrap_or(&default_mnts); + for m in spec_mounts.iter() { + let mount_dest = m.destination().display().to_string(); + if &mount_dest == "/dev" { let (flags, _, _) = parse_mount(m); if flags.contains(MsFlags::MS_RDONLY) { mount( @@ -1056,7 +1089,7 @@ pub fn finish_rootfs(cfd_log: RawFd, spec: &Spec, process: &Process) -> Result<( } } - if spec.root.as_ref().unwrap().readonly { + if spec.root().as_ref().unwrap().readonly().unwrap_or_default() { let flags = MsFlags::MS_BIND | MsFlags::MS_RDONLY | MsFlags::MS_NODEV | MsFlags::MS_REMOUNT; mount(Some("/"), "/", None::<&str>, flags, None::<&str>)?; @@ -1125,7 +1158,6 @@ fn check_paths(path: &str) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use crate::assert_result; use std::fs::create_dir; use std::fs::create_dir_all; use std::fs::remove_dir_all; @@ -1134,6 +1166,7 @@ mod tests { use std::os::unix::fs; use std::os::unix::io::AsRawFd; use tempfile::tempdir; + use test_utils::assert_result; use test_utils::skip_if_not_root; #[test] @@ -1153,7 +1186,7 @@ mod tests { ); // there is no spec.Root, should fail - spec.linux = Some(oci::Linux::default()); + spec.set_linux(Some(oci::Linux::default())); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); assert!( ret.is_err(), @@ -1165,10 +1198,10 @@ mod tests { let ret = create_dir(rootfs.path().join("dev")); assert!(ret.is_ok(), "Got: {:?}", ret); - spec.root = Some(oci::Root { - path: rootfs.path().to_str().unwrap().to_string(), - readonly: false, - }); + let mut oci_root = oci::Root::default(); + oci_root.set_path(rootfs.path().to_path_buf()); + oci_root.set_readonly(Some(false)); + spec.set_root(Some(oci_root)); // there is no spec.mounts, but should pass let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); @@ -1176,13 +1209,16 @@ mod tests { let _ = remove_dir_all(rootfs.path().join("dev")); let _ = create_dir(rootfs.path().join("dev")); + if spec.mounts().is_none() { + spec.set_mounts(Some(Vec::new())); + } // Adding bad mount point to spec.mounts - spec.mounts.push(oci::Mount { - destination: "error".into(), - r#type: "bind".into(), - source: "error".into(), - options: vec!["shared".into(), "rw".into(), "dev".into()], - }); + let mut oci_mount = oci::Mount::default(); + oci_mount.set_destination("error".into()); + oci_mount.set_typ(Some("bind".to_string())); + oci_mount.set_source(Some("error".into())); + oci_mount.set_options(Some(vec!["shared".into(), "rw".into(), "dev".into()])); + spec.mounts_mut().as_mut().unwrap().push(oci_mount); // destination doesn't start with /, should fail let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); @@ -1191,31 +1227,31 @@ mod tests { "Should fail: destination doesn't start with '/'. Got: {:?}", ret ); - spec.mounts.pop(); + spec.mounts_mut().as_mut().unwrap().pop(); let _ = remove_dir_all(rootfs.path().join("dev")); let _ = create_dir(rootfs.path().join("dev")); // mounting a cgroup - spec.mounts.push(oci::Mount { - destination: "/cgroup".into(), - r#type: "cgroup".into(), - source: "/cgroup".into(), - options: vec!["shared".into()], - }); + let mut oci_mount = oci::Mount::default(); + oci_mount.set_destination("/cgroup".into()); + oci_mount.set_typ(Some("cgroup".into())); + oci_mount.set_source(Some("/cgroup".into())); + oci_mount.set_options(Some(vec!["shared".into()])); + spec.mounts_mut().as_mut().unwrap().push(oci_mount); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); - spec.mounts.pop(); + spec.mounts_mut().as_mut().unwrap().pop(); let _ = remove_dir_all(rootfs.path().join("dev")); let _ = create_dir(rootfs.path().join("dev")); // mounting /dev - spec.mounts.push(oci::Mount { - destination: "/dev".into(), - r#type: "bind".into(), - source: "/dev".into(), - options: vec!["shared".into()], - }); + let mut oci_mount = oci::Mount::default(); + oci_mount.set_destination("/dev".into()); + oci_mount.set_typ(Some("bind".into())); + oci_mount.set_source(Some("/dev".into())); + oci_mount.set_options(Some(vec!["shared".into()])); + spec.mounts_mut().as_mut().unwrap().push(oci_mount); let ret = init_rootfs(stdout_fd, &spec, &cpath, &mounts, true); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); @@ -1225,12 +1261,13 @@ mod tests { #[serial(chdir)] fn test_mount_cgroups() { let stdout_fd = std::io::stdout().as_raw_fd(); - let mount = oci::Mount { - destination: "/cgroups".to_string(), - r#type: "cgroup".to_string(), - source: "/cgroups".to_string(), - options: vec!["shared".to_string()], - }; + + let mut mount = oci::Mount::default(); + mount.set_destination("/cgroup".into()); + mount.set_typ(Some("cgroup".into())); + mount.set_source(Some("/cgroup".into())); + mount.set_options(Some(vec!["shared".into()])); + let tempdir = tempdir().unwrap(); let rootfs = tempdir.path().to_str().unwrap().to_string(); let flags = MsFlags::MS_RDONLY; @@ -1310,19 +1347,27 @@ mod tests { let stdout_fd = std::io::stdout().as_raw_fd(); let mut spec = oci::Spec::default(); - spec.linux = Some(oci::Linux::default()); - spec.linux.as_mut().unwrap().masked_paths = vec!["/tmp".to_string()]; - spec.linux.as_mut().unwrap().readonly_paths = vec!["/tmp".to_string()]; - spec.root = Some(oci::Root { - path: "/tmp".to_string(), - readonly: true, - }); - spec.mounts = vec![oci::Mount { - destination: "/dev".to_string(), - r#type: "bind".to_string(), - source: "/dev".to_string(), - options: vec!["ro".to_string(), "shared".to_string()], - }]; + spec.set_linux(Some(oci::Linux::default())); + spec.linux_mut() + .as_mut() + .unwrap() + .set_masked_paths(Some(vec!["/tmp".to_string()])); + spec.linux_mut() + .as_mut() + .unwrap() + .set_readonly_paths(Some(vec!["/tmp".to_string()])); + + let mut oci_root = oci::Root::default(); + oci_root.set_path(PathBuf::from("/tmp")); + oci_root.set_readonly(Some(true)); + spec.set_root(Some(oci_root)); + + let mut oci_mount = oci::Mount::default(); + oci_mount.set_destination("/dev".into()); + oci_mount.set_typ(Some("bind".into())); + oci_mount.set_source(Some("/dev".into())); + oci_mount.set_options(Some(vec!["shared".into()])); + spec.set_mounts(Some(vec![oci_mount])); let ret = finish_rootfs(stdout_fd, &spec, &oci::Process::default()); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); @@ -1346,15 +1391,16 @@ mod tests { skip_if_not_root!(); let path = "/dev/fifo-test"; - let dev = oci::LinuxDevice { - path: path.to_string(), - r#type: "c".to_string(), - major: 0, - minor: 0, - file_mode: Some(0660), - uid: Some(unistd::getuid().as_raw()), - gid: Some(unistd::getgid().as_raw()), - }; + let dev = oci::LinuxDeviceBuilder::default() + .path(PathBuf::from(path)) + .typ(oci::LinuxDeviceType::C) + .major(0) + .minor(0) + .file_mode(0660 as u32) + .uid(unistd::getuid().as_raw()) + .gid(unistd::getgid().as_raw()) + .build() + .unwrap(); let ret = mknod_dev(&dev, Path::new(path)); assert!(ret.is_ok(), "Should pass. Got: {:?}", ret); @@ -1370,6 +1416,7 @@ mod tests { #[test] fn test_mount_from() { #[derive(Debug)] + #[allow(dead_code)] struct TestData<'a> { source: &'a str, destination: &'a str, @@ -1444,12 +1491,11 @@ mod tests { std::fs::write(&source_path, []).unwrap(); } - let mount = Mount { - source: source_path, - destination: d.destination.to_string(), - r#type: d.r#type.to_string(), - options: vec![], - }; + let mut mount = oci::Mount::default(); + mount.set_destination(d.destination.into()); + mount.set_typ(Some("bind".into())); + mount.set_source(Some(source_path.into())); + mount.set_options(Some(vec![])); let result = mount_from( wfd, @@ -1524,30 +1570,27 @@ mod tests { #[test] fn test_check_proc_mount() { - let mount = oci::Mount { - destination: "/proc".to_string(), - r#type: "bind".to_string(), - source: "/test".to_string(), - options: vec!["shared".to_string()], - }; + let mut mount = oci::Mount::default(); + mount.set_destination("/proc".into()); + mount.set_typ(Some("bind".into())); + mount.set_source(Some("/test".into())); + mount.set_options(Some(vec!["shared".to_string()])); assert!(check_proc_mount(&mount).is_err()); - let mount = oci::Mount { - destination: "/proc/cpuinfo".to_string(), - r#type: "bind".to_string(), - source: "/test".to_string(), - options: vec!["shared".to_string()], - }; + let mut mount = oci::Mount::default(); + mount.set_destination("/proc/cpuinfo".into()); + mount.set_typ(Some("bind".into())); + mount.set_source(Some("/test".into())); + mount.set_options(Some(vec!["shared".to_string()])); assert!(check_proc_mount(&mount).is_ok()); - let mount = oci::Mount { - destination: "/proc/test".to_string(), - r#type: "bind".to_string(), - source: "/test".to_string(), - options: vec!["shared".to_string()], - }; + let mut mount = oci::Mount::default(); + mount.set_destination("/proc/test".into()); + mount.set_typ(Some("bind".into())); + mount.set_source(Some("/test".into())); + mount.set_options(Some(vec!["shared".to_string()])); assert!(check_proc_mount(&mount).is_err()); } @@ -1755,22 +1798,37 @@ mod tests { #[test] fn test_dev_rel_path() { // Valid device paths - assert_eq!(dev_rel_path("/dev/sda").unwrap(), Path::new("dev/sda")); - assert_eq!(dev_rel_path("//dev/sda").unwrap(), Path::new("dev/sda")); assert_eq!( - dev_rel_path("/dev/vfio/99").unwrap(), + dev_rel_path(&PathBuf::from("/dev/sda")).unwrap(), + Path::new("dev/sda") + ); + assert_eq!( + dev_rel_path(&PathBuf::from("//dev/sda")).unwrap(), + Path::new("dev/sda") + ); + assert_eq!( + dev_rel_path(&PathBuf::from("/dev/vfio/99")).unwrap(), Path::new("dev/vfio/99") ); - assert_eq!(dev_rel_path("/dev/...").unwrap(), Path::new("dev/...")); - assert_eq!(dev_rel_path("/dev/a..b").unwrap(), Path::new("dev/a..b")); - assert_eq!(dev_rel_path("/dev//foo").unwrap(), Path::new("dev/foo")); + assert_eq!( + dev_rel_path(&PathBuf::from("/dev/...")).unwrap(), + Path::new("dev/...") + ); + assert_eq!( + dev_rel_path(&PathBuf::from("/dev/a..b")).unwrap(), + Path::new("dev/a..b") + ); + assert_eq!( + dev_rel_path(&PathBuf::from("/dev//foo")).unwrap(), + Path::new("dev/foo") + ); // Bad device paths - assert!(dev_rel_path("/devfoo").is_none()); - assert!(dev_rel_path("/etc/passwd").is_none()); - assert!(dev_rel_path("/dev/../etc/passwd").is_none()); - assert!(dev_rel_path("dev/foo").is_none()); - assert!(dev_rel_path("").is_none()); - assert!(dev_rel_path("/dev").is_none()); + assert!(dev_rel_path(&PathBuf::from("/devfoo")).is_none()); + assert!(dev_rel_path(&PathBuf::from("/etc/passwd")).is_none()); + assert!(dev_rel_path(&PathBuf::from("/dev/../etc/passwd")).is_none()); + assert!(dev_rel_path(&PathBuf::from("dev/foo")).is_none()); + assert!(dev_rel_path(&PathBuf::from("")).is_none()); + assert!(dev_rel_path(&PathBuf::from("/dev")).is_none()); } } diff --git a/src/agent/rustjail/src/process.rs b/src/agent/rustjail/src/process.rs index 4b24a7fee..d912f8695 100644 --- a/src/agent/rustjail/src/process.rs +++ b/src/agent/rustjail/src/process.rs @@ -16,6 +16,7 @@ use nix::unistd::{self, Pid}; use nix::Result; use oci::Process as OCIProcess; +use oci_spec::runtime as oci; use slog::Logger; use crate::pipestream::PipeStream; @@ -147,7 +148,7 @@ impl Process { exit_tx: Some(exit_tx), exit_rx: Some(exit_rx), extra_files: Vec::new(), - tty: ocip.terminal, + tty: ocip.terminal().unwrap_or_default(), term_master: None, parent_stdin: None, parent_stdout: None, diff --git a/src/agent/rustjail/src/seccomp.rs b/src/agent/rustjail/src/seccomp.rs index 4b0c89515..99ffd0507 100644 --- a/src/agent/rustjail/src/seccomp.rs +++ b/src/agent/rustjail/src/seccomp.rs @@ -5,9 +5,11 @@ use anyhow::{anyhow, Result}; use libseccomp::*; -use oci::{LinuxSeccomp, LinuxSeccompArg}; use std::str::FromStr; +use oci::{LinuxSeccomp, LinuxSeccompArg}; +use oci_spec::runtime as oci; + fn get_filter_attr_from_flag(flag: &str) -> Result { match flag { "SECCOMP_FILTER_FLAG_TSYNC" => Ok(ScmpFilterAttr::CtlTsync), @@ -22,19 +24,15 @@ fn get_rule_conditions(args: &[LinuxSeccompArg]) -> Result> let mut conditions: Vec = Vec::new(); for arg in args { - if arg.op.is_empty() { - return Err(anyhow!("seccomp opreator is required")); - } - - let mut op = ScmpCompareOp::from_str(&arg.op)?; - let mut value = arg.value; + let mut op = ScmpCompareOp::from_str(&arg.op().to_string())?; + let mut value = arg.value(); // For SCMP_CMP_MASKED_EQ, arg.value is the mask and arg.value_two is the value if op == ScmpCompareOp::MaskedEqual(u64::default()) { - op = ScmpCompareOp::MaskedEqual(arg.value); - value = arg.value_two; + op = ScmpCompareOp::MaskedEqual(arg.value()); + value = arg.value_two().unwrap_or(0); } - let cond = ScmpArgCompare::new(arg.index, op, value); + let cond = ScmpArgCompare::new(arg.index() as u32, op, value); conditions.push(cond); } @@ -44,9 +42,9 @@ fn get_rule_conditions(args: &[LinuxSeccompArg]) -> Result> pub fn get_unknown_syscalls(scmp: &LinuxSeccomp) -> Option> { let mut unknown_syscalls: Vec = Vec::new(); - - for syscall in &scmp.syscalls { - for name in &syscall.names { + let scmp_syscalls = scmp.syscalls().clone().unwrap_or_default(); + for syscall in scmp_syscalls.iter() { + for name in syscall.names().iter() { if ScmpSyscall::from_name(name).is_err() { unknown_syscalls.push(name.to_string()); } @@ -63,14 +61,15 @@ pub fn get_unknown_syscalls(scmp: &LinuxSeccomp) -> Option> { // init_seccomp creates a seccomp filter and loads it for the current process // including all the child processes. pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> { - let def_action = ScmpAction::from_str(scmp.default_action.as_str(), Some(libc::EPERM))?; + let def_action = ScmpAction::from_str(&scmp.default_action().to_string(), Some(libc::EPERM))?; // Create a new filter context let mut filter = ScmpFilterContext::new_filter(def_action)?; // Add extra architectures - for arch in &scmp.architectures { - let scmp_arch = ScmpArch::from_str(arch)?; + let architectures = scmp.architectures().clone().unwrap_or_default(); + for arch in architectures { + let scmp_arch = ScmpArch::from_str(&arch.to_string())?; filter.add_arch(scmp_arch)?; } @@ -78,17 +77,23 @@ pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> { filter.set_ctl_nnp(false)?; // Add a rule for each system call - for syscall in &scmp.syscalls { - if syscall.names.is_empty() { + let scmp_syscalls = scmp.syscalls().clone().unwrap_or_default(); + for syscall in scmp_syscalls { + if syscall.names().is_empty() { return Err(anyhow!("syscall name is required")); } - let action = ScmpAction::from_str(&syscall.action, Some(syscall.errno_ret as i32))?; + let action = ScmpAction::from_str( + &syscall.action().to_string(), + syscall + .errno_ret() + .map_or(Some(libc::EPERM), |x| Some(x as i32)), + )?; if action == def_action { continue; } - for name in &syscall.names { + for name in syscall.names() { let syscall_num = match ScmpSyscall::from_name(name) { Ok(num) => num, Err(_) => { @@ -98,18 +103,20 @@ pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> { } }; - if syscall.args.is_empty() { + if syscall.args().is_none() { filter.add_rule(action, syscall_num)?; } else { - let conditions = get_rule_conditions(&syscall.args)?; + let syscall_args = syscall.args().clone().unwrap_or_default(); + let conditions = get_rule_conditions(&syscall_args)?; filter.add_rule_conditional(action, syscall_num, &conditions)?; } } } // Set filter attributes for each seccomp flag - for flag in &scmp.flags { - let scmp_attr = get_filter_attr_from_flag(flag)?; + let flags = scmp.flags().clone().unwrap_or_default(); + for flag in flags { + let scmp_attr = get_filter_attr_from_flag(&flag.to_string())?; filter.set_filter_attr(scmp_attr, 1)?; } @@ -123,6 +130,7 @@ pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> { mod tests { use super::*; use libc::{dup3, process_vm_readv, EPERM, O_CLOEXEC}; + use oci_spec::runtime as oci; use std::io::Error; use std::ptr::null; use test_utils::skip_if_not_root; @@ -233,21 +241,23 @@ mod tests { if cfg!(target_endian = "little") { // For little-endian architectures arch = vec![ - "SCMP_ARCH_X86".to_string(), - "SCMP_ARCH_X32".to_string(), - "SCMP_ARCH_X86_64".to_string(), - "SCMP_ARCH_AARCH64".to_string(), - "SCMP_ARCH_ARM".to_string(), - "SCMP_ARCH_PPC64LE".to_string(), + "SCMP_ARCH_X86".parse::().unwrap(), + "SCMP_ARCH_X32".parse::().unwrap(), + "SCMP_ARCH_X86_64".parse::().unwrap(), + "SCMP_ARCH_AARCH64".parse::().unwrap(), + "SCMP_ARCH_ARM".parse::().unwrap(), + "SCMP_ARCH_PPC64LE".parse::().unwrap(), ]; } else { // For big-endian architectures - arch = vec!["SCMP_ARCH_S390X".to_string()]; + arch = vec!["SCMP_ARCH_S390X".parse::().unwrap()]; } - scmp.architectures.append(&mut arch); + let mut archs = scmp.architectures().clone().unwrap(); + archs.append(&mut arch); + scmp.set_architectures(Some(archs)); - init_seccomp(&scmp).unwrap(); + assert!(init_seccomp(&scmp).is_ok()); // Basic syscall with simple rule syscall_assert!(unsafe { dup3(0, 1, O_CLOEXEC) }, -EPERM); diff --git a/src/agent/rustjail/src/specconv.rs b/src/agent/rustjail/src/specconv.rs index ee7f59a3a..d577ba787 100644 --- a/src/agent/rustjail/src/specconv.rs +++ b/src/agent/rustjail/src/specconv.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -use oci::Spec; +use oci_spec::runtime::Spec; #[derive(Serialize, Deserialize, Debug, Default, Clone)] pub struct CreateOpts { diff --git a/src/agent/rustjail/src/validator.rs b/src/agent/rustjail/src/validator.rs index 1d01c7b07..b4da4cc95 100644 --- a/src/agent/rustjail/src/validator.rs +++ b/src/agent/rustjail/src/validator.rs @@ -6,19 +6,26 @@ use crate::container::Config; use anyhow::{anyhow, Context, Result}; use oci::{Linux, LinuxIdMapping, LinuxNamespace, Spec}; +use oci_spec::runtime as oci; use regex::Regex; use std::collections::HashMap; +use std::convert::TryFrom; use std::path::{Component, PathBuf}; fn get_linux(oci: &Spec) -> Result<&Linux> { - oci.linux + oci.linux() .as_ref() .ok_or_else(|| anyhow!("Unable to get Linux section from Spec")) } fn contain_namespace(nses: &[LinuxNamespace], key: &str) -> bool { + let nstype = match oci::LinuxNamespaceType::try_from(key) { + Ok(ns_type) => ns_type, + Err(_e) => return false, + }; + for ns in nses { - if ns.r#type.as_str() == key { + if ns.typ() == nstype { return true; } } @@ -73,12 +80,13 @@ fn rootfs(root: &str) -> Result<()> { } fn hostname(oci: &Spec) -> Result<()> { - if oci.hostname.is_empty() { + if oci.hostname().is_none() { return Ok(()); } let linux = get_linux(oci)?; - if !contain_namespace(&linux.namespaces, "uts") { + let default_vec = vec![]; + if !contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_vec), "uts") { return Err(anyhow!("Linux namespace does not contain uts")); } @@ -90,26 +98,30 @@ fn security(oci: &Spec) -> Result<()> { let label_pattern = r".*_u:.*_r:.*_t:s[0-9]|1[0-5].*"; let label_regex = Regex::new(label_pattern)?; - if let Some(ref process) = oci.process { - if !process.selinux_label.is_empty() && !label_regex.is_match(&process.selinux_label) { + let default_vec = vec![]; + if let Some(process) = oci.process().as_ref() { + if process.selinux_label().is_some() + && !label_regex.is_match(process.selinux_label().as_ref().unwrap()) + { return Err(anyhow!( - "SELinux label for the process is invalid format: {}", - &process.selinux_label + "SELinux label for the process is invalid format: {:?}", + &process.selinux_label() )); } } - if !linux.mount_label.is_empty() && !label_regex.is_match(&linux.mount_label) { + if linux.mount_label().is_some() && !label_regex.is_match(linux.mount_label().as_ref().unwrap()) + { return Err(anyhow!( "SELinux label for the mount is invalid format: {}", - &linux.mount_label + linux.mount_label().as_ref().unwrap() )); } - if linux.masked_paths.is_empty() && linux.readonly_paths.is_empty() { + if linux.masked_paths().is_none() && linux.readonly_paths().is_none() { return Ok(()); } - if !contain_namespace(&linux.namespaces, "mount") { + if !contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_vec), "mnt") { return Err(anyhow!("Linux namespace does not contain mount")); } @@ -118,7 +130,7 @@ fn security(oci: &Spec) -> Result<()> { fn idmapping(maps: &[LinuxIdMapping]) -> Result<()> { for map in maps { - if map.size > 0 { + if map.size() > 0 { return Ok(()); } } @@ -129,18 +141,22 @@ fn idmapping(maps: &[LinuxIdMapping]) -> Result<()> { fn usernamespace(oci: &Spec) -> Result<()> { let linux = get_linux(oci)?; - if contain_namespace(&linux.namespaces, "user") { + let default_vec = vec![]; + if contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_vec), "user") { let user_ns = PathBuf::from("/proc/self/ns/user"); if !user_ns.exists() { return Err(anyhow!("user namespace not supported!")); } // check if idmappings is correct, at least I saw idmaps // with zero size was passed to agent - idmapping(&linux.uid_mappings).context("idmapping uid")?; - idmapping(&linux.gid_mappings).context("idmapping gid")?; + let default_vec2 = vec![]; + idmapping(linux.uid_mappings().as_ref().unwrap_or(&default_vec2)) + .context("idmapping uid")?; + idmapping(linux.gid_mappings().as_ref().unwrap_or(&default_vec2)) + .context("idmapping gid")?; } else { // no user namespace but idmap - if !linux.uid_mappings.is_empty() || !linux.gid_mappings.is_empty() { + if !linux.uid_mappings().is_none() || !linux.gid_mappings().is_none() { return Err(anyhow!("No user namespace, but uid or gid mapping exists")); } } @@ -151,7 +167,11 @@ fn usernamespace(oci: &Spec) -> Result<()> { fn cgroupnamespace(oci: &Spec) -> Result<()> { let linux = get_linux(oci)?; - if contain_namespace(&linux.namespaces, "cgroup") { + let default_vec = vec![]; + if contain_namespace( + linux.namespaces().as_ref().unwrap_or(&default_vec), + "cgroup", + ) { let path = PathBuf::from("/proc/self/ns/cgroup"); if !path.exists() { return Err(anyhow!("cgroup unsupported!")); @@ -178,9 +198,13 @@ lazy_static! { fn sysctl(oci: &Spec) -> Result<()> { let linux = get_linux(oci)?; - for (key, _) in linux.sysctl.iter() { + let default_hash = HashMap::new(); + let sysctl_hash = linux.sysctl().as_ref().unwrap_or(&default_hash); + let default_vec = vec![]; + let linux_namespaces = linux.namespaces().as_ref().unwrap_or(&default_vec); + for (key, _) in sysctl_hash.iter() { if SYSCTLS.contains_key(key.as_str()) || key.starts_with("fs.mqueue.") { - if contain_namespace(&linux.namespaces, "ipc") { + if contain_namespace(linux_namespaces, "ipc") { continue; } else { return Err(anyhow!("Linux namespace does not contain ipc")); @@ -192,7 +216,7 @@ fn sysctl(oci: &Spec) -> Result<()> { continue; } - if contain_namespace(&linux.namespaces, "uts") { + if contain_namespace(linux_namespaces, "uts") { if key == "kernel.domainname" { continue; } @@ -210,11 +234,12 @@ fn sysctl(oci: &Spec) -> Result<()> { fn rootless_euid_mapping(oci: &Spec) -> Result<()> { let linux = get_linux(oci)?; - if !contain_namespace(&linux.namespaces, "user") { + let default_ns = vec![]; + if !contain_namespace(linux.namespaces().as_ref().unwrap_or(&default_ns), "user") { return Err(anyhow!("Linux namespace is missing user")); } - if linux.uid_mappings.is_empty() || linux.gid_mappings.is_empty() { + if linux.uid_mappings().is_none() || linux.gid_mappings().is_none() { return Err(anyhow!( "Rootless containers require at least one UID/GID mapping" )); @@ -225,7 +250,7 @@ fn rootless_euid_mapping(oci: &Spec) -> Result<()> { fn has_idmapping(maps: &[LinuxIdMapping], id: u32) -> bool { for map in maps { - if id >= map.container_id && id < map.container_id + map.size { + if id >= map.container_id() && id < map.container_id() + map.size() { return true; } } @@ -235,8 +260,12 @@ fn has_idmapping(maps: &[LinuxIdMapping], id: u32) -> bool { fn rootless_euid_mount(oci: &Spec) -> Result<()> { let linux = get_linux(oci)?; - for mnt in oci.mounts.iter() { - for opt in mnt.options.iter() { + let default_mounts = vec![]; + let oci_mounts = oci.mounts().as_ref().unwrap_or(&default_mounts); + for mnt in oci_mounts.iter() { + let default_options = vec![]; + let mnt_options = mnt.options().as_ref().unwrap_or(&default_options); + for opt in mnt_options.iter() { if opt.starts_with("uid=") || opt.starts_with("gid=") { let fields: Vec<&str> = opt.split('=').collect(); @@ -249,11 +278,15 @@ fn rootless_euid_mount(oci: &Spec) -> Result<()> { .parse::() .context(format!("parse field {}", &fields[1]))?; - if opt.starts_with("uid=") && !has_idmapping(&linux.uid_mappings, id) { + if opt.starts_with("uid=") + && !has_idmapping(linux.uid_mappings().as_ref().unwrap_or(&vec![]), id) + { return Err(anyhow!("uid of {} does not have a valid mapping", id)); } - if opt.starts_with("gid=") && !has_idmapping(&linux.gid_mappings, id) { + if opt.starts_with("gid=") + && !has_idmapping(linux.gid_mappings().as_ref().unwrap_or(&vec![]), id) + { return Err(anyhow!("gid of {} does not have a valid mapping", id)); } } @@ -275,16 +308,16 @@ pub fn validate(conf: &Config) -> Result<()> { .as_ref() .ok_or_else(|| anyhow!("Invalid config spec"))?; - if oci.linux.is_none() { + if oci.linux().is_none() { return Err(anyhow!("oci Linux is none")); } - let root = match oci.root.as_ref() { - Some(v) => v.path.as_str(), + let root = match oci.root().as_ref() { + Some(v) => v.path().display().to_string(), None => return Err(anyhow!("oci root is none")), }; - rootfs(root).context("rootfs")?; + rootfs(&root).context("rootfs")?; hostname(oci).context("hostname")?; security(oci).context("security")?; usernamespace(oci).context("usernamespace")?; @@ -301,19 +334,22 @@ pub fn validate(conf: &Config) -> Result<()> { #[cfg(test)] mod tests { use super::*; - use oci::{Mount, Process}; + use oci::{LinuxIdMappingBuilder, LinuxNamespaceBuilder, LinuxNamespaceType, Process, Spec}; + use oci_spec::runtime as oci; #[test] fn test_namespace() { let namespaces = [ - LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }, - LinuxNamespace { - r#type: "uts".to_owned(), - path: "/sys/cgroups/uts".to_owned(), - }, + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .path("/sys/cgroups/uts") + .build() + .unwrap(), ]; assert_eq!(contain_namespace(&namespaces, "net"), true); @@ -347,24 +383,27 @@ mod tests { fn test_hostname() { let mut spec = Spec::default(); - hostname(&spec).unwrap(); + assert!(hostname(&spec).is_ok()); - spec.hostname = "a.test.com".to_owned(); - hostname(&spec).unwrap_err(); + spec.set_hostname(Some("a.test.com".to_owned())); + assert!(hostname(&spec).is_ok()); let mut linux = Linux::default(); - linux.namespaces = vec![ - LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }, - LinuxNamespace { - r#type: "uts".to_owned(), - path: "/sys/cgroups/uts".to_owned(), - }, + let namespaces = vec![ + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .path("/sys/cgroups/uts") + .build() + .unwrap(), ]; - spec.linux = Some(linux); - hostname(&spec).unwrap(); + linux.set_namespaces(Some(namespaces)); + spec.set_linux(Some(linux)); + assert!(hostname(&spec).is_ok()); } #[test] @@ -372,88 +411,89 @@ mod tests { let mut spec = Spec::default(); let linux = Linux::default(); - spec.linux = Some(linux); + spec.set_linux(Some(linux)); security(&spec).unwrap(); let mut linux = Linux::default(); - linux.masked_paths.push("/test".to_owned()); - linux.namespaces = vec![ - LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }, - LinuxNamespace { - r#type: "uts".to_owned(), - path: "/sys/cgroups/uts".to_owned(), - }, + linux.set_masked_paths(Some(vec!["/test".to_owned()])); + let namespaces = vec![ + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .path("/sys/cgroups/uts") + .build() + .unwrap(), ]; - spec.linux = Some(linux); + linux.set_namespaces(Some(namespaces)); + spec.set_linux(Some(linux)); security(&spec).unwrap_err(); let mut linux = Linux::default(); - linux.masked_paths.push("/test".to_owned()); - linux.namespaces = vec![ - LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }, - LinuxNamespace { - r#type: "mount".to_owned(), - path: "/sys/cgroups/mount".to_owned(), - }, + linux.set_masked_paths(Some(vec!["/test".to_owned()])); + let namespaces = vec![ + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Mount) + .path("/sys/cgroups/mount") + .build() + .unwrap(), ]; - spec.linux = Some(linux); - security(&spec).unwrap(); + linux.set_namespaces(Some(namespaces)); + spec.set_linux(Some(linux)); + assert!(security(&spec).is_ok()); // SELinux let valid_label = "system_u:system_r:container_t:s0:c123,c456"; let mut process = Process::default(); - process.selinux_label = valid_label.to_string(); - spec.process = Some(process); + process.set_selinux_label(Some(valid_label.to_string())); + spec.set_process(Some(process)); security(&spec).unwrap(); let mut linux = Linux::default(); - linux.mount_label = valid_label.to_string(); - spec.linux = Some(linux); + linux.set_mount_label(Some(valid_label.to_string())); + spec.set_linux(Some(linux)); security(&spec).unwrap(); let invalid_label = "system_u:system_r:container_t"; let mut process = Process::default(); - process.selinux_label = invalid_label.to_string(); - spec.process = Some(process); + process.set_selinux_label(Some(invalid_label.to_string())); + spec.set_process(Some(process)); security(&spec).unwrap_err(); let mut linux = Linux::default(); - linux.mount_label = invalid_label.to_string(); - spec.linux = Some(linux); + linux.set_mount_label(Some(valid_label.to_string())); + spec.set_linux(Some(linux)); security(&spec).unwrap_err(); } #[test] fn test_usernamespace() { let mut spec = Spec::default(); - usernamespace(&spec).unwrap_err(); + assert!(usernamespace(&spec).is_ok()); let linux = Linux::default(); - spec.linux = Some(linux); + spec.set_linux(Some(linux)); usernamespace(&spec).unwrap(); let mut linux = Linux::default(); - linux.uid_mappings = vec![LinuxIdMapping { - container_id: 0, - host_id: 1000, - size: 0, - }]; - spec.linux = Some(linux); - usernamespace(&spec).unwrap_err(); - let mut linux = Linux::default(); - linux.uid_mappings = vec![LinuxIdMapping { - container_id: 0, - host_id: 1000, - size: 100, - }]; - spec.linux = Some(linux); + let uidmap = LinuxIdMappingBuilder::default() + .container_id(0u32) + .host_id(1000u32) + .size(0u32) + .build() + .unwrap(); + + linux.set_uid_mappings(Some(vec![uidmap])); + spec.set_linux(Some(linux)); usernamespace(&spec).unwrap_err(); } @@ -467,62 +507,73 @@ mod tests { // Test case: without user namespace let linux = Linux::default(); - spec.linux = Some(linux); + spec.set_linux(Some(linux)); rootless_euid_mapping(&spec).unwrap_err(); // Test case: without user namespace - let linux = spec.linux.as_mut().unwrap(); - linux.namespaces = vec![ - LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }, - LinuxNamespace { - r#type: "uts".to_owned(), - path: "/sys/cgroups/uts".to_owned(), - }, + let linux = spec.linux_mut().as_mut().unwrap(); + let namespaces = vec![ + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .path("/sys/cgroups/uts") + .build() + .unwrap(), ]; + linux.set_namespaces(Some(namespaces)); rootless_euid_mapping(&spec).unwrap_err(); - let linux = spec.linux.as_mut().unwrap(); - linux.namespaces = vec![ - LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }, - LinuxNamespace { - r#type: "user".to_owned(), - path: "/sys/cgroups/user".to_owned(), - }, + let linux = spec.linux_mut().as_mut().unwrap(); + let namespaces = vec![ + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::User) + .path("/sys/cgroups/user") + .build() + .unwrap(), ]; - linux.uid_mappings = vec![LinuxIdMapping { - container_id: 0, - host_id: 1000, - size: 1000, - }]; - linux.gid_mappings = vec![LinuxIdMapping { - container_id: 0, - host_id: 1000, - size: 1000, - }]; + linux.set_namespaces(Some(namespaces)); + + let uidmap = LinuxIdMappingBuilder::default() + .container_id(0u32) + .host_id(1000u32) + .size(1000u32) + .build() + .unwrap(); + let gidmap = LinuxIdMappingBuilder::default() + .container_id(0u32) + .host_id(1000u32) + .size(1000u32) + .build() + .unwrap(); + + linux.set_uid_mappings(Some(vec![uidmap])); + linux.set_gid_mappings(Some(vec![gidmap])); rootless_euid_mapping(&spec).unwrap(); - spec.mounts.push(Mount { - destination: "/app".to_owned(), - r#type: "tmpfs".to_owned(), - source: "".to_owned(), - options: vec!["uid=10000".to_owned()], - }); + let mut oci_mount = oci::Mount::default(); + oci_mount.set_destination("/app".into()); + oci_mount.set_typ(Some("tmpfs".to_owned())); + oci_mount.set_source(Some("".into())); + oci_mount.set_options(Some(vec!["uid=10000".to_owned()])); + spec.mounts_mut().as_mut().unwrap().push(oci_mount); rootless_euid_mount(&spec).unwrap_err(); - spec.mounts = vec![ - (Mount { - destination: "/app".to_owned(), - r#type: "tmpfs".to_owned(), - source: "".to_owned(), - options: vec!["uid=500".to_owned(), "gid=500".to_owned()], - }), - ]; + let mut oci_mount = oci::Mount::default(); + oci_mount.set_destination("/app".into()); + oci_mount.set_typ(Some("tmpfs".to_owned())); + oci_mount.set_source(Some("".into())); + oci_mount.set_options(Some(vec!["uid=500".to_owned(), "gid=500".to_owned()])); + spec.set_mounts(Some(vec![oci_mount])); + rootless_euid(&spec).unwrap(); } @@ -531,25 +582,34 @@ mod tests { let mut spec = Spec::default(); let mut linux = Linux::default(); - linux.namespaces = vec![LinuxNamespace { - r#type: "net".to_owned(), - path: "/sys/cgroups/net".to_owned(), - }]; - linux - .sysctl - .insert("kernel.domainname".to_owned(), "test.com".to_owned()); - spec.linux = Some(linux); + let namespaces = vec![LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Network) + .path("/sys/cgroups/net") + .build() + .unwrap()]; + linux.set_namespaces(Some(namespaces)); + + let mut sysctl_hash = HashMap::new(); + sysctl_hash.insert("kernel.domainname".to_owned(), "test.com".to_owned()); + linux.set_sysctl(Some(sysctl_hash)); + + spec.set_linux(Some(linux)); sysctl(&spec).unwrap_err(); - spec.linux + spec.linux_mut() .as_mut() .unwrap() - .namespaces - .push(LinuxNamespace { - r#type: "uts".to_owned(), - path: "/sys/cgroups/uts".to_owned(), - }); - sysctl(&spec).unwrap(); + .namespaces_mut() + .as_mut() + .unwrap() + .push( + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::User) + .path("/sys/cgroups/user") + .build() + .unwrap(), + ); + assert!(sysctl(&spec).is_err()); } #[test] @@ -569,7 +629,7 @@ mod tests { validate(&config).unwrap_err(); let linux = Linux::default(); - config.spec.as_mut().unwrap().linux = Some(linux); + config.spec.as_mut().unwrap().set_linux(Some(linux)); validate(&config).unwrap_err(); } } diff --git a/src/agent/src/device.rs b/src/agent/src/device.rs index 41d84e59f..a8faacc06 100644 --- a/src/agent/src/device.rs +++ b/src/agent/src/device.rs @@ -23,7 +23,8 @@ use crate::sandbox::Sandbox; use crate::uevent::{wait_for_uevent, Uevent, UeventMatcher}; use anyhow::{anyhow, Context, Result}; use cfg_if::cfg_if; -use oci::{LinuxDeviceCgroup, LinuxResources, Spec}; +use oci::{LinuxDeviceCgroup, Spec}; +use oci_spec::runtime as oci; use protocols::agent::Device; use tracing::instrument; @@ -621,21 +622,25 @@ impl> From for SpecUpdate { #[instrument] fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) -> Result<()> { let linux = spec - .linux + .linux_mut() .as_mut() .ok_or_else(|| anyhow!("Spec didn't contain linux field"))?; - let mut res_updates = HashMap::<(&str, i64, i64), DeviceInfo>::with_capacity(updates.len()); + let mut res_updates = HashMap::<(String, i64, i64), DeviceInfo>::with_capacity(updates.len()); - for specdev in &mut linux.devices { - if let Some(update) = updates.remove(specdev.path.as_str()) { - let host_major = specdev.major; - let host_minor = specdev.minor; + let mut default_devices = Vec::new(); + let linux_devices = linux.devices_mut().as_mut().unwrap_or(&mut default_devices); + for specdev in linux_devices.iter_mut() { + let devtype = specdev.typ().as_str().to_string(); + if let Some(update) = updates.remove(specdev.path().clone().display().to_string().as_str()) + { + let host_major = specdev.major(); + let host_minor = specdev.minor(); info!( sl(), "update_spec_devices() updating device"; - "container_path" => &specdev.path, - "type" => &specdev.r#type, + "container_path" => &specdev.path().display().to_string(), + "type" => &devtype, "host_major" => host_major, "host_minor" => host_minor, "guest_major" => update.info.guest_major, @@ -643,17 +648,14 @@ fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) - "final_path" => update.final_path.as_ref(), ); - specdev.major = update.info.guest_major; - specdev.minor = update.info.guest_minor; + specdev.set_major(update.info.guest_major); + specdev.set_minor(update.info.guest_minor); if let Some(final_path) = update.final_path { - specdev.path = final_path; + specdev.set_path(PathBuf::from(&final_path)); } if res_updates - .insert( - (specdev.r#type.as_str(), host_major, host_minor), - update.info, - ) + .insert((devtype, host_major, host_minor), update.info) .is_some() { return Err(anyhow!( @@ -677,23 +679,27 @@ fn update_spec_devices(spec: &mut Spec, mut updates: HashMap<&str, DevUpdate>) - )); } - if let Some(resources) = linux.resources.as_mut() { - for r in &mut resources.devices { - if let (Some(host_major), Some(host_minor)) = (r.major, r.minor) { - if let Some(update) = res_updates.get(&(r.r#type.as_str(), host_major, host_minor)) - { - info!( - sl(), - "update_spec_devices() updating resource"; - "type" => &r.r#type, - "host_major" => host_major, - "host_minor" => host_minor, - "guest_major" => update.guest_major, - "guest_minor" => update.guest_minor, - ); + if let Some(resources) = linux.resources_mut().as_mut() { + if let Some(resources_devices) = resources.devices_mut().as_mut() { + for d in resources_devices.iter_mut() { + let dev_type = d.typ().unwrap_or_default().as_str().to_string(); + if let (Some(host_major), Some(host_minor)) = (d.major(), d.minor()) { + if let Some(update) = + res_updates.get(&(dev_type.clone(), host_major, host_minor)) + { + info!( + sl(), + "update_spec_devices() updating resource"; + "type" => &dev_type, + "host_major" => host_major, + "host_minor" => host_minor, + "guest_major" => update.guest_major, + "guest_minor" => update.guest_minor, + ); - r.major = Some(update.guest_major); - r.minor = Some(update.guest_minor); + d.set_major(Some(update.guest_major)); + d.set_minor(Some(update.guest_minor)); + } } } } @@ -984,8 +990,10 @@ pub async fn add_devices( } } - if let Some(process) = spec.process.as_mut() { - update_env_pci(&mut process.env, &sandbox.lock().await.pcimap)? + if let Some(process) = spec.process_mut() { + let env_vec: &mut Vec = + &mut process.env_mut().get_or_insert_with(Vec::new).to_vec(); + update_env_pci(env_vec, &sandbox.lock().await.pcimap)? } update_spec_devices(spec, dev_updates) } @@ -1031,31 +1039,40 @@ pub fn insert_devices_cgroup_rule( access: &str, ) -> Result<()> { let linux = spec - .linux + .linux_mut() .as_mut() .ok_or_else(|| anyhow!("Spec didn't container linux field"))?; - - let resources = linux.resources.get_or_insert(LinuxResources::default()); - - let cgroup = LinuxDeviceCgroup { - allow, - major: Some(dev_info.guest_major), - minor: Some(dev_info.guest_minor), - r#type: dev_info.cgroup_type.clone(), - access: access.to_owned(), - }; + let devcgrp_type = dev_info + .cgroup_type + .parse::() + .context(format!( + "Failed to parse {:?} to Enum LinuxDeviceType", + dev_info.cgroup_type + ))?; + let linux_resource = &mut oci::LinuxResources::default(); + let resource = linux.resources_mut().as_mut().unwrap_or(linux_resource); + let mut device_cgrp = LinuxDeviceCgroup::default(); + device_cgrp.set_allow(allow); + device_cgrp.set_major(Some(dev_info.guest_major)); + device_cgrp.set_minor(Some(dev_info.guest_minor)); + device_cgrp.set_typ(Some(devcgrp_type)); + device_cgrp.set_access(Some(access.to_owned())); debug!( sl(), "Insert a devices cgroup rule"; - "linux_device_cgroup" => cgroup.allow, - "guest_major" => cgroup.major, - "guest_minor" => cgroup.minor, - "type" => cgroup.r#type.as_str(), - "access" => cgroup.access.as_str(), + "linux_device_cgroup" => device_cgrp.allow(), + "guest_major" => device_cgrp.major(), + "guest_minor" => device_cgrp.minor(), + "type" => device_cgrp.typ().unwrap().as_str(), + "access" => device_cgrp.access().as_ref().unwrap().as_str(), ); - resources.devices.push(cgroup); + if let Some(devices) = resource.devices_mut() { + devices.push(device_cgrp); + } else { + resource.set_devices(Some(vec![device_cgrp])); + } Ok(()) } @@ -1064,7 +1081,11 @@ pub fn insert_devices_cgroup_rule( mod tests { use super::*; use crate::uevent::spawn_test_watcher; - use oci::Linux; + use oci::{ + Linux, LinuxBuilder, LinuxDeviceBuilder, LinuxDeviceCgroupBuilder, LinuxDeviceType, + LinuxResources, LinuxResourcesBuilder, SpecBuilder, + }; + use oci_spec::runtime as oci; use std::iter::FromIterator; use tempfile::tempdir; @@ -1072,15 +1093,23 @@ mod tests { #[test] fn test_update_device_cgroup() { - let mut spec = Spec { - linux: Some(Linux::default()), - ..Default::default() - }; + let mut linux = Linux::default(); + linux.set_resources(Some(LinuxResources::default())); + let mut spec = SpecBuilder::default().linux(linux).build().unwrap(); let dev_info = DeviceInfo::new(VM_ROOTFS, false).unwrap(); insert_devices_cgroup_rule(&mut spec, &dev_info, false, "rw").unwrap(); - let devices = spec.linux.unwrap().resources.unwrap().devices; + let devices = spec + .linux() + .as_ref() + .unwrap() + .resources() + .as_ref() + .unwrap() + .devices() + .clone() + .unwrap(); assert_eq!(devices.len(), 1); let meta = fs::metadata(VM_ROOTFS).unwrap(); @@ -1088,8 +1117,8 @@ mod tests { let major = stat::major(rdev) as i64; let minor = stat::minor(rdev) as i64; - assert_eq!(devices[0].major, Some(major)); - assert_eq!(devices[0].minor, Some(minor)); + assert_eq!(devices[0].major(), Some(major)); + assert_eq!(devices[0].minor(), Some(minor)); } #[test] @@ -1113,7 +1142,7 @@ mod tests { ); assert!(res.is_err()); - spec.linux = Some(Linux::default()); + spec.set_linux(Some(Linux::default())); // linux.devices doesn't contain the updated device let res = update_spec_devices( @@ -1125,12 +1154,15 @@ mod tests { ); assert!(res.is_err()); - spec.linux.as_mut().unwrap().devices = vec![oci::LinuxDevice { - path: "/dev/null2".to_string(), - major, - minor, - ..oci::LinuxDevice::default() - }]; + spec.linux_mut() + .as_mut() + .unwrap() + .set_devices(Some(vec![LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/null2")) + .major(major) + .minor(minor) + .build() + .unwrap()])); // guest and host path are not the same let res = update_spec_devices( @@ -1148,7 +1180,13 @@ mod tests { spec ); - spec.linux.as_mut().unwrap().devices[0].path = container_path.to_string(); + spec.linux_mut() + .as_mut() + .unwrap() + .devices_mut() + .as_mut() + .unwrap()[0] + .set_path(PathBuf::from(container_path)); // spec.linux.resources is empty let res = update_spec_devices( @@ -1161,21 +1199,26 @@ mod tests { assert!(res.is_ok()); // update both devices and cgroup lists - spec.linux.as_mut().unwrap().devices = vec![oci::LinuxDevice { - path: container_path.to_string(), - major, - minor, - ..oci::LinuxDevice::default() - }]; + spec.linux_mut() + .as_mut() + .unwrap() + .set_devices(Some(vec![LinuxDeviceBuilder::default() + .path(PathBuf::from(container_path)) + .major(major) + .minor(minor) + .build() + .unwrap()])); - spec.linux.as_mut().unwrap().resources = Some(oci::LinuxResources { - devices: vec![oci::LinuxDeviceCgroup { - major: Some(major), - minor: Some(minor), - ..oci::LinuxDeviceCgroup::default() - }], - ..oci::LinuxResources::default() - }); + spec.linux_mut().as_mut().unwrap().set_resources(Some( + oci::LinuxResourcesBuilder::default() + .devices(vec![LinuxDeviceCgroupBuilder::default() + .major(major) + .minor(minor) + .build() + .unwrap()]) + .build() + .unwrap(), + )); let res = update_spec_devices( &mut spec, @@ -1198,45 +1241,49 @@ mod tests { let host_major_b = stat::major(zero_rdev) as i64; let host_minor_b = stat::minor(zero_rdev) as i64; - let mut spec = Spec { - linux: Some(Linux { - devices: vec![ - oci::LinuxDevice { - path: "/dev/a".to_string(), - r#type: "c".to_string(), - major: host_major_a, - minor: host_minor_a, - ..oci::LinuxDevice::default() - }, - oci::LinuxDevice { - path: "/dev/b".to_string(), - r#type: "c".to_string(), - major: host_major_b, - minor: host_minor_b, - ..oci::LinuxDevice::default() - }, - ], - resources: Some(LinuxResources { - devices: vec![ - oci::LinuxDeviceCgroup { - r#type: "c".to_string(), - major: Some(host_major_a), - minor: Some(host_minor_a), - ..oci::LinuxDeviceCgroup::default() - }, - oci::LinuxDeviceCgroup { - r#type: "c".to_string(), - major: Some(host_major_b), - minor: Some(host_minor_b), - ..oci::LinuxDeviceCgroup::default() - }, - ], - ..LinuxResources::default() - }), - ..Linux::default() - }), - ..Spec::default() - }; + let mut spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .devices(vec![ + LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/a")) + .typ(LinuxDeviceType::C) + .major(host_major_a) + .minor(host_minor_a) + .build() + .unwrap(), + LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/b")) + .typ(LinuxDeviceType::C) + .major(host_major_b) + .minor(host_minor_b) + .build() + .unwrap(), + ]) + .resources( + LinuxResourcesBuilder::default() + .devices(vec![ + LinuxDeviceCgroupBuilder::default() + .typ(LinuxDeviceType::C) + .major(host_major_a) + .minor(host_minor_a) + .build() + .unwrap(), + LinuxDeviceCgroupBuilder::default() + .typ(LinuxDeviceType::C) + .major(host_major_b) + .minor(host_minor_b) + .build() + .unwrap(), + ]) + .build() + .unwrap(), + ) + .build() + .unwrap(), + ) + .build() + .unwrap(); let container_path_a = "/dev/a"; let vm_path_a = "/dev/zero"; @@ -1250,17 +1297,26 @@ mod tests { let guest_major_b = stat::major(full_rdev) as i64; let guest_minor_b = stat::minor(full_rdev) as i64; - let specdevices = &spec.linux.as_ref().unwrap().devices; - assert_eq!(host_major_a, specdevices[0].major); - assert_eq!(host_minor_a, specdevices[0].minor); - assert_eq!(host_major_b, specdevices[1].major); - assert_eq!(host_minor_b, specdevices[1].minor); + let specdevices = &spec.linux().as_ref().unwrap().devices().clone().unwrap(); + assert_eq!(host_major_a, specdevices[0].major()); + assert_eq!(host_minor_a, specdevices[0].minor()); + assert_eq!(host_major_b, specdevices[1].major()); + assert_eq!(host_minor_b, specdevices[1].minor()); - let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); - assert_eq!(Some(host_major_a), specresources.devices[0].major); - assert_eq!(Some(host_minor_a), specresources.devices[0].minor); - assert_eq!(Some(host_major_b), specresources.devices[1].major); - assert_eq!(Some(host_minor_b), specresources.devices[1].minor); + let specresources_devices = spec + .linux() + .as_ref() + .unwrap() + .resources() + .as_ref() + .unwrap() + .devices() + .clone() + .unwrap(); + assert_eq!(Some(host_major_a), specresources_devices[0].major()); + assert_eq!(Some(host_minor_a), specresources_devices[0].minor()); + assert_eq!(Some(host_major_b), specresources_devices[1].major()); + assert_eq!(Some(host_minor_b), specresources_devices[1].minor()); let updates = HashMap::from_iter(vec![ ( @@ -1275,17 +1331,26 @@ mod tests { let res = update_spec_devices(&mut spec, updates); assert!(res.is_ok()); - let specdevices = &spec.linux.as_ref().unwrap().devices; - assert_eq!(guest_major_a, specdevices[0].major); - assert_eq!(guest_minor_a, specdevices[0].minor); - assert_eq!(guest_major_b, specdevices[1].major); - assert_eq!(guest_minor_b, specdevices[1].minor); + let specdevices = &spec.linux().as_ref().unwrap().devices().clone().unwrap(); + assert_eq!(guest_major_a, specdevices[0].major()); + assert_eq!(guest_minor_a, specdevices[0].minor()); + assert_eq!(guest_major_b, specdevices[1].major()); + assert_eq!(guest_minor_b, specdevices[1].minor()); - let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); - assert_eq!(Some(guest_major_a), specresources.devices[0].major); - assert_eq!(Some(guest_minor_a), specresources.devices[0].minor); - assert_eq!(Some(guest_major_b), specresources.devices[1].major); - assert_eq!(Some(guest_minor_b), specresources.devices[1].minor); + let specresources_devices = spec + .linux() + .as_ref() + .unwrap() + .resources() + .as_ref() + .unwrap() + .devices() + .clone() + .unwrap(); + assert_eq!(Some(guest_major_a), specresources_devices[0].major()); + assert_eq!(Some(guest_minor_a), specresources_devices[0].minor()); + assert_eq!(Some(guest_major_b), specresources_devices[1].major()); + assert_eq!(Some(guest_minor_b), specresources_devices[1].minor()); } #[test] @@ -1297,54 +1362,67 @@ mod tests { let host_major: i64 = 99; let host_minor: i64 = 99; - let mut spec = Spec { - linux: Some(Linux { - devices: vec![ - oci::LinuxDevice { - path: "/dev/char".to_string(), - r#type: "c".to_string(), - major: host_major, - minor: host_minor, - ..oci::LinuxDevice::default() - }, - oci::LinuxDevice { - path: "/dev/block".to_string(), - r#type: "b".to_string(), - major: host_major, - minor: host_minor, - ..oci::LinuxDevice::default() - }, - ], - resources: Some(LinuxResources { - devices: vec![ - LinuxDeviceCgroup { - r#type: "c".to_string(), - major: Some(host_major), - minor: Some(host_minor), - ..LinuxDeviceCgroup::default() - }, - LinuxDeviceCgroup { - r#type: "b".to_string(), - major: Some(host_major), - minor: Some(host_minor), - ..LinuxDeviceCgroup::default() - }, - ], - ..LinuxResources::default() - }), - ..Linux::default() - }), - ..Spec::default() - }; + let mut spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .devices(vec![ + LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/char")) + .typ(LinuxDeviceType::C) + .major(host_major) + .minor(host_minor) + .build() + .unwrap(), + LinuxDeviceBuilder::default() + .path(PathBuf::from("/dev/block")) + .typ(LinuxDeviceType::B) + .major(host_major) + .minor(host_minor) + .build() + .unwrap(), + ]) + .resources( + LinuxResourcesBuilder::default() + .devices(vec![ + LinuxDeviceCgroupBuilder::default() + .typ(LinuxDeviceType::C) + .major(host_major) + .minor(host_minor) + .build() + .unwrap(), + LinuxDeviceCgroupBuilder::default() + .typ(LinuxDeviceType::B) + .major(host_major) + .minor(host_minor) + .build() + .unwrap(), + ]) + .build() + .unwrap(), + ) + .build() + .unwrap(), + ) + .build() + .unwrap(); let container_path = "/dev/char"; let vm_path = "/dev/null"; - let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); - assert_eq!(Some(host_major), specresources.devices[0].major); - assert_eq!(Some(host_minor), specresources.devices[0].minor); - assert_eq!(Some(host_major), specresources.devices[1].major); - assert_eq!(Some(host_minor), specresources.devices[1].minor); + let specresources_devices = spec + .linux() + .as_ref() + .unwrap() + .resources() + .as_ref() + .unwrap() + .devices() + .clone() + .unwrap(); + assert_eq!(Some(host_major), specresources_devices[0].major()); + assert_eq!(Some(host_minor), specresources_devices[0].minor()); + assert_eq!(Some(host_major), specresources_devices[1].major()); + assert_eq!(Some(host_minor), specresources_devices[1].minor()); let res = update_spec_devices( &mut spec, @@ -1356,11 +1434,20 @@ mod tests { assert!(res.is_ok()); // Only the char device, not the block device should be updated - let specresources = spec.linux.as_ref().unwrap().resources.as_ref().unwrap(); - assert_eq!(Some(guest_major), specresources.devices[0].major); - assert_eq!(Some(guest_minor), specresources.devices[0].minor); - assert_eq!(Some(host_major), specresources.devices[1].major); - assert_eq!(Some(host_minor), specresources.devices[1].minor); + let specresources_devices = spec + .linux() + .as_ref() + .unwrap() + .resources() + .as_ref() + .unwrap() + .devices() + .clone() + .unwrap(); + assert_eq!(Some(guest_major), specresources_devices[0].major()); + assert_eq!(Some(guest_minor), specresources_devices[0].minor()); + assert_eq!(Some(host_major), specresources_devices[1].major()); + assert_eq!(Some(host_minor), specresources_devices[1].minor()); } #[test] @@ -1373,19 +1460,21 @@ mod tests { let host_major: i64 = 99; let host_minor: i64 = 99; - let mut spec = Spec { - linux: Some(Linux { - devices: vec![oci::LinuxDevice { - path: container_path.to_string(), - r#type: "c".to_string(), - major: host_major, - minor: host_minor, - ..oci::LinuxDevice::default() - }], - ..Linux::default() - }), - ..Spec::default() - }; + let mut spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .devices(vec![LinuxDeviceBuilder::default() + .path(PathBuf::from(container_path)) + .typ(LinuxDeviceType::C) + .major(host_major) + .minor(host_minor) + .build() + .unwrap()]) + .build() + .unwrap(), + ) + .build() + .unwrap(); let vm_path = "/dev/null"; let final_path = "/dev/new"; @@ -1399,10 +1488,10 @@ mod tests { ); assert!(res.is_ok()); - let specdevices = &spec.linux.as_ref().unwrap().devices; - assert_eq!(guest_major, specdevices[0].major); - assert_eq!(guest_minor, specdevices[0].minor); - assert_eq!(final_path, specdevices[0].path); + let specdevices = &spec.linux().as_ref().unwrap().devices().clone().unwrap(); + assert_eq!(guest_major, specdevices[0].major()); + assert_eq!(guest_minor, specdevices[0].minor()); + assert_eq!(&PathBuf::from(final_path), specdevices[0].path()); } #[test] diff --git a/src/agent/src/image.rs b/src/agent/src/image.rs index c4fc21b57..1a53fa5f4 100644 --- a/src/agent/src/image.rs +++ b/src/agent/src/image.rs @@ -15,6 +15,7 @@ use std::sync::Arc; use anyhow::{anyhow, bail, Context, Result}; use image_rs::image::ImageClient; use kata_sys_util::validate::verify_id; +use oci_spec::runtime as oci; use tokio::sync::Mutex; use crate::rpc::CONTAINER_BASE; @@ -78,7 +79,7 @@ impl ImageService { })?) .context("load image config file")?; - let image_oci_process = image_oci.process.ok_or_else(|| { + let image_oci_process = image_oci.process().as_ref().ok_or_else(|| { anyhow!("The guest pause image config does not contain a process specification. Please check the pause image.") })?; info!( @@ -88,11 +89,12 @@ impl ImageService { ); // Ensure that the args vector is not empty before accessing its elements. - let args = image_oci_process.args; // Check the number of arguments. - if args.is_empty() { + let args = if let Some(args_vec) = image_oci_process.args() { + args_vec + } else { bail!("The number of args should be greater than or equal to one! Please check the pause image."); - } + }; let pause_bundle = scoped_join(CONTAINER_BASE, cid)?; fs::create_dir_all(&pause_bundle)?; diff --git a/src/agent/src/main.rs b/src/agent/src/main.rs index ccde79e98..6d1327d76 100644 --- a/src/agent/src/main.rs +++ b/src/agent/src/main.rs @@ -6,7 +6,6 @@ #[macro_use] extern crate lazy_static; extern crate capctl; -extern crate oci; extern crate prometheus; extern crate protocols; extern crate regex; diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index f7244dc89..78dee8b4f 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -8,6 +8,7 @@ use rustjail::{pipestream::PipeStream, process::StreamType}; use tokio::io::{AsyncReadExt, AsyncWriteExt, ReadHalf}; use tokio::sync::Mutex; +use std::convert::TryFrom; use std::ffi::{CString, OsStr}; use std::fmt::Debug; use std::io; @@ -22,7 +23,8 @@ use ttrpc::{ use anyhow::{anyhow, Context, Result}; use cgroups::freezer::FreezerState; -use oci::{LinuxNamespace, Root, Spec}; +use oci::{Hooks, LinuxNamespace, Spec}; +use oci_spec::runtime as oci; use protobuf::MessageField; use protocols::agent::{ AddSwapRequest, AgentDetails, CopyFileRequest, GetIPTablesRequest, GetIPTablesResponse, @@ -64,6 +66,7 @@ use crate::pci; use crate::random; use crate::sandbox::Sandbox; use crate::storage::{add_storages, update_ephemeral_mounts, STORAGE_HANDLERS}; +use crate::util; use crate::version::{AGENT_VERSION, API_VERSION}; use crate::AGENT_CONFIG; @@ -194,11 +197,10 @@ impl AgentService { kata_sys_util::validate::verify_id(&cid)?; - let mut oci_spec = req.OCI.clone(); let use_sandbox_pidns = req.sandbox_pidns(); - let mut oci = match oci_spec.as_mut() { - Some(spec) => rustjail::grpc_to_oci(spec), + let mut oci = match req.OCI.into_option() { + Some(spec) => spec.into(), None => { error!(sl(), "no oci spec in the create container request!"); return Err(anyhow!(nix::Error::EINVAL)); @@ -222,15 +224,17 @@ impl AgentService { if let Some(cdh) = self.cdh_client.as_ref() { let process = oci - .process + .process_mut() .as_mut() .ok_or_else(|| anyhow!("Spec didn't contain process field"))?; - for env in process.env.iter_mut() { - match cdh.unseal_env(env).await { - Ok(unsealed_env) => *env = unsealed_env.to_string(), - Err(e) => { - warn!(sl(), "Failed to unseal secret: {}", e) + if let Some(envs) = process.env_mut().as_mut() { + for env in envs.iter_mut() { + match cdh.unseal_env(env).await { + Ok(unsealed_env) => *env = unsealed_env.to_string(), + Err(e) => { + warn!(sl(), "Failed to unseal secret: {}", e) + } } } } @@ -263,7 +267,13 @@ impl AgentService { // systemd: "[slice]:[prefix]:[name]" // fs: "/path_a/path_b" // If agent is init we can't use systemd cgroup mode, no matter what the host tells us - let cgroups_path = oci.linux.as_ref().map_or("", |linux| &linux.cgroups_path); + let cgroups_path = &oci + .linux() + .as_ref() + .and_then(|linux| linux.cgroups_path().as_ref()) + .map(|cgrps_path| cgrps_path.display().to_string()) + .unwrap_or_default(); + let use_systemd_cgroup = if self.init_mode { false } else { @@ -291,8 +301,8 @@ impl AgentService { let pipe_size = AGENT_CONFIG.container_pipe_size; - let p = if let Some(p) = oci.process { - Process::new(&sl(), &p, cid.as_str(), true, pipe_size, proc_io)? + let p = if let Some(p) = oci.process() { + Process::new(&sl(), p, cid.as_str(), true, pipe_size, proc_io)? } else { info!(sl(), "no process configurations!"); return Err(anyhow!(nix::Error::EINVAL)); @@ -408,8 +418,7 @@ impl AgentService { update_env_pci(&mut process.Env, &sandbox.pcimap)?; let pipe_size = AGENT_CONFIG.container_pipe_size; - let ocip = rustjail::process_grpc_to_oci(&process); - + let ocip = process.into(); let p = Process::new(&sl(), &ocip, exec_id.as_str(), false, pipe_size, proc_io)?; let ctr = sandbox @@ -759,7 +768,7 @@ impl agent_ttrpc::AgentService for AgentService { .get_container(&req.container_id) .map_ttrpc_err(ttrpc::Code::INVALID_ARGUMENT, "invalid container id")?; if let Some(res) = req.resources.as_ref() { - let oci_res = rustjail::resources_grpc_to_oci(res); + let oci_res = res.clone().into(); ctr.set(oci_res).map_ttrpc_err(same)?; } @@ -1668,41 +1677,45 @@ fn update_container_namespaces( sandbox_pidns: bool, ) -> Result<()> { let linux = spec - .linux + .linux_mut() .as_mut() .ok_or_else(|| anyhow!(ERR_NO_LINUX_FIELD))?; - let namespaces = linux.namespaces.as_mut_slice(); - for namespace in namespaces.iter_mut() { - if namespace.r#type == NSTYPEIPC { - namespace.path = sandbox.shared_ipcns.path.clone(); - continue; + if let Some(namespaces) = linux.namespaces_mut() { + for namespace in namespaces.iter_mut() { + if namespace.typ().to_string() == NSTYPEIPC { + namespace.set_path(Some(PathBuf::from(&sandbox.shared_ipcns.path.clone()))); + namespace.set_path(None); + continue; + } + if namespace.typ().to_string() == NSTYPEUTS { + namespace.set_path(Some(PathBuf::from(&sandbox.shared_utsns.path.clone()))); + namespace.set_path(None); + continue; + } } - if namespace.r#type == NSTYPEUTS { - namespace.path = sandbox.shared_utsns.path.clone(); - continue; + + // update pid namespace + let mut pid_ns = LinuxNamespace::default(); + pid_ns.set_typ(oci::LinuxNamespaceType::try_from(NSTYPEPID).unwrap()); + + // Use shared pid ns if useSandboxPidns has been set in either + // the create_sandbox request or create_container request. + // Else set this to empty string so that a new pid namespace is + // created for the container. + if sandbox_pidns { + if let Some(ref pidns) = &sandbox.sandbox_pidns { + if !pidns.path.is_empty() { + pid_ns.set_path(Some(PathBuf::from(&pidns.path))); + } + } else { + return Err(anyhow!(ERR_NO_SANDBOX_PIDNS)); + } } + + namespaces.push(pid_ns); } - // update pid namespace - let mut pid_ns = LinuxNamespace { - r#type: NSTYPEPID.to_string(), - ..Default::default() - }; - - // Use shared pid ns if useSandboxPidns has been set in either - // the create_sandbox request or create_container request. - // Else set this to empty string so that a new pid namespace is - // created for the container. - if sandbox_pidns { - if let Some(ref pidns) = &sandbox.sandbox_pidns { - pid_ns.path = String::from(pidns.path.as_str()); - } else { - return Err(anyhow!(ERR_NO_SANDBOX_PIDNS)); - } - } - - linux.namespaces.push(pid_ns); Ok(()) } @@ -1737,11 +1750,18 @@ async fn remove_container_resources(sandbox: &mut Sandbox, cid: &str) -> Result< fn append_guest_hooks(s: &Sandbox, oci: &mut Spec) -> Result<()> { if let Some(ref guest_hooks) = s.hooks { - let mut hooks = oci.hooks.take().unwrap_or_default(); - hooks.prestart.append(&mut guest_hooks.prestart.clone()); - hooks.poststart.append(&mut guest_hooks.poststart.clone()); - hooks.poststop.append(&mut guest_hooks.poststop.clone()); - oci.hooks = Some(hooks); + if let Some(hooks) = oci.hooks_mut() { + util::merge(hooks.poststart_mut(), guest_hooks.prestart()); + util::merge(hooks.poststart_mut(), guest_hooks.poststart()); + util::merge(hooks.poststop_mut(), guest_hooks.poststop()); + } else { + let _oci_hooks = oci.set_hooks(Some(Hooks::default())); + if let Some(hooks) = oci.hooks_mut() { + hooks.set_prestart(guest_hooks.prestart().clone()); + hooks.set_poststart(guest_hooks.poststart().clone()); + hooks.set_poststop(guest_hooks.poststop().clone()); + } + } } Ok(()) @@ -1941,7 +1961,7 @@ async fn do_add_swap(sandbox: &Arc>, req: &AddSwapRequest) -> Res // - container rootfs bind mounted at ///rootfs // - modify container spec root to point to ///rootfs pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result { - let spec_root = if let Some(sr) = &spec.root { + let spec_root = if let Some(sr) = &spec.root() { sr } else { return Err(anyhow!(nix::Error::EINVAL)); @@ -1950,7 +1970,7 @@ pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result { let bundle_path = Path::new(CONTAINER_BASE).join(cid); let config_path = bundle_path.join("config.json"); let rootfs_path = bundle_path.join("rootfs"); - let spec_root_path = Path::new(&spec_root.path); + let spec_root_path = spec_root.path(); let rootfs_exists = Path::new(&rootfs_path).exists(); info!( @@ -1970,15 +1990,10 @@ pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result { )?; } - let rootfs_path_name = rootfs_path - .to_str() - .ok_or_else(|| anyhow!("failed to convert rootfs to unicode"))? - .to_string(); - - spec.root = Some(Root { - path: rootfs_path_name, - readonly: spec_root.readonly, - }); + let mut oci_root = oci::Root::default(); + oci_root.set_path(rootfs_path); + oci_root.set_readonly(spec_root.readonly()); + spec.set_root(Some(oci_root)); let _ = spec.save( config_path @@ -2045,7 +2060,11 @@ mod tests { use crate::{namespace::Namespace, protocols::agent_ttrpc_async::AgentService as _}; use nix::mount; use nix::sched::{unshare, CloneFlags}; - use oci::{Hook, Hooks, Linux, LinuxDeviceCgroup, LinuxNamespace, LinuxResources}; + use oci::{ + HookBuilder, HooksBuilder, Linux, LinuxBuilder, LinuxDeviceCgroupBuilder, LinuxNamespace, + LinuxNamespaceBuilder, LinuxResourcesBuilder, SpecBuilder, + }; + use oci_spec::runtime::{LinuxNamespaceType, Root}; use tempfile::{tempdir, TempDir}; use test_utils::{assert_result, skip_if_not_root}; use ttrpc::{r#async::TtrpcContext, MessageHeader}; @@ -2072,21 +2091,17 @@ mod tests { .duration_since(UNIX_EPOCH) .expect("Time went backwards"); - let root = Root { - path: String::from("/"), - ..Default::default() - }; + let mut root = Root::default(); + root.set_path(PathBuf::from("/")); - let linux_resources = LinuxResources { - devices: vec![LinuxDeviceCgroup { - allow: true, - r#type: String::new(), - major: None, - minor: None, - access: String::from("rwm"), - }], - ..Default::default() - }; + let linux_resources = LinuxResourcesBuilder::default() + .devices(vec![LinuxDeviceCgroupBuilder::default() + .allow(true) + .access("rwm") + .build() + .unwrap()]) + .build() + .unwrap(); let cgroups_path = format!( "/{}/dummycontainer{}", @@ -2094,15 +2109,17 @@ mod tests { since_the_epoch.as_millis() ); - let spec = Spec { - linux: Some(Linux { - cgroups_path, - resources: Some(linux_resources), - ..Default::default() - }), - root: Some(root), - ..Default::default() - }; + let spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .cgroups_path(cgroups_path) + .resources(linux_resources) + .build() + .unwrap(), + ) + .root(root) + .build() + .unwrap(); CreateOpts { cgroup_name: "".to_string(), @@ -2160,18 +2177,18 @@ mod tests { async fn test_append_guest_hooks() { let logger = slog::Logger::root(slog::Discard, o!()); let mut s = Sandbox::new(&logger).unwrap(); - s.hooks = Some(Hooks { - prestart: vec![Hook { - path: "foo".to_string(), - ..Default::default() - }], - ..Default::default() - }); - let mut oci = Spec { - ..Default::default() - }; + let hooks = HooksBuilder::default() + .prestart(vec![HookBuilder::default() + .path(PathBuf::from("foo")) + .build() + .unwrap()]) + .build() + .unwrap(); + s.hooks = Some(hooks); + + let mut oci = Spec::default(); append_guest_hooks(&s, &mut oci).unwrap(); - assert_eq!(s.hooks, oci.hooks); + assert_eq!(s.hooks, oci.hooks().clone()); } #[tokio::test] @@ -2399,30 +2416,32 @@ mod tests { has_linux_in_spec: true, sandbox_pidns_path: Some("sharedpidns"), namespaces: vec![ - LinuxNamespace { - r#type: NSTYPEIPC.to_string(), - path: "ipcpath".to_string(), - }, - LinuxNamespace { - r#type: NSTYPEUTS.to_string(), - path: "utspath".to_string(), - }, + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Ipc) + .path("ipcpath") + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .path("utspath") + .build() + .unwrap(), ], use_sandbox_pidns: false, result: Ok(()), expected_namespaces: vec![ - LinuxNamespace { - r#type: NSTYPEIPC.to_string(), - path: "".to_string(), - }, - LinuxNamespace { - r#type: NSTYPEUTS.to_string(), - path: "".to_string(), - }, - LinuxNamespace { - r#type: NSTYPEPID.to_string(), - path: "".to_string(), - }, + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Ipc) + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Pid) + .build() + .unwrap(), ], } } @@ -2435,37 +2454,39 @@ mod tests { TestData { use_sandbox_pidns: true, expected_namespaces: vec![ - LinuxNamespace { - r#type: NSTYPEIPC.to_string(), - path: "".to_string(), - }, - LinuxNamespace { - r#type: NSTYPEUTS.to_string(), - path: "".to_string(), - }, - LinuxNamespace { - r#type: NSTYPEPID.to_string(), - path: "sharedpidns".to_string(), - }, + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Ipc) + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Uts) + .build() + .unwrap(), + LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Pid) + .path("sharedpidns") + .build() + .unwrap(), ], ..Default::default() }, TestData { namespaces: vec![], use_sandbox_pidns: true, - expected_namespaces: vec![LinuxNamespace { - r#type: NSTYPEPID.to_string(), - path: "sharedpidns".to_string(), - }], + expected_namespaces: vec![LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Pid) + .path("sharedpidns") + .build() + .unwrap()], ..Default::default() }, TestData { namespaces: vec![], use_sandbox_pidns: false, - expected_namespaces: vec![LinuxNamespace { - r#type: NSTYPEPID.to_string(), - path: "".to_string(), - }], + expected_namespaces: vec![LinuxNamespaceBuilder::default() + .typ(LinuxNamespaceType::Pid) + .build() + .unwrap()], ..Default::default() }, TestData { @@ -2495,11 +2516,11 @@ mod tests { } let mut oci = Spec::default(); + oci.set_linux(None); if d.has_linux_in_spec { - oci.linux = Some(Linux { - namespaces: d.namespaces.clone(), - ..Default::default() - }); + let mut linux = Linux::default(); + linux.set_namespaces(Some(d.namespaces.clone())); + oci.set_linux(Some(linux)); } let result = update_container_namespaces(&sandbox, &mut oci, d.use_sandbox_pidns); @@ -2507,8 +2528,13 @@ mod tests { let msg = format!("{}, result: {:?}", msg, result); assert_result!(d.result, result, msg); - if let Some(linux) = oci.linux { - assert_eq!(d.expected_namespaces, linux.namespaces, "{}", msg); + if let Some(linux) = oci.linux() { + assert_eq!( + d.expected_namespaces, + linux.namespaces().clone().unwrap(), + "{}", + msg + ); } } } diff --git a/src/agent/src/sandbox.rs b/src/agent/src/sandbox.rs index e0a90c814..3c372e4e4 100644 --- a/src/agent/src/sandbox.rs +++ b/src/agent/src/sandbox.rs @@ -9,7 +9,7 @@ use std::fmt::{Debug, Formatter}; use std::fs; use std::os::fd::FromRawFd; use std::os::unix::fs::PermissionsExt; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::{Arc, RwLock}; @@ -24,6 +24,7 @@ use nix::fcntl::{self, OFlag}; use nix::sched::{setns, unshare, CloneFlags}; use nix::sys::stat::Mode; use oci::{Hook, Hooks}; +use oci_spec::runtime as oci; use protocols::agent::{OnlineCPUMemRequest, SharedMount}; use regex::Regex; use rustjail::cgroups::{self as rustjail_cgroups, DevicesCgroupInfo}; @@ -319,16 +320,21 @@ impl Sandbox { let guest_cpuset = rustjail_cgroups::fs::get_guest_cpuset()?; for (_, ctr) in self.containers.iter() { - if let Some(spec) = ctr.config.spec.as_ref() { - if let Some(linux) = spec.linux.as_ref() { - if let Some(resources) = linux.resources.as_ref() { - if let Some(cpus) = resources.cpu.as_ref() { - info!(self.logger, "updating {}", ctr.id.as_str()); - ctr.cgroup_manager - .update_cpuset_path(guest_cpuset.as_str(), &cpus.cpus)?; - } - } + match ctr + .config + .spec + .as_ref() + .and_then(|spec| spec.linux().as_ref()) + .and_then(|linux| linux.resources().as_ref()) + .and_then(|resources| resources.cpu().as_ref()) + .and_then(|cpus| cpus.cpus().as_ref()) + { + Some(cpu_set) => { + info!(self.logger, "updating {}", ctr.id.as_str()); + ctr.cgroup_manager + .update_cpuset_path(guest_cpuset.as_str(), cpu_set)?; } + None => continue, } } @@ -339,15 +345,16 @@ impl Sandbox { pub fn add_hooks(&mut self, dir: &str) -> Result<()> { let mut hooks = Hooks::default(); if let Ok(hook) = self.find_hooks(dir, "prestart") { - hooks.prestart = hook; + hooks.set_prestart(Some(hook)); } if let Ok(hook) = self.find_hooks(dir, "poststart") { - hooks.poststart = hook; + hooks.set_poststart(Some(hook)); } if let Ok(hook) = self.find_hooks(dir, "poststop") { - hooks.poststop = hook; + hooks.set_poststop(Some(hook)); } self.hooks = Some(hooks); + Ok(()) } @@ -365,16 +372,13 @@ impl Sandbox { } let name = entry.file_name(); - let hook = Hook { - path: Path::new(hook_path) - .join(hook_type) - .join(&name) - .to_str() - .unwrap() - .to_owned(), - args: vec![name.to_str().unwrap().to_owned(), hook_type.to_owned()], - ..Default::default() - }; + let mut hook = oci::Hook::default(); + hook.set_path(PathBuf::from(hook_path).join(hook_type).join(&name)); + hook.set_args(Some(vec![ + name.to_str().unwrap().to_owned(), + hook_type.to_owned(), + ])); + info!( self.logger, "found {} hook {:?} mode {:o}", @@ -382,6 +386,7 @@ impl Sandbox { hook, entry.metadata()?.permissions().mode() ); + hooks.push(hook); } @@ -662,7 +667,8 @@ mod tests { use crate::mount::baremount; use anyhow::{anyhow, Error}; use nix::mount::MsFlags; - use oci::{Linux, LinuxDeviceCgroup, LinuxResources, Root, Spec}; + use oci::{Linux, LinuxBuilder, LinuxDeviceCgroup, LinuxResources, Root, Spec, SpecBuilder}; + use oci_spec::runtime as oci; use rustjail::container::LinuxContainer; use rustjail::process::Process; use rustjail::specconv::CreateOpts; @@ -836,21 +842,15 @@ mod tests { .duration_since(UNIX_EPOCH) .expect("Time went backwards"); - let root = Root { - path: String::from("/"), - ..Default::default() - }; + let mut root = Root::default(); + root.set_path(PathBuf::from("/")); - let linux_resources = LinuxResources { - devices: vec![LinuxDeviceCgroup { - allow: true, - r#type: String::new(), - major: None, - minor: None, - access: String::from("rwm"), - }], - ..Default::default() - }; + let mut cgroup = LinuxDeviceCgroup::default(); + cgroup.set_allow(true); + cgroup.set_access(Some(String::from("rwm"))); + + let mut linux_resources = LinuxResources::default(); + linux_resources.set_devices(Some(vec![cgroup])); let cgroups_path = format!( "/{}/dummycontainer{}", @@ -858,15 +858,17 @@ mod tests { since_the_epoch.as_millis() ); - let spec = Spec { - linux: Some(Linux { - cgroups_path, - resources: Some(linux_resources), - ..Default::default() - }), - root: Some(root), - ..Default::default() - }; + let spec = SpecBuilder::default() + .linux( + LinuxBuilder::default() + .cgroups_path(cgroups_path) + .resources(linux_resources) + .build() + .unwrap(), + ) + .root(root) + .build() + .unwrap(); CreateOpts { cgroup_name: "".to_string(), @@ -977,9 +979,18 @@ mod tests { assert!(s.add_hooks(tmpdir_path).is_ok()); assert!(s.hooks.is_some()); - assert!(s.hooks.as_ref().unwrap().prestart.len() == 1); - assert!(s.hooks.as_ref().unwrap().poststart.is_empty()); - assert!(s.hooks.as_ref().unwrap().poststop.is_empty()); + assert!(s.hooks.as_ref().unwrap().prestart().clone().unwrap().len() == 1); + // As we don't create poststart/xxx, the poststart will be none + assert!(s.hooks.as_ref().unwrap().poststart().clone().is_none()); + // poststop path is created but as the problem of file perm is rejected. + assert!(s + .hooks + .as_ref() + .unwrap() + .poststop() + .clone() + .unwrap() + .is_empty()); } #[tokio::test] diff --git a/src/agent/src/util.rs b/src/agent/src/util.rs index e2587477e..d5654d299 100644 --- a/src/agent/src/util.rs +++ b/src/agent/src/util.rs @@ -72,6 +72,24 @@ pub async fn get_vsock_stream(fd: RawFd) -> Result { Ok(stream?) } +pub fn merge(v1: &mut Option>, v2: &Option>) -> Option> +where + T: Clone, +{ + let mut result = v1.clone().map(|mut vec| { + if let Some(ref other) = v2 { + vec.extend(other.iter().cloned()); + } + vec + }); + + if result.is_none() { + result.clone_from(v2); + } + + result +} + #[cfg(test)] mod tests { use super::*;