Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 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
ef8c8cd
[confcom] Fix lint errors
MahatiC Apr 14, 2026
608d129
[confcom] Add --platform commandline option
MahatiC Apr 14, 2026
b6816c4
[confcom] fix Virtual Node bug
MahatiC Apr 14, 2026
fa1c0e2
[confcom] Derive image platform and add --platform validation
MahatiC Apr 14, 2026
53515f9
[confcom] update help text and README
MahatiC Apr 15, 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
+++++
* 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
18 changes: 18 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 Expand Up @@ -57,6 +60,21 @@ The `confcom` extension does not currently support:
- Variables and Parameters with non-primitive data types e.g. objects and arrays
- Nested and Linked ARM Templates

## Platform Support (Linux and Windows Policies)

The `--platform` parameter controls whether policies are generated for Linux (`linux/amd64`, the default) or Windows (`windows/amd64`) containers.

**Docker Desktop must be running in the matching container mode** to produce correct layer hashes:

| Policy Target | Docker Container Mode | Where to Run |
|---|---|---|
| Linux (`--platform linux/amd64`) | Linux containers | WSL or PowerShell |
| Windows (`--platform windows/amd64`) | Windows containers | PowerShell only |

- **Windows policies cannot be generated from WSL**, because Windows layer hashing (CIMfs) requires Windows APIs.
- **Linux policies can be generated from either WSL or PowerShell**, as long as Docker Desktop is in Linux containers mode.
- Running with the wrong Docker container mode may produce **incorrect layer hashes** that will cause the container to be rejected at runtime.

## Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
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
8 changes: 7 additions & 1 deletion src/confcom/azext_confcom/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@
type: boolean
short-summary: 'When enabled, the default fragments are not included in the generated policy. This includes containers needed to mount azure files, mount secrets, mount git repos, and other common ACI features'

- name: --platform
type: string
short-summary: 'Target platform for policy generation (linux/amd64 or windows/amd64). Defaults to linux/amd64. Docker Desktop must be running in the matching container mode to produce correct layer hashes.'

examples:
- name: Input an ARM Template file to inject a base64 encoded Confidential Container Security Policy into the ARM Template
text: az confcom acipolicygen --template-file "./template.json"
Expand All @@ -116,6 +120,8 @@
text: az confcom acipolicygen --template-file "./template.json" --tar "./image.tar"
- name: Input an ARM Template file and use a fragments JSON file to generate a policy
text: az confcom acipolicygen --template-file "./template.json" --fragments-json "./fragments.json" --include-fragments
- name: Generate a Windows container policy (requires Docker Desktop in Windows containers mode)
text: az confcom acipolicygen --template-file "./template.json" --platform windows/amd64 --outraw-pretty-print
"""

helps[
Expand Down Expand Up @@ -340,7 +346,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
11 changes: 11 additions & 0 deletions src/confcom/azext_confcom/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ def load_arguments(self, _):
help="Image Name",
validator=validate_aci_source
)
c.argument(
"platform",
options_list=("--platform",),
required=False,
default="linux/amd64",
help="Target platform for policy generation. Defaults to linux/amd64. "
"Note: Docker Desktop must be running in the matching container mode "
"(Linux containers for linux/amd64, Windows containers for windows/amd64) "
"to produce correct layer hashes.",
choices=["linux/amd64", "windows/amd64"],
)
c.argument(
"tar_mapping_location",
options_list=("--tar",),
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
39 changes: 32 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"],
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,27 @@ 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 +814,17 @@ 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 []
)
# If is_vn2, add the VN2 environment rules
if is_vn2:
env_rules += _INJECTED_SERVICE_VN2_ENV_RULES
Expand Down
11 changes: 8 additions & 3 deletions src/confcom/azext_confcom/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def acipolicygen_confcom(
virtual_node_yaml_path: str,
infrastructure_svn: str,
tar_mapping_location: str,
platform: str = "linux/amd64",
container_definitions: Optional[list] = None,
approve_wildcards: str = False,
outraw: bool = False,
Expand Down Expand Up @@ -120,6 +121,7 @@ def acipolicygen_confcom(
if output_type == security_policy.OutputType.DEFAULT
else "clear text",
)
logger.warning("Using platform: %s", platform)
# error checking for making sure an input is provided is above
if input_path:
container_group_policies = security_policy.load_policy_from_json_file(
Expand All @@ -128,6 +130,7 @@ def acipolicygen_confcom(
infrastructure_svn=infrastructure_svn,
disable_stdio=(not stdio_enabled),
exclude_default_fragments=exclude_default_fragments,
platform=platform,
)
elif arm_template:
container_group_policies = security_policy.load_policy_from_arm_template_file(
Expand All @@ -140,10 +143,12 @@ def acipolicygen_confcom(
diff_mode=diff,
rego_imports=fragments_list,
exclude_default_fragments=exclude_default_fragments,
platform=platform,
)
elif image_name:
container_group_policies = security_policy.load_policy_from_image_name(
image_name, debug_mode=debug_mode, disable_stdio=(not stdio_enabled)
image_name, debug_mode=debug_mode, disable_stdio=(not stdio_enabled),
platform=platform,
)
elif virtual_node_yaml_path:
container_group_policies = security_policy.load_policy_from_virtual_node_yaml_file(
Expand All @@ -155,6 +160,7 @@ def acipolicygen_confcom(
rego_imports=fragments_list,
exclude_default_fragments=exclude_default_fragments,
infrastructure_svn=infrastructure_svn,
platform=platform,
)
elif container_definitions:
container_group_policies = AciPolicy(
Expand Down Expand Up @@ -193,7 +199,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 +586,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
Loading
Loading