Skip to content

Commit 316acb6

Browse files
committed
fix: Address CoPilot feedback
- Centralize host-specific `dmverity-vhd resolution` in `rootfs_proxy.py` - Updated `get_image_layers()` to use the resolver - Added Darwin `arm64`, `x86_64`/`amd64`, and unsupported arch coverage - Bumped extension version to 2.2.0 - Clarified Docker requirements for image workflows
1 parent 45570e9 commit 316acb6

8 files changed

Lines changed: 121 additions & 36 deletions

File tree

‎src/confcom/HISTORY.rst‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
Release History
44
===============
55

6+
2.4.0
7+
+++++
8+
* Add MacOS support for Linux-container policy generation
9+
610
2.3.0
711
+++++
812
* Add an option to include the kube-proxy image-attached fragment in Linux VN2 policies
@@ -25,7 +29,6 @@ Release History
2529
+++++
2630

2731
* Added de-duping of exec_processes in the generated policies so same command used in two probes results in one exec_processes entry
28-
* Add MacOS support for Linux-container policy generation
2932

3033
2.1.0
3134
+++++

‎src/confcom/README.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
- Ubuntu 18.04 or later comes with python 3.6+ by default
2525
- **64-bit** MacOS on Apple Silicon or Intel
2626
- Container runtime: Docker
27-
- Docker must be running when generating Linux policies.
27+
- Docker must be running when generating Linux policies from container images.
28+
- Docker is not required when all image layers are supplied with `--tar`.
2829
- Windows: [Docker Desktop](https://www.docker.com/products/docker-desktop) and [WSL2](https://docs.microsoft.com/en-us/windows/wsl/install)
2930
- **CimWriter.dll** (Windows only, for Windows container support)
3031
- Required for generating security policies for Windows containers
@@ -61,7 +62,7 @@ The `confcom` extension does not currently support:
6162

6263
The `--platform` parameter controls whether policies are generated for Linux (`linux/amd64`, the default) or Windows (`windows/amd64`) containers. On macOS, only Linux-container policies are supported.
6364

64-
Docker must be running when generating Linux policies.
65+
Docker must be running when generating Linux policies from container images. It is not required when all image layers are supplied with `--tar`.
6566

6667
Windows policies require Docker in Windows-container mode:
6768

‎src/confcom/azext_confcom/_help.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
108108
- name: --platform
109109
type: string
110-
short-summary: 'Target platform for policy generation (linux/amd64 or windows/amd64). Defaults to linux/amd64. Docker must be running for Linux policies; Docker in Windows-container mode is required for Windows policies.'
110+
short-summary: 'Target platform for policy generation (linux/amd64 or windows/amd64). Defaults to linux/amd64. Docker must be running for Linux image workflows; it is not required when all image layers are supplied with --tar. Docker in Windows-container mode is required for Windows policies.'
111111
112112
- name: --allow-kubeproxy
113113
type: boolean

‎src/confcom/azext_confcom/_params.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ def load_arguments(self, _):
121121
required=False,
122122
default="linux/amd64",
123123
help="Target platform for policy generation. Defaults to linux/amd64. "
124-
"Docker must be running for Linux policies; Docker in "
124+
"Docker must be running for Linux image workflows; it is not "
125+
"required when all image layers are supplied with --tar. Docker in "
125126
"Windows-container mode is required for Windows policies.",
126127
choices=["linux/amd64", "windows/amd64"],
127128
)

‎src/confcom/azext_confcom/lib/images.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import logging
88
import subprocess
99
import docker
10+
from azext_confcom.rootfs_proxy import get_dmverity_vhd_path
1011

11-
from pathlib import Path
1212

1313
logger = logging.getLogger(__name__)
1414

@@ -57,8 +57,7 @@ def get_image_platform(image_reference: str) -> str:
5757

5858
def get_image_layers(image: str, platform: str = "linux/amd64") -> list[str]:
5959

60-
binary_path = Path(__file__).parent.parent / "bin" / "dmverity-vhd"
61-
60+
binary_path = get_dmverity_vhd_path()
6261
get_image(image)
6362

6463
arg_list = [binary_path.as_posix(), "-d", "roothash", "-i", image]

‎src/confcom/azext_confcom/rootfs_proxy.py‎

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import stat
1212
import subprocess
1313
import sys
14+
from pathlib import Path
1415
from typing import List, Dict
1516

1617
import requests
@@ -48,12 +49,12 @@
4849
"url": "https://github.com/microsoft/integrity-vhd/releases/download/v2.3/dmverity-vhd.exe",
4950
"sha256": "c3652fb1a4ce1155c388e0e2f7db6b2b985e0ad6da7db062ce82a00bb2b6b6f8",
5051
},
51-
"Darwin-arm64": {
52+
{
5253
"path": _binaries_dir / "dmverity-vhd-darwin-arm64",
5354
"url": "https://github.com/microsoft/integrity-vhd/releases/download/v2.3/dmverity-vhd-darwin-arm64",
5455
"sha256": "e7174315c8df15ceb59120d720b63cb62f7cbd1ec6cb3b13436370da72c792be",
5556
},
56-
"Darwin-amd64": {
57+
{
5758
"path": _binaries_dir / "dmverity-vhd-darwin-amd64",
5859
"url": "https://github.com/microsoft/integrity-vhd/releases/download/v2.3/dmverity-vhd-darwin-amd64",
5960
"sha256": "4cb5a2970030b120370afd03b25b12605343fd098c4ce539f9f117a17fc2112d",
@@ -71,6 +72,30 @@
7172
]
7273

7374

75+
def get_dmverity_vhd_path() -> Path:
76+
"""Return the host-specific dmverity-vhd binary path."""
77+
binary_name = "dmverity-vhd"
78+
79+
if host_os == "Windows":
80+
if machine.endswith("64"):
81+
binary_name += ".exe"
82+
else:
83+
eprint("32-bit Windows is not supported.")
84+
elif host_os == "Darwin":
85+
if machine == "arm64":
86+
binary_name += "-darwin-arm64"
87+
elif machine in ("x86_64", "amd64"):
88+
binary_name += "-darwin-amd64"
89+
else:
90+
eprint(f"Unsupported MacOS architecture: {machine}.")
91+
elif host_os != "Linux":
92+
eprint(
93+
"Unknown target platform. The extension only works with Windows, Linux, and Darwin"
94+
)
95+
96+
return Path(__file__).parent / "bin" / binary_name
97+
98+
7499
class SecurityPolicyProxy: # pylint: disable=too-few-public-methods
75100
# static variable to cache layer hashes between container groups
76101
layer_cache = {}
@@ -88,31 +113,7 @@ def download_binaries():
88113
f.write(dmverity_vhd_fetch_resp.content)
89114

90115
def __init__(self):
91-
script_directory = os.path.dirname(os.path.realpath(__file__))
92-
DEFAULT_LIB = "./bin/dmverity-vhd"
93-
94-
if host_os == "Linux":
95-
pass
96-
elif host_os == "Windows":
97-
if machine.endswith("64"):
98-
DEFAULT_LIB += ".exe"
99-
else:
100-
eprint(
101-
"32-bit Windows is not supported."
102-
)
103-
elif host_os == "Darwin":
104-
if machine == "arm64":
105-
DEFAULT_LIB += "-darwin-arm64"
106-
elif machine in ("x86_64", "amd64"):
107-
DEFAULT_LIB += "-darwin-amd64"
108-
else:
109-
eprint(f"Unsupported MacOS architecture: {machine}.")
110-
else:
111-
eprint(
112-
"Unknown target platform. The extension only works with Windows, Linux, and Darwin"
113-
)
114-
115-
self.policy_bin = os.path.join(f"{script_directory}", f"{DEFAULT_LIB}")
116+
self.policy_bin = str(get_dmverity_vhd_path())
116117

117118
# check if the extension binary exists
118119
if not os.path.exists(self.policy_bin):
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# --------------------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for license information.
4+
# --------------------------------------------------------------------------------------------
5+
6+
import subprocess
7+
import unittest
8+
from pathlib import Path
9+
from unittest import mock
10+
11+
from azext_confcom import cose_proxy, rootfs_proxy
12+
from azext_confcom.lib import images
13+
14+
15+
class TestDarwinBinarySelection(unittest.TestCase):
16+
def test_dmverity_binary_selection(self):
17+
for machine, expected in (
18+
("arm64", "dmverity-vhd-darwin-arm64"),
19+
("x86_64", "dmverity-vhd-darwin-amd64"),
20+
("amd64", "dmverity-vhd-darwin-amd64"),
21+
):
22+
with self.subTest(machine=machine):
23+
with mock.patch.object(rootfs_proxy, "host_os", "Darwin"), \
24+
mock.patch.object(rootfs_proxy, "machine", machine), \
25+
mock.patch.object(rootfs_proxy.os.path, "exists", return_value=True), \
26+
mock.patch.object(rootfs_proxy.os, "access", return_value=True):
27+
self.assertEqual(
28+
Path(rootfs_proxy.SecurityPolicyProxy().policy_bin).name,
29+
expected,
30+
)
31+
32+
def test_cose_binary_selection(self):
33+
for machine, expected in (
34+
("arm64", "sign1util-darwin-arm64"),
35+
("x86_64", "sign1util-darwin-amd64"),
36+
("amd64", "sign1util-darwin-amd64"),
37+
):
38+
with self.subTest(machine=machine):
39+
with mock.patch.object(cose_proxy, "host_os", "Darwin"), \
40+
mock.patch.object(cose_proxy, "machine", machine), \
41+
mock.patch.object(cose_proxy.os.path, "exists", return_value=True), \
42+
mock.patch.object(cose_proxy.os, "access", return_value=True):
43+
self.assertEqual(
44+
Path(cose_proxy.CoseSignToolProxy().policy_bin).name,
45+
expected,
46+
)
47+
48+
def test_unsupported_dmverity_architecture(self):
49+
with mock.patch.object(rootfs_proxy, "host_os", "Darwin"), \
50+
mock.patch.object(rootfs_proxy, "machine", "powerpc64"):
51+
with self.assertRaises(SystemExit) as exception:
52+
rootfs_proxy.get_dmverity_vhd_path()
53+
self.assertEqual(exception.exception.code, 1)
54+
55+
def test_unsupported_cose_architecture(self):
56+
with mock.patch.object(cose_proxy, "host_os", "Darwin"), \
57+
mock.patch.object(cose_proxy, "machine", "powerpc64"):
58+
with self.assertRaises(SystemExit) as exception:
59+
cose_proxy.CoseSignToolProxy()
60+
self.assertEqual(exception.exception.code, 1)
61+
62+
@mock.patch.object(images, "get_image")
63+
@mock.patch.object(images.subprocess, "run")
64+
@mock.patch.object(images, "get_dmverity_vhd_path")
65+
def test_image_layer_hashing_uses_host_binary(self, get_binary_path, run, get_image):
66+
binary_path = Path("/tmp/dmverity-vhd-darwin-arm64")
67+
get_binary_path.return_value = binary_path
68+
run.return_value = subprocess.CompletedProcess(
69+
args=[],
70+
returncode=0,
71+
stdout="Layer 0 root hash: abc123\n",
72+
)
73+
74+
self.assertEqual(images.get_image_layers("image:tag"), ["abc123"])
75+
self.assertEqual(run.call_args[0][0][0], binary_path.as_posix())
76+
get_image.assert_called_once_with("image:tag")
77+
78+
79+
if __name__ == "__main__":
80+
unittest.main()

‎src/confcom/setup.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
logger.warn("Wheel is not available, disabling bdist_wheel hook")
2121

22-
VERSION = "2.3.0"
22+
VERSION = "2.4.0"
2323

2424
# The full list of classifiers is available at
2525
# https://pypi.python.org/pypi?%3Aaction=list_classifiers

0 commit comments

Comments
 (0)