Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CVM: add attestation report test for Azure Linux using snpguest tool #3529

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions lisa/tools/cargo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
import re
from pathlib import PurePath
from typing import Any, List, Optional, Type, cast
from typing import Any, List, Optional, Type, Union, cast

from lisa.executable import Tool
from lisa.operating_system import CBLMariner, Posix, Ubuntu
Expand Down Expand Up @@ -117,6 +117,7 @@ def __install_dependencies(self) -> None:

def build(
self,
options: str = "",
Copy link
Member

Choose a reason for hiding this comment

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

The options is flexible, but not easy to reuse logic. Please define specified arguments as much as possible.

sudo: bool = False,
cwd: Optional[PurePath] = None,
) -> ExecutableResult:
Expand All @@ -132,8 +133,9 @@ def build(
if os.path.dirname(self._command) not in path:
path = f"{os.path.dirname(self._command)}:{path}"

command = f"build {options}"
result = self.run(
"build",
command,
expected_exit_code=0,
expected_exit_code_failure_message=err_msg,
sudo=sudo,
Expand Down
23 changes: 15 additions & 8 deletions microsoft/testsuites/cvm/cvm_attestation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from pathlib import Path
from typing import Any, Dict

from assertpy import assert_that

from lisa import (
Environment,
Logger,
Expand All @@ -13,14 +15,15 @@
features,
)
from lisa.features.security_profile import CvmEnabled
from lisa.operating_system import Ubuntu
from lisa.operating_system import CBLMariner, Ubuntu
from lisa.sut_orchestrator import AZURE
from lisa.testsuite import TestResult, simple_requirement
from lisa.tools import Ls
from lisa.util import SkippedException, UnsupportedDistroException
from microsoft.testsuites.cvm.cvm_attestation_tool import (
AzureCVMAttestationTests,
NestedCVMAttestationTests,
SnpGuest,
)


Expand All @@ -34,10 +37,11 @@
class AzureCVMAttestationTestSuite(TestSuite):
def before_case(self, log: Logger, **kwargs: Any) -> None:
node: Node = kwargs["node"]
if not isinstance(node.os, Ubuntu):
if not isinstance(node.os, Ubuntu) and not isinstance(node.os, CBLMariner):
raise SkippedException(
UnsupportedDistroException(
node.os, "CVM attestation report supports only Ubuntu."
node.os,
"CVM attestation report supports only Ubuntu and Azure Linux.",
)
)

Expand All @@ -61,11 +65,14 @@ def verify_azure_cvm_attestation_report(
result: TestResult,
variables: Dict[str, Any],
) -> None:
node.tools[AzureCVMAttestationTests].run_cvm_attestation(
result,
environment,
log_path,
)
if isinstance(node.os, Ubuntu):
node.tools[AzureCVMAttestationTests].run_cvm_attestation(
result,
environment,
log_path,
)
elif isinstance(node.os, CBLMariner):
assert_that(node.tools[SnpGuest].run_cvm_attestation()).is_true()


@TestSuiteMetadata(
Expand Down
150 changes: 148 additions & 2 deletions microsoft/testsuites/cvm/cvm_attestation_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
from lisa import Environment
from lisa.executable import Tool
from lisa.features import SerialConsole
from lisa.operating_system import Posix, Ubuntu
from lisa.operating_system import CBLMariner, Posix, Ubuntu
from lisa.testsuite import TestResult
from lisa.tools import Dmesg, Echo, Git, Make
from lisa.tools import Cargo, Dmesg, Echo, Git, Make, Mkdir
from lisa.util import UnsupportedDistroException
from lisa.util.process import ExecutableResult


class AzureCVMAttestationTests(Tool):
Expand Down Expand Up @@ -121,6 +122,151 @@ def _save_attestation_report(self, output: str, log_path: Path) -> None:
f.write(output)


class SnpGuest(Tool):
_snpguest_repo = "https://github.com/virtee/snpguest"
cmd_path: PurePath
repo_root: PurePath

@property
def command(self) -> str:
return str(self.cmd_path)

@property
def can_install(self) -> bool:
return True

@property
def dependencies(self) -> List[Type[Tool]]:
return [Git, Cargo, Mkdir]

def _initialize(self, *args: Any, **kwargs: Any) -> None:
tool_path = self.get_tool_path(use_global=True)

self.repo_root = tool_path / "snpguest"
self.cmd_path = self.repo_root / "target" / "release" / "snpguest"

def _install(self) -> bool:
if isinstance(self.node.os, CBLMariner):
self.node.os.install_packages(["perl", "tpm2-tss-devel"])
tool_path = self.get_tool_path(use_global=True)
git = self.node.tools[Git]
git.clone(self._snpguest_repo, tool_path)

cargo = self.node.tools[Cargo]
cargo.build(
options="--release --features=hyperv", sudo=False, cwd=self.repo_root
)

return self._check_exists()

def _fetch_ca(
self,
certs_dir: str,
encoding: str = "der",
processor_model: str = "milan",
endorser: str = "vcek",
) -> ExecutableResult:
failure_msg = "failed to request CA chain from the KDS"
return self.run(
f"fetch ca {encoding} {processor_model} {certs_dir} --endorser {endorser}",
expected_exit_code=0,
expected_exit_code_failure_message=failure_msg,
shell=True,
sudo=False,
force_run=True,
)

def _fetch_vcek(
self,
certs_dir: str,
attestation_report_path: str,
encoding: str = "der",
processor_model: str = "milan",
) -> ExecutableResult:
failure_msg = "failed to request VCEK from the KDS"
return self.run(
f"fetch vcek {encoding} {processor_model} {certs_dir} "
f"{attestation_report_path}",
expected_exit_code=0,
expected_exit_code_failure_message=failure_msg,
shell=True,
sudo=False,
force_run=True,
)

def _request_attestation_report(
self, attestation_report_path: str, request_file_path: str
) -> ExecutableResult:
failure_msg = "failed to request attestation report from the host"
return self.run(
f"report {attestation_report_path} {request_file_path} --platform --vmpl 0",
expected_exit_code=0,
expected_exit_code_failure_message=failure_msg,
shell=True,
sudo=True,
force_run=True,
)

def _verify_certs(self, certs_dir: str) -> ExecutableResult:
failure_msg = "failed to verify certificates"
return self.run(
f"verify certs {certs_dir}",
expected_exit_code=0,
expected_exit_code_failure_message=failure_msg,
shell=True,
sudo=False,
force_run=True,
)

def _verify_attestation(
self, certs_dir: str, attestation_report_path: str
) -> ExecutableResult:
failure_msg = "failed to verify attestation report"
return self.run(
f"verify attestation {certs_dir} {attestation_report_path}",
expected_exit_code=0,
expected_exit_code_failure_message=failure_msg,
shell=True,
sudo=False,
force_run=True,
)

def run_cvm_attestation(self, processor_model: str = "milan") -> bool:
"""Regular attestation workflow

1. Request attestation report
2. Request AMD Root Key (ARK) and AMD SEV Key (ASK) from AMD Key Distribution
Service (KDS)
3. Request the Versioned Chip Endorsement Key (VCEK) from AMD KDS
4. Verify the certificates obtained
5. Verify the attestation report
"""
data_dir = self.repo_root / "data"
certs_dir = data_dir / "certs"
attestation_report_path = data_dir / "attestation-report.bin"
request_file_path = data_dir / "request-file.txt"

mkdir = self.node.tools[Mkdir]
mkdir.create_directory(certs_dir.as_posix())

self._request_attestation_report(
attestation_report_path.as_posix(), request_file_path.as_posix()
)
self._fetch_ca(certs_dir.as_posix(), processor_model=processor_model)
self._fetch_vcek(
certs_dir.as_posix(),
attestation_report_path.as_posix(),
processor_model=processor_model,
)

self._verify_certs(certs_dir.as_posix())
self._verify_attestation(
certs_dir.as_posix(), attestation_report_path.as_posix()
)

return True


class NestedCVMAttestationTests(Tool):
repo = "https://github.com/microsoft/confidential-sidecar-containers.git"
cmd_path: str
Expand Down
Loading