diff --git a/src/tools/genpolicy/Cargo.lock b/src/tools/genpolicy/Cargo.lock new file mode 100644 index 0000000000..f3c1f4bd08 --- /dev/null +++ b/src/tools/genpolicy/Cargo.lock @@ -0,0 +1,2196 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" +dependencies = [ + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "docker_credential" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f2821ba7f89de240e70f4af347ba5260512d0799a53556c10750805bad7c7fc" +dependencies = [ + "base64 0.10.1", + "serde", + "serde_json", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", +] + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "libz-ng-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "genpolicy" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "base64 0.21.0", + "clap", + "docker_credential", + "env_logger", + "flate2", + "generic-array", + "log", + "oci", + "oci-distribution", + "openssl", + "protobuf 3.3.0", + "protocols", + "serde", + "serde-transcode", + "serde_ignored", + "serde_json", + "serde_yaml", + "sha2", + "tarindex", + "tempfile", + "tokio", + "zerocopy", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-auth" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5430cacd7a1f9a02fbeb350dfc81a0e5ed42d81f3398cb0ba184017f85bdcfbc" +dependencies = [ + "memchr", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix 0.36.8", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jwt" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +dependencies = [ + "base64 0.13.1", + "crypto-common", + "digest", + "hmac", + "serde", + "serde_json", + "sha2", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "libz-ng-sys" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468756f34903b582fe7154dc1ffdebd89d0562c4a43b53c621bb0f1b1043ccb" +dependencies = [ + "cmake", + "libc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.45.0", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "oci" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "oci-distribution" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac5b780ce1bd6c3c2ff72a3013f4b2d56d53ae03b20d424e99d2f6556125138" +dependencies = [ + "futures", + "futures-util", + "http", + "http-auth", + "jwt", + "lazy_static", + "olpc-cjson", + "regex", + "reqwest", + "serde", + "serde_json", + "sha2", + "thiserror", + "tokio", + "tokio-util", + "tracing", + "unicase", +] + +[[package]] +name = "olpc-cjson" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d637c9c15b639ccff597da8f4fa968300651ad2f1e968aefc3b4927a6fb2027a" +dependencies = [ + "serde", + "serde_json", + "unicode-normalization", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "openssl" +version = "0.10.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-src" +version = "111.25.1+1.1.1t" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef9a9cc6ea7d9d5e7c4a913dc4b48d0e359eddf01af1dfec96ba7064b4aba10" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +dependencies = [ + "bytes", + "heck 0.3.3", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes", + "prost", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "protobuf" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b65f4a8ec18723a734e5dc09c173e0abf9690432da5340285d536edcb4dac190" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-codegen" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +dependencies = [ + "protobuf 2.28.0", +] + +[[package]] +name = "protobuf-codegen" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e85514a216b1c73111d9032e26cc7a5ecb1bb3d4d9539e91fb72a4395060f78" +dependencies = [ + "anyhow", + "once_cell", + "protobuf 3.3.0", + "protobuf-parse", + "regex", + "tempfile", + "thiserror", +] + +[[package]] +name = "protobuf-parse" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d6fbd6697c9e531873e81cec565a85e226b99a0f10e1acc079be057fe2fcba" +dependencies = [ + "anyhow", + "indexmap", + "log", + "protobuf 3.3.0", + "protobuf-support", + "tempfile", + "thiserror", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6872f4d4f4b98303239a2b5838f5bbbb77b01ffc892d627957f37a22d7cfe69c" +dependencies = [ + "thiserror", +] + +[[package]] +name = "protocols" +version = "0.1.0" +dependencies = [ + "oci", + "protobuf 3.3.0", + "serde", + "serde_json", + "ttrpc", + "ttrpc-codegen", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64 0.21.0", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "winreg", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags 1.3.2", + "errno 0.2.8", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +dependencies = [ + "bitflags 1.3.2", + "errno 0.3.1", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.7", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.38.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +dependencies = [ + "bitflags 2.4.1", + "errno 0.3.1", + "libc", + "linux-raw-sys 0.4.10", + "windows-sys 0.48.0", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-transcode" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "590c0e25c2a5bb6e85bf5c1bce768ceb86b316e7a01bdf07d2cb4ec2271990e2" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "serde_ignored" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94eb4a4087ba8bdf14a9208ac44fddbf55c01a6195f7edfc511ddaff6cae45a6" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tarfs-defs" +version = "0.1.0" +source = "git+https://github.com/kata-containers/tardev-snapshotter?rev=06183a5#06183a5e2a83c3261740f4f0f6ce4aa16b14e436" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "tarindex" +version = "0.1.0" +source = "git+https://github.com/kata-containers/tardev-snapshotter?rev=06183a5#06183a5e2a83c3261740f4f0f6ce4aa16b14e436" +dependencies = [ + "tar", + "tarfs-defs", + "zerocopy", +] + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix 0.37.3", + "windows-sys 0.45.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.4", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "ttrpc" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35f22a2964bea14afee161665bb260b83cb48e665e0260ca06ec0e775c8b06c" +dependencies = [ + "byteorder", + "libc", + "log", + "nix", + "protobuf 3.3.0", + "protobuf-codegen 3.3.0", + "thiserror", +] + +[[package]] +name = "ttrpc-codegen" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7f7631d7a9ebed715a47cd4cb6072cbc7ae1d4ec01598971bbec0024340c2" +dependencies = [ + "protobuf 2.28.0", + "protobuf-codegen 3.3.0", + "protobuf-support", + "ttrpc-compiler", +] + +[[package]] +name = "ttrpc-compiler" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0672eb06e5663ad190c7b93b2973f5d730259859b62e4e3381301a12a7441107" +dependencies = [ + "derive-new", + "prost", + "prost-build", + "prost-types", + "protobuf 2.28.0", + "protobuf-codegen 2.28.0", + "tempfile", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "url" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "wasm-streams" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bbae3363c08332cadccd13b67db371814cd214c2524020932f0804b8cf7c078" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.19", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zerocopy" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/src/tools/genpolicy/Cargo.toml b/src/tools/genpolicy/Cargo.toml new file mode 100644 index 0000000000..cc05460d07 --- /dev/null +++ b/src/tools/genpolicy/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "genpolicy" +version = "0.1.0" +authors = ["The Confidential Containers community https://github.com/confidential-containers"] +edition = "2021" + +[dependencies] +# Logging. +env_logger = "0.10.0" +log = "0.4.17" + +# Command line parsing. +clap = { version = "4.1.8", features = ["derive"] } + +# YAML file serialization/deserialization. +base64 = "0.21.0" +serde = { version = "1.0.159", features = ["derive"] } + +# Newer serde_yaml versions are using unsafe-libyaml instead of yaml-rust, +# and incorrectly change on serialization: +# +# value: "yes" +# +# to: +# +# value: yes +# +# In YAML, the value yes without quotes is reserved for boolean, +# and confuses kubectl, that expects a string value. +serde_yaml = "0.8" + +# Container repository. +anyhow = "1.0.32" +async-trait = "0.1.68" +docker_credential = "1.2.0" +flate2 = { version = "1.0.26", features = ["zlib-ng"], default-features = false } +oci-distribution = { version = "0.9.4" } +openssl = { version = "0.10.54", features = ["vendored"] } +serde_ignored = "0.1.7" +serde_json = "1.0.39" +serde-transcode = "1.1.1" +tokio = {version = "1.33.0", features = ["rt-multi-thread"]} + +# OCI container specs. +oci = { path = "../../libs/oci" } + +# Kata Agent prototol. +protocols = { path = "../../libs/protocols", features = ["with-serde"] } +protobuf = "3.2.0" + +# dm-verity root hash support +generic-array = "0.14.6" +sha2 = "0.10.6" +tarindex = { git = "https://github.com/kata-containers/tardev-snapshotter", rev = "06183a5" } +tempfile = "3.5.0" +zerocopy = "0.6.1" diff --git a/src/tools/genpolicy/Makefile b/src/tools/genpolicy/Makefile new file mode 100644 index 0000000000..fe56fa0982 --- /dev/null +++ b/src/tools/genpolicy/Makefile @@ -0,0 +1,40 @@ +# Copyright (c) 2020 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +include ../../../utils.mk + +ifeq ($(ARCH), ppc64le) + override ARCH = powerpc64le + endif + +.DEFAULT_GOAL := default +default: build + +build: + @RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo build --target $(TRIPLE) --$(BUILD_TYPE) + +static-checks-build: + @echo "INFO: static-checks-build do nothing.." + +clean: + cargo clean + +vendor: + cargo vendor + +test: + +install: + @RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo install --locked --target $(TRIPLE) --path . + +check: standard_rust_check + +.PHONY: \ + build \ + check \ + clean \ + install \ + test \ + vendor diff --git a/src/tools/genpolicy/README.md b/src/tools/genpolicy/README.md new file mode 100644 index 0000000000..146511c703 --- /dev/null +++ b/src/tools/genpolicy/README.md @@ -0,0 +1,29 @@ +# Agent Policy generation tool + +The Kata Containers policy generation tool (`genpolicy`): + +1. Reads user's Kubernetes YAML file. + +1. Infers user's intentions based on the contents of that file. + +1. Generates a Kata Containers Agent (`kata-agent`) policy file +corresponding to the input YAML, using the Rego/Open Policy Agent +format. + +1. Appends the policy as an annotation to user's YAML file. + +When the user deploys that YAML file, the Kata Agent uses the attached +policy to reject possible Agent API calls that are not consistent with +the policy. + +Example: + +```sh +$ genpolicy -y samples/pod-one-container.yaml +``` + +For a usage statement, run: + +```sh +$ genpolicy --help +``` diff --git a/src/tools/genpolicy/genpolicy-settings.json b/src/tools/genpolicy/genpolicy-settings.json new file mode 100644 index 0000000000..8b438cc7cf --- /dev/null +++ b/src/tools/genpolicy/genpolicy-settings.json @@ -0,0 +1,291 @@ +{ + "pause_container": { + "Root": { + "Path": "$(cpath)/$(bundle-id)", + "Readonly": true + }, + "Mounts": [ + { + "destination": "/dev/shm", + "type_": "bind", + "source": "/run/kata-containers/sandbox/shm", + "options": [ + "rbind" + ] + }, + { + "destination": "/etc/resolv.conf", + "type_": "bind", + "options": [ + "rbind", + "ro", + "nosuid", + "nodev", + "noexec" + ] + } + ], + "Annotations": { + "io.kubernetes.cri.container-type": "sandbox", + "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$", + "io.kubernetes.cri.sandbox-log-directory": "^/var/log/pods/$(sandbox-namespace)_$(sandbox-name)_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + "io.katacontainers.pkg.oci.container_type": "pod_sandbox", + "io.kubernetes.cri.sandbox-namespace": "default", + "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)" + }, + "Process": { + "Args": [ + "/pause" + ] + }, + "Linux": { + "MaskedPaths": [ + "/proc/acpi", + "/proc/asound", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi" + ], + "ReadonlyPaths": [ + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger" + ] + } + }, + "other_container": { + "Root": { + "Path": "$(cpath)/$(bundle-id)" + }, + "Mounts": [ + { + "destination": "/etc/hosts", + "type_": "bind", + "options": [ + "rbind", + "rprivate", + "rw" + ] + }, + { + "destination": "/dev/termination-log", + "type_": "bind", + "options": [ + "rbind", + "rprivate", + "rw" + ] + }, + { + "destination": "/etc/hostname", + "type_": "bind", + "options": [ + "rbind", + "rprivate" + ] + }, + { + "destination": "/etc/resolv.conf", + "type_": "bind", + "options": [ + "rbind", + "rprivate" + ] + }, + { + "destination": "/dev/shm", + "type_": "bind", + "source": "/run/kata-containers/sandbox/shm", + "options": [ + "rbind" + ] + }, + { + "destination": "/var/run/secrets/kubernetes.io/serviceaccount", + "type_": "bind", + "options": [ + "rbind", + "rprivate", + "ro" + ] + }, + { + "destination": "/var/run/secrets/azure/tokens", + "source": "$(sfprefix)tokens$", + "type_": "bind", + "options": [ + "rbind", + "rprivate", + "ro" + ] + } + ], + "Annotations": { + "io.katacontainers.pkg.oci.bundle_path": "/run/containerd/io.containerd.runtime.v2.task/k8s.io/$(bundle-id)", + "io.kubernetes.cri.sandbox-id": "^[a-z0-9]{64}$", + "io.katacontainers.pkg.oci.container_type": "pod_container", + "io.kubernetes.cri.container-type": "container" + } + }, + "volumes": { + "emptyDir": { + "mount_type": "local", + "mount_source": "^$(cpath)/$(sandbox-id)/local/", + "mount_point": "^$(cpath)/$(sandbox-id)/local/", + "driver": "local", + "source": "local", + "fstype": "local", + "options": [ + "mode=0777" + ] + }, + "emptyDir_memory": { + "mount_type": "bind", + "mount_source": "^/run/kata-containers/sandbox/ephemeral/", + "mount_point": "^/run/kata-containers/sandbox/ephemeral/", + "driver": "ephemeral", + "source": "tmpfs", + "fstype": "tmpfs", + "options": [] + }, + "configMap": { + "mount_type": "bind", + "mount_source": "$(sfprefix)", + "mount_point": "^$(cpath)/watchable/$(bundle-id)-[a-z0-9]{16}-", + "driver": "watchable-bind", + "fstype": "bind", + "options": [ + "rbind", + "rprivate", + "ro" + ] + }, + "confidential_configMap": { + "mount_type": "bind", + "mount_source": "$(sfprefix)", + "mount_point": "$(sfprefix)", + "driver": "local", + "fstype": "bind", + "options": [ + "rbind", + "rprivate", + "ro" + ] + } + }, + "mount_destinations": [ + "/sys/fs/cgroup", + "/etc/hosts", + "/dev/termination-log", + "/etc/hostname", + "/etc/resolv.conf", + "/dev/shm", + "/var/run/secrets/kubernetes.io/serviceaccount", + "/var/run/secrets/azure/tokens" + ], + "common": { + "cpath": "/run/kata-containers/shared/containers", + "sfprefix": "^$(cpath)/$(bundle-id)-[a-z0-9]{16}-", + "ip_p": "[0-9]{1,5}", + "ipv4_a": "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])", + "svc_name": "[A-Z_\\.\\-]+", + "dns_label": "[a-zA-Z0-9_\\.\\-]+", + "default_caps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE" + ], + "privileged_caps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_DAC_READ_SEARCH", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETPCAP", + "CAP_LINUX_IMMUTABLE", + "CAP_NET_BIND_SERVICE", + "CAP_NET_BROADCAST", + "CAP_NET_ADMIN", + "CAP_NET_RAW", + "CAP_IPC_LOCK", + "CAP_IPC_OWNER", + "CAP_SYS_MODULE", + "CAP_SYS_RAWIO", + "CAP_SYS_CHROOT", + "CAP_SYS_PTRACE", + "CAP_SYS_PACCT", + "CAP_SYS_ADMIN", + "CAP_SYS_BOOT", + "CAP_SYS_NICE", + "CAP_SYS_RESOURCE", + "CAP_SYS_TIME", + "CAP_SYS_TTY_CONFIG", + "CAP_MKNOD", + "CAP_LEASE", + "CAP_AUDIT_WRITE", + "CAP_AUDIT_CONTROL", + "CAP_SETFCAP", + "CAP_MAC_OVERRIDE", + "CAP_MAC_ADMIN", + "CAP_SYSLOG", + "CAP_WAKE_ALARM", + "CAP_BLOCK_SUSPEND", + "CAP_AUDIT_READ", + "CAP_PERFMON", + "CAP_BPF", + "CAP_CHECKPOINT_RESTORE" + ] + }, + "kata_config": { + "confidential_guest": true + }, + "request_defaults": { + "CreateContainerRequest": { + "allow_env_regex": [ + "^HOSTNAME=$(dns_label)$", + "^$(svc_name)_PORT_$(ip_p)_TCP=tcp://$(ipv4_a):$(ip_p)$", + "^$(svc_name)_PORT_$(ip_p)_TCP_PROTO=tcp$", + "^$(svc_name)_PORT_$(ip_p)_TCP_PORT=$(ip_p)$", + "^$(svc_name)_PORT_$(ip_p)_TCP_ADDR=$(ipv4_a)$", + "^$(svc_name)_SERVICE_HOST=$(ipv4_a)$", + "^$(svc_name)_SERVICE_PORT=$(ip_p)$", + "^$(svc_name)_SERVICE_PORT_$(dns_label)=$(ip_p)$", + "^$(svc_name)_PORT=tcp://$(ipv4_a):$(ip_p)$", + "^AZURE_CLIENT_ID=[A-Fa-f0-9-]+$", + "^AZURE_TENANT_ID=[A-Fa-f0-9-]+$", + "^AZURE_FEDERATED_TOKEN_FILE=/var/run/secrets/azure/tokens/azure-identity-token$", + "^AZURE_AUTHORITY_HOST=https://login\\.microsoftonline\\.com/$" + ] + }, + "CopyFileRequest": [ + "^$(cpath)/" + ], + "ExecProcessRequest": { + "commands": [], + "regex": [] + }, + "ReadStreamRequest": false, + "WriteStreamRequest": false + } +} \ No newline at end of file diff --git a/src/tools/genpolicy/rules.rego b/src/tools/genpolicy/rules.rego new file mode 100644 index 0000000000..481b70b0d1 --- /dev/null +++ b/src/tools/genpolicy/rules.rego @@ -0,0 +1,1120 @@ +package agent_policy + +import future.keywords.in +import future.keywords.every + +import input + +# Default values, returned by OPA when rules cannot be evaluated to true. +default CopyFileRequest := false +default CreateContainerRequest := false +default CreateSandboxRequest := true +default DestroySandboxRequest := true +default ExecProcessRequest := false +default GetOOMEventRequest := true +default GuestDetailsRequest := true +default OnlineCPUMemRequest := true +default PullImageRequest := true +default ReadStreamRequest := false +default RemoveContainerRequest := true +default RemoveStaleVirtiofsShareMountsRequest := true +default SignalProcessRequest := true +default StartContainerRequest := true +default StatsContainerRequest := true +default TtyWinResizeRequest := true +default UpdateEphemeralMountsRequest := true +default UpdateInterfaceRequest := true +default UpdateRoutesRequest := true +default WaitProcessRequest := true +default WriteStreamRequest := false + +# AllowRequestsFailingPolicy := true configures the Agent to *allow any +# requests causing a policy failure*. This is an unsecure configuration +# but is useful for allowing unsecure pods to start, then connect to +# them and inspect OPA logs for the root cause of a failure. +default AllowRequestsFailingPolicy := false + +CreateContainerRequest { + i_oci := input.OCI + i_storages := input.storages + + some p_container in policy_data.containers + print("======== CreateContainerRequest: trying next policy container") + + p_oci := p_container.OCI + p_storages := p_container.storages + + print("CreateContainerRequest: p Version =", p_oci.Version, "i Version =", i_oci.Version) + p_oci.Version == i_oci.Version + + print("CreateContainerRequest: p Readonly =", p_oci.Root.Readonly, "i Readonly =", i_oci.Root.Readonly) + p_oci.Root.Readonly == i_oci.Root.Readonly + + allow_anno(p_oci, i_oci) + allow_by_anno(p_oci, i_oci, p_storages, i_storages) + allow_linux(p_oci, i_oci) + + print("CreateContainerRequest: true") +} + +# Reject unexpected annotations. +allow_anno(p_oci, i_oci) { + print("allow_anno 1: start") + + not i_oci.Annotations + + print("allow_anno 1: true") +} +allow_anno(p_oci, i_oci) { + print("allow_anno 2: p Annotations =", p_oci.Annotations) + print("allow_anno 2: i Annotations =", i_oci.Annotations) + + i_keys := object.keys(i_oci.Annotations) + print("allow_anno 2: i keys =", i_keys) + + every i_key in i_keys { + allow_anno_key(i_key, p_oci) + } + + print("allow_anno 2: true") +} + +allow_anno_key(i_key, p_oci) { + print("allow_anno_key 1: i key =", i_key) + + startswith(i_key, "io.kubernetes.cri.") + + print("allow_anno_key 1: true") +} +allow_anno_key(i_key, p_oci) { + print("allow_anno_key 2: i key =", i_key) + + some p_key, _ in p_oci.Annotations + p_key == i_key + + print("allow_anno_key 2: true") +} + +# Get the value of the "io.kubernetes.cri.sandbox-name" annotation and +# correlate it with other annotations and process fields. +allow_by_anno(p_oci, i_oci, p_storages, i_storages) { + print("allow_by_anno 1: start") + + s_name := "io.kubernetes.cri.sandbox-name" + + not p_oci.Annotations[s_name] + + i_s_name := i_oci.Annotations[s_name] + print("allow_by_anno 1: i_s_name =", i_s_name) + + allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name) + + print("allow_by_anno 1: true") +} +allow_by_anno(p_oci, i_oci, p_storages, i_storages) { + print("allow_by_anno 2: start") + + s_name := "io.kubernetes.cri.sandbox-name" + + p_s_name := p_oci.Annotations[s_name] + i_s_name := i_oci.Annotations[s_name] + print("allow_by_anno 2: i_s_name =", i_s_name, "p_s_name =", p_s_name) + + allow_sandbox_name(p_s_name, i_s_name) + allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, i_s_name) + + print("allow_by_anno 2: true") +} + +allow_by_sandbox_name(p_oci, i_oci, p_storages, i_storages, s_name) { + print("allow_by_sandbox_name: start") + + s_namespace := "io.kubernetes.cri.sandbox-namespace" + + p_namespace := p_oci.Annotations[s_namespace] + i_namespace := i_oci.Annotations[s_namespace] + print("allow_by_sandbox_name: p_namespace =", p_namespace, "i_namespace =", i_namespace) + p_namespace == i_namespace + + allow_by_container_types(p_oci, i_oci, s_name, p_namespace) + allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages) + allow_process(p_oci, i_oci, s_name) + + print("allow_by_sandbox_name: true") +} + +allow_sandbox_name(p_s_name, i_s_name) { + print("allow_sandbox_name 1: start") + + p_s_name == i_s_name + + print("allow_sandbox_name 1: true") +} +allow_sandbox_name(p_s_name, i_s_name) { + print("allow_sandbox_name 2: start") + + # TODO: should generated names be handled differently? + contains(p_s_name, "$(generated-name)") + + print("allow_sandbox_name 2: true") +} + +# Check that the "io.kubernetes.cri.container-type" and +# "io.katacontainers.pkg.oci.container_type" annotations designate the +# expected type - either a "sandbox" or a "container". Then, validate +# other annotations based on the actual "sandbox" or "container" value +# from the input container. +allow_by_container_types(p_oci, i_oci, s_name, s_namespace) { + print("allow_by_container_types: checking io.kubernetes.cri.container-type") + + c_type := "io.kubernetes.cri.container-type" + + p_cri_type := p_oci.Annotations[c_type] + i_cri_type := i_oci.Annotations[c_type] + print("allow_by_container_types: p_cri_type =", p_cri_type, "i_cri_type =", i_cri_type) + p_cri_type == i_cri_type + + allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) + + print("allow_by_container_types: true") +} + +allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) { + print("allow_by_container_type 1: i_cri_type =", i_cri_type) + i_cri_type == "sandbox" + + i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"] + print("allow_by_container_type 1: i_kata_type =", i_kata_type) + i_kata_type == "pod_sandbox" + + allow_sandbox_container_name(p_oci, i_oci) + allow_sandbox_net_namespace(p_oci, i_oci) + allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace) + + print("allow_by_container_type 1: true") +} + +allow_by_container_type(i_cri_type, p_oci, i_oci, s_name, s_namespace) { + print("allow_by_container_type 2: i_cri_type =", i_cri_type) + i_cri_type == "container" + + i_kata_type := i_oci.Annotations["io.katacontainers.pkg.oci.container_type"] + print("allow_by_container_type 2: i_kata_type =", i_kata_type) + i_kata_type == "pod_container" + + allow_container_name(p_oci, i_oci) + allow_net_namespace(p_oci, i_oci) + allow_log_directory(p_oci, i_oci) + + print("allow_by_container_type 2: true") +} + +# "io.kubernetes.cri.container-name" annotation +allow_sandbox_container_name(p_oci, i_oci) { + print("allow_sandbox_container_name: start") + + container_annotation_missing(p_oci, i_oci, "io.kubernetes.cri.container-name") + + print("allow_sandbox_container_name: true") +} + +allow_container_name(p_oci, i_oci) { + print("allow_container_name: start") + + allow_container_annotation(p_oci, i_oci, "io.kubernetes.cri.container-name") + + print("allow_container_name: true") +} + +container_annotation_missing(p_oci, i_oci, key) { + print("container_annotation_missing:", key) + + not p_oci.Annotations[key] + not i_oci.Annotations[key] + + print("container_annotation_missing: true") +} + +allow_container_annotation(p_oci, i_oci, key) { + print("allow_container_annotation: key =", key) + + p_value := p_oci.Annotations[key] + i_value := i_oci.Annotations[key] + print("allow_container_annotation: p_value =", p_value, "i_value =", i_value) + + p_value == i_value + + print("allow_container_annotation: true") +} + +# "nerdctl/network-namespace" annotation +allow_sandbox_net_namespace(p_oci, i_oci) { + print("allow_sandbox_net_namespace: start") + + key := "nerdctl/network-namespace" + + p_namespace := p_oci.Annotations[key] + i_namespace := i_oci.Annotations[key] + print("allow_sandbox_net_namespace: p_namespace =", p_namespace, "i_namespace =", i_namespace) + + regex.match(p_namespace, i_namespace) + + print("allow_sandbox_net_namespace: true") +} + +allow_net_namespace(p_oci, i_oci) { + print("allow_net_namespace: start") + + key := "nerdctl/network-namespace" + + not p_oci.Annotations[key] + not i_oci.Annotations[key] + + print("allow_net_namespace: true") +} + +# "io.kubernetes.cri.sandbox-log-directory" annotation +allow_sandbox_log_directory(p_oci, i_oci, s_name, s_namespace) { + print("allow_sandbox_log_directory: start") + + key := "io.kubernetes.cri.sandbox-log-directory" + + p_dir := p_oci.Annotations[key] + regex1 := replace(p_dir, "$(sandbox-name)", s_name) + regex2 := replace(regex1, "$(sandbox-namespace)", s_namespace) + print("allow_sandbox_log_directory: regex2 =", regex2) + + i_dir := i_oci.Annotations[key] + print("allow_sandbox_log_directory: i_dir =", i_dir) + + regex.match(regex2, i_dir) + + print("allow_sandbox_log_directory: true") +} + +allow_log_directory(p_oci, i_oci) { + print("allow_log_directory: start") + + key := "io.kubernetes.cri.sandbox-log-directory" + + not p_oci.Annotations[key] + not i_oci.Annotations[key] + + print("allow_log_directory: true") +} + +allow_linux(p_oci, i_oci) { + p_namespaces := p_oci.Linux.Namespaces + print("allow_linux: p namespaces =", p_namespaces) + + i_namespaces := i_oci.Linux.Namespaces + print("allow_linux: i namespaces =", i_namespaces) + + p_namespaces == i_namespaces + + allow_masked_paths(p_oci, i_oci) + allow_readonly_paths(p_oci, i_oci) + + print("allow_linux: true") +} + +allow_masked_paths(p_oci, i_oci) { + p_paths := p_oci.Linux.MaskedPaths + print("allow_masked_paths 1: p_paths =", p_paths) + + i_paths := i_oci.Linux.MaskedPaths + print("allow_masked_paths 1: i_paths =", i_paths) + + allow_masked_paths_array(p_paths, i_paths) + + print("allow_masked_paths 1: true") +} +allow_masked_paths(p_oci, i_oci) { + print("allow_masked_paths 2: start") + + not p_oci.Linux.MaskedPaths + not i_oci.Linux.MaskedPaths + + print("allow_masked_paths 2: true") +} + +# All the policy masked paths must be masked in the input data too. +# Input is allowed to have more masked paths than the policy. +allow_masked_paths_array(p_array, i_array) { + every p_elem in p_array { + allow_masked_path(p_elem, i_array) + } +} + +allow_masked_path(p_elem, i_array) { + print("allow_masked_path: p_elem =", p_elem) + + some i_elem in i_array + p_elem == i_elem + + print("allow_masked_path: true") +} + +allow_readonly_paths(p_oci, i_oci) { + p_paths := p_oci.Linux.ReadonlyPaths + print("allow_readonly_paths 1: p_paths =", p_paths) + + i_paths := i_oci.Linux.ReadonlyPaths + print("allow_readonly_paths 1: i_paths =", i_paths) + + allow_readonly_paths_array(p_paths, i_paths, i_oci.Linux.MaskedPaths) + + print("allow_readonly_paths 1: true") +} +allow_readonly_paths(p_oci, i_oci) { + print("allow_readonly_paths 2: start") + + not p_oci.Linux.ReadonlyPaths + not i_oci.Linux.ReadonlyPaths + + print("allow_readonly_paths 2: true") +} + +# All the policy readonly paths must be either: +# - Present in the input readonly paths, or +# - Present in the input masked paths. +# Input is allowed to have more readonly paths than the policy. +allow_readonly_paths_array(p_array, i_array, masked_paths) { + every p_elem in p_array { + allow_readonly_path(p_elem, i_array, masked_paths) + } +} + +allow_readonly_path(p_elem, i_array, masked_paths) { + print("allow_readonly_path 1: p_elem =", p_elem) + + some i_elem in i_array + p_elem == i_elem + + print("allow_readonly_path 1: true") +} +allow_readonly_path(p_elem, i_array, masked_paths) { + print("allow_readonly_path 2: p_elem =", p_elem) + + some i_masked in masked_paths + p_elem == i_masked + + print("allow_readonly_path 2: true") +} + +# Check the consistency of the input "io.katacontainers.pkg.oci.bundle_path" +# and io.kubernetes.cri.sandbox-id" values with other fields. +allow_by_bundle_or_sandbox_id(p_oci, i_oci, p_storages, i_storages) { + print("allow_by_bundle_or_sandbox_id: start") + + bundle_path := i_oci.Annotations["io.katacontainers.pkg.oci.bundle_path"] + bundle_id := replace(bundle_path, "/run/containerd/io.containerd.runtime.v2.task/k8s.io/", "") + + key := "io.kubernetes.cri.sandbox-id" + + p_regex := p_oci.Annotations[key] + sandbox_id := i_oci.Annotations[key] + + print("allow_by_bundle_or_sandbox_id: sandbox_id =", sandbox_id, "regex =", p_regex) + regex.match(p_regex, sandbox_id) + + allow_root_path(p_oci, i_oci, bundle_id) + + every i_mount in input.OCI.Mounts { + allow_mount(p_oci, i_mount, bundle_id, sandbox_id) + } + + allow_storages(p_storages, i_storages, bundle_id, sandbox_id) + + print("allow_by_bundle_or_sandbox_id: true") +} + +allow_process(p_oci, i_oci, s_name) { + p_process := p_oci.Process + i_process := i_oci.Process + + print("allow_process: i terminal =", i_process.Terminal, "p terminal =", p_process.Terminal) + p_process.Terminal == i_process.Terminal + + print("allow_process: i cwd =", i_process.Cwd, "i cwd =", p_process.Cwd) + p_process.Cwd == i_process.Cwd + + print("allow_process: i noNewPrivileges =", i_process.NoNewPrivileges, "p noNewPrivileges =", p_process.NoNewPrivileges) + p_process.NoNewPrivileges == i_process.NoNewPrivileges + + allow_caps(p_process.Capabilities, i_process.Capabilities) + allow_user(p_process, i_process) + allow_args(p_process, i_process, s_name) + allow_env(p_process, i_process, s_name) + + print("allow_process: true") +} + +allow_user(p_process, i_process) { + p_user := p_process.User + i_user := i_process.User + + # TODO: track down the reason for mcr.microsoft.com/oss/bitnami/redis:6.0.8 being + # executed with uid = 0 despite having "User": "1001" in its container image + # config. + #print("allow_user: input uid =", i_user.UID, "policy uid =", p_user.UID) + #p_user.UID == i_user.UID + + # TODO: track down the reason for registry.k8s.io/pause:3.9 being + # executed with gid = 0 despite having "65535:65535" in its container image + # config. + #print("allow_user: input gid =", i_user.GID, "policy gid =", p_user.GID) + #p_user.GID == i_user.GID + + # TODO: compare the additionalGids field too after computing its value + # based on /etc/passwd and /etc/group from the container image. +} + +allow_args(p_process, i_process, s_name) { + print("allow_args 1: no args") + + not p_process.Args + not i_process.Args + + print("allow_args 1: true") +} +allow_args(p_process, i_process, s_name) { + print("allow_args 2: policy args =", p_process.Args) + print("allow_args 2: input args =", i_process.Args) + + count(p_process.Args) == count(i_process.Args) + + every i, i_arg in i_process.Args { + allow_arg(i, i_arg, p_process, s_name) + } + + print("allow_args 2: true") +} +allow_arg(i, i_arg, p_process, s_name) { + p_arg := p_process.Args[i] + print("allow_arg 1: i =", i, "i_arg =", i_arg, "p_arg =", p_arg) + + p_arg2 := replace(p_arg, "$$", "$") + p_arg2 == i_arg + + print("allow_arg 1: true") +} +allow_arg(i, i_arg, p_process, s_name) { + p_arg := p_process.Args[i] + print("allow_arg 2: i =", i, "i_arg =", i_arg, "p_arg =", p_arg) + + # TODO: can $(node-name) be handled better? + contains(p_arg, "$(node-name)") + + print("allow_arg 2: true") +} +allow_arg(i, i_arg, p_process, s_name) { + p_arg := p_process.Args[i] + print("allow_arg 3: i =", i, "i_arg =", i_arg, "p_arg =", p_arg) + + p_arg2 := replace(p_arg, "$$", "$") + p_arg3 := replace(p_arg2, "$(sandbox-name)", s_name) + print("allow_arg 3: p_arg3 =", p_arg3) + p_arg3 == i_arg + + print("allow_arg 3: true") +} + +# OCI process.Env field +allow_env(p_process, i_process, s_name) { + print("allow_env: p env =", p_process.Env) + print("allow_env: i env =", i_process.Env) + + every i_var in i_process.Env { + allow_var(p_process, i_process, i_var, s_name) + } + + print("allow_env: true") +} + +# Allow input env variables that are present in the policy data too. +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 1: i_var =", i_var) + + some p_var in p_process.Env + p_var == i_var + + print("allow_var 1: true") +} + +# Match input with one of the policy variables, after substituting $(sandbox-name). +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 2: i_var =", i_var) + + some p_var in p_process.Env + p_var2 := replace(p_var, "$(sandbox-name)", s_name) + print("allow_var 2: p_var2 =", p_var2) + + p_var2 == i_var + + print("allow_var 2: true") +} + +# Allow input env variables that match with a request_defaults regex. +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 3: start") + + some p_regex1 in policy_data.request_defaults.CreateContainerRequest.allow_env_regex + print("allow_var 3: p_regex1 =", p_regex1) + + p_regex2 := replace(p_regex1, "$(ipv4_a)", policy_data.common.ipv4_a) + print("allow_var 3: p_regex2 =", p_regex2) + + p_regex3 := replace(p_regex2, "$(ip_p)", policy_data.common.ip_p) + print("allow_var 3: p_regex3 =", p_regex3) + + p_regex4 := replace(p_regex3, "$(svc_name)", policy_data.common.svc_name) + print("allow_var 3: p_regex4 =", p_regex4) + + p_regex5 := replace(p_regex4, "$(dns_label)", policy_data.common.dns_label) + print("allow_var 3: p_regex5 =", p_regex5) + + print("allow_var 3: i_var =", i_var) + regex.match(p_regex5, i_var) + + print("allow_var 3: true") +} + +# Allow fieldRef "fieldPath: status.podIP" values. +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 4: i_var =", i_var) + + name_value := split(i_var, "=") + count(name_value) == 2 + is_ip(name_value[1]) + + some p_var in p_process.Env + allow_pod_ip_var(name_value[0], p_var) + + print("allow_var 4: true") +} + +# Allow common fieldRef variables. +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 5: i_var =", i_var) + + name_value := split(i_var, "=") + count(name_value) == 2 + + some p_var in p_process.Env + p_name_value := split(p_var, "=") + count(p_name_value) == 2 + + p_name_value[0] == name_value[0] + + # TODO: should these be handled in a different way? + always_allowed := ["$(host-name)", "$(node-name)", "$(pod-uid)"] + some allowed in always_allowed + contains(p_name_value[1], allowed) + + print("allow_var 5: true") +} + +# Allow fieldRef "fieldPath: status.hostIP" values. +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 6: i_var =", i_var) + + name_value := split(i_var, "=") + count(name_value) == 2 + is_ip(name_value[1]) + + some p_var in p_process.Env + allow_host_ip_var(name_value[0], p_var) + + print("allow_var 6: true") +} + +# Allow resourceFieldRef values (e.g., "limits.cpu"). +allow_var(p_process, i_process, i_var, s_name) { + print("allow_var 7: i_var =", i_var) + + name_value := split(i_var, "=") + count(name_value) == 2 + + some p_var in p_process.Env + p_name_value := split(p_var, "=") + count(p_name_value) == 2 + + p_name_value[0] == name_value[0] + + # TODO: should these be handled in a different way? + always_allowed = ["$(resource-field)", "$(todo-annotation)"] + some allowed in always_allowed + contains(p_name_value[1], allowed) + + print("allow_var 7: true") +} + +allow_pod_ip_var(var_name, p_var) { + print("allow_pod_ip_var: var_name =", var_name, "p_var =", p_var) + + p_name_value := split(p_var, "=") + count(p_name_value) == 2 + + p_name_value[0] == var_name + p_name_value[1] == "$(pod-ip)" + + print("allow_pod_ip_var: true") +} + +allow_host_ip_var(var_name, p_var) { + print("allow_host_ip_var: var_name =", var_name, "p_var =", p_var) + + p_name_value := split(p_var, "=") + count(p_name_value) == 2 + + p_name_value[0] == var_name + p_name_value[1] == "$(host-ip)" + + print("allow_host_ip_var: true") +} + +is_ip(value) { + bytes = split(value, ".") + count(bytes) == 4 + + is_ip_first_byte(bytes[0]) + is_ip_other_byte(bytes[1]) + is_ip_other_byte(bytes[2]) + is_ip_other_byte(bytes[3]) +} +is_ip_first_byte(component) { + number = to_number(component) + number >= 1 + number <= 255 +} +is_ip_other_byte(component) { + number = to_number(component) + number >= 0 + number <= 255 +} + +# OCI root.Path +allow_root_path(p_oci, i_oci, bundle_id) { + p_path1 := p_oci.Root.Path + print("allow_root_path: p_path1 =", p_path1) + + p_path2 := replace(p_path1, "$(cpath)", policy_data.common.cpath) + print("allow_root_path: p_path2 =", p_path2) + + p_path3 := replace(p_path2, "$(bundle-id)", bundle_id) + print("allow_root_path: p_path3 =", p_path3) + + p_path3 == i_oci.Root.Path + + print("allow_root_path: true") +} + +# device mounts +allow_mount(p_oci, i_mount, bundle_id, sandbox_id) { + print("allow_mount: start") + + some p_mount in p_oci.Mounts + check_mount(p_mount, i_mount, bundle_id, sandbox_id) + + # TODO: are there any other required policy checks for mounts - e.g., + # multiple mounts with same source or destination? + + print("allow_mount: true") +} + +check_mount(p_mount, i_mount, bundle_id, sandbox_id) { + print("check_mount 1: p_mount =", p_mount) + print("check_mount 1: i_mount =", i_mount) + + p_mount == i_mount + + print("check_mount 1: true") +} +check_mount(p_mount, i_mount, bundle_id, sandbox_id) { + print("check_mount 2: i destination =", i_mount.destination, "p destination =", p_mount.destination) + p_mount.destination == i_mount.destination + + print("check_mount 2: i type =", i_mount.type_, "p type =", p_mount.type_) + p_mount.type_ == i_mount.type_ + + print("check_mount 2: i options =", i_mount.options) + print("check_mount 2: p options =", p_mount.options) + p_mount.options == i_mount.options + + mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) + + print("check_mount 2: true") +} + +mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) { + print("mount_source_allows 1: i_mount.source =", i_mount.source) + + regex1 := p_mount.source + print("mount_source_allows 1: regex1 =", regex1) + + regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix) + print("mount_source_allows 1: regex2 =", regex2) + + regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath) + print("mount_source_allows 1: regex3 =", regex3) + + regex4 := replace(regex3, "$(bundle-id)", bundle_id) + print("mount_source_allows 1: regex4 =", regex4) + + regex.match(regex4, i_mount.source) + + print("mount_source_allows 1: true") +} +mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) { + print("mount_source_allows 2: i_mount.source=", i_mount.source) + + regex1 := p_mount.source + print("mount_source_allows 2: regex1 =", regex1) + + regex2 := replace(regex1, "$(sfprefix)", policy_data.common.sfprefix) + print("mount_source_allows 2: regex2 =", regex2) + + regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath) + print("mount_source_allows 2: regex3 =", regex3) + + regex4 := replace(regex3, "$(sandbox-id)", sandbox_id) + print("mount_source_allows 2: regex4 =", regex4) + + regex.match(regex4, i_mount.source) + + print("mount_source_allows 2: true") +} + +###################################################################### +# Storages + +allow_storages(p_storages, i_storages, bundle_id, sandbox_id) { + p_count := count(p_storages) + i_count := count(i_storages) + print("allow_storages: p_count =", p_count, "i_count =", i_count) + + p_count == i_count + + # Get the container image layer IDs and verity root hashes, from the "overlayfs" storage. + some overlay_storage in p_storages + overlay_storage.driver == "overlayfs" + print("allow_storages: overlay_storage =", overlay_storage) + count(overlay_storage.options) == 2 + + layer_ids := split(overlay_storage.options[0], ":") + print("allow_storages: layer_ids =", layer_ids) + + root_hashes := split(overlay_storage.options[1], ":") + print("allow_storages: root_hashes =", root_hashes) + + every i_storage in i_storages { + allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes) + } + + print("allow_storages: true") +} + +allow_storage(p_storages, i_storage, bundle_id, sandbox_id, layer_ids, root_hashes) { + some p_storage in p_storages + + print("allow_storage: p_storage =", p_storage) + print("allow_storage: i_storage =", i_storage) + + p_storage.driver == i_storage.driver + p_storage.driver_options == i_storage.driver_options + p_storage.fs_group == i_storage.fs_group + + allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) + allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) + + # TODO: validate the source field too. + + print("allow_storage: true") +} + +allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) { + print("allow_storage_options 1: start") + + p_storage.driver != "blk" + p_storage.driver != "overlayfs" + p_storage.options == i_storage.options + + print("allow_storage_options 1: true") +} +allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) { + print("allow_storage_options 2: start") + + p_storage.driver == "overlayfs" + count(p_storage.options) == 2 + + policy_ids := split(p_storage.options[0], ":") + print("allow_storage_options 2: policy_ids =", policy_ids) + policy_ids == layer_ids + + policy_hashes := split(p_storage.options[1], ":") + print("allow_storage_options 2: policy_hashes =", policy_hashes) + + p_count := count(policy_ids) + print("allow_storage_options 2: p_count =", p_count) + p_count >= 1 + p_count == count(policy_hashes) + + i_count := count(i_storage.options) + print("allow_storage_options 2: i_count =", i_count) + i_count == p_count + 3 + + print("allow_storage_options 2: i_storage.options[0] =", i_storage.options[0]) + i_storage.options[0] == "io.katacontainers.fs-opt.layer-src-prefix=/var/lib/containerd/io.containerd.snapshotter.v1.tardev/layers" + + print("allow_storage_options 2: i_storage.options[i_count - 2] =", i_storage.options[i_count - 2]) + i_storage.options[i_count - 2] == "io.katacontainers.fs-opt.overlay-rw" + + lowerdir := concat("=", ["lowerdir", p_storage.options[0]]) + print("allow_storage_options 2: lowerdir =", lowerdir) + + i_storage.options[i_count - 1] == lowerdir + print("allow_storage_options 2: i_storage.options[i_count - 1] =", i_storage.options[i_count - 1]) + + every i, policy_id in policy_ids { + allow_overlay_layer(policy_id, policy_hashes[i], i_storage.options[i + 1]) + } + + print("allow_storage_options 2: true") +} +allow_storage_options(p_storage, i_storage, layer_ids, root_hashes) { + print("allow_storage_options 3: start") + + p_storage.driver == "blk" + count(p_storage.options) == 1 + + startswith(p_storage.options[0], "$(hash") + hash_suffix := trim_left(p_storage.options[0], "$(hash") + + endswith(hash_suffix, ")") + hash_index := trim_right(hash_suffix, ")") + i := to_number(hash_index) + print("allow_storage_options 3: i =", i) + + hash_option := concat("=", ["io.katacontainers.fs-opt.root-hash", root_hashes[i]]) + print("allow_storage_options 3: hash_option =", hash_option) + + count(i_storage.options) == 4 + i_storage.options[0] == "ro" + i_storage.options[1] == "io.katacontainers.fs-opt.block_device=file" + i_storage.options[2] == "io.katacontainers.fs-opt.is-layer" + i_storage.options[3] == hash_option + + print("allow_storage_options 3: true") +} + +allow_overlay_layer(policy_id, policy_hash, i_option) { + print("allow_overlay_layer: policy_id =", policy_id, "policy_hash =", policy_hash) + print("allow_overlay_layer: i_option =", i_option) + + startswith(i_option, "io.katacontainers.fs-opt.layer=") + i_value := replace(i_option, "io.katacontainers.fs-opt.layer=", "") + i_value_decoded := base64.decode(i_value) + print("allow_overlay_layer: i_value_decoded =", i_value_decoded) + + policy_suffix := concat("=", ["tar,ro,io.katacontainers.fs-opt.block_device=file,io.katacontainers.fs-opt.is-layer,io.katacontainers.fs-opt.root-hash", policy_hash]) + p_value := concat(",", [policy_id, policy_suffix]) + print("allow_overlay_layer: p_value =", p_value) + + p_value == i_value_decoded + + print("allow_overlay_layer: true") +} + +allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) { + print("allow_mount_point 1: i_storage.mount_point =", i_storage.mount_point) + p_storage.fstype == "tar" + + startswith(p_storage.mount_point, "$(layer") + mount_suffix := trim_left(p_storage.mount_point, "$(layer") + + endswith(mount_suffix, ")") + layer_index := trim_right(mount_suffix, ")") + i := to_number(layer_index) + print("allow_mount_point 1: i =", i) + + layer_id := layer_ids[i] + print("allow_mount_point 1: layer_id =", layer_id) + + p_mount := concat("/", ["/run/kata-containers/sandbox/layers", layer_id]) + print("allow_mount_point 1: p_mount =", p_mount) + + p_mount == i_storage.mount_point + + print("allow_mount_point 1: true") +} +allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) { + print("allow_mount_point 2: i_storage.mount_point =", i_storage.mount_point) + p_storage.fstype == "fuse3.kata-overlay" + + mount1 := replace(p_storage.mount_point, "$(cpath)", policy_data.common.cpath) + mount2 := replace(mount1, "$(bundle-id)", bundle_id) + print("allow_mount_point 2: mount2 =", mount2) + + mount2 == i_storage.mount_point + + print("allow_mount_point 2: true") +} +allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) { + print("allow_mount_point 3: i_storage.mount_point =", i_storage.mount_point) + p_storage.fstype == "local" + + mount1 := p_storage.mount_point + print("allow_mount_point 3: mount1 =", mount1) + + mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath) + print("allow_mount_point 3: mount2 =", mount2) + + mount3 := replace(mount2, "$(sandbox-id)", sandbox_id) + print("allow_mount_point 3: mount3 =", mount3) + + regex.match(mount3, i_storage.mount_point) + + print("allow_mount_point 3: true") +} +allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) { + print("allow_mount_point 4: i_storage.mount_point =", i_storage.mount_point) + p_storage.fstype == "bind" + + mount1 := p_storage.mount_point + print("allow_mount_point 4: mount1 =", mount1) + + mount2 := replace(mount1, "$(cpath)", policy_data.common.cpath) + print("allow_mount_point 4: mount2 =", mount2) + + mount3 := replace(mount2, "$(bundle-id)", bundle_id) + print("allow_mount_point 4: mount3 =", mount3) + + regex.match(mount3, i_storage.mount_point) + + print("allow_mount_point 4: true") +} +allow_mount_point(p_storage, i_storage, bundle_id, sandbox_id, layer_ids) { + print("allow_mount_point 5: i_storage.mount_point =", i_storage.mount_point) + p_storage.fstype == "tmpfs" + + mount1 := p_storage.mount_point + print("allow_mount_point 5: mount1 =", mount1) + + regex.match(mount1, i_storage.mount_point) + + print("allow_mount_point 5: true") +} + +# process.Capabilities +allow_caps(p_caps, i_caps) { + print("allow_caps: policy Ambient =", p_caps.Ambient) + print("allow_caps: input Ambient =", i_caps.Ambient) + match_caps(p_caps.Ambient, i_caps.Ambient) + + print("allow_caps: policy Bounding =", p_caps.Bounding) + print("allow_caps: input Bounding =", i_caps.Bounding) + match_caps(p_caps.Bounding, i_caps.Bounding) + + print("allow_caps: policy Effective =", p_caps.Effective) + print("allow_caps: input Effective =", i_caps.Effective) + match_caps(p_caps.Effective, i_caps.Effective) + + print("allow_caps: policy Inheritable =", p_caps.Inheritable) + print("allow_caps: input Inheritable =", i_caps.Inheritable) + match_caps(p_caps.Inheritable, i_caps.Inheritable) + + print("allow_caps: policy Permitted =", p_caps.Permitted) + print("allow_caps: input Permitted =", i_caps.Permitted) + match_caps(p_caps.Permitted, i_caps.Permitted) +} + +match_caps(p_caps, i_caps) { + print("match_caps 1: start") + + p_caps == i_caps + + print("match_caps 1: true") +} +match_caps(p_caps, i_caps) { + print("match_caps 2: start") + + count(p_caps) == 1 + p_caps[0] == "$(default_caps)" + + print("match_caps 2: default_caps =", policy_data.common.default_caps) + policy_data.common.default_caps == i_caps + + print("match_caps 2: true") +} +match_caps(p_caps, i_caps) { + print("match_caps 3: start") + + count(p_caps) == 1 + p_caps[0] == "$(privileged_caps)" + + print("match_caps 3: privileged_caps =", policy_data.common.privileged_caps) + policy_data.common.privileged_caps == i_caps + + print("match_caps 3: true") +} + +###################################################################### +CopyFileRequest { + print("CopyFileRequest: input.path =", input.path) + + some regex1 in policy_data.request_defaults.CopyFileRequest + regex2 := replace(regex1, "$(cpath)", policy_data.common.cpath) + regex.match(regex2, input.path) + + print("CopyFileRequest: true") +} + +ExecProcessRequest { + print("ExecProcessRequest 1: input =", input) + + i_command = concat(" ", input.process.Args) + print("ExecProcessRequest 3: i_command =", i_command) + + some p_command in policy_data.request_defaults.ExecProcessRequest.commands + p_command == i_command + + print("ExecProcessRequest 1: true") +} +ExecProcessRequest { + print("ExecProcessRequest 2: input =", input) + + # TODO: match input container ID with its corresponding container.exec_commands. + i_command = concat(" ", input.process.Args) + print("ExecProcessRequest 3: i_command =", i_command) + + some container in policy_data.containers + some p_command in container.exec_commands + print("ExecProcessRequest 2: p_command =", p_command) + + # TODO: should other input data fields be validated as well? + p_command == i_command + + print("ExecProcessRequest 2: true") +} +ExecProcessRequest { + print("ExecProcessRequest 3: input =", input) + + i_command = concat(" ", input.process.Args) + print("ExecProcessRequest 3: i_command =", i_command) + + some p_regex in policy_data.request_defaults.ExecProcessRequest.regex + print("ExecProcessRequest 3: p_regex =", p_regex) + + regex.match(p_regex, i_command) + + print("ExecProcessRequest 3: true") +} + +ReadStreamRequest { + policy_data.request_defaults.ReadStreamRequest == true +} + +WriteStreamRequest { + policy_data.request_defaults.WriteStreamRequest == true +} diff --git a/src/tools/genpolicy/samples/pod-one-container.yaml b/src/tools/genpolicy/samples/pod-one-container.yaml new file mode 100644 index 0000000000..26aa612d59 --- /dev/null +++ b/src/tools/genpolicy/samples/pod-one-container.yaml @@ -0,0 +1,57 @@ +--- +apiVersion: v1 +kind: Pod +metadata: + name: one-container + labels: + run: busybox + annotations: + io.katacontainers.config.agent.policy: cGFja2FnZSBhZ2VudF9wb2xpY3kKCmltcG9ydCBmdXR1cmUua2V5d29yZHMuaW4KaW1wb3J0IGZ1dHVyZS5rZXl3b3Jkcy5ldmVyeQoKaW1wb3J0IGlucHV0CgojIERlZmF1bHQgdmFsdWVzLCByZXR1cm5lZCBieSBPUEEgd2hlbiBydWxlcyBjYW5ub3QgYmUgZXZhbHVhdGVkIHRvIHRydWUuCmRlZmF1bHQgQ29weUZpbGVSZXF1ZXN0IDo9IGZhbHNlCmRlZmF1bHQgQ3JlYXRlQ29udGFpbmVyUmVxdWVzdCA6PSBmYWxzZQpkZWZhdWx0IENyZWF0ZVNhbmRib3hSZXF1ZXN0IDo9IHRydWUKZGVmYXVsdCBEZXN0cm95U2FuZGJveFJlcXVlc3QgOj0gdHJ1ZQpkZWZhdWx0IEV4ZWNQcm9jZXNzUmVxdWVzdCA6PSBmYWxzZQpkZWZhdWx0IEdldE9PTUV2ZW50UmVxdWVzdCA6PSB0cnVlCmRlZmF1bHQgR3Vlc3REZXRhaWxzUmVxdWVzdCA6PSB0cnVlCmRlZmF1bHQgT25saW5lQ1BVTWVtUmVxdWVzdCA6PSB0cnVlCmRlZmF1bHQgUHVsbEltYWdlUmVxdWVzdCA6PSB0cnVlCmRlZmF1bHQgUmVhZFN0cmVhbVJlcXVlc3QgOj0gZmFsc2UKZGVmYXVsdCBSZW1vdmVDb250YWluZXJSZXF1ZXN0IDo9IHRydWUKZGVmYXVsdCBSZW1vdmVTdGFsZVZpcnRpb2ZzU2hhcmVNb3VudHNSZXF1ZXN0IDo9IHRydWUKZGVmYXVsdCBTaWduYWxQcm9jZXNzUmVxdWVzdCA6PSB0cnVlCmRlZmF1bHQgU3RhcnRDb250YWluZXJSZXF1ZXN0IDo9IHRydWUKZGVmYXVsdCBTdGF0c0NvbnRhaW5lclJlcXVlc3QgOj0gdHJ1ZQpkZWZhdWx0IFR0eVdpblJlc2l6ZVJlcXVlc3QgOj0gdHJ1ZQpkZWZhdWx0IFVwZGF0ZUVwaGVtZXJhbE1vdW50c1JlcXVlc3QgOj0gdHJ1ZQpkZWZhdWx0IFVwZGF0ZUludGVyZmFjZVJlcXVlc3QgOj0gdHJ1ZQpkZWZhdWx0IFVwZGF0ZVJvdXRlc1JlcXVlc3QgOj0gdHJ1ZQpkZWZhdWx0IFdhaXRQcm9jZXNzUmVxdWVzdCA6PSB0cnVlCmRlZmF1bHQgV3JpdGVTdHJlYW1SZXF1ZXN0IDo9IGZhbHNlCgojIEFsbG93UmVxdWVzdHNGYWlsaW5nUG9saWN5IDo9IHRydWUgY29uZmlndXJlcyB0aGUgQWdlbnQgdG8gKmFsbG93IGFueQojIHJlcXVlc3RzIGNhdXNpbmcgYSBwb2xpY3kgZmFpbHVyZSouIFRoaXMgaXMgYW4gdW5zZWN1cmUgY29uZmlndXJhdGlvbgojIGJ1dCBpcyB1c2VmdWwgZm9yIGFsbG93aW5nIHVuc2VjdXJlIHBvZHMgdG8gc3RhcnQsIHRoZW4gY29ubmVjdCB0bwojIHRoZW0gYW5kIGluc3BlY3QgT1BBIGxvZ3MgZm9yIHRoZSByb290IGNhdXNlIG9mIGEgZmFpbHVyZS4KZGVmYXVsdCBBbGxvd1JlcXVlc3RzRmFpbGluZ1BvbGljeSA6PSBmYWxzZQoKQ3JlYXRlQ29udGFpbmVyUmVxdWVzdCB7CiAgICBpX29jaSA6PSBpbnB1dC5PQ0kKICAgIGlfc3RvcmFnZXMgOj0gaW5wdXQuc3RvcmFnZXMKCiAgICBzb21lIHBfY29udGFpbmVyIGluIHBvbGljeV9kYXRhLmNvbnRhaW5lcnMKICAgIHByaW50KCI9PT09PT09PSBDcmVhdGVDb250YWluZXJSZXF1ZXN0OiB0cnlpbmcgbmV4dCBwb2xpY3kgY29udGFpbmVyIikKCiAgICBwX29jaSA6PSBwX2NvbnRhaW5lci5PQ0kKICAgIHBfc3RvcmFnZXMgOj0gcF9jb250YWluZXIuc3RvcmFnZXMKCiAgICBwcmludCgiQ3JlYXRlQ29udGFpbmVyUmVxdWVzdDogcCBWZXJzaW9uID0iLCBwX29jaS5WZXJzaW9uLCAiaSBWZXJzaW9uID0iLCBpX29jaS5WZXJzaW9uKQogICAgcF9vY2kuVmVyc2lvbiA9PSBpX29jaS5WZXJzaW9uCgogICAgcHJpbnQoIkNyZWF0ZUNvbnRhaW5lclJlcXVlc3Q6IHAgUmVhZG9ubHkgPSIsIHBfb2NpLlJvb3QuUmVhZG9ubHksICJpIFJlYWRvbmx5ID0iLCBpX29jaS5Sb290LlJlYWRvbmx5KQogICAgcF9vY2kuUm9vdC5SZWFkb25seSA9PSBpX29jaS5Sb290LlJlYWRvbmx5CgogICAgYWxsb3dfYW5ubyhwX29jaSwgaV9vY2kpCiAgICBhbGxvd19ieV9hbm5vKHBfb2NpLCBpX29jaSwgcF9zdG9yYWdlcywgaV9zdG9yYWdlcykKICAgIGFsbG93X2xpbnV4KHBfb2NpLCBpX29jaSkKCiAgICBwcmludCgiQ3JlYXRlQ29udGFpbmVyUmVxdWVzdDogdHJ1ZSIpCn0KCiMgUmVqZWN0IHVuZXhwZWN0ZWQgYW5ub3RhdGlvbnMuCmFsbG93X2Fubm8ocF9vY2ksIGlfb2NpKSB7CiAgICBwcmludCgiYWxsb3dfYW5ubyAxOiBzdGFydCIpCgogICAgbm90IGlfb2NpLkFubm90YXRpb25zCgogICAgcHJpbnQoImFsbG93X2Fubm8gMTogdHJ1ZSIpCn0KYWxsb3dfYW5ubyhwX29jaSwgaV9vY2kpIHsKICAgIHByaW50KCJhbGxvd19hbm5vIDI6IHAgQW5ub3RhdGlvbnMgPSIsIHBfb2NpLkFubm90YXRpb25zKQogICAgcHJpbnQoImFsbG93X2Fubm8gMjogaSBBbm5vdGF0aW9ucyA9IiwgaV9vY2kuQW5ub3RhdGlvbnMpCgogICAgaV9rZXlzIDo9IG9iamVjdC5rZXlzKGlfb2NpLkFubm90YXRpb25zKQogICAgcHJpbnQoImFsbG93X2Fubm8gMjogaSBrZXlzID0iLCBpX2tleXMpCgogICAgZXZlcnkgaV9rZXkgaW4gaV9rZXlzIHsKICAgICAgICBhbGxvd19hbm5vX2tleShpX2tleSwgcF9vY2kpCiAgICB9CgogICAgcHJpbnQoImFsbG93X2Fubm8gMjogdHJ1ZSIpCn0KCmFsbG93X2Fubm9fa2V5KGlfa2V5LCBwX29jaSkgewogICAgcHJpbnQoImFsbG93X2Fubm9fa2V5IDE6IGkga2V5ID0iLCBpX2tleSkKCiAgICBzdGFydHN3aXRoKGlfa2V5LCAiaW8ua3ViZXJuZXRlcy5jcmkuIikKCiAgICBwcmludCgiYWxsb3dfYW5ub19rZXkgMTogdHJ1ZSIpCn0KYWxsb3dfYW5ub19rZXkoaV9rZXksIHBfb2NpKSB7CiAgICBwcmludCgiYWxsb3dfYW5ub19rZXkgMjogaSBrZXkgPSIsIGlfa2V5KQoKICAgIHNvbWUgcF9rZXksIF8gaW4gcF9vY2kuQW5ub3RhdGlvbnMKICAgIHBfa2V5ID09IGlfa2V5CgogICAgcHJpbnQoImFsbG93X2Fubm9fa2V5IDI6IHRydWUiKQp9CgojIEdldCB0aGUgdmFsdWUgb2YgdGhlICJpby5rdWJlcm5ldGVzLmNyaS5zYW5kYm94LW5hbWUiIGFubm90YXRpb24gYW5kCiMgY29ycmVsYXRlIGl0IHdpdGggb3RoZXIgYW5ub3RhdGlvbnMgYW5kIHByb2Nlc3MgZmllbGRzLgphbGxvd19ieV9hbm5vKHBfb2NpLCBpX29jaSwgcF9zdG9yYWdlcywgaV9zdG9yYWdlcykgewogICAgcHJpbnQoImFsbG93X2J5X2Fubm8gMTogc3RhcnQiKQoKICAgIHNfbmFtZSA6PSAiaW8ua3ViZXJuZXRlcy5jcmkuc2FuZGJveC1uYW1lIgoKICAgIG5vdCBwX29jaS5Bbm5vdGF0aW9uc1tzX25hbWVdCgogICAgaV9zX25hbWUgOj0gaV9vY2kuQW5ub3RhdGlvbnNbc19uYW1lXQogICAgcHJpbnQoImFsbG93X2J5X2Fubm8gMTogaV9zX25hbWUgPSIsIGlfc19uYW1lKQoKICAgIGFsbG93X2J5X3NhbmRib3hfbmFtZShwX29jaSwgaV9vY2ksIHBfc3RvcmFnZXMsIGlfc3RvcmFnZXMsIGlfc19uYW1lKQoKICAgIHByaW50KCJhbGxvd19ieV9hbm5vIDE6IHRydWUiKQp9CmFsbG93X2J5X2Fubm8ocF9vY2ksIGlfb2NpLCBwX3N0b3JhZ2VzLCBpX3N0b3JhZ2VzKSB7CiAgICBwcmludCgiYWxsb3dfYnlfYW5ubyAyOiBzdGFydCIpCgogICAgc19uYW1lIDo9ICJpby5rdWJlcm5ldGVzLmNyaS5zYW5kYm94LW5hbWUiCgogICAgcF9zX25hbWUgOj0gcF9vY2kuQW5ub3RhdGlvbnNbc19uYW1lXQogICAgaV9zX25hbWUgOj0gaV9vY2kuQW5ub3RhdGlvbnNbc19uYW1lXQogICAgcHJpbnQoImFsbG93X2J5X2Fubm8gMjogaV9zX25hbWUgPSIsIGlfc19uYW1lLCAicF9zX25hbWUgPSIsIHBfc19uYW1lKQoKICAgIGFsbG93X3NhbmRib3hfbmFtZShwX3NfbmFtZSwgaV9zX25hbWUpCiAgICBhbGxvd19ieV9zYW5kYm94X25hbWUocF9vY2ksIGlfb2NpLCBwX3N0b3JhZ2VzLCBpX3N0b3JhZ2VzLCBpX3NfbmFtZSkKCiAgICBwcmludCgiYWxsb3dfYnlfYW5ubyAyOiB0cnVlIikKfQoKYWxsb3dfYnlfc2FuZGJveF9uYW1lKHBfb2NpLCBpX29jaSwgcF9zdG9yYWdlcywgaV9zdG9yYWdlcywgc19uYW1lKSB7CiAgICBwcmludCgiYWxsb3dfYnlfc2FuZGJveF9uYW1lOiBzdGFydCIpCgogICAgc19uYW1lc3BhY2UgOj0gImlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtbmFtZXNwYWNlIgoKICAgIHBfbmFtZXNwYWNlIDo9IHBfb2NpLkFubm90YXRpb25zW3NfbmFtZXNwYWNlXQogICAgaV9uYW1lc3BhY2UgOj0gaV9vY2kuQW5ub3RhdGlvbnNbc19uYW1lc3BhY2VdCiAgICBwcmludCgiYWxsb3dfYnlfc2FuZGJveF9uYW1lOiBwX25hbWVzcGFjZSA9IiwgcF9uYW1lc3BhY2UsICJpX25hbWVzcGFjZSA9IiwgaV9uYW1lc3BhY2UpCiAgICBwX25hbWVzcGFjZSA9PSBpX25hbWVzcGFjZQoKICAgIGFsbG93X2J5X2NvbnRhaW5lcl90eXBlcyhwX29jaSwgaV9vY2ksIHNfbmFtZSwgcF9uYW1lc3BhY2UpCiAgICBhbGxvd19ieV9idW5kbGVfb3Jfc2FuZGJveF9pZChwX29jaSwgaV9vY2ksIHBfc3RvcmFnZXMsIGlfc3RvcmFnZXMpCiAgICBhbGxvd19wcm9jZXNzKHBfb2NpLCBpX29jaSwgc19uYW1lKQoKICAgIHByaW50KCJhbGxvd19ieV9zYW5kYm94X25hbWU6IHRydWUiKQp9CgphbGxvd19zYW5kYm94X25hbWUocF9zX25hbWUsIGlfc19uYW1lKSB7CiAgICBwcmludCgiYWxsb3dfc2FuZGJveF9uYW1lIDE6IHN0YXJ0IikKCiAgICBwX3NfbmFtZSA9PSBpX3NfbmFtZQoKICAgIHByaW50KCJhbGxvd19zYW5kYm94X25hbWUgMTogdHJ1ZSIpCn0KYWxsb3dfc2FuZGJveF9uYW1lKHBfc19uYW1lLCBpX3NfbmFtZSkgewogICAgcHJpbnQoImFsbG93X3NhbmRib3hfbmFtZSAyOiBzdGFydCIpCgogICAgIyBUT0RPOiBzaG91bGQgZ2VuZXJhdGVkIG5hbWVzIGJlIGhhbmRsZWQgZGlmZmVyZW50bHk/CiAgICBjb250YWlucyhwX3NfbmFtZSwgIiQoZ2VuZXJhdGVkLW5hbWUpIikKCiAgICBwcmludCgiYWxsb3dfc2FuZGJveF9uYW1lIDI6IHRydWUiKQp9CgojIENoZWNrIHRoYXQgdGhlICJpby5rdWJlcm5ldGVzLmNyaS5jb250YWluZXItdHlwZSIgYW5kCiMgImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuY29udGFpbmVyX3R5cGUiIGFubm90YXRpb25zIGRlc2lnbmF0ZSB0aGUKIyBleHBlY3RlZCB0eXBlIC0gZWl0aGVyIGEgInNhbmRib3giIG9yIGEgImNvbnRhaW5lciIuIFRoZW4sIHZhbGlkYXRlCiMgb3RoZXIgYW5ub3RhdGlvbnMgYmFzZWQgb24gdGhlIGFjdHVhbCAic2FuZGJveCIgb3IgImNvbnRhaW5lciIgdmFsdWUKIyBmcm9tIHRoZSBpbnB1dCBjb250YWluZXIuCmFsbG93X2J5X2NvbnRhaW5lcl90eXBlcyhwX29jaSwgaV9vY2ksIHNfbmFtZSwgc19uYW1lc3BhY2UpIHsKICAgIHByaW50KCJhbGxvd19ieV9jb250YWluZXJfdHlwZXM6IGNoZWNraW5nIGlvLmt1YmVybmV0ZXMuY3JpLmNvbnRhaW5lci10eXBlIikKCiAgICBjX3R5cGUgOj0gImlvLmt1YmVybmV0ZXMuY3JpLmNvbnRhaW5lci10eXBlIgogICAgCiAgICBwX2NyaV90eXBlIDo9IHBfb2NpLkFubm90YXRpb25zW2NfdHlwZV0KICAgIGlfY3JpX3R5cGUgOj0gaV9vY2kuQW5ub3RhdGlvbnNbY190eXBlXQogICAgcHJpbnQoImFsbG93X2J5X2NvbnRhaW5lcl90eXBlczogcF9jcmlfdHlwZSA9IiwgcF9jcmlfdHlwZSwgImlfY3JpX3R5cGUgPSIsIGlfY3JpX3R5cGUpCiAgICBwX2NyaV90eXBlID09IGlfY3JpX3R5cGUKCiAgICBhbGxvd19ieV9jb250YWluZXJfdHlwZShpX2NyaV90eXBlLCBwX29jaSwgaV9vY2ksIHNfbmFtZSwgc19uYW1lc3BhY2UpCgogICAgcHJpbnQoImFsbG93X2J5X2NvbnRhaW5lcl90eXBlczogdHJ1ZSIpCn0KCmFsbG93X2J5X2NvbnRhaW5lcl90eXBlKGlfY3JpX3R5cGUsIHBfb2NpLCBpX29jaSwgc19uYW1lLCBzX25hbWVzcGFjZSkgewogICAgcHJpbnQoImFsbG93X2J5X2NvbnRhaW5lcl90eXBlIDE6IGlfY3JpX3R5cGUgPSIsIGlfY3JpX3R5cGUpCiAgICBpX2NyaV90eXBlID09ICJzYW5kYm94IgoKICAgIGlfa2F0YV90eXBlIDo9IGlfb2NpLkFubm90YXRpb25zWyJpby5rYXRhY29udGFpbmVycy5wa2cub2NpLmNvbnRhaW5lcl90eXBlIl0KICAgIHByaW50KCJhbGxvd19ieV9jb250YWluZXJfdHlwZSAxOiBpX2thdGFfdHlwZSA9IiwgaV9rYXRhX3R5cGUpCiAgICBpX2thdGFfdHlwZSA9PSAicG9kX3NhbmRib3giCgogICAgYWxsb3dfc2FuZGJveF9jb250YWluZXJfbmFtZShwX29jaSwgaV9vY2kpCiAgICBhbGxvd19zYW5kYm94X25ldF9uYW1lc3BhY2UocF9vY2ksIGlfb2NpKQogICAgYWxsb3dfc2FuZGJveF9sb2dfZGlyZWN0b3J5KHBfb2NpLCBpX29jaSwgc19uYW1lLCBzX25hbWVzcGFjZSkKCiAgICBwcmludCgiYWxsb3dfYnlfY29udGFpbmVyX3R5cGUgMTogdHJ1ZSIpCn0KCmFsbG93X2J5X2NvbnRhaW5lcl90eXBlKGlfY3JpX3R5cGUsIHBfb2NpLCBpX29jaSwgc19uYW1lLCBzX25hbWVzcGFjZSkgewogICAgcHJpbnQoImFsbG93X2J5X2NvbnRhaW5lcl90eXBlIDI6IGlfY3JpX3R5cGUgPSIsIGlfY3JpX3R5cGUpCiAgICBpX2NyaV90eXBlID09ICJjb250YWluZXIiCgogICAgaV9rYXRhX3R5cGUgOj0gaV9vY2kuQW5ub3RhdGlvbnNbImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuY29udGFpbmVyX3R5cGUiXQogICAgcHJpbnQoImFsbG93X2J5X2NvbnRhaW5lcl90eXBlIDI6IGlfa2F0YV90eXBlID0iLCBpX2thdGFfdHlwZSkKICAgIGlfa2F0YV90eXBlID09ICJwb2RfY29udGFpbmVyIgoKICAgIGFsbG93X2NvbnRhaW5lcl9uYW1lKHBfb2NpLCBpX29jaSkKICAgIGFsbG93X25ldF9uYW1lc3BhY2UocF9vY2ksIGlfb2NpKQogICAgYWxsb3dfbG9nX2RpcmVjdG9yeShwX29jaSwgaV9vY2kpCgogICAgcHJpbnQoImFsbG93X2J5X2NvbnRhaW5lcl90eXBlIDI6IHRydWUiKQp9CgojICJpby5rdWJlcm5ldGVzLmNyaS5jb250YWluZXItbmFtZSIgYW5ub3RhdGlvbgphbGxvd19zYW5kYm94X2NvbnRhaW5lcl9uYW1lKHBfb2NpLCBpX29jaSkgewogICAgcHJpbnQoImFsbG93X3NhbmRib3hfY29udGFpbmVyX25hbWU6IHN0YXJ0IikKCiAgICBjb250YWluZXJfYW5ub3RhdGlvbl9taXNzaW5nKHBfb2NpLCBpX29jaSwgImlvLmt1YmVybmV0ZXMuY3JpLmNvbnRhaW5lci1uYW1lIikKCiAgICBwcmludCgiYWxsb3dfc2FuZGJveF9jb250YWluZXJfbmFtZTogdHJ1ZSIpCn0KCmFsbG93X2NvbnRhaW5lcl9uYW1lKHBfb2NpLCBpX29jaSkgewogICAgcHJpbnQoImFsbG93X2NvbnRhaW5lcl9uYW1lOiBzdGFydCIpCgogICAgYWxsb3dfY29udGFpbmVyX2Fubm90YXRpb24ocF9vY2ksIGlfb2NpLCAiaW8ua3ViZXJuZXRlcy5jcmkuY29udGFpbmVyLW5hbWUiKQoKICAgIHByaW50KCJhbGxvd19jb250YWluZXJfbmFtZTogdHJ1ZSIpCn0KCmNvbnRhaW5lcl9hbm5vdGF0aW9uX21pc3NpbmcocF9vY2ksIGlfb2NpLCBrZXkpIHsKICAgIHByaW50KCJjb250YWluZXJfYW5ub3RhdGlvbl9taXNzaW5nOiIsIGtleSkKCiAgICBub3QgcF9vY2kuQW5ub3RhdGlvbnNba2V5XQogICAgbm90IGlfb2NpLkFubm90YXRpb25zW2tleV0KCiAgICBwcmludCgiY29udGFpbmVyX2Fubm90YXRpb25fbWlzc2luZzogdHJ1ZSIpCn0KCmFsbG93X2NvbnRhaW5lcl9hbm5vdGF0aW9uKHBfb2NpLCBpX29jaSwga2V5KSB7CiAgICBwcmludCgiYWxsb3dfY29udGFpbmVyX2Fubm90YXRpb246IGtleSA9Iiwga2V5KQoKICAgIHBfdmFsdWUgOj0gcF9vY2kuQW5ub3RhdGlvbnNba2V5XQogICAgaV92YWx1ZSA6PSBpX29jaS5Bbm5vdGF0aW9uc1trZXldCiAgICBwcmludCgiYWxsb3dfY29udGFpbmVyX2Fubm90YXRpb246IHBfdmFsdWUgPSIsIHBfdmFsdWUsICJpX3ZhbHVlID0iLCBpX3ZhbHVlKQoKICAgIHBfdmFsdWUgPT0gaV92YWx1ZQoKICAgIHByaW50KCJhbGxvd19jb250YWluZXJfYW5ub3RhdGlvbjogdHJ1ZSIpCn0KCiMgIm5lcmRjdGwvbmV0d29yay1uYW1lc3BhY2UiIGFubm90YXRpb24KYWxsb3dfc2FuZGJveF9uZXRfbmFtZXNwYWNlKHBfb2NpLCBpX29jaSkgewogICAgcHJpbnQoImFsbG93X3NhbmRib3hfbmV0X25hbWVzcGFjZTogc3RhcnQiKQoKICAgIGtleSA6PSAibmVyZGN0bC9uZXR3b3JrLW5hbWVzcGFjZSIKCiAgICBwX25hbWVzcGFjZSA6PSBwX29jaS5Bbm5vdGF0aW9uc1trZXldCiAgICBpX25hbWVzcGFjZSA6PSBpX29jaS5Bbm5vdGF0aW9uc1trZXldCiAgICBwcmludCgiYWxsb3dfc2FuZGJveF9uZXRfbmFtZXNwYWNlOiBwX25hbWVzcGFjZSA9IiwgcF9uYW1lc3BhY2UsICJpX25hbWVzcGFjZSA9IiwgaV9uYW1lc3BhY2UpCgogICAgcmVnZXgubWF0Y2gocF9uYW1lc3BhY2UsIGlfbmFtZXNwYWNlKQoKICAgIHByaW50KCJhbGxvd19zYW5kYm94X25ldF9uYW1lc3BhY2U6IHRydWUiKQp9CgphbGxvd19uZXRfbmFtZXNwYWNlKHBfb2NpLCBpX29jaSkgewogICAgcHJpbnQoImFsbG93X25ldF9uYW1lc3BhY2U6IHN0YXJ0IikKCiAgICBrZXkgOj0gIm5lcmRjdGwvbmV0d29yay1uYW1lc3BhY2UiCgogICAgbm90IHBfb2NpLkFubm90YXRpb25zW2tleV0KICAgIG5vdCBpX29jaS5Bbm5vdGF0aW9uc1trZXldCgogICAgcHJpbnQoImFsbG93X25ldF9uYW1lc3BhY2U6IHRydWUiKQp9CgojICJpby5rdWJlcm5ldGVzLmNyaS5zYW5kYm94LWxvZy1kaXJlY3RvcnkiIGFubm90YXRpb24KYWxsb3dfc2FuZGJveF9sb2dfZGlyZWN0b3J5KHBfb2NpLCBpX29jaSwgc19uYW1lLCBzX25hbWVzcGFjZSkgewogICAgcHJpbnQoImFsbG93X3NhbmRib3hfbG9nX2RpcmVjdG9yeTogc3RhcnQiKQoKICAgIGtleSA6PSAiaW8ua3ViZXJuZXRlcy5jcmkuc2FuZGJveC1sb2ctZGlyZWN0b3J5IgoKICAgIHBfZGlyIDo9IHBfb2NpLkFubm90YXRpb25zW2tleV0KICAgIHJlZ2V4MSA6PSByZXBsYWNlKHBfZGlyLCAiJChzYW5kYm94LW5hbWUpIiwgc19uYW1lKQogICAgcmVnZXgyIDo9IHJlcGxhY2UocmVnZXgxLCAiJChzYW5kYm94LW5hbWVzcGFjZSkiLCBzX25hbWVzcGFjZSkKICAgIHByaW50KCJhbGxvd19zYW5kYm94X2xvZ19kaXJlY3Rvcnk6IHJlZ2V4MiA9IiwgcmVnZXgyKQoKICAgIGlfZGlyIDo9IGlfb2NpLkFubm90YXRpb25zW2tleV0KICAgIHByaW50KCJhbGxvd19zYW5kYm94X2xvZ19kaXJlY3Rvcnk6IGlfZGlyID0iLCBpX2RpcikKCiAgICByZWdleC5tYXRjaChyZWdleDIsIGlfZGlyKQoKICAgIHByaW50KCJhbGxvd19zYW5kYm94X2xvZ19kaXJlY3Rvcnk6IHRydWUiKQp9CgphbGxvd19sb2dfZGlyZWN0b3J5KHBfb2NpLCBpX29jaSkgewogICAgcHJpbnQoImFsbG93X2xvZ19kaXJlY3Rvcnk6IHN0YXJ0IikKCiAgICBrZXkgOj0gImlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtbG9nLWRpcmVjdG9yeSIKCiAgICBub3QgcF9vY2kuQW5ub3RhdGlvbnNba2V5XQogICAgbm90IGlfb2NpLkFubm90YXRpb25zW2tleV0KCiAgICBwcmludCgiYWxsb3dfbG9nX2RpcmVjdG9yeTogdHJ1ZSIpCn0KCmFsbG93X2xpbnV4KHBfb2NpLCBpX29jaSkgewogICAgcF9uYW1lc3BhY2VzIDo9IHBfb2NpLkxpbnV4Lk5hbWVzcGFjZXMKICAgIHByaW50KCJhbGxvd19saW51eDogcCBuYW1lc3BhY2VzID0iLCBwX25hbWVzcGFjZXMpCgogICAgaV9uYW1lc3BhY2VzIDo9IGlfb2NpLkxpbnV4Lk5hbWVzcGFjZXMKICAgIHByaW50KCJhbGxvd19saW51eDogaSBuYW1lc3BhY2VzID0iLCBpX25hbWVzcGFjZXMpCgogICAgcF9uYW1lc3BhY2VzID09IGlfbmFtZXNwYWNlcwoKICAgIGFsbG93X21hc2tlZF9wYXRocyhwX29jaSwgaV9vY2kpCiAgICBhbGxvd19yZWFkb25seV9wYXRocyhwX29jaSwgaV9vY2kpCgogICAgcHJpbnQoImFsbG93X2xpbnV4OiB0cnVlIikKfQoKYWxsb3dfbWFza2VkX3BhdGhzKHBfb2NpLCBpX29jaSkgewogICAgcF9wYXRocyA6PSBwX29jaS5MaW51eC5NYXNrZWRQYXRocwogICAgcHJpbnQoImFsbG93X21hc2tlZF9wYXRocyAxOiBwX3BhdGhzID0iLCBwX3BhdGhzKQoKICAgIGlfcGF0aHMgOj0gaV9vY2kuTGludXguTWFza2VkUGF0aHMKICAgIHByaW50KCJhbGxvd19tYXNrZWRfcGF0aHMgMTogaV9wYXRocyA9IiwgaV9wYXRocykKCiAgICBhbGxvd19tYXNrZWRfcGF0aHNfYXJyYXkocF9wYXRocywgaV9wYXRocykKCiAgICBwcmludCgiYWxsb3dfbWFza2VkX3BhdGhzIDE6IHRydWUiKQp9CmFsbG93X21hc2tlZF9wYXRocyhwX29jaSwgaV9vY2kpIHsKICAgIHByaW50KCJhbGxvd19tYXNrZWRfcGF0aHMgMjogc3RhcnQiKQoKICAgIG5vdCBwX29jaS5MaW51eC5NYXNrZWRQYXRocwogICAgbm90IGlfb2NpLkxpbnV4Lk1hc2tlZFBhdGhzCgogICAgcHJpbnQoImFsbG93X21hc2tlZF9wYXRocyAyOiB0cnVlIikKfQoKIyBBbGwgdGhlIHBvbGljeSBtYXNrZWQgcGF0aHMgbXVzdCBiZSBtYXNrZWQgaW4gdGhlIGlucHV0IGRhdGEgdG9vLgojIElucHV0IGlzIGFsbG93ZWQgdG8gaGF2ZSBtb3JlIG1hc2tlZCBwYXRocyB0aGFuIHRoZSBwb2xpY3kuCmFsbG93X21hc2tlZF9wYXRoc19hcnJheShwX2FycmF5LCBpX2FycmF5KSB7CiAgICBldmVyeSBwX2VsZW0gaW4gcF9hcnJheSB7CiAgICAgICAgYWxsb3dfbWFza2VkX3BhdGgocF9lbGVtLCBpX2FycmF5KQogICAgfQp9CgphbGxvd19tYXNrZWRfcGF0aChwX2VsZW0sIGlfYXJyYXkpIHsKICAgIHByaW50KCJhbGxvd19tYXNrZWRfcGF0aDogcF9lbGVtID0iLCBwX2VsZW0pCgogICAgc29tZSBpX2VsZW0gaW4gaV9hcnJheQogICAgcF9lbGVtID09IGlfZWxlbQoKICAgIHByaW50KCJhbGxvd19tYXNrZWRfcGF0aDogdHJ1ZSIpCn0KCmFsbG93X3JlYWRvbmx5X3BhdGhzKHBfb2NpLCBpX29jaSkgewogICAgcF9wYXRocyA6PSBwX29jaS5MaW51eC5SZWFkb25seVBhdGhzCiAgICBwcmludCgiYWxsb3dfcmVhZG9ubHlfcGF0aHMgMTogcF9wYXRocyA9IiwgcF9wYXRocykKCiAgICBpX3BhdGhzIDo9IGlfb2NpLkxpbnV4LlJlYWRvbmx5UGF0aHMKICAgIHByaW50KCJhbGxvd19yZWFkb25seV9wYXRocyAxOiBpX3BhdGhzID0iLCBpX3BhdGhzKQoKICAgIGFsbG93X3JlYWRvbmx5X3BhdGhzX2FycmF5KHBfcGF0aHMsIGlfcGF0aHMsIGlfb2NpLkxpbnV4Lk1hc2tlZFBhdGhzKQoKICAgIHByaW50KCJhbGxvd19yZWFkb25seV9wYXRocyAxOiB0cnVlIikKfQphbGxvd19yZWFkb25seV9wYXRocyhwX29jaSwgaV9vY2kpIHsKICAgIHByaW50KCJhbGxvd19yZWFkb25seV9wYXRocyAyOiBzdGFydCIpCgogICAgbm90IHBfb2NpLkxpbnV4LlJlYWRvbmx5UGF0aHMKICAgIG5vdCBpX29jaS5MaW51eC5SZWFkb25seVBhdGhzCgogICAgcHJpbnQoImFsbG93X3JlYWRvbmx5X3BhdGhzIDI6IHRydWUiKQp9CgojIEFsbCB0aGUgcG9saWN5IHJlYWRvbmx5IHBhdGhzIG11c3QgYmUgZWl0aGVyOgojIC0gUHJlc2VudCBpbiB0aGUgaW5wdXQgcmVhZG9ubHkgcGF0aHMsIG9yCiMgLSBQcmVzZW50IGluIHRoZSBpbnB1dCBtYXNrZWQgcGF0aHMuCiMgSW5wdXQgaXMgYWxsb3dlZCB0byBoYXZlIG1vcmUgcmVhZG9ubHkgcGF0aHMgdGhhbiB0aGUgcG9saWN5LgphbGxvd19yZWFkb25seV9wYXRoc19hcnJheShwX2FycmF5LCBpX2FycmF5LCBtYXNrZWRfcGF0aHMpIHsKICAgIGV2ZXJ5IHBfZWxlbSBpbiBwX2FycmF5IHsKICAgICAgICBhbGxvd19yZWFkb25seV9wYXRoKHBfZWxlbSwgaV9hcnJheSwgbWFza2VkX3BhdGhzKQogICAgfQp9CgphbGxvd19yZWFkb25seV9wYXRoKHBfZWxlbSwgaV9hcnJheSwgbWFza2VkX3BhdGhzKSB7CiAgICBwcmludCgiYWxsb3dfcmVhZG9ubHlfcGF0aCAxOiBwX2VsZW0gPSIsIHBfZWxlbSkKCiAgICBzb21lIGlfZWxlbSBpbiBpX2FycmF5CiAgICBwX2VsZW0gPT0gaV9lbGVtCgogICAgcHJpbnQoImFsbG93X3JlYWRvbmx5X3BhdGggMTogdHJ1ZSIpCn0KYWxsb3dfcmVhZG9ubHlfcGF0aChwX2VsZW0sIGlfYXJyYXksIG1hc2tlZF9wYXRocykgewogICAgcHJpbnQoImFsbG93X3JlYWRvbmx5X3BhdGggMjogcF9lbGVtID0iLCBwX2VsZW0pCgogICAgc29tZSBpX21hc2tlZCBpbiBtYXNrZWRfcGF0aHMKICAgIHBfZWxlbSA9PSBpX21hc2tlZAoKICAgIHByaW50KCJhbGxvd19yZWFkb25seV9wYXRoIDI6IHRydWUiKQp9CgojIENoZWNrIHRoZSBjb25zaXN0ZW5jeSBvZiB0aGUgaW5wdXQgImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuYnVuZGxlX3BhdGgiCiMgYW5kIGlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtaWQiIHZhbHVlcyB3aXRoIG90aGVyIGZpZWxkcy4KYWxsb3dfYnlfYnVuZGxlX29yX3NhbmRib3hfaWQocF9vY2ksIGlfb2NpLCBwX3N0b3JhZ2VzLCBpX3N0b3JhZ2VzKSB7CiAgICBwcmludCgiYWxsb3dfYnlfYnVuZGxlX29yX3NhbmRib3hfaWQ6IHN0YXJ0IikKCiAgICBidW5kbGVfcGF0aCA6PSBpX29jaS5Bbm5vdGF0aW9uc1siaW8ua2F0YWNvbnRhaW5lcnMucGtnLm9jaS5idW5kbGVfcGF0aCJdCiAgICBidW5kbGVfaWQgOj0gcmVwbGFjZShidW5kbGVfcGF0aCwgIi9ydW4vY29udGFpbmVyZC9pby5jb250YWluZXJkLnJ1bnRpbWUudjIudGFzay9rOHMuaW8vIiwgIiIpCgogICAga2V5IDo9ICJpby5rdWJlcm5ldGVzLmNyaS5zYW5kYm94LWlkIgoKICAgIHBfcmVnZXggOj0gcF9vY2kuQW5ub3RhdGlvbnNba2V5XQogICAgc2FuZGJveF9pZCA6PSBpX29jaS5Bbm5vdGF0aW9uc1trZXldCgogICAgcHJpbnQoImFsbG93X2J5X2J1bmRsZV9vcl9zYW5kYm94X2lkOiBzYW5kYm94X2lkID0iLCBzYW5kYm94X2lkLCAicmVnZXggPSIsIHBfcmVnZXgpCiAgICByZWdleC5tYXRjaChwX3JlZ2V4LCBzYW5kYm94X2lkKQoKICAgIGFsbG93X3Jvb3RfcGF0aChwX29jaSwgaV9vY2ksIGJ1bmRsZV9pZCkKCiAgICBldmVyeSBpX21vdW50IGluIGlucHV0Lk9DSS5Nb3VudHMgewogICAgICAgIGFsbG93X21vdW50KHBfb2NpLCBpX21vdW50LCBidW5kbGVfaWQsIHNhbmRib3hfaWQpCiAgICB9CgogICAgYWxsb3dfc3RvcmFnZXMocF9zdG9yYWdlcywgaV9zdG9yYWdlcywgYnVuZGxlX2lkLCBzYW5kYm94X2lkKQoKICAgIHByaW50KCJhbGxvd19ieV9idW5kbGVfb3Jfc2FuZGJveF9pZDogdHJ1ZSIpCn0KCmFsbG93X3Byb2Nlc3MocF9vY2ksIGlfb2NpLCBzX25hbWUpIHsKICAgIHBfcHJvY2VzcyA6PSBwX29jaS5Qcm9jZXNzCiAgICBpX3Byb2Nlc3MgOj0gaV9vY2kuUHJvY2VzcwoKICAgIHByaW50KCJhbGxvd19wcm9jZXNzOiBpIHRlcm1pbmFsID0iLCBpX3Byb2Nlc3MuVGVybWluYWwsICJwIHRlcm1pbmFsID0iLCBwX3Byb2Nlc3MuVGVybWluYWwpCiAgICBwX3Byb2Nlc3MuVGVybWluYWwgPT0gaV9wcm9jZXNzLlRlcm1pbmFsCgogICAgcHJpbnQoImFsbG93X3Byb2Nlc3M6IGkgY3dkID0iLCBpX3Byb2Nlc3MuQ3dkLCAiaSBjd2QgPSIsIHBfcHJvY2Vzcy5Dd2QpCiAgICBwX3Byb2Nlc3MuQ3dkID09IGlfcHJvY2Vzcy5Dd2QKCiAgICBwcmludCgiYWxsb3dfcHJvY2VzczogaSBub05ld1ByaXZpbGVnZXMgPSIsIGlfcHJvY2Vzcy5Ob05ld1ByaXZpbGVnZXMsICJwIG5vTmV3UHJpdmlsZWdlcyA9IiwgcF9wcm9jZXNzLk5vTmV3UHJpdmlsZWdlcykKICAgIHBfcHJvY2Vzcy5Ob05ld1ByaXZpbGVnZXMgPT0gaV9wcm9jZXNzLk5vTmV3UHJpdmlsZWdlcwoKICAgIGFsbG93X2NhcHMocF9wcm9jZXNzLkNhcGFiaWxpdGllcywgaV9wcm9jZXNzLkNhcGFiaWxpdGllcykKICAgIGFsbG93X3VzZXIocF9wcm9jZXNzLCBpX3Byb2Nlc3MpCiAgICBhbGxvd19hcmdzKHBfcHJvY2VzcywgaV9wcm9jZXNzLCBzX25hbWUpCiAgICBhbGxvd19lbnYocF9wcm9jZXNzLCBpX3Byb2Nlc3MsIHNfbmFtZSkKCiAgICBwcmludCgiYWxsb3dfcHJvY2VzczogdHJ1ZSIpCn0KCmFsbG93X3VzZXIocF9wcm9jZXNzLCBpX3Byb2Nlc3MpIHsKICAgIHBfdXNlciA6PSBwX3Byb2Nlc3MuVXNlcgogICAgaV91c2VyIDo9IGlfcHJvY2Vzcy5Vc2VyCgogICAgIyBUT0RPOiB0cmFjayBkb3duIHRoZSByZWFzb24gZm9yIG1jci5taWNyb3NvZnQuY29tL29zcy9iaXRuYW1pL3JlZGlzOjYuMC44IGJlaW5nCiAgICAjICAgICAgIGV4ZWN1dGVkIHdpdGggdWlkID0gMCBkZXNwaXRlIGhhdmluZyAiVXNlciI6ICIxMDAxIiBpbiBpdHMgY29udGFpbmVyIGltYWdlCiAgICAjICAgICAgIGNvbmZpZy4KICAgICNwcmludCgiYWxsb3dfdXNlcjogaW5wdXQgdWlkID0iLCBpX3VzZXIuVUlELCAicG9saWN5IHVpZCA9IiwgcF91c2VyLlVJRCkKICAgICNwX3VzZXIuVUlEID09IGlfdXNlci5VSUQKCiAgICAjIFRPRE86IHRyYWNrIGRvd24gdGhlIHJlYXNvbiBmb3IgcmVnaXN0cnkuazhzLmlvL3BhdXNlOjMuOSBiZWluZwogICAgIyAgICAgICBleGVjdXRlZCB3aXRoIGdpZCA9IDAgZGVzcGl0ZSBoYXZpbmcgIjY1NTM1OjY1NTM1IiBpbiBpdHMgY29udGFpbmVyIGltYWdlCiAgICAjICAgICAgIGNvbmZpZy4KICAgICNwcmludCgiYWxsb3dfdXNlcjogaW5wdXQgZ2lkID0iLCBpX3VzZXIuR0lELCAicG9saWN5IGdpZCA9IiwgcF91c2VyLkdJRCkKICAgICNwX3VzZXIuR0lEID09IGlfdXNlci5HSUQKCiAgICAjIFRPRE86IGNvbXBhcmUgdGhlIGFkZGl0aW9uYWxHaWRzIGZpZWxkIHRvbyBhZnRlciBjb21wdXRpbmcgaXRzIHZhbHVlCiAgICAjIGJhc2VkIG9uIC9ldGMvcGFzc3dkIGFuZCAvZXRjL2dyb3VwIGZyb20gdGhlIGNvbnRhaW5lciBpbWFnZS4KfQoKYWxsb3dfYXJncyhwX3Byb2Nlc3MsIGlfcHJvY2Vzcywgc19uYW1lKSB7CiAgICBwcmludCgiYWxsb3dfYXJncyAxOiBubyBhcmdzIikKCiAgICBub3QgcF9wcm9jZXNzLkFyZ3MKICAgIG5vdCBpX3Byb2Nlc3MuQXJncwoKICAgIHByaW50KCJhbGxvd19hcmdzIDE6IHRydWUiKQp9CmFsbG93X2FyZ3MocF9wcm9jZXNzLCBpX3Byb2Nlc3MsIHNfbmFtZSkgewogICAgcHJpbnQoImFsbG93X2FyZ3MgMjogcG9saWN5IGFyZ3MgPSIsIHBfcHJvY2Vzcy5BcmdzKQogICAgcHJpbnQoImFsbG93X2FyZ3MgMjogaW5wdXQgYXJncyA9IiwgaV9wcm9jZXNzLkFyZ3MpCgogICAgY291bnQocF9wcm9jZXNzLkFyZ3MpID09IGNvdW50KGlfcHJvY2Vzcy5BcmdzKQoKICAgIGV2ZXJ5IGksIGlfYXJnIGluIGlfcHJvY2Vzcy5BcmdzIHsKICAgICAgICBhbGxvd19hcmcoaSwgaV9hcmcsIHBfcHJvY2Vzcywgc19uYW1lKQogICAgfQoKICAgIHByaW50KCJhbGxvd19hcmdzIDI6IHRydWUiKQp9CmFsbG93X2FyZyhpLCBpX2FyZywgcF9wcm9jZXNzLCBzX25hbWUpIHsKICAgIHBfYXJnIDo9IHBfcHJvY2Vzcy5BcmdzW2ldCiAgICBwcmludCgiYWxsb3dfYXJnIDE6IGkgPSIsIGksICJpX2FyZyA9IiwgaV9hcmcsICJwX2FyZyA9IiwgcF9hcmcpCgogICAgcF9hcmcyIDo9IHJlcGxhY2UocF9hcmcsICIkJCIsICIkIikKICAgIHBfYXJnMiA9PSBpX2FyZwoKICAgIHByaW50KCJhbGxvd19hcmcgMTogdHJ1ZSIpCn0KYWxsb3dfYXJnKGksIGlfYXJnLCBwX3Byb2Nlc3MsIHNfbmFtZSkgewogICAgcF9hcmcgOj0gcF9wcm9jZXNzLkFyZ3NbaV0KICAgIHByaW50KCJhbGxvd19hcmcgMjogaSA9IiwgaSwgImlfYXJnID0iLCBpX2FyZywgInBfYXJnID0iLCBwX2FyZykKCiAgICAjIFRPRE86IGNhbiAkKG5vZGUtbmFtZSkgYmUgaGFuZGxlZCBiZXR0ZXI/CiAgICBjb250YWlucyhwX2FyZywgIiQobm9kZS1uYW1lKSIpCgogICAgcHJpbnQoImFsbG93X2FyZyAyOiB0cnVlIikKfQphbGxvd19hcmcoaSwgaV9hcmcsIHBfcHJvY2Vzcywgc19uYW1lKSB7CiAgICBwX2FyZyA6PSBwX3Byb2Nlc3MuQXJnc1tpXQogICAgcHJpbnQoImFsbG93X2FyZyAzOiBpID0iLCBpLCAiaV9hcmcgPSIsIGlfYXJnLCAicF9hcmcgPSIsIHBfYXJnKQoKICAgIHBfYXJnMiA6PSByZXBsYWNlKHBfYXJnLCAiJCQiLCAiJCIpCiAgICBwX2FyZzMgOj0gcmVwbGFjZShwX2FyZzIsICIkKHNhbmRib3gtbmFtZSkiLCBzX25hbWUpCiAgICBwcmludCgiYWxsb3dfYXJnIDM6IHBfYXJnMyA9IiwgcF9hcmczKQogICAgcF9hcmczID09IGlfYXJnCgogICAgcHJpbnQoImFsbG93X2FyZyAzOiB0cnVlIikKfQoKIyBPQ0kgcHJvY2Vzcy5FbnYgZmllbGQKYWxsb3dfZW52KHBfcHJvY2VzcywgaV9wcm9jZXNzLCBzX25hbWUpIHsKICAgIHByaW50KCJhbGxvd19lbnY6IHAgZW52ID0iLCBwX3Byb2Nlc3MuRW52KQogICAgcHJpbnQoImFsbG93X2VudjogaSBlbnYgPSIsIGlfcHJvY2Vzcy5FbnYpCgogICAgZXZlcnkgaV92YXIgaW4gaV9wcm9jZXNzLkVudiB7CiAgICAgICAgYWxsb3dfdmFyKHBfcHJvY2VzcywgaV9wcm9jZXNzLCBpX3Zhciwgc19uYW1lKQogICAgfQoKICAgIHByaW50KCJhbGxvd19lbnY6IHRydWUiKQp9CgojIEFsbG93IGlucHV0IGVudiB2YXJpYWJsZXMgdGhhdCBhcmUgcHJlc2VudCBpbiB0aGUgcG9saWN5IGRhdGEgdG9vLgphbGxvd192YXIocF9wcm9jZXNzLCBpX3Byb2Nlc3MsIGlfdmFyLCBzX25hbWUpIHsKICAgIHByaW50KCJhbGxvd192YXIgMTogaV92YXIgPSIsIGlfdmFyKQoKICAgIHNvbWUgcF92YXIgaW4gcF9wcm9jZXNzLkVudgogICAgcF92YXIgPT0gaV92YXIKCiAgICBwcmludCgiYWxsb3dfdmFyIDE6IHRydWUiKQp9CgojIE1hdGNoIGlucHV0IHdpdGggb25lIG9mIHRoZSBwb2xpY3kgdmFyaWFibGVzLCBhZnRlciBzdWJzdGl0dXRpbmcgJChzYW5kYm94LW5hbWUpLgphbGxvd192YXIocF9wcm9jZXNzLCBpX3Byb2Nlc3MsIGlfdmFyLCBzX25hbWUpIHsKICAgIHByaW50KCJhbGxvd192YXIgMjogaV92YXIgPSIsIGlfdmFyKQoKICAgIHNvbWUgcF92YXIgaW4gcF9wcm9jZXNzLkVudgogICAgcF92YXIyIDo9IHJlcGxhY2UocF92YXIsICIkKHNhbmRib3gtbmFtZSkiLCBzX25hbWUpCiAgICBwcmludCgiYWxsb3dfdmFyIDI6IHBfdmFyMiA9IiwgcF92YXIyKQoKICAgIHBfdmFyMiA9PSBpX3ZhcgoKICAgIHByaW50KCJhbGxvd192YXIgMjogdHJ1ZSIpCn0KCiMgQWxsb3cgaW5wdXQgZW52IHZhcmlhYmxlcyB0aGF0IG1hdGNoIHdpdGggYSByZXF1ZXN0X2RlZmF1bHRzIHJlZ2V4LgphbGxvd192YXIocF9wcm9jZXNzLCBpX3Byb2Nlc3MsIGlfdmFyLCBzX25hbWUpIHsKICAgIHByaW50KCJhbGxvd192YXIgMzogc3RhcnQiKQoKICAgIHNvbWUgcF9yZWdleDEgaW4gcG9saWN5X2RhdGEucmVxdWVzdF9kZWZhdWx0cy5DcmVhdGVDb250YWluZXJSZXF1ZXN0LmFsbG93X2Vudl9yZWdleAogICAgcHJpbnQoImFsbG93X3ZhciAzOiBwX3JlZ2V4MSA9IiwgcF9yZWdleDEpCgogICAgcF9yZWdleDIgOj0gcmVwbGFjZShwX3JlZ2V4MSwgIiQoaXB2NF9hKSIsIHBvbGljeV9kYXRhLmNvbW1vbi5pcHY0X2EpCiAgICBwcmludCgiYWxsb3dfdmFyIDM6IHBfcmVnZXgyID0iLCBwX3JlZ2V4MikKCiAgICBwX3JlZ2V4MyA6PSByZXBsYWNlKHBfcmVnZXgyLCAiJChpcF9wKSIsIHBvbGljeV9kYXRhLmNvbW1vbi5pcF9wKQogICAgcHJpbnQoImFsbG93X3ZhciAzOiBwX3JlZ2V4MyA9IiwgcF9yZWdleDMpCgogICAgcF9yZWdleDQgOj0gcmVwbGFjZShwX3JlZ2V4MywgIiQoc3ZjX25hbWUpIiwgcG9saWN5X2RhdGEuY29tbW9uLnN2Y19uYW1lKQogICAgcHJpbnQoImFsbG93X3ZhciAzOiBwX3JlZ2V4NCA9IiwgcF9yZWdleDQpCgogICAgcF9yZWdleDUgOj0gcmVwbGFjZShwX3JlZ2V4NCwgIiQoZG5zX2xhYmVsKSIsIHBvbGljeV9kYXRhLmNvbW1vbi5kbnNfbGFiZWwpCiAgICBwcmludCgiYWxsb3dfdmFyIDM6IHBfcmVnZXg1ID0iLCBwX3JlZ2V4NSkKCiAgICBwcmludCgiYWxsb3dfdmFyIDM6IGlfdmFyID0iLCBpX3ZhcikKICAgIHJlZ2V4Lm1hdGNoKHBfcmVnZXg1LCBpX3ZhcikKCiAgICBwcmludCgiYWxsb3dfdmFyIDM6IHRydWUiKQp9CgojIEFsbG93IGZpZWxkUmVmICJmaWVsZFBhdGg6IHN0YXR1cy5wb2RJUCIgdmFsdWVzLgphbGxvd192YXIocF9wcm9jZXNzLCBpX3Byb2Nlc3MsIGlfdmFyLCBzX25hbWUpIHsKICAgIHByaW50KCJhbGxvd192YXIgNDogaV92YXIgPSIsIGlfdmFyKQoKICAgIG5hbWVfdmFsdWUgOj0gc3BsaXQoaV92YXIsICI9IikKICAgIGNvdW50KG5hbWVfdmFsdWUpID09IDIKICAgIGlzX2lwKG5hbWVfdmFsdWVbMV0pCgogICAgc29tZSBwX3ZhciBpbiBwX3Byb2Nlc3MuRW52CiAgICBhbGxvd19wb2RfaXBfdmFyKG5hbWVfdmFsdWVbMF0sIHBfdmFyKQoKICAgIHByaW50KCJhbGxvd192YXIgNDogdHJ1ZSIpCn0KCiMgQWxsb3cgY29tbW9uIGZpZWxkUmVmIHZhcmlhYmxlcy4KYWxsb3dfdmFyKHBfcHJvY2VzcywgaV9wcm9jZXNzLCBpX3Zhciwgc19uYW1lKSB7CiAgICBwcmludCgiYWxsb3dfdmFyIDU6IGlfdmFyID0iLCBpX3ZhcikKCiAgICBuYW1lX3ZhbHVlIDo9IHNwbGl0KGlfdmFyLCAiPSIpCiAgICBjb3VudChuYW1lX3ZhbHVlKSA9PSAyCgogICAgc29tZSBwX3ZhciBpbiBwX3Byb2Nlc3MuRW52CiAgICBwX25hbWVfdmFsdWUgOj0gc3BsaXQocF92YXIsICI9IikKICAgIGNvdW50KHBfbmFtZV92YWx1ZSkgPT0gMgoKICAgIHBfbmFtZV92YWx1ZVswXSA9PSBuYW1lX3ZhbHVlWzBdCgogICAgIyBUT0RPOiBzaG91bGQgdGhlc2UgYmUgaGFuZGxlZCBpbiBhIGRpZmZlcmVudCB3YXk/CiAgICBhbHdheXNfYWxsb3dlZCA6PSBbIiQoaG9zdC1uYW1lKSIsICIkKG5vZGUtbmFtZSkiLCAiJChwb2QtdWlkKSJdCiAgICBzb21lIGFsbG93ZWQgaW4gYWx3YXlzX2FsbG93ZWQKICAgIGNvbnRhaW5zKHBfbmFtZV92YWx1ZVsxXSwgYWxsb3dlZCkKCiAgICBwcmludCgiYWxsb3dfdmFyIDU6IHRydWUiKQp9CgojIEFsbG93IGZpZWxkUmVmICJmaWVsZFBhdGg6IHN0YXR1cy5ob3N0SVAiIHZhbHVlcy4KYWxsb3dfdmFyKHBfcHJvY2VzcywgaV9wcm9jZXNzLCBpX3Zhciwgc19uYW1lKSB7CiAgICBwcmludCgiYWxsb3dfdmFyIDY6IGlfdmFyID0iLCBpX3ZhcikKCiAgICBuYW1lX3ZhbHVlIDo9IHNwbGl0KGlfdmFyLCAiPSIpCiAgICBjb3VudChuYW1lX3ZhbHVlKSA9PSAyCiAgICBpc19pcChuYW1lX3ZhbHVlWzFdKQoKICAgIHNvbWUgcF92YXIgaW4gcF9wcm9jZXNzLkVudgogICAgYWxsb3dfaG9zdF9pcF92YXIobmFtZV92YWx1ZVswXSwgcF92YXIpCgogICAgcHJpbnQoImFsbG93X3ZhciA2OiB0cnVlIikKfQoKIyBBbGxvdyByZXNvdXJjZUZpZWxkUmVmIHZhbHVlcyAoZS5nLiwgImxpbWl0cy5jcHUiKS4KYWxsb3dfdmFyKHBfcHJvY2VzcywgaV9wcm9jZXNzLCBpX3Zhciwgc19uYW1lKSB7CiAgICBwcmludCgiYWxsb3dfdmFyIDc6IGlfdmFyID0iLCBpX3ZhcikKCiAgICBuYW1lX3ZhbHVlIDo9IHNwbGl0KGlfdmFyLCAiPSIpCiAgICBjb3VudChuYW1lX3ZhbHVlKSA9PSAyCgogICAgc29tZSBwX3ZhciBpbiBwX3Byb2Nlc3MuRW52CiAgICBwX25hbWVfdmFsdWUgOj0gc3BsaXQocF92YXIsICI9IikKICAgIGNvdW50KHBfbmFtZV92YWx1ZSkgPT0gMgoKICAgIHBfbmFtZV92YWx1ZVswXSA9PSBuYW1lX3ZhbHVlWzBdCgogICAgIyBUT0RPOiBzaG91bGQgdGhlc2UgYmUgaGFuZGxlZCBpbiBhIGRpZmZlcmVudCB3YXk/CiAgICBhbHdheXNfYWxsb3dlZCA9IFsiJChyZXNvdXJjZS1maWVsZCkiLCAiJCh0b2RvLWFubm90YXRpb24pIl0KICAgIHNvbWUgYWxsb3dlZCBpbiBhbHdheXNfYWxsb3dlZAogICAgY29udGFpbnMocF9uYW1lX3ZhbHVlWzFdLCBhbGxvd2VkKQoKICAgIHByaW50KCJhbGxvd192YXIgNzogdHJ1ZSIpCn0KCmFsbG93X3BvZF9pcF92YXIodmFyX25hbWUsIHBfdmFyKSB7CiAgICBwcmludCgiYWxsb3dfcG9kX2lwX3ZhcjogdmFyX25hbWUgPSIsIHZhcl9uYW1lLCAicF92YXIgPSIsIHBfdmFyKQoKICAgIHBfbmFtZV92YWx1ZSA6PSBzcGxpdChwX3ZhciwgIj0iKQogICAgY291bnQocF9uYW1lX3ZhbHVlKSA9PSAyCgogICAgcF9uYW1lX3ZhbHVlWzBdID09IHZhcl9uYW1lCiAgICBwX25hbWVfdmFsdWVbMV0gPT0gIiQocG9kLWlwKSIKCiAgICBwcmludCgiYWxsb3dfcG9kX2lwX3ZhcjogdHJ1ZSIpCn0KCmFsbG93X2hvc3RfaXBfdmFyKHZhcl9uYW1lLCBwX3ZhcikgewogICAgcHJpbnQoImFsbG93X2hvc3RfaXBfdmFyOiB2YXJfbmFtZSA9IiwgdmFyX25hbWUsICJwX3ZhciA9IiwgcF92YXIpCgogICAgcF9uYW1lX3ZhbHVlIDo9IHNwbGl0KHBfdmFyLCAiPSIpCiAgICBjb3VudChwX25hbWVfdmFsdWUpID09IDIKCiAgICBwX25hbWVfdmFsdWVbMF0gPT0gdmFyX25hbWUKICAgIHBfbmFtZV92YWx1ZVsxXSA9PSAiJChob3N0LWlwKSIKCiAgICBwcmludCgiYWxsb3dfaG9zdF9pcF92YXI6IHRydWUiKQp9Cgppc19pcCh2YWx1ZSkgewogICAgYnl0ZXMgPSBzcGxpdCh2YWx1ZSwgIi4iKQogICAgY291bnQoYnl0ZXMpID09IDQKCiAgICBpc19pcF9maXJzdF9ieXRlKGJ5dGVzWzBdKQogICAgaXNfaXBfb3RoZXJfYnl0ZShieXRlc1sxXSkKICAgIGlzX2lwX290aGVyX2J5dGUoYnl0ZXNbMl0pCiAgICBpc19pcF9vdGhlcl9ieXRlKGJ5dGVzWzNdKQp9CmlzX2lwX2ZpcnN0X2J5dGUoY29tcG9uZW50KSB7CiAgICBudW1iZXIgPSB0b19udW1iZXIoY29tcG9uZW50KQogICAgbnVtYmVyID49IDEKICAgIG51bWJlciA8PSAyNTUKfQppc19pcF9vdGhlcl9ieXRlKGNvbXBvbmVudCkgewogICAgbnVtYmVyID0gdG9fbnVtYmVyKGNvbXBvbmVudCkKICAgIG51bWJlciA+PSAwCiAgICBudW1iZXIgPD0gMjU1Cn0KCiMgT0NJIHJvb3QuUGF0aAphbGxvd19yb290X3BhdGgocF9vY2ksIGlfb2NpLCBidW5kbGVfaWQpIHsKICAgIHBfcGF0aDEgOj0gcF9vY2kuUm9vdC5QYXRoCiAgICBwcmludCgiYWxsb3dfcm9vdF9wYXRoOiBwX3BhdGgxID0iLCBwX3BhdGgxKQoKICAgIHBfcGF0aDIgOj0gcmVwbGFjZShwX3BhdGgxLCAiJChjcGF0aCkiLCBwb2xpY3lfZGF0YS5jb21tb24uY3BhdGgpCiAgICBwcmludCgiYWxsb3dfcm9vdF9wYXRoOiBwX3BhdGgyID0iLCBwX3BhdGgyKQoKICAgIHBfcGF0aDMgOj0gcmVwbGFjZShwX3BhdGgyLCAiJChidW5kbGUtaWQpIiwgYnVuZGxlX2lkKQogICAgcHJpbnQoImFsbG93X3Jvb3RfcGF0aDogcF9wYXRoMyA9IiwgcF9wYXRoMykKCiAgICBwX3BhdGgzID09IGlfb2NpLlJvb3QuUGF0aAoKICAgIHByaW50KCJhbGxvd19yb290X3BhdGg6IHRydWUiKQp9CgojIGRldmljZSBtb3VudHMKYWxsb3dfbW91bnQocF9vY2ksIGlfbW91bnQsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCkgewogICAgcHJpbnQoImFsbG93X21vdW50OiBzdGFydCIpCgogICAgc29tZSBwX21vdW50IGluIHBfb2NpLk1vdW50cwogICAgY2hlY2tfbW91bnQocF9tb3VudCwgaV9tb3VudCwgYnVuZGxlX2lkLCBzYW5kYm94X2lkKQoKICAgICMgVE9ETzogYXJlIHRoZXJlIGFueSBvdGhlciByZXF1aXJlZCBwb2xpY3kgY2hlY2tzIGZvciBtb3VudHMgLSBlLmcuLAogICAgIyAgICAgICBtdWx0aXBsZSBtb3VudHMgd2l0aCBzYW1lIHNvdXJjZSBvciBkZXN0aW5hdGlvbj8KCiAgICBwcmludCgiYWxsb3dfbW91bnQ6IHRydWUiKQp9CgpjaGVja19tb3VudChwX21vdW50LCBpX21vdW50LCBidW5kbGVfaWQsIHNhbmRib3hfaWQpIHsKICAgIHByaW50KCJjaGVja19tb3VudCAxOiBwX21vdW50ID0iLCBwX21vdW50KQogICAgcHJpbnQoImNoZWNrX21vdW50IDE6IGlfbW91bnQgPSIsIGlfbW91bnQpCgogICAgcF9tb3VudCA9PSBpX21vdW50CgogICAgcHJpbnQoImNoZWNrX21vdW50IDE6IHRydWUiKQp9CmNoZWNrX21vdW50KHBfbW91bnQsIGlfbW91bnQsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCkgewogICAgcHJpbnQoImNoZWNrX21vdW50IDI6IGkgZGVzdGluYXRpb24gPSIsIGlfbW91bnQuZGVzdGluYXRpb24sICJwIGRlc3RpbmF0aW9uID0iLCBwX21vdW50LmRlc3RpbmF0aW9uKQogICAgcF9tb3VudC5kZXN0aW5hdGlvbiA9PSBpX21vdW50LmRlc3RpbmF0aW9uCgogICAgcHJpbnQoImNoZWNrX21vdW50IDI6IGkgdHlwZSA9IiwgaV9tb3VudC50eXBlXywgInAgdHlwZSA9IiwgcF9tb3VudC50eXBlXykKICAgIHBfbW91bnQudHlwZV8gPT0gaV9tb3VudC50eXBlXwoKICAgIHByaW50KCJjaGVja19tb3VudCAyOiBpIG9wdGlvbnMgPSIsIGlfbW91bnQub3B0aW9ucykKICAgIHByaW50KCJjaGVja19tb3VudCAyOiBwIG9wdGlvbnMgPSIsIHBfbW91bnQub3B0aW9ucykKICAgIHBfbW91bnQub3B0aW9ucyA9PSBpX21vdW50Lm9wdGlvbnMKCiAgICBtb3VudF9zb3VyY2VfYWxsb3dzKHBfbW91bnQsIGlfbW91bnQsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCkKCiAgICBwcmludCgiY2hlY2tfbW91bnQgMjogdHJ1ZSIpCn0KCm1vdW50X3NvdXJjZV9hbGxvd3MocF9tb3VudCwgaV9tb3VudCwgYnVuZGxlX2lkLCBzYW5kYm94X2lkKSB7CiAgICBwcmludCgibW91bnRfc291cmNlX2FsbG93cyAxOiBpX21vdW50LnNvdXJjZSA9IiwgaV9tb3VudC5zb3VyY2UpCgogICAgcmVnZXgxIDo9IHBfbW91bnQuc291cmNlCiAgICBwcmludCgibW91bnRfc291cmNlX2FsbG93cyAxOiByZWdleDEgPSIsIHJlZ2V4MSkKCiAgICByZWdleDIgOj0gcmVwbGFjZShyZWdleDEsICIkKHNmcHJlZml4KSIsIHBvbGljeV9kYXRhLmNvbW1vbi5zZnByZWZpeCkKICAgIHByaW50KCJtb3VudF9zb3VyY2VfYWxsb3dzIDE6IHJlZ2V4MiA9IiwgcmVnZXgyKQoKICAgIHJlZ2V4MyA6PSByZXBsYWNlKHJlZ2V4MiwgIiQoY3BhdGgpIiwgcG9saWN5X2RhdGEuY29tbW9uLmNwYXRoKQogICAgcHJpbnQoIm1vdW50X3NvdXJjZV9hbGxvd3MgMTogcmVnZXgzID0iLCByZWdleDMpCgogICAgcmVnZXg0IDo9IHJlcGxhY2UocmVnZXgzLCAiJChidW5kbGUtaWQpIiwgYnVuZGxlX2lkKQogICAgcHJpbnQoIm1vdW50X3NvdXJjZV9hbGxvd3MgMTogcmVnZXg0ID0iLCByZWdleDQpCgogICAgcmVnZXgubWF0Y2gocmVnZXg0LCBpX21vdW50LnNvdXJjZSkKCiAgICBwcmludCgibW91bnRfc291cmNlX2FsbG93cyAxOiB0cnVlIikKfQptb3VudF9zb3VyY2VfYWxsb3dzKHBfbW91bnQsIGlfbW91bnQsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCkgewogICAgcHJpbnQoIm1vdW50X3NvdXJjZV9hbGxvd3MgMjogaV9tb3VudC5zb3VyY2U9IiwgaV9tb3VudC5zb3VyY2UpCgogICAgcmVnZXgxIDo9IHBfbW91bnQuc291cmNlCiAgICBwcmludCgibW91bnRfc291cmNlX2FsbG93cyAyOiByZWdleDEgPSIsIHJlZ2V4MSkKCiAgICByZWdleDIgOj0gcmVwbGFjZShyZWdleDEsICIkKHNmcHJlZml4KSIsIHBvbGljeV9kYXRhLmNvbW1vbi5zZnByZWZpeCkKICAgIHByaW50KCJtb3VudF9zb3VyY2VfYWxsb3dzIDI6IHJlZ2V4MiA9IiwgcmVnZXgyKQoKICAgIHJlZ2V4MyA6PSByZXBsYWNlKHJlZ2V4MiwgIiQoY3BhdGgpIiwgcG9saWN5X2RhdGEuY29tbW9uLmNwYXRoKQogICAgcHJpbnQoIm1vdW50X3NvdXJjZV9hbGxvd3MgMjogcmVnZXgzID0iLCByZWdleDMpCgogICAgcmVnZXg0IDo9IHJlcGxhY2UocmVnZXgzLCAiJChzYW5kYm94LWlkKSIsIHNhbmRib3hfaWQpCiAgICBwcmludCgibW91bnRfc291cmNlX2FsbG93cyAyOiByZWdleDQgPSIsIHJlZ2V4NCkKCiAgICByZWdleC5tYXRjaChyZWdleDQsIGlfbW91bnQuc291cmNlKQoKICAgIHByaW50KCJtb3VudF9zb3VyY2VfYWxsb3dzIDI6IHRydWUiKQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgU3RvcmFnZXMKCmFsbG93X3N0b3JhZ2VzKHBfc3RvcmFnZXMsIGlfc3RvcmFnZXMsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCkgewogICAgcF9jb3VudCA6PSBjb3VudChwX3N0b3JhZ2VzKQogICAgaV9jb3VudCA6PSBjb3VudChpX3N0b3JhZ2VzKQogICAgcHJpbnQoImFsbG93X3N0b3JhZ2VzOiBwX2NvdW50ID0iLCBwX2NvdW50LCAiaV9jb3VudCA9IiwgaV9jb3VudCkKCiAgICBwX2NvdW50ID09IGlfY291bnQKCiAgICAjIEdldCB0aGUgY29udGFpbmVyIGltYWdlIGxheWVyIElEcyBhbmQgdmVyaXR5IHJvb3QgaGFzaGVzLCBmcm9tIHRoZSAib3ZlcmxheWZzIiBzdG9yYWdlLgogICAgc29tZSBvdmVybGF5X3N0b3JhZ2UgaW4gcF9zdG9yYWdlcwogICAgb3ZlcmxheV9zdG9yYWdlLmRyaXZlciA9PSAib3ZlcmxheWZzIgogICAgcHJpbnQoImFsbG93X3N0b3JhZ2VzOiBvdmVybGF5X3N0b3JhZ2UgPSIsIG92ZXJsYXlfc3RvcmFnZSkKICAgIGNvdW50KG92ZXJsYXlfc3RvcmFnZS5vcHRpb25zKSA9PSAyCgogICAgbGF5ZXJfaWRzIDo9IHNwbGl0KG92ZXJsYXlfc3RvcmFnZS5vcHRpb25zWzBdLCAiOiIpCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZXM6IGxheWVyX2lkcyA9IiwgbGF5ZXJfaWRzKQoKICAgIHJvb3RfaGFzaGVzIDo9IHNwbGl0KG92ZXJsYXlfc3RvcmFnZS5vcHRpb25zWzFdLCAiOiIpCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZXM6IHJvb3RfaGFzaGVzID0iLCByb290X2hhc2hlcykKCiAgICBldmVyeSBpX3N0b3JhZ2UgaW4gaV9zdG9yYWdlcyB7CiAgICAgICAgYWxsb3dfc3RvcmFnZShwX3N0b3JhZ2VzLCBpX3N0b3JhZ2UsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCwgbGF5ZXJfaWRzLCByb290X2hhc2hlcykKICAgIH0KCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZXM6IHRydWUiKQp9CgphbGxvd19zdG9yYWdlKHBfc3RvcmFnZXMsIGlfc3RvcmFnZSwgYnVuZGxlX2lkLCBzYW5kYm94X2lkLCBsYXllcl9pZHMsIHJvb3RfaGFzaGVzKSB7CiAgICBzb21lIHBfc3RvcmFnZSBpbiBwX3N0b3JhZ2VzCgogICAgcHJpbnQoImFsbG93X3N0b3JhZ2U6IHBfc3RvcmFnZSA9IiwgcF9zdG9yYWdlKQogICAgcHJpbnQoImFsbG93X3N0b3JhZ2U6IGlfc3RvcmFnZSA9IiwgaV9zdG9yYWdlKQoKICAgIHBfc3RvcmFnZS5kcml2ZXIgICAgICAgICAgID09IGlfc3RvcmFnZS5kcml2ZXIKICAgIHBfc3RvcmFnZS5kcml2ZXJfb3B0aW9ucyAgID09IGlfc3RvcmFnZS5kcml2ZXJfb3B0aW9ucwogICAgcF9zdG9yYWdlLmZzX2dyb3VwICAgICAgICAgPT0gaV9zdG9yYWdlLmZzX2dyb3VwCgogICAgYWxsb3dfc3RvcmFnZV9vcHRpb25zKHBfc3RvcmFnZSwgaV9zdG9yYWdlLCBsYXllcl9pZHMsIHJvb3RfaGFzaGVzKQogICAgYWxsb3dfbW91bnRfcG9pbnQocF9zdG9yYWdlLCBpX3N0b3JhZ2UsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCwgbGF5ZXJfaWRzKQoKICAgICMgVE9ETzogdmFsaWRhdGUgdGhlIHNvdXJjZSBmaWVsZCB0b28uCgogICAgcHJpbnQoImFsbG93X3N0b3JhZ2U6IHRydWUiKQp9CgphbGxvd19zdG9yYWdlX29wdGlvbnMocF9zdG9yYWdlLCBpX3N0b3JhZ2UsIGxheWVyX2lkcywgcm9vdF9oYXNoZXMpIHsKICAgIHByaW50KCJhbGxvd19zdG9yYWdlX29wdGlvbnMgMTogc3RhcnQiKQoKICAgIHBfc3RvcmFnZS5kcml2ZXIgIT0gImJsayIKICAgIHBfc3RvcmFnZS5kcml2ZXIgIT0gIm92ZXJsYXlmcyIKICAgIHBfc3RvcmFnZS5vcHRpb25zID09IGlfc3RvcmFnZS5vcHRpb25zCgogICAgcHJpbnQoImFsbG93X3N0b3JhZ2Vfb3B0aW9ucyAxOiB0cnVlIikKfQphbGxvd19zdG9yYWdlX29wdGlvbnMocF9zdG9yYWdlLCBpX3N0b3JhZ2UsIGxheWVyX2lkcywgcm9vdF9oYXNoZXMpIHsKICAgIHByaW50KCJhbGxvd19zdG9yYWdlX29wdGlvbnMgMjogc3RhcnQiKQoKICAgIHBfc3RvcmFnZS5kcml2ZXIgPT0gIm92ZXJsYXlmcyIKICAgIGNvdW50KHBfc3RvcmFnZS5vcHRpb25zKSA9PSAyCgogICAgcG9saWN5X2lkcyA6PSBzcGxpdChwX3N0b3JhZ2Uub3B0aW9uc1swXSwgIjoiKQogICAgcHJpbnQoImFsbG93X3N0b3JhZ2Vfb3B0aW9ucyAyOiBwb2xpY3lfaWRzID0iLCBwb2xpY3lfaWRzKQogICAgcG9saWN5X2lkcyA9PSBsYXllcl9pZHMKCiAgICBwb2xpY3lfaGFzaGVzIDo9IHNwbGl0KHBfc3RvcmFnZS5vcHRpb25zWzFdLCAiOiIpCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZV9vcHRpb25zIDI6IHBvbGljeV9oYXNoZXMgPSIsIHBvbGljeV9oYXNoZXMpCgogICAgcF9jb3VudCA6PSBjb3VudChwb2xpY3lfaWRzKQogICAgcHJpbnQoImFsbG93X3N0b3JhZ2Vfb3B0aW9ucyAyOiBwX2NvdW50ID0iLCBwX2NvdW50KQogICAgcF9jb3VudCA+PSAxCiAgICBwX2NvdW50ID09IGNvdW50KHBvbGljeV9oYXNoZXMpCgogICAgaV9jb3VudCA6PSBjb3VudChpX3N0b3JhZ2Uub3B0aW9ucykKICAgIHByaW50KCJhbGxvd19zdG9yYWdlX29wdGlvbnMgMjogaV9jb3VudCA9IiwgaV9jb3VudCkKICAgIGlfY291bnQgPT0gcF9jb3VudCArIDMKCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZV9vcHRpb25zIDI6IGlfc3RvcmFnZS5vcHRpb25zWzBdID0iLCBpX3N0b3JhZ2Uub3B0aW9uc1swXSkKICAgIGlfc3RvcmFnZS5vcHRpb25zWzBdID09ICJpby5rYXRhY29udGFpbmVycy5mcy1vcHQubGF5ZXItc3JjLXByZWZpeD0vdmFyL2xpYi9jb250YWluZXJkL2lvLmNvbnRhaW5lcmQuc25hcHNob3R0ZXIudjEudGFyZGV2L2xheWVycyIKCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZV9vcHRpb25zIDI6IGlfc3RvcmFnZS5vcHRpb25zW2lfY291bnQgLSAyXSA9IiwgaV9zdG9yYWdlLm9wdGlvbnNbaV9jb3VudCAtIDJdKQogICAgaV9zdG9yYWdlLm9wdGlvbnNbaV9jb3VudCAtIDJdID09ICJpby5rYXRhY29udGFpbmVycy5mcy1vcHQub3ZlcmxheS1ydyIKCiAgICBsb3dlcmRpciA6PSBjb25jYXQoIj0iLCBbImxvd2VyZGlyIiwgcF9zdG9yYWdlLm9wdGlvbnNbMF1dKQogICAgcHJpbnQoImFsbG93X3N0b3JhZ2Vfb3B0aW9ucyAyOiBsb3dlcmRpciA9IiwgbG93ZXJkaXIpCgogICAgaV9zdG9yYWdlLm9wdGlvbnNbaV9jb3VudCAtIDFdID09IGxvd2VyZGlyCiAgICBwcmludCgiYWxsb3dfc3RvcmFnZV9vcHRpb25zIDI6IGlfc3RvcmFnZS5vcHRpb25zW2lfY291bnQgLSAxXSA9IiwgaV9zdG9yYWdlLm9wdGlvbnNbaV9jb3VudCAtIDFdKQoKICAgIGV2ZXJ5IGksIHBvbGljeV9pZCBpbiBwb2xpY3lfaWRzIHsKICAgICAgICBhbGxvd19vdmVybGF5X2xheWVyKHBvbGljeV9pZCwgcG9saWN5X2hhc2hlc1tpXSwgaV9zdG9yYWdlLm9wdGlvbnNbaSArIDFdKQogICAgfQoKICAgIHByaW50KCJhbGxvd19zdG9yYWdlX29wdGlvbnMgMjogdHJ1ZSIpCn0KYWxsb3dfc3RvcmFnZV9vcHRpb25zKHBfc3RvcmFnZSwgaV9zdG9yYWdlLCBsYXllcl9pZHMsIHJvb3RfaGFzaGVzKSB7CiAgICBwcmludCgiYWxsb3dfc3RvcmFnZV9vcHRpb25zIDM6IHN0YXJ0IikKCiAgICBwX3N0b3JhZ2UuZHJpdmVyID09ICJibGsiCiAgICBjb3VudChwX3N0b3JhZ2Uub3B0aW9ucykgPT0gMQoKICAgIHN0YXJ0c3dpdGgocF9zdG9yYWdlLm9wdGlvbnNbMF0sICIkKGhhc2giKQogICAgaGFzaF9zdWZmaXggOj0gdHJpbV9sZWZ0KHBfc3RvcmFnZS5vcHRpb25zWzBdLCAiJChoYXNoIikKCiAgICBlbmRzd2l0aChoYXNoX3N1ZmZpeCwgIikiKQogICAgaGFzaF9pbmRleCA6PSB0cmltX3JpZ2h0KGhhc2hfc3VmZml4LCAiKSIpCiAgICBpIDo9IHRvX251bWJlcihoYXNoX2luZGV4KQogICAgcHJpbnQoImFsbG93X3N0b3JhZ2Vfb3B0aW9ucyAzOiBpID0iLCBpKQoKICAgIGhhc2hfb3B0aW9uIDo9IGNvbmNhdCgiPSIsIFsiaW8ua2F0YWNvbnRhaW5lcnMuZnMtb3B0LnJvb3QtaGFzaCIsIHJvb3RfaGFzaGVzW2ldXSkKICAgIHByaW50KCJhbGxvd19zdG9yYWdlX29wdGlvbnMgMzogaGFzaF9vcHRpb24gPSIsIGhhc2hfb3B0aW9uKQoKICAgIGNvdW50KGlfc3RvcmFnZS5vcHRpb25zKSA9PSA0CiAgICBpX3N0b3JhZ2Uub3B0aW9uc1swXSA9PSAicm8iCiAgICBpX3N0b3JhZ2Uub3B0aW9uc1sxXSA9PSAiaW8ua2F0YWNvbnRhaW5lcnMuZnMtb3B0LmJsb2NrX2RldmljZT1maWxlIgogICAgaV9zdG9yYWdlLm9wdGlvbnNbMl0gPT0gImlvLmthdGFjb250YWluZXJzLmZzLW9wdC5pcy1sYXllciIKICAgIGlfc3RvcmFnZS5vcHRpb25zWzNdID09IGhhc2hfb3B0aW9uCgogICAgcHJpbnQoImFsbG93X3N0b3JhZ2Vfb3B0aW9ucyAzOiB0cnVlIikKfQoKYWxsb3dfb3ZlcmxheV9sYXllcihwb2xpY3lfaWQsIHBvbGljeV9oYXNoLCBpX29wdGlvbikgewogICAgcHJpbnQoImFsbG93X292ZXJsYXlfbGF5ZXI6IHBvbGljeV9pZCA9IiwgcG9saWN5X2lkLCAicG9saWN5X2hhc2ggPSIsIHBvbGljeV9oYXNoKQogICAgcHJpbnQoImFsbG93X292ZXJsYXlfbGF5ZXI6IGlfb3B0aW9uID0iLCBpX29wdGlvbikKCiAgICBzdGFydHN3aXRoKGlfb3B0aW9uLCAiaW8ua2F0YWNvbnRhaW5lcnMuZnMtb3B0LmxheWVyPSIpCiAgICBpX3ZhbHVlIDo9IHJlcGxhY2UoaV9vcHRpb24sICJpby5rYXRhY29udGFpbmVycy5mcy1vcHQubGF5ZXI9IiwgIiIpCiAgICBpX3ZhbHVlX2RlY29kZWQgOj0gYmFzZTY0LmRlY29kZShpX3ZhbHVlKQogICAgcHJpbnQoImFsbG93X292ZXJsYXlfbGF5ZXI6IGlfdmFsdWVfZGVjb2RlZCA9IiwgaV92YWx1ZV9kZWNvZGVkKQoKICAgIHBvbGljeV9zdWZmaXggOj0gY29uY2F0KCI9IiwgWyJ0YXIscm8saW8ua2F0YWNvbnRhaW5lcnMuZnMtb3B0LmJsb2NrX2RldmljZT1maWxlLGlvLmthdGFjb250YWluZXJzLmZzLW9wdC5pcy1sYXllcixpby5rYXRhY29udGFpbmVycy5mcy1vcHQucm9vdC1oYXNoIiwgcG9saWN5X2hhc2hdKQogICAgcF92YWx1ZSA6PSBjb25jYXQoIiwiLCBbcG9saWN5X2lkLCBwb2xpY3lfc3VmZml4XSkKICAgIHByaW50KCJhbGxvd19vdmVybGF5X2xheWVyOiBwX3ZhbHVlID0iLCBwX3ZhbHVlKQoKICAgIHBfdmFsdWUgPT0gaV92YWx1ZV9kZWNvZGVkCgogICAgcHJpbnQoImFsbG93X292ZXJsYXlfbGF5ZXI6IHRydWUiKQp9CgphbGxvd19tb3VudF9wb2ludChwX3N0b3JhZ2UsIGlfc3RvcmFnZSwgYnVuZGxlX2lkLCBzYW5kYm94X2lkLCBsYXllcl9pZHMpIHsKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCAxOiBpX3N0b3JhZ2UubW91bnRfcG9pbnQgPSIsIGlfc3RvcmFnZS5tb3VudF9wb2ludCkKICAgIHBfc3RvcmFnZS5mc3R5cGUgPT0gInRhciIKCiAgICBzdGFydHN3aXRoKHBfc3RvcmFnZS5tb3VudF9wb2ludCwgIiQobGF5ZXIiKQogICAgbW91bnRfc3VmZml4IDo9IHRyaW1fbGVmdChwX3N0b3JhZ2UubW91bnRfcG9pbnQsICIkKGxheWVyIikKCiAgICBlbmRzd2l0aChtb3VudF9zdWZmaXgsICIpIikKICAgIGxheWVyX2luZGV4IDo9IHRyaW1fcmlnaHQobW91bnRfc3VmZml4LCAiKSIpCiAgICBpIDo9IHRvX251bWJlcihsYXllcl9pbmRleCkKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCAxOiBpID0iLCBpKQoKICAgIGxheWVyX2lkIDo9IGxheWVyX2lkc1tpXQogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDE6IGxheWVyX2lkID0iLCBsYXllcl9pZCkKCiAgICBwX21vdW50IDo9IGNvbmNhdCgiLyIsIFsiL3J1bi9rYXRhLWNvbnRhaW5lcnMvc2FuZGJveC9sYXllcnMiLCBsYXllcl9pZF0pCiAgICBwcmludCgiYWxsb3dfbW91bnRfcG9pbnQgMTogcF9tb3VudCA9IiwgcF9tb3VudCkKCiAgICBwX21vdW50ID09IGlfc3RvcmFnZS5tb3VudF9wb2ludAoKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCAxOiB0cnVlIikKfQphbGxvd19tb3VudF9wb2ludChwX3N0b3JhZ2UsIGlfc3RvcmFnZSwgYnVuZGxlX2lkLCBzYW5kYm94X2lkLCBsYXllcl9pZHMpIHsKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCAyOiBpX3N0b3JhZ2UubW91bnRfcG9pbnQgPSIsIGlfc3RvcmFnZS5tb3VudF9wb2ludCkKICAgIHBfc3RvcmFnZS5mc3R5cGUgPT0gImZ1c2UzLmthdGEtb3ZlcmxheSIKCiAgICBtb3VudDEgOj0gcmVwbGFjZShwX3N0b3JhZ2UubW91bnRfcG9pbnQsICIkKGNwYXRoKSIsIHBvbGljeV9kYXRhLmNvbW1vbi5jcGF0aCkKICAgIG1vdW50MiA6PSByZXBsYWNlKG1vdW50MSwgIiQoYnVuZGxlLWlkKSIsIGJ1bmRsZV9pZCkKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCAyOiBtb3VudDIgPSIsIG1vdW50MikKCiAgICBtb3VudDIgPT0gaV9zdG9yYWdlLm1vdW50X3BvaW50CgogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDI6IHRydWUiKQp9CmFsbG93X21vdW50X3BvaW50KHBfc3RvcmFnZSwgaV9zdG9yYWdlLCBidW5kbGVfaWQsIHNhbmRib3hfaWQsIGxheWVyX2lkcykgewogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDM6IGlfc3RvcmFnZS5tb3VudF9wb2ludCA9IiwgaV9zdG9yYWdlLm1vdW50X3BvaW50KQogICAgcF9zdG9yYWdlLmZzdHlwZSA9PSAibG9jYWwiCgogICAgbW91bnQxIDo9IHBfc3RvcmFnZS5tb3VudF9wb2ludAogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDM6IG1vdW50MSA9IiwgbW91bnQxKQoKICAgIG1vdW50MiA6PSByZXBsYWNlKG1vdW50MSwgIiQoY3BhdGgpIiwgcG9saWN5X2RhdGEuY29tbW9uLmNwYXRoKQogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDM6IG1vdW50MiA9IiwgbW91bnQyKQoKICAgIG1vdW50MyA6PSByZXBsYWNlKG1vdW50MiwgIiQoc2FuZGJveC1pZCkiLCBzYW5kYm94X2lkKQogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDM6IG1vdW50MyA9IiwgbW91bnQzKQoKICAgIHJlZ2V4Lm1hdGNoKG1vdW50MywgaV9zdG9yYWdlLm1vdW50X3BvaW50KQoKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCAzOiB0cnVlIikKfQphbGxvd19tb3VudF9wb2ludChwX3N0b3JhZ2UsIGlfc3RvcmFnZSwgYnVuZGxlX2lkLCBzYW5kYm94X2lkLCBsYXllcl9pZHMpIHsKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCA0OiBpX3N0b3JhZ2UubW91bnRfcG9pbnQgPSIsIGlfc3RvcmFnZS5tb3VudF9wb2ludCkKICAgIHBfc3RvcmFnZS5mc3R5cGUgPT0gImJpbmQiCgogICAgbW91bnQxIDo9IHBfc3RvcmFnZS5tb3VudF9wb2ludAogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDQ6IG1vdW50MSA9IiwgbW91bnQxKQoKICAgIG1vdW50MiA6PSByZXBsYWNlKG1vdW50MSwgIiQoY3BhdGgpIiwgcG9saWN5X2RhdGEuY29tbW9uLmNwYXRoKQogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDQ6IG1vdW50MiA9IiwgbW91bnQyKQoKICAgIG1vdW50MyA6PSByZXBsYWNlKG1vdW50MiwgIiQoYnVuZGxlLWlkKSIsIGJ1bmRsZV9pZCkKICAgIHByaW50KCJhbGxvd19tb3VudF9wb2ludCA0OiBtb3VudDMgPSIsIG1vdW50MykKCiAgICByZWdleC5tYXRjaChtb3VudDMsIGlfc3RvcmFnZS5tb3VudF9wb2ludCkKCiAgICBwcmludCgiYWxsb3dfbW91bnRfcG9pbnQgNDogdHJ1ZSIpCn0KYWxsb3dfbW91bnRfcG9pbnQocF9zdG9yYWdlLCBpX3N0b3JhZ2UsIGJ1bmRsZV9pZCwgc2FuZGJveF9pZCwgbGF5ZXJfaWRzKSB7CiAgICBwcmludCgiYWxsb3dfbW91bnRfcG9pbnQgNTogaV9zdG9yYWdlLm1vdW50X3BvaW50ID0iLCBpX3N0b3JhZ2UubW91bnRfcG9pbnQpCiAgICBwX3N0b3JhZ2UuZnN0eXBlID09ICJ0bXBmcyIKCiAgICBtb3VudDEgOj0gcF9zdG9yYWdlLm1vdW50X3BvaW50CiAgICBwcmludCgiYWxsb3dfbW91bnRfcG9pbnQgNTogbW91bnQxID0iLCBtb3VudDEpCgogICAgcmVnZXgubWF0Y2gobW91bnQxLCBpX3N0b3JhZ2UubW91bnRfcG9pbnQpCgogICAgcHJpbnQoImFsbG93X21vdW50X3BvaW50IDU6IHRydWUiKQp9CgojIHByb2Nlc3MuQ2FwYWJpbGl0aWVzCmFsbG93X2NhcHMocF9jYXBzLCBpX2NhcHMpIHsKICAgIHByaW50KCJhbGxvd19jYXBzOiBwb2xpY3kgQW1iaWVudCA9IiwgcF9jYXBzLkFtYmllbnQpCiAgICBwcmludCgiYWxsb3dfY2FwczogaW5wdXQgQW1iaWVudCA9IiwgaV9jYXBzLkFtYmllbnQpCiAgICBtYXRjaF9jYXBzKHBfY2Fwcy5BbWJpZW50LCBpX2NhcHMuQW1iaWVudCkKCiAgICBwcmludCgiYWxsb3dfY2FwczogcG9saWN5IEJvdW5kaW5nID0iLCBwX2NhcHMuQm91bmRpbmcpCiAgICBwcmludCgiYWxsb3dfY2FwczogaW5wdXQgQm91bmRpbmcgPSIsIGlfY2Fwcy5Cb3VuZGluZykKICAgIG1hdGNoX2NhcHMocF9jYXBzLkJvdW5kaW5nLCBpX2NhcHMuQm91bmRpbmcpCgogICAgcHJpbnQoImFsbG93X2NhcHM6IHBvbGljeSBFZmZlY3RpdmUgPSIsIHBfY2Fwcy5FZmZlY3RpdmUpCiAgICBwcmludCgiYWxsb3dfY2FwczogaW5wdXQgRWZmZWN0aXZlID0iLCBpX2NhcHMuRWZmZWN0aXZlKQogICAgbWF0Y2hfY2FwcyhwX2NhcHMuRWZmZWN0aXZlLCBpX2NhcHMuRWZmZWN0aXZlKQoKICAgIHByaW50KCJhbGxvd19jYXBzOiBwb2xpY3kgSW5oZXJpdGFibGUgPSIsIHBfY2Fwcy5Jbmhlcml0YWJsZSkKICAgIHByaW50KCJhbGxvd19jYXBzOiBpbnB1dCBJbmhlcml0YWJsZSA9IiwgaV9jYXBzLkluaGVyaXRhYmxlKQogICAgbWF0Y2hfY2FwcyhwX2NhcHMuSW5oZXJpdGFibGUsIGlfY2Fwcy5Jbmhlcml0YWJsZSkKCiAgICBwcmludCgiYWxsb3dfY2FwczogcG9saWN5IFBlcm1pdHRlZCA9IiwgcF9jYXBzLlBlcm1pdHRlZCkKICAgIHByaW50KCJhbGxvd19jYXBzOiBpbnB1dCBQZXJtaXR0ZWQgPSIsIGlfY2Fwcy5QZXJtaXR0ZWQpCiAgICBtYXRjaF9jYXBzKHBfY2Fwcy5QZXJtaXR0ZWQsIGlfY2Fwcy5QZXJtaXR0ZWQpCn0KCm1hdGNoX2NhcHMocF9jYXBzLCBpX2NhcHMpIHsKICAgIHByaW50KCJtYXRjaF9jYXBzIDE6IHN0YXJ0IikKCiAgICBwX2NhcHMgPT0gaV9jYXBzCgogICAgcHJpbnQoIm1hdGNoX2NhcHMgMTogdHJ1ZSIpCn0KbWF0Y2hfY2FwcyhwX2NhcHMsIGlfY2FwcykgewogICAgcHJpbnQoIm1hdGNoX2NhcHMgMjogc3RhcnQiKQoKICAgIGNvdW50KHBfY2FwcykgPT0gMQogICAgcF9jYXBzWzBdID09ICIkKGRlZmF1bHRfY2FwcykiCgogICAgcHJpbnQoIm1hdGNoX2NhcHMgMjogZGVmYXVsdF9jYXBzID0iLCBwb2xpY3lfZGF0YS5jb21tb24uZGVmYXVsdF9jYXBzKQogICAgcG9saWN5X2RhdGEuY29tbW9uLmRlZmF1bHRfY2FwcyA9PSBpX2NhcHMKCiAgICBwcmludCgibWF0Y2hfY2FwcyAyOiB0cnVlIikKfQptYXRjaF9jYXBzKHBfY2FwcywgaV9jYXBzKSB7CiAgICBwcmludCgibWF0Y2hfY2FwcyAzOiBzdGFydCIpCgogICAgY291bnQocF9jYXBzKSA9PSAxCiAgICBwX2NhcHNbMF0gPT0gIiQocHJpdmlsZWdlZF9jYXBzKSIKCiAgICBwcmludCgibWF0Y2hfY2FwcyAzOiBwcml2aWxlZ2VkX2NhcHMgPSIsIHBvbGljeV9kYXRhLmNvbW1vbi5wcml2aWxlZ2VkX2NhcHMpCiAgICBwb2xpY3lfZGF0YS5jb21tb24ucHJpdmlsZWdlZF9jYXBzID09IGlfY2FwcwoKICAgIHByaW50KCJtYXRjaF9jYXBzIDM6IHRydWUiKQp9CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCkNvcHlGaWxlUmVxdWVzdCB7CiAgICBwcmludCgiQ29weUZpbGVSZXF1ZXN0OiBpbnB1dC5wYXRoID0iLCBpbnB1dC5wYXRoKQoKICAgIHNvbWUgcmVnZXgxIGluIHBvbGljeV9kYXRhLnJlcXVlc3RfZGVmYXVsdHMuQ29weUZpbGVSZXF1ZXN0CiAgICByZWdleDIgOj0gcmVwbGFjZShyZWdleDEsICIkKGNwYXRoKSIsIHBvbGljeV9kYXRhLmNvbW1vbi5jcGF0aCkKICAgIHJlZ2V4Lm1hdGNoKHJlZ2V4MiwgaW5wdXQucGF0aCkKCiAgICBwcmludCgiQ29weUZpbGVSZXF1ZXN0OiB0cnVlIikKfQoKRXhlY1Byb2Nlc3NSZXF1ZXN0IHsKICAgIHByaW50KCJFeGVjUHJvY2Vzc1JlcXVlc3QgMTogaW5wdXQgPSIsIGlucHV0KQoKICAgIGlfY29tbWFuZCA9IGNvbmNhdCgiICIsIGlucHV0LnByb2Nlc3MuQXJncykKICAgIHByaW50KCJFeGVjUHJvY2Vzc1JlcXVlc3QgMzogaV9jb21tYW5kID0iLCBpX2NvbW1hbmQpCgogICAgc29tZSBwX2NvbW1hbmQgaW4gcG9saWN5X2RhdGEucmVxdWVzdF9kZWZhdWx0cy5FeGVjUHJvY2Vzc1JlcXVlc3QuY29tbWFuZHMKICAgIHBfY29tbWFuZCA9PSBpX2NvbW1hbmQKCiAgICBwcmludCgiRXhlY1Byb2Nlc3NSZXF1ZXN0IDE6IHRydWUiKQp9CkV4ZWNQcm9jZXNzUmVxdWVzdCB7CiAgICBwcmludCgiRXhlY1Byb2Nlc3NSZXF1ZXN0IDI6IGlucHV0ID0iLCBpbnB1dCkKCiAgICAjIFRPRE86IG1hdGNoIGlucHV0IGNvbnRhaW5lciBJRCB3aXRoIGl0cyBjb3JyZXNwb25kaW5nIGNvbnRhaW5lci5leGVjX2NvbW1hbmRzLgogICAgaV9jb21tYW5kID0gY29uY2F0KCIgIiwgaW5wdXQucHJvY2Vzcy5BcmdzKQogICAgcHJpbnQoIkV4ZWNQcm9jZXNzUmVxdWVzdCAzOiBpX2NvbW1hbmQgPSIsIGlfY29tbWFuZCkKCiAgICBzb21lIGNvbnRhaW5lciBpbiBwb2xpY3lfZGF0YS5jb250YWluZXJzCiAgICBzb21lIHBfY29tbWFuZCBpbiBjb250YWluZXIuZXhlY19jb21tYW5kcwogICAgcHJpbnQoIkV4ZWNQcm9jZXNzUmVxdWVzdCAyOiBwX2NvbW1hbmQgPSIsIHBfY29tbWFuZCkKCiAgICAjIFRPRE86IHNob3VsZCBvdGhlciBpbnB1dCBkYXRhIGZpZWxkcyBiZSB2YWxpZGF0ZWQgYXMgd2VsbD8KICAgIHBfY29tbWFuZCA9PSBpX2NvbW1hbmQKCiAgICBwcmludCgiRXhlY1Byb2Nlc3NSZXF1ZXN0IDI6IHRydWUiKQp9CkV4ZWNQcm9jZXNzUmVxdWVzdCB7CiAgICBwcmludCgiRXhlY1Byb2Nlc3NSZXF1ZXN0IDM6IGlucHV0ID0iLCBpbnB1dCkKCiAgICBpX2NvbW1hbmQgPSBjb25jYXQoIiAiLCBpbnB1dC5wcm9jZXNzLkFyZ3MpCiAgICBwcmludCgiRXhlY1Byb2Nlc3NSZXF1ZXN0IDM6IGlfY29tbWFuZCA9IiwgaV9jb21tYW5kKQoKICAgIHNvbWUgcF9yZWdleCBpbiBwb2xpY3lfZGF0YS5yZXF1ZXN0X2RlZmF1bHRzLkV4ZWNQcm9jZXNzUmVxdWVzdC5yZWdleAogICAgcHJpbnQoIkV4ZWNQcm9jZXNzUmVxdWVzdCAzOiBwX3JlZ2V4ID0iLCBwX3JlZ2V4KQoKICAgIHJlZ2V4Lm1hdGNoKHBfcmVnZXgsIGlfY29tbWFuZCkKCiAgICBwcmludCgiRXhlY1Byb2Nlc3NSZXF1ZXN0IDM6IHRydWUiKQp9CgpSZWFkU3RyZWFtUmVxdWVzdCB7CiAgICBwb2xpY3lfZGF0YS5yZXF1ZXN0X2RlZmF1bHRzLlJlYWRTdHJlYW1SZXF1ZXN0ID09IHRydWUKfQoKV3JpdGVTdHJlYW1SZXF1ZXN0IHsKICAgIHBvbGljeV9kYXRhLnJlcXVlc3RfZGVmYXVsdHMuV3JpdGVTdHJlYW1SZXF1ZXN0ID09IHRydWUKfQoKcG9saWN5X2RhdGEgOj0gewogICJjb250YWluZXJzIjogWwogICAgewogICAgICAiT0NJIjogewogICAgICAgICJWZXJzaW9uIjogIjEuMS4wLXJjLjEiLAogICAgICAgICJQcm9jZXNzIjogewogICAgICAgICAgIlRlcm1pbmFsIjogZmFsc2UsCiAgICAgICAgICAiVXNlciI6IHsKICAgICAgICAgICAgIlVJRCI6IDY1NTM1LAogICAgICAgICAgICAiR0lEIjogNjU1MzUsCiAgICAgICAgICAgICJBZGRpdGlvbmFsR2lkcyI6IFtdLAogICAgICAgICAgICAiVXNlcm5hbWUiOiAiIgogICAgICAgICAgfSwKICAgICAgICAgICJBcmdzIjogWwogICAgICAgICAgICAiL3BhdXNlIgogICAgICAgICAgXSwKICAgICAgICAgICJFbnYiOiBbCiAgICAgICAgICAgICJQQVRIPS91c3IvbG9jYWwvc2JpbjovdXNyL2xvY2FsL2JpbjovdXNyL3NiaW46L3Vzci9iaW46L3NiaW46L2JpbiIKICAgICAgICAgIF0sCiAgICAgICAgICAiQ3dkIjogIi8iLAogICAgICAgICAgIkNhcGFiaWxpdGllcyI6IHsKICAgICAgICAgICAgIkFtYmllbnQiOiBbXSwKICAgICAgICAgICAgIkJvdW5kaW5nIjogWwogICAgICAgICAgICAgICIkKGRlZmF1bHRfY2FwcykiCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJFZmZlY3RpdmUiOiBbCiAgICAgICAgICAgICAgIiQoZGVmYXVsdF9jYXBzKSIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgIkluaGVyaXRhYmxlIjogW10sCiAgICAgICAgICAgICJQZXJtaXR0ZWQiOiBbCiAgICAgICAgICAgICAgIiQoZGVmYXVsdF9jYXBzKSIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgICJOb05ld1ByaXZpbGVnZXMiOiB0cnVlCiAgICAgICAgfSwKICAgICAgICAiUm9vdCI6IHsKICAgICAgICAgICJQYXRoIjogIiQoY3BhdGgpLyQoYnVuZGxlLWlkKSIsCiAgICAgICAgICAiUmVhZG9ubHkiOiB0cnVlCiAgICAgICAgfSwKICAgICAgICAiTW91bnRzIjogWwogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL3Byb2MiLAogICAgICAgICAgICAic291cmNlIjogInByb2MiLAogICAgICAgICAgICAidHlwZV8iOiAicHJvYyIsCiAgICAgICAgICAgICJvcHRpb25zIjogWwogICAgICAgICAgICAgICJub3N1aWQiLAogICAgICAgICAgICAgICJub2V4ZWMiLAogICAgICAgICAgICAgICJub2RldiIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9kZXYiLAogICAgICAgICAgICAic291cmNlIjogInRtcGZzIiwKICAgICAgICAgICAgInR5cGVfIjogInRtcGZzIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgIm5vc3VpZCIsCiAgICAgICAgICAgICAgInN0cmljdGF0aW1lIiwKICAgICAgICAgICAgICAibW9kZT03NTUiLAogICAgICAgICAgICAgICJzaXplPTY1NTM2ayIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9kZXYvcHRzIiwKICAgICAgICAgICAgInNvdXJjZSI6ICJkZXZwdHMiLAogICAgICAgICAgICAidHlwZV8iOiAiZGV2cHRzIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgIm5vc3VpZCIsCiAgICAgICAgICAgICAgIm5vZXhlYyIsCiAgICAgICAgICAgICAgIm5ld2luc3RhbmNlIiwKICAgICAgICAgICAgICAicHRteG1vZGU9MDY2NiIsCiAgICAgICAgICAgICAgIm1vZGU9MDYyMCIsCiAgICAgICAgICAgICAgImdpZD01IgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL2Rldi9zaG0iLAogICAgICAgICAgICAic291cmNlIjogIi9ydW4va2F0YS1jb250YWluZXJzL3NhbmRib3gvc2htIiwKICAgICAgICAgICAgInR5cGVfIjogImJpbmQiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAicmJpbmQiCiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJkZXN0aW5hdGlvbiI6ICIvZGV2L21xdWV1ZSIsCiAgICAgICAgICAgICJzb3VyY2UiOiAibXF1ZXVlIiwKICAgICAgICAgICAgInR5cGVfIjogIm1xdWV1ZSIsCiAgICAgICAgICAgICJvcHRpb25zIjogWwogICAgICAgICAgICAgICJub3N1aWQiLAogICAgICAgICAgICAgICJub2V4ZWMiLAogICAgICAgICAgICAgICJub2RldiIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9zeXMiLAogICAgICAgICAgICAic291cmNlIjogInN5c2ZzIiwKICAgICAgICAgICAgInR5cGVfIjogInN5c2ZzIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgIm5vc3VpZCIsCiAgICAgICAgICAgICAgIm5vZXhlYyIsCiAgICAgICAgICAgICAgIm5vZGV2IiwKICAgICAgICAgICAgICAicm8iCiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJkZXN0aW5hdGlvbiI6ICIvZXRjL3Jlc29sdi5jb25mIiwKICAgICAgICAgICAgInNvdXJjZSI6ICIkKHNmcHJlZml4KXJlc29sdi5jb25mJCIsCiAgICAgICAgICAgICJ0eXBlXyI6ICJiaW5kIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgInJiaW5kIiwKICAgICAgICAgICAgICAicm8iLAogICAgICAgICAgICAgICJub3N1aWQiLAogICAgICAgICAgICAgICJub2RldiIsCiAgICAgICAgICAgICAgIm5vZXhlYyIKICAgICAgICAgICAgXQogICAgICAgICAgfQogICAgICAgIF0sCiAgICAgICAgIkFubm90YXRpb25zIjogewogICAgICAgICAgImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuYnVuZGxlX3BhdGgiOiAiL3J1bi9jb250YWluZXJkL2lvLmNvbnRhaW5lcmQucnVudGltZS52Mi50YXNrL2s4cy5pby8kKGJ1bmRsZS1pZCkiLAogICAgICAgICAgImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuY29udGFpbmVyX3R5cGUiOiAicG9kX3NhbmRib3giLAogICAgICAgICAgImlvLmt1YmVybmV0ZXMuY3JpLmNvbnRhaW5lci10eXBlIjogInNhbmRib3giLAogICAgICAgICAgImlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtaWQiOiAiXlthLXowLTldezY0fSQiLAogICAgICAgICAgImlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtbG9nLWRpcmVjdG9yeSI6ICJeL3Zhci9sb2cvcG9kcy8kKHNhbmRib3gtbmFtZXNwYWNlKV8kKHNhbmRib3gtbmFtZSlfWzAtOWEtZl17OH0tWzAtOWEtZl17NH0tWzAtOWEtZl17NH0tWzAtOWEtZl17NH0tWzAtOWEtZl17MTJ9JCIsCiAgICAgICAgICAiaW8ua3ViZXJuZXRlcy5jcmkuc2FuZGJveC1uYW1lIjogIm9uZS1jb250YWluZXIiLAogICAgICAgICAgImlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtbmFtZXNwYWNlIjogImRlZmF1bHQiLAogICAgICAgICAgIm5lcmRjdGwvbmV0d29yay1uYW1lc3BhY2UiOiAiXi92YXIvcnVuL25ldG5zL2NuaS1bMC05YS1mXXs4fS1bMC05YS1mXXs0fS1bMC05YS1mXXs0fS1bMC05YS1mXXs0fS1bMC05YS1mXXsxMn0kIgogICAgICAgIH0sCiAgICAgICAgIkxpbnV4IjogewogICAgICAgICAgIk5hbWVzcGFjZXMiOiBbCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAiVHlwZSI6ICJpcGMiLAogICAgICAgICAgICAgICJQYXRoIjogIiIKICAgICAgICAgICAgfSwKICAgICAgICAgICAgewogICAgICAgICAgICAgICJUeXBlIjogInV0cyIsCiAgICAgICAgICAgICAgIlBhdGgiOiAiIgogICAgICAgICAgICB9LAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgIlR5cGUiOiAibW91bnQiLAogICAgICAgICAgICAgICJQYXRoIjogIiIKICAgICAgICAgICAgfQogICAgICAgICAgXSwKICAgICAgICAgICJNYXNrZWRQYXRocyI6IFsKICAgICAgICAgICAgIi9wcm9jL2FjcGkiLAogICAgICAgICAgICAiL3Byb2MvYXNvdW5kIiwKICAgICAgICAgICAgIi9wcm9jL2tjb3JlIiwKICAgICAgICAgICAgIi9wcm9jL2tleXMiLAogICAgICAgICAgICAiL3Byb2MvbGF0ZW5jeV9zdGF0cyIsCiAgICAgICAgICAgICIvcHJvYy90aW1lcl9saXN0IiwKICAgICAgICAgICAgIi9wcm9jL3RpbWVyX3N0YXRzIiwKICAgICAgICAgICAgIi9wcm9jL3NjaGVkX2RlYnVnIiwKICAgICAgICAgICAgIi9zeXMvZmlybXdhcmUiLAogICAgICAgICAgICAiL3Byb2Mvc2NzaSIKICAgICAgICAgIF0sCiAgICAgICAgICAiUmVhZG9ubHlQYXRocyI6IFsKICAgICAgICAgICAgIi9wcm9jL2J1cyIsCiAgICAgICAgICAgICIvcHJvYy9mcyIsCiAgICAgICAgICAgICIvcHJvYy9pcnEiLAogICAgICAgICAgICAiL3Byb2Mvc3lzIiwKICAgICAgICAgICAgIi9wcm9jL3N5c3JxLXRyaWdnZXIiCiAgICAgICAgICBdCiAgICAgICAgfQogICAgICB9LAogICAgICAic3RvcmFnZXMiOiBbCiAgICAgICAgewogICAgICAgICAgImRyaXZlciI6ICJibGsiLAogICAgICAgICAgImRyaXZlcl9vcHRpb25zIjogW10sCiAgICAgICAgICAic291cmNlIjogIiIsCiAgICAgICAgICAiZnN0eXBlIjogInRhciIsCiAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgIiQoaGFzaDApIgogICAgICAgICAgXSwKICAgICAgICAgICJtb3VudF9wb2ludCI6ICIkKGxheWVyMCkiLAogICAgICAgICAgImZzX2dyb3VwIjogbnVsbAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgImRyaXZlciI6ICJvdmVybGF5ZnMiLAogICAgICAgICAgImRyaXZlcl9vcHRpb25zIjogW10sCiAgICAgICAgICAic291cmNlIjogIiIsCiAgICAgICAgICAiZnN0eXBlIjogImZ1c2UzLmthdGEtb3ZlcmxheSIsCiAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgIjVhNWFhZDgwMDU1ZmYyMDAxMmE1MGRjMjVmOGRmN2EyOTkyNDQ3NDMyNGQ2NWY3ZDUzMDZlZThlZTI3ZmY3MWQiLAogICAgICAgICAgICAiODE3MjUwZjFhM2UzMzZkYTc2ZjViZDNmYTc4NGUxYjI2ZDk1OWI5YzEzMTg3NjgxNWJhMjYwNDA0OGI3MGMxOCIKICAgICAgICAgIF0sCiAgICAgICAgICAibW91bnRfcG9pbnQiOiAiJChjcGF0aCkvJChidW5kbGUtaWQpIiwKICAgICAgICAgICJmc19ncm91cCI6IG51bGwKICAgICAgICB9CiAgICAgIF0sCiAgICAgICJleGVjX2NvbW1hbmRzIjogW10KICAgIH0sCiAgICB7CiAgICAgICJPQ0kiOiB7CiAgICAgICAgIlZlcnNpb24iOiAiMS4xLjAtcmMuMSIsCiAgICAgICAgIlByb2Nlc3MiOiB7CiAgICAgICAgICAiVGVybWluYWwiOiBmYWxzZSwKICAgICAgICAgICJVc2VyIjogewogICAgICAgICAgICAiVUlEIjogMCwKICAgICAgICAgICAgIkdJRCI6IDAsCiAgICAgICAgICAgICJBZGRpdGlvbmFsR2lkcyI6IFtdLAogICAgICAgICAgICAiVXNlcm5hbWUiOiAiIgogICAgICAgICAgfSwKICAgICAgICAgICJBcmdzIjogWwogICAgICAgICAgICAiL2Jpbi9zaCIsCiAgICAgICAgICAgICItYyIsCiAgICAgICAgICAgICJ3aGlsZSB0cnVlOyBkbyBlY2hvICQoc2FuZGJveC1uYW1lKTsgc2xlZXAgMTA7IGRvbmUiCiAgICAgICAgICBdLAogICAgICAgICAgIkVudiI6IFsKICAgICAgICAgICAgIlBBVEg9L3Vzci9sb2NhbC9zYmluOi91c3IvbG9jYWwvYmluOi91c3Ivc2JpbjovdXNyL2Jpbjovc2JpbjovYmluIiwKICAgICAgICAgICAgIkhPU1ROQU1FPSQoaG9zdC1uYW1lKSIsCiAgICAgICAgICAgICJQT0RfTkFNRT0kKHNhbmRib3gtbmFtZSkiLAogICAgICAgICAgICAiUE9EX05BTUVTUEFDRT1kZWZhdWx0IiwKICAgICAgICAgICAgIlBPRF9JUD0kKHBvZC1pcCkiLAogICAgICAgICAgICAiU0VSVklDRV9BQ0NPVU5UPWRlZmF1bHQiLAogICAgICAgICAgICAiUFJPWFlfQ09ORklHPXt9XG4iLAogICAgICAgICAgICAiSVNUSU9fTUVUQV9QT0RfUE9SVFM9W1xuXSIsCiAgICAgICAgICAgICJJU1RJT19NRVRBX0FQUF9DT05UQUlORVJTPXNlcnZpY2VhY2xpZW50IiwKICAgICAgICAgICAgIklTVElPX01FVEFfQ0xVU1RFUl9JRD1LdWJlcm5ldGVzIiwKICAgICAgICAgICAgIklTVElPX01FVEFfTk9ERV9OQU1FPSQobm9kZS1uYW1lKSIKICAgICAgICAgIF0sCiAgICAgICAgICAiQ3dkIjogIi8iLAogICAgICAgICAgIkNhcGFiaWxpdGllcyI6IHsKICAgICAgICAgICAgIkFtYmllbnQiOiBbXSwKICAgICAgICAgICAgIkJvdW5kaW5nIjogWwogICAgICAgICAgICAgICIkKHByaXZpbGVnZWRfY2FwcykiCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgICJFZmZlY3RpdmUiOiBbCiAgICAgICAgICAgICAgIiQocHJpdmlsZWdlZF9jYXBzKSIKICAgICAgICAgICAgXSwKICAgICAgICAgICAgIkluaGVyaXRhYmxlIjogW10sCiAgICAgICAgICAgICJQZXJtaXR0ZWQiOiBbCiAgICAgICAgICAgICAgIiQocHJpdmlsZWdlZF9jYXBzKSIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgICJOb05ld1ByaXZpbGVnZXMiOiBmYWxzZQogICAgICAgIH0sCiAgICAgICAgIlJvb3QiOiB7CiAgICAgICAgICAiUGF0aCI6ICIkKGNwYXRoKS8kKGJ1bmRsZS1pZCkiLAogICAgICAgICAgIlJlYWRvbmx5IjogZmFsc2UKICAgICAgICB9LAogICAgICAgICJNb3VudHMiOiBbCiAgICAgICAgICB7CiAgICAgICAgICAgICJkZXN0aW5hdGlvbiI6ICIvcHJvYyIsCiAgICAgICAgICAgICJzb3VyY2UiOiAicHJvYyIsCiAgICAgICAgICAgICJ0eXBlXyI6ICJwcm9jIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgIm5vc3VpZCIsCiAgICAgICAgICAgICAgIm5vZXhlYyIsCiAgICAgICAgICAgICAgIm5vZGV2IgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL2RldiIsCiAgICAgICAgICAgICJzb3VyY2UiOiAidG1wZnMiLAogICAgICAgICAgICAidHlwZV8iOiAidG1wZnMiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAibm9zdWlkIiwKICAgICAgICAgICAgICAic3RyaWN0YXRpbWUiLAogICAgICAgICAgICAgICJtb2RlPTc1NSIsCiAgICAgICAgICAgICAgInNpemU9NjU1MzZrIgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL2Rldi9wdHMiLAogICAgICAgICAgICAic291cmNlIjogImRldnB0cyIsCiAgICAgICAgICAgICJ0eXBlXyI6ICJkZXZwdHMiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAibm9zdWlkIiwKICAgICAgICAgICAgICAibm9leGVjIiwKICAgICAgICAgICAgICAibmV3aW5zdGFuY2UiLAogICAgICAgICAgICAgICJwdG14bW9kZT0wNjY2IiwKICAgICAgICAgICAgICAibW9kZT0wNjIwIiwKICAgICAgICAgICAgICAiZ2lkPTUiCiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJkZXN0aW5hdGlvbiI6ICIvZGV2L3NobSIsCiAgICAgICAgICAgICJzb3VyY2UiOiAiL3J1bi9rYXRhLWNvbnRhaW5lcnMvc2FuZGJveC9zaG0iLAogICAgICAgICAgICAidHlwZV8iOiAiYmluZCIsCiAgICAgICAgICAgICJvcHRpb25zIjogWwogICAgICAgICAgICAgICJyYmluZCIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9kZXYvbXF1ZXVlIiwKICAgICAgICAgICAgInNvdXJjZSI6ICJtcXVldWUiLAogICAgICAgICAgICAidHlwZV8iOiAibXF1ZXVlIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgIm5vc3VpZCIsCiAgICAgICAgICAgICAgIm5vZXhlYyIsCiAgICAgICAgICAgICAgIm5vZGV2IgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL3N5cyIsCiAgICAgICAgICAgICJzb3VyY2UiOiAic3lzZnMiLAogICAgICAgICAgICAidHlwZV8iOiAic3lzZnMiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAibm9zdWlkIiwKICAgICAgICAgICAgICAibm9leGVjIiwKICAgICAgICAgICAgICAibm9kZXYiLAogICAgICAgICAgICAgICJydyIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9zeXMvZnMvY2dyb3VwIiwKICAgICAgICAgICAgInNvdXJjZSI6ICJjZ3JvdXAiLAogICAgICAgICAgICAidHlwZV8iOiAiY2dyb3VwIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgIm5vc3VpZCIsCiAgICAgICAgICAgICAgIm5vZXhlYyIsCiAgICAgICAgICAgICAgIm5vZGV2IiwKICAgICAgICAgICAgICAicmVsYXRpbWUiLAogICAgICAgICAgICAgICJydyIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9ldGMvaG9zdHMiLAogICAgICAgICAgICAic291cmNlIjogIiQoc2ZwcmVmaXgpaG9zdHMkIiwKICAgICAgICAgICAgInR5cGVfIjogImJpbmQiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAicmJpbmQiLAogICAgICAgICAgICAgICJycHJpdmF0ZSIsCiAgICAgICAgICAgICAgInJ3IgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL2Rldi90ZXJtaW5hdGlvbi1sb2ciLAogICAgICAgICAgICAic291cmNlIjogIiQoc2ZwcmVmaXgpdGVybWluYXRpb24tbG9nJCIsCiAgICAgICAgICAgICJ0eXBlXyI6ICJiaW5kIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgInJiaW5kIiwKICAgICAgICAgICAgICAicnByaXZhdGUiLAogICAgICAgICAgICAgICJydyIKICAgICAgICAgICAgXQogICAgICAgICAgfSwKICAgICAgICAgIHsKICAgICAgICAgICAgImRlc3RpbmF0aW9uIjogIi9ldGMvaG9zdG5hbWUiLAogICAgICAgICAgICAic291cmNlIjogIiQoc2ZwcmVmaXgpaG9zdG5hbWUkIiwKICAgICAgICAgICAgInR5cGVfIjogImJpbmQiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAicmJpbmQiLAogICAgICAgICAgICAgICJycHJpdmF0ZSIsCiAgICAgICAgICAgICAgInJ3IgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL2V0Yy9yZXNvbHYuY29uZiIsCiAgICAgICAgICAgICJzb3VyY2UiOiAiJChzZnByZWZpeClyZXNvbHYuY29uZiQiLAogICAgICAgICAgICAidHlwZV8iOiAiYmluZCIsCiAgICAgICAgICAgICJvcHRpb25zIjogWwogICAgICAgICAgICAgICJyYmluZCIsCiAgICAgICAgICAgICAgInJwcml2YXRlIiwKICAgICAgICAgICAgICAicnciCiAgICAgICAgICAgIF0KICAgICAgICAgIH0sCiAgICAgICAgICB7CiAgICAgICAgICAgICJkZXN0aW5hdGlvbiI6ICIvdmFyL3J1bi9zZWNyZXRzL2t1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQiLAogICAgICAgICAgICAic291cmNlIjogIiQoc2ZwcmVmaXgpc2VydmljZWFjY291bnQkIiwKICAgICAgICAgICAgInR5cGVfIjogImJpbmQiLAogICAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgICAicmJpbmQiLAogICAgICAgICAgICAgICJycHJpdmF0ZSIsCiAgICAgICAgICAgICAgInJvIgogICAgICAgICAgICBdCiAgICAgICAgICB9LAogICAgICAgICAgewogICAgICAgICAgICAiZGVzdGluYXRpb24iOiAiL3Zhci9ydW4vc2VjcmV0cy9henVyZS90b2tlbnMiLAogICAgICAgICAgICAic291cmNlIjogIiQoc2ZwcmVmaXgpdG9rZW5zJCIsCiAgICAgICAgICAgICJ0eXBlXyI6ICJiaW5kIiwKICAgICAgICAgICAgIm9wdGlvbnMiOiBbCiAgICAgICAgICAgICAgInJiaW5kIiwKICAgICAgICAgICAgICAicnByaXZhdGUiLAogICAgICAgICAgICAgICJybyIKICAgICAgICAgICAgXQogICAgICAgICAgfQogICAgICAgIF0sCiAgICAgICAgIkFubm90YXRpb25zIjogewogICAgICAgICAgImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuYnVuZGxlX3BhdGgiOiAiL3J1bi9jb250YWluZXJkL2lvLmNvbnRhaW5lcmQucnVudGltZS52Mi50YXNrL2s4cy5pby8kKGJ1bmRsZS1pZCkiLAogICAgICAgICAgImlvLmthdGFjb250YWluZXJzLnBrZy5vY2kuY29udGFpbmVyX3R5cGUiOiAicG9kX2NvbnRhaW5lciIsCiAgICAgICAgICAiaW8ua3ViZXJuZXRlcy5jcmkuY29udGFpbmVyLW5hbWUiOiAiYnVzeWJveCIsCiAgICAgICAgICAiaW8ua3ViZXJuZXRlcy5jcmkuY29udGFpbmVyLXR5cGUiOiAiY29udGFpbmVyIiwKICAgICAgICAgICJpby5rdWJlcm5ldGVzLmNyaS5pbWFnZS1uYW1lIjogInF1YXkuaW8vcHJvbWV0aGV1cy9idXN5Ym94OmxhdGVzdCIsCiAgICAgICAgICAiaW8ua3ViZXJuZXRlcy5jcmkuc2FuZGJveC1pZCI6ICJeW2EtejAtOV17NjR9JCIsCiAgICAgICAgICAiaW8ua3ViZXJuZXRlcy5jcmkuc2FuZGJveC1uYW1lIjogIm9uZS1jb250YWluZXIiLAogICAgICAgICAgImlvLmt1YmVybmV0ZXMuY3JpLnNhbmRib3gtbmFtZXNwYWNlIjogImRlZmF1bHQiCiAgICAgICAgfSwKICAgICAgICAiTGludXgiOiB7CiAgICAgICAgICAiTmFtZXNwYWNlcyI6IFsKICAgICAgICAgICAgewogICAgICAgICAgICAgICJUeXBlIjogImlwYyIsCiAgICAgICAgICAgICAgIlBhdGgiOiAiIgogICAgICAgICAgICB9LAogICAgICAgICAgICB7CiAgICAgICAgICAgICAgIlR5cGUiOiAidXRzIiwKICAgICAgICAgICAgICAiUGF0aCI6ICIiCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAiVHlwZSI6ICJtb3VudCIsCiAgICAgICAgICAgICAgIlBhdGgiOiAiIgogICAgICAgICAgICB9CiAgICAgICAgICBdLAogICAgICAgICAgIk1hc2tlZFBhdGhzIjogW10sCiAgICAgICAgICAiUmVhZG9ubHlQYXRocyI6IFtdCiAgICAgICAgfQogICAgICB9LAogICAgICAic3RvcmFnZXMiOiBbCiAgICAgICAgewogICAgICAgICAgImRyaXZlciI6ICJibGsiLAogICAgICAgICAgImRyaXZlcl9vcHRpb25zIjogW10sCiAgICAgICAgICAic291cmNlIjogIiIsCiAgICAgICAgICAiZnN0eXBlIjogInRhciIsCiAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgIiQoaGFzaDApIgogICAgICAgICAgXSwKICAgICAgICAgICJtb3VudF9wb2ludCI6ICIkKGxheWVyMCkiLAogICAgICAgICAgImZzX2dyb3VwIjogbnVsbAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgImRyaXZlciI6ICJibGsiLAogICAgICAgICAgImRyaXZlcl9vcHRpb25zIjogW10sCiAgICAgICAgICAic291cmNlIjogIiIsCiAgICAgICAgICAiZnN0eXBlIjogInRhciIsCiAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgIiQoaGFzaDEpIgogICAgICAgICAgXSwKICAgICAgICAgICJtb3VudF9wb2ludCI6ICIkKGxheWVyMSkiLAogICAgICAgICAgImZzX2dyb3VwIjogbnVsbAogICAgICAgIH0sCiAgICAgICAgewogICAgICAgICAgImRyaXZlciI6ICJvdmVybGF5ZnMiLAogICAgICAgICAgImRyaXZlcl9vcHRpb25zIjogW10sCiAgICAgICAgICAic291cmNlIjogIiIsCiAgICAgICAgICAiZnN0eXBlIjogImZ1c2UzLmthdGEtb3ZlcmxheSIsCiAgICAgICAgICAib3B0aW9ucyI6IFsKICAgICAgICAgICAgIjg5NzVjNWJlZGMwYmI2NTE0NjU2ZTAwOTAyZTNlMDA5NGI4MzI5YzhlY2E4MmYwYTcyYjU3ZDQ3ZjcyMmI0Y2Q6MTkzYzFlYzI2MGJmNmY1YzBiNmJmNDc4YjQ4MDg0Mjk3OWU5ZDAzMWU4YWJmNjdiOGMwNjI2MWRhYzU4Zjg0YSIsCiAgICAgICAgICAgICJiMGE0NDNkYzYwYTc5MDZmNTBiZTQ4MjhlYzk3OGU5NDAxM2M3ZTg1ODE4NGUyMzE4NzAyZDVlOTBkMzdjNmE5OjMxOGYyMTA0MDVkOGE4YmFjMDczZGUwMWU5MmJiOTE4YTQwMWMyNjgxOGIxZjE4ZTczMmM5MTkyMGQwZDQ3YjYiCiAgICAgICAgICBdLAogICAgICAgICAgIm1vdW50X3BvaW50IjogIiQoY3BhdGgpLyQoYnVuZGxlLWlkKSIsCiAgICAgICAgICAiZnNfZ3JvdXAiOiBudWxsCiAgICAgICAgfQogICAgICBdLAogICAgICAiZXhlY19jb21tYW5kcyI6IFtdCiAgICB9CiAgXSwKICAiY29tbW9uIjogewogICAgImNwYXRoIjogIi9ydW4va2F0YS1jb250YWluZXJzL3NoYXJlZC9jb250YWluZXJzIiwKICAgICJzZnByZWZpeCI6ICJeJChjcGF0aCkvJChidW5kbGUtaWQpLVthLXowLTldezE2fS0iLAogICAgImlwdjRfYSI6ICIoKFswLTldfFsxLTldWzAtOV18MVswLTldezJ9fDJbMC00XVswLTldfDI1WzAtNV0pXFwuKXszfShbMC05XXxbMS05XVswLTldfDFbMC05XXsyfXwyWzAtNF1bMC05XXwyNVswLTVdKSIsCiAgICAiaXBfcCI6ICJbMC05XXsxLDV9IiwKICAgICJzdmNfbmFtZSI6ICJbQS1aX1xcLlxcLV0rIiwKICAgICJkbnNfbGFiZWwiOiAiW2EtekEtWjAtOV9cXC5cXC1dKyIsCiAgICAiZGVmYXVsdF9jYXBzIjogWwogICAgICAiQ0FQX0NIT1dOIiwKICAgICAgIkNBUF9EQUNfT1ZFUlJJREUiLAogICAgICAiQ0FQX0ZTRVRJRCIsCiAgICAgICJDQVBfRk9XTkVSIiwKICAgICAgIkNBUF9NS05PRCIsCiAgICAgICJDQVBfTkVUX1JBVyIsCiAgICAgICJDQVBfU0VUR0lEIiwKICAgICAgIkNBUF9TRVRVSUQiLAogICAgICAiQ0FQX1NFVEZDQVAiLAogICAgICAiQ0FQX1NFVFBDQVAiLAogICAgICAiQ0FQX05FVF9CSU5EX1NFUlZJQ0UiLAogICAgICAiQ0FQX1NZU19DSFJPT1QiLAogICAgICAiQ0FQX0tJTEwiLAogICAgICAiQ0FQX0FVRElUX1dSSVRFIgogICAgXSwKICAgICJwcml2aWxlZ2VkX2NhcHMiOiBbCiAgICAgICJDQVBfQ0hPV04iLAogICAgICAiQ0FQX0RBQ19PVkVSUklERSIsCiAgICAgICJDQVBfREFDX1JFQURfU0VBUkNIIiwKICAgICAgIkNBUF9GT1dORVIiLAogICAgICAiQ0FQX0ZTRVRJRCIsCiAgICAgICJDQVBfS0lMTCIsCiAgICAgICJDQVBfU0VUR0lEIiwKICAgICAgIkNBUF9TRVRVSUQiLAogICAgICAiQ0FQX1NFVFBDQVAiLAogICAgICAiQ0FQX0xJTlVYX0lNTVVUQUJMRSIsCiAgICAgICJDQVBfTkVUX0JJTkRfU0VSVklDRSIsCiAgICAgICJDQVBfTkVUX0JST0FEQ0FTVCIsCiAgICAgICJDQVBfTkVUX0FETUlOIiwKICAgICAgIkNBUF9ORVRfUkFXIiwKICAgICAgIkNBUF9JUENfTE9DSyIsCiAgICAgICJDQVBfSVBDX09XTkVSIiwKICAgICAgIkNBUF9TWVNfTU9EVUxFIiwKICAgICAgIkNBUF9TWVNfUkFXSU8iLAogICAgICAiQ0FQX1NZU19DSFJPT1QiLAogICAgICAiQ0FQX1NZU19QVFJBQ0UiLAogICAgICAiQ0FQX1NZU19QQUNDVCIsCiAgICAgICJDQVBfU1lTX0FETUlOIiwKICAgICAgIkNBUF9TWVNfQk9PVCIsCiAgICAgICJDQVBfU1lTX05JQ0UiLAogICAgICAiQ0FQX1NZU19SRVNPVVJDRSIsCiAgICAgICJDQVBfU1lTX1RJTUUiLAogICAgICAiQ0FQX1NZU19UVFlfQ09ORklHIiwKICAgICAgIkNBUF9NS05PRCIsCiAgICAgICJDQVBfTEVBU0UiLAogICAgICAiQ0FQX0FVRElUX1dSSVRFIiwKICAgICAgIkNBUF9BVURJVF9DT05UUk9MIiwKICAgICAgIkNBUF9TRVRGQ0FQIiwKICAgICAgIkNBUF9NQUNfT1ZFUlJJREUiLAogICAgICAiQ0FQX01BQ19BRE1JTiIsCiAgICAgICJDQVBfU1lTTE9HIiwKICAgICAgIkNBUF9XQUtFX0FMQVJNIiwKICAgICAgIkNBUF9CTE9DS19TVVNQRU5EIiwKICAgICAgIkNBUF9BVURJVF9SRUFEIiwKICAgICAgIkNBUF9QRVJGTU9OIiwKICAgICAgIkNBUF9CUEYiLAogICAgICAiQ0FQX0NIRUNLUE9JTlRfUkVTVE9SRSIKICAgIF0KICB9LAogICJyZXF1ZXN0X2RlZmF1bHRzIjogewogICAgIkNyZWF0ZUNvbnRhaW5lclJlcXVlc3QiOiB7CiAgICAgICJhbGxvd19lbnZfcmVnZXgiOiBbCiAgICAgICAgIl5IT1NUTkFNRT0kKGRuc19sYWJlbCkkIiwKICAgICAgICAiXiQoc3ZjX25hbWUpX1BPUlRfJChpcF9wKV9UQ1A9dGNwOi8vJChpcHY0X2EpOiQoaXBfcCkkIiwKICAgICAgICAiXiQoc3ZjX25hbWUpX1BPUlRfJChpcF9wKV9UQ1BfUFJPVE89dGNwJCIsCiAgICAgICAgIl4kKHN2Y19uYW1lKV9QT1JUXyQoaXBfcClfVENQX1BPUlQ9JChpcF9wKSQiLAogICAgICAgICJeJChzdmNfbmFtZSlfUE9SVF8kKGlwX3ApX1RDUF9BRERSPSQoaXB2NF9hKSQiLAogICAgICAgICJeJChzdmNfbmFtZSlfU0VSVklDRV9IT1NUPSQoaXB2NF9hKSQiLAogICAgICAgICJeJChzdmNfbmFtZSlfU0VSVklDRV9QT1JUPSQoaXBfcCkkIiwKICAgICAgICAiXiQoc3ZjX25hbWUpX1NFUlZJQ0VfUE9SVF8kKGRuc19sYWJlbCk9JChpcF9wKSQiLAogICAgICAgICJeJChzdmNfbmFtZSlfUE9SVD10Y3A6Ly8kKGlwdjRfYSk6JChpcF9wKSQiLAogICAgICAgICJeQVpVUkVfQ0xJRU5UX0lEPVtBLUZhLWYwLTktXSskIiwKICAgICAgICAiXkFaVVJFX1RFTkFOVF9JRD1bQS1GYS1mMC05LV0rJCIsCiAgICAgICAgIl5BWlVSRV9GRURFUkFURURfVE9LRU5fRklMRT0vdmFyL3J1bi9zZWNyZXRzL2F6dXJlL3Rva2Vucy9henVyZS1pZGVudGl0eS10b2tlbiQiLAogICAgICAgICJeQVpVUkVfQVVUSE9SSVRZX0hPU1Q9aHR0cHM6Ly9sb2dpblxcLm1pY3Jvc29mdG9ubGluZVxcLmNvbS8kIgogICAgICBdCiAgICB9LAogICAgIkNvcHlGaWxlUmVxdWVzdCI6IFsKICAgICAgIl4kKGNwYXRoKS8iCiAgICBdLAogICAgIkV4ZWNQcm9jZXNzUmVxdWVzdCI6IHsKICAgICAgImNvbW1hbmRzIjogW10sCiAgICAgICJyZWdleCI6IFtdCiAgICB9LAogICAgIlJlYWRTdHJlYW1SZXF1ZXN0IjogZmFsc2UsCiAgICAiV3JpdGVTdHJlYW1SZXF1ZXN0IjogZmFsc2UKICB9Cn0= +spec: + restartPolicy: Never + runtimeClassName: kata-cc + containers: + - name: busybox + image: "quay.io/prometheus/busybox:latest" + stdin: true + env: + - name: POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: POD_IP + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: status.podIP + - name: SERVICE_ACCOUNT + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.serviceAccountName + - name: PROXY_CONFIG + value: "{}\n" + - name: ISTIO_META_POD_PORTS + value: "[\n]" + - name: ISTIO_META_APP_CONTAINERS + value: serviceaclient + - name: ISTIO_META_CLUSTER_ID + value: Kubernetes + - name: ISTIO_META_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + securityContext: + privileged: true + command: + - /bin/sh + args: + - "-c" + - while true; do echo $(POD_NAME); sleep 10; done diff --git a/src/tools/genpolicy/src/config_map.rs b/src/tools/genpolicy/src/config_map.rs new file mode 100644 index 0000000000..a6f2fa0e38 --- /dev/null +++ b/src/tools/genpolicy/src/config_map.rs @@ -0,0 +1,128 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::obj_meta; +use crate::pod; +use crate::policy; +use crate::settings; +use crate::yaml; + +use async_trait::async_trait; +use log::debug; +use protocols::agent; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; +use std::fs::File; + +/// See Reference / Kubernetes API / Config and Storage Resources / ConfigMap. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ConfigMap { + apiVersion: String, + kind: String, + pub metadata: obj_meta::ObjectMeta, + + #[serde(skip_serializing_if = "Option::is_none")] + pub data: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub binaryData: Option>>, + + #[serde(skip_serializing_if = "Option::is_none")] + immutable: Option, + + #[serde(skip)] + doc_mapping: serde_yaml::Value, +} + +impl ConfigMap { + pub fn new(file: &str) -> anyhow::Result { + debug!("Reading ConfigMap..."); + let config_map: ConfigMap = serde_yaml::from_reader(File::open(file)?)?; + debug!("\nRead ConfigMap => {:#?}", config_map); + + Ok(config_map) + } + + pub fn get_value(&self, value_from: &pod::EnvVarSource) -> Option { + if let Some(key_ref) = &value_from.configMapKeyRef { + if let Some(name) = &key_ref.name { + if let Some(my_name) = &self.metadata.name { + if my_name.eq(name) { + if let Some(data) = &self.data { + if let Some(value) = data.get(&key_ref.key) { + return Some(value.clone()); + } + } + } + } + } + } + + None + } +} + +pub fn get_value(value_from: &pod::EnvVarSource, config_maps: &Vec) -> Option { + for config_map in config_maps { + if let Some(value) = config_map.get_value(value_from) { + return Some(value); + } + } + + None +} + +#[async_trait] +impl yaml::K8sResource for ConfigMap { + async fn init( + &mut self, + _use_cache: bool, + doc_mapping: &serde_yaml::Value, + _silent_unsupported_fields: bool, + ) { + self.doc_mapping = doc_mapping.clone(); + } + + fn get_sandbox_name(&self) -> Option { + panic!("Unsupported"); + } + + fn get_namespace(&self) -> String { + panic!("Unsupported"); + } + + fn get_container_mounts_and_storages( + &self, + _policy_mounts: &mut Vec, + _storages: &mut Vec, + _container: &pod::Container, + _settings: &settings::Settings, + ) { + panic!("Unsupported"); + } + + fn generate_policy(&self, _agent_policy: &policy::AgentPolicy) -> String { + "".to_string() + } + + fn serialize(&mut self, _policy: &str) -> String { + serde_yaml::to_string(&self.doc_mapping).unwrap() + } + + fn get_containers(&self) -> &Vec { + panic!("Unsupported"); + } + + fn get_annotations(&self) -> &Option> { + &self.metadata.annotations + } + + fn use_host_network(&self) -> bool { + panic!("Unsupported"); + } +} diff --git a/src/tools/genpolicy/src/containerd.rs b/src/tools/genpolicy/src/containerd.rs new file mode 100644 index 0000000000..03f47f3cec --- /dev/null +++ b/src/tools/genpolicy/src/containerd.rs @@ -0,0 +1,163 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use crate::policy; + +// Default process field from containerd. +pub fn get_process(privileged_container: bool, common: &policy::CommonData) -> policy::KataProcess { + let capabilities = if privileged_container { + policy::KataLinuxCapabilities { + Ambient: vec![], + Bounding: common.privileged_caps.clone(), + Effective: common.privileged_caps.clone(), + Inheritable: vec![], + Permitted: common.privileged_caps.clone(), + } + } else { + policy::KataLinuxCapabilities { + Ambient: vec![], + Bounding: common.default_caps.clone(), + Effective: common.default_caps.clone(), + Inheritable: vec![], + Permitted: common.default_caps.clone(), + } + }; + + policy::KataProcess { + Terminal: false, + User: Default::default(), + Args: Vec::new(), + Env: Vec::new(), + Cwd: "/".to_string(), + Capabilities: capabilities, + NoNewPrivileges: true, + } +} + +// Default mounts field from containerd. +pub fn get_mounts(is_pause_container: bool, privileged_container: bool) -> Vec { + let sysfs_read_write_option = if privileged_container { "rw" } else { "ro" }; + + let mut mounts = vec![ + policy::KataMount { + destination: "/proc".to_string(), + type_: "proc".to_string(), + source: "proc".to_string(), + options: vec![ + "nosuid".to_string(), + "noexec".to_string(), + "nodev".to_string(), + ], + }, + policy::KataMount { + destination: "/dev".to_string(), + type_: "tmpfs".to_string(), + source: "tmpfs".to_string(), + options: vec![ + "nosuid".to_string(), + "strictatime".to_string(), + "mode=755".to_string(), + "size=65536k".to_string(), + ], + }, + policy::KataMount { + destination: "/dev/pts".to_string(), + type_: "devpts".to_string(), + source: "devpts".to_string(), + options: vec![ + "nosuid".to_string(), + "noexec".to_string(), + "newinstance".to_string(), + "ptmxmode=0666".to_string(), + "mode=0620".to_string(), + "gid=5".to_string(), + ], + }, + policy::KataMount { + destination: "/dev/shm".to_string(), + type_: "tmpfs".to_string(), + source: "shm".to_string(), + options: vec![ + "nosuid".to_string(), + "noexec".to_string(), + "nodev".to_string(), + "mode=1777".to_string(), + "size=65536k".to_string(), + ], + }, + policy::KataMount { + destination: "/dev/mqueue".to_string(), + type_: "mqueue".to_string(), + source: "mqueue".to_string(), + options: vec![ + "nosuid".to_string(), + "noexec".to_string(), + "nodev".to_string(), + ], + }, + policy::KataMount { + destination: "/sys".to_string(), + type_: "sysfs".to_string(), + source: "sysfs".to_string(), + options: vec![ + "nosuid".to_string(), + "noexec".to_string(), + "nodev".to_string(), + sysfs_read_write_option.to_string(), + ], + }, + ]; + + if !is_pause_container { + mounts.push(policy::KataMount { + destination: "/sys/fs/cgroup".to_string(), + type_: "cgroup".to_string(), + source: "cgroup".to_string(), + options: vec![ + "nosuid".to_string(), + "noexec".to_string(), + "nodev".to_string(), + "relatime".to_string(), + sysfs_read_write_option.to_string(), + ], + }); + } + + mounts +} + +// Default policy::KataLinux field from containerd. +pub fn get_linux(privileged_container: bool) -> policy::KataLinux { + if !privileged_container { + policy::KataLinux { + Namespaces: vec![], + MaskedPaths: vec![ + "/proc/acpi".to_string(), + "/proc/kcore".to_string(), + "/proc/keys".to_string(), + "/proc/latency_stats".to_string(), + "/proc/timer_list".to_string(), + "/proc/timer_stats".to_string(), + "/proc/sched_debug".to_string(), + "/proc/scsi".to_string(), + "/sys/firmware".to_string(), + ], + ReadonlyPaths: vec![ + "/proc/asound".to_string(), + "/proc/bus".to_string(), + "/proc/fs".to_string(), + "/proc/irq".to_string(), + "/proc/sys".to_string(), + "/proc/sysrq-trigger".to_string(), + ], + } + } else { + policy::KataLinux { + Namespaces: vec![], + MaskedPaths: vec![], + ReadonlyPaths: vec![], + } + } +} diff --git a/src/tools/genpolicy/src/main.rs b/src/tools/genpolicy/src/main.rs new file mode 100644 index 0000000000..1c582b798f --- /dev/null +++ b/src/tools/genpolicy/src/main.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use clap::Parser; +use env_logger; +use log::{debug, info}; + +mod config_map; +mod containerd; +mod mount_and_storage; +mod no_policy; +mod obj_meta; +mod persistent_volume_claim; +mod pod; +mod pod_template; +mod policy; +mod registry; +mod secret; +mod settings; +mod utils; +mod verity; +mod volume; +mod yaml; + +#[derive(Debug, Parser)] +struct CommandLineOptions { + #[clap( + short, + long, + help = "Kubernetes input/output YAML file path. stdin/stdout get used if this option is not specified." + )] + yaml_file: Option, + + #[clap( + short, + long, + help = "Optional Kubernetes config map YAML input file path" + )] + config_map_file: Option, + + #[clap( + short = 'j', + long, + default_value_t = String::from("genpolicy-settings.json"), + help = "genpolicy settings file name" + )] + settings_file_name: String, + + #[clap( + short, + long, + default_value_t = String::from("."), + help = "Path to the rules.rego and settings input files" + )] + input_files_path: String, + + #[clap( + short, + long, + help = "Create and use a cache of container image layer contents and dm-verity information (in ./layers_cache/)" + )] + use_cached_files: bool, + + #[clap( + short, + long, + help = "Print the output Rego policy text to standard output" + )] + raw_out: bool, + + #[clap( + short, + long, + help = "Print the base64 encoded output Rego policy to standard output" + )] + base64_out: bool, + + #[clap( + short, + long, + help = "Ignore unsupported input Kubernetes YAML fields. This is not recommeded unless you understand exactly how genpolicy works!" + )] + silent_unsupported_fields: bool, +} + +#[tokio::main] +async fn main() { + env_logger::init(); + + let args = CommandLineOptions::parse(); + + let mut config_map_files = Vec::new(); + if let Some(config_map_file) = &args.config_map_file { + config_map_files.push(config_map_file.clone()); + } + + let config = utils::Config::new( + args.use_cached_files, + args.yaml_file, + &args.input_files_path, + &args.settings_file_name, + &config_map_files, + args.silent_unsupported_fields, + args.raw_out, + args.base64_out, + ); + + debug!("Creating policy from yaml, settings, and rules.rego files..."); + let mut policy = policy::AgentPolicy::from_files(&config).await.unwrap(); + + debug!("Exporting policy to yaml file..."); + policy.export_policy(); + info!("Success!"); +} diff --git a/src/tools/genpolicy/src/mount_and_storage.rs b/src/tools/genpolicy/src/mount_and_storage.rs new file mode 100644 index 0000000000..ddcde8ac51 --- /dev/null +++ b/src/tools/genpolicy/src/mount_and_storage.rs @@ -0,0 +1,340 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow OCI spec field names. +#![allow(non_snake_case)] + +use crate::pod; +use crate::policy; +use crate::settings; +use crate::volume; + +use log::debug; +use protocols::agent; +use std::ffi::OsString; +use std::path::Path; +use std::str; + +pub fn get_policy_mounts( + settings: &settings::Settings, + p_mounts: &mut Vec, + yaml_container: &pod::Container, + is_pause_container: bool, +) { + let c_settings = settings.get_container_settings(is_pause_container); + let settings_mounts = &c_settings.Mounts; + let rootfs_access = if yaml_container.read_only_root_filesystem() { + "ro" + } else { + "rw" + }; + + for s_mount in settings_mounts { + if keep_settings_mount(settings, &s_mount, &yaml_container.volumeMounts) { + let mut mount = s_mount.clone(); + adjust_termination_path(&mut mount, &yaml_container); + + if mount.source.is_empty() && mount.type_.eq("bind") { + if let Some(file_name) = Path::new(&mount.destination).file_name() { + if let Some(file_name) = file_name.to_str() { + mount.source = format!("$(sfprefix){file_name}$"); + } + } + } + + if let Some(policy_mount) = p_mounts + .iter_mut() + .find(|m| m.destination.eq(&s_mount.destination)) + { + // Update an already existing mount. + policy_mount.type_ = mount.type_.clone(); + policy_mount.source = mount.source.clone(); + policy_mount.options = mount.options.iter().map(String::from).collect(); + } else { + // Add a new mount. + if !is_pause_container { + if s_mount.destination.eq("/etc/hostname") + || s_mount.destination.eq("/etc/resolv.conf") + { + mount.options.push(rootfs_access.to_string()); + } + } + p_mounts.push(mount); + } + } + } +} + +fn keep_settings_mount( + settings: &settings::Settings, + s_mount: &policy::KataMount, + yaml_mounts: &Option>, +) -> bool { + let destinations = &settings.mount_destinations; + let mut keep = destinations.iter().any(|d| s_mount.destination.eq(d)); + + if !keep { + if let Some(mounts) = yaml_mounts { + keep = mounts.iter().any(|m| m.mountPath.eq(&s_mount.destination)); + } + } + + keep +} + +fn adjust_termination_path(mount: &mut policy::KataMount, yaml_container: &pod::Container) { + if mount.destination == "/dev/termination-log" { + if let Some(path) = &yaml_container.terminationMessagePath { + mount.destination = path.clone(); + } + } +} + +pub fn get_mount_and_storage( + settings: &settings::Settings, + p_mounts: &mut Vec, + storages: &mut Vec, + yaml_volume: &volume::Volume, + yaml_mount: &pod::VolumeMount, +) { + if let Some(emptyDir) = &yaml_volume.emptyDir { + let memory_medium = if let Some(medium) = &emptyDir.medium { + medium == "Memory" + } else { + false + }; + get_empty_dir_mount_and_storage(settings, p_mounts, storages, yaml_mount, memory_medium); + } else if yaml_volume.persistentVolumeClaim.is_some() || yaml_volume.azureFile.is_some() { + get_shared_bind_mount(yaml_mount, p_mounts, "rprivate", "rw"); + } else if yaml_volume.hostPath.is_some() { + get_host_path_mount(yaml_mount, yaml_volume, p_mounts); + } else if yaml_volume.configMap.is_some() || yaml_volume.secret.is_some() { + get_config_map_mount_and_storage(settings, p_mounts, storages, yaml_mount); + } else if yaml_volume.projected.is_some() { + get_shared_bind_mount(yaml_mount, p_mounts, "rprivate", "ro"); + } else if yaml_volume.downwardAPI.is_some() { + get_downward_api_mount(yaml_mount, p_mounts); + } else { + todo!("Unsupported volume type {:?}", yaml_volume); + } +} + +fn get_empty_dir_mount_and_storage( + settings: &settings::Settings, + p_mounts: &mut Vec, + storages: &mut Vec, + yaml_mount: &pod::VolumeMount, + memory_medium: bool, +) { + let settings_volumes = &settings.volumes; + let settings_empty_dir = if memory_medium { + &settings_volumes.emptyDir_memory + } else { + &settings_volumes.emptyDir + }; + debug!("Settings emptyDir: {:?}", settings_empty_dir); + + if yaml_mount.subPathExpr.is_none() { + storages.push(agent::Storage { + driver: settings_empty_dir.driver.clone(), + driver_options: Vec::new(), + source: settings_empty_dir.source.clone(), + fstype: settings_empty_dir.fstype.clone(), + options: settings_empty_dir.options.clone(), + mount_point: format!("{}{}$", &settings_empty_dir.mount_point, &yaml_mount.name), + fs_group: protobuf::MessageField::none(), + special_fields: ::protobuf::SpecialFields::new(), + }); + } + + let source = if yaml_mount.subPathExpr.is_some() { + let file_name = Path::new(&yaml_mount.mountPath).file_name().unwrap(); + let name = OsString::from(file_name).into_string().unwrap(); + format!("{}{name}$", &settings_volumes.configMap.mount_source) + } else { + format!("{}{}$", &settings_empty_dir.mount_source, &yaml_mount.name) + }; + + let mount_type = if yaml_mount.subPathExpr.is_some() { + "bind" + } else { + &settings_empty_dir.mount_type + }; + + p_mounts.push(policy::KataMount { + destination: yaml_mount.mountPath.to_string(), + type_: mount_type.to_string(), + source, + options: vec![ + "rbind".to_string(), + "rprivate".to_string(), + "rw".to_string(), + ], + }); +} + +fn get_host_path_mount( + yaml_mount: &pod::VolumeMount, + yaml_volume: &volume::Volume, + p_mounts: &mut Vec, +) { + let host_path = yaml_volume.hostPath.as_ref().unwrap().path.clone(); + let path = Path::new(&host_path); + + let mut biderectional = false; + if let Some(mount_propagation) = &yaml_mount.mountPropagation { + if mount_propagation.eq("Bidirectional") { + debug!("get_host_path_mount: Bidirectional"); + biderectional = true; + } + } + + // TODO: + // + // - When volume.hostPath.path: /dev/ttyS0 + // "source": "/dev/ttyS0" + // - When volume.hostPath.path: /tmp/results + // "source": "^/run/kata-containers/shared/containers/$(bundle-id)-[a-z0-9]{16}-results$" + // + // What is the reason for this source path difference in the Guest OS? + if !path.starts_with("/dev/") && !path.starts_with("/sys/") { + debug!("get_host_path_mount: calling get_shared_bind_mount"); + let propagation = if biderectional { "rshared" } else { "rprivate" }; + get_shared_bind_mount(yaml_mount, p_mounts, propagation, "rw"); + } else { + let dest = yaml_mount.mountPath.clone(); + let type_ = "bind".to_string(); + let mount_option = if biderectional { "rshared" } else { "rprivate" }; + let options = vec![ + "rbind".to_string(), + mount_option.to_string(), + "rw".to_string(), + ]; + + if let Some(policy_mount) = p_mounts.iter_mut().find(|m| m.destination.eq(&dest)) { + debug!("get_host_path_mount: updating dest = {dest}, source = {host_path}"); + policy_mount.type_ = type_; + policy_mount.source = host_path; + policy_mount.options = options; + } else { + debug!("get_host_path_mount: adding dest = {dest}, source = {host_path}"); + p_mounts.push(policy::KataMount { + destination: dest, + type_, + source: host_path, + options, + }); + } + } +} + +fn get_config_map_mount_and_storage( + settings: &settings::Settings, + p_mounts: &mut Vec, + storages: &mut Vec, + yaml_mount: &pod::VolumeMount, +) { + let settings_volumes = &settings.volumes; + let settings_config_map = if settings.kata_config.confidential_guest { + &settings_volumes.confidential_configMap + } else { + &settings_volumes.configMap + }; + debug!("Settings configMap: {:?}", settings_config_map); + + if !settings.kata_config.confidential_guest { + let mount_path = Path::new(&yaml_mount.mountPath).file_name().unwrap(); + let mount_path_str = OsString::from(mount_path).into_string().unwrap(); + + storages.push(agent::Storage { + driver: settings_config_map.driver.clone(), + driver_options: Vec::new(), + source: format!("{}{}$", &settings_config_map.mount_source, &yaml_mount.name), + fstype: settings_config_map.fstype.clone(), + options: settings_config_map.options.clone(), + mount_point: format!("{}{mount_path_str}$", &settings_config_map.mount_point), + fs_group: protobuf::MessageField::none(), + special_fields: ::protobuf::SpecialFields::new(), + }); + } + + let file_name = Path::new(&yaml_mount.mountPath).file_name().unwrap(); + let name = OsString::from(file_name).into_string().unwrap(); + p_mounts.push(policy::KataMount { + destination: yaml_mount.mountPath.clone(), + type_: settings_config_map.mount_type.clone(), + source: format!("{}{name}$", &settings_config_map.mount_point), + options: settings_config_map.options.clone(), + }); +} + +fn get_shared_bind_mount( + yaml_mount: &pod::VolumeMount, + p_mounts: &mut Vec, + propagation: &str, + access: &str, +) { + let mount_path = if let Some(byte_index) = str::rfind(&yaml_mount.mountPath, '/') { + str::from_utf8(&yaml_mount.mountPath.as_bytes()[byte_index + 1..]).unwrap() + } else { + &yaml_mount.mountPath + }; + let source = format!("$(sfprefix){mount_path}$"); + + let dest = yaml_mount.mountPath.clone(); + let type_ = "bind".to_string(); + let options = vec![ + "rbind".to_string(), + propagation.to_string(), + access.to_string(), + ]; + + if let Some(policy_mount) = p_mounts.iter_mut().find(|m| m.destination.eq(&dest)) { + debug!("get_shared_bind_mount: updating dest = {dest}, source = {source}"); + policy_mount.type_ = type_; + policy_mount.source = source; + policy_mount.options = options; + } else { + debug!("get_shared_bind_mount: adding dest = {dest}, source = {source}"); + p_mounts.push(policy::KataMount { + destination: dest, + type_, + source, + options, + }); + } +} + +fn get_downward_api_mount(yaml_mount: &pod::VolumeMount, p_mounts: &mut Vec) { + let mount_path = if let Some(byte_index) = str::rfind(&yaml_mount.mountPath, '/') { + str::from_utf8(&yaml_mount.mountPath.as_bytes()[byte_index + 1..]).unwrap() + } else { + &yaml_mount.mountPath + }; + let source = format!("$(sfprefix){mount_path}$"); + + let dest = yaml_mount.mountPath.clone(); + let type_ = "bind".to_string(); + let options = vec![ + "rbind".to_string(), + "rprivate".to_string(), + "ro".to_string(), + ]; + + if let Some(policy_mount) = p_mounts.iter_mut().find(|m| m.destination.eq(&dest)) { + debug!("get_downward_api_mount: updating dest = {dest}, source = {source}"); + policy_mount.type_ = type_; + policy_mount.source = source; + policy_mount.options = options; + } else { + debug!("get_downward_api_mount: adding dest = {dest}, source = {source}"); + p_mounts.push(policy::KataMount { + destination: dest, + type_, + source, + options, + }); + } +} diff --git a/src/tools/genpolicy/src/no_policy.rs b/src/tools/genpolicy/src/no_policy.rs new file mode 100644 index 0000000000..30c1e038fe --- /dev/null +++ b/src/tools/genpolicy/src/no_policy.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::pod; +use crate::policy; +use crate::settings; +use crate::yaml; + +use async_trait::async_trait; +use protocols::agent; +use std::collections::BTreeMap; + +#[derive(Clone, Debug)] +pub struct NoPolicyResource { + pub yaml: String, +} + +#[async_trait] +impl yaml::K8sResource for NoPolicyResource { + async fn init( + &mut self, + _use_cache: bool, + _doc_mapping: &serde_yaml::Value, + _silent_unsupported_fields: bool, + ) { + } + + fn get_sandbox_name(&self) -> Option { + panic!("Unsupported"); + } + + fn get_namespace(&self) -> String { + panic!("Unsupported"); + } + + fn get_container_mounts_and_storages( + &self, + _policy_mounts: &mut Vec, + _storages: &mut Vec, + _container: &pod::Container, + _settings: &settings::Settings, + ) { + panic!("Unsupported"); + } + + fn generate_policy(&self, _agent_policy: &policy::AgentPolicy) -> String { + return "".to_string(); + } + + fn serialize(&mut self, _policy: &str) -> String { + self.yaml.clone() + } + + fn get_containers(&self) -> &Vec { + panic!("Unsupported"); + } + + fn get_annotations(&self) -> &Option> { + panic!("Unsupported"); + } + + fn use_host_network(&self) -> bool { + panic!("Unsupported"); + } +} diff --git a/src/tools/genpolicy/src/obj_meta.rs b/src/tools/genpolicy/src/obj_meta.rs new file mode 100644 index 0000000000..e8c55452f1 --- /dev/null +++ b/src/tools/genpolicy/src/obj_meta.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// See ObjectMeta in the Kubernetes API reference. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct ObjectMeta { + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + generateName: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + labels: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub annotations: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub namespace: Option, +} + +impl ObjectMeta { + pub fn get_name(&self) -> String { + if let Some(name) = &self.name { + name.clone() + } else if self.generateName.is_some() { + return "$(generated-name)".to_string(); + } else { + String::new() + } + } + + pub fn get_namespace(&self) -> String { + if let Some(namespace) = &self.namespace { + namespace.clone() + } else { + "default".to_string() + } + } +} diff --git a/src/tools/genpolicy/src/persistent_volume_claim.rs b/src/tools/genpolicy/src/persistent_volume_claim.rs new file mode 100644 index 0000000000..5ffa41b5f9 --- /dev/null +++ b/src/tools/genpolicy/src/persistent_volume_claim.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::obj_meta; + +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// See Reference / Kubernetes API / Config and Storage Resources / PersistentVolumeClaim. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct PersistentVolumeClaim { + #[serde(skip_serializing_if = "Option::is_none")] + apiVersion: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + kind: Option, + + pub metadata: obj_meta::ObjectMeta, + spec: PersistentVolumeClaimSpec, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / PersistentVolumeClaim. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +struct PersistentVolumeClaimSpec { + resources: ResourceRequirements, + + #[serde(skip_serializing_if = "Option::is_none")] + accessModes: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + storageClassName: Option, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / PersistentVolumeClaim. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct ResourceRequirements { + #[serde(skip_serializing_if = "Option::is_none")] + requests: Option>, +} diff --git a/src/tools/genpolicy/src/pod.rs b/src/tools/genpolicy/src/pod.rs new file mode 100644 index 0000000000..60dc54767b --- /dev/null +++ b/src/tools/genpolicy/src/pod.rs @@ -0,0 +1,808 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::config_map; +use crate::obj_meta; +use crate::policy; +use crate::registry; +use crate::secret; +use crate::settings; +use crate::volume; +use crate::yaml; + +use async_trait::async_trait; +use log::{debug, warn}; +use protocols::agent; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Pod { + apiVersion: String, + kind: String, + pub metadata: obj_meta::ObjectMeta, + pub spec: PodSpec, + + #[serde(skip)] + doc_mapping: serde_yaml::Value, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PodSpec { + pub containers: Vec, + + #[serde(skip_serializing_if = "Option::is_none")] + nodeSelector: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + restartPolicy: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + runtimeClassName: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub initContainers: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + imagePullSecrets: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + affinity: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub volumes: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + serviceAccountName: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + serviceAccount: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + terminationGracePeriodSeconds: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + tolerations: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + hostname: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub hostNetwork: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct Container { + /// Container image registry information. + #[serde(skip)] + pub registry: registry::Container, + + pub name: String, + pub image: String, + + #[serde(skip_serializing_if = "Option::is_none")] + imagePullPolicy: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + securityContext: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub volumeMounts: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + env: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + resources: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + ports: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub command: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + pub args: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + lifecycle: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + livenessProbe: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + readinessProbe: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + startupProbe: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub serviceAccountName: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + stdin: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub tty: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub terminationMessagePath: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Affinity { + #[serde(skip_serializing_if = "Option::is_none")] + pub podAntiAffinity: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub podAffinity: Option, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct PodAffinity { + #[serde(skip_serializing_if = "Option::is_none")] + requiredDuringSchedulingIgnoredDuringExecution: Option>, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct PodAntiAffinity { + #[serde(skip_serializing_if = "Option::is_none")] + preferredDuringSchedulingIgnoredDuringExecution: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + requiredDuringSchedulingIgnoredDuringExecution: Option>, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct WeightedPodAffinityTerm { + weight: i32, + podAffinityTerm: PodAffinityTerm, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct PodAffinityTerm { + topologyKey: String, + + #[serde(skip_serializing_if = "Option::is_none")] + labelSelector: Option, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Probe { + #[serde(skip_serializing_if = "Option::is_none")] + exec: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + initialDelaySeconds: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + timeoutSeconds: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + periodSeconds: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + failureThreshold: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + successThreshold: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + httpGet: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + tcpSocket: Option, + // TODO: additional fiels. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct TCPSocketAction { + port: String, + + #[serde(skip_serializing_if = "Option::is_none")] + host: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct HTTPGetAction { + port: String, + + #[serde(skip_serializing_if = "Option::is_none")] + host: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + path: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + scheme: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + httpHeaders: Option>, + // TODO: additional fiels. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct HTTPHeader { + name: String, + value: String, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct SecurityContext { + #[serde(skip_serializing_if = "Option::is_none")] + readOnlyRootFilesystem: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + allowPrivilegeEscalation: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + privileged: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + capabilities: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + runAsUser: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Lifecycle { + #[serde(skip_serializing_if = "Option::is_none")] + postStart: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + preStop: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct LifecycleHandler { + #[serde(skip_serializing_if = "Option::is_none")] + exec: Option, + // TODO: additional fiels. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct ExecAction { + command: Vec, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Capabilities { + #[serde(skip_serializing_if = "Option::is_none")] + add: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + drop: Option>, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct ContainerPort { + containerPort: i32, + + #[serde(skip_serializing_if = "Option::is_none")] + hostIP: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + hostPort: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + protocol: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct EnvVar { + name: String, + + #[serde(skip_serializing_if = "Option::is_none")] + value: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + valueFrom: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EnvVarSource { + #[serde(skip_serializing_if = "Option::is_none")] + pub configMapKeyRef: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + fieldRef: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub secretKeyRef: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + resourceFieldRef: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SecretKeySelector { + pub key: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + optional: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ConfigMapKeySelector { + pub key: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + optional: Option, +} + +/// See Reference / Kubernetes API / Common Definitions / ResourceFieldSelector. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct ResourceFieldSelector { + resource: String, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Common Definitions / ObjectFieldSelector. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ObjectFieldSelector { + fieldPath: String, + + #[serde(skip_serializing_if = "Option::is_none")] + apiVersion: Option, +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct VolumeMount { + pub mountPath: String, + pub name: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub mountPropagation: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub subPathExpr: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub readOnly: Option, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct ResourceRequirements { + #[serde(skip_serializing_if = "Option::is_none")] + requests: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + limits: Option>, + // TODO: claims field. +} + +/// See Reference / Kubernetes API / Workload Resources / Pod. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Toleration { + #[serde(skip_serializing_if = "Option::is_none")] + key: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + operator: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + value: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + effect: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + tolerationSeconds: Option, +} + +/// See Reference / Kubernetes API / Common Definitions / LocalObjectReference. +#[derive(Clone, Debug, Serialize, Deserialize)] +struct LocalObjectReference { + name: String, +} + +impl Container { + pub async fn init(&mut self, use_cache: bool) { + // Load container image properties from the registry. + self.registry = registry::get_container(use_cache, &self.image) + .await + .unwrap(); + } + + pub fn get_env_variables( + &self, + dest_env: &mut Vec, + config_maps: &Vec, + secrets: &Vec, + namespace: &str, + annotations: &Option>, + service_account_name: &str, + ) { + if let Some(source_env) = &self.env { + for env_variable in source_env { + let value = env_variable.get_value( + config_maps, + secrets, + namespace, + annotations, + service_account_name, + ); + let src_string = format!("{}={value}", &env_variable.name); + + if !dest_env.contains(&src_string) { + dest_env.push(src_string.clone()); + } + } + } + } + + pub fn is_privileged(&self) -> bool { + if let Some(context) = &self.securityContext { + if let Some(privileged) = context.privileged { + return privileged; + } + } + false + } + + pub fn allow_privilege_escalation(&self) -> bool { + if let Some(context) = &self.securityContext { + if let Some(allow) = context.allowPrivilegeEscalation { + return allow; + } + } + true + } + + pub fn read_only_root_filesystem(&self) -> bool { + if let Some(context) = &self.securityContext { + if let Some(read_only) = context.readOnlyRootFilesystem { + return read_only; + } + } + false + } + + pub fn get_process_args(&self, policy_args: &mut Vec) -> (bool, bool) { + let mut yaml_has_command = true; + let mut yaml_has_args = true; + + if let Some(commands) = &self.command { + for command in commands { + policy_args.push(command.clone()); + } + } else { + yaml_has_command = false; + } + + if let Some(args) = &self.args { + for arg in args { + policy_args.push(arg.clone()); + } + } else { + yaml_has_args = false; + } + + (yaml_has_command, yaml_has_args) + } + + pub fn get_exec_commands(&self) -> Vec { + let mut commands = Vec::new(); + + if let Some(probe) = &self.livenessProbe { + if let Some(exec) = &probe.exec { + commands.push(exec.command.join(" ")); + } + } + + if let Some(probe) = &self.readinessProbe { + if let Some(exec) = &probe.exec { + commands.push(exec.command.join(" ")); + } + } + + if let Some(probe) = &self.startupProbe { + if let Some(exec) = &probe.exec { + commands.push(exec.command.join(" ")); + } + } + + if let Some(lifecycle) = &self.lifecycle { + if let Some(postStart) = &lifecycle.postStart { + if let Some(exec) = &postStart.exec { + commands.push(exec.command.join(" ")); + } + } + if let Some(preStop) = &lifecycle.preStop { + if let Some(exec) = &preStop.exec { + commands.push(exec.command.join(" ")); + } + } + } + + commands + } +} + +impl EnvVar { + pub fn get_value( + &self, + config_maps: &Vec, + secrets: &Vec, + namespace: &str, + annotations: &Option>, + service_account_name: &str, + ) -> String { + if let Some(value) = &self.value { + return value.clone(); + } + + if let Some(value_from) = &self.valueFrom { + if let Some(value) = config_map::get_value(value_from, config_maps) { + return value.clone(); + } + + if let Some(value) = secret::get_value(value_from, secrets) { + return value.clone(); + } + + if let Some(field_ref) = &value_from.fieldRef { + let path: &str = &field_ref.fieldPath; + match path { + "metadata.name" => return "$(sandbox-name)".to_string(), + "metadata.namespace" => return namespace.to_string(), + "metadata.uid" => return "$(pod-uid)".to_string(), + "status.hostIP" => return "$(host-ip)".to_string(), + "status.podIP" => return "$(pod-ip)".to_string(), + "spec.nodeName" => return "$(node-name)".to_string(), + "spec.serviceAccountName" => return service_account_name.to_string(), + _ => { + if let Some(value) = self.get_annotation_value(path, annotations) { + return value; + } else { + panic!( + "Env var: unsupported field reference: {}", + &field_ref.fieldPath + ) + } + } + } + } + + if value_from.resourceFieldRef.is_some() { + // TODO: should resource fields such as "limits.cpu" or "limits.memory" + // be handled in a different way? + return "$(resource-field)".to_string(); + } + } else { + panic!("Environment variable without value or valueFrom!"); + } + + panic!("Couldn't get the value of env var: {}", &self.name); + } + + fn get_annotation_value( + &self, + reference: &str, + anno: &Option>, + ) -> Option { + let prefix = "metadata.annotations['"; + let suffix = "']"; + if reference.starts_with(prefix) && reference.ends_with(suffix) { + if let Some(annotations) = anno { + let start = prefix.len(); + let end = reference.len() - 2; + let annotation = reference[start..end].to_string(); + + if let Some(value) = annotations.get(&annotation) { + return Some(value.clone()); + } else { + warn!( + "Can't find the value of annotation {}. Allowing any value.", + &annotation + ); + } + } + + // TODO: should missing annotations be handled differently? + return Some("$(todo-annotation)".to_string()); + } + None + } +} + +#[async_trait] +impl yaml::K8sResource for Pod { + async fn init(&mut self, use_cache: bool, doc_mapping: &serde_yaml::Value, _silent: bool) { + yaml::k8s_resource_init(&mut self.spec, use_cache).await; + self.doc_mapping = doc_mapping.clone(); + } + + fn get_sandbox_name(&self) -> Option { + let name = self.metadata.get_name(); + if !name.is_empty() { + return Some(name); + } + panic!("No pod name."); + } + + fn get_namespace(&self) -> String { + self.metadata.get_namespace() + } + + fn get_container_mounts_and_storages( + &self, + policy_mounts: &mut Vec, + storages: &mut Vec, + container: &Container, + settings: &settings::Settings, + ) { + if let Some(volumes) = &self.spec.volumes { + yaml::get_container_mounts_and_storages( + policy_mounts, + storages, + container, + settings, + volumes, + ); + } + } + + fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String { + agent_policy.generate_policy(self) + } + + fn serialize(&mut self, policy: &str) -> String { + yaml::add_policy_annotation(&mut self.doc_mapping, "metadata", policy); + serde_yaml::to_string(&self.doc_mapping).unwrap() + } + + fn get_containers(&self) -> &Vec { + &self.spec.containers + } + + fn get_annotations(&self) -> &Option> { + &self.metadata.annotations + } + + fn use_host_network(&self) -> bool { + if let Some(host_network) = self.spec.hostNetwork { + return host_network; + } + false + } +} + +impl Container { + pub fn apply_capabilities( + &self, + capabilities: &mut policy::KataLinuxCapabilities, + defaults: &policy::CommonData, + ) { + assert!(capabilities.Ambient.is_empty()); + assert!(capabilities.Inheritable.is_empty()); + + if let Some(securityContext) = &self.securityContext { + if let Some(yaml_capabilities) = &securityContext.capabilities { + if let Some(drop) = &yaml_capabilities.drop { + for c in drop { + if c == "ALL" { + capabilities.Bounding.clear(); + capabilities.Permitted.clear(); + capabilities.Effective.clear(); + } else { + let cap = "CAP_".to_string() + &c; + + capabilities.Bounding.retain(|x| !x.eq(&cap)); + capabilities.Permitted.retain(|x| !x.eq(&cap)); + capabilities.Effective.retain(|x| !x.eq(&cap)); + } + } + } + if let Some(add) = &yaml_capabilities.add { + for c in add { + let cap = "CAP_".to_string() + &c; + + if !capabilities.Bounding.contains(&cap) { + capabilities.Bounding.push(cap.clone()); + } + if !capabilities.Permitted.contains(&cap) { + capabilities.Permitted.push(cap.clone()); + } + if !capabilities.Effective.contains(&cap) { + capabilities.Effective.push(cap.clone()); + } + } + } + } + } + compress_default_capabilities(capabilities, defaults); + } +} + +fn compress_default_capabilities( + capabilities: &mut policy::KataLinuxCapabilities, + defaults: &policy::CommonData, +) { + assert!(capabilities.Ambient.is_empty()); + assert!(capabilities.Inheritable.is_empty()); + + compress_capabilities(&mut capabilities.Bounding, defaults); + compress_capabilities(&mut capabilities.Permitted, defaults); + compress_capabilities(&mut capabilities.Effective, defaults); +} + +fn compress_capabilities(capabilities: &mut Vec, defaults: &policy::CommonData) { + let default_caps = if capabilities == &defaults.default_caps { + "$(default_caps)" + } else if capabilities == &defaults.privileged_caps { + "$(privileged_caps)" + } else { + "" + }; + + if default_caps.len() != 0 { + capabilities.clear(); + capabilities.push(default_caps.to_string()); + } +} + +pub async fn add_pause_container(containers: &mut Vec, use_cache: bool) { + debug!("Adding pause container..."); + let mut pause_container = Container { + // TODO: load this path from the settings file. + image: "mcr.microsoft.com/oss/kubernetes/pause:3.6".to_string(), + + name: String::new(), + imagePullPolicy: None, + securityContext: Some(SecurityContext { + readOnlyRootFilesystem: Some(true), + allowPrivilegeEscalation: Some(false), + privileged: None, + capabilities: None, + runAsUser: None, + }), + ..Default::default() + }; + pause_container.init(use_cache).await; + containers.insert(0, pause_container); + debug!("pause container added."); +} diff --git a/src/tools/genpolicy/src/pod_template.rs b/src/tools/genpolicy/src/pod_template.rs new file mode 100644 index 0000000000..f4b7c6c2eb --- /dev/null +++ b/src/tools/genpolicy/src/pod_template.rs @@ -0,0 +1,28 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::obj_meta; +use crate::pod; + +use serde::{Deserialize, Serialize}; + +/// Reference / Kubernetes API / Workload / Resources / PodTemplate. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PodTemplate { + apiVersion: String, + kind: String, + metadata: obj_meta::ObjectMeta, + spec: PodTemplateSpec, +} + +/// Reference / Kubernetes API / Workload / Resources / PodTemplate. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PodTemplateSpec { + pub metadata: obj_meta::ObjectMeta, + pub spec: pod::PodSpec, +} diff --git a/src/tools/genpolicy/src/policy.rs b/src/tools/genpolicy/src/policy.rs new file mode 100644 index 0000000000..17458da9e7 --- /dev/null +++ b/src/tools/genpolicy/src/policy.rs @@ -0,0 +1,900 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow OCI spec field names. +#![allow(non_snake_case)] + +use crate::config_map; +use crate::containerd; +use crate::mount_and_storage; +use crate::pod; +use crate::policy; +use crate::registry; +use crate::secret; +use crate::settings; +use crate::utils; +use crate::yaml; + +use anyhow::Result; +use base64::{engine::general_purpose, Engine as _}; +use log::debug; +use protocols::agent; +use serde::{Deserialize, Serialize}; +use serde_yaml::Value; +use sha2::{Digest, Sha256}; +use std::boxed; +use std::collections::BTreeMap; +use std::fs::read_to_string; +use std::io::Write; + +// TODO: load this value from the settings file. +const DEFAULT_OCI_VERSION: &str = "1.1.0-rc.1"; + +/// Intermediary format of policy data. +pub struct AgentPolicy { + /// K8s resources described by the input YAML file. + resources: Vec>, + + /// K8s ConfigMap resources described by an additional input YAML file + /// or by the "main" input YAML file, containing additional pod settings. + config_maps: Vec, + + /// K8s Secret resources, containing additional pod settings. + secrets: Vec, + + /// Rego rules read from a file (rules.rego). + pub rules: String, + + /// Settings loaded from genpolicy-settings.json. + pub settings: settings::Settings, + + /// Additional Policy settings. + pub config: utils::Config, +} + +/// Representation of the policy_data field from the output policy text. +#[derive(Debug, Serialize)] +pub struct PolicyData { + /// Policy properties for each container allowed to be executed in a pod. + pub containers: Vec, + + /// Settings read from genpolicy-settings.json. + pub common: CommonData, + + /// Settings read from genpolicy-settings.json, related directly to each + /// kata agent endpoint, that get added to the output policy. + pub request_defaults: RequestDefaults, +} + +/// OCI Container spec. This struct is very similar to the Spec struct from +/// Kata Containers. The main difference is that the Annotations field below +/// is ordered, thus resulting in the same output policy contents every time +/// when this apps runs with the same inputs. Also, it preserves the upper +/// case field names, for consistency with the structs used by agent's rpc.rs. +#[derive(Debug, Deserialize, Serialize)] +pub struct KataSpec { + /// Version of the Open Container Initiative Runtime Specification with which the bundle complies. + #[serde(default = "version_default")] + pub Version: String, + + /// Process configures the container process. + #[serde(default)] + pub Process: KataProcess, + + /// Root configures the container's root filesystem. + pub Root: KataRoot, + + /// Mounts configures additional mounts (on top of Root). + #[serde(skip_serializing_if = "Vec::is_empty")] + pub Mounts: Vec, + + /// Hooks configures callbacks for container lifecycle events. + #[serde(skip_serializing_if = "Option::is_none")] + pub Hooks: Option, + + /// Annotations contains arbitrary metadata for the container. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub Annotations: BTreeMap, + + /// Linux is platform-specific configuration for Linux based containers. + #[serde(default)] + pub Linux: KataLinux, +} + +fn version_default() -> String { + DEFAULT_OCI_VERSION.to_string() +} + +/// OCI container Process struct. This struct is very similar to the Process +/// struct generated from oci.proto. The main difference is that it preserves +/// the upper case field names from oci.proto, for consistency with the structs +/// used by agent's rpc.rs. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +pub struct KataProcess { + /// Terminal creates an interactive terminal for the container. + #[serde(default)] + pub Terminal: bool, + + /// User specifies user information for the process. + #[serde(default)] + pub User: KataUser, + + /// Args specifies the binary and arguments for the application to execute. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub Args: Vec, + + /// Env populates the process environment for the process. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub Env: Vec, + + /// Cwd is the current working directory for the process and must be + /// relative to the container's root. + #[serde(default, skip_serializing_if = "String::is_empty")] + pub Cwd: String, + + /// Capabilities are Linux capabilities that are kept for the process. + #[serde(default)] + pub Capabilities: KataLinuxCapabilities, + + /// NoNewPrivileges controls whether additional privileges could be gained by processes in the container. + #[serde(default)] + pub NoNewPrivileges: bool, +} + +/// OCI container User struct. This struct is very similar to the User +/// struct generated from oci.proto. The main difference is that it preserves +/// the upper case field names from oci.proto, for consistency with the structs +/// used by agent's rpc.rs. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +pub struct KataUser { + /// UID is the user id. + pub UID: u32, + + /// GID is the group id. + pub GID: u32, + + /// AdditionalGids are additional group ids set for the container's process. + pub AdditionalGids: Vec, + + /// Username is the user name. + pub Username: String, +} + +/// OCI container Root struct. This struct is very similar to the Root +/// struct generated from oci.proto. The main difference is that it preserves the +/// upper case field names from oci.proto, for consistency with the structs used +/// by agent's rpc.rs. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct KataRoot { + /// Path is the absolute path to the container's root filesystem. + pub Path: String, + + /// Readonly makes the root filesystem for the container readonly before the process is executed. + #[serde(default)] + pub Readonly: bool, +} + +/// OCI container Linux struct. This struct is similar to the Linux struct +/// generated from oci.proto, but includes just the fields that are currently +/// relevant for automatic generation of policy. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +pub struct KataLinux { + /// Namespaces contains the namespaces that are created and/or joined by the container + #[serde(default)] + pub Namespaces: Vec, + + /// MaskedPaths masks over the provided paths inside the container. + pub MaskedPaths: Vec, + + /// ReadonlyPaths sets the provided paths as RO inside the container. + pub ReadonlyPaths: Vec, +} + +/// OCI container LinuxNamespace struct. This struct is similar to the LinuxNamespace +/// struct generated from oci.proto, but includes just the fields that are currently +/// relevant for automatic generation of policy. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct KataLinuxNamespace { + /// Type is the type of namespace + pub Type: String, + + /// Path is a path to an existing namespace persisted on disk that can be joined + /// and is of the same type + pub Path: String, +} + +/// OCI container LinuxCapabilities struct. This struct is very similar to the +/// LinuxCapabilities struct generated from oci.proto. The main difference is +/// that it preserves the upper case field names from oci.proto, for consistency +/// with the structs used by agent's rpc.rs. +#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] +pub struct KataLinuxCapabilities { + // Ambient is the ambient set of capabilities that are kept. + pub Ambient: Vec, + + /// Bounding is the set of capabilities checked by the kernel. + pub Bounding: Vec, + + /// Effective is the set of capabilities checked by the kernel. + pub Effective: Vec, + + /// Inheritable is the capabilities preserved across execve. + pub Inheritable: Vec, + + /// Permitted is the limiting superset for effective capabilities. + pub Permitted: Vec, +} + +/// OCI container Mount struct. This struct is very similar to the Mount +/// struct generated from oci.proto. The main difference is that it preserves +/// the field names from oci.proto, for consistency with the structs used by +/// agent's rpc.rs. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct KataMount { + /// destination is the path inside the container expect when it starts with "tmp:/" + pub destination: String, + + /// source is the path inside the container expect when it starts with "vm:/dev/" or "tmp:/" + /// the path which starts with "vm:/dev/" refers the guest vm's "/dev", + /// especially, "vm:/dev/hostfs/" refers to the shared filesystem. + /// "tmp:/" is a temporary directory which is used for temporary mounts. + #[serde(default)] + pub source: String, + + pub type_: String, + pub options: Vec, +} + +/// Policy data for a container, included in the output of this app. +#[derive(Debug, Serialize)] +pub struct ContainerPolicy { + /// Data compared with req.OCI for CreateContainerRequest calls. + pub OCI: KataSpec, + + /// Data compared with req.storages for CreateContainerRequest calls. + storages: Vec, + + /// Allow list of ommand lines that are allowed to be executed using + /// ExecProcessRequest. By default, all ExecProcessRequest calls are blocked + /// by the policy. + exec_commands: Vec, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Volumes { + /// K8s EmptyDir Volume. + pub emptyDir: Option, + + /// K8s PersistentVolumeClaim Volume. + pub persistentVolumeClaim: Option, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EmptyDirVolume { + pub mount_type: String, + pub mount_point: String, + pub mount_source: String, + pub driver: String, + pub source: String, + pub fstype: String, + pub options: Vec, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PersistentVolumeClaimVolume { + pub mount_type: String, + pub mount_source: String, +} + +/// CreateContainerRequest settings from genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CreateContainerRequestDefaults { + /// Allow env variables that match any of these regexes. + allow_env_regex: Vec, +} + +/// ExecProcessRequest settings from genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ExecProcessRequestDefaults { + /// Allow these commands to be executed. + commands: Vec, + + /// Allow commands matching these regexes to be executed. + regex: Vec, +} + +/// Settings specific to each kata agent endpoint, loaded from +/// genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RequestDefaults { + /// Settings for CreateContainerRequest. + pub CreateContainerRequest: CreateContainerRequestDefaults, + + /// Guest file paths matching these regular expressions can be copied by the Host. + pub CopyFileRequest: Vec, + + /// Commands allowed to be executed by the Host in all Guest containers. + pub ExecProcessRequest: ExecProcessRequestDefaults, + + /// Allow Host reading from Guest containers stdout and stderr. + pub ReadStreamRequest: bool, + + /// Allow Host writing to Guest containers stdin. + pub WriteStreamRequest: bool, +} + +/// Struct used to read data from the settings file and copy that data into the policy. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct CommonData { + /// Path to the shared container files - e.g., "/run/kata-containers/shared/containers". + pub cpath: String, + + /// Regex prefix for shared file paths - e.g., "^$(cpath)/$(bundle-id)-[a-z0-9]{16}-". + pub sfprefix: String, + + /// Regex for an IPv4 address. + pub ipv4_a: String, + + /// Regex for an IP port number. + pub ip_p: String, + + /// Regex for a K8s service name. + pub svc_name: String, + + // Regex for a DNS label (e.g., host name). + pub dns_label: String, + + /// Default capabilities for a non-privileged container. + pub default_caps: Vec, + + /// Default capabilities for a privileged container. + pub privileged_caps: Vec, +} + +impl AgentPolicy { + pub async fn from_files(config: &utils::Config) -> Result { + let mut config_maps = Vec::new(); + let mut secrets = Vec::new(); + let mut resources = Vec::new(); + let yaml_contents = yaml::get_input_yaml(&config.yaml_file)?; + + for document in serde_yaml::Deserializer::from_str(&yaml_contents) { + let doc_mapping = Value::deserialize(document)?; + let yaml_string = serde_yaml::to_string(&doc_mapping)?; + + let silent = config.silent_unsupported_fields; + let (mut resource, kind) = yaml::new_k8s_resource(&yaml_string, silent)?; + resource.init(config.use_cache, &doc_mapping, silent).await; + + // ConfigMap and Secret documents contain additional input for policy generation. + if kind.eq("ConfigMap") { + let config_map: config_map::ConfigMap = serde_yaml::from_str(&yaml_string)?; + debug!("{:#?}", &config_map); + config_maps.push(config_map); + } else if kind.eq("Secret") { + let secret: secret::Secret = serde_yaml::from_str(&yaml_string)?; + debug!("{:#?}", &secret); + secrets.push(secret); + } + + // Although copies of ConfigMap and Secret resources get created above, + // those resources still have to be present in the resources vector, because + // the elements of this vector will eventually be used to create the output + // YAML file. + resources.push(resource); + } + + let settings = settings::Settings::new(&config.settings_file); + + if let Some(config_map_files) = &config.config_map_files { + for file in config_map_files { + config_maps.push(config_map::ConfigMap::new(&file)?); + } + } + + if let Ok(rules) = read_to_string(&config.rules_file) { + Ok(AgentPolicy { + resources, + rules, + settings, + config_maps, + secrets, + config: config.clone(), + }) + } else { + panic!("Cannot open file {}. Please copy it to the current directory or specify the path to it using the -i parameter.", + &config.rules_file); + } + } + + pub fn export_policy(&mut self) { + let mut yaml_string = String::new(); + for i in 0..self.resources.len() { + let policy = self.resources[i].generate_policy(self); + if self.config.base64_out { + println!("{}", policy); + } + yaml_string += &self.resources[i].serialize(&policy); + } + + if let Some(yaml_file) = &self.config.yaml_file { + std::fs::OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(yaml_file) + .unwrap() + .write_all(&yaml_string.as_bytes()) + .unwrap(); + } else { + // When input YAML came through stdin, print the output YAML to stdout. + std::io::stdout() + .write_all(&yaml_string.as_bytes()) + .unwrap(); + } + } + + pub fn generate_policy(&self, resource: &dyn yaml::K8sResource) -> String { + let yaml_containers = resource.get_containers(); + let mut policy_containers = Vec::new(); + + for i in 0..yaml_containers.len() { + policy_containers.push(self.get_container_policy( + resource, + &yaml_containers[i], + i == 0, + )); + } + + let policy_data = policy::PolicyData { + containers: policy_containers, + request_defaults: self.settings.request_defaults.clone(), + common: self.settings.common.clone(), + }; + + let json_data = serde_json::to_string_pretty(&policy_data).unwrap(); + let policy = format!("{}\npolicy_data := {json_data}", &self.rules); + if self.config.raw_out { + std::io::stdout().write_all(policy.as_bytes()).unwrap(); + } + general_purpose::STANDARD.encode(policy.as_bytes()) + } + + pub fn get_container_policy( + &self, + resource: &dyn yaml::K8sResource, + yaml_container: &pod::Container, + is_pause_container: bool, + ) -> ContainerPolicy { + let c_settings = self.settings.get_container_settings(is_pause_container); + let mut root = c_settings.Root.clone(); + root.Readonly = yaml_container.read_only_root_filesystem(); + + let namespace = resource.get_namespace(); + let use_host_network = resource.use_host_network(); + let annotations = get_container_annotations( + resource, + yaml_container, + is_pause_container, + &namespace, + c_settings, + use_host_network, + ); + + let is_privileged = yaml_container.is_privileged(); + let process = self.get_container_process( + resource, + yaml_container, + is_pause_container, + &namespace, + c_settings, + is_privileged, + ); + + let mut mounts = containerd::get_mounts(is_pause_container, is_privileged); + mount_and_storage::get_policy_mounts( + &self.settings, + &mut mounts, + yaml_container, + is_pause_container, + ); + + let image_layers = yaml_container.registry.get_image_layers(); + let mut storages = Default::default(); + get_image_layer_storages(&mut storages, &image_layers, &root); + resource.get_container_mounts_and_storages( + &mut mounts, + &mut storages, + yaml_container, + &self.settings, + ); + + let mut linux = containerd::get_linux(is_privileged); + linux.Namespaces = get_kata_namespaces(is_pause_container, use_host_network); + + if !c_settings.Linux.MaskedPaths.is_empty() { + linux.MaskedPaths = c_settings.Linux.MaskedPaths.clone(); + } + if !c_settings.Linux.ReadonlyPaths.is_empty() { + linux.ReadonlyPaths = c_settings.Linux.ReadonlyPaths.clone(); + } + + let exec_commands = yaml_container.get_exec_commands(); + + ContainerPolicy { + OCI: KataSpec { + Version: version_default(), + Process: process, + Root: root, + Mounts: mounts, + Hooks: None, + Annotations: annotations, + Linux: linux, + }, + storages, + exec_commands, + } + } + + fn get_container_process( + &self, + resource: &dyn yaml::K8sResource, + yaml_container: &pod::Container, + is_pause_container: bool, + namespace: &str, + c_settings: &KataSpec, + is_privileged: bool, + ) -> KataProcess { + // Start with the Default Unix Spec from + // https://github.com/containerd/containerd/blob/release/1.6/oci/spec.go#L132 + let mut process = containerd::get_process(is_privileged, &self.settings.common); + + yaml_container.apply_capabilities(&mut process.Capabilities, &self.settings.common); + + let (yaml_has_command, yaml_has_args) = yaml_container.get_process_args(&mut process.Args); + yaml_container + .registry + .get_process(&mut process, yaml_has_command, yaml_has_args); + + if let Some(tty) = yaml_container.tty { + process.Terminal = tty; + if tty && !is_pause_container { + process.Env.push("TERM=xterm".to_string()); + } + } + + if !is_pause_container { + process.Env.push("HOSTNAME=$(host-name)".to_string()); + } + + let service_account_name = if let Some(s) = &yaml_container.serviceAccountName { + s + } else { + "default" + }; + + yaml_container.get_env_variables( + &mut process.Env, + &self.config_maps, + &self.secrets, + namespace, + resource.get_annotations(), + service_account_name, + ); + + substitute_env_variables(&mut process.Env); + substitute_args_env_variables(&mut process.Args, &process.Env); + c_settings.get_process_fields(&mut process); + process.NoNewPrivileges = !yaml_container.allow_privilege_escalation(); + + process + } +} + +impl KataSpec { + fn add_annotations(&self, annotations: &mut BTreeMap) { + for a in &self.Annotations { + annotations.entry(a.0.clone()).or_insert(a.1.clone()); + } + } + + fn get_process_fields(&self, process: &mut KataProcess) { + if process.User.UID == 0 { + process.User.UID = self.Process.User.UID; + } + if process.User.GID == 0 { + process.User.GID = self.Process.User.GID; + } + + process.User.AdditionalGids = self.Process.User.AdditionalGids.to_vec(); + process.User.Username = String::from(&self.Process.User.Username); + add_missing_strings(&self.Process.Args, &mut process.Args); + + add_missing_strings(&self.Process.Env, &mut process.Env); + } +} + +fn get_image_layer_storages( + storages: &mut Vec, + image_layers: &Vec, + root: &KataRoot, +) { + let mut new_storages: Vec = Vec::new(); + let mut layer_names: Vec = Vec::new(); + let mut layer_hashes: Vec = Vec::new(); + let mut previous_chain_id = String::new(); + let layers_count = image_layers.len(); + let mut layer_index = layers_count; + + for layer in image_layers { + // See https://github.com/opencontainers/image-spec/blob/main/config.md#layer-chainid + let chain_id = if previous_chain_id.is_empty() { + layer.diff_id.clone() + } else { + let mut hasher = Sha256::new(); + hasher.update(format!("{previous_chain_id} {}", &layer.diff_id)); + format!("sha256:{:x}", hasher.finalize()) + }; + debug!( + "previous_chain_id = {}, chain_id = {}", + &previous_chain_id, &chain_id + ); + previous_chain_id = chain_id.clone(); + + layer_names.push(name_to_hash(&chain_id)); + layer_hashes.push(layer.verity_hash.to_string()); + layer_index -= 1; + + new_storages.push(agent::Storage { + driver: "blk".to_string(), + driver_options: Vec::new(), + source: String::new(), // TODO + fstype: "tar".to_string(), + options: vec![format!("$(hash{layer_index})")], + mount_point: format!("$(layer{layer_index})"), + fs_group: protobuf::MessageField::none(), + special_fields: ::protobuf::SpecialFields::new(), + }); + } + + new_storages.reverse(); + for storage in new_storages { + storages.push(storage); + } + + layer_names.reverse(); + layer_hashes.reverse(); + + let overlay_storage = agent::Storage { + driver: "overlayfs".to_string(), + driver_options: Vec::new(), + source: String::new(), // TODO + fstype: "fuse3.kata-overlay".to_string(), + options: vec![layer_names.join(":"), layer_hashes.join(":")], + mount_point: root.Path.clone(), + fs_group: protobuf::MessageField::none(), + special_fields: ::protobuf::SpecialFields::new(), + }; + + storages.push(overlay_storage); +} + +/// Converts the given name to a string representation of its sha256 hash. +fn name_to_hash(name: &str) -> String { + let mut hasher = Sha256::new(); + hasher.update(name); + format!("{:x}", hasher.finalize()) +} + +fn substitute_env_variables(env: &mut Vec) { + loop { + let mut substituted = false; + + for i in 0..env.len() { + let components: Vec<&str> = env[i].split('=').collect(); + if components.len() == 2 { + if let Some((start, end)) = find_subst_target(&components[1]) { + if let Some(new_value) = substitute_variable(&components[1], start, end, env) { + let new_var = format!("{}={new_value}", &components[0]); + debug!("Replacing env variable <{}> with <{new_var}>", &env[i]); + env[i] = new_var; + substituted = true; + } + } + } + } + + if !substituted { + break; + } + } +} + +fn find_subst_target(env_value: &str) -> Option<(usize, usize)> { + if let Some(mut start) = env_value.find("$(") { + start += 2; + if env_value.len() > start { + if let Some(end) = env_value[start..].find(")") { + return Some((start, start + end)); + } + } + } + + None +} + +fn substitute_variable( + env_var: &str, + name_start: usize, + name_end: usize, + env: &Vec, +) -> Option { + // Variables generated by this application. + let internal_vars = vec![ + "bundle-id", + "host-ip", + "node-name", + "pod-ip", + "pod-uid", + "sandbox-id", + "sandbox-name", + "sandbox-namespace", + ]; + + assert!(name_start < name_end); + assert!(name_end < env_var.len()); + let name = env_var[name_start..name_end].to_string(); + debug!("Searching for the value of <{}>", &name); + + for other_var in env { + let components: Vec<&str> = other_var.split('=').collect(); + if components[0].eq(&name) { + debug!("Found {} in <{}>", &name, &other_var); + if components.len() == 2 { + let mut replace = true; + let value = &components[1]; + + if let Some((start, end)) = find_subst_target(value) { + if internal_vars.contains(&&value[start..end]) { + // Variables used internally for Policy don't get expanded + // in the current design, so it's OK to use them as replacement + // in other env variables or command arguments. + } else { + // Don't substitute if the value includes variables to be + // substituted, to avoid circular substitutions. + replace = false; + } + } + + if replace { + let from = format!("$({name})"); + return Some(env_var.replace(&from, value)); + } + } + } + } + + None +} + +fn substitute_args_env_variables(args: &mut Vec, env: &Vec) { + for arg in args { + substitute_arg_env_variables(arg, env); + } +} + +fn substitute_arg_env_variables(arg: &mut String, env: &Vec) { + loop { + let mut substituted = false; + + if let Some((start, end)) = find_subst_target(arg) { + if let Some(new_value) = substitute_variable(arg, start, end, env) { + debug!( + "substitute_arg_env_variables: replacing {} with {}", + &arg[start..end], + &new_value + ); + *arg = new_value; + substituted = true; + } + } + + if !substituted { + break; + } + } +} + +fn get_container_annotations( + resource: &dyn yaml::K8sResource, + yaml_container: &pod::Container, + is_pause_container: bool, + namespace: &str, + c_settings: &KataSpec, + use_host_network: bool, +) -> BTreeMap { + let mut annotations = if let Some(a) = resource.get_annotations() { + let mut a_cloned = a.clone(); + yaml::remove_policy_annotation(&mut a_cloned); + a_cloned + } else { + BTreeMap::new() + }; + + c_settings.add_annotations(&mut annotations); + + if let Some(name) = resource.get_sandbox_name() { + annotations + .entry("io.kubernetes.cri.sandbox-name".to_string()) + .or_insert(name); + } + + if !is_pause_container { + let mut image_name = yaml_container.image.clone(); + if image_name.find(':').is_none() { + image_name += ":latest"; + } + annotations + .entry("io.kubernetes.cri.image-name".to_string()) + .or_insert(image_name); + } + + annotations.insert( + "io.kubernetes.cri.sandbox-namespace".to_string(), + namespace.to_string(), + ); + + if !yaml_container.name.is_empty() { + annotations + .entry("io.kubernetes.cri.container-name".to_string()) + .or_insert(yaml_container.name.clone()); + } + + if is_pause_container { + let mut network_namespace = "^/var/run/netns/cni".to_string(); + if use_host_network { + network_namespace += "test"; + } + network_namespace += "-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"; + annotations + .entry("nerdctl/network-namespace".to_string()) + .or_insert(network_namespace); + } + + annotations +} + +fn add_missing_strings(src: &Vec, dest: &mut Vec) { + for src_string in src { + if !dest.contains(src_string) { + dest.push(src_string.clone()); + } + } + debug!("src = {:?}, dest = {:?}", src, dest) +} + +pub fn get_kata_namespaces(is_pause_container: bool, use_host_network: bool) -> Vec { + let mut namespaces: Vec = vec![KataLinuxNamespace { + Type: "ipc".to_string(), + Path: "".to_string(), + }]; + + if !is_pause_container || !use_host_network { + namespaces.push(KataLinuxNamespace { + Type: "uts".to_string(), + Path: "".to_string(), + }); + } + + namespaces.push(KataLinuxNamespace { + Type: "mount".to_string(), + Path: "".to_string(), + }); + + namespaces +} diff --git a/src/tools/genpolicy/src/registry.rs b/src/tools/genpolicy/src/registry.rs new file mode 100644 index 0000000000..fef7a5672d --- /dev/null +++ b/src/tools/genpolicy/src/registry.rs @@ -0,0 +1,464 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow Docker image config field names. +#![allow(non_snake_case)] + +use crate::policy; +use crate::verity; + +use anyhow::{anyhow, Result}; +use docker_credential::{CredentialRetrievalError, DockerCredential}; +use log::warn; +use log::{debug, info, LevelFilter}; +use oci_distribution::client::{linux_amd64_resolver, ClientConfig}; +use oci_distribution::{manifest, secrets::RegistryAuth, Client, Reference}; +use serde::{Deserialize, Serialize}; +use sha2::{digest::typenum::Unsigned, digest::OutputSizeUser, Sha256}; +use std::{io, io::Seek, io::Write, path::Path}; +use tokio::{fs, io::AsyncWriteExt}; + +/// Container image properties obtained from an OCI repository. +#[derive(Clone, Debug, Default)] +pub struct Container { + config_layer: DockerConfigLayer, + image_layers: Vec, +} + +/// Image config layer properties. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +struct DockerConfigLayer { + architecture: String, + config: DockerImageConfig, + rootfs: DockerRootfs, +} + +/// Image config properties. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +struct DockerImageConfig { + User: Option, + Tty: Option, + Env: Vec, + Cmd: Option>, + WorkingDir: Option, + Entrypoint: Option>, +} + +/// Container rootfs information. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +struct DockerRootfs { + r#type: String, + diff_ids: Vec, +} + +/// This application's image layer properties. +#[derive(Clone, Debug)] +pub struct ImageLayer { + pub diff_id: String, + pub verity_hash: String, +} + +impl Container { + pub async fn new(use_cached_files: bool, image: &str) -> Result { + info!("============================================"); + info!("Pulling manifest and config for {:?}", image); + let reference: Reference = image.to_string().parse().unwrap(); + let auth = build_auth(&reference); + + let mut client = Client::new(ClientConfig { + platform_resolver: Some(Box::new(linux_amd64_resolver)), + ..Default::default() + }); + + match client.pull_manifest_and_config(&reference, &auth).await { + Ok((manifest, digest_hash, config_layer_str)) => { + debug!("digest_hash: {:?}", digest_hash); + debug!( + "manifest: {}", + serde_json::to_string_pretty(&manifest).unwrap() + ); + + // Log the contents of the config layer. + if log::max_level() >= LevelFilter::Debug { + let mut deserializer = serde_json::Deserializer::from_str(&config_layer_str); + let mut serializer = serde_json::Serializer::pretty(io::stderr()); + serde_transcode::transcode(&mut deserializer, &mut serializer).unwrap(); + } + + let config_layer: DockerConfigLayer = + serde_json::from_str(&config_layer_str).unwrap(); + let image_layers = get_image_layers( + use_cached_files, + &mut client, + &reference, + &manifest, + &config_layer, + ) + .await + .unwrap(); + + Ok(Container { + config_layer, + image_layers, + }) + } + Err(oci_distribution::errors::OciDistributionError::AuthenticationFailure(message)) => { + panic!("Container image registry authentication failure ({}). Are docker credentials set-up for current user?", &message); + } + Err(e) => { + panic!( + "Failed to pull container image manifest and config - error: {:#?}", + &e + ); + } + } + } + + // Convert Docker image config to policy data. + pub fn get_process( + &self, + process: &mut policy::KataProcess, + yaml_has_command: bool, + yaml_has_args: bool, + ) { + debug!("Getting process field from docker config layer..."); + let docker_config = &self.config_layer.config; + + if let Some(image_user) = &docker_config.User { + if !image_user.is_empty() { + debug!("Splitting Docker config user = {:?}", image_user); + let user: Vec<&str> = image_user.split(':').collect(); + if !user.is_empty() { + debug!("Parsing uid from user[0] = {}", &user[0]); + match user[0].parse() { + Ok(id) => process.User.UID = id, + Err(e) => { + // "image: prom/prometheus" has user = "nobody", but + // process.User.UID is an u32 value. + warn!( + "Failed to parse {} as u32, using uid = 0 - error {e}", + &user[0] + ); + process.User.UID = 0; + } + } + } + if user.len() > 1 { + debug!("Parsing gid from user[1] = {:?}", user[1]); + process.User.GID = user[1].parse().unwrap(); + } + } + } + + if let Some(terminal) = docker_config.Tty { + process.Terminal = terminal; + } else { + process.Terminal = false; + } + + for env in &docker_config.Env { + process.Env.push(env.clone()); + } + + let policy_args = &mut process.Args; + debug!("Already existing policy args: {:?}", policy_args); + + if let Some(entry_points) = &docker_config.Entrypoint { + debug!("Image Entrypoint: {:?}", entry_points); + if !yaml_has_command { + debug!("Inserting Entrypoint into policy args"); + + let mut reversed_entry_points = entry_points.clone(); + reversed_entry_points.reverse(); + + for entry_point in reversed_entry_points { + policy_args.insert(0, entry_point.clone()); + } + } else { + debug!("Ignoring image Entrypoint because YAML specified the container command"); + } + } else { + debug!("No image Entrypoint"); + } + + debug!("Updated policy args: {:?}", policy_args); + + if yaml_has_command { + debug!("Ignoring image Cmd because YAML specified the container command"); + } else if yaml_has_args { + debug!("Ignoring image Cmd because YAML specified the container args"); + } else if let Some(commands) = &docker_config.Cmd { + debug!("Adding to policy args the image Cmd: {:?}", commands); + + for cmd in commands { + policy_args.push(cmd.clone()); + } + } else { + debug!("Image Cmd field is not present"); + } + + debug!("Updated policy args: {:?}", policy_args); + + if let Some(working_dir) = &docker_config.WorkingDir { + if !working_dir.is_empty() { + process.Cwd = working_dir.clone(); + } + } + + debug!("get_process succeeded."); + } + + pub fn get_image_layers(&self) -> Vec { + self.image_layers.clone() + } +} + +async fn get_image_layers( + use_cached_files: bool, + client: &mut Client, + reference: &Reference, + manifest: &manifest::OciImageManifest, + config_layer: &DockerConfigLayer, +) -> Result> { + let mut layer_index = 0; + let mut layers = Vec::new(); + + for layer in &manifest.layers { + if layer + .media_type + .eq(manifest::IMAGE_DOCKER_LAYER_GZIP_MEDIA_TYPE) + { + if layer_index < config_layer.rootfs.diff_ids.len() { + layers.push(ImageLayer { + diff_id: config_layer.rootfs.diff_ids[layer_index].clone(), + verity_hash: get_verity_hash( + use_cached_files, + client, + reference, + &layer.digest, + ) + .await?, + }); + } else { + return Err(anyhow!("Too many Docker gzip layers")); + } + + layer_index += 1; + } + } + + Ok(layers) +} + +fn delete_files(decompressed_path: &Path, compressed_path: &Path, verity_path: &Path) { + let _ = fs::remove_file(&decompressed_path); + let _ = fs::remove_file(&compressed_path); + let _ = fs::remove_file(&verity_path); +} + +async fn get_verity_hash( + use_cached_files: bool, + client: &mut Client, + reference: &Reference, + layer_digest: &str, +) -> Result { + let base_dir = std::path::Path::new("layers_cache"); + + // Use file names supported by both Linux and Windows. + let file_name = str::replace(&layer_digest, ":", "-"); + + let mut decompressed_path = base_dir.join(file_name); + decompressed_path.set_extension("tar"); + + let mut compressed_path = decompressed_path.clone(); + compressed_path.set_extension("gz"); + + let mut verity_path = decompressed_path.clone(); + verity_path.set_extension("verity"); + + let mut verity_hash = "".to_string(); + let mut error_message = "".to_string(); + let mut error = false; + + if use_cached_files && verity_path.exists() { + info!("Using cached file {:?}", &verity_path); + } else if let Err(e) = create_verity_hash_file( + use_cached_files, + client, + reference, + layer_digest, + &base_dir, + &decompressed_path, + &compressed_path, + &verity_path, + ) + .await + { + error = true; + error_message = format!("Failed to create verity hash for {layer_digest}, error {e}"); + } + + if !error { + match std::fs::read_to_string(&verity_path) { + Err(e) => { + error = true; + error_message = format!("Failed to read {:?}, error {e}", &verity_path); + } + Ok(v) => { + verity_hash = v; + info!("dm-verity root hash: {verity_hash}"); + } + } + } + + if !use_cached_files { + let _ = std::fs::remove_dir_all(&base_dir); + } else if error { + delete_files(&decompressed_path, &compressed_path, &verity_path); + } + + if error { + panic!("{error_message}"); + } else { + Ok(verity_hash) + } +} + +async fn create_verity_hash_file( + use_cached_files: bool, + client: &mut Client, + reference: &Reference, + layer_digest: &str, + base_dir: &Path, + decompressed_path: &Path, + compressed_path: &Path, + verity_path: &Path, +) -> Result<()> { + if use_cached_files && decompressed_path.exists() { + info!("Using cached file {:?}", &decompressed_path); + } else { + std::fs::create_dir_all(&base_dir)?; + + create_decompressed_layer_file( + use_cached_files, + client, + reference, + layer_digest, + &decompressed_path, + &compressed_path, + ) + .await?; + } + + do_create_verity_hash_file(decompressed_path, verity_path) +} + +async fn create_decompressed_layer_file( + use_cached_files: bool, + client: &mut Client, + reference: &Reference, + layer_digest: &str, + decompressed_path: &Path, + compressed_path: &Path, +) -> Result<()> { + if use_cached_files && compressed_path.exists() { + info!("Using cached file {:?}", &compressed_path); + } else { + info!("Pulling layer {layer_digest}"); + let mut file = tokio::fs::File::create(&compressed_path) + .await + .map_err(|e| anyhow!(e))?; + client + .pull_blob(&reference, layer_digest, &mut file) + .await + .map_err(|e| anyhow!(e))?; + file.flush().await.map_err(|e| anyhow!(e))?; + } + + info!("Decompressing layer"); + let compressed_file = std::fs::File::open(&compressed_path).map_err(|e| anyhow!(e))?; + let mut decompressed_file = std::fs::OpenOptions::new() + .read(true) + .write(true) + .create(true) + .truncate(true) + .open(&decompressed_path)?; + let mut gz_decoder = flate2::read::GzDecoder::new(compressed_file); + std::io::copy(&mut gz_decoder, &mut decompressed_file).map_err(|e| anyhow!(e))?; + + info!("Adding tarfs index to layer"); + decompressed_file.seek(std::io::SeekFrom::Start(0))?; + tarindex::append_index(&mut decompressed_file).map_err(|e| anyhow!(e))?; + decompressed_file.flush().map_err(|e| anyhow!(e))?; + + Ok(()) +} + +fn do_create_verity_hash_file(path: &Path, verity_path: &Path) -> Result<()> { + info!("Calculating dm-verity root hash"); + let mut file = std::fs::File::open(path)?; + let size = file.seek(std::io::SeekFrom::End(0))?; + if size < 4096 { + return Err(anyhow!("Block device {:?} is too small: {size}", &path)); + } + + let salt = [0u8; ::OutputSize::USIZE]; + let v = verity::Verity::::new(size, 4096, 4096, &salt, 0)?; + let hash = verity::traverse_file(&mut file, 0, false, v, &mut verity::no_write)?; + let result = format!("{:x}", hash); + + let mut verity_file = std::fs::File::create(verity_path).map_err(|e| anyhow!(e))?; + verity_file + .write_all(result.as_bytes()) + .map_err(|e| anyhow!(e))?; + verity_file.flush().map_err(|e| anyhow!(e))?; + + Ok(()) +} + +pub async fn get_container(use_cache: bool, image: &str) -> Result { + Container::new(use_cache, image).await +} + +fn build_auth(reference: &Reference) -> RegistryAuth { + debug!("build_auth: {:?}", reference); + + let server = reference + .resolve_registry() + .strip_suffix("/") + .unwrap_or_else(|| reference.resolve_registry()); + + match docker_credential::get_credential(server) { + Ok(DockerCredential::UsernamePassword(username, password)) => { + debug!("build_auth: Found docker credentials"); + return RegistryAuth::Basic(username, password); + } + Ok(DockerCredential::IdentityToken(_)) => { + warn!("build_auth: Cannot use contents of docker config, identity token not supported. Using anonymous access."); + } + Err(CredentialRetrievalError::ConfigNotFound) => { + debug!("build_auth: Docker config not found - using anonymous access."); + } + Err(CredentialRetrievalError::NoCredentialConfigured) => { + debug!("build_auth: Docker credentials not configured - using anonymous access."); + } + Err(CredentialRetrievalError::ConfigReadError) => { + debug!("build_auth: Cannot read docker credentials - using anonymous access."); + } + Err(CredentialRetrievalError::HelperFailure { stdout, stderr }) => { + if stdout == "credentials not found in native keychain\n" { + // On WSL, this error is generated when credentials are not + // available in ~/.docker/config.json. + debug!("build_auth: Docker credentials not found - using anonymous access."); + } else { + warn!("build_auth: Docker credentials not found - using anonymous access. stderr = {}, stdout = {}", + &stderr, &stdout); + } + } + Err(e) => panic!("Error handling docker configuration file: {}", e), + } + + RegistryAuth::Anonymous +} diff --git a/src/tools/genpolicy/src/secret.rs b/src/tools/genpolicy/src/secret.rs new file mode 100644 index 0000000000..620d7b8834 --- /dev/null +++ b/src/tools/genpolicy/src/secret.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::obj_meta; +use crate::pod; +use crate::policy; +use crate::settings; +use crate::yaml; + +use async_trait::async_trait; +use base64::{engine::general_purpose, Engine as _}; +use protocols::agent; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +/// See Reference / Kubernetes API / Config and Storage Resources / Secret. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Secret { + #[serde(skip)] + doc_mapping: serde_yaml::Value, + + apiVersion: String, + kind: String, + metadata: obj_meta::ObjectMeta, + + #[serde(skip_serializing_if = "Option::is_none")] + data: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + immutable: Option, + // TODO: additional fields. +} + +impl Secret { + pub fn get_value(&self, value_from: &pod::EnvVarSource) -> Option { + if let Some(key_ref) = &value_from.secretKeyRef { + if let Some(name) = &key_ref.name { + if let Some(my_name) = &self.metadata.name { + if my_name.eq(name) { + if let Some(data) = &self.data { + if let Some(value) = data.get(&key_ref.key) { + let value_bytes = general_purpose::STANDARD.decode(&value).unwrap(); + let value_string = std::str::from_utf8(&value_bytes).unwrap(); + return Some(value_string.to_string()); + } + } + } + } + } + } + + None + } +} + +pub fn get_value(value_from: &pod::EnvVarSource, secrets: &Vec) -> Option { + for secret in secrets { + if let Some(value) = secret.get_value(value_from) { + return Some(value); + } + } + + None +} + +#[async_trait] +impl yaml::K8sResource for Secret { + async fn init(&mut self, _use_cache: bool, doc_mapping: &serde_yaml::Value, _silent: bool) { + self.doc_mapping = doc_mapping.clone(); + } + + fn get_sandbox_name(&self) -> Option { + panic!("Unsupported"); + } + + fn get_namespace(&self) -> String { + panic!("Unsupported"); + } + + fn get_container_mounts_and_storages( + &self, + _policy_mounts: &mut Vec, + _storages: &mut Vec, + _container: &pod::Container, + _settings: &settings::Settings, + ) { + panic!("Unsupported"); + } + + fn generate_policy(&self, _agent_policy: &policy::AgentPolicy) -> String { + "".to_string() + } + + fn serialize(&mut self, _policy: &str) -> String { + serde_yaml::to_string(&self.doc_mapping).unwrap() + } + + fn get_containers(&self) -> &Vec { + panic!("Unsupported"); + } + + fn get_annotations(&self) -> &Option> { + panic!("Unsupported"); + } + + fn use_host_network(&self) -> bool { + panic!("Unsupported"); + } +} diff --git a/src/tools/genpolicy/src/settings.rs b/src/tools/genpolicy/src/settings.rs new file mode 100644 index 0000000000..2d043031e1 --- /dev/null +++ b/src/tools/genpolicy/src/settings.rs @@ -0,0 +1,87 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow OCI spec field names. +#![allow(non_snake_case)] + +use crate::policy; + +use log::debug; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::str; + +/// Policy settings loaded from genpolicy-settings.json. +#[derive(Debug, Deserialize, Serialize)] +pub struct Settings { + pub pause_container: policy::KataSpec, + pub other_container: policy::KataSpec, + pub volumes: Volumes, + pub kata_config: KataConfig, + pub request_defaults: policy::RequestDefaults, + pub common: policy::CommonData, + pub mount_destinations: Vec, +} + +/// Volume settings loaded from genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Volumes { + pub emptyDir: EmptyDirVolume, + pub emptyDir_memory: EmptyDirVolume, + pub configMap: ConfigMapVolume, + pub confidential_configMap: ConfigMapVolume, +} + +/// EmptyDir volume settings loaded from genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EmptyDirVolume { + pub mount_type: String, + pub mount_source: String, + pub mount_point: String, + pub driver: String, + pub fstype: String, + pub options: Vec, + pub source: String, +} + +/// ConfigMap volume settings loaded from genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ConfigMapVolume { + pub mount_type: String, + pub mount_source: String, + pub mount_point: String, + pub driver: String, + pub fstype: String, + pub options: Vec, +} + +/// Data corresponding to the kata runtime config file data, loaded from +/// genpolicy-settings.json. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KataConfig { + pub confidential_guest: bool, +} + +impl Settings { + pub fn new(settings_file: &str) -> Self { + debug!("Loading settings file..."); + if let Ok(file) = File::open(settings_file) { + let settings: Self = serde_json::from_reader(file).unwrap(); + debug!("settings = {:?}", &settings); + settings + } else { + panic!("Cannot open file {}. Please copy it to the current directory or specify the path to it using the -i parameter.", + settings_file); + } + } + + pub fn get_container_settings(&self, is_pause_container: bool) -> &policy::KataSpec { + if is_pause_container { + &self.pause_container + } else { + &self.other_container + } + } +} diff --git a/src/tools/genpolicy/src/utils.rs b/src/tools/genpolicy/src/utils.rs new file mode 100644 index 0000000000..4484abecd2 --- /dev/null +++ b/src/tools/genpolicy/src/utils.rs @@ -0,0 +1,57 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use log::debug; + +/// Application configuration, derived from on command line parameters. +#[derive(Clone, Debug)] +pub struct Config { + pub use_cache: bool, + + pub yaml_file: Option, + pub rules_file: String, + pub settings_file: String, + pub config_map_files: Option>, + + pub silent_unsupported_fields: bool, + pub raw_out: bool, + pub base64_out: bool, +} + +impl Config { + pub fn new( + use_cache: bool, + yaml_file: Option, + input_files_path: &str, + settings_file_name: &str, + config_map_files: &Vec, + silent_unsupported_fields: bool, + raw_out: bool, + base64_out: bool, + ) -> Self { + let rules_file = format!("{input_files_path}/rules.rego"); + debug!("Rules file: {rules_file}"); + + let settings_file = format!("{input_files_path}/{settings_file_name}"); + debug!("Settings file: {settings_file}"); + + let cm_files = if !config_map_files.is_empty() { + Some(config_map_files.clone()) + } else { + None + }; + + Self { + use_cache, + yaml_file, + rules_file, + settings_file, + config_map_files: cm_files, + silent_unsupported_fields, + raw_out, + base64_out, + } + } +} diff --git a/src/tools/genpolicy/src/verity.rs b/src/tools/genpolicy/src/verity.rs new file mode 100644 index 0000000000..5fceb81aa1 --- /dev/null +++ b/src/tools/genpolicy/src/verity.rs @@ -0,0 +1,225 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use generic_array::{typenum::Unsigned, GenericArray}; +use sha2::Digest; +use std::fs::File; +use std::io::{self, Read, Seek, SeekFrom}; +use zerocopy::byteorder::{LE, U32, U64}; +use zerocopy::AsBytes; + +#[derive(Default, zerocopy::AsBytes, zerocopy::FromBytes, zerocopy::Unaligned)] +#[repr(C)] +pub struct SuperBlock { + pub data_block_size: U32, + pub hash_block_size: U32, + pub data_block_count: U64, +} + +#[derive(Clone)] +struct Level { + next_index: usize, + file_offset: u64, + data: Vec, +} + +pub struct Verity { + levels: Vec, + seeded: T, + data_block_size: usize, + hash_block_size: usize, + block_remaining_count: u64, + super_block: SuperBlock, +} + +impl Verity { + const HASH_SIZE: usize = T::OutputSize::USIZE; + + /// Creates a new `Verity` instance. + pub fn new( + data_size: u64, + data_block_size: usize, + hash_block_size: usize, + salt: &[u8], + mut write_file_offset: u64, + ) -> io::Result { + let level_count = { + let mut max_size = data_block_size as u64; + let mut count = 0usize; + + while max_size < data_size { + count += 1; + max_size *= (hash_block_size / Self::HASH_SIZE) as u64; + } + count + }; + + let mut data = Vec::new(); + data.resize(hash_block_size, 0); + + let mut levels = Vec::new(); + levels.resize( + level_count, + Level { + next_index: 0, + file_offset: 0, + data, + }, + ); + + for (i, l) in levels.iter_mut().enumerate() { + let entry_size = (data_block_size as u64) + * ((hash_block_size / Self::HASH_SIZE) as u64).pow(level_count as u32 - i as u32); + let count = (data_size + entry_size - 1) / entry_size; + l.file_offset = write_file_offset; + write_file_offset += hash_block_size as u64 * count; + } + + let block_count = data_size / (data_block_size as u64); + Ok(Self { + levels, + seeded: T::new_with_prefix(salt), + data_block_size, + block_remaining_count: block_count, + hash_block_size, + super_block: SuperBlock { + data_block_size: (data_block_size as u32).into(), + hash_block_size: (hash_block_size as u32).into(), + data_block_count: block_count.into(), + }, + }) + } + + /// Determines if more blocks are expected. + /// + /// This is based on file size specified when this instance was created. + fn more_blocks(&self) -> bool { + self.block_remaining_count > 0 + } + + /// Adds the given hash to the level. + /// + /// Returns `true` is the level is now full; `false` is there is still room for more hashes. + fn add_hash(&mut self, l: usize, hash: &[u8]) -> bool { + let level = &mut self.levels[l]; + level.data[level.next_index * Self::HASH_SIZE..][..Self::HASH_SIZE].copy_from_slice(hash); + level.next_index += 1; + level.next_index >= self.hash_block_size / Self::HASH_SIZE + } + + /// Finalises the level despite potentially not having filled it. + /// + /// It zeroes out the remaining bytes of the level so that its hash can be calculated + /// consistently. + fn finalize_level(&mut self, l: usize) { + let level = &mut self.levels[l]; + for b in &mut level.data[level.next_index * Self::HASH_SIZE..] { + *b = 0; + } + level.next_index = 0; + } + + fn uplevel(&mut self, l: usize, reader: &mut File, writer: &mut F) -> io::Result + where + F: FnMut(&mut File, &[u8], u64) -> io::Result<()>, + { + self.finalize_level(l); + writer(reader, &self.levels[l].data, self.levels[l].file_offset)?; + self.levels[l].file_offset += self.hash_block_size as u64; + let h = self.digest(&self.levels[l].data); + Ok(self.add_hash(l - 1, h.as_slice())) + } + + fn digest(&self, block: &[u8]) -> GenericArray { + let mut hasher = self.seeded.clone(); + hasher.update(block); + hasher.finalize() + } + + fn add_block(&mut self, b: &[u8], reader: &mut File, writer: &mut F) -> io::Result<()> + where + F: FnMut(&mut File, &[u8], u64) -> io::Result<()>, + { + if self.block_remaining_count == 0 { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "unexpected block", + )); + } + + self.block_remaining_count -= 1; + + let count = self.levels.len(); + let hash = self.digest(b); + if self.add_hash(count - 1, hash.as_slice()) { + // Go up the levels as far as it can. + for l in (1..count).rev() { + if !self.uplevel(l, reader, writer)? { + break; + } + } + } + Ok(()) + } + + fn finalize( + mut self, + write_superblock: bool, + reader: &mut File, + writer: &mut impl FnMut(&mut File, &[u8], u64) -> io::Result<()>, + ) -> io::Result> { + let len = self.levels.len(); + for mut l in (1..len).rev() { + if self.levels[l].next_index != 0 { + while l > 0 { + self.uplevel(l, reader, writer)?; + l -= 1; + } + break; + } + } + + self.finalize_level(0); + + writer(reader, &self.levels[0].data, self.levels[0].file_offset)?; + self.levels[0].file_offset += self.hash_block_size as u64; + + if write_superblock { + writer( + reader, + self.super_block.as_bytes(), + self.levels[len - 1].file_offset + 4096 - 512, + )?; + + // TODO: Align to the hash_block_size... + // Align to 4096 bytes. + writer(reader, &[0u8], self.levels[len - 1].file_offset + 4095)?; + } + + Ok(self.digest(&self.levels[0].data)) + } +} + +pub fn traverse_file( + file: &mut File, + mut read_offset: u64, + write_superblock: bool, + mut verity: Verity, + writer: &mut impl FnMut(&mut File, &[u8], u64) -> io::Result<()>, +) -> io::Result> { + let mut buf = Vec::new(); + buf.resize(verity.data_block_size, 0); + while verity.more_blocks() { + file.seek(SeekFrom::Start(read_offset))?; + file.read_exact(&mut buf)?; + verity.add_block(&buf, file, writer)?; + read_offset += verity.data_block_size as u64; + } + verity.finalize(write_superblock, file, writer) +} + +pub fn no_write(_: &mut File, _: &[u8], _: u64) -> io::Result<()> { + Ok(()) +} diff --git a/src/tools/genpolicy/src/volume.rs b/src/tools/genpolicy/src/volume.rs new file mode 100644 index 0000000000..7fbe542d12 --- /dev/null +++ b/src/tools/genpolicy/src/volume.rs @@ -0,0 +1,131 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::pod; + +use serde::{Deserialize, Serialize}; + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Volume { + pub name: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub emptyDir: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub hostPath: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub persistentVolumeClaim: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub configMap: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub azureFile: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub projected: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub secret: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub downwardAPI: Option + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct HostPathVolumeSource { + pub path: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub r#type: Option, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EmptyDirVolumeSource { + #[serde(skip_serializing_if = "Option::is_none")] + pub medium: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub sizeLimit: Option, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PersistentVolumeClaimVolumeSource { + pub claimName: String, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ConfigMapVolumeSource { + pub name: String, + pub items: Option>, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KeyToPath { + pub key: String, + pub path: String, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AzureFileVolumeSource { + pub secretName: String, + pub shareName: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub readOnly: Option, +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ProjectedVolumeSource { + #[serde(skip_serializing_if = "Option::is_none")] + pub defaultMode: Option, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SecretVolumeSource { + secretName: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub defaultMode: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub items: Option>, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DownwardAPIVolumeSource { + #[serde(skip_serializing_if = "Option::is_none")] + pub items: Option>, + // TODO: additional fields. +} + +/// See Reference / Kubernetes API / Config and Storage Resources / Volume. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct DownwardAPIVolumeFile { + pub path: String, + + #[serde(skip_serializing_if = "Option::is_none")] + pub fieldRef: Option, +} diff --git a/src/tools/genpolicy/src/yaml.rs b/src/tools/genpolicy/src/yaml.rs new file mode 100644 index 0000000000..200c9d552c --- /dev/null +++ b/src/tools/genpolicy/src/yaml.rs @@ -0,0 +1,242 @@ +// Copyright (c) 2023 Microsoft Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +// Allow K8s YAML field names. +#![allow(non_snake_case)] + +use crate::config_map; +use crate::mount_and_storage; +use crate::no_policy; +use crate::pod; +use crate::policy; +use crate::secret; +use crate::settings; +use crate::volume; + +use async_trait::async_trait; +use core::fmt::Debug; +use log::debug; +use protocols::agent; +use serde::{Deserialize, Serialize}; +use serde_yaml; +use std::boxed; +use std::collections::BTreeMap; +use std::fs::read_to_string; + +/// K8s API version and resource type. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct YamlHeader { + pub apiVersion: String, + pub kind: String, +} + +/// Trait implemented by each supportes K8s resource type (e.g., Pod or Deployment). +#[async_trait] +pub trait K8sResource { + async fn init( + &mut self, + use_cache: bool, + doc_mapping: &serde_yaml::Value, + silent_unsupported_fields: bool, + ); + + fn generate_policy(&self, agent_policy: &policy::AgentPolicy) -> String; + fn serialize(&mut self, policy: &str) -> String; + + fn get_sandbox_name(&self) -> Option; + fn get_namespace(&self) -> String; + + fn get_container_mounts_and_storages( + &self, + policy_mounts: &mut Vec, + storages: &mut Vec, + container: &pod::Container, + settings: &settings::Settings, + ); + + fn get_containers(&self) -> &Vec; + fn get_annotations(&self) -> &Option>; + fn use_host_network(&self) -> bool; +} + +/// See Reference / Kubernetes API / Common Definitions / LabelSelector. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct LabelSelector { + #[serde(skip_serializing_if = "Option::is_none")] + matchLabels: Option>, + + #[serde(skip_serializing_if = "Option::is_none")] + matchExpressions: Option>, +} + +/// See Reference / Kubernetes API / Common Definitions / LabelSelector. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct LabelSelectorRequirement { + key: String, + operator: String, + + #[serde(skip_serializing_if = "Option::is_none")] + values: Option>, +} + +/// Creates one of the supported K8s objects from a YAML string. +pub fn new_k8s_resource( + yaml: &str, + silent_unsupported_fields: bool, +) -> anyhow::Result<(boxed::Box, String)> { + let header = get_yaml_header(yaml)?; + let kind: &str = &header.kind; + let d = serde_yaml::Deserializer::from_str(&yaml); + + match kind { + "ConfigMap" => { + let config_map: config_map::ConfigMap = serde_ignored::deserialize(d, |path| { + handle_unused_field(&path.to_string(), silent_unsupported_fields); + }) + .unwrap(); + debug!("{:#?}", &config_map); + Ok((boxed::Box::new(config_map), header.kind)) + } + "Pod" => { + let pod: pod::Pod = serde_ignored::deserialize(d, |path| { + handle_unused_field(&path.to_string(), silent_unsupported_fields); + }) + .unwrap(); + debug!("{:#?}", &pod); + Ok((boxed::Box::new(pod), header.kind)) + } + "Secret" => { + let secret: secret::Secret = serde_ignored::deserialize(d, |path| { + handle_unused_field(&path.to_string(), silent_unsupported_fields); + }) + .unwrap(); + debug!("{:#?}", &secret); + Ok((boxed::Box::new(secret), header.kind)) + } + "ClusterRole" + | "ClusterRoleBinding" + | "LimitRange" + | "Namespace" + | "PersistentVolume" + | "PersistentVolumeClaim" + | "PriorityClass" + | "ResourceQuota" + | "RoleBinding" + | "Service" + | "ServiceAccount" => { + let no_policy = no_policy::NoPolicyResource { + yaml: yaml.to_string(), + }; + debug!("{:#?}", &no_policy); + Ok((boxed::Box::new(no_policy), header.kind)) + } + _ => todo!("Unsupported YAML spec kind: {}", kind), + } +} + +pub fn get_input_yaml(yaml_file: &Option) -> anyhow::Result { + let yaml_string = if let Some(yaml) = yaml_file { + read_to_string(&yaml)? + } else { + std::io::read_to_string(std::io::stdin())? + }; + + Ok(yaml_string) +} + +pub fn get_yaml_header(yaml: &str) -> anyhow::Result { + return Ok(serde_yaml::from_str(yaml)?); +} + +pub async fn k8s_resource_init(spec: &mut pod::PodSpec, use_cache: bool) { + for container in &mut spec.containers { + container.init(use_cache).await; + } + + pod::add_pause_container(&mut spec.containers, use_cache).await; + + if let Some(init_containers) = &spec.initContainers { + for container in init_containers { + let mut new_container = container.clone(); + new_container.init(use_cache).await; + spec.containers.insert(1, new_container); + } + } +} + +pub fn get_container_mounts_and_storages( + policy_mounts: &mut Vec, + storages: &mut Vec, + container: &pod::Container, + settings: &settings::Settings, + volumes: &Vec, +) { + if let Some(volume_mounts) = &container.volumeMounts { + for volume in volumes { + for volume_mount in volume_mounts { + if volume_mount.name.eq(&volume.name) { + mount_and_storage::get_mount_and_storage( + settings, + policy_mounts, + storages, + volume, + volume_mount, + ); + } + } + } + } +} + +/// Add the "io.katacontainers.config.agent.policy" annotation into +/// a serde representation of a K8s resource YAML. +pub fn add_policy_annotation( + mut ancestor: &mut serde_yaml::Value, + metadata_path: &str, + policy: &str, +) { + let annotations_key = serde_yaml::Value::String("annotations".to_string()); + let policy_key = serde_yaml::Value::String("io.katacontainers.config.agent.policy".to_string()); + let policy_value = serde_yaml::Value::String(policy.to_string()); + + let path_components = metadata_path.split('.'); + for name in path_components { + ancestor = ancestor.get_mut(&name).unwrap(); + } + + if let Some(annotations) = ancestor.get_mut(&annotations_key) { + if let Some(annotation) = annotations.get_mut(&policy_key) { + *annotation = policy_value; + } else if let Some(mapping_mut) = annotations.as_mapping_mut() { + mapping_mut.insert(policy_key, policy_value); + } else { + let mut new_annotations = serde_yaml::Mapping::new(); + new_annotations.insert(policy_key, policy_value); + *annotations = serde_yaml::Value::Mapping(new_annotations); + } + } else { + let mut new_annotations = serde_yaml::Mapping::new(); + new_annotations.insert(policy_key, policy_value); + ancestor + .as_mapping_mut() + .unwrap() + .insert(annotations_key, serde_yaml::Value::Mapping(new_annotations)); + } +} + +pub fn remove_policy_annotation(annotations: &mut BTreeMap) { + annotations.remove("io.katacontainers.config.agent.policy"); +} + +/// Report a fatal error if this app encounters an unsupported input YAML field, +/// unless the user requested this app to ignore unsupported input fields. +/// "Silent unsupported fields" is an expert level feature, because some of +/// the fields ignored silently might be relevant for the output policy, +/// with hard to predict outcomes. +fn handle_unused_field(path: &str, silent_unsupported_fields: bool) { + if !silent_unsupported_fields { + panic!("Unsupported field: {}", path); + } +}