Persistent storage

The Anjuna Nitro Runtime provides a seamless experience for running AWS Nitro Enclaves with persistent storage. This section describes the persistent storage options that the Anjuna Nitro Runtime supports and describes setting up AWS Nitro Enclaves with persistent storage.

Block mounts & basic mounts

The Anjuna Nitro Runtime provides two methods to store persistent data: block mounts and basic mounts. Both options look the same from within the enclave and provide a persistent mount point.

Block mounts allow persisting data inside an enclave, and they are more performant than basic mounts. Block mounts are created and managed by the Anjuna Nitro Runtime. Block mounts are persistent, and the data is saved on the parent instance. However, only one enclave can access the contents of the block mount, and neither the parent instance nor another enclave can mount that volume when it is in use by an enclave. You can create multiple block mounts with different sizes. A single enclave can mount a maximum of one volume at a time.

Basic mounts allow binding an existing file or directory from the parent instance into an enclave. It is less performant compared to block mounts but does not require creating a dedicated volume. As opposed to block mounts, basic mounts can be shared between enclaves on the same host.

Persistent storage security

Persistent volume mounts provide shared storage between the AWS Nitro Enclave and the untrusted parent instance. This is a convenient way to externally configure an enclave application, provide input data, obtain results from an enclave, or share data between enclaves on the same host. However, if used incorrectly, this feature can open up vulnerabilities that could compromise the security of the enclave, either by exposing sensitive data or by allowing an attacker to modify the operation of the enclave application. Therefore, it is important that the end user analyze the potential implications of opening access to an untrusted mount directory (the mountPath) and ensure that the enclave application does not trust any data within the file tree of a volume mount or access it by a method that could compromise security.

Some examples of potential security issues created by shared storage:

  • The enclave application reads configuration files from a volume mount, but allows for configuration options that are critical to the operation of the enclave and could be used to modify execution or obtain sensitive information.

  • The enclave application reads configuration files from a volume mount, does not allow critical options to be specified in the config, but due to a security bug (such as a buffer overflow in the config parser) allows an attacker to gain control of the enclave application.

  • The enclave application uses a volume mount for config files, but may also store sensitive data such as log files, which are exposed to the untrusted parent instance.

  • The enclave application provides a way to customize functionality through external scripts or executables that are within a volume mount. These executables would be under the control of the parent instance and could therefore be used to take control of the enclave application or modify its execution.

  • The enclave application accesses shared libraries on a volume mount, which allows an attacker to execute arbitrary code.

There are additional security implications of the forceMount option. This option provides a convenient way to mount over existing content in a Docker image. This is useful when the Docker image is distributed by a third party or is not easily changed. However, if used incorrectly it can expose the enclave application data or introduce additional vulnerabilities. The most secure option is to remove the conflicting resource from the Docker image, when creating the EIF file. However, if this is not practical, then the forceMount option can be used to overwrite the target mountPath.

In order to limit the scope of the override, this option uses individual flags to specify the type of resources that will be overwritten. These flags should be limited to the expected type of conflicting resources. If, for example, a Docker image has an empty directory for configuration files that the user wants to configure externally from the parent instance, the forceMount option could be set to empty, as shown below:

mounts:
- type: basic
  name: example-volume
  mountPath: /etc/app
  forceMount: empty

If instead of empty, the all option was used, an update to the Docker image by the third party, with a new unexpected feature that has an executable script or shared library executed by the application now in the volume mountPath, would expose the application to an attack when a new EIF image is built. With the more restrictive option, a newly built EIF image would fail to start, recognizing that there are now files where an empty directory was expected.

The full option can be used in cases where a directory contains content that the persistent storage should be mounted over. However, there are no checks in this case on what the content of the directory is and whether it might change something critical in the Docker image. This flag should be considered the least secure flag with the forceMount option.