agent: Align agent OCI spec with oci-spec-rs

Fixes #9766

Signed-off-by: Alex Lyn <alex.lyn@antgroup.com>
This commit is contained in:
Alex Lyn 2024-07-16 16:41:23 +08:00
parent 882385858d
commit b56313472b
26 changed files with 1741 additions and 2401 deletions

217
src/agent/Cargo.lock generated
View File

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

View File

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

View File

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

View File

@ -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<HashSet<LinuxCapability>>) -> CapsHashSet {
let mut r = CapsHashSet::new();
let binding: HashSet<LinuxCapability> = 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"));

View File

@ -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::<HugeTlbController>();
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<DeviceResource> {
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<DevicePermissions> = vec![];
for p in d.access.chars().collect::<Vec<char>>() {
for p in d
.access()
.as_ref()
.unwrap_or(&"".to_owned())
.chars()
.collect::<Vec<char>>()
{
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<Devic
}
Some(DeviceResource {
allow: d.allow,
allow: d.allow(),
devtype: dev_type,
major: d.major.unwrap_or(0),
minor: d.minor.unwrap_or(0),
major: d.major().unwrap_or(0),
minor: d.minor().unwrap_or(0),
access: permissions,
})
}
@ -592,58 +622,64 @@ lazy_static! {
pub static ref DEFAULT_ALLOWED_DEVICES: Vec<LinuxDeviceCgroup> = {
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/<N>
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<DeviceResource> {
/// Convert LinuxDevice to DeviceResource.
fn linux_device_to_device_resource(d: &LinuxDevice) -> Option<DeviceResource> {
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<DeviceResource> {
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<String>,
}
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(),
);

View File

@ -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;

View File

@ -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')
}

View File

@ -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;

View File

@ -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)));

View File

@ -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())));
}

View File

@ -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!(

View File

@ -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());

View File

@ -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(

View File

@ -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<oci::LinuxNamespaceType, &'static str> = {
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<LinuxDevice> = {
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<Gid> = guser
.additional_gids
if let Some(additional_gids) = guser.additional_gids() {
let gids: Vec<Gid> = 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<PidNs> {
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<PidNs> {
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<LinuxNamespace> {
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::<Vec<_>>()
.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<dyn Manager + Send + Sync> = 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]

File diff suppressed because it is too large Load Diff

View File

@ -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::<libc::statfs>::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());
}
}

View File

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

View File

@ -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<ScmpFilterAttr> {
match flag {
"SECCOMP_FILTER_FLAG_TSYNC" => Ok(ScmpFilterAttr::CtlTsync),
@ -22,19 +24,15 @@ fn get_rule_conditions(args: &[LinuxSeccompArg]) -> Result<Vec<ScmpArgCompare>>
let mut conditions: Vec<ScmpArgCompare> = 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<Vec<ScmpArgCompare>>
pub fn get_unknown_syscalls(scmp: &LinuxSeccomp) -> Option<Vec<String>> {
let mut unknown_syscalls: Vec<String> = 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<Vec<String>> {
// 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::<oci::Arch>().unwrap(),
"SCMP_ARCH_X32".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_X86_64".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_AARCH64".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_ARM".parse::<oci::Arch>().unwrap(),
"SCMP_ARCH_PPC64LE".parse::<oci::Arch>().unwrap(),
];
} else {
// For big-endian architectures
arch = vec!["SCMP_ARCH_S390X".to_string()];
arch = vec!["SCMP_ARCH_S390X".parse::<oci::Arch>().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);

View File

@ -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 {

View File

@ -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::<u32>()
.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();
}
}

View File

@ -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<T: Into<DevUpdate>> From<T> 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<String> =
&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::<oci::LinuxDeviceType>()
.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]

View File

@ -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)?;

View File

@ -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;

View File

@ -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<Mutex<Sandbox>>, req: &AddSwapRequest) -> Res
// - container rootfs bind mounted at /<CONTAINER_BASE>/<cid>/rootfs
// - modify container spec root to point to /<CONTAINER_BASE>/<cid>/rootfs
pub fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> {
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<PathBuf> {
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<PathBuf> {
)?;
}
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
);
}
}
}

View File

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

View File

@ -72,6 +72,24 @@ pub async fn get_vsock_stream(fd: RawFd) -> Result<VsockStream> {
Ok(stream?)
}
pub fn merge<T>(v1: &mut Option<Vec<T>>, v2: &Option<Vec<T>>) -> Option<Vec<T>>
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::*;