Skip to content
Merged
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
7 changes: 7 additions & 0 deletions src/confcom/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Release History
===============

1.2.3
++++++
* adding fragment support for VN2
* bugfix for vn2 workload identities
* no longer encouraged to have multiple images in the same tar file

1.2.2
++++++
* support for pure OCI v1 schema 2 formatted images
Expand Down
42 changes: 25 additions & 17 deletions src/confcom/azext_confcom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,22 +181,7 @@ Users just need to make a tar file by using the `docker save` command above, inc
When generating security policy without using `--tar` argument, the confcom extension CLI tool attemps to fetch the image remotely if it is not locally available.
However, the CLI tool does not attempt to fetch remotely if `--tar` argument is used.

Example 11: The process used in example 10 can also be used to save multiple images into the same tar file. See the following example:

```bash
docker save ImageTag1 ImageTag2 ImageTag3 -o file.tar
```

Disconnect from network and delete the local image from the docker daemon.
Use the following command to generate CCE policy for the image.

```bash
az confcom acipolicygen -a .\sample-template-input.json --tar .\file.tar
```

Note that multiple images saved to the tar file is only available using the docker-archive format for tar files. OCI does not support multi-image tar files at this time.

Example 12: If it is necessary to put images in their own tarballs, an external file can be used that maps images to their respective tarball paths. See the following example:
Example 11: If it is necessary to put images in their own tarballs, an external file can be used that maps images to their respective tarball paths. See the following example:

```bash
docker save image:tag1 -o file1.tar
Expand All @@ -221,7 +206,7 @@ Use the following command to generate CCE policy for the image.
az confcom acipolicygen -a .\sample-template-input.json --tar .\tar_mappings.json
```

Example 13: Some use cases necessitate the use of regular expressions to allow for environment variables where either their values are secret, or unknown at policy-generation time. For these cases, the workflow below can be used:
Example 12: Some use cases necessitate the use of regular expressions to allow for environment variables where either their values are secret, or unknown at policy-generation time. For these cases, the workflow below can be used:

Create parameters in the ARM Template for each environment variable that has an unknown or secret value such as:

Expand Down Expand Up @@ -292,6 +277,29 @@ Use the following command to generate and print a security policy for an AKS pod
az confcom acipolicygen --virtual-node-yaml ./pod.yaml --print-policy
```

To generate a security policy using a policy config file for Virtual Node, the `scenario` field must be equal to `"vn2"`. This looks like:

```json
{
"version": "1.0",
"scenario": "vn2",
"containers": [
{
"name": "my-image",
"properties": {
"image": "mcr.microsoft.com/acc/samples/aci/helloworld:2.8"
}
}
]
}
```

This `scenario` field adds the necessary environment variables and mount values to containers in the config file.

### Workload Identity

To use workload identities with VN2, the associated label [described here](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet#pod-labels) must be present. Having this will add the requisite environment variables and mounts to each container's policy.

> [!NOTE]
> The `acipolicygen` command is specific to generating policies for ACI-based containers. For generating security policies for the [Confidential Containers on AKS](https://learn.microsoft.com/en-us/azure/aks/confidential-containers-overview) feature, use the `katapolicygen` command.

Expand Down
2 changes: 2 additions & 0 deletions src/confcom/azext_confcom/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
validate_fragment_json_policy,
validate_image_target,
validate_upload_fragment,
validate_infrastructure_svn,
)


Expand Down Expand Up @@ -88,6 +89,7 @@ def load_arguments(self, _):
options_list=("--infrastructure-svn",),
required=False,
help="Minimum Allowed Software Version Number for Infrastructure Fragment",
validator=validate_infrastructure_svn,
)
c.argument(
"debug_mode",
Expand Down
5 changes: 5 additions & 0 deletions src/confcom/azext_confcom/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ def validate_print_format(namespace):
raise CLIError("Can only print in one format at a time")


def validate_infrastructure_svn(namespace):
if namespace.infrastructure_svn and namespace.exclude_default_fragments:
raise CLIError("Cannot set infrastructure SVN without using default fragments")


def validate_aci_source(namespace):
if sum(map(bool, [
namespace.input_path,
Expand Down
12 changes: 12 additions & 0 deletions src/confcom/azext_confcom/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
ACI_FIELD_RESOURCES = "resources"
ACI_FIELD_RESOURCES_NAME = "name"
ACI_FIELD_CONTAINERS = "containers"
ACI_FIELD_SCENARIO = "scenario"
ACI_FIELD_CONTAINERS_NAME = "name"
ACI_FIELD_CONTAINERS_CONTAINERIMAGE = "containerImage"
ACI_FIELD_CONTAINERS_ENVS = "environmentVariables"
Expand Down Expand Up @@ -63,6 +64,7 @@
ACI_FIELD_TEMPLATE_RESOURCE_LABEL = "Microsoft.ContainerInstance/containerGroups"
ACI_FIELD_TEMPLATE_RESOURCE_PROFILE_LABEL = "Microsoft.ContainerInstance/containerGroupProfiles"
ACI_FIELD_TEMPLATE_COMMAND = "command"
ACI_FIELD_TEMPLATE_ENTRYPOINT = "entrypoint"
ACI_FIELD_TEMPLATE_ENVS = "environmentVariables"
ACI_FIELD_TEMPLATE_VOLUME_MOUNTS = "volumeMounts"
ACI_FIELD_TEMPLATE_MOUNTS_TYPE = "mountType"
Expand All @@ -84,8 +86,12 @@
ACI_FIELD_YAML_LIVENESS_PROBE = "livenessProbe"
ACI_FIELD_YAML_READINESS_PROBE = "readinessProbe"
ACI_FIELD_YAML_STARTUP_PROBE = "startupProbe"
ACI_FIELD_TEMPLATE_SPECIAL_ENV_VAR_REGEX_NAME = "THIM_ENDPOINT"
ACI_FIELD_TEMPLATE_SPECIAL_ENV_VAR_REGEX_VALUE = "^===CONFIDENTIAL.THIM.ENDPOINT===$"

VIRTUAL_NODE_YAML_METADATA = "metadata"
VIRTUAL_NODE_YAML_COMMAND = "command"
VIRTUAL_NODE_YAML_ARGS = "args"
VIRTUAL_NODE_YAML_NAME = "name"
VIRTUAL_NODE_YAML_ANNOTATIONS = "annotations"
VIRTUAL_NODE_YAML_LABELS = "labels"
Expand Down Expand Up @@ -158,6 +164,11 @@
REGO_CONTAINER_START = "containers := "
REGO_FRAGMENT_START = "fragments := "

# scenario options
VN2 = "vn2"
ACI = "aci"
KATA = "kata"


CONFIG_FILE = "./data/internal_config.json"

Expand Down Expand Up @@ -191,6 +202,7 @@
DEFAULT_MOUNTS_USER_VIRTUAL_NODE = _config["mount"]["default_mounts_user_virtual_node"]
DEFAULT_MOUNTS_VIRTUAL_NODE = _config["mount"]["default_mounts_virtual_node"]
DEFAULT_MOUNTS_PRIVILEGED_VIRTUAL_NODE = _config["mount"]["default_mounts_virtual_node_privileged"]
DEFAULT_MOUNTS_WORKLOAD_IDENTITY_VIRTUAL_NODE = _config["mount"]["default_mounts_workload_identity_virtual_node"]
# default mounts policy options for all containers
DEFAULT_MOUNT_POLICY = _config["mount"]["default_policy"]
# default rego policy to be added to all user containers
Expand Down
32 changes: 29 additions & 3 deletions src/confcom/azext_confcom/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ def extract_working_dir(container_json: Any) -> str:
return workingDir


def extract_entrypoint(container_json: Any) -> List[str]:
# parse entrypoint. can either be a list of strings or None in the case of non-VN2 policy generation
entrypoint = case_insensitive_dict_get(
container_json, config.ACI_FIELD_TEMPLATE_ENTRYPOINT
)
if not isinstance(entrypoint, list) and entrypoint is not None:
eprint(
f'Field ["{config.ACI_FIELD_CONTAINERS}"]'
+ f'["{config.ACI_FIELD_TEMPLATE_ENTRYPOINT}"] must be list of Strings.'
)
return entrypoint


def extract_command(container_json: Any) -> List[str]:
# parse command
command = case_insensitive_dict_get(
Expand Down Expand Up @@ -512,7 +525,7 @@ def extract_get_signals(container_json: Any) -> List:


class ContainerImage:
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-instance-attributes, too-many-public-methods

@classmethod
def from_json(
Expand All @@ -523,6 +536,7 @@ def from_json(
id_val = extract_id(container_json)
container_name = extract_container_name(container_json)
environment_rules = extract_env_rules(container_json=container_json)
entrypoint = extract_entrypoint(container_json)
command = extract_command(container_json)
working_dir = extract_working_dir(container_json)
mounts = extract_mounts(container_json)
Expand All @@ -543,6 +557,7 @@ def from_json(
containerImage=container_image,
containerName=container_name,
environmentRules=environment_rules,
entrypoint=entrypoint,
command=command,
workingDir=working_dir,
mounts=mounts,
Expand All @@ -568,14 +583,15 @@ def __init__(
allow_elevated: bool,
id_val: str,
extraEnvironmentRules: Dict,
entrypoint: List[str] = None,
capabilities: Dict = copy.deepcopy(_CAPABILITIES),
user: Dict = copy.deepcopy(_DEFAULT_USER),
seccomp_profile_sha256: str = "",
allowStdioAccess: bool = True,
allowPrivilegeEscalation: bool = True,
execProcesses: List = None,
signals: List = None,
containerName: str = ""
containerName: str = "",
) -> None:
self.containerImage = containerImage
self.containerName = containerName
Expand All @@ -584,6 +600,7 @@ def __init__(
else:
self.base, self.tag = containerImage, "latest"
self._environmentRules = environmentRules
self._entrypoint = entrypoint
self._command = command
self._workingDir = workingDir
self._layers = []
Expand Down Expand Up @@ -621,6 +638,11 @@ def set_signals(self, signals: List) -> None:
def set_working_dir(self, workingDir: str) -> None:
self._workingDir = workingDir

# note that entrypoint is only used for VN2 containers because of kubernetes discrepancy in naming
# entrypoint -> command, args -> command
def get_entrypoint(self) -> List[str]:
return self._entrypoint

def get_command(self) -> List[str]:
return self._command

Expand All @@ -645,6 +667,9 @@ def set_user(self, user: Dict) -> None:
def get_mounts(self) -> List:
return self._mounts

def set_mounts(self, mounts) -> None:
self._mounts = mounts

def get_seccomp_profile_sha256(self) -> str:
return self._seccomp_profile_sha256

Expand Down Expand Up @@ -774,10 +799,11 @@ def from_json(
image.get_mounts().extend(_DEFAULT_MOUNTS_VN2)

# Start with the customer environment rules
env_rules = _INJECTED_CUSTOMER_ENV_RULES
env_rules = copy.deepcopy(_INJECTED_CUSTOMER_ENV_RULES)
# If is_vn2, add the VN2 environment rules
if is_vn2:
env_rules += _INJECTED_SERVICE_VN2_ENV_RULES
image.set_mounts(image.get_mounts() + copy.deepcopy(config.DEFAULT_MOUNTS_VIRTUAL_NODE))

image.set_extra_environment_rules(env_rules)
return image
Expand Down
5 changes: 4 additions & 1 deletion src/confcom/azext_confcom/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ def acipolicygen_confcom(
debug_mode=debug_mode,
disable_stdio=disable_stdio,
approve_wildcards=approve_wildcards,
diff_mode=diff
diff_mode=diff,
rego_imports=fragments_list,
exclude_default_fragments=exclude_default_fragments,
infrastructure_svn=infrastructure_svn,
)

exit_code = 0
Expand Down
10 changes: 9 additions & 1 deletion src/confcom/azext_confcom/data/internal_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.2.2",
"version": "1.2.3",
"hcsshim_config": {
"maxVersion": "1.0.0",
"minVersion": "0.0.1"
Expand Down Expand Up @@ -282,6 +282,14 @@
"mountType": "emptyDir",
"mountPath": "/etc/hostname"
}
],
"default_mounts_workload_identity_virtual_node": [
{
"name": "azure-tokens",
"mountType": "emptyDir",
"mountPath": "/var/run/secrets/azure/tokens",
"readonly": true
}
]
},
"sidecar_base_names": [
Expand Down
Loading
Loading