Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2cc50f9
Support windows platform images
DomAyre Oct 15, 2025
acf0de5
Pull specific versions of integrity-vhd instead of latest
DomAyre Oct 15, 2025
cce8274
Set version to be wcow
DomAyre Oct 15, 2025
6f94642
Make version compliant
DomAyre Oct 15, 2025
88dae8a
Make the policy windows shaped
DomAyre Oct 17, 2025
261d815
Support --debug-mode
DomAyre Nov 3, 2025
7633693
Make lib a module
DomAyre Nov 3, 2025
5a4707f
Only add platform flag on windows
DomAyre Nov 3, 2025
4f3c176
Merge branch 'main' into tingmao/windows-platform-support
micromaomao Apr 1, 2026
8952513
Use updated integrity-vhd for fixed C-WCOW policy gen
micromaomao Apr 1, 2026
413707d
Support windows images also in the new "containers from_image" command
micromaomao Apr 1, 2026
d9c63da
[confcom]: acipolicygen: Fix missing platform field in generated poli…
micromaomao Apr 9, 2026
59627ed
Update tooling to consume json
MahatiC Apr 13, 2026
28eec7d
Bump framework version for windows
MahatiC Apr 10, 2026
358bd33
Update dll requirements
MahatiC Apr 10, 2026
2efb197
Bump confcom version 2.0.0 and pick v2.0 dmverity-vhd release
MahatiC Apr 10, 2026
631c766
Merge branch 'main' into HEAD
micromaomao Apr 13, 2026
a8acdb9
[confcom] Update policy api version for "new style" command too
micromaomao Apr 13, 2026
8889074
[confcom] Make the to-be-released version 2.0.0b1 instead of an actua…
micromaomao Apr 13, 2026
ace2f50
Update azext_confcom/data/README
micromaomao Apr 14, 2026
53bf34b
Update all sample policies to the new api version and add the rw_moun…
micromaomao Apr 14, 2026
c47d62d
Remove misleading comment
micromaomao Apr 14, 2026
393e97d
[confcom] Fix trying to fetch image with a name containing unresolved…
micromaomao Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/confcom/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
Release History
===============

2.0.0b1
+++++
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reStructuredText section underline length doesn’t match the header length (2.0.0b1 has 7 characters but the underline is only 5 +). This will produce docutils/Sphinx formatting warnings; adjust the underline to match the version string length (consistent with other entries like 1.8.0).

Suggested change
+++++
+++++++

Copilot uses AI. Check for mistakes.
* Add Windows container support with CIM-based layer hashing
* Support for mounted_cim field in security policies for Windows containers

1.8.0
+++++
* Ensure that fragments are attached to the correct manifest for a multiarch image.
Expand Down
3 changes: 3 additions & 0 deletions src/confcom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
```

- Windows: [Docker Desktop](https://www.docker.com/products/docker-desktop) and [WSL2](https://docs.microsoft.com/en-us/windows/wsl/install)
- **CimWriter.dll** (Windows only, for Windows container support)
- Required for generating security policies for Windows containers
- Windows Server 2025 or newer is recommended for deterministic hash generation

## Installation Instructions (End User)

Expand Down
3 changes: 2 additions & 1 deletion src/confcom/azext_confcom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ package policy
import future.keywords.every
import future.keywords.in

api_version := "0.10.0"
api_version := "0.11.0"
framework_version := "0.1.0"

fragments := [...]
Expand Down Expand Up @@ -432,6 +432,7 @@ runtime_logging := data.framework.runtime_logging
load_fragment := data.framework.load_fragment
scratch_mount := data.framework.scratch_mount
scratch_unmount := data.framework.scratch_unmount
rw_mount_device := data.framework.rw_mount_device

reason := {"errors": data.framework.errors}
```
Expand Down
2 changes: 1 addition & 1 deletion src/confcom/azext_confcom/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@
parameters:
- name: --platform
type: str
short-summary: 'The name of the platform the container definition will run on'
short-summary: 'The name of the platform the container definition will run on. Must be either "aci" or "vn2".'


examples:
Expand Down
5 changes: 3 additions & 2 deletions src/confcom/azext_confcom/azext_metadata.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"azext.minCliCoreVersion": "2.26.2"
}
"azext.minCliCoreVersion": "2.26.2",
"azext.isPreview": true
}
4 changes: 2 additions & 2 deletions src/confcom/azext_confcom/command/containers_from_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
from azext_confcom.lib.containers import from_image as lib_containers_from_image


def containers_from_image(image: str, platform: str) -> None:
print(json.dumps(lib_containers_from_image(image, platform)))
def containers_from_image(image: str, aci_or_vn2: str) -> None:
print(json.dumps(lib_containers_from_image(image, aci_or_vn2)))
2 changes: 1 addition & 1 deletion src/confcom/azext_confcom/command/containers_from_vn2.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def containers_from_vn2(

container_defs = []
for template_container, template_doc in template_containers:
image_container_def = container_from_image(template_container.get("image"), platform="vn2")
image_container_def = container_from_image(template_container.get("image"), aci_or_vn2="vn2")

template_container_def = {
"name": template_container.get("name"),
Expand Down
3 changes: 3 additions & 0 deletions src/confcom/azext_confcom/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@
POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS_RULE = "pattern"
POLICY_FIELD_CONTAINERS_ELEMENTS_REQUIRED = "required"
POLICY_FIELD_CONTAINERS_ELEMENTS_LAYERS = "layers"
POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTED_CIM = "mounted_cim"
POLICY_FIELD_CONTAINERS_ELEMENTS_WORKINGDIR = "working_dir"
POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS = "mounts"
POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS_SOURCE = "source"
Expand Down Expand Up @@ -211,6 +212,7 @@
DEFAULT_REGO_FRAGMENTS = _config["default_rego_fragments"]
# things that need to be set for debug mode
DEBUG_MODE_SETTINGS = _config["debugMode"]
DEBUG_MODE_SETTINGS_WINDOWS = _config["debugModeWindows"]
# reserved fragment names for existing pieces of Rego
RESERVED_FRAGMENT_NAMES = _config["reserved_fragment_namespaces"]
# fragment artifact type
Expand All @@ -227,6 +229,7 @@
}
"""
CUSTOMER_REGO_POLICY = load_str_from_file(REGO_FILE_PATH)
CUSTOMER_REGO_POLICY_WINDOWS = load_str_from_file(f"{script_directory}/data/customer_rego_policy_windows.txt")
CUSTOMER_REGO_FRAGMENT = load_str_from_file(REGO_FRAGMENT_FILE_PATH)
# sidecar rego file
SIDECAR_REGO_FILE = "./data/sidecar_rego_policy.txt"
Expand Down
37 changes: 30 additions & 7 deletions src/confcom/azext_confcom/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ def from_json(
mounts=mounts,
allow_elevated=allow_elevated,
extraEnvironmentRules=[],
platform=container_json["platform"],
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContainerImage.from_json now requires container_json["platform"], which will raise a KeyError for older inputs (e.g., previously generated JSON/policies without a platform field). Use a default such as container_json.get("platform", "linux/amd64") (and similarly for other direct indexing) to preserve backward compatibility.

Suggested change
platform=container_json["platform"],
platform=container_json.get("platform", "linux/amd64"),

Copilot uses AI. Check for mistakes.
execProcesses=exec_processes,
signals=signals,
user=user,
Expand All @@ -583,6 +584,7 @@ def __init__(
allow_elevated: bool,
id_val: str,
extraEnvironmentRules: Dict,
platform: str = "linux/amd64",
entrypoint: List[str] = None,
capabilities: Dict = copy.deepcopy(_CAPABILITIES),
user: Dict = copy.deepcopy(_DEFAULT_USER),
Expand All @@ -604,6 +606,7 @@ def __init__(
self._command = command
self._workingDir = workingDir
self._layers = []
self._mounted_cim = []
self._mounts = mounts
self._allow_elevated = allow_elevated
self._allow_stdio_access = allowStdioAccess
Expand All @@ -615,6 +618,7 @@ def __init__(
self._exec_processes = execProcesses or []
self._signals = signals or []
self._extraEnvironmentRules = extraEnvironmentRules
self._platform = platform

def get_policy_json(self, omit_id: bool = False) -> str:
return self._populate_policy_json_elements(omit_id=omit_id)
Expand Down Expand Up @@ -658,6 +662,12 @@ def get_layers(self) -> List[str]:
def set_layers(self, layers: List[str]) -> None:
self._layers = layers

def get_mounted_cim(self) -> List[str]:
return self._mounted_cim

def set_mounted_cim(self, mounted_cim: List[str]) -> None:
self._mounted_cim = mounted_cim

def get_user(self) -> Dict:
return self._user

Expand Down Expand Up @@ -764,16 +774,28 @@ def _populate_policy_json_elements(self, omit_id: bool = False) -> Dict[str, Any
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS: self._get_environment_rules(),
config.POLICY_FIELD_CONTAINERS_ELEMENTS_WORKINGDIR: self._workingDir,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS: self._get_mounts_json(),
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED: self._allow_elevated,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_EXEC_PROCESSES: self._exec_processes,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_SIGNAL_CONTAINER_PROCESSES: self._signals,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user(),
config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES: self._capabilities,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256: self._seccomp_profile_sha256,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS: self._allow_stdio_access,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES: not self._allow_privilege_escalation
}

if self._platform.startswith("linux"):
elements.update({
config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES: self._capabilities,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256: self._seccomp_profile_sha256,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user(),
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED: self._allow_elevated,
config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES: not self._allow_privilege_escalation,
})
elif self._platform.startswith("windows"):
elements.update({
config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user()["user_idname"]["pattern"],
})
# Add mounted_cim for Windows if present
if self._mounted_cim:
elements[config.POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTED_CIM] = self._mounted_cim


if not omit_id:
elements[config.POLICY_FIELD_CONTAINERS_ID] = self._identifier
# if we are omitting the id, we should remove the id value from the policy if it's in the name field
Expand All @@ -793,13 +815,14 @@ def from_json(
image.__class__ = UserContainerImage
# inject default mounts for user container
if (image.base not in config.BASELINE_SIDECAR_CONTAINERS) and (not is_vn2):
image.get_mounts().extend(_DEFAULT_MOUNTS)
if container_json["platform"].startswith("linux"):
image.get_mounts().extend(_DEFAULT_MOUNTS)

if (image.base not in config.BASELINE_SIDECAR_CONTAINERS) and (is_vn2):
image.get_mounts().extend(_DEFAULT_MOUNTS_VN2)

# Start with the customer environment rules
env_rules = copy.deepcopy(_INJECTED_CUSTOMER_ENV_RULES)
env_rules = copy.deepcopy(_INJECTED_CUSTOMER_ENV_RULES) if container_json["platform"].startswith("linux") else dict()
# If is_vn2, add the VN2 environment rules
if is_vn2:
env_rules += _INJECTED_SERVICE_VN2_ENV_RULES
Expand Down
3 changes: 1 addition & 2 deletions src/confcom/azext_confcom/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ def acipolicygen_confcom(
policy.set_fragment_contents(fragment_policy_list)

for count, policy in enumerate(container_group_policies):
# this is where parameters and variables are populated
policy.populate_policy_content_for_all_images(
individual_image=bool(image_name), tar_mapping=tar_mapping, faster_hashing=faster_hashing
)
Expand Down Expand Up @@ -581,7 +580,7 @@ def containers_from_image(
) -> None:
_containers_from_image(
image=image,
platform=platform,
aci_or_vn2=platform,
)


Expand Down
1 change: 1 addition & 0 deletions src/confcom/azext_confcom/data/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
internal_config.json and customer_rego_policy.txt are used by the "old style" acipolicygen command. New work should (also) happen in src/confcom/azext_confcom/lib/policy.py (or ensure that it is implemented for `containers from_image`), as eventually the old command will invoke that.
1 change: 1 addition & 0 deletions src/confcom/azext_confcom/data/customer_rego_policy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ runtime_logging := data.framework.runtime_logging
load_fragment := data.framework.load_fragment
scratch_mount := data.framework.scratch_mount
scratch_unmount := data.framework.scratch_unmount
rw_mount_device := data.framework.rw_mount_device

reason := {"errors": data.framework.errors}
30 changes: 30 additions & 0 deletions src/confcom/azext_confcom/data/customer_rego_policy_windows.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package policy

import future.keywords.every
import future.keywords.in

api_version := %s
framework_version := "0.4.1"

fragments := %s

containers := %s

allow_properties_access := %s
allow_dump_stacks := %s
allow_runtime_logging := %s
allow_environment_variable_dropping := %s

create_container := data.framework.create_container
exec_in_container := data.framework.exec_in_container
exec_external := data.framework.exec_external
shutdown_container := data.framework.shutdown_container
signal_container_process := data.framework.signal_container_process
get_properties := data.framework.get_properties
dump_stacks := data.framework.dump_stacks
runtime_logging := data.framework.runtime_logging
load_fragment := data.framework.load_fragment
scratch_mount := data.framework.scratch_mount
mount_cims := data.framework.mount_cims

reason := {"errors": data.framework.errors}
25 changes: 24 additions & 1 deletion src/confcom/azext_confcom/data/internal_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"maxVersion": "1.0.0",
"minVersion": "0.0.1"
},
"version_api": "0.10.0",
"version_api": "0.11.0",
"openGCS": {
"environmentVariables": [
{
Expand Down Expand Up @@ -193,6 +193,29 @@
"allowCapabilityDropping": true,
"allowUnencryptedScratch": false
},
"debugModeWindows": {
"environmentVariables": [
{
"name": ".+",
"value": ".+",
"strategy": "re2",
"required": false
}
],
"execProcesses": [
{
"command": [
"cmd.exe"
],
"signals": [],
"allow_stdio_access": true
}
],
"allowPropertiesAccess": true,
"allowDumpStacks": true,
"allowRuntimeLogging": true,
"allowEnvironmentVariableDropping": true
},
"containerd": {
"defaultWorkingDir": "/"
},
Expand Down
3 changes: 2 additions & 1 deletion src/confcom/azext_confcom/docs/policy_enforcement_points.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ package mypolicy
import future.keywords.every
import future.keywords.in

api_version := "0.10.0"
api_version := "0.11.0"
framework_version := "0.1.0"

fragments := [...]
Expand Down Expand Up @@ -71,6 +71,7 @@ runtime_logging := data.framework.runtime_logging
load_fragment := data.framework.load_fragment
scratch_mount := data.framework.scratch_mount
scratch_unmount := data.framework.scratch_unmount
rw_mount_device := data.framework.rw_mount_device

reason := {"errors": data.framework.errors}
```
Expand Down
12 changes: 8 additions & 4 deletions src/confcom/azext_confcom/lib/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
# Licensed under the MIT License. See License.txt in the project root for license information.
# --------------------------------------------------------------------------------------------

from typing import Optional
from dataclasses import asdict
from azext_confcom.lib.images import get_image_layers, get_image_config
from azext_confcom.lib.images import get_image_layers, get_image_config, get_image_platform
from azext_confcom.lib.platform import ACI_MOUNTS, VN2_MOUNTS


Expand Down Expand Up @@ -35,17 +36,20 @@ def merge_containers(*args) -> dict:
return merged_container


def from_image(image: str, platform: str) -> dict:
def from_image(image: str, aci_or_vn2: str) -> dict:

mounts = {
"aci": [asdict(mount) for mount in ACI_MOUNTS],
"vn2": VN2_MOUNTS,
}.get(platform, None)
}.get(aci_or_vn2, None)

image_platform = get_image_platform(image)

return {
"id": image,
"name": image,
"layers": get_image_layers(image),
"layers": get_image_layers(image, platform=image_platform),
"platform": image_platform,
**({"mounts": mounts} if mounts else {}),
**get_image_config(image),
}
13 changes: 13 additions & 0 deletions src/confcom/azext_confcom/lib/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from azext_confcom import config

def get_debug_mode_exec_procs(debug_mode: bool, platform: str) -> list:

if not debug_mode:
return []

if platform.startswith("linux"):
return config.DEBUG_MODE_SETTINGS.get(config.ACI_FIELD_CONTAINERS_EXEC_PROCESSES)
elif platform.startswith("windows"):
return config.DEBUG_MODE_SETTINGS_WINDOWS.get(config.ACI_FIELD_CONTAINERS_EXEC_PROCESSES)
else:
raise ValueError(f"Unsupported platform for debug mode settings: {platform}")
Loading
Loading