Skip to content

Commit c24579e

Browse files
authored
Merge branch 'Azure:main' into henrydai/implementChangeStateCRUD0901
2 parents 62b31b7 + ff6e40b commit c24579e

File tree

197 files changed

+2513
-1858
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

197 files changed

+2513
-1858
lines changed

src/confcom/HISTORY.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
Release History
44
===============
55

6+
2.0.0b1
7+
+++++++
8+
* Add Windows container support with CIM-based layer hashing
9+
* Support for mounted_cim field in security policies for Windows containers
10+
611
1.8.0
712
+++++
813
* Ensure that fragments are attached to the correct manifest for a multiarch image.

src/confcom/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
```
3030

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

3336
## Installation Instructions (End User)
3437

@@ -57,6 +60,21 @@ The `confcom` extension does not currently support:
5760
- Variables and Parameters with non-primitive data types e.g. objects and arrays
5861
- Nested and Linked ARM Templates
5962

63+
## Platform Support (Linux and Windows Policies)
64+
65+
The `--platform` parameter controls whether policies are generated for Linux (`linux/amd64`, the default) or Windows (`windows/amd64`) containers.
66+
67+
**Docker Desktop must be running in the matching container mode** to produce correct layer hashes:
68+
69+
| Policy Target | Docker Container Mode | Where to Run |
70+
|---|---|---|
71+
| Linux (`--platform linux/amd64`) | Linux containers | WSL or PowerShell |
72+
| Windows (`--platform windows/amd64`) | Windows containers | PowerShell only |
73+
74+
- **Windows policies cannot be generated from WSL**, because Windows layer hashing (CIMfs) requires Windows APIs.
75+
- **Linux policies can be generated from either WSL or PowerShell**, as long as Docker Desktop is in Linux containers mode.
76+
- Running with the wrong Docker container mode may produce **incorrect layer hashes** that will cause the container to be rejected at runtime.
77+
6078
## Trademarks
6179

6280
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft

src/confcom/azext_confcom/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ package policy
399399
import future.keywords.every
400400
import future.keywords.in
401401
402-
api_version := "0.10.0"
402+
api_version := "0.11.0"
403403
framework_version := "0.1.0"
404404
405405
fragments := [...]
@@ -432,6 +432,7 @@ runtime_logging := data.framework.runtime_logging
432432
load_fragment := data.framework.load_fragment
433433
scratch_mount := data.framework.scratch_mount
434434
scratch_unmount := data.framework.scratch_unmount
435+
rw_mount_device := data.framework.rw_mount_device
435436
436437
reason := {"errors": data.framework.errors}
437438
```

src/confcom/azext_confcom/_help.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@
105105
type: boolean
106106
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'
107107
108+
- name: --platform
109+
type: string
110+
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.'
111+
108112
examples:
109113
- name: Input an ARM Template file to inject a base64 encoded Confidential Container Security Policy into the ARM Template
110114
text: az confcom acipolicygen --template-file "./template.json"
@@ -116,6 +120,8 @@
116120
text: az confcom acipolicygen --template-file "./template.json" --tar "./image.tar"
117121
- name: Input an ARM Template file and use a fragments JSON file to generate a policy
118122
text: az confcom acipolicygen --template-file "./template.json" --fragments-json "./fragments.json" --include-fragments
123+
- name: Generate a Windows container policy (requires Docker Desktop in Windows containers mode)
124+
text: az confcom acipolicygen --template-file "./template.json" --platform windows/amd64 --outraw-pretty-print
119125
"""
120126

121127
helps[
@@ -340,7 +346,7 @@
340346
parameters:
341347
- name: --platform
342348
type: str
343-
short-summary: 'The name of the platform the container definition will run on'
349+
short-summary: 'The name of the platform the container definition will run on. Must be either "aci" or "vn2".'
344350
345351
346352
examples:

src/confcom/azext_confcom/_params.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,17 @@ def load_arguments(self, _):
114114
help="Image Name",
115115
validator=validate_aci_source
116116
)
117+
c.argument(
118+
"platform",
119+
options_list=("--platform",),
120+
required=False,
121+
default="linux/amd64",
122+
help="Target platform for policy generation. Defaults to linux/amd64. "
123+
"Note: Docker Desktop must be running in the matching container mode "
124+
"(Linux containers for linux/amd64, Windows containers for windows/amd64) "
125+
"to produce correct layer hashes.",
126+
choices=["linux/amd64", "windows/amd64"],
127+
)
117128
c.argument(
118129
"tar_mapping_location",
119130
options_list=("--tar",),
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"azext.minCliCoreVersion": "2.26.2"
3-
}
2+
"azext.minCliCoreVersion": "2.26.2",
3+
"azext.isPreview": true
4+
}

src/confcom/azext_confcom/command/containers_from_image.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
from azext_confcom.lib.containers import from_image as lib_containers_from_image
99

1010

11-
def containers_from_image(image: str, platform: str) -> None:
12-
print(json.dumps(lib_containers_from_image(image, platform)))
11+
def containers_from_image(image: str, aci_or_vn2: str) -> None:
12+
print(json.dumps(lib_containers_from_image(image, aci_or_vn2)))

src/confcom/azext_confcom/command/containers_from_vn2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ def containers_from_vn2(
192192

193193
container_defs = []
194194
for template_container, template_doc in template_containers:
195-
image_container_def = container_from_image(template_container.get("image"), platform="vn2")
195+
image_container_def = container_from_image(template_container.get("image"), aci_or_vn2="vn2")
196196

197197
template_container_def = {
198198
"name": template_container.get("name"),

src/confcom/azext_confcom/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS_RULE = "pattern"
128128
POLICY_FIELD_CONTAINERS_ELEMENTS_REQUIRED = "required"
129129
POLICY_FIELD_CONTAINERS_ELEMENTS_LAYERS = "layers"
130+
POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTED_CIM = "mounted_cim"
130131
POLICY_FIELD_CONTAINERS_ELEMENTS_WORKINGDIR = "working_dir"
131132
POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS = "mounts"
132133
POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS_SOURCE = "source"
@@ -211,6 +212,7 @@
211212
DEFAULT_REGO_FRAGMENTS = _config["default_rego_fragments"]
212213
# things that need to be set for debug mode
213214
DEBUG_MODE_SETTINGS = _config["debugMode"]
215+
DEBUG_MODE_SETTINGS_WINDOWS = _config["debugModeWindows"]
214216
# reserved fragment names for existing pieces of Rego
215217
RESERVED_FRAGMENT_NAMES = _config["reserved_fragment_namespaces"]
216218
# fragment artifact type
@@ -227,6 +229,7 @@
227229
}
228230
"""
229231
CUSTOMER_REGO_POLICY = load_str_from_file(REGO_FILE_PATH)
232+
CUSTOMER_REGO_POLICY_WINDOWS = load_str_from_file(f"{script_directory}/data/customer_rego_policy_windows.txt")
230233
CUSTOMER_REGO_FRAGMENT = load_str_from_file(REGO_FRAGMENT_FILE_PATH)
231234
# sidecar rego file
232235
SIDECAR_REGO_FILE = "./data/sidecar_rego_policy.txt"

src/confcom/azext_confcom/container.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,7 @@ def from_json(
563563
mounts=mounts,
564564
allow_elevated=allow_elevated,
565565
extraEnvironmentRules=[],
566+
platform=container_json.get("platform", "linux/amd64"),
566567
execProcesses=exec_processes,
567568
signals=signals,
568569
user=user,
@@ -583,6 +584,7 @@ def __init__(
583584
allow_elevated: bool,
584585
id_val: str,
585586
extraEnvironmentRules: Dict,
587+
platform: str = "linux/amd64",
586588
entrypoint: List[str] = None,
587589
capabilities: Dict = copy.deepcopy(_CAPABILITIES),
588590
user: Dict = copy.deepcopy(_DEFAULT_USER),
@@ -604,6 +606,7 @@ def __init__(
604606
self._command = command
605607
self._workingDir = workingDir
606608
self._layers = []
609+
self._mounted_cim = []
607610
self._mounts = mounts
608611
self._allow_elevated = allow_elevated
609612
self._allow_stdio_access = allowStdioAccess
@@ -615,6 +618,7 @@ def __init__(
615618
self._exec_processes = execProcesses or []
616619
self._signals = signals or []
617620
self._extraEnvironmentRules = extraEnvironmentRules
621+
self._platform = platform
618622

619623
def get_policy_json(self, omit_id: bool = False) -> str:
620624
return self._populate_policy_json_elements(omit_id=omit_id)
@@ -658,6 +662,12 @@ def get_layers(self) -> List[str]:
658662
def set_layers(self, layers: List[str]) -> None:
659663
self._layers = layers
660664

665+
def get_mounted_cim(self) -> List[str]:
666+
return self._mounted_cim
667+
668+
def set_mounted_cim(self, mounted_cim: List[str]) -> None:
669+
self._mounted_cim = mounted_cim
670+
661671
def get_user(self) -> Dict:
662672
return self._user
663673

@@ -764,16 +774,27 @@ def _populate_policy_json_elements(self, omit_id: bool = False) -> Dict[str, Any
764774
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ENVS: self._get_environment_rules(),
765775
config.POLICY_FIELD_CONTAINERS_ELEMENTS_WORKINGDIR: self._workingDir,
766776
config.POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTS: self._get_mounts_json(),
767-
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED: self._allow_elevated,
768777
config.POLICY_FIELD_CONTAINERS_ELEMENTS_EXEC_PROCESSES: self._exec_processes,
769778
config.POLICY_FIELD_CONTAINERS_ELEMENTS_SIGNAL_CONTAINER_PROCESSES: self._signals,
770-
config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user(),
771-
config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES: self._capabilities,
772-
config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256: self._seccomp_profile_sha256,
773779
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS: self._allow_stdio_access,
774-
config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES: not self._allow_privilege_escalation
775780
}
776781

782+
if self._platform.startswith("linux"):
783+
elements.update({
784+
config.POLICY_FIELD_CONTAINERS_ELEMENTS_CAPABILITIES: self._capabilities,
785+
config.POLICY_FIELD_CONTAINERS_ELEMENTS_SECCOMP_PROFILE_SHA256: self._seccomp_profile_sha256,
786+
config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user(),
787+
config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_ELEVATED: self._allow_elevated,
788+
config.POLICY_FIELD_CONTAINERS_ELEMENTS_NO_NEW_PRIVILEGES: not self._allow_privilege_escalation,
789+
})
790+
elif self._platform.startswith("windows"):
791+
elements.update({
792+
config.POLICY_FIELD_CONTAINERS_ELEMENTS_USER: self.get_user()["user_idname"]["pattern"],
793+
})
794+
# Add mounted_cim for Windows if present
795+
if self._mounted_cim:
796+
elements[config.POLICY_FIELD_CONTAINERS_ELEMENTS_MOUNTED_CIM] = self._mounted_cim
797+
777798
if not omit_id:
778799
elements[config.POLICY_FIELD_CONTAINERS_ID] = self._identifier
779800
# if we are omitting the id, we should remove the id value from the policy if it's in the name field
@@ -793,13 +814,17 @@ def from_json(
793814
image.__class__ = UserContainerImage
794815
# inject default mounts for user container
795816
if (image.base not in config.BASELINE_SIDECAR_CONTAINERS) and (not is_vn2):
796-
image.get_mounts().extend(_DEFAULT_MOUNTS)
817+
if container_json.get("platform", "linux/amd64").startswith("linux"):
818+
image.get_mounts().extend(_DEFAULT_MOUNTS)
797819

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

801823
# Start with the customer environment rules
802-
env_rules = copy.deepcopy(_INJECTED_CUSTOMER_ENV_RULES)
824+
env_rules = (
825+
copy.deepcopy(_INJECTED_CUSTOMER_ENV_RULES)
826+
if container_json.get("platform", "linux/amd64").startswith("linux") else []
827+
)
803828
# If is_vn2, add the VN2 environment rules
804829
if is_vn2:
805830
env_rules += _INJECTED_SERVICE_VN2_ENV_RULES

0 commit comments

Comments
 (0)