Merge pull request #240 from living-ip/fix/docker-capability-readback
Some checks are pending
CI / lint-and-test (push) Waiting to run

Handle canonical Docker capability readback
This commit is contained in:
Fawaz 2026-07-23 13:43:20 -07:00 committed by GitHub
commit 0312209c2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 74 additions and 3 deletions

View file

@ -1156,10 +1156,16 @@ def _validate_starting_container(
"container_security",
"container_cap_drop_mismatch",
)
cap_add = host.get("CapAdd")
normalized_cap_add = (
[item.removeprefix("CAP_") for item in cap_add]
if isinstance(cap_add, list) and all(isinstance(item, str) for item in cap_add)
else None
)
_verification_require(
isinstance(host.get("CapAdd"), list)
and all(isinstance(item, str) for item in host["CapAdd"])
and sorted(host["CapAdd"]) == ["CHOWN", "SETGID", "SETPCAP", "SETUID"],
normalized_cap_add is not None
and len(normalized_cap_add) == len(set(normalized_cap_add))
and sorted(normalized_cap_add) == ["CHOWN", "SETGID", "SETPCAP", "SETUID"],
"container_security",
"container_cap_add_mismatch",
)

View file

@ -725,6 +725,71 @@ def test_container_contract_failures_have_field_specific_bounded_codes(
assert caught.value.failure_reason == failure_reason
def test_container_cap_add_accepts_docker_28_canonical_names(tmp_path: Path) -> None:
bundle, release, image = release_bundle(tmp_path, "2")
paths = install_paths(tmp_path)
host = FakeHost(paths)
host.add_release(release, image)
installer.install_release(
bundle,
paths=paths,
source_binding=source_binding(),
runner=host,
sleeper=lambda _seconds: None,
execute_restart=True,
)
assert host.container is not None
host.container["HostConfig"]["CapAdd"] = [
"CAP_CHOWN",
"CAP_SETGID",
"CAP_SETPCAP",
"CAP_SETUID",
]
verification = installer.verify_running_release(
release,
runner=host,
paths=paths,
sleeper=lambda _seconds: None,
)
assert verification["container_id"] == host.container["Id"]
def test_container_cap_add_rejects_mixed_duplicate_names(tmp_path: Path) -> None:
bundle, release, image = release_bundle(tmp_path, "2")
paths = install_paths(tmp_path)
host = FakeHost(paths)
host.add_release(release, image)
installer.install_release(
bundle,
paths=paths,
source_binding=source_binding(),
runner=host,
sleeper=lambda _seconds: None,
execute_restart=True,
)
assert host.container is not None
host.container["HostConfig"]["CapAdd"] = [
"CHOWN",
"CAP_CHOWN",
"SETGID",
"SETPCAP",
"SETUID",
]
with pytest.raises(installer.VerificationError) as caught:
installer.verify_running_release(
release,
runner=host,
paths=paths,
sleeper=lambda _seconds: None,
)
assert caught.value.failure_stage == "container_security"
assert caught.value.failure_reason == "container_cap_add_mismatch"
def test_container_inspection_shape_has_bounded_code(tmp_path: Path) -> None:
bundle, release, image = release_bundle(tmp_path, "2")
paths = install_paths(tmp_path)