mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-06 20:09:44 +00:00
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:
parent
882385858d
commit
b56313472b
217
src/agent/Cargo.lock
generated
217
src/agent/Cargo.lock
generated
@ -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]]
|
||||
|
@ -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"
|
||||
|
@ -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"]
|
||||
|
@ -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"));
|
||||
|
||||
|
@ -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(),
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -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')
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)));
|
||||
|
@ -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())));
|
||||
}
|
||||
|
||||
|
@ -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!(
|
||||
|
@ -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());
|
||||
|
@ -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(
|
||||
|
@ -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
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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)?;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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::*;
|
||||
|
Loading…
Reference in New Issue
Block a user