Merge pull request #12542 from fidencio/topic/genpolicy-distribute-different-settings-rather-than-patching-for-ci

genpolicy: settings.d drop-ins and scenario example drop-ins
This commit is contained in:
Fabiano Fidêncio
2026-03-05 07:37:30 +01:00
committed by GitHub
16 changed files with 454 additions and 144 deletions

View File

@@ -958,6 +958,7 @@ dependencies = [
"env_logger",
"flate2",
"fs2",
"json-patch 4.1.0",
"k8s-cri",
"kata-agent-policy",
"kata-types",
@@ -1532,7 +1533,19 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b1fb8864823fad91877e6caea0baca82e49e8db50f8e5c9f9a453e27d3330fc"
dependencies = [
"jsonptr",
"jsonptr 0.4.7",
"serde",
"serde_json",
"thiserror 1.0.40",
]
[[package]]
name = "json-patch"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f300e415e2134745ef75f04562dd0145405c2f7fd92065db029ac4b16b57fe90"
dependencies = [
"jsonptr 0.7.1",
"serde",
"serde_json",
"thiserror 1.0.40",
@@ -1549,6 +1562,16 @@ dependencies = [
"serde_json",
]
[[package]]
name = "jsonptr"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "jwt"
version = "0.16.0"
@@ -1581,7 +1604,7 @@ name = "kata-agent-policy"
version = "0.1.0"
dependencies = [
"anyhow",
"json-patch",
"json-patch 2.0.0",
"regorus",
"serde",
"serde_json",

View File

@@ -46,6 +46,7 @@ oci-client = { version = "0.12.0" }
openssl = { version = "0.10.73", features = ["vendored"] }
serde_ignored = "0.1.7"
serde_json = "1.0.39"
json-patch = "4.1"
tempfile = "3.19.1"
tokio = { version = "1.38.0", features = ["rt-multi-thread"] }

View File

@@ -47,6 +47,13 @@ For advanced command line parameters, see [`genpolicy` advanced command line par
`genpolicy` has support for automatic Policy generation based on Kubernetes `DaemonSet`, `Deployment`, `Job`, `Pod`, `ReplicaSet`, `ReplicationController`, and `StatefulSet` input `YAML` files.
# Settings directory and drop-ins
You can pass a **directory** to `-j` instead of a single file. In that case `genpolicy` loads `genpolicy-settings.json` from that directory and applies all `genpolicy-settings.d/*.json` files (sorted by name) in order. Each drop-in must be an [RFC 6902 JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902): a JSON array of operations (`add`, `remove`, `replace`, `move`, `copy`, `test`). This gives precise control (e.g. array indices) and optional `test` for assertions.
- **`genpolicy-settings.d/`** — empty by default; add your drop-in JSON Patch files here.
- **`drop-in-examples/`** — example scenario drop-ins (`10-*.json` platform base, `20-*.json` overlays), each a JSON Patch array. Copy the ones you need into your own `genpolicy-settings.d/`. See the [drop-in examples documentation](drop-in-examples/README.md). These examples are tested in Kata Containers CI.
# Policy details
See [auto-generated Policy details](genpolicy-auto-generated-policy-details.md).

View File

@@ -0,0 +1,82 @@
[
{
"op": "replace",
"path": "/cluster_config/guest_pull",
"value": false
},
{
"op": "replace",
"path": "/cluster_config/pause_container_id_policy",
"value": "v2"
},
{
"op": "replace",
"path": "/cluster_config/pause_container_image",
"value": "mcr.microsoft.com/oss/v2/kubernetes/pause:3.6"
},
{
"op": "replace",
"path": "/common/root_path",
"value": "/run/kata-containers/shared/containers/$(bundle-id)/rootfs"
},
{
"op": "replace",
"path": "/kata_config/enable_configmap_secret_storages",
"value": true
},
{
"op": "replace",
"path": "/pause_container/Process/User/UID",
"value": 0
},
{
"op": "replace",
"path": "/pause_container/Process/User/GID",
"value": 0
},
{
"op": "replace",
"path": "/request_defaults/UpdateEphemeralMountsRequest",
"value": true
},
{
"op": "replace",
"path": "/sandbox/storages",
"value": [
{
"driver": "ephemeral",
"driver_options": [],
"fs_group": null,
"fstype": "tmpfs",
"mount_point": "/run/kata-containers/sandbox/shm",
"options": [
"noexec",
"nosuid",
"nodev",
"mode=1777",
"size=67108864"
],
"source": "shm"
},
{
"driver": "virtio-fs",
"driver_options": [],
"fs_group": null,
"fstype": "virtiofs",
"mount_point": "/run/kata-containers/shared/containers/",
"options": [],
"source": "kataShared"
}
]
},
{
"op": "replace",
"path": "/volumes/configMap/driver",
"value": "watchable-bind"
},
{
"op": "replace",
"path": "/volumes/configMap/mount_point",
"value": "^$(cpath)/watchable/$(bundle-id)-[a-z0-9]{16}-"
}
]

View File

@@ -0,0 +1,82 @@
[
{
"op": "replace",
"path": "/cluster_config/guest_pull",
"value": false
},
{
"op": "replace",
"path": "/cluster_config/pause_container_id_policy",
"value": "v2"
},
{
"op": "replace",
"path": "/cluster_config/pause_container_image",
"value": "mcr.microsoft.com/oss/v2/kubernetes/pause:3.6"
},
{
"op": "replace",
"path": "/common/root_path",
"value": "/run/kata-containers/shared/containers/$(bundle-id)/rootfs"
},
{
"op": "replace",
"path": "/kata_config/enable_configmap_secret_storages",
"value": true
},
{
"op": "replace",
"path": "/pause_container/Process/User/UID",
"value": 0
},
{
"op": "replace",
"path": "/pause_container/Process/User/GID",
"value": 0
},
{
"op": "replace",
"path": "/request_defaults/UpdateEphemeralMountsRequest",
"value": true
},
{
"op": "replace",
"path": "/sandbox/storages",
"value": [
{
"driver": "ephemeral",
"driver_options": [],
"fs_group": null,
"fstype": "tmpfs",
"mount_point": "/run/kata-containers/sandbox/shm",
"options": [
"noexec",
"nosuid",
"nodev",
"mode=1777",
"size=67108864"
],
"source": "shm"
},
{
"driver": "virtio-fs",
"driver_options": [],
"fs_group": null,
"fstype": "virtiofs",
"mount_point": "/run/kata-containers/shared/containers/",
"options": [],
"source": "kataShared"
}
]
},
{
"op": "replace",
"path": "/volumes/configMap/driver",
"value": "watchable-bind"
},
{
"op": "replace",
"path": "/volumes/configMap/mount_point",
"value": "^$(cpath)/watchable/$(bundle-id)-[a-z0-9]{16}-"
}
]

View File

@@ -0,0 +1,62 @@
[
{
"op": "replace",
"path": "/cluster_config/guest_pull",
"value": false
},
{
"op": "replace",
"path": "/common/root_path",
"value": "/run/kata-containers/shared/containers/$(bundle-id)/rootfs"
},
{
"op": "replace",
"path": "/kata_config/enable_configmap_secret_storages",
"value": true
},
{
"op": "replace",
"path": "/request_defaults/UpdateEphemeralMountsRequest",
"value": true
},
{
"op": "replace",
"path": "/sandbox/storages",
"value": [
{
"driver": "ephemeral",
"driver_options": [],
"fs_group": null,
"fstype": "tmpfs",
"mount_point": "/run/kata-containers/sandbox/shm",
"options": [
"noexec",
"nosuid",
"nodev",
"mode=1777",
"size=67108864"
],
"source": "shm"
},
{
"driver": "virtio-fs",
"driver_options": [],
"fs_group": null,
"fstype": "virtiofs",
"mount_point": "/run/kata-containers/shared/containers/",
"options": [],
"source": "kataShared"
}
]
},
{
"op": "replace",
"path": "/volumes/configMap/driver",
"value": "watchable-bind"
},
{
"op": "replace",
"path": "/volumes/configMap/mount_point",
"value": "^$(cpath)/watchable/$(bundle-id)-[a-z0-9]{16}-"
}
]

View File

@@ -0,0 +1,7 @@
[
{
"op": "replace",
"path": "/cluster_config/guest_pull",
"value": false
}
]

View File

@@ -0,0 +1,7 @@
[
{
"op": "replace",
"path": "/kata_config/oci_version",
"value": "1.2.0"
}
]

View File

@@ -0,0 +1,7 @@
[
{
"op": "replace",
"path": "/kata_config/oci_version",
"value": "1.2.1"
}
]

View File

@@ -0,0 +1,7 @@
[
{
"op": "replace",
"path": "/kata_config/oci_version",
"value": "1.3.0"
}
]

View File

@@ -0,0 +1,31 @@
# Example drop-ins for genpolicy settings
Copy the drop-in file(s) you need into the `genpolicy-settings.d/` subdirectory next to your `genpolicy-settings.json`, then point `genpolicy -j` at the parent directory. For example:
```
my-settings/
genpolicy-settings.json
genpolicy-settings.d/
10-non-coco-drop-in.json
20-oci-1.2.1-drop-in.json
```
```sh
genpolicy -j my-settings/ ...
```
Each drop-in is an [RFC 6902 JSON Patch](https://datatracker.ietf.org/doc/html/rfc6902): a JSON array of operations (`add`, `remove`, `replace`, `move`, `copy`, `test`). Use `replace` for existing paths, `add` for new keys or array append (path ending in `/-`), and optional `test` to assert values before changing them.
Drop-ins are layered: `10-*` files set the platform base, `20-*` files overlay OCI version and other adjustments. You can combine multiple drop-ins (e.g. `10-non-coco-drop-in.json` + `20-oci-1.2.1-drop-in.json`).
| Drop-in file | Use case |
|--------------|----------|
| `10-non-coco-drop-in.json` | Non-confidential guest (e.g. standard VMs) |
| `10-non-coco-aks-drop-in.json` | Non-confidential guest on AKS |
| `10-non-coco-aks-cbl-mariner-drop-in.json` | Non-confidential guest on AKS with CBL-Mariner host |
| `20-oci-1.2.0-drop-in.json` | OCI bundle version 1.2.0 (e.g. CBL-Mariner) |
| `20-oci-1.2.1-drop-in.json` | OCI bundle version 1.2.1 (e.g. k3s, rke2, NVIDIA GPU) |
| `20-oci-1.3.0-drop-in.json` | OCI bundle version 1.3.0 (e.g. containerd 2.2.x) |
| `20-experimental-force-guest-pull-drop-in.json` | Disable guest pull |
Request/exec overrides (e.g. allowing `kubectl exec` or specific ttRPC requests) are not shipped as drop-in examples; build your own drop-in or merge the needed `request_defaults` into a local file in `genpolicy-settings.d/`.

View File

@@ -86,20 +86,22 @@ To print the `base64` encoded Policy, in addition to adding it into the `YAML` f
$ genpolicy -b -y test.yaml
```
# Use a custom `genpolicy` settings file
# Use a custom `genpolicy` settings file or directory
The default `genpolicy` settings file is `./genpolicy-settings.json`. Users can specify in the command line a different settings file by using the `-j` parameter - e.g.,
The default is `./genpolicy-settings.json`. With the `-j` parameter you can pass either a settings file or a **directory**. If you pass a directory, `genpolicy` loads `genpolicy-settings.json` from it and applies all `genpolicy-settings.d/*.json` drop-ins (sorted by name) as RFC 6902 JSON Patches.
```bash
$ genpolicy -j my-settings.json -y test.yaml
$ genpolicy -j /path/to/settings-dir -y test.yaml
```
# Use a custom path to `genpolicy` input files
By default, the `genpolicy` input files [`rules.rego`](rules.rego) and [`genpolicy-settings.json`](genpolicy-settings.json) must be present in the current directory - otherwise `genpolicy` returns an error. Users can specify different paths to these two files, using the `-p` and `-j` command line parameters - e.g.,
By default, the `genpolicy` input files [`rules.rego`](rules.rego) and [`genpolicy-settings.json`](genpolicy-settings.json) must be present in the current directory - otherwise `genpolicy` returns an error. You can pass a different file or directory with `-j` and a different rules file with `-p` - e.g.,
```bash
$ genpolicy -p /tmp/rules.rego -j /tmp/genpolicy-settings.json -y test.yaml
$ genpolicy -p /tmp/rules.rego -j /tmp/settings-dir -y test.yaml
```
# Silently ignore unsupported input `YAML` fields

View File

@@ -8,10 +8,12 @@
use crate::policy;
use json_patch::{patch, Patch};
use log::debug;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::str;
use serde_json::Value;
use std::fs;
use std::path::Path;
/// Policy settings loaded from genpolicy-settings.json.
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -79,17 +81,64 @@ pub struct KataConfig {
pub enable_configmap_secret_storages: bool,
}
/// Drop-ins in genpolicy-settings.d/ must be RFC 6902 JSON Patch documents (JSON array of
/// operations: add, remove, replace, move, copy, test). This allows precise control (e.g. array
/// indices) and optional `test` for assertions.
impl Settings {
pub fn new(json_settings_path: &str) -> Self {
debug!("Loading settings file...");
if let Ok(file) = File::open(json_settings_path) {
let settings: Self = serde_json::from_reader(file).unwrap();
debug!("settings = {:?}", &settings);
Self::validate_settings(&settings);
settings
debug!("Loading settings from: {}", json_settings_path);
let path = Path::new(json_settings_path);
let (base_path, drop_in_dir) = if path.is_dir() {
(
path.join("genpolicy-settings.json"),
Some(path.join("genpolicy-settings.d")),
)
} else {
panic!("Cannot open file {json_settings_path}. Please copy it to the current directory or specify the path to it using the -j parameter.");
(path.to_path_buf(), None)
};
let mut base: Value = {
let contents = fs::read_to_string(&base_path).unwrap_or_else(|e| {
panic!(
"Cannot read {}: {}. Specify the path using the -j parameter.",
base_path.display(),
e
)
});
serde_json::from_str(&contents)
.unwrap_or_else(|e| panic!("Invalid JSON in {}: {}", base_path.display(), e))
};
if let Some(ref drop_in_dir) = drop_in_dir {
if drop_in_dir.is_dir() {
let mut entries: Vec<_> = fs::read_dir(drop_in_dir)
.unwrap_or_else(|e| {
panic!("Cannot read drop-in dir {}: {}", drop_in_dir.display(), e)
})
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().is_some_and(|ext| ext == "json"))
.collect();
entries.sort_by_cached_key(|e| e.file_name());
for entry in entries {
let p = entry.path();
debug!("Applying drop-in: {:?}", p);
let contents = fs::read_to_string(&p)
.unwrap_or_else(|e| panic!("Cannot read drop-in {}: {}", p.display(), e));
let patch_ops: Patch = serde_json::from_str(&contents).unwrap_or_else(|e| {
panic!("Invalid JSON Patch in drop-in {}: {}", p.display(), e)
});
patch(&mut base, &patch_ops).unwrap_or_else(|e| {
panic!("Failed to apply JSON Patch from {}: {}", p.display(), e)
});
}
}
}
let settings: Self = serde_json::from_value(base)
.unwrap_or_else(|e| panic!("Merged settings are invalid: {}", e));
debug!("settings = {:?}", &settings);
Self::validate_settings(&settings);
settings
}
pub fn get_container_settings(&self, is_pause_container: bool) -> &policy::KataSpec {

View File

@@ -42,7 +42,7 @@ struct CommandLineOptions {
short = 'j',
long,
default_value_t = String::from("genpolicy-settings.json"),
help = "Path to genpolicy settings file"
help = "Path to genpolicy settings file or directory (with genpolicy-settings.json and optional genpolicy-settings.d/*.json)"
)]
json_settings_path: String,